capsules_extra/net/ipv6/ipv6.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! This file contains structs, traits, and methods associated with the IP layer
6//! of the networking stack. This includes the declaration and methods for the
7//! IP6Header, IP6Packet, and IP6Payload structs. These methods implement the
8//! bulk of the functionality required for manipulating the fields of the
9//! IPv6 header. Additionally, the IP6Packet struct allows for multiple types
10//! of transport-level structures to be encapsulated within.
11//!
12//! An implementation for the structure of an IPv6 packet is provided by this
13//! file, and a rough outline is given below:
14//!
15//! ```txt
16//! ----------------------------------------------
17//! | IP6Packet |
18//! |--------------------------------------------|
19//! | | IPPayload |
20//! | IP6Header |--------------------------|
21//! | |TransportHeader | Payload |
22//! ----------------------------------------------
23//! ```
24//!
25//! The [IP6Packet](struct.IP6Packet.html) struct contains an
26//! [IP6Header](struct.IP6Header.html) struct and an
27//! [IPPayload](struct.IPPayload.html) struct, with the `IPPayload` struct
28//! also containing a [TransportHeader](enum.TransportHeader.html) enum and
29//! a `Payload` buffer. Note that transport-level headers are contained inside
30//! the `TransportHeader`.
31//!
32//! For a client interested in using this interface, they first statically
33//! allocate an `IP6Packet` struct, then set the appropriate headers and
34//! payload using the functions defined for the different structs. These
35//! methods are described in greater detail below.
36
37// Discussion of Design Decisions
38// ------------------------------
39// Although still a work-in-progress, the IPv6 layer is quite complicated, and
40// this initial interface represents some of the compromises made in trying
41// to design a memory efficient, modular IP layer. The primary decision made
42// for the IPv6 layer was the design of the `IP6Packet` struct. We noticed
43// that the mutable payload buffer should always be associated with some type
44// of headers; that is, we noticed that the payload for an IP packet should
45// be perminantly owned by an instance of an IPv6 packet. This avoids runtime
46// checks, as Rust can guarantee that the payload for an IPv6 packet is always
47// there, as it cannot be moved. In order to facilitate this design while still
48// allowing for (somewhat) arbitrary transport-level headers, we needed to
49// separate out the `TransportHeader` enum from the payload itself. Since
50// we did not want the IP layer to always have to have knowledge of/deal with
51// the transport-level header, we decided to add an intermediate `IPPayload`
52// struct, which encapsulated the `TransportHeader` and associated payload.
53//
54// Known Problems and Remaining Work
55// ---------------------------------
56// This layer is still in the early stages of implementation, and both the
57// interfaces and underlying code will change substantially. There are two main
58// areas of focus for additional work: 1) ensuring that the IP6Packet/IP6Header/
59// IPPayload design makes sense and is properly layered, and 2) figuring out
60// and implementing a receive path that uses this encapsulation.
61//
62// One of the primary problems with the current encapsulation design is that
63// it is impossible to encode recursive headers - any subsequent headers (IPv6
64// or transport) must be serialized and carried in the raw payload. This may
65// be avoided with references and allocation, but since we do not have
66// a memory allocator we could not allocate all possible headers at compile
67// time. Additionally, we couldn't just allocate headers "as-needed" on the
68// stack, as the network send interface is asynchronous, so anything allocated
69// on the stack would eventually be popped/disappear. Although this is not
70// a major problem in general, it makes handling encapsulated IPv6 packets
71// (as required by 6LoWPAN) difficult.
72
73use crate::net::icmpv6::ICMP6Header;
74use crate::net::ipv6::ip_utils::{compute_icmp_checksum, compute_udp_checksum, ip6_nh, IPAddr};
75use crate::net::stream::SResult;
76use crate::net::stream::{decode_bytes, decode_u16, decode_u8};
77use crate::net::stream::{encode_bytes, encode_u16, encode_u8};
78use crate::net::tcp::TCPHeader;
79use crate::net::udp::UDPHeader;
80
81use kernel::utilities::leasable_buffer::SubSliceMut;
82use kernel::ErrorCode;
83
84pub const UDP_HDR_LEN: usize = 8;
85pub const ICMP_HDR_LEN: usize = 8;
86
87/// This is the struct definition for an IPv6 header. It contains (in order)
88/// the same fields as a normal IPv6 header.
89#[repr(C, packed)]
90#[derive(Copy, Clone)]
91pub struct IP6Header {
92 pub version_class_flow: [u8; 4],
93 pub payload_len: u16,
94 pub next_header: u8,
95 pub hop_limit: u8,
96 pub src_addr: IPAddr,
97 pub dst_addr: IPAddr,
98}
99
100impl Default for IP6Header {
101 fn default() -> IP6Header {
102 let version = 0x60;
103 let hop_limit = 255;
104 IP6Header {
105 version_class_flow: [version, 0, 0, 0],
106 payload_len: 0,
107 next_header: ip6_nh::NO_NEXT,
108 hop_limit,
109 src_addr: IPAddr::new(),
110 dst_addr: IPAddr::new(),
111 }
112 }
113}
114
115impl IP6Header {
116 /// This function returns an IP6Header struct initialized to the default
117 /// values.
118 pub fn new() -> IP6Header {
119 IP6Header::default()
120 }
121
122 /// This function is used to transform a raw buffer into an IP6Header
123 /// struct. This is useful for deserializing a header upon reception.
124 ///
125 /// # Arguments
126 ///
127 /// `buf` - The serialized version of an IPv6 header
128 ///
129 /// # Return Value
130 ///
131 /// `SResult<IP6Header>` - The resulting decoded IP6Header struct wrapped
132 /// in an SResult
133 pub fn decode(buf: &[u8]) -> SResult<IP6Header> {
134 // TODO: Let size of header be a constant
135 stream_len_cond!(buf, 40);
136
137 let mut ip6_header = Self::new();
138 // Note that `dec_consume!` uses the length of the output buffer to
139 // determine how many bytes are to be read.
140 let off = dec_consume!(buf, 0; decode_bytes, &mut ip6_header.version_class_flow);
141 let (off, payload_len_be) = dec_try!(buf, off; decode_u16);
142 ip6_header.payload_len = u16::from_be(payload_len_be);
143 let (off, next_header) = dec_try!(buf, off; decode_u8);
144 ip6_header.next_header = next_header;
145 let (off, hop_limit) = dec_try!(buf, off; decode_u8);
146 ip6_header.hop_limit = hop_limit;
147 let off = dec_consume!(buf, off; decode_bytes, &mut ip6_header.src_addr.0);
148 let off = dec_consume!(buf, off; decode_bytes, &mut ip6_header.dst_addr.0);
149 stream_done!(off, ip6_header);
150 }
151
152 /// This function transforms the `self` instance of an IP6Header into a
153 /// byte array
154 ///
155 /// # Arguments
156 ///
157 /// `buf` - A mutable array where the serialized version of the IP6Header
158 /// struct is written to
159 ///
160 /// # Return Value
161 ///
162 /// `SResult<usize>` - The offset wrapped in an SResult
163 pub fn encode(&self, buf: &mut [u8]) -> SResult<usize> {
164 stream_len_cond!(buf, 40);
165
166 let mut off = enc_consume!(buf, 0; encode_bytes, &self.version_class_flow);
167 off = enc_consume!(buf, off; encode_u16, self.payload_len.to_be());
168 off = enc_consume!(buf, off; encode_u8, self.next_header);
169 off = enc_consume!(buf, off; encode_u8, self.hop_limit);
170 off = enc_consume!(buf, off; encode_bytes, &self.src_addr.0);
171 off = enc_consume!(buf, off; encode_bytes, &self.dst_addr.0);
172 stream_done!(off, off);
173 }
174
175 pub fn get_src_addr(&self) -> IPAddr {
176 self.src_addr
177 }
178
179 pub fn get_dst_addr(&self) -> IPAddr {
180 self.dst_addr
181 }
182
183 // Version should always be 6
184 pub fn get_version(&self) -> u8 {
185 (self.version_class_flow[0] & 0xf0) >> 4
186 }
187
188 pub fn get_traffic_class(&self) -> u8 {
189 (self.version_class_flow[0] & 0x0f) << 4 | (self.version_class_flow[1] & 0xf0) >> 4
190 }
191
192 pub fn set_traffic_class(&mut self, new_tc: u8) {
193 self.version_class_flow[0] &= 0xf0;
194 self.version_class_flow[0] |= (new_tc & 0xf0) >> 4;
195 self.version_class_flow[1] &= 0x0f;
196 self.version_class_flow[1] |= (new_tc & 0x0f) << 4;
197 }
198
199 fn get_dscp_unshifted(&self) -> u8 {
200 self.get_traffic_class() & 0b11111100
201 }
202
203 pub fn get_dscp(&self) -> u8 {
204 self.get_dscp_unshifted() >> 2
205 }
206
207 pub fn set_dscp(&mut self, new_dscp: u8) {
208 let ecn = self.get_ecn();
209 self.set_traffic_class(ecn | ((new_dscp << 2) & 0b11111100));
210 }
211
212 pub fn get_ecn(&self) -> u8 {
213 self.get_traffic_class() & 0b11
214 }
215
216 pub fn set_ecn(&mut self, new_ecn: u8) {
217 let dscp_unshifted = self.get_dscp_unshifted();
218 self.set_traffic_class(dscp_unshifted | (new_ecn & 0b11));
219 }
220
221 // This returns the flow label as the lower 20 bits of a u32
222 pub fn get_flow_label(&self) -> u32 {
223 let mut flow_label: u32 = 0;
224 flow_label |= ((self.version_class_flow[1] & 0x0f) as u32) << 16;
225 flow_label |= (self.version_class_flow[2] as u32) << 8;
226 flow_label |= self.version_class_flow[3] as u32;
227 flow_label
228 }
229
230 pub fn set_flow_label(&mut self, new_fl_val: u32) {
231 self.version_class_flow[1] &= 0xf0;
232 self.version_class_flow[1] |= ((new_fl_val >> 16) & 0x0f) as u8;
233 self.version_class_flow[2] = (new_fl_val >> 8) as u8;
234 self.version_class_flow[3] = new_fl_val as u8;
235 }
236
237 pub fn get_payload_len(&self) -> u16 {
238 u16::from_be(self.payload_len)
239 }
240
241 // TODO: 40 = size of IP6header - find idiomatic way to compute
242 pub fn get_total_len(&self) -> u16 {
243 40 + self.get_payload_len()
244 }
245
246 pub fn set_payload_len(&mut self, new_len: u16) {
247 self.payload_len = new_len.to_be();
248 }
249
250 pub fn get_next_header(&self) -> u8 {
251 self.next_header
252 }
253
254 pub fn set_next_header(&mut self, new_nh: u8) {
255 self.next_header = new_nh;
256 }
257
258 pub fn get_hop_limit(&self) -> u8 {
259 self.hop_limit
260 }
261
262 pub fn set_hop_limit(&mut self, new_hl: u8) {
263 self.hop_limit = new_hl;
264 }
265
266 /// Utility function for verifying whether a transport layer checksum of a received
267 /// packet is correct. Is called on the assocaite IPv6 Header, and passed the buffer
268 /// containing the remainder of the packet.
269 pub fn check_transport_checksum(&self, buf: &[u8]) -> Result<(), ErrorCode> {
270 match self.next_header {
271 ip6_nh::UDP => {
272 let mut udp_header: [u8; UDP_HDR_LEN] = [0; UDP_HDR_LEN];
273 udp_header.copy_from_slice(&buf[..UDP_HDR_LEN]);
274 let checksum = match UDPHeader::decode(&udp_header).done() {
275 Some((_offset, hdr)) => u16::from_be(compute_udp_checksum(
276 self,
277 &hdr,
278 buf.len() as u16,
279 &buf[UDP_HDR_LEN..],
280 )),
281 None => 0xffff, //Will be dropped, as ones comp -0 checksum is invalid
282 };
283 if checksum != 0 {
284 return Err(ErrorCode::FAIL); //Incorrect cksum
285 }
286 Ok(())
287 }
288 ip6_nh::ICMP => {
289 // Untested (10/5/18)
290 let mut icmp_header: [u8; ICMP_HDR_LEN] = [0; ICMP_HDR_LEN];
291 icmp_header.copy_from_slice(&buf[..ICMP_HDR_LEN]);
292 let checksum = match ICMP6Header::decode(&icmp_header).done() {
293 Some((_offset, mut hdr)) => {
294 hdr.set_len(buf.len() as u16);
295 u16::from_be(compute_icmp_checksum(self, &hdr, &buf[ICMP_HDR_LEN..]))
296 }
297 None => 0xffff, //Will be dropped, as ones comp -0 checksum is invalid
298 };
299 if checksum != 0 {
300 return Err(ErrorCode::FAIL); //Incorrect cksum
301 }
302 Ok(())
303 }
304 _ => Err(ErrorCode::NOSUPPORT),
305 }
306 }
307}
308
309/// This defines the currently supported `TransportHeader` types.
310///
311/// The contents of each header is encapsulated by the enum type. Note
312/// that this definition of `TransportHeader`s means that recursive
313/// headers are not supported. As of now, there is no support for
314/// sending raw IP packets without a transport header. Currently we
315/// accept the overhead of copying these structs in/out of an
316/// OptionalCell in `udp_send.rs`.
317#[derive(Copy, Clone)]
318pub enum TransportHeader {
319 UDP(UDPHeader),
320 TCP(TCPHeader),
321 ICMP(ICMP6Header),
322}
323
324/// The `IPPayload` struct contains a `TransportHeader` and a mutable buffer
325/// (the payload).
326pub struct IPPayload<'a> {
327 pub header: TransportHeader,
328 pub payload: &'a mut [u8],
329}
330
331impl<'a> IPPayload<'a> {
332 /// This function constructs a new `IPPayload` struct
333 ///
334 /// # Arguments
335 ///
336 /// `header` - A `TransportHeader` for the `IPPayload`
337 /// `payload` - A reference to a mutable buffer for the raw payload
338 pub fn new(header: TransportHeader, payload: &'a mut [u8]) -> IPPayload<'a> {
339 IPPayload { header, payload }
340 }
341
342 /// This function sets the payload for the `IPPayload`, and sets both the
343 /// TransportHeader and copies the provided payload buffer.
344 ///
345 /// # Arguments
346 ///
347 /// `transport_header` - The new `TransportHeader` header for the payload
348 /// `payload` - The payload to be copied into the `IPPayload`
349 ///
350 /// # Return Value
351 ///
352 /// `(u8, u16)` - Returns a tuple of the `ip6_nh` type of the
353 /// `transport_header` and the total length of the `IPPayload`
354 /// (when serialized)
355 pub fn set_payload(
356 &mut self,
357 transport_header: TransportHeader,
358 payload: &SubSliceMut<'static, u8>,
359 ) -> (u8, u16) {
360 for i in 0..payload.len() {
361 self.payload[i] = payload[i];
362 }
363 match transport_header {
364 TransportHeader::UDP(mut udp_header) => {
365 let length = (payload.len() + udp_header.get_hdr_size()) as u16;
366 udp_header.set_len(length);
367 self.header = transport_header;
368 (ip6_nh::UDP, length)
369 }
370 TransportHeader::ICMP(mut icmp_header) => {
371 let length = (payload.len() + icmp_header.get_hdr_size()) as u16;
372 icmp_header.set_len(length);
373 self.header = transport_header;
374 (ip6_nh::ICMP, length)
375 }
376 _ => (ip6_nh::NO_NEXT, payload.len() as u16),
377 }
378 }
379
380 /// This function encodes the `IPPayload` as a byte array
381 ///
382 /// # Arguments
383 ///
384 /// `buf` - SubSliceMut to write the serialized `IPPayload` to
385 /// `offset` - Current offset into the buffer
386 ///
387 /// # Return Value
388 ///
389 /// `SResult<usize>` - The final offset into the buffer `buf` is returned
390 /// wrapped in an SResult
391 pub fn encode(&self, buf: &mut [u8], offset: usize) -> SResult<usize> {
392 let (offset, _) = match self.header {
393 TransportHeader::UDP(udp_header) => udp_header.encode(buf, offset).done().unwrap(),
394 TransportHeader::ICMP(icmp_header) => icmp_header.encode(buf, offset).done().unwrap(),
395 _ => {
396 unimplemented!();
397 }
398 };
399 let payload_length = self.get_payload_length();
400 let offset = enc_consume!(buf, offset; encode_bytes, &self.payload[..payload_length]);
401 stream_done!(offset, offset)
402 }
403
404 fn get_payload_length(&self) -> usize {
405 match self.header {
406 TransportHeader::UDP(udp_header) => {
407 udp_header.get_len() as usize - udp_header.get_hdr_size()
408 }
409 TransportHeader::ICMP(icmp_header) => {
410 icmp_header.get_len() as usize - icmp_header.get_hdr_size()
411 }
412 _ => {
413 unimplemented!();
414 }
415 }
416 }
417}
418
419/// This struct defines the `IP6Packet` format, and contains an `IP6Header`
420/// and an `IPPayload`.
421pub struct IP6Packet<'a> {
422 pub header: IP6Header,
423 pub payload: IPPayload<'a>,
424}
425
426// Note: We want to have the IP6Header struct implement these methods,
427// as there are cases where we want to allocate/modify the IP6Header without
428// allocating/modifying the entire IP6Packet
429impl<'a> IP6Packet<'a> {
430 // Sets fields to appropriate defaults
431
432 /// This function returns a new `IP6Packet` struct. Note that the
433 /// `IP6Packet.header` field is set to `IP6Header::default()`
434 ///
435 /// # Arguments
436 ///
437 /// `payload` - The `IPPayload` struct for the `IP6Packet`
438 pub fn new(payload: IPPayload<'a>) -> IP6Packet<'a> {
439 IP6Packet {
440 header: IP6Header::default(),
441 payload,
442 }
443 }
444
445 pub fn reset(&mut self) {
446 self.header = IP6Header::default();
447 }
448
449 pub fn get_total_len(&self) -> u16 {
450 40 + self.header.get_payload_len()
451 }
452
453 pub fn get_payload(&self) -> &[u8] {
454 self.payload.payload
455 }
456
457 pub fn get_total_hdr_size(&self) -> usize {
458 let transport_hdr_size = match self.payload.header {
459 TransportHeader::UDP(udp_hdr) => udp_hdr.get_hdr_size(),
460 TransportHeader::ICMP(icmp_header) => icmp_header.get_hdr_size(),
461 _ => unimplemented!(),
462 };
463 40 + transport_hdr_size
464 }
465
466 pub fn set_transport_checksum(&mut self) {
467 // Looks at internal buffer assuming
468 // it contains a valid IP packet, checks the payload type. If the payload
469 // type requires a cksum calculation, this function calculates the
470 // psuedoheader cksum and calls the appropriate transport packet function
471 // using this pseudoheader cksum to set the transport packet cksum
472
473 match self.payload.header {
474 TransportHeader::UDP(ref mut udp_header) => {
475 let cksum = compute_udp_checksum(
476 &self.header,
477 udp_header,
478 udp_header.get_len(),
479 self.payload.payload,
480 );
481 udp_header.set_cksum(cksum);
482 }
483 TransportHeader::ICMP(ref mut icmp_header) => {
484 let cksum = compute_icmp_checksum(&self.header, icmp_header, self.payload.payload);
485 icmp_header.set_cksum(cksum);
486 }
487 _ => {
488 unimplemented!();
489 }
490 }
491 }
492
493 /// This function should be the function used to set the payload for a
494 /// given `IP6Packet` object. It first calls the `IPPayload::set_payload`
495 /// method to set the transport header and transport payload, which then
496 /// returns the `ip6_nh` value for the `TransportHeader` and the length of
497 /// the serialized `IPPayload` region. This function then sets the
498 /// `IP6Header` next header field correctly. **Without using this function,
499 /// the `IP6Header.next_header` field may not agree with the actual
500 /// next header (`IP6Header.payload.header`)**
501 ///
502 /// # Arguments
503 ///
504 /// `transport_header` - The `TransportHeader` to be set as the next header
505 /// `payload` - The transport payload to be copied into the `IPPayload`
506 /// transport payload
507 pub fn set_payload(
508 &mut self,
509 transport_header: TransportHeader,
510 payload: &SubSliceMut<'static, u8>,
511 ) {
512 let (next_header, payload_len) = self.payload.set_payload(transport_header, payload);
513 self.header.set_next_header(next_header);
514 self.header.set_payload_len(payload_len);
515 }
516
517 // TODO: Do we need a decode equivalent? I don't think so, but we might
518
519 pub fn encode(&self, buf: &mut [u8]) -> SResult<usize> {
520 let ip6_header = self.header;
521
522 // TODO: Handle unwrap safely
523 let (off, _) = ip6_header.encode(buf).done().unwrap();
524 self.payload.encode(buf, off)
525 }
526}