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