capsules_extra/net/icmpv6/
icmpv6.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 the types, structs and methods associated with the
6//! ICMPv6 header, including getter and setter methods and encode/decode
7//! functionality necessary for transmission.
8//!
9//! - Author: Conor McAvity <cmcavity@stanford.edu>
10
11use crate::net::stream::SResult;
12use crate::net::stream::{decode_u16, decode_u32, decode_u8};
13use crate::net::stream::{encode_u16, encode_u32, encode_u8};
14
15/// A struct representing an ICMPv6 header.
16#[derive(Copy, Clone)]
17pub struct ICMP6Header {
18    pub code: u8,
19    pub cksum: u16,
20    pub options: ICMP6HeaderOptions,
21    pub len: u16, // Not a real ICMP field, here for convenience
22}
23
24#[derive(Copy, Clone)]
25pub enum ICMP6HeaderOptions {
26    Type1 { unused: u32 },
27    Type3 { unused: u32 },
28    Type128 { id: u16, seqno: u16 },
29    Type129 { id: u16, seqno: u16 },
30}
31
32#[derive(Copy, Clone)]
33pub enum ICMP6Type {
34    Type1,   // Destination Unreachable
35    Type3,   // Time Exceeded
36    Type128, // Echo Request
37    Type129, // Echo Reply
38}
39
40impl ICMP6Header {
41    pub fn new(icmp_type: ICMP6Type) -> ICMP6Header {
42        let options = match icmp_type {
43            ICMP6Type::Type1 => ICMP6HeaderOptions::Type1 { unused: 0 },
44            ICMP6Type::Type3 => ICMP6HeaderOptions::Type3 { unused: 0 },
45            ICMP6Type::Type128 => ICMP6HeaderOptions::Type128 { id: 0, seqno: 0 },
46            ICMP6Type::Type129 => ICMP6HeaderOptions::Type129 { id: 0, seqno: 0 },
47        };
48
49        ICMP6Header {
50            code: 0,
51            cksum: 0,
52            options,
53            len: 0,
54        }
55    }
56
57    pub fn set_type(&mut self, icmp_type: ICMP6Type) {
58        match icmp_type {
59            ICMP6Type::Type1 => self.set_options(ICMP6HeaderOptions::Type1 { unused: 0 }),
60            ICMP6Type::Type3 => self.set_options(ICMP6HeaderOptions::Type3 { unused: 0 }),
61            ICMP6Type::Type128 => self.set_options(ICMP6HeaderOptions::Type128 { id: 0, seqno: 0 }),
62            ICMP6Type::Type129 => self.set_options(ICMP6HeaderOptions::Type129 { id: 0, seqno: 0 }),
63        }
64    }
65
66    pub fn set_code(&mut self, code: u8) {
67        self.code = code;
68    }
69
70    pub fn set_cksum(&mut self, cksum: u16) {
71        self.cksum = cksum;
72    }
73
74    pub fn set_options(&mut self, options: ICMP6HeaderOptions) {
75        self.options = options;
76    }
77
78    pub fn set_len(&mut self, len: u16) {
79        self.len = len;
80    }
81
82    pub fn get_type(&self) -> ICMP6Type {
83        match self.options {
84            ICMP6HeaderOptions::Type1 { .. } => ICMP6Type::Type1,
85            ICMP6HeaderOptions::Type3 { .. } => ICMP6Type::Type3,
86            ICMP6HeaderOptions::Type128 { .. } => ICMP6Type::Type128,
87            ICMP6HeaderOptions::Type129 { .. } => ICMP6Type::Type129,
88        }
89    }
90
91    pub fn get_type_as_int(&self) -> u8 {
92        match self.get_type() {
93            ICMP6Type::Type1 => 1,
94            ICMP6Type::Type3 => 3,
95            ICMP6Type::Type128 => 128,
96            ICMP6Type::Type129 => 129,
97        }
98    }
99
100    pub fn get_code(&self) -> u8 {
101        self.code
102    }
103
104    pub fn get_cksum(&self) -> u16 {
105        self.cksum
106    }
107
108    pub fn get_options(&self) -> ICMP6HeaderOptions {
109        self.options
110    }
111
112    pub fn get_len(&self) -> u16 {
113        self.len
114    }
115
116    pub fn get_hdr_size(&self) -> usize {
117        8
118    }
119
120    /// Serializes an `ICMP6Header` into a buffer.
121    ///
122    /// # Arguments
123    ///
124    /// `buf` - A buffer to serialize the `ICMP6Header` into
125    /// `offset` - The current offset into the provided buffer
126    ///
127    /// # Return Value
128    ///
129    /// This function returns the new offset into the buffer,
130    /// wrapped in an SResult
131    pub fn encode(&self, buf: &mut [u8], offset: usize) -> SResult<usize> {
132        let mut off = offset;
133
134        off = enc_consume!(buf, off; encode_u8, self.get_type_as_int());
135        off = enc_consume!(buf, off; encode_u8, self.code);
136        off = enc_consume!(buf, off; encode_u16, self.cksum);
137
138        match self.options {
139            ICMP6HeaderOptions::Type1 { unused } | ICMP6HeaderOptions::Type3 { unused } => {
140                off = enc_consume!(buf, off; encode_u32, unused);
141            }
142            ICMP6HeaderOptions::Type128 { id, seqno }
143            | ICMP6HeaderOptions::Type129 { id, seqno } => {
144                off = enc_consume!(buf, off; encode_u16, id);
145                off = enc_consume!(buf, off; encode_u16, seqno);
146            }
147        }
148
149        stream_done!(off, off);
150    }
151
152    /// Deserializes an `ICMP6Header` from a buffer.
153    ///
154    /// # Arguments
155    ///
156    /// `buf` - The byte array corresponding to the serialized `ICMP6Header`
157    ///
158    /// # Return Value
159    ///
160    /// This function returns the `ICMP6Header`, wrapped in an SResult
161    pub fn decode(buf: &[u8]) -> SResult<ICMP6Header> {
162        let off = 0;
163        let (off, type_num) = dec_try!(buf, off; decode_u8);
164
165        let icmp_type = match type_num {
166            1 => ICMP6Type::Type1,
167            3 => ICMP6Type::Type3,
168            128 => ICMP6Type::Type128,
169            129 => ICMP6Type::Type129,
170            _ => return SResult::Error(()),
171        };
172
173        let mut icmp_header = Self::new(icmp_type);
174
175        let (off, code) = dec_try!(buf, off; decode_u8);
176        icmp_header.set_code(code);
177        let (off, cksum) = dec_try!(buf, off; decode_u16);
178        icmp_header.set_cksum(u16::from_be(cksum));
179
180        match icmp_type {
181            ICMP6Type::Type1 => {
182                let (_off, unused) = dec_try!(buf, off; decode_u32);
183                let unused = u32::from_be(unused);
184                icmp_header.set_options(ICMP6HeaderOptions::Type1 { unused });
185            }
186            ICMP6Type::Type3 => {
187                let (_off, unused) = dec_try!(buf, off; decode_u32);
188                let unused = u32::from_be(unused);
189                icmp_header.set_options(ICMP6HeaderOptions::Type3 { unused });
190            }
191            ICMP6Type::Type128 => {
192                let (_off, id) = dec_try!(buf, off; decode_u16);
193                let id = u16::from_be(id);
194                let (_off, seqno) = dec_try!(buf, off; decode_u16);
195                let seqno = u16::from_be(seqno);
196                icmp_header.set_options(ICMP6HeaderOptions::Type128 { id, seqno });
197            }
198            ICMP6Type::Type129 => {
199                let (_off, id) = dec_try!(buf, off; decode_u16);
200                let id = u16::from_be(id);
201                let (_off, seqno) = dec_try!(buf, off; decode_u16);
202                let seqno = u16::from_be(seqno);
203                icmp_header.set_options(ICMP6HeaderOptions::Type129 { id, seqno });
204            }
205        }
206
207        stream_done!(off, icmp_header);
208    }
209}