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    /// fid -> sender that opened it. See `new_fid` / `is_owner`:
84    /// sequential fids are guessable, so every operation must be checked
85    /// against the opener's identity.
86    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    /// Allocate the next fid and record its owner (the sender that opened
297    /// it).
298    ///
299    /// Fids are small sequential integers, trivially guessable: without
300    /// this ownership record, any process could read another process's TCP
301    /// connection, hijack its UDP socket or reconfigure the IP stack by
302    /// issuing operations on a guessed fid.
303    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    /// True when `sender` opened `fid`. Unknown fids belong to nobody:
310    /// every sender gets EBADF for them.
311    pub(crate) fn is_owner(&self, fid: u64, sender: u64) -> bool {
312        self.handle_owners.get(&fid) == Some(&sender)
313    }
314
315    /// Drop the ownership record of a closed fid.
316    pub(crate) fn forget_handle(&mut self, fid: u64) {
317        self.handle_owners.remove(&fid);
318    }
319}