1use std::net::Ipv4Addr;
8
9#[derive(Debug, Clone, Default)]
11pub struct Ipv4Route {
12 pub interface: Option<String>,
14 pub source: Option<Ipv4Addr>,
16 pub gateway: Option<Ipv4Addr>,
18 pub is_local: bool,
20 pub prefix_len: u8,
22 pub metric: u32,
24}
25
26impl Ipv4Route {
27 #[must_use]
29 pub fn none() -> Self {
30 Self::default()
31 }
32
33 #[must_use]
35 pub fn local(interface: String, source: Ipv4Addr) -> Self {
36 Self {
37 interface: Some(interface),
38 source: Some(source),
39 gateway: None,
40 is_local: true,
41 prefix_len: 32,
42 metric: 0,
43 }
44 }
45
46 #[must_use]
48 pub fn via_gateway(interface: String, source: Ipv4Addr, gateway: Ipv4Addr) -> Self {
49 Self {
50 interface: Some(interface),
51 source: Some(source),
52 gateway: Some(gateway),
53 is_local: false,
54 prefix_len: 0,
55 metric: 0,
56 }
57 }
58
59 #[must_use]
61 pub fn is_valid(&self) -> bool {
62 self.interface.is_some()
63 }
64
65 #[must_use]
67 pub fn next_hop(&self, dst: Ipv4Addr) -> Ipv4Addr {
68 self.gateway.unwrap_or(dst)
69 }
70}
71
72pub trait Ipv4Router {
74 fn route(&self, dst: Ipv4Addr) -> Ipv4Route;
76}
77
78#[must_use]
83pub fn get_route(dst: Ipv4Addr) -> Ipv4Route {
84 if dst.is_loopback() {
86 return Ipv4Route::local("lo".to_string(), Ipv4Addr::LOCALHOST);
87 }
88
89 if dst.is_broadcast() || dst == Ipv4Addr::BROADCAST {
90 return get_default_route()
91 .map(|r| Ipv4Route {
92 is_local: true,
93 ..r
94 })
95 .unwrap_or_default();
96 }
97
98 if dst.is_multicast() {
99 return get_default_route()
101 .map(|r| Ipv4Route {
102 gateway: None,
103 is_local: true,
104 ..r
105 })
106 .unwrap_or_default();
107 }
108
109 let interfaces = pnet_datalink::interfaces();
111
112 for iface in &interfaces {
114 if !iface.is_up() || iface.is_loopback() {
115 continue;
116 }
117
118 for ip_network in &iface.ips {
119 if let std::net::IpAddr::V4(ip) = ip_network.ip() {
120 if ip_network.contains(std::net::IpAddr::V4(dst)) {
122 return Ipv4Route {
123 interface: Some(iface.name.clone()),
124 source: Some(ip),
125 gateway: None,
126 is_local: true,
127 prefix_len: ip_network.prefix(),
128 metric: 0,
129 };
130 }
131 }
132 }
133 }
134
135 if let Some(route) = get_default_route() {
137 return route;
138 }
139
140 Ipv4Route::none()
141}
142
143#[must_use]
145pub fn get_default_route() -> Option<Ipv4Route> {
146 let default_iface = default_net::get_default_interface().ok()?;
148 let gateway = default_net::get_default_gateway().ok()?;
149
150 let interfaces = pnet_datalink::interfaces();
151 let iface = interfaces.iter().find(|i| i.name == default_iface.name)?;
152
153 let source = iface.ips.iter().find_map(|ip| {
155 if let std::net::IpAddr::V4(v4) = ip.ip() {
156 Some(v4)
157 } else {
158 None
159 }
160 })?;
161
162 let gw_ip = match gateway.ip_addr {
163 std::net::IpAddr::V4(v4) => v4,
164 _ => return None,
165 };
166
167 Some(Ipv4Route {
168 interface: Some(iface.name.clone()),
169 source: Some(source),
170 gateway: Some(gw_ip),
171 is_local: false,
172 prefix_len: 0,
173 metric: 0,
174 })
175}
176
177#[must_use]
181pub fn get_source_for_dst(dst: Ipv4Addr) -> Option<Ipv4Addr> {
182 get_route(dst).source
183}
184
185#[must_use]
187pub fn get_interface_for_dst(dst: Ipv4Addr) -> Option<String> {
188 get_route(dst).interface
189}
190
191#[must_use]
193pub fn is_local_destination(dst: Ipv4Addr) -> bool {
194 get_route(dst).is_local
195}
196
197#[must_use]
199pub fn get_ipv4_interfaces() -> Vec<Ipv4Interface> {
200 pnet_datalink::interfaces()
201 .into_iter()
202 .filter_map(|iface| {
203 if !iface.is_up() {
204 return None;
205 }
206
207 let addrs: Vec<_> = iface
208 .ips
209 .iter()
210 .filter_map(|ip| {
211 if let std::net::IpAddr::V4(v4) = ip.ip() {
212 Some(Ipv4InterfaceAddr {
213 address: v4,
214 prefix_len: ip.prefix(),
215 broadcast: match ip.broadcast() {
217 std::net::IpAddr::V4(v4) => Some(v4),
218 _ => None,
219 },
220 })
221 } else {
222 None
223 }
224 })
225 .collect();
226
227 if addrs.is_empty() {
228 return None;
229 }
230
231 let is_loopback = iface.is_loopback();
234 let is_up = iface.is_up();
235 let is_running = iface.is_running();
236 let is_multicast = iface.is_multicast();
237 let is_broadcast = iface.is_broadcast();
238
239 Some(Ipv4Interface {
240 name: iface.name, index: iface.index,
242 mac: iface.mac.map(|m| m.octets()),
243 addresses: addrs,
244 is_loopback,
245 is_up,
246 is_running,
247 is_multicast,
248 is_broadcast,
249 mtu: None, })
251 })
252 .collect()
253}
254
255#[derive(Debug, Clone)]
257pub struct Ipv4Interface {
258 pub name: String,
260 pub index: u32,
262 pub mac: Option<[u8; 6]>,
264 pub addresses: Vec<Ipv4InterfaceAddr>,
266 pub is_loopback: bool,
268 pub is_up: bool,
270 pub is_running: bool,
272 pub is_multicast: bool,
274 pub is_broadcast: bool,
276 pub mtu: Option<u32>,
278}
279
280impl Ipv4Interface {
281 #[must_use]
283 pub fn primary_address(&self) -> Option<Ipv4Addr> {
284 self.addresses.first().map(|a| a.address)
285 }
286
287 #[must_use]
289 pub fn contains(&self, addr: Ipv4Addr) -> bool {
290 self.addresses.iter().any(|a| {
291 let mask = prefix_to_mask(a.prefix_len);
292 (u32::from(a.address) & mask) == (u32::from(addr) & mask)
293 })
294 }
295}
296
297#[derive(Debug, Clone)]
299pub struct Ipv4InterfaceAddr {
300 pub address: Ipv4Addr,
302 pub prefix_len: u8,
304 pub broadcast: Option<Ipv4Addr>,
306}
307
308impl Ipv4InterfaceAddr {
309 #[must_use]
311 pub fn network(&self) -> Ipv4Addr {
312 let mask = prefix_to_mask(self.prefix_len);
313 Ipv4Addr::from(u32::from(self.address) & mask)
314 }
315
316 #[must_use]
318 pub fn netmask(&self) -> Ipv4Addr {
319 Ipv4Addr::from(prefix_to_mask(self.prefix_len))
320 }
321}
322
323#[must_use]
325pub fn prefix_to_mask(prefix: u8) -> u32 {
326 if prefix >= 32 {
327 0xFFFFFFFF
328 } else if prefix == 0 {
329 0
330 } else {
331 !((1u32 << (32 - prefix)) - 1)
332 }
333}
334
335#[must_use]
337pub fn mask_to_prefix(mask: Ipv4Addr) -> u8 {
338 let mask_u32 = u32::from(mask);
339 mask_u32.leading_ones() as u8
340}
341
342#[must_use]
344pub fn ip_in_network(ip: Ipv4Addr, network: Ipv4Addr, prefix: u8) -> bool {
345 let mask = prefix_to_mask(prefix);
346 (u32::from(ip) & mask) == (u32::from(network) & mask)
347}
348
349#[must_use]
351pub fn broadcast_address(network: Ipv4Addr, prefix: u8) -> Ipv4Addr {
352 let mask = prefix_to_mask(prefix);
353 Ipv4Addr::from(u32::from(network) | !mask)
354}
355
356#[must_use]
358pub fn network_host_count(prefix: u8) -> u32 {
359 if prefix >= 31 {
360 2u32.saturating_sub(u32::from(prefix) - 30)
362 } else {
363 (1u32 << (32 - prefix)) - 2 }
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370
371 #[test]
372 fn test_prefix_to_mask() {
373 assert_eq!(prefix_to_mask(0), 0x00000000);
374 assert_eq!(prefix_to_mask(8), 0xFF000000);
375 assert_eq!(prefix_to_mask(16), 0xFFFF0000);
376 assert_eq!(prefix_to_mask(24), 0xFFFFFF00);
377 assert_eq!(prefix_to_mask(32), 0xFFFFFFFF);
378 }
379
380 #[test]
381 fn test_mask_to_prefix() {
382 assert_eq!(mask_to_prefix(Ipv4Addr::new(255, 0, 0, 0)), 8);
383 assert_eq!(mask_to_prefix(Ipv4Addr::new(255, 255, 0, 0)), 16);
384 assert_eq!(mask_to_prefix(Ipv4Addr::new(255, 255, 255, 0)), 24);
385 assert_eq!(mask_to_prefix(Ipv4Addr::new(255, 255, 255, 255)), 32);
386 }
387
388 #[test]
389 fn test_ip_in_network() {
390 let network = Ipv4Addr::new(192, 168, 1, 0);
391 assert!(ip_in_network(Ipv4Addr::new(192, 168, 1, 100), network, 24));
392 assert!(ip_in_network(Ipv4Addr::new(192, 168, 1, 255), network, 24));
393 assert!(!ip_in_network(Ipv4Addr::new(192, 168, 2, 1), network, 24));
394 }
395
396 #[test]
397 fn test_broadcast_address() {
398 assert_eq!(
399 broadcast_address(Ipv4Addr::new(192, 168, 1, 0), 24),
400 Ipv4Addr::new(192, 168, 1, 255)
401 );
402 assert_eq!(
403 broadcast_address(Ipv4Addr::new(10, 0, 0, 0), 8),
404 Ipv4Addr::new(10, 255, 255, 255)
405 );
406 }
407
408 #[test]
409 fn test_network_host_count() {
410 assert_eq!(network_host_count(24), 254);
411 assert_eq!(network_host_count(16), 65534);
412 assert_eq!(network_host_count(8), 16777214);
413 }
414
415 #[test]
416 fn test_route_loopback() {
417 let route = get_route(Ipv4Addr::LOCALHOST);
418 assert!(route.is_local);
419 assert_eq!(route.interface, Some("lo".to_string()));
420 }
421
422 #[test]
423 fn test_ipv4_route() {
424 let route = Ipv4Route::local("eth0".to_string(), Ipv4Addr::new(192, 168, 1, 100));
425 assert!(route.is_valid());
426 assert!(route.is_local);
427 assert_eq!(
428 route.next_hop(Ipv4Addr::new(192, 168, 1, 200)),
429 Ipv4Addr::new(192, 168, 1, 200)
430 );
431
432 let route = Ipv4Route::via_gateway(
433 "eth0".to_string(),
434 Ipv4Addr::new(192, 168, 1, 100),
435 Ipv4Addr::new(192, 168, 1, 1),
436 );
437 assert!(!route.is_local);
438 assert_eq!(
439 route.next_hop(Ipv4Addr::new(8, 8, 8, 8)),
440 Ipv4Addr::new(192, 168, 1, 1)
441 );
442 }
443
444 #[test]
445 fn test_get_ipv4_interfaces() {
446 let interfaces = get_ipv4_interfaces();
447 for iface in &interfaces {
448 assert!(!iface.name.is_empty());
449 assert!(!iface.addresses.is_empty());
450 }
451 }
452
453 #[test]
454 fn test_interface_addr() {
455 let addr = Ipv4InterfaceAddr {
456 address: Ipv4Addr::new(192, 168, 1, 100),
457 prefix_len: 24,
458 broadcast: Some(Ipv4Addr::new(192, 168, 1, 255)),
459 };
460
461 assert_eq!(addr.network(), Ipv4Addr::new(192, 168, 1, 0));
462 assert_eq!(addr.netmask(), Ipv4Addr::new(255, 255, 255, 0));
463 }
464}