system_info/unix/linux/
network.rs

1//! Network information.
2
3extern crate alloc;
4
5use alloc::vec::Vec;
6
7use core::mem;
8
9pub use crate::data::network::{Ip, Address};
10pub use crate::unix::posix::network::{Interfaces, InterfacesIter, Interface, Addresses};
11use crate::unix::posix::network::{slice_c_str, InterfaceData};
12
13const ALIGN_SIZE: usize = 4;
14const NETLINK_HEADER_SIZE: usize = mem::size_of::<libc::nlmsghdr>();
15const NETLINK_ADDR_REQ_SIZE: usize = mem::size_of::<NetlinkAddrReq>();
16
17#[repr(C)]
18#[repr(align(4))]
19struct RtaAttr {
20    rta_len: u16,
21    rta_type: u16,
22}
23
24impl RtaAttr {
25    #[inline(always)]
26    fn is_ok(&self, payload_len: u32) -> bool {
27        payload_len >= mem::size_of::<Self>() as u32 &&
28        self.rta_len as usize >= mem::size_of::<RtaAttr>() &&
29        self.rta_len as u32 <= payload_len
30    }
31}
32
33#[repr(C)]
34struct IfAddrMsg {
35    ifa_family: u8,
36    ifa_prefixlen: u8,
37    ifa_flags: u8,
38    ifa_scope: u8,
39    ifa_index: u32,
40}
41
42#[repr(C)]
43#[repr(align(4))]
44struct NetlinkAddrReq {
45    header: libc::nlmsghdr,
46    msg: IfAddrMsg,
47}
48
49unsafe fn extract_rta_data<T: Copy>(rta_attr: &RtaAttr) -> T {
50    let mut out = mem::MaybeUninit::<T>::zeroed();
51
52    let rta_data = (rta_attr as *const _ as *const u8).add(mem::size_of_val(rta_attr)) as *const u8;
53    let rta_len = (rta_attr.rta_len as usize) - mem::size_of_val(rta_attr);
54
55    (out.as_mut_ptr() as *mut u8).copy_from_nonoverlapping(rta_data, rta_len as usize);
56    out.assume_init()
57}
58
59struct Socket {
60    fd: libc::c_int,
61    addr: libc::sockaddr_nl,
62}
63
64impl Socket {
65    fn new() -> Option<Self> {
66        let mut addr = unsafe {
67            mem::MaybeUninit::<libc::sockaddr_nl>::zeroed().assume_init()
68        };
69        addr.nl_family = libc::AF_NETLINK as _;
70
71        socket().map(|fd| Self {
72            fd,
73            addr
74        })
75    }
76
77    fn send(&self, mut msg: NetlinkAddrReq) -> bool {
78        let mut msg = libc::iovec {
79            iov_base: &mut msg as *mut _ as *mut _,
80            iov_len: msg.header.nlmsg_len as _,
81        };
82        let mut req = unsafe {
83            mem::MaybeUninit::<libc::msghdr>::zeroed().assume_init()
84        };
85        req.msg_name = &self.addr as *const _ as *mut _;
86        req.msg_namelen = mem::size_of_val(&self.addr) as _;
87        req.msg_iov = &mut msg as *mut _ as *mut _;
88        req.msg_iovlen = 1;
89
90        let res = unsafe {
91            libc::sendmsg(self.fd, &mut req as *mut _, 0)
92        };
93        res >= 0
94    }
95
96    fn recv(&self, buffer: &mut [u8]) -> Option<usize> {
97        let mut msg = libc::iovec {
98            iov_base: buffer.as_mut_ptr() as *mut _,
99            iov_len: buffer.len(),
100        };
101        let mut req = unsafe {
102            mem::MaybeUninit::<libc::msghdr>::zeroed().assume_init()
103        };
104        req.msg_name = &self.addr as *const _ as *mut _;
105        req.msg_namelen = mem::size_of_val(&self.addr) as _;
106        req.msg_iov = &mut msg as *mut _ as *mut _;
107        req.msg_iovlen = 1;
108
109        let res = unsafe {
110            libc::recvmsg(self.fd, &mut req as *mut _, 0)
111        };
112
113        if res < 0  {
114            None
115        } else {
116            Some(res as usize)
117        }
118
119    }
120}
121
122impl Drop for Socket {
123    #[inline(always)]
124    fn drop(&mut self) {
125        unsafe {
126            libc::close(self.fd);
127        }
128    }
129}
130
131fn socket() -> Option<libc::c_int> {
132    let fd = unsafe {
133        libc::socket(libc::AF_NETLINK, libc::SOCK_DGRAM | libc::SOCK_CLOEXEC, libc::NETLINK_ROUTE)
134    };
135
136    if fd == -1 {
137        return None;
138    }
139
140    Some(fd)
141}
142
143impl Interfaces {
144    #[inline(always)]
145    //It can fail if interface_index is invalid
146    fn store_interface(&mut self, interface_index: u32) -> Option<&mut InterfaceData> {
147        let mut name = [0u8; libc::IFNAMSIZ];
148        let result = unsafe {
149            libc::if_indextoname(interface_index, name.as_mut_ptr() as _)
150        };
151
152        if result.is_null() {
153            None
154        } else {
155            let real_name = slice_c_str(&name);
156            match self.inner.binary_search_by_key(&real_name, |interface| interface.name()) {
157                Ok(idx) => Some(unsafe {
158                    self.inner.get_unchecked_mut(idx)
159                }),
160                Err(idx) => {
161                    let interface = InterfaceData {
162                        name,
163                        addresses: Vec::new(),
164                    };
165                    self.inner.insert(idx, interface);
166
167                    Some(unsafe {
168                        self.inner.get_unchecked_mut(idx)
169                    })
170                }
171            }
172        }
173    }
174
175    ///Creates new instance.
176    ///
177    ///In case of failure please check `std::io::Error::last_os_error()`
178    pub fn new() -> Option<Self> {
179        let netlink = Socket::new()?;
180        let mut req = unsafe {
181            mem::MaybeUninit::<NetlinkAddrReq>::zeroed().assume_init()
182        };
183        req.header.nlmsg_flags = (libc::NLM_F_REQUEST | libc::NLM_F_DUMP) as u16;
184        req.header.nlmsg_type = 22; //RTM_GETADDR
185        req.header.nlmsg_len = NETLINK_ADDR_REQ_SIZE as u32;
186        req.msg.ifa_family = libc::AF_UNSPEC as _; //All IPs
187        req.msg.ifa_index = 0; //All interfaces
188
189        if !netlink.send(req) {
190            return None;
191        }
192
193        let mut result = Interfaces {
194            inner: Vec::new()
195        };
196        let mut buf = [0u8; 65536];
197        while let Some(mut size) = netlink.recv(&mut buf) {
198            let mut cursor_ptr = buf.as_ptr();
199            let mut cursor = unsafe {
200                &*(cursor_ptr as *const NetlinkAddrReq)
201            };
202
203            const DONE: u16 = libc::NLMSG_DONE as u16;
204            const ERROR: u16 = libc::NLMSG_ERROR as u16;
205            const NEW_ADDR: u16 = 20; //RTM_NEWADDR
206
207            while size >= NETLINK_HEADER_SIZE && cursor.header.nlmsg_len >= NETLINK_HEADER_SIZE as u32 && cursor.header.nlmsg_len <= size as u32 {
208                match cursor.header.nlmsg_type {
209                    DONE => return Some(result),
210                    ERROR => return None,
211                    NEW_ADDR => unsafe {
212                        let if_req = &cursor.msg;
213                        let mut data_len = cursor.header.nlmsg_len - mem::size_of_val(cursor) as u32;
214                        let rta_attr = cursor_ptr.add(mem::size_of_val(cursor)) as *const RtaAttr;
215                        let mut rta_attr = &*rta_attr;
216
217                        while rta_attr.is_ok(data_len) {
218                            if rta_attr.rta_type == 2  {
219                                //IFA_LOCAL
220                                //RTM_GETADDR only responds with ipv4
221                                if if_req.ifa_family == libc::AF_INET as u8 {
222                                    let interface = result.store_interface(if_req.ifa_index)?;
223
224                                    let ip = extract_rta_data::<[u8; mem::size_of::<u32>()]>(rta_attr);
225                                    let ip = Ip::V4(ip);
226
227                                    interface.push(Address {
228                                        ip,
229                                        prefix: if_req.ifa_prefixlen,
230                                    });
231                                }
232                            } else if rta_attr.rta_type == 1  {
233                                //IFA_ADDRESS
234                                //RTM_GETADDR responds with ipv6
235                                if if_req.ifa_family == libc::AF_INET6 as u8 {
236                                    let interface = result.store_interface(if_req.ifa_index)?;
237                                    let ip = extract_rta_data::<[u16; 8]>(rta_attr);
238                                    let ip = Ip::V6(ip);
239
240                                    interface.push(Address {
241                                        ip,
242                                        prefix: if_req.ifa_prefixlen,
243                                    });
244                                }
245                            }
246
247                            //go to next RTA
248                            let rta_size = (rta_attr.rta_len as usize + ALIGN_SIZE - 1) & !(ALIGN_SIZE - 1); //aligned
249                            data_len -= rta_size as u32;
250                            rta_attr = &*((rta_attr as *const _ as *const u8).add(rta_size) as *const RtaAttr);
251                        }
252                    },
253                    //we don't care about anything else
254                    _ => (),
255                }
256
257                //Go to next message
258                let msg_size = (cursor.header.nlmsg_len as usize + ALIGN_SIZE - 1) & !(ALIGN_SIZE - 1); //aligned
259                size -= msg_size;
260                unsafe {
261                    cursor_ptr = cursor_ptr.add(msg_size);
262                    cursor = &*(cursor_ptr as *const NetlinkAddrReq);
263                }
264            }
265        }
266
267        //Failed to read from socket
268        None
269    }
270}