1#[repr(C)]
5pub struct IfAddrs {
6 pub ifa_next: *mut IfAddrs,
7 pub ifa_name: *mut c_char,
8 pub ifa_flags: c_uint,
9 pub ifa_addr: *mut c_void,
10 pub ifa_netmask: *mut c_void,
11 pub ifa_ifu: *mut c_void,
12 pub ifa_data: *mut c_void,
13}
14
15#[repr(C, align(8))]
16#[derive(Copy, Clone)]
17struct SockAny {
18 data: [u8; 64],
19}
20
21#[repr(C)]
22struct IfaStorage {
23 ifa: IfAddrs,
24 hash_next: *mut IfaStorage,
25 addr: SockAny,
26 netmask: SockAny,
27 ifu: SockAny,
28 index: c_uint,
29 name: [c_char; 17],
30}
31
32const IFADDRS_HASH_SIZE: usize = 64;
33
34struct IfaCtx {
35 first: *mut IfAddrs,
36 last: *mut IfAddrs,
37 hash: [*mut IfaStorage; IFADDRS_HASH_SIZE],
38}
39
40#[repr(C)]
41struct NlMsgHdr {
42 len: u32,
43 type_: u16,
44 flags: u16,
45 seq: u32,
46 pid: u32,
47}
48
49#[repr(C)]
50struct NlRta {
51 len: u16,
52 type_: u16,
53}
54
55#[repr(C)]
56struct IfInfoMsg {
57 family: u8,
58 pad: u8,
59 type_: u16,
60 index: i32,
61 flags: u32,
62 change: u32,
63}
64
65#[repr(C)]
66struct IfAddrMsg {
67 family: u8,
68 prefixlen: u8,
69 flags: u8,
70 scope: u8,
71 index: u32,
72}
73
74#[repr(C)]
75struct SockAddrNl {
76 family: u16,
77 pad: u16,
78 pid: u32,
79 groups: u32,
80}
81
82const NLMSG_ERROR: u16 = 2;
83const NLMSG_DONE: u16 = 3;
84const RTM_NEWLINK: u16 = 16;
85const RTM_NEWADDR: u16 = 20;
86const RTM_GETLINK: u16 = 18;
87const RTM_GETADDR: u16 = 22;
88const IFLA_ADDRESS: u16 = 1;
89const IFLA_BROADCAST: u16 = 2;
90const IFLA_IFNAME: u16 = 3;
91const IFA_ADDRESS: u16 = 1;
92const IFA_LOCAL: u16 = 2;
93const IFA_LABEL: u16 = 3;
94const IFA_BROADCAST: u16 = 4;
95
96unsafe fn copy_addr(
97 r: *mut *mut c_void,
98 af: c_int,
99 sa: *mut SockAny,
100 addr: *const u8,
101 addrlen: usize,
102 ifindex: c_int,
103) {
104 let base = sa as *mut u8;
105 let dst: *mut u8;
106 let len: usize;
107 if af == AF_INET {
108 dst = base.add(4);
109 len = 4;
110 } else if af == AF_INET6 {
111 dst = base.add(8);
112 len = 16;
113 let linklocal = addrlen >= 2 && *addr == 0xfe && ((*addr.add(1) & 0xc0) == 0x80);
114 let mclink = addrlen >= 2 && *addr == 0xff && ((*addr.add(1) & 0x0f) == 2);
115 if linklocal || mclink {
116 *(base.add(24) as *mut u32) = ifindex as u32;
117 }
118 } else {
119 return;
120 }
121 if addrlen < len { return; }
122 *(base as *mut u16) = af as u16;
123 core::ptr::copy_nonoverlapping(addr, dst, len);
124 *r = sa as *mut c_void;
125}
126
127unsafe fn gen_netmask(r: *mut *mut c_void, af: c_int, sa: *mut SockAny, prefixlen: c_int) {
128 let mut addr = [0u8; 16];
129 let mut pl = prefixlen;
130 if pl > 128 { pl = 128; }
131 let i = (pl / 8) as usize;
132 for k in 0..i { addr[k] = 0xff; }
133 if i < 16 {
134 addr[i] = (0xffu16 << (8 - (pl % 8))) as u8;
135 }
136 copy_addr(r, af, sa, addr.as_ptr(), 16, 0);
137}
138
139unsafe fn copy_lladdr(
140 r: *mut *mut c_void,
141 sa: *mut SockAny,
142 addr: *const u8,
143 addrlen: usize,
144 ifindex: c_int,
145 hatype: u16,
146) {
147 if addrlen > 24 { return; }
148 let base = sa as *mut u8;
149 *(base as *mut u16) = 17; *(base.add(2) as *mut u16) = 0;
151 *(base.add(4) as *mut i32) = ifindex;
152 *(base.add(8) as *mut u16) = hatype;
153 *base.add(10) = 0;
154 *base.add(11) = addrlen as u8;
155 core::ptr::copy_nonoverlapping(addr, base.add(12), addrlen);
156 *r = sa as *mut c_void;
157}
158
159unsafe fn parse_rtattrs<F: FnMut(u16, *const u8, usize)>(
160 hdr: *const NlMsgHdr,
161 payload_off: usize,
162 mut cb: F,
163) {
164 let start = hdr as *const u8;
165 let end = start.add((*hdr).len as usize);
166 let mut p = start.add(payload_off);
167 while p.add(4) <= end {
168 let rta = p as *const NlRta;
169 let alen = (*rta).len as usize;
170 if alen < 4 || p.add(alen) > end { break; }
171 let atype = (*rta).type_ & 0x3fff;
172 cb(atype, p.add(4), alen - 4);
173 p = p.add((alen + 3) & !3usize);
174 }
175}
176
177unsafe fn netlink_msg_to_ifaddr(ctx: *mut IfaCtx, hdr: *const NlMsgHdr) -> c_int {
178 let htype = (*hdr).type_;
179 let ifs: *mut IfaStorage;
180 let mut ifs0: *mut IfaStorage = core::ptr::null_mut();
181 if htype == RTM_NEWLINK {
182 let hb = hdr as *const u8;
183 let payload = hb.add(16) as *const IfInfoMsg;
184 ifs = crate::calloc(1, core::mem::size_of::<IfaStorage>()) as *mut IfaStorage;
185 if ifs.is_null() { return -1; }
186 (*ifs).index = (*payload).index as c_uint;
187 (*ifs).ifa.ifa_flags = (*payload).flags;
188 let index = (*payload).index;
189 let hatype = (*payload).type_;
190 parse_rtattrs(hdr, 16 + core::mem::size_of::<IfInfoMsg>(), |atype, data, len| {
191 match atype {
192 IFLA_IFNAME => {
193 if len < (*ifs).name.len() {
194 core::ptr::copy_nonoverlapping(data, (*ifs).name.as_mut_ptr() as *mut u8, len);
195 (*ifs).name[len] = 0;
196 (*ifs).ifa.ifa_name = (*ifs).name.as_mut_ptr();
197 }
198 }
199 IFLA_ADDRESS => {
200 copy_lladdr(&mut (*ifs).ifa.ifa_addr, &mut (*ifs).addr, data, len, index, hatype);
201 }
202 IFLA_BROADCAST => {
203 copy_lladdr(&mut (*ifs).ifa.ifa_ifu, &mut (*ifs).ifu, data, len, index, hatype);
204 }
205 _ => {}
206 }
207 });
208 if !(*ifs).ifa.ifa_name.is_null() {
209 let bucket = ((*ifs).index as usize) % IFADDRS_HASH_SIZE;
210 (*ifs).hash_next = (*ctx).hash[bucket];
211 (*ctx).hash[bucket] = ifs;
212 }
213 } else {
214 let payload = (hdr as *const u8).add(16) as *const IfAddrMsg;
215 let index = (*payload).index;
216 let bucket = (index as usize) % IFADDRS_HASH_SIZE;
217 let mut it = (*ctx).hash[bucket];
218 while !it.is_null() {
219 if (*it).index == index { ifs0 = it; break; }
220 it = (*it).hash_next;
221 }
222 if ifs0.is_null() { return 0; }
223 ifs = crate::calloc(1, core::mem::size_of::<IfaStorage>()) as *mut IfaStorage;
224 if ifs.is_null() { return -1; }
225 (*ifs).index = index as c_uint;
226 (*ifs).ifa.ifa_name = (*ifs0).ifa.ifa_name;
227 (*ifs).ifa.ifa_flags = (*ifs0).ifa.ifa_flags;
228 let family = (*payload).family as c_int;
229 let prefixlen = (*payload).prefixlen as c_int;
230 parse_rtattrs(hdr, 16 + core::mem::size_of::<IfAddrMsg>(), |atype, data, len| {
231 match atype {
232 IFA_ADDRESS => {
233 if !(*ifs).ifa.ifa_addr.is_null() {
234 copy_addr(&mut (*ifs).ifa.ifa_ifu, family, &mut (*ifs).ifu, data, len, index as c_int);
235 } else {
236 copy_addr(&mut (*ifs).ifa.ifa_addr, family, &mut (*ifs).addr, data, len, index as c_int);
237 }
238 }
239 IFA_BROADCAST => {
240 copy_addr(&mut (*ifs).ifa.ifa_ifu, family, &mut (*ifs).ifu, data, len, index as c_int);
241 }
242 IFA_LOCAL => {
243 if !(*ifs).ifa.ifa_addr.is_null() {
244 (*ifs).ifu = (*ifs).addr;
245 (*ifs).ifa.ifa_ifu = &mut (*ifs).ifu as *mut SockAny as *mut c_void;
246 (*ifs).addr = SockAny { data: [0; 64] };
247 }
248 copy_addr(&mut (*ifs).ifa.ifa_addr, family, &mut (*ifs).addr, data, len, index as c_int);
249 }
250 IFA_LABEL => {
251 if len < (*ifs).name.len() {
252 core::ptr::copy_nonoverlapping(data, (*ifs).name.as_mut_ptr() as *mut u8, len);
253 (*ifs).name[len] = 0;
254 (*ifs).ifa.ifa_name = (*ifs).name.as_mut_ptr();
255 }
256 }
257 _ => {}
258 }
259 });
260 if !(*ifs).ifa.ifa_addr.is_null() {
261 gen_netmask(&mut (*ifs).ifa.ifa_netmask, family, &mut (*ifs).netmask, prefixlen);
262 }
263 }
264
265 if !(*ifs).ifa.ifa_name.is_null() {
266 if (*ctx).first.is_null() {
267 (*ctx).first = &mut (*ifs).ifa;
268 } else {
269 (*(*ctx).last).ifa_next = &mut (*ifs).ifa;
270 }
271 (*ctx).last = &mut (*ifs).ifa;
272 } else {
273 crate::free(ifs as *mut c_void);
274 }
275 0
276}
277
278unsafe fn netlink_enumerate(
279 fd: c_int,
280 seq: u32,
281 msgtype: u16,
282 cb: unsafe fn(*mut IfaCtx, *const NlMsgHdr) -> c_int,
283 ctx: *mut IfaCtx,
284) -> c_int {
285 let mut req = [0u8; 20];
286 let h = req.as_mut_ptr() as *mut NlMsgHdr;
287 (*h).len = 20;
288 (*h).type_ = msgtype;
289 (*h).flags = 0x0301; (*h).seq = seq;
291 (*h).pid = 0;
292 req[16] = 0; let sa = SockAddrNl { family: 16, pad: 0, pid: 0, groups: 0 };
294 let sent = crate::sendto(
295 fd,
296 req.as_ptr() as *const c_void,
297 req.len(),
298 0,
299 &sa as *const SockAddrNl as *const crate::sockaddr,
300 core::mem::size_of::<SockAddrNl>() as u32,
301 );
302 if sent < 0 { return -1; }
303 let mut buf = [0u8; 8192];
304 loop {
305 let n = crate::recv(fd, buf.as_mut_ptr() as *mut c_void, buf.len(), 0);
306 if n <= 0 { return -1; }
307 let mut off = 0usize;
308 while off + 16 <= n as usize {
309 let hdr = buf.as_ptr().add(off) as *const NlMsgHdr;
310 let len = (*hdr).len as usize;
311 if len < 16 || off + len > n as usize { break; }
312 if (*hdr).type_ == NLMSG_DONE { return 0; }
313 if (*hdr).type_ == NLMSG_ERROR { return -1; }
314 let r = cb(ctx, hdr);
315 if r != 0 { return r; }
316 off += (len + 3) & !3usize;
317 }
318 }
319}
320
321#[no_mangle]
322pub unsafe extern "C" fn getifaddrs(ifap: *mut *mut IfAddrs) -> c_int {
323 if ifap.is_null() { ERRNO = EINVAL; return -1; }
324 *ifap = core::ptr::null_mut();
325 let mut ctx: IfaCtx = core::mem::zeroed();
326 let fd = crate::socket(16, 3 | 0x80000, 0); if fd < 0 { return -1; }
328 let mut r = netlink_enumerate(fd, 1, RTM_GETLINK, netlink_msg_to_ifaddr, &mut ctx);
329 if r == 0 {
330 r = netlink_enumerate(fd, 2, RTM_GETADDR, netlink_msg_to_ifaddr, &mut ctx);
331 }
332 crate::close(fd);
333 if r == 0 {
334 *ifap = ctx.first;
335 } else {
336 freeifaddrs(ctx.first);
337 }
338 r
339}
340
341#[no_mangle]
342pub unsafe extern "C" fn freeifaddrs(mut ifa: *mut IfAddrs) {
343 while !ifa.is_null() {
344 let next = (*ifa).ifa_next;
345 crate::free(ifa as *mut c_void);
346 ifa = next;
347 }
348}
349
350unsafe fn sys_read_ifindex(name: *const c_char) -> u32 {
355 let mut path = [0 as c_char; 128];
356 let mut o = 0usize;
357 for &c in b"/sys/class/net/" { path[o] = c as c_char; o += 1; }
358 let n = crate::strlen(name);
359 if o + n + 12 >= path.len() { return 0; }
360 core::ptr::copy_nonoverlapping(name, path.as_mut_ptr().add(o), n);
361 o += n;
362 for &c in b"/ifindex\0" { path[o] = c as c_char; o += 1; }
363 let fd = crate::open(path.as_ptr(), 0, 0);
364 if fd < 0 { return 0; }
365 let mut buf = [0u8; 32];
366 let n = crate::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() - 1);
367 crate::close(fd);
368 if n <= 0 { return 0; }
369 buf[n as usize] = 0;
370 crate::strtoul(buf.as_ptr() as *const c_char, core::ptr::null_mut(), 10) as u32
371}
372
373#[no_mangle]
374pub unsafe extern "C" fn if_nametoindex(ifname: *const c_char) -> c_uint {
375 if ifname.is_null() { return 0; }
376 sys_read_ifindex(ifname)
377}
378
379#[repr(align(8))]
380struct DirentBuf([u8; 4096]);
381
382unsafe fn list_net_names<F: FnMut(*const c_char, usize)>(mut cb: F) {
383 let fd = crate::open(b"/sys/class/net\0".as_ptr() as *const c_char, crate::O_DIRECTORY, 0);
384 if fd < 0 { return; }
385 let mut buf = DirentBuf([0u8; 4096]);
386 let name_off = core::mem::offset_of!(linux_dirent64, d_name);
387 loop {
388 let n = <Arch as Syscalls>::syscall3(
389 SYS_GETDENTS64,
390 fd as i64,
391 buf.0.as_mut_ptr() as i64,
392 buf.0.len() as i64,
393 ) as isize;
394 if n <= 0 { break; }
395 let mut off = 0usize;
396 while off + name_off < n as usize {
397 let ent = buf.0.as_ptr().add(off) as *const linux_dirent64;
398 let reclen = (*ent).d_reclen as usize;
399 if reclen == 0 { break; }
400 let name = buf.0.as_ptr().add(off + name_off) as *const c_char;
401 let len = crate::strlen(name);
402 if len > 0 && *name != b'.' as c_char {
403 cb(name, len);
404 }
405 off += reclen;
406 }
407 }
408 crate::close(fd);
409}
410
411#[no_mangle]
412pub unsafe extern "C" fn if_indextoname(ifindex: c_uint, ifname: *mut c_char) -> *mut c_char {
413 if ifindex == 0 || ifname.is_null() { ERRNO = 19; return core::ptr::null_mut(); }
414 let mut found = false;
415 list_net_names(|name, len| {
416 if found { return; }
417 if sys_read_ifindex(name) == ifindex {
418 core::ptr::copy_nonoverlapping(name, ifname, len + 1);
419 found = true;
420 }
421 });
422 if found { ifname } else { ERRNO = 19; core::ptr::null_mut() }
423}
424
425#[repr(C)]
426pub struct IfNameIndex {
427 pub if_index: c_uint,
428 pub if_name: *mut c_char,
429}
430
431struct NameList {
432 arr: *mut IfNameIndex,
433 count: usize,
434 cap: usize,
435}
436
437#[no_mangle]
438pub unsafe extern "C" fn if_nameindex() -> *mut IfNameIndex {
439 let mut list = NameList {
440 arr: core::ptr::null_mut(),
441 count: 0,
442 cap: 0,
443 };
444 list.arr = crate::calloc(8, core::mem::size_of::<IfNameIndex>()) as *mut IfNameIndex;
445 if list.arr.is_null() { return core::ptr::null_mut(); }
446 list.cap = 8;
447 list_net_names(|name, len| {
448 let idx = sys_read_ifindex(name);
449 if idx == 0 { return; }
450 if list.count + 1 >= list.cap {
451 let ncap = list.cap * 2;
452 let na = crate::realloc(list.arr as *mut c_void, ncap * core::mem::size_of::<IfNameIndex>()) as *mut IfNameIndex;
453 if na.is_null() { return; }
454 list.arr = na;
455 list.cap = ncap;
456 }
457 let copy = crate::malloc(len + 1) as *mut c_char;
458 if copy.is_null() { return; }
459 core::ptr::copy_nonoverlapping(name, copy, len + 1);
460 (*list.arr.add(list.count)).if_index = idx;
461 (*list.arr.add(list.count)).if_name = copy;
462 list.count += 1;
463 });
464 if list.count == 0 {
465 crate::free(list.arr as *mut c_void);
466 return core::ptr::null_mut();
467 }
468 (*list.arr.add(list.count)).if_index = 0;
469 (*list.arr.add(list.count)).if_name = core::ptr::null_mut();
470 list.arr
471}
472
473#[no_mangle]
474pub unsafe extern "C" fn if_freenameindex(mut ptr: *mut IfNameIndex) {
475 if ptr.is_null() { return; }
476 let base = ptr;
477 while (*ptr).if_index != 0 && !(*ptr).if_name.is_null() {
478 crate::free((*ptr).if_name as *mut c_void);
479 ptr = ptr.add(1);
480 }
481 crate::free(base as *mut c_void);
482}