|
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