Skip to main content

strate_net_silo/
config.rs

1use smoltcp::{
2    socket::{dhcpv4, dns},
3    wire::{DnsQueryType, IpAddress, IpCidr, Ipv4Address, Ipv6Address, Ipv6Cidr},
4};
5use strate_net::syscalls::clock_gettime_ns;
6
7use crate::{
8    ip::{
9        broadcast_from_host_prefix, mask_from_prefix, parse_ipv4, parse_ipv6, IpConfig, Ipv6Config,
10    },
11    now_instant, sleep_micros,
12    state::NetworkStrate,
13};
14
15impl NetworkStrate {
16    pub(crate) fn process_dhcp(&mut self) {
17        if !self.dhcp_enabled {
18            return;
19        }
20
21        let event = self
22            .sockets
23            .get_mut::<dhcpv4::Socket>(self.dhcp_handle)
24            .poll();
25
26        match event {
27            Some(dhcpv4::Event::Configured(config)) => {
28                crate::log("[strate-net] DHCP: address acquired\n");
29
30                self.interface.update_ip_addrs(|addrs| {
31                    let cidr = IpCidr::new(
32                        IpAddress::Ipv4(config.address.address()),
33                        config.address.prefix_len(),
34                    );
35                    for addr in addrs.iter_mut() {
36                        if matches!(addr, IpCidr::Ipv4(_)) {
37                            *addr = cidr;
38                            return;
39                        }
40                    }
41                    let _ = addrs.push(cidr);
42                });
43
44                let _ = self.interface.routes_mut().remove_default_ipv4_route();
45                if let Some(gw) = config.router {
46                    self.interface.routes_mut().add_default_ipv4_route(gw).ok();
47                }
48
49                let mut dns = [None::<Ipv4Address>; 3];
50                self.dns_servers = [None; 3];
51                for ((slot_v4, slot_any), &server) in dns
52                    .iter_mut()
53                    .zip(self.dns_servers.iter_mut())
54                    .zip(config.dns_servers.iter())
55                {
56                    *slot_v4 = Some(server);
57                    *slot_any = Some(IpAddress::Ipv4(server));
58                }
59                self.dns_from_dhcp = true;
60
61                let host = config.address.address();
62                let prefix_len = config.address.prefix_len();
63                let netmask = mask_from_prefix(prefix_len);
64                let broadcast = broadcast_from_host_prefix(host, prefix_len);
65                self.ip_config = Some(IpConfig {
66                    address: config.address,
67                    host,
68                    prefix_len,
69                    netmask,
70                    broadcast,
71                    gateway: config.router,
72                    dns,
73                });
74                self.refresh_dns_servers();
75            }
76            Some(dhcpv4::Event::Deconfigured) => {
77                crate::log("[strate-net] DHCP: deconfigured (lease expired?)\n");
78                if self.dns_from_dhcp {
79                    self.dns_servers = [None; 3];
80                    self.dns_from_dhcp = false;
81                }
82                self.clear_ipv4_runtime_config();
83            }
84            None => {}
85        }
86    }
87
88    pub(crate) fn refresh_dns_servers(&mut self) {
89        let mut servers = [IpAddress::Ipv4(Ipv4Address::new(0, 0, 0, 0)); 3];
90        let mut count = 0usize;
91
92        for server in self.dns_servers.iter().flatten() {
93            if count < servers.len() {
94                servers[count] = *server;
95                count += 1;
96            }
97        }
98
99        if count == 0 {
100            if let Some(ref cfg) = self.ip_config {
101                if let Some(gw) = cfg.gateway {
102                    servers[0] = IpAddress::Ipv4(gw);
103                    count = 1;
104                }
105            }
106        }
107
108        if count == 0 {
109            if let Some(ref cfg) = self.ipv6_config {
110                if let Some(gw) = cfg.gateway {
111                    servers[0] = IpAddress::Ipv6(gw);
112                    count = 1;
113                }
114            }
115        }
116
117        let socket = self.sockets.get_mut::<dns::Socket>(self.dns_handle);
118        socket.update_servers(&servers[..count]);
119    }
120
121    pub(crate) fn apply_ipv4_config(
122        &mut self,
123        address: smoltcp::wire::Ipv4Cidr,
124        gateway: Option<Ipv4Address>,
125    ) {
126        let host = address.address();
127        let prefix_len = address.prefix_len();
128        let netmask = mask_from_prefix(prefix_len);
129        let broadcast = broadcast_from_host_prefix(host, prefix_len);
130
131        self.interface.update_ip_addrs(|addrs| {
132            let cidr = IpCidr::new(IpAddress::Ipv4(host), prefix_len);
133            for addr in addrs.iter_mut() {
134                if matches!(addr, IpCidr::Ipv4(_)) {
135                    *addr = cidr;
136                    return;
137                }
138            }
139            let _ = addrs.push(cidr);
140        });
141
142        let _ = self.interface.routes_mut().remove_default_ipv4_route();
143        if let Some(gw) = gateway {
144            let _ = self.interface.routes_mut().add_default_ipv4_route(gw);
145        }
146
147        let mut dns = [None; 3];
148        for (slot, server) in dns.iter_mut().zip(self.dns_servers.iter()) {
149            if let Some(IpAddress::Ipv4(addr)) = server {
150                *slot = Some(*addr);
151            }
152        }
153
154        self.ip_config = Some(IpConfig {
155            address,
156            host,
157            prefix_len,
158            netmask,
159            broadcast,
160            gateway,
161            dns,
162        });
163        self.dns_from_dhcp = false;
164        self.refresh_dns_servers();
165    }
166
167    pub(crate) fn apply_ipv6_config(&mut self, address: Ipv6Cidr, gateway: Option<Ipv6Address>) {
168        self.interface.update_ip_addrs(|addrs| {
169            let cidr = IpCidr::new(IpAddress::Ipv6(address.address()), address.prefix_len());
170            for addr in addrs.iter_mut() {
171                if let IpCidr::Ipv6(current) = addr {
172                    if !current.address().is_unicast_link_local() {
173                        *addr = cidr;
174                        return;
175                    }
176                }
177            }
178            let _ = addrs.push(cidr);
179        });
180        if let Some(gw) = gateway {
181            let _ = self.interface.routes_mut().add_default_ipv6_route(gw);
182        }
183        self.ipv6_config = Some(Ipv6Config { address, gateway });
184        self.refresh_dns_servers();
185    }
186
187    pub(crate) fn resolve_hostname_query(
188        &mut self,
189        name: &str,
190        q_type: DnsQueryType,
191    ) -> core::result::Result<IpAddress, i32> {
192        let query = {
193            let cx = self.interface.context();
194            let socket = self.sockets.get_mut::<dns::Socket>(self.dns_handle);
195            match socket.start_query(cx, name, q_type) {
196                Ok(query) => query,
197                Err(_) => return Err(-22),
198            }
199        };
200
201        let deadline_ns = clock_gettime_ns()
202            .unwrap_or(0)
203            .saturating_add(3_000_000_000);
204        loop {
205            let now = now_instant();
206            let _ = self
207                .interface
208                .poll(now, &mut self.device, &mut self.sockets);
209            self.process_dhcp();
210            self.process_ipv6_slaac();
211            self.process_icmp();
212
213            let res = self
214                .sockets
215                .get_mut::<dns::Socket>(self.dns_handle)
216                .get_query_result(query);
217            match res {
218                Ok(addrs) => {
219                    if let Some(addr) = addrs.into_iter().next() {
220                        return Ok(addr);
221                    }
222                    return Err(-2);
223                }
224                Err(dns::GetQueryResultError::Failed) => return Err(-2),
225                Err(dns::GetQueryResultError::Pending) => {
226                    if clock_gettime_ns().unwrap_or(0) >= deadline_ns {
227                        return Err(-110);
228                    }
229                    sleep_micros(10_000);
230                }
231            }
232        }
233    }
234
235    pub(crate) fn resolve_hostname_blocking(
236        &mut self,
237        name: &str,
238    ) -> core::result::Result<IpAddress, i32> {
239        if let Some(ip) = parse_ipv4(name) {
240            return Ok(IpAddress::Ipv4(ip));
241        }
242        if let Some(ip) = parse_ipv6(name) {
243            return Ok(IpAddress::Ipv6(ip));
244        }
245        if self.ip_config.is_none() && self.ipv6_config.is_none() {
246            return Err(-11);
247        }
248
249        let mut last_err = -2;
250        if self.ipv6_config.is_some() {
251            match self.resolve_hostname_query(name, DnsQueryType::Aaaa) {
252                Ok(addr) => return Ok(addr),
253                Err(-2) => {}
254                Err(err) => last_err = err,
255            }
256        }
257        if self.ip_config.is_some() {
258            match self.resolve_hostname_query(name, DnsQueryType::A) {
259                Ok(addr) => return Ok(addr),
260                Err(err) => last_err = err,
261            }
262        }
263        Err(last_err)
264    }
265
266    pub(crate) fn process_ipv6_slaac(&mut self) {
267        let mut global_addr: Option<Ipv6Cidr> = None;
268        self.interface.update_ip_addrs(|addrs| {
269            for addr in addrs.iter() {
270                if let IpCidr::Ipv6(cidr) = addr {
271                    if !cidr.address().is_unicast_link_local() && global_addr.is_none() {
272                        global_addr = Some(*cidr);
273                    }
274                }
275            }
276        });
277
278        let mut gateway: Option<Ipv6Address> = None;
279        self.interface.routes_mut().update(|table| {
280            for route in table.iter() {
281                if let (IpCidr::Ipv6(cidr), IpAddress::Ipv6(via)) = (route.cidr, route.via_router) {
282                    if cidr.prefix_len() == 0 {
283                        gateway = Some(via);
284                        break;
285                    }
286                }
287            }
288        });
289
290        match (global_addr, &mut self.ipv6_config) {
291            (Some(addr), Some(cfg)) => {
292                cfg.address = addr;
293                cfg.gateway = gateway;
294            }
295            (Some(addr), cfg @ None) => {
296                *cfg = Some(Ipv6Config {
297                    address: addr,
298                    gateway,
299                });
300            }
301            (None, _) => {
302                self.ipv6_config = None;
303            }
304        }
305    }
306}