zenoh_util/net/
mod.rs

1//
2// Copyright (c) 2023 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14use 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
130/// Get the network interface to bind the UDP sending port to when not specified by user
131pub 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        // On windows, bind to [::], the system will select the default interface
151        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
204/// Get the network interface to bind the UDP sending port to when not specified by user
205pub 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        // On windows, bind to [::] or [::], the system will select the default interface
224        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    // Get first all IPv4 addresses
391    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    // Get next all IPv6 addresses
402    let ipv6_iter = ipaddrs.iter().filter_map(|x| match x {
403        IpAddr::V4(_) => None,
404        IpAddr::V6(a) => Some(a),
405    });
406
407    // First match non-linklocal IPv6 addresses
408    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    // Second match public IPv4 addresses
414    let pub_ipv4_addrs = ipv4_iter
415        .clone()
416        .filter(|x| !x.is_private())
417        .map(|x| IpAddr::V4(*x));
418
419    // Third match linklocal IPv6 addresses
420    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    // Fourth match private IPv4 addresses
425    let priv_ipv4_addrs = ipv4_iter
426        .clone()
427        .filter(|x| x.is_private())
428        .map(|x| IpAddr::V4(*x));
429
430    // Extend
431    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}