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