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 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 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 let ipv6_iter = ipaddrs.iter().filter_map(|x| match x {
405 IpAddr::V4(_) => None,
406 IpAddr::V6(a) => Some(a),
407 });
408
409 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 let pub_ipv4_addrs = ipv4_iter
417 .clone()
418 .filter(|x| !x.is_private())
419 .map(|x| IpAddr::V4(*x));
420
421 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 let priv_ipv4_addrs = ipv4_iter
428 .clone()
429 .filter(|x| x.is_private())
430 .map(|x| IpAddr::V4(*x));
431
432 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}