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}