1use alloc::{collections::BTreeMap, string::String};
2
3use smoltcp::{
4 iface::{Config, Interface, SocketHandle, SocketSet},
5 socket::{dhcpv4, dns, icmp, tcp, udp},
6 time::Instant,
7 wire::{EthernetAddress, IpAddress, IpCidr, IpEndpoint, Ipv4Address, Ipv6Address},
8};
9use strate_net::IpcMessage;
10
11use crate::{
12 device::Strat9NetDevice,
13 ip::{link_local_from_mac, IpConfig, Ipv6Config},
14 ipc::reply_read,
15};
16
17pub(crate) const PING_TIMEOUT_NS: u64 = 5_000_000_000;
18
19pub(crate) struct PendingPing {
20 pub(crate) seq: u16,
21 pub(crate) token: u64,
22 pub(crate) send_ts_ns: u64,
23 pub(crate) is_v6: bool,
24}
25
26#[derive(Clone, Copy)]
27pub(crate) struct TcpListenerState {
28 pub(crate) socket: SocketHandle,
29 pub(crate) port: u16,
30 pub(crate) auto_relisten: bool,
31}
32
33#[derive(Copy, Clone)]
34pub(crate) struct TcpConnState {
35 pub(crate) socket: SocketHandle,
36 pub(crate) local_port: u16,
37 pub(crate) remote: IpEndpoint,
38}
39
40#[derive(Copy, Clone)]
41pub(crate) struct UdpBoundState {
42 pub(crate) socket: SocketHandle,
43 pub(crate) local_port: u16,
44 pub(crate) last_peer: Option<IpEndpoint>,
45}
46
47#[derive(Copy, Clone)]
48pub(crate) struct UdpConnState {
49 pub(crate) socket: SocketHandle,
50 pub(crate) local_port: u16,
51 pub(crate) remote: IpEndpoint,
52}
53
54pub(crate) struct OpenedFile {
55 pub(crate) path: String,
56 pub(crate) cached_content: Option<alloc::vec::Vec<u8>>,
57 pub(crate) read_spill: Option<alloc::vec::Vec<u8>>,
58}
59
60impl OpenedFile {
61 pub(crate) fn new(path: &str) -> Self {
62 Self {
63 path: String::from(path),
64 cached_content: None,
65 read_spill: None,
66 }
67 }
68}
69
70pub(crate) struct NetworkStrate {
71 pub(crate) device: Strat9NetDevice,
72 pub(crate) interface: Interface,
73 pub(crate) sockets: SocketSet<'static>,
74 pub(crate) dhcp_handle: SocketHandle,
75 pub(crate) dns_handle: SocketHandle,
76 pub(crate) icmp_handle: SocketHandle,
77 pub(crate) ip_config: Option<IpConfig>,
78 pub(crate) ipv6_config: Option<Ipv6Config>,
79 pub(crate) dns_servers: [Option<IpAddress>; 3],
80 pub(crate) dns_from_dhcp: bool,
81 pub(crate) link_local_addr: Ipv6Address,
82 pub(crate) open_handles: BTreeMap<u64, OpenedFile>,
83 pub(crate) handle_owners: BTreeMap<u64, u64>,
87 pub(crate) tcp_listeners: BTreeMap<u64, TcpListenerState>,
88 pub(crate) tcp_connections: BTreeMap<u64, TcpConnState>,
89 pub(crate) udp_bound: BTreeMap<u64, UdpBoundState>,
90 pub(crate) udp_connections: BTreeMap<u64, UdpConnState>,
91 pub(crate) lingering_sockets: alloc::vec::Vec<SocketHandle>,
92 pub(crate) next_fid: u64,
93 pub(crate) ping_ident: u16,
94 pub(crate) next_ping_token: u64,
95 pub(crate) pending_pings: alloc::vec::Vec<PendingPing>,
96 pub(crate) ping_replies: alloc::vec::Vec<(u16, u64)>,
97 pub(crate) dhcp_enabled: bool,
98}
99
100impl NetworkStrate {
101 pub(crate) fn new(mac: [u8; 6]) -> Self {
102 let mut device = Strat9NetDevice;
103 let config = Config::new(EthernetAddress(mac).into());
104 let mut interface = Interface::new(config, &mut device, Instant::from_micros(0));
105 let mut sockets = SocketSet::new(alloc::vec![]);
106
107 let link_local = link_local_from_mac(mac);
108 interface.update_ip_addrs(|addrs| {
109 let _ = addrs.push(IpCidr::new(IpAddress::Ipv6(link_local), 64));
110 });
111
112 let dhcp_socket = dhcpv4::Socket::new();
113 let dhcp_handle = sockets.add(dhcp_socket);
114
115 let dns_socket = dns::Socket::new(&[], alloc::vec![]);
116 let dns_handle = sockets.add(dns_socket);
117
118 let icmp_rx_buf = icmp::PacketBuffer::new(
119 alloc::vec![icmp::PacketMetadata::EMPTY; 16],
120 alloc::vec![0u8; 4096],
121 );
122 let icmp_tx_buf = icmp::PacketBuffer::new(
123 alloc::vec![icmp::PacketMetadata::EMPTY; 16],
124 alloc::vec![0u8; 4096],
125 );
126 let mut icmp_socket = icmp::Socket::new(icmp_rx_buf, icmp_tx_buf);
127 icmp_socket.bind(icmp::Endpoint::Unspecified).ok();
128 let icmp_handle = sockets.add(icmp_socket);
129
130 Self {
131 device,
132 interface,
133 sockets,
134 dhcp_handle,
135 dns_handle,
136 icmp_handle,
137 ip_config: None,
138 ipv6_config: None,
139 dns_servers: [None; 3],
140 dns_from_dhcp: false,
141 link_local_addr: link_local,
142 open_handles: BTreeMap::new(),
143 handle_owners: BTreeMap::new(),
144 tcp_listeners: BTreeMap::new(),
145 tcp_connections: BTreeMap::new(),
146 udp_bound: BTreeMap::new(),
147 udp_connections: BTreeMap::new(),
148 lingering_sockets: alloc::vec::Vec::new(),
149 next_fid: 1,
150 ping_ident: 0x9001,
151 next_ping_token: 1,
152 pending_pings: alloc::vec::Vec::new(),
153 ping_replies: alloc::vec::Vec::new(),
154 dhcp_enabled: true,
155 }
156 }
157
158 pub(crate) fn clear_ipv4_runtime_config(&mut self) {
159 self.ip_config = None;
160 self.interface.update_ip_addrs(|addrs| {
161 let mut kept = [IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0); 8];
162 let mut n = 0usize;
163 for addr in addrs.iter() {
164 if matches!(addr, IpCidr::Ipv6(_)) && n < kept.len() {
165 kept[n] = *addr;
166 n += 1;
167 }
168 }
169 addrs.clear();
170 for addr in kept.into_iter().take(n) {
171 let _ = addrs.push(addr);
172 }
173 });
174 let _ = self.interface.routes_mut().remove_default_ipv4_route();
175 self.refresh_dns_servers();
176 }
177
178 pub(crate) fn drain_spilled_read(
179 &mut self,
180 sender: u64,
181 file_id: u64,
182 requested: usize,
183 ) -> Option<IpcMessage> {
184 let max_inline = requested.min(IpcMessage::READ_INLINE_CAPACITY);
185 let mut spill = self.open_handles.get_mut(&file_id)?.read_spill.take()?;
186 if spill.len() > max_inline {
187 let rest = spill.split_off(max_inline);
188 if let Some(handle) = self.open_handles.get_mut(&file_id) {
189 handle.read_spill = Some(rest);
190 }
191 }
192 Some(reply_read(sender, &spill))
193 }
194
195 pub(crate) fn reply_read_spilling(
196 &mut self,
197 sender: u64,
198 file_id: u64,
199 requested: usize,
200 mut data: alloc::vec::Vec<u8>,
201 ) -> IpcMessage {
202 let max_inline = requested.min(IpcMessage::READ_INLINE_CAPACITY);
203 if data.len() > max_inline {
204 let rest = data.split_off(max_inline);
205 if let Some(handle) = self.open_handles.get_mut(&file_id) {
206 handle.read_spill = Some(rest);
207 }
208 }
209 reply_read(sender, &data)
210 }
211
212 pub(crate) fn reset_dhcp_socket(&mut self) {
213 self.sockets
214 .get_mut::<dhcpv4::Socket>(self.dhcp_handle)
215 .reset();
216 }
217
218 pub(crate) fn enable_dhcp(&mut self) {
219 self.dhcp_enabled = true;
220 self.dns_servers = [None; 3];
221 self.dns_from_dhcp = false;
222 self.clear_ipv4_runtime_config();
223 self.reset_dhcp_socket();
224 }
225
226 pub(crate) fn alloc_ping_token(&mut self) -> u64 {
227 let token = self.next_ping_token;
228 self.next_ping_token = self.next_ping_token.wrapping_add(1);
229 token
230 }
231
232 pub(crate) fn udp_port_in_use(&self, port: u16) -> bool {
233 self.udp_bound
234 .values()
235 .any(|state| state.local_port == port)
236 || self
237 .udp_connections
238 .values()
239 .any(|state| state.local_port == port)
240 }
241
242 pub(crate) fn alloc_udp_ephemeral_port(&self) -> Option<u16> {
243 const BASE: u16 = 49_152;
244 const COUNT: usize = 16_384;
245 let start = (self.next_fid as usize) % COUNT;
246 for step in 0..COUNT {
247 let port = BASE + ((start + step) % COUNT) as u16;
248 if !self.udp_port_in_use(port) {
249 return Some(port);
250 }
251 }
252 None
253 }
254
255 pub(crate) fn create_udp_socket(
256 &mut self,
257 local_port: u16,
258 ) -> core::result::Result<SocketHandle, i32> {
259 let rx_buf = udp::PacketBuffer::new(
260 alloc::vec![udp::PacketMetadata::EMPTY; 32],
261 alloc::vec![0u8; 65536],
262 );
263 let tx_buf = udp::PacketBuffer::new(
264 alloc::vec![udp::PacketMetadata::EMPTY; 32],
265 alloc::vec![0u8; 65536],
266 );
267 let mut socket = udp::Socket::new(rx_buf, tx_buf);
268 if socket.bind(local_port).is_err() {
269 return Err(-98);
270 }
271 Ok(self.sockets.add(socket))
272 }
273
274 pub(crate) fn tcp_state_name(state: tcp::State) -> &'static str {
275 match state {
276 tcp::State::Closed => "CLOSED",
277 tcp::State::Listen => "LISTEN",
278 tcp::State::SynSent => "SYN-SENT",
279 tcp::State::SynReceived => "SYN-RECEIVED",
280 tcp::State::Established => "ESTABLISHED",
281 tcp::State::FinWait1 => "FIN-WAIT-1",
282 tcp::State::FinWait2 => "FIN-WAIT-2",
283 tcp::State::CloseWait => "CLOSE-WAIT",
284 tcp::State::Closing => "CLOSING",
285 tcp::State::LastAck => "LAST-ACK",
286 tcp::State::TimeWait => "TIME-WAIT",
287 }
288 }
289
290 pub(crate) fn alloc_fid(&mut self) -> u64 {
291 let id = self.next_fid;
292 self.next_fid += 1;
293 id
294 }
295
296 pub(crate) fn new_fid(&mut self, owner: u64) -> u64 {
304 let id = self.alloc_fid();
305 self.handle_owners.insert(id, owner);
306 id
307 }
308
309 pub(crate) fn is_owner(&self, fid: u64, sender: u64) -> bool {
312 self.handle_owners.get(&fid) == Some(&sender)
313 }
314
315 pub(crate) fn forget_handle(&mut self, fid: u64) {
317 self.handle_owners.remove(&fid);
318 }
319}