Skip to main content

strate_net_silo/
ip.rs

1use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr, Ipv6Address, Ipv6Cidr};
2use strat9_abi::ip::{parse_ipv4_literal, parse_ipv6_literal};
3
4pub(crate) struct IpConfig {
5    pub(crate) address: Ipv4Cidr,
6    pub(crate) host: Ipv4Address,
7    pub(crate) prefix_len: u8,
8    pub(crate) netmask: Ipv4Address,
9    pub(crate) broadcast: Ipv4Address,
10    pub(crate) gateway: Option<Ipv4Address>,
11    pub(crate) dns: [Option<Ipv4Address>; 3],
12}
13
14#[derive(Debug, Clone)]
15pub(crate) struct Ipv6Config {
16    pub(crate) address: Ipv6Cidr,
17    pub(crate) gateway: Option<Ipv6Address>,
18}
19
20pub(crate) fn icmp_checksum(data: &[u8]) -> u16 {
21    let mut sum: u32 = 0;
22    let mut i = 0;
23    while i + 1 < data.len() {
24        sum += u16::from_be_bytes([data[i], data[i + 1]]) as u32;
25        i += 2;
26    }
27    if i < data.len() {
28        sum += (data[i] as u32) << 8;
29    }
30    while sum >> 16 != 0 {
31        sum = (sum & 0xFFFF) + (sum >> 16);
32    }
33    !(sum as u16)
34}
35
36pub(crate) fn mask_from_prefix(prefix: u8) -> Ipv4Address {
37    let mask: u32 = if prefix == 0 {
38        0
39    } else if prefix >= 32 {
40        u32::MAX
41    } else {
42        u32::MAX << (32 - prefix)
43    };
44    let b = mask.to_be_bytes();
45    Ipv4Address::new(b[0], b[1], b[2], b[3])
46}
47
48pub(crate) fn broadcast_from_host_prefix(host: Ipv4Address, prefix: u8) -> Ipv4Address {
49    let h = u32::from_be_bytes(host.octets());
50    let m = u32::from_be_bytes(mask_from_prefix(prefix).octets());
51    let b = (h & m) | (!m);
52    let o = b.to_be_bytes();
53    Ipv4Address::new(o[0], o[1], o[2], o[3])
54}
55
56pub(crate) fn parse_ipv4_cidr(s: &str) -> Option<Ipv4Cidr> {
57    let slash = s.find('/')?;
58    let ip = parse_ipv4(&s[..slash])?;
59    let prefix = s[slash + 1..].parse::<u8>().ok()?;
60    if prefix > 32 {
61        return None;
62    }
63    Some(Ipv4Cidr::new(ip, prefix))
64}
65
66pub(crate) fn parse_ipv6(s: &str) -> Option<Ipv6Address> {
67    parse_ipv6_literal(s).map(Ipv6Address::from_octets)
68}
69
70pub(crate) fn parse_ipv6_cidr(s: &str) -> Option<Ipv6Cidr> {
71    let slash = s.find('/')?;
72    let addr = parse_ipv6(&s[..slash])?;
73    let prefix = s[slash + 1..].parse::<u8>().ok()?;
74    if prefix > 128 {
75        return None;
76    }
77    Some(Ipv6Cidr::new(addr, prefix))
78}
79
80pub(crate) fn link_local_from_mac(mac: [u8; 6]) -> Ipv6Address {
81    let eui64: [u8; 8] = [
82        mac[0] ^ 0x02,
83        mac[1],
84        mac[2],
85        0xFF,
86        0xFE,
87        mac[3],
88        mac[4],
89        mac[5],
90    ];
91    let addr_bytes: [u8; 16] = [
92        0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, eui64[0], eui64[1], eui64[2], eui64[3],
93        eui64[4], eui64[5], eui64[6], eui64[7],
94    ];
95    Ipv6Address::from_octets(addr_bytes)
96}
97
98pub(crate) fn icmpv6_checksum(src: &[u8], dst: &[u8], data: &[u8]) -> u16 {
99    let mut sum: u32 = 0;
100    for chunk in src.chunks_exact(2) {
101        sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
102    }
103    for chunk in dst.chunks_exact(2) {
104        sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
105    }
106    let len = data.len() as u32;
107    sum += len >> 16;
108    sum += len & 0xFFFF;
109    sum += 58u32;
110    let mut i = 0;
111    while i + 1 < data.len() {
112        sum += u16::from_be_bytes([data[i], data[i + 1]]) as u32;
113        i += 2;
114    }
115    if i < data.len() {
116        sum += (data[i] as u32) << 8;
117    }
118    while sum >> 16 != 0 {
119        sum = (sum & 0xFFFF) + (sum >> 16);
120    }
121    !(sum as u16)
122}
123
124pub(crate) fn parse_ipv4(s: &str) -> Option<Ipv4Address> {
125    let octets = parse_ipv4_literal(s)?;
126    Some(Ipv4Address::new(octets[0], octets[1], octets[2], octets[3]))
127}
128
129pub(crate) fn parse_ip(s: &str) -> Option<IpAddress> {
130    if let Some(ip) = parse_ipv4(s) {
131        Some(IpAddress::Ipv4(ip))
132    } else {
133        parse_ipv6(s).map(IpAddress::Ipv6)
134    }
135}
136
137pub(crate) fn parse_ip_cidr(s: &str) -> Option<IpCidr> {
138    if let Some(cidr) = parse_ipv4_cidr(s) {
139        Some(IpCidr::Ipv4(cidr))
140    } else {
141        parse_ipv6_cidr(s).map(IpCidr::Ipv6)
142    }
143}