network_interface/target/
linux.rs

1use std::collections::HashMap;
2use std::net::{Ipv4Addr, Ipv6Addr};
3use std::slice::from_raw_parts;
4
5use libc::{
6    AF_INET, AF_INET6, AF_PACKET, IFF_LOOPBACK, if_nametoindex, sockaddr_in, sockaddr_in6,
7    sockaddr_ll, strlen,
8};
9
10use crate::target::getifaddrs;
11use crate::{Error, NetworkInterface, NetworkInterfaceConfig, Result};
12use crate::utils::{ipv4_from_in_addr, ipv6_from_in6_addr, make_ipv4_netmask, make_ipv6_netmask};
13
14impl NetworkInterfaceConfig for NetworkInterface {
15    fn show() -> Result<Vec<NetworkInterface>> {
16        let mut network_interfaces: HashMap<String, NetworkInterface> = HashMap::new();
17
18        for netifa in getifaddrs()? {
19            let netifa_addr = netifa.ifa_addr;
20            let netifa_family = if netifa_addr.is_null() {
21                continue;
22            } else {
23                unsafe { (*netifa_addr).sa_family as i32 }
24            };
25
26            let internal = netifa.ifa_flags & IFF_LOOPBACK as u32 != 0;
27
28            let mut network_interface = match netifa_family {
29                AF_PACKET => {
30                    let name = make_netifa_name(&netifa)?;
31                    let mac = make_mac_addrs(&netifa);
32                    let index = netifa_index(&netifa);
33                    NetworkInterface {
34                        name,
35                        addr: Vec::new(),
36                        mac_addr: Some(mac),
37                        index,
38                        internal,
39                    }
40                }
41                AF_INET => {
42                    let socket_addr = netifa_addr as *mut sockaddr_in;
43                    let internet_address = unsafe { (*socket_addr).sin_addr };
44                    let name = make_netifa_name(&netifa)?;
45                    let index = netifa_index(&netifa);
46                    let netmask = make_ipv4_netmask(&netifa);
47                    let addr = ipv4_from_in_addr(&internet_address)?;
48                    let broadcast = make_ipv4_broadcast_addr(&netifa)?;
49                    NetworkInterface::new_afinet(
50                        name.as_str(),
51                        addr,
52                        netmask,
53                        broadcast,
54                        index,
55                        internal,
56                    )
57                }
58                AF_INET6 => {
59                    let socket_addr = netifa_addr as *mut sockaddr_in6;
60                    let internet_address = unsafe { (*socket_addr).sin6_addr };
61                    let name = make_netifa_name(&netifa)?;
62                    let index = netifa_index(&netifa);
63                    let netmask = make_ipv6_netmask(&netifa);
64                    let addr = ipv6_from_in6_addr(&internet_address)?;
65                    let broadcast = make_ipv6_broadcast_addr(&netifa)?;
66                    NetworkInterface::new_afinet6(
67                        name.as_str(),
68                        addr,
69                        netmask,
70                        broadcast,
71                        index,
72                        internal,
73                    )
74                }
75                _ => continue,
76            };
77
78            network_interfaces
79                .entry(network_interface.name.clone())
80                .and_modify(|old| old.addr.append(&mut network_interface.addr))
81                .or_insert(network_interface);
82        }
83
84        Ok(network_interfaces.into_values().collect())
85    }
86}
87
88/// Retrieves the network interface name
89fn make_netifa_name(netifa: &libc::ifaddrs) -> Result<String> {
90    let data = netifa.ifa_name as *const libc::c_char;
91    let len = unsafe { strlen(data) };
92    let bytes_slice = unsafe { from_raw_parts(data as *const u8, len) };
93
94    match String::from_utf8(bytes_slice.to_vec()) {
95        Ok(s) => Ok(s),
96        Err(e) => Err(Error::ParseUtf8Error(e)),
97    }
98}
99
100/// Retrieves the broadcast address for the network interface provided of the
101/// AF_INET family.
102///
103/// ## References
104///
105/// https://man7.org/linux/man-pages/man3/getifaddrs.3.html
106fn make_ipv4_broadcast_addr(netifa: &libc::ifaddrs) -> Result<Option<Ipv4Addr>> {
107    let ifa_dstaddr = netifa.ifa_ifu;
108
109    if ifa_dstaddr.is_null() {
110        return Ok(None);
111    }
112
113    let socket_addr = ifa_dstaddr as *mut sockaddr_in;
114    let internet_address = unsafe { (*socket_addr).sin_addr };
115    let addr = ipv4_from_in_addr(&internet_address)?;
116
117    Ok(Some(addr))
118}
119
120/// Retrieves the broadcast address for the network interface provided of the
121/// AF_INET6 family.
122///
123/// ## References
124///
125/// https://man7.org/linux/man-pages/man3/getifaddrs.3.html
126fn make_ipv6_broadcast_addr(netifa: &libc::ifaddrs) -> Result<Option<Ipv6Addr>> {
127    let ifa_dstaddr = netifa.ifa_ifu;
128
129    if ifa_dstaddr.is_null() {
130        return Ok(None);
131    }
132
133    let socket_addr = ifa_dstaddr as *mut sockaddr_in6;
134    let internet_address = unsafe { (*socket_addr).sin6_addr };
135    let addr = ipv6_from_in6_addr(&internet_address)?;
136
137    Ok(Some(addr))
138}
139
140fn make_mac_addrs(netifa: &libc::ifaddrs) -> String {
141    let netifa_addr = netifa.ifa_addr;
142    let socket_addr = netifa_addr as *mut sockaddr_ll;
143    let mac_array = unsafe { (*socket_addr).sll_addr };
144    let addr_len = unsafe { (*socket_addr).sll_halen };
145    let real_addr_len = std::cmp::min(addr_len as usize, mac_array.len());
146    let mac_slice = unsafe { std::slice::from_raw_parts(mac_array.as_ptr(), real_addr_len) };
147
148    mac_slice
149        .iter()
150        .map(|x| format!("{x:02x}"))
151        .collect::<Vec<_>>()
152        .join(":")
153}
154
155/// Retreives the name for the the network interface provided
156///
157/// ## References
158///
159/// https://man7.org/linux/man-pages/man3/if_nametoindex.3.html
160fn netifa_index(netifa: &libc::ifaddrs) -> u32 {
161    let name = netifa.ifa_name as *const libc::c_char;
162
163    unsafe { if_nametoindex(name) }
164}