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 mask_from_prefix(prefix: u8) -> Ipv4Address {
21    let mask: u32 = if prefix == 0 {
22        0
23    } else if prefix >= 32 {
24        u32::MAX
25    } else {
26        u32::MAX << (32 - prefix)
27    };
28    let b = mask.to_be_bytes();
29    Ipv4Address::new(b[0], b[1], b[2], b[3])
30}
31
32pub(crate) fn broadcast_from_host_prefix(host: Ipv4Address, prefix: u8) -> Ipv4Address {
33    let h = u32::from_be_bytes(host.octets());
34    let m = u32::from_be_bytes(mask_from_prefix(prefix).octets());
35    let b = (h & m) | (!m);
36    let o = b.to_be_bytes();
37    Ipv4Address::new(o[0], o[1], o[2], o[3])
38}
39
40pub(crate) fn parse_ipv4_cidr(s: &str) -> Option<Ipv4Cidr> {
41    let slash = s.find('/')?;
42    let ip = parse_ipv4(&s[..slash])?;
43    let prefix = s[slash + 1..].parse::<u8>().ok()?;
44    if prefix > 32 {
45        return None;
46    }
47    Some(Ipv4Cidr::new(ip, prefix))
48}
49
50pub(crate) fn parse_ipv6(s: &str) -> Option<Ipv6Address> {
51    parse_ipv6_literal(s).map(Ipv6Address::from_octets)
52}
53
54pub(crate) fn parse_ipv6_cidr(s: &str) -> Option<Ipv6Cidr> {
55    let slash = s.find('/')?;
56    let addr = parse_ipv6(&s[..slash])?;
57    let prefix = s[slash + 1..].parse::<u8>().ok()?;
58    if prefix > 128 {
59        return None;
60    }
61    Some(Ipv6Cidr::new(addr, prefix))
62}
63
64pub(crate) fn link_local_from_mac(mac: [u8; 6]) -> Ipv6Address {
65    let eui64: [u8; 8] = [
66        mac[0] ^ 0x02,
67        mac[1],
68        mac[2],
69        0xFF,
70        0xFE,
71        mac[3],
72        mac[4],
73        mac[5],
74    ];
75    let addr_bytes: [u8; 16] = [
76        0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, eui64[0], eui64[1], eui64[2], eui64[3],
77        eui64[4], eui64[5], eui64[6], eui64[7],
78    ];
79    Ipv6Address::from_octets(addr_bytes)
80}
81
82pub(crate) fn icmpv6_checksum(src: &[u8], dst: &[u8], data: &[u8]) -> u16 {
83    let mut sum: u32 = 0;
84    for chunk in src.chunks_exact(2) {
85        sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
86    }
87    for chunk in dst.chunks_exact(2) {
88        sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
89    }
90    let len = data.len() as u32;
91    sum += len >> 16;
92    sum += len & 0xFFFF;
93    sum += 58u32;
94    let mut i = 0;
95    while i + 1 < data.len() {
96        sum += u16::from_be_bytes([data[i], data[i + 1]]) as u32;
97        i += 2;
98    }
99    if i < data.len() {
100        sum += (data[i] as u32) << 8;
101    }
102    while sum >> 16 != 0 {
103        sum = (sum & 0xFFFF) + (sum >> 16);
104    }
105    !(sum as u16)
106}
107
108pub(crate) fn icmp_checksum(data: &[u8]) -> u16 {
109    let mut sum: u32 = 0;
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}