|
1 | | -use std::ffi::CStr; |
2 | | -use std::net::IpAddr; |
3 | | -use std::{net, ptr}; |
4 | | - |
5 | | -use ::std::io::{Error, ErrorKind}; |
6 | | - |
7 | 1 | use crate::ifaces::{Interface, Kind, NextHop}; |
8 | 2 |
|
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 { |
67 | | - pub fn ifu_broadaddr(&mut self) -> *mut nix::sys::socket::sockaddr { |
68 | | - self.data as *mut nix::sys::socket::sockaddr |
69 | | - } |
70 | | - 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" { |
87 | | - pub fn getifaddrs(ifap: *mut *mut ifaddrs) -> ::std::os::raw::c_int; |
88 | | - pub fn freeifaddrs(ifa: *mut ifaddrs) -> ::std::os::raw::c_void; |
89 | | - #[allow(dead_code)] |
90 | | - pub fn if_nametoindex(ifname: *const ::std::os::raw::c_char) -> ::std::os::raw::c_uint; |
91 | | -} |
92 | | - |
93 | | -pub fn nix_socketaddr_to_sockaddr(sa: *mut nix::sys::socket::sockaddr) -> Option<net::SocketAddr> { |
94 | | - if sa.is_null() { |
95 | | - return None; |
| 3 | +use nix::sys::socket::{AddressFamily, SockaddrLike, SockaddrStorage}; |
| 4 | +use std::io::Error; |
| 5 | +use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; |
| 6 | + |
| 7 | +fn ss_to_netsa(ss: &SockaddrStorage) -> Option<SocketAddr> { |
| 8 | + match ss.family() { |
| 9 | + Some(AddressFamily::Inet) => ss.as_sockaddr_in().map(|sin| { |
| 10 | + SocketAddr::V4(SocketAddrV4::new( |
| 11 | + std::net::Ipv4Addr::from(sin.ip()), |
| 12 | + sin.port(), |
| 13 | + )) |
| 14 | + }), |
| 15 | + Some(AddressFamily::Inet6) => ss.as_sockaddr_in6().map(|sin6| { |
| 16 | + SocketAddr::V6(SocketAddrV6::new( |
| 17 | + sin6.ip(), |
| 18 | + sin6.port(), |
| 19 | + sin6.flowinfo(), |
| 20 | + sin6.scope_id(), |
| 21 | + )) |
| 22 | + }), |
| 23 | + _ => None, |
96 | 24 | } |
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 | | - (IpAddr::V4(Into::into(u32::from_be(addr))), port) |
104 | | - } |
105 | | - AF_INET6 => { |
106 | | - let sa: *const nix::sys::socket::sockaddr_in6 = sa as *const nix::libc::sockaddr_in6; |
107 | | - let sa = &unsafe { *sa }; |
108 | | - let (addr, port) = (sa.sin6_addr.s6_addr, sa.sin6_port); |
109 | | - (Into::into(addr), port) |
110 | | - } |
111 | | - _ => return None, |
112 | | - }; |
113 | | - Some(net::SocketAddr::new(addr, port)) |
114 | 25 | } |
115 | 26 |
|
116 | 27 | /// Query the local system for all interface addresses. |
117 | 28 | pub fn ifaces() -> Result<Vec<Interface>, Error> { |
118 | | - let mut ifaddrs_ptr: *mut ifaddrs = ptr::null_mut(); |
119 | | - match unsafe { getifaddrs(&mut ifaddrs_ptr as *mut _) } { |
120 | | - 0 => { |
121 | | - let mut ret = Vec::new(); |
122 | | - let mut item: *mut ifaddrs = ifaddrs_ptr; |
123 | | - loop { |
124 | | - if item.is_null() { |
125 | | - break; |
126 | | - } |
127 | | - let name = String::from_utf8( |
128 | | - unsafe { CStr::from_ptr((*item).ifa_name) } |
129 | | - .to_bytes() |
130 | | - .to_vec(), |
131 | | - ); |
132 | | - unsafe { |
133 | | - if name.is_err() || (*item).ifa_addr.is_null() { |
134 | | - item = (*item).ifa_next; |
135 | | - continue; |
136 | | - } |
137 | | - } |
138 | | - let kind = match unsafe { (*(*item).ifa_addr).sa_family as i32 } { |
139 | | - AF_INET => Some(Kind::Ipv4), |
140 | | - AF_INET6 => Some(Kind::Ipv6), |
141 | | - AF_PACKET => Some(Kind::Packet), |
142 | | - AF_LINK => Some(Kind::Link), |
143 | | - code => Some(Kind::Unknow(code)), |
144 | | - }; |
145 | | - if kind.is_none() { |
146 | | - item = unsafe { (*item).ifa_next }; |
147 | | - continue; |
148 | | - } |
149 | | - |
150 | | - let addr = nix_socketaddr_to_sockaddr(unsafe { (*item).ifa_addr }); |
151 | | - let mask = nix_socketaddr_to_sockaddr(unsafe { (*item).ifa_netmask }); |
152 | | - let hop = unsafe { |
153 | | - if (*item).ifa_flags & SiocgifFlags::Broadcast as ::std::os::raw::c_uint |
154 | | - == SiocgifFlags::Broadcast as ::std::os::raw::c_uint |
155 | | - { |
156 | | - nix_socketaddr_to_sockaddr((*item).ifa_ifu.ifu_broadaddr()) |
157 | | - .map(NextHop::Broadcast) |
158 | | - } else { |
159 | | - nix_socketaddr_to_sockaddr((*item).ifa_ifu.ifu_dstaddr()) |
160 | | - .map(NextHop::Destination) |
161 | | - } |
162 | | - }; |
163 | | - |
164 | | - if let Some(kind) = kind { |
165 | | - match kind { |
166 | | - Kind::Unknow(_) => {} |
167 | | - _ => { |
168 | | - ret.push(Interface { |
169 | | - name: name.unwrap(), |
170 | | - kind, |
171 | | - addr, |
172 | | - mask, |
173 | | - hop, |
174 | | - }); |
175 | | - } |
176 | | - }; |
177 | | - }; |
178 | | - |
179 | | - item = unsafe { (*item).ifa_next }; |
180 | | - } |
181 | | - unsafe { freeifaddrs(ifaddrs_ptr) }; |
182 | | - Ok(ret) |
| 29 | + let mut ret = Vec::new(); |
| 30 | + for ifa in nix::ifaddrs::getifaddrs()? { |
| 31 | + if let Some(kind) = ifa |
| 32 | + .address |
| 33 | + .as_ref() |
| 34 | + .and_then(SockaddrStorage::family) |
| 35 | + .and_then(|af| match af { |
| 36 | + AddressFamily::Inet => Some(Kind::Ipv4), |
| 37 | + AddressFamily::Inet6 => Some(Kind::Ipv6), |
| 38 | + #[cfg(any( |
| 39 | + target_os = "android", |
| 40 | + target_os = "linux", |
| 41 | + target_os = "illumos", |
| 42 | + target_os = "fuchsia", |
| 43 | + target_os = "solaris" |
| 44 | + ))] |
| 45 | + AddressFamily::Packet => Some(Kind::Packet), |
| 46 | + #[cfg(any( |
| 47 | + target_os = "dragonfly", |
| 48 | + target_os = "freebsd", |
| 49 | + target_os = "ios", |
| 50 | + target_os = "macos", |
| 51 | + target_os = "illumos", |
| 52 | + target_os = "netbsd", |
| 53 | + target_os = "openbsd" |
| 54 | + ))] |
| 55 | + AddressFamily::Link => Some(Kind::Link), |
| 56 | + _ => None, |
| 57 | + }) |
| 58 | + { |
| 59 | + let name = ifa.interface_name; |
| 60 | + let dst = ifa.destination.as_ref().and_then(ss_to_netsa); |
| 61 | + let broadcast = ifa.broadcast.as_ref().and_then(ss_to_netsa); |
| 62 | + let hop = dst |
| 63 | + .map(NextHop::Destination) |
| 64 | + .or(broadcast.map(NextHop::Broadcast)); |
| 65 | + let addr = ifa.address.as_ref().and_then(ss_to_netsa); |
| 66 | + let mask = ifa.netmask.as_ref().and_then(ss_to_netsa); |
| 67 | + |
| 68 | + ret.push(Interface { |
| 69 | + name, |
| 70 | + kind, |
| 71 | + addr, |
| 72 | + mask, |
| 73 | + hop, |
| 74 | + }); |
183 | 75 | } |
184 | | - _ => Err(Error::new(ErrorKind::Other, "Oh, no ...")), // Err(nix::errno::Errno::last()); |
185 | 76 | } |
| 77 | + |
| 78 | + Ok(ret) |
186 | 79 | } |
0 commit comments