Skip to main content

c/
ifaddrs.rs

1// getifaddrs() via rtnetlink, plus if_nametoindex/if_indextoname/
2// if_nameindex backed by /sys/class/net.
3
4#[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; // AF_PACKET
150    *(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; // NLM_F_DUMP | NLM_F_REQUEST
290    (*h).seq = seq;
291    (*h).pid = 0;
292    req[16] = 0; // rtgen_family = AF_UNSPEC
293    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); // AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC
327    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
350// ------------------------------------------------------------------
351// Interface names via /sys/class/net
352// ------------------------------------------------------------------
353
354unsafe 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}