1use std::net::{IpAddr, Ipv6Addr};
15
16#[cfg(unix)]
17use lazy_static::lazy_static;
18#[cfg(unix)]
19use pnet_datalink::NetworkInterface;
20use tokio::net::{TcpSocket, UdpSocket};
21use zenoh_core::zconfigurable;
22#[cfg(unix)]
23use zenoh_result::zerror;
24use zenoh_result::{bail, ZResult};
25
26zconfigurable! {
27 static ref WINDOWS_GET_ADAPTERS_ADDRESSES_BUF_SIZE: u32 = 8192;
28 static ref WINDOWS_GET_ADAPTERS_ADDRESSES_MAX_RETRIES: u32 = 3;
29}
30
31#[cfg(unix)]
32lazy_static! {
33 static ref IFACES: Vec<NetworkInterface> = pnet_datalink::interfaces();
34}
35
36#[cfg(windows)]
37unsafe fn get_adapters_addresses(af_spec: i32) -> ZResult<Vec<u8>> {
38 use winapi::um::iptypes::IP_ADAPTER_ADDRESSES_LH;
39
40 let mut ret;
41 let mut retries = 0;
42 let mut size: u32 = *WINDOWS_GET_ADAPTERS_ADDRESSES_BUF_SIZE;
43 let mut buffer: Vec<u8>;
44 loop {
45 buffer = Vec::with_capacity(size as usize);
46 ret = winapi::um::iphlpapi::GetAdaptersAddresses(
47 af_spec.try_into().unwrap(),
48 0,
49 std::ptr::null_mut(),
50 buffer.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH,
51 &mut size,
52 );
53 if ret != winapi::shared::winerror::ERROR_BUFFER_OVERFLOW {
54 break;
55 }
56 if retries >= *WINDOWS_GET_ADAPTERS_ADDRESSES_MAX_RETRIES {
57 break;
58 }
59 retries += 1;
60 }
61
62 if ret != 0 {
63 bail!("GetAdaptersAddresses returned {}", ret)
64 }
65
66 Ok(buffer)
67}
68pub fn get_interface(name: &str) -> ZResult<Option<IpAddr>> {
69 #[cfg(unix)]
70 {
71 for iface in IFACES.iter() {
72 if iface.name == name {
73 for ifaddr in &iface.ips {
74 if ifaddr.is_ipv4() {
75 return Ok(Some(ifaddr.ip()));
76 }
77 }
78 }
79 for ifaddr in &iface.ips {
80 if ifaddr.ip().to_string() == name {
81 return Ok(Some(ifaddr.ip()));
82 }
83 }
84 }
85 Ok(None)
86 }
87
88 #[cfg(windows)]
89 {
90 unsafe {
91 use winapi::um::iptypes::IP_ADAPTER_ADDRESSES_LH;
92
93 use crate::ffi;
94
95 let buffer = get_adapters_addresses(winapi::shared::ws2def::AF_INET)?;
96
97 let mut next_iface = (buffer.as_ptr() as *mut IP_ADAPTER_ADDRESSES_LH).as_ref();
98 while let Some(iface) = next_iface {
99 if name == ffi::pstr_to_string(iface.AdapterName)
100 || name == ffi::pwstr_to_string(iface.FriendlyName)
101 || name == ffi::pwstr_to_string(iface.Description)
102 {
103 let mut next_ucast_addr = iface.FirstUnicastAddress.as_ref();
104 while let Some(ucast_addr) = next_ucast_addr {
105 if let Ok(ifaddr) = ffi::win::sockaddr_to_addr(ucast_addr.Address) {
106 if ifaddr.is_ipv4() {
107 return Ok(Some(ifaddr.ip()));
108 }
109 }
110 next_ucast_addr = ucast_addr.Next.as_ref();
111 }
112 }
113
114 let mut next_ucast_addr = iface.FirstUnicastAddress.as_ref();
115 while let Some(ucast_addr) = next_ucast_addr {
116 if let Ok(ifaddr) = ffi::win::sockaddr_to_addr(ucast_addr.Address) {
117 if ifaddr.ip().to_string() == name {
118 return Ok(Some(ifaddr.ip()));
119 }
120 }
121 next_ucast_addr = ucast_addr.Next.as_ref();
122 }
123 next_iface = iface.Next.as_ref();
124 }
125 Ok(None)
126 }
127 }
128}
129
130pub fn get_multicast_interfaces() -> Vec<IpAddr> {
132 #[cfg(unix)]
133 {
134 IFACES
135 .iter()
136 .filter_map(|iface| {
137 if iface.is_up() && iface.is_running() && iface.is_multicast() {
138 for ipaddr in &iface.ips {
139 if ipaddr.is_ipv4() {
140 return Some(ipaddr.ip());
141 }
142 }
143 }
144 None
145 })
146 .collect()
147 }
148 #[cfg(windows)]
149 {
150 vec![IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED)]
152 }
153}
154
155pub fn get_local_addresses(interface: Option<&str>) -> ZResult<Vec<IpAddr>> {
156 #[cfg(unix)]
157 {
158 Ok(IFACES
159 .iter()
160 .filter(|iface| {
161 if let Some(interface) = interface.as_ref() {
162 if iface.name != *interface {
163 return false;
164 }
165 }
166 iface.is_up() && iface.is_running()
167 })
168 .flat_map(|iface| iface.ips.clone())
169 .map(|ipnet| ipnet.ip())
170 .collect())
171 }
172
173 #[cfg(windows)]
174 {
175 unsafe {
176 use winapi::um::iptypes::IP_ADAPTER_ADDRESSES_LH;
177
178 use crate::ffi;
179
180 let buffer = get_adapters_addresses(winapi::shared::ws2def::AF_UNSPEC)?;
181
182 let mut result = vec![];
183 let mut next_iface = (buffer.as_ptr() as *mut IP_ADAPTER_ADDRESSES_LH).as_ref();
184 while let Some(iface) = next_iface {
185 if let Some(interface) = interface.as_ref() {
186 if ffi::pstr_to_string(iface.AdapterName) != *interface {
187 continue;
188 }
189 }
190 let mut next_ucast_addr = iface.FirstUnicastAddress.as_ref();
191 while let Some(ucast_addr) = next_ucast_addr {
192 if let Ok(ifaddr) = ffi::win::sockaddr_to_addr(ucast_addr.Address) {
193 result.push(ifaddr.ip());
194 }
195 next_ucast_addr = ucast_addr.Next.as_ref();
196 }
197 next_iface = iface.Next.as_ref();
198 }
199 Ok(result)
200 }
201 }
202}
203
204pub fn get_unicast_addresses_of_multicast_interfaces() -> Vec<IpAddr> {
206 #[cfg(unix)]
207 {
208 IFACES
209 .iter()
210 .filter(|iface| iface.is_up() && iface.is_running() && iface.is_multicast())
211 .flat_map(|iface| {
212 iface
213 .ips
214 .iter()
215 .filter(|ip| !ip.ip().is_multicast())
216 .map(|x| x.ip())
217 .collect::<Vec<IpAddr>>()
218 })
219 .collect()
220 }
221 #[cfg(windows)]
222 {
223 vec![]
225 }
226}
227
228pub fn get_unicast_addresses_of_interface(name: &str) -> ZResult<Vec<IpAddr>> {
229 #[cfg(unix)]
230 {
231 match IFACES.iter().find(|iface| iface.name == name) {
232 Some(iface) => {
233 if !iface.is_up() {
234 bail!("Interface {name} is not up");
235 }
236 if !iface.is_running() {
237 bail!("Interface {name} is not running");
238 }
239 let addrs = iface
240 .ips
241 .iter()
242 .filter(|ip| !ip.ip().is_multicast())
243 .map(|x| x.ip())
244 .collect::<Vec<IpAddr>>();
245 Ok(addrs)
246 }
247 None => bail!("Interface {name} not found"),
248 }
249 }
250
251 #[cfg(windows)]
252 {
253 unsafe {
254 use winapi::um::iptypes::IP_ADAPTER_ADDRESSES_LH;
255
256 use crate::ffi;
257
258 let buffer = get_adapters_addresses(winapi::shared::ws2def::AF_INET)?;
259
260 let mut addrs = vec![];
261 let mut next_iface = (buffer.as_ptr() as *mut IP_ADAPTER_ADDRESSES_LH).as_ref();
262 while let Some(iface) = next_iface {
263 if name == ffi::pstr_to_string(iface.AdapterName)
264 || name == ffi::pwstr_to_string(iface.FriendlyName)
265 || name == ffi::pwstr_to_string(iface.Description)
266 {
267 let mut next_ucast_addr = iface.FirstUnicastAddress.as_ref();
268 while let Some(ucast_addr) = next_ucast_addr {
269 if let Ok(ifaddr) = ffi::win::sockaddr_to_addr(ucast_addr.Address) {
270 addrs.push(ifaddr.ip());
271 }
272 next_ucast_addr = ucast_addr.Next.as_ref();
273 }
274 }
275 next_iface = iface.Next.as_ref();
276 }
277 Ok(addrs)
278 }
279 }
280}
281
282pub fn get_index_of_interface(addr: IpAddr) -> ZResult<u32> {
283 #[cfg(unix)]
284 {
285 IFACES
286 .iter()
287 .find(|iface| iface.ips.iter().any(|ipnet| ipnet.ip() == addr))
288 .map(|iface| iface.index)
289 .ok_or_else(|| zerror!("No interface found with address {addr}").into())
290 }
291 #[cfg(windows)]
292 {
293 unsafe {
294 use winapi::um::iptypes::IP_ADAPTER_ADDRESSES_LH;
295
296 use crate::ffi;
297
298 let buffer = get_adapters_addresses(winapi::shared::ws2def::AF_INET)?;
299
300 let mut next_iface = (buffer.as_ptr() as *mut IP_ADAPTER_ADDRESSES_LH).as_ref();
301 while let Some(iface) = next_iface {
302 let mut next_ucast_addr = iface.FirstUnicastAddress.as_ref();
303 while let Some(ucast_addr) = next_ucast_addr {
304 if let Ok(ifaddr) = ffi::win::sockaddr_to_addr(ucast_addr.Address) {
305 if ifaddr.ip() == addr {
306 return Ok(iface.Ipv6IfIndex);
307 }
308 }
309 next_ucast_addr = ucast_addr.Next.as_ref();
310 }
311 next_iface = iface.Next.as_ref();
312 }
313 bail!("No interface found with address {addr}")
314 }
315 }
316}
317
318pub fn get_interface_names_by_addr(addr: IpAddr) -> ZResult<Vec<String>> {
319 #[cfg(unix)]
320 {
321 if addr.is_unspecified() {
322 Ok(IFACES
323 .iter()
324 .map(|iface| iface.name.clone())
325 .collect::<Vec<String>>())
326 } else {
327 Ok(IFACES
328 .iter()
329 .filter(|iface| iface.ips.iter().any(|ipnet| ipnet.ip() == addr))
330 .map(|iface| iface.name.clone())
331 .collect::<Vec<String>>())
332 }
333 }
334 #[cfg(windows)]
335 {
336 let mut result = vec![];
337 unsafe {
338 use winapi::um::iptypes::IP_ADAPTER_ADDRESSES_LH;
339
340 use crate::ffi;
341
342 let buffer = get_adapters_addresses(winapi::shared::ws2def::AF_UNSPEC)?;
343
344 if addr.is_unspecified() {
345 let mut next_iface = (buffer.as_ptr() as *mut IP_ADAPTER_ADDRESSES_LH).as_ref();
346 while let Some(iface) = next_iface {
347 result.push(ffi::pstr_to_string(iface.AdapterName));
348 next_iface = iface.Next.as_ref();
349 }
350 } else {
351 let mut next_iface = (buffer.as_ptr() as *mut IP_ADAPTER_ADDRESSES_LH).as_ref();
352 while let Some(iface) = next_iface {
353 let mut next_ucast_addr = iface.FirstUnicastAddress.as_ref();
354 while let Some(ucast_addr) = next_ucast_addr {
355 if let Ok(ifaddr) = ffi::win::sockaddr_to_addr(ucast_addr.Address) {
356 if ifaddr.ip() == addr {
357 result.push(ffi::pstr_to_string(iface.AdapterName));
358 }
359 }
360 next_ucast_addr = ucast_addr.Next.as_ref();
361 }
362 next_iface = iface.Next.as_ref();
363 }
364 }
365 }
366 Ok(result)
367 }
368}
369
370pub fn get_ipv4_ipaddrs(interface: Option<&str>) -> Vec<IpAddr> {
371 get_local_addresses(interface)
372 .unwrap_or_else(|_| vec![])
373 .drain(..)
374 .filter_map(|x| match x {
375 IpAddr::V4(a) => Some(a),
376 IpAddr::V6(_) => None,
377 })
378 .filter(|x| !x.is_loopback() && !x.is_multicast())
379 .map(IpAddr::V4)
380 .collect()
381}
382
383pub fn get_ipv6_ipaddrs(interface: Option<&str>) -> Vec<IpAddr> {
384 const fn is_unicast_link_local(addr: &Ipv6Addr) -> bool {
385 (addr.segments()[0] & 0xffc0) == 0xfe80
386 }
387
388 let ipaddrs = get_local_addresses(interface).unwrap_or_else(|_| vec![]);
389
390 let ipv4_iter = ipaddrs
392 .iter()
393 .filter_map(|x| match x {
394 IpAddr::V4(a) => Some(a),
395 IpAddr::V6(_) => None,
396 })
397 .filter(|x| {
398 !x.is_loopback() && !x.is_link_local() && !x.is_multicast() && !x.is_broadcast()
399 });
400
401 let ipv6_iter = ipaddrs.iter().filter_map(|x| match x {
403 IpAddr::V4(_) => None,
404 IpAddr::V6(a) => Some(a),
405 });
406
407 let nll_ipv6_addrs = ipv6_iter
409 .clone()
410 .filter(|x| !x.is_loopback() && !x.is_multicast() && !is_unicast_link_local(x))
411 .map(|x| IpAddr::V6(*x));
412
413 let pub_ipv4_addrs = ipv4_iter
415 .clone()
416 .filter(|x| !x.is_private())
417 .map(|x| IpAddr::V4(*x));
418
419 let yll_ipv6_addrs = ipv6_iter
421 .filter(|x| !x.is_loopback() && !x.is_multicast() && is_unicast_link_local(x))
422 .map(|x| IpAddr::V6(*x));
423
424 let priv_ipv4_addrs = ipv4_iter
426 .clone()
427 .filter(|x| x.is_private())
428 .map(|x| IpAddr::V4(*x));
429
430 nll_ipv6_addrs
432 .chain(pub_ipv4_addrs)
433 .chain(yll_ipv6_addrs)
434 .chain(priv_ipv4_addrs)
435 .collect()
436}
437
438#[cfg(any(target_os = "linux", target_os = "android"))]
439pub fn set_bind_to_device_tcp_socket(socket: &TcpSocket, iface: &str) -> ZResult<()> {
440 socket.bind_device(Some(iface.as_bytes()))?;
441 Ok(())
442}
443
444#[cfg(any(target_os = "linux", target_os = "android"))]
445pub fn set_bind_to_device_udp_socket(socket: &UdpSocket, iface: &str) -> ZResult<()> {
446 socket.bind_device(Some(iface.as_bytes()))?;
447 Ok(())
448}
449
450#[cfg(any(target_os = "macos", target_os = "windows"))]
451pub fn set_bind_to_device_tcp_socket(socket: &TcpSocket, iface: &str) -> ZResult<()> {
452 tracing::warn!("Binding the socket {socket:?} to the interface {iface} is not supported on macOS and Windows");
453 Ok(())
454}
455
456#[cfg(any(target_os = "macos", target_os = "windows"))]
457pub fn set_bind_to_device_udp_socket(socket: &UdpSocket, iface: &str) -> ZResult<()> {
458 tracing::warn!("Binding the socket {socket:?} to the interface {iface} is not supported on macOS and Windows");
459 Ok(())
460}