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            let addr = addr.to_canonical();
328            Ok(IFACES
329                .iter()
330                .filter(|iface| iface.ips.iter().any(|ipnet| ipnet.ip() == addr))
331                .map(|iface| iface.name.clone())
332                .collect::<Vec<String>>())
333        }
334    }
335    #[cfg(windows)]
336    {
337        let mut result = vec![];
338        unsafe {
339            use winapi::um::iptypes::IP_ADAPTER_ADDRESSES_LH;
340
341            use crate::ffi;
342
343            let buffer = get_adapters_addresses(winapi::shared::ws2def::AF_UNSPEC)?;
344
345            if addr.is_unspecified() {
346                let mut next_iface = (buffer.as_ptr() as *mut IP_ADAPTER_ADDRESSES_LH).as_ref();
347                while let Some(iface) = next_iface {
348                    result.push(ffi::pstr_to_string(iface.AdapterName));
349                    next_iface = iface.Next.as_ref();
350                }
351            } else {
352                let addr = addr.to_canonical();
353                let mut next_iface = (buffer.as_ptr() as *mut IP_ADAPTER_ADDRESSES_LH).as_ref();
354                while let Some(iface) = next_iface {
355                    let mut next_ucast_addr = iface.FirstUnicastAddress.as_ref();
356                    while let Some(ucast_addr) = next_ucast_addr {
357                        if let Ok(ifaddr) = ffi::win::sockaddr_to_addr(ucast_addr.Address) {
358                            if ifaddr.ip() == addr {
359                                result.push(ffi::pstr_to_string(iface.AdapterName));
360                            }
361                        }
362                        next_ucast_addr = ucast_addr.Next.as_ref();
363                    }
364                    next_iface = iface.Next.as_ref();
365                }
366            }
367        }
368        Ok(result)
369    }
370}
371
372pub fn get_ipv4_ipaddrs(interface: Option<&str>) -> Vec<IpAddr> {
373    get_local_addresses(interface)
374        .unwrap_or_else(|_| vec![])
375        .drain(..)
376        .filter_map(|x| match x {
377            IpAddr::V4(a) => Some(a),
378            IpAddr::V6(_) => None,
379        })
380        .filter(|x| !x.is_loopback() && !x.is_multicast())
381        .map(IpAddr::V4)
382        .collect()
383}
384
385pub fn get_ipv6_ipaddrs(interface: Option<&str>) -> Vec<IpAddr> {
386    const fn is_unicast_link_local(addr: &Ipv6Addr) -> bool {
387        (addr.segments()[0] & 0xffc0) == 0xfe80
388    }
389
390    let ipaddrs = get_local_addresses(interface).unwrap_or_else(|_| vec![]);
391
392    // Get first all IPv4 addresses
393    let ipv4_iter = ipaddrs
394        .iter()
395        .filter_map(|x| match x {
396            IpAddr::V4(a) => Some(a),
397            IpAddr::V6(_) => None,
398        })
399        .filter(|x| {
400            !x.is_loopback() && !x.is_link_local() && !x.is_multicast() && !x.is_broadcast()
401        });
402
403    // Get next all IPv6 addresses
404    let ipv6_iter = ipaddrs.iter().filter_map(|x| match x {
405        IpAddr::V4(_) => None,
406        IpAddr::V6(a) => Some(a),
407    });
408
409    // First match non-linklocal IPv6 addresses
410    let nll_ipv6_addrs = ipv6_iter
411        .clone()
412        .filter(|x| !x.is_loopback() && !x.is_multicast() && !is_unicast_link_local(x))
413        .map(|x| IpAddr::V6(*x));
414
415    // Second match public IPv4 addresses
416    let pub_ipv4_addrs = ipv4_iter
417        .clone()
418        .filter(|x| !x.is_private())
419        .map(|x| IpAddr::V4(*x));
420
421    // Third match linklocal IPv6 addresses
422    let yll_ipv6_addrs = ipv6_iter
423        .filter(|x| !x.is_loopback() && !x.is_multicast() && is_unicast_link_local(x))
424        .map(|x| IpAddr::V6(*x));
425
426    // Fourth match private IPv4 addresses
427    let priv_ipv4_addrs = ipv4_iter
428        .clone()
429        .filter(|x| x.is_private())
430        .map(|x| IpAddr::V4(*x));
431
432    // Extend
433    nll_ipv6_addrs
434        .chain(pub_ipv4_addrs)
435        .chain(yll_ipv6_addrs)
436        .chain(priv_ipv4_addrs)
437        .collect()
438}
439
440#[cfg(any(target_os = "linux", target_os = "android"))]
441pub fn set_bind_to_device_tcp_socket(socket: &TcpSocket, iface: &str) -> ZResult<()> {
442    socket.bind_device(Some(iface.as_bytes()))?;
443    Ok(())
444}
445
446#[cfg(any(target_os = "linux", target_os = "android"))]
447pub fn set_bind_to_device_udp_socket(socket: &UdpSocket, iface: &str) -> ZResult<()> {
448    socket.bind_device(Some(iface.as_bytes()))?;
449    Ok(())
450}
451
452#[cfg(any(target_os = "macos", target_os = "windows"))]
453pub fn set_bind_to_device_tcp_socket(socket: &TcpSocket, iface: &str) -> ZResult<()> {
454    tracing::warn!("Binding the socket {socket:?} to the interface {iface} is not supported on macOS and Windows");
455    Ok(())
456}
457
458#[cfg(any(target_os = "macos", target_os = "windows"))]
459pub fn set_bind_to_device_udp_socket(socket: &UdpSocket, iface: &str) -> ZResult<()> {
460    tracing::warn!("Binding the socket {socket:?} to the interface {iface} is not supported on macOS and Windows");
461    Ok(())
462}