xref: /webrtc/util/src/ifaces/ffi/unix/mod.rs (revision ffe74184)
1 use ::std::io::{Error, ErrorKind};
2 use std::ffi::CStr;
3 use std::{net, ptr};
4 
5 use std::net::IpAddr;
6 
7 use crate::ifaces::{Interface, Kind, NextHop};
8 
9 // https://github.com/Exa-Networks/exaproxy/blob/master/lib/exaproxy/util/interfaces.py
10 
11 pub const AF_INET: i32 = nix::sys::socket::AddressFamily::Inet as i32;
12 pub const AF_INET6: i32 = nix::sys::socket::AddressFamily::Inet6 as i32;
13 
14 #[cfg(any(
15     target_os = "macos",
16     target_os = "ios",
17     target_os = "freebsd",
18     target_os = "openbsd",
19     target_os = "netbsd"
20 ))]
21 pub const AF_LINK: i32 = nix::libc::AF_LINK;
22 #[cfg(any(
23     target_os = "macos",
24     target_os = "ios",
25     target_os = "freebsd",
26     target_os = "openbsd",
27     target_os = "netbsd"
28 ))]
29 pub const AF_PACKET: i32 = -1;
30 
31 #[cfg(any(target_os = "linux", target_os = "android"))]
32 pub const AF_LINK: i32 = -1;
33 #[cfg(any(target_os = "linux", target_os = "android"))]
34 pub const AF_PACKET: i32 = nix::libc::AF_PACKET;
35 
36 #[allow(dead_code, non_camel_case_types)]
37 #[repr(C)]
38 pub enum SiocgifFlags {
39     Up = 0x1,           /* Interface is up.  */
40     Broadcast = 0x2,    /* Broadcast address valid.  */
41     Debug = 0x4,        /* Turn on debugging.  */
42     Loopback = 0x8,     /* Is a loopback net.  */
43     Pointopoint = 0x10, /* Interface is point-to-point link.  */
44     Notrailers = 0x20,  /* Avoid use of trailers.  */
45     Running = 0x40,     /* Resources allocated.  */
46     Noarp = 0x80,       /* No address resolution protocol.  */
47     Promisc = 0x100,    /* Receive all packets.  */
48 
49     /* Not supported */
50     Allmulti = 0x200, /* Receive all multicast packets.  */
51 
52     Master = 0x400, /* Master of a load balancer.  */
53     Slave = 0x800,  /* Slave of a load balancer.  */
54 
55     Multicast = 0x1000, /* Supports multicast.  */
56 
57     Portsel = 0x2000,   /* Can set media type.  */
58     Automedia = 0x4000, /* Auto media select active.  */
59     Dynamic = 0x8000,   /* Dialup device with changing addresses.  */
60 }
61 
62 #[repr(C)]
63 pub struct union_ifa_ifu {
64     pub data: *mut ::std::os::raw::c_void,
65 }
66 impl union_ifa_ifu {
ifu_broadaddr(&mut self) -> *mut nix::sys::socket::sockaddr67     pub fn ifu_broadaddr(&mut self) -> *mut nix::sys::socket::sockaddr {
68         self.data as *mut nix::sys::socket::sockaddr
69     }
ifu_dstaddr(&mut self) -> *mut nix::sys::socket::sockaddr70     pub fn ifu_dstaddr(&mut self) -> *mut nix::sys::socket::sockaddr {
71         self.data as *mut nix::sys::socket::sockaddr
72     }
73 }
74 
75 #[repr(C)]
76 pub struct ifaddrs {
77     pub ifa_next: *mut ifaddrs,
78     pub ifa_name: *mut ::std::os::raw::c_char,
79     pub ifa_flags: ::std::os::raw::c_uint,
80     pub ifa_addr: *mut nix::sys::socket::sockaddr,
81     pub ifa_netmask: *mut nix::sys::socket::sockaddr,
82     pub ifa_ifu: union_ifa_ifu,
83     pub ifa_data: *mut ::std::os::raw::c_void,
84 }
85 
86 extern "C" {
getifaddrs(ifap: *mut *mut ifaddrs) -> ::std::os::raw::c_int87     pub fn getifaddrs(ifap: *mut *mut ifaddrs) -> ::std::os::raw::c_int;
freeifaddrs(ifa: *mut ifaddrs) -> ::std::os::raw::c_void88     pub fn freeifaddrs(ifa: *mut ifaddrs) -> ::std::os::raw::c_void;
89     #[allow(dead_code)]
if_nametoindex(ifname: *const ::std::os::raw::c_char) -> ::std::os::raw::c_uint90     pub fn if_nametoindex(ifname: *const ::std::os::raw::c_char) -> ::std::os::raw::c_uint;
91 }
92 
nix_socketaddr_to_sockaddr(sa: *mut nix::sys::socket::sockaddr) -> Option<net::SocketAddr>93 pub fn nix_socketaddr_to_sockaddr(sa: *mut nix::sys::socket::sockaddr) -> Option<net::SocketAddr> {
94     if sa.is_null() {
95         return None;
96     }
97 
98     let (addr, port) = match unsafe { *sa }.sa_family as i32 {
99         AF_INET => {
100             let sa: *const nix::sys::socket::sockaddr_in = sa as *const nix::libc::sockaddr_in;
101             let sa = &unsafe { *sa };
102             let (addr, port) = (sa.sin_addr.s_addr, sa.sin_port);
103             (
104                 IpAddr::V4(net::Ipv4Addr::new(
105                     (addr & 0x000000FF) as u8,
106                     ((addr & 0x0000FF00) >> 8) as u8,
107                     ((addr & 0x00FF0000) >> 16) as u8,
108                     ((addr & 0xFF000000) >> 24) as u8,
109                 )),
110                 port,
111             )
112         }
113         AF_INET6 => {
114             let sa: *const nix::sys::socket::sockaddr_in6 = sa as *const nix::libc::sockaddr_in6;
115             let sa = &unsafe { *sa };
116             let (addr, port) = (sa.sin6_addr.s6_addr, sa.sin6_port);
117             (
118                 IpAddr::V6(net::Ipv6Addr::new(
119                     addr[0] as u16,
120                     addr[1] as u16,
121                     addr[2] as u16,
122                     addr[3] as u16,
123                     addr[4] as u16,
124                     addr[5] as u16,
125                     addr[6] as u16,
126                     addr[7] as u16,
127                 )),
128                 port,
129             )
130         }
131         _ => return None,
132     };
133     Some(net::SocketAddr::new(addr, port))
134 }
135 
136 /// Query the local system for all interface addresses.
ifaces() -> Result<Vec<Interface>, Error>137 pub fn ifaces() -> Result<Vec<Interface>, Error> {
138     let mut ifaddrs_ptr: *mut ifaddrs = ptr::null_mut();
139     match unsafe { getifaddrs(&mut ifaddrs_ptr as *mut _) } {
140         0 => {
141             let mut ret = Vec::new();
142             let mut item: *mut ifaddrs = ifaddrs_ptr;
143             loop {
144                 if item.is_null() {
145                     break;
146                 }
147                 let name = String::from_utf8(
148                     unsafe { CStr::from_ptr((*item).ifa_name) }
149                         .to_bytes()
150                         .to_vec(),
151                 );
152                 unsafe {
153                     if name.is_err() || (*item).ifa_addr.is_null() {
154                         item = (*item).ifa_next;
155                         continue;
156                     }
157                 }
158                 let kind = match unsafe { (*(*item).ifa_addr).sa_family as i32 } {
159                     AF_INET => Some(Kind::Ipv4),
160                     AF_INET6 => Some(Kind::Ipv6),
161                     AF_PACKET => Some(Kind::Packet),
162                     AF_LINK => Some(Kind::Link),
163                     code => Some(Kind::Unknow(code)),
164                 };
165                 if kind.is_none() {
166                     item = unsafe { (*item).ifa_next };
167                     continue;
168                 }
169 
170                 let addr = nix_socketaddr_to_sockaddr(unsafe { (*item).ifa_addr });
171                 let mask = nix_socketaddr_to_sockaddr(unsafe { (*item).ifa_netmask });
172                 let hop = unsafe {
173                     if (*item).ifa_flags & SiocgifFlags::Broadcast as ::std::os::raw::c_uint
174                         == SiocgifFlags::Broadcast as ::std::os::raw::c_uint
175                     {
176                         nix_socketaddr_to_sockaddr((*item).ifa_ifu.ifu_broadaddr())
177                             .map(NextHop::Broadcast)
178                     } else {
179                         nix_socketaddr_to_sockaddr((*item).ifa_ifu.ifu_dstaddr())
180                             .map(NextHop::Destination)
181                     }
182                 };
183 
184                 if let Some(kind) = kind {
185                     match kind {
186                         Kind::Unknow(_) => {}
187                         _ => {
188                             ret.push(Interface {
189                                 name: name.unwrap(),
190                                 kind,
191                                 addr,
192                                 mask,
193                                 hop,
194                             });
195                         }
196                     };
197                 };
198 
199                 item = unsafe { (*item).ifa_next };
200             }
201             unsafe { freeifaddrs(ifaddrs_ptr) };
202             Ok(ret)
203         }
204         _ => Err(Error::new(ErrorKind::Other, "Oh, no ...")), // Err(nix::errno::Errno::last());
205     }
206 }
207