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}