Skip to main content

strate_net_silo/
state.rs

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) tcp_listeners: BTreeMap<u64, TcpListenerState>,
84    pub(crate) tcp_connections: BTreeMap<u64, TcpConnState>,
85    pub(crate) udp_bound: BTreeMap<u64, UdpBoundState>,
86    pub(crate) udp_connections: BTreeMap<u64, UdpConnState>,
87    pub(crate) lingering_sockets: alloc::vec::Vec<SocketHandle>,
88    pub(crate) next_fid: u64,
89    pub(crate) ping_ident: u16,
90    pub(crate) next_ping_token: u64,
91    pub(crate) pending_pings: alloc::vec::Vec<PendingPing>,
92    pub(crate) ping_replies: alloc::vec::Vec<(u16, u64)>,
93    pub(crate) dhcp_enabled: bool,
94}
95
96impl NetworkStrate {
97    pub(crate) fn new(mac: [u8; 6]) -> Self {
98        let mut device = Strat9NetDevice;
99        let config = Config::new(EthernetAddress(mac).into());
100        let mut interface = Interface::new(config, &mut device, Instant::from_micros(0));
101        let mut sockets = SocketSet::new(alloc::vec![]);
102
103        let link_local = link_local_from_mac(mac);
104        interface.update_ip_addrs(|addrs| {
105            let _ = addrs.push(IpCidr::new(IpAddress::Ipv6(link_local), 64));
106        });
107
108        let dhcp_socket = dhcpv4::Socket::new();
109        let dhcp_handle = sockets.add(dhcp_socket);
110
111        let dns_socket = dns::Socket::new(&[], alloc::vec![]);
112        let dns_handle = sockets.add(dns_socket);
113
114        let icmp_rx_buf = icmp::PacketBuffer::new(
115            alloc::vec![icmp::PacketMetadata::EMPTY; 16],
116            alloc::vec![0u8; 4096],
117        );
118        let icmp_tx_buf = icmp::PacketBuffer::new(
119            alloc::vec![icmp::PacketMetadata::EMPTY; 16],
120            alloc::vec![0u8; 4096],
121        );
122        let icmp_socket = icmp::Socket::new(icmp_rx_buf, icmp_tx_buf);
123        let icmp_handle = sockets.add(icmp_socket);
124
125        Self {
126            device,
127            interface,
128            sockets,
129            dhcp_handle,
130            dns_handle,
131            icmp_handle,
132            ip_config: None,
133            ipv6_config: None,
134            dns_servers: [None; 3],
135            dns_from_dhcp: false,
136            link_local_addr: link_local,
137            open_handles: BTreeMap::new(),
138            tcp_listeners: BTreeMap::new(),
139            tcp_connections: BTreeMap::new(),
140            udp_bound: BTreeMap::new(),
141            udp_connections: BTreeMap::new(),
142            lingering_sockets: alloc::vec::Vec::new(),
143            next_fid: 1,
144            ping_ident: 0x9001,
145            next_ping_token: 1,
146            pending_pings: alloc::vec::Vec::new(),
147            ping_replies: alloc::vec::Vec::new(),
148            dhcp_enabled: true,
149        }
150    }
151
152    pub(crate) fn clear_ipv4_runtime_config(&mut self) {
153        self.ip_config = None;
154        self.interface.update_ip_addrs(|addrs| {
155            let mut kept = [IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0); 8];
156            let mut n = 0usize;
157            for addr in addrs.iter() {
158                if matches!(addr, IpCidr::Ipv6(_)) && n < kept.len() {
159                    kept[n] = *addr;
160                    n += 1;
161                }
162            }
163            addrs.clear();
164            for addr in kept.into_iter().take(n) {
165                let _ = addrs.push(addr);
166            }
167        });
168        let _ = self.interface.routes_mut().remove_default_ipv4_route();
169        self.refresh_dns_servers();
170    }
171
172    pub(crate) fn drain_spilled_read(
173        &mut self,
174        sender: u64,
175        file_id: u64,
176        requested: usize,
177    ) -> Option<IpcMessage> {
178        let max_inline = requested.min(IpcMessage::READ_INLINE_CAPACITY);
179        let mut spill = self.open_handles.get_mut(&file_id)?.read_spill.take()?;
180        if spill.len() > max_inline {
181            let rest = spill.split_off(max_inline);
182            if let Some(handle) = self.open_handles.get_mut(&file_id) {
183                handle.read_spill = Some(rest);
184            }
185        }
186        Some(reply_read(sender, &spill))
187    }
188
189    pub(crate) fn reply_read_spilling(
190        &mut self,
191        sender: u64,
192        file_id: u64,
193        requested: usize,
194        mut data: alloc::vec::Vec<u8>,
195    ) -> IpcMessage {
196        let max_inline = requested.min(IpcMessage::READ_INLINE_CAPACITY);
197        if data.len() > max_inline {
198            let rest = data.split_off(max_inline);
199            if let Some(handle) = self.open_handles.get_mut(&file_id) {
200                handle.read_spill = Some(rest);
201            }
202        }
203        reply_read(sender, &data)
204    }
205
206    pub(crate) fn reset_dhcp_socket(&mut self) {
207        self.sockets
208            .get_mut::<dhcpv4::Socket>(self.dhcp_handle)
209            .reset();
210    }
211
212    pub(crate) fn enable_dhcp(&mut self) {
213        self.dhcp_enabled = true;
214        self.dns_servers = [None; 3];
215        self.dns_from_dhcp = false;
216        self.clear_ipv4_runtime_config();
217        self.reset_dhcp_socket();
218    }
219
220    pub(crate) fn alloc_ping_token(&mut self) -> u64 {
221        let token = self.next_ping_token;
222        self.next_ping_token = self.next_ping_token.wrapping_add(1);
223        token
224    }
225
226    pub(crate) fn udp_port_in_use(&self, port: u16) -> bool {
227        self.udp_bound
228            .values()
229            .any(|state| state.local_port == port)
230            || self
231                .udp_connections
232                .values()
233                .any(|state| state.local_port == port)
234    }
235
236    pub(crate) fn alloc_udp_ephemeral_port(&self) -> Option<u16> {
237        const BASE: u16 = 49_152;
238        const COUNT: usize = 16_384;
239        let start = (self.next_fid as usize) % COUNT;
240        for step in 0..COUNT {
241            let port = BASE + ((start + step) % COUNT) as u16;
242            if !self.udp_port_in_use(port) {
243                return Some(port);
244            }
245        }
246        None
247    }
248
249    pub(crate) fn create_udp_socket(
250        &mut self,
251        local_port: u16,
252    ) -> core::result::Result<SocketHandle, i32> {
253        let rx_buf = udp::PacketBuffer::new(
254            alloc::vec![udp::PacketMetadata::EMPTY; 32],
255            alloc::vec![0u8; 65536],
256        );
257        let tx_buf = udp::PacketBuffer::new(
258            alloc::vec![udp::PacketMetadata::EMPTY; 32],
259            alloc::vec![0u8; 65536],
260        );
261        let mut socket = udp::Socket::new(rx_buf, tx_buf);
262        if socket.bind(local_port).is_err() {
263            return Err(-98);
264        }
265        Ok(self.sockets.add(socket))
266    }
267
268    pub(crate) fn tcp_state_name(state: tcp::State) -> &'static str {
269        match state {
270            tcp::State::Closed => "CLOSED",
271            tcp::State::Listen => "LISTEN",
272            tcp::State::SynSent => "SYN-SENT",
273            tcp::State::SynReceived => "SYN-RECEIVED",
274            tcp::State::Established => "ESTABLISHED",
275            tcp::State::FinWait1 => "FIN-WAIT-1",
276            tcp::State::FinWait2 => "FIN-WAIT-2",
277            tcp::State::CloseWait => "CLOSE-WAIT",
278            tcp::State::Closing => "CLOSING",
279            tcp::State::LastAck => "LAST-ACK",
280            tcp::State::TimeWait => "TIME-WAIT",
281        }
282    }
283
284    pub(crate) fn alloc_fid(&mut self) -> u64 {
285        let id = self.next_fid;
286        self.next_fid += 1;
287        id
288    }
289}