system_info/unix/linux/
network.rs1extern 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 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 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; req.header.nlmsg_len = NETLINK_ADDR_REQ_SIZE as u32;
186 req.msg.ifa_family = libc::AF_UNSPEC as _; req.msg.ifa_index = 0; 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; 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 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 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 let rta_size = (rta_attr.rta_len as usize + ALIGN_SIZE - 1) & !(ALIGN_SIZE - 1); 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 _ => (),
255 }
256
257 let msg_size = (cursor.header.nlmsg_len as usize + ALIGN_SIZE - 1) & !(ALIGN_SIZE - 1); 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 None
269 }
270}