capsules_extra/net/ipv6/
ip_utils.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//! Utilities used by the components of the IP stack.
6//!
7//! Note that this file also contains the definition for the
8//! [IPAddr](struct.IPAddr.html) struct and associated helper
9//! functions.
10
11use crate::net::icmpv6::{ICMP6Header, ICMP6HeaderOptions};
12use crate::net::ieee802154::MacAddress;
13use crate::net::ipv6::IP6Header;
14use crate::net::udp::UDPHeader;
15
16#[derive(Copy, Clone, PartialEq)]
17pub enum MacAddr {
18    ShortAddr(u16),
19    LongAddr([u8; 8]),
20}
21
22pub mod ip6_nh {
23    pub const HOP_OPTS: u8 = 0;
24    pub const TCP: u8 = 6;
25    pub const UDP: u8 = 17;
26    pub const IP6: u8 = 41;
27    pub const ROUTING: u8 = 43;
28    pub const FRAGMENT: u8 = 44;
29    pub const ICMP: u8 = 58;
30    pub const NO_NEXT: u8 = 59;
31    pub const DST_OPTS: u8 = 60;
32    pub const MOBILITY: u8 = 135;
33}
34
35#[derive(Copy, Clone, Debug)]
36pub struct IPAddr(pub [u8; 16]);
37
38impl PartialEq for IPAddr {
39    fn eq(&self, other: &IPAddr) -> bool {
40        self.0 == other.0
41    }
42}
43
44impl Eq for IPAddr {}
45
46impl IPAddr {
47    pub fn new() -> IPAddr {
48        // Defaults to the unspecified address
49        IPAddr([0; 16])
50    }
51
52    /// Method for generating a new ipv6 link local address from a short or extended 15.4 MAC address
53    /// Based off of section 3.2.2 of rfc 6282
54    pub fn generate_from_mac(mac_addr: MacAddress) -> IPAddr {
55        let mut ip_addr = IPAddr([0; 16]);
56        match mac_addr {
57            MacAddress::Long(ref long_addr) => {
58                ip_addr.set_unicast_link_local();
59                ip_addr.0[15] = long_addr[7];
60                ip_addr.0[14] = long_addr[6];
61                ip_addr.0[13] = long_addr[5];
62                ip_addr.0[12] = long_addr[4];
63                ip_addr.0[11] = long_addr[3];
64                ip_addr.0[10] = long_addr[2];
65                ip_addr.0[9] = long_addr[1];
66                ip_addr.0[8] = long_addr[0] ^ 0b00000010;
67            }
68            MacAddress::Short(ref short_addr) => {
69                ip_addr.set_unicast_link_local();
70                ip_addr.0[15] = (short_addr & 0x00ff) as u8;
71                ip_addr.0[14] = ((short_addr & 0xff00) >> 8) as u8;
72                ip_addr.0[13] = 0x00;
73                ip_addr.0[12] = 0xfe;
74                ip_addr.0[11] = 0xff;
75                ip_addr.0[10] = 0x00;
76                ip_addr.0[9] = 0x00;
77                ip_addr.0[8] = 0x00;
78            }
79        }
80        ip_addr
81    }
82
83    pub fn is_unspecified(&self) -> bool {
84        self.0.iter().all(|&b| b == 0)
85    }
86
87    pub fn is_unicast_link_local(&self) -> bool {
88        self.0[0] == 0xfe
89            && (self.0[1] & 0xc0) == 0x80
90            && (self.0[1] & 0x3f) == 0
91            && self.0[2..8].iter().all(|&b| b == 0)
92    }
93
94    pub fn set_unicast_link_local(&mut self) {
95        self.0[0] = 0xfe;
96        self.0[1] = 0x80;
97        for i in 2..8 {
98            self.0[i] = 0;
99        }
100    }
101
102    // Panics if prefix slice does not contain enough bits
103    pub fn set_prefix(&mut self, prefix: &[u8], prefix_len: u8) {
104        let full_bytes = (prefix_len / 8) as usize;
105        let remaining = (prefix_len & 0x7) as usize;
106        let bytes = full_bytes + usize::from(remaining != 0);
107        assert!(bytes <= prefix.len() && bytes <= 16);
108
109        self.0[0..full_bytes].copy_from_slice(&prefix[0..full_bytes]);
110        if remaining != 0 {
111            let mask = 0xff_u8 << (8 - remaining);
112            self.0[full_bytes] &= !mask;
113            self.0[full_bytes] |= mask & prefix[full_bytes];
114        }
115    }
116
117    pub fn is_multicast(&self) -> bool {
118        self.0[0] == 0xff
119    }
120}
121
122pub fn compute_udp_checksum(
123    ip6_header: &IP6Header,
124    udp_header: &UDPHeader,
125    udp_length: u16,
126    payload: &[u8],
127) -> u16 {
128    //This checksum is calculated according to some of the recommendations found in RFC 1071.
129
130    let src_port = udp_header.get_src_port();
131    let dst_port = udp_header.get_dst_port();
132    let mut sum: u32 = 0;
133    {
134        //First, iterate through src/dst address and add them to the sum
135        let mut i = 0;
136        while i <= 14 {
137            let msb_src: u16 = ((ip6_header.src_addr.0[i]) as u16) << 8;
138            let lsb_src: u16 = ip6_header.src_addr.0[i + 1] as u16;
139            let temp_src: u16 = msb_src + lsb_src;
140            sum += temp_src as u32;
141
142            let msb_dst: u16 = ((ip6_header.dst_addr.0[i]) as u16) << 8;
143            let lsb_dst: u16 = ip6_header.dst_addr.0[i + 1] as u16;
144            let temp_dst: u16 = msb_dst + lsb_dst;
145            sum += temp_dst as u32;
146
147            i += 2; //Iterate two bytes at a time bc 16 bit checksum
148        }
149    }
150    sum += udp_header.get_len() as u32;
151    //Finally, add UDP next header
152    sum += 17; //was "padded next header"
153
154    //return sum as u16;
155    //Next, add the UDP header elements to the sum
156    sum += src_port as u32;
157    sum += dst_port as u32;
158    sum += udp_header.get_len() as u32;
159    sum += udp_header.get_cksum() as u32;
160    //Now just need to iterate thru data and add it to the sum
161    {
162        let mut i: usize = 0;
163        while i < ((udp_length - 8) as usize) {
164            let msb_dat: u16 = ((payload[i]) as u16) << 8;
165            let mut lsb_dat: u16 = 0;
166            if i + 1 < udp_length as usize - 8 {
167                lsb_dat = payload[i + 1] as u16;
168            }
169            let temp_dat: u16 = msb_dat + lsb_dat;
170            sum += temp_dat as u32;
171
172            i += 2; //Iterate two bytes at a time bc 16 bit checksum
173        }
174    }
175    //now all 16 bit addition has occurred
176
177    while sum > 65535 {
178        let sum_high: u32 = sum >> 16; //upper 16 bits of sum
179        let sum_low: u32 = sum & 65535; //lower 16 bits of sum
180        sum = sum_high + sum_low;
181    }
182
183    //Finally, flip all bits
184    sum = !sum;
185    sum &= 65535; //Remove upper 16 bits (which should be FFFF after flip)
186    sum as u16 //Return result as u16 in host byte order */
187}
188
189pub fn compute_icmp_checksum(
190    ipv6_header: &IP6Header,
191    icmp_header: &ICMP6Header,
192    payload: &[u8],
193) -> u16 {
194    let mut sum: u32 = 0;
195
196    // add ipv6 pseudo-header
197    sum += compute_ipv6_ph_sum(ipv6_header);
198
199    // add type and code
200    let msb = (icmp_header.get_type_as_int() as u32) << 8;
201    let lsb = icmp_header.get_code() as u32;
202    sum += msb + lsb;
203
204    // add options
205    match icmp_header.get_options() {
206        ICMP6HeaderOptions::Type1 { unused } | ICMP6HeaderOptions::Type3 { unused } => {
207            sum += unused >> 16; // upper 16 bits
208            sum += unused & 0xffff; // lower 16 bits
209        }
210        ICMP6HeaderOptions::Type128 { id, seqno } | ICMP6HeaderOptions::Type129 { id, seqno } => {
211            sum += id as u32;
212            sum += seqno as u32;
213        }
214    }
215
216    // add icmp payload
217    let payload_len = icmp_header.get_len() - icmp_header.get_hdr_size() as u16;
218    sum += compute_sum(payload, payload_len);
219
220    // carry overflow
221    while sum > 0xffff {
222        let sum_upper = sum >> 16;
223        let sum_lower = sum & 0xffff;
224        sum += sum_upper + sum_lower;
225    }
226
227    sum = !sum;
228    sum &= 0xffff;
229
230    sum as u16
231}
232
233pub fn compute_ipv6_ph_sum(ip6_header: &IP6Header) -> u32 {
234    let mut sum: u32 = 0;
235
236    // sum over src/dest addresses
237    let mut i = 0;
238    while i < 16 {
239        let msb_src = (ip6_header.src_addr.0[i] as u32) << 8;
240        let lsb_src = ip6_header.src_addr.0[i + 1] as u32;
241        sum += msb_src + lsb_src;
242
243        let msb_dst = (ip6_header.dst_addr.0[i] as u32) << 8;
244        let lsb_dst = ip6_header.dst_addr.0[i + 1] as u32;
245        sum += msb_dst + lsb_dst;
246
247        i += 2;
248    }
249
250    sum += ip6_header.payload_len as u32;
251    sum += ip6_header.next_header as u32;
252
253    sum
254}
255
256pub fn compute_sum(buf: &[u8], len: u16) -> u32 {
257    let mut sum: u32 = 0;
258
259    let mut i: usize = 0;
260    while i < (len as usize) {
261        let msb = (buf[i] as u32) << 8;
262        let lsb = buf[i + 1] as u32;
263        sum += msb + lsb;
264        i += 2;
265    }
266
267    sum
268}