zenoh_link_udp/
lib.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//
14
15//! ⚠️ WARNING ⚠️
16//!
17//! This crate is intended for Zenoh's internal use.
18//!
19//! [Click here for Zenoh's documentation](https://docs.rs/zenoh/latest/zenoh)
20mod multicast;
21mod pktinfo;
22mod unicast;
23
24use std::{net::SocketAddr, str::FromStr};
25
26use async_trait::async_trait;
27pub use multicast::*;
28pub use unicast::*;
29use zenoh_core::zconfigurable;
30use zenoh_link_commons::LocatorInspector;
31use zenoh_protocol::{
32    core::{endpoint::Address, Locator, Metadata, Reliability},
33    transport::BatchSize,
34};
35use zenoh_result::{zerror, ZResult};
36
37// NOTE: In case of using UDP in high-throughput scenarios, it is recommended to set the
38//       UDP buffer size on the host to a reasonable size. Usually, default values for UDP buffers
39//       size are undersized. Setting UDP buffers on the host to a size of 4M can be considered
40//       as a safe choice.
41//       Usually, on Linux systems this could be achieved by executing:
42//           $ sysctl -w net.core.rmem_max=4194304
43//           $ sysctl -w net.core.rmem_default=4194304
44
45/// Maximum MTU (UDP PDU) in bytes.
46///
47/// # Note
48///
49/// The theoretical Maximum Transmission Unit (MTU) of UDP is `u16::MAX`. From that we subtract the
50/// size of a UDP header (8 bytes) and the size of IPv4/IPv6 headers (resp. 20 and 40 bytes).
51///
52/// Although in IPv6 it is possible to have UDP datagrams of size greater than 65,535 bytes via IPv6
53/// Jumbograms, its usage in Zenoh is discouraged unless the consequences are very well understood.
54const UDP_MAX_MTU: BatchSize = u16::MAX - 8 - 40;
55
56pub const UDP_LOCATOR_PREFIX: &str = "udp";
57
58#[cfg(any(target_os = "linux", target_os = "windows"))]
59// Linux default value of a maximum datagram size is set to UDP MAX MTU.
60const UDP_MTU_LIMIT: BatchSize = UDP_MAX_MTU;
61
62#[cfg(target_os = "macos")]
63// Mac OS X default value of a maximum datagram size is set to 9216 bytes.
64const UDP_MTU_LIMIT: BatchSize = 9_216;
65
66#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
67const UDP_MTU_LIMIT: BatchSize = 8_192;
68
69const IS_RELIABLE: bool = false;
70
71zconfigurable! {
72    // Default MTU (UDP PDU) in bytes.
73    static ref UDP_DEFAULT_MTU: BatchSize = UDP_MTU_LIMIT;
74    // Amount of time in microseconds to throttle the accept loop upon an error.
75    // Default set to 100 ms.
76    static ref UDP_ACCEPT_THROTTLE_TIME: u64 = 100_000;
77}
78
79#[derive(Default, Clone, Copy)]
80pub struct UdpLocatorInspector;
81#[async_trait]
82impl LocatorInspector for UdpLocatorInspector {
83    fn protocol(&self) -> &str {
84        UDP_LOCATOR_PREFIX
85    }
86
87    async fn is_multicast(&self, locator: &Locator) -> ZResult<bool> {
88        let is_multicast = get_udp_addrs(locator.address())
89            .await?
90            .any(|x| x.ip().is_multicast());
91        Ok(is_multicast)
92    }
93
94    fn is_reliable(&self, locator: &Locator) -> ZResult<bool> {
95        if let Some(reliability) = locator
96            .metadata()
97            .get(Metadata::RELIABILITY)
98            .map(Reliability::from_str)
99            .transpose()?
100        {
101            Ok(reliability == Reliability::Reliable)
102        } else {
103            Ok(IS_RELIABLE)
104        }
105    }
106}
107
108pub mod config {
109    pub const UDP_MULTICAST_IFACE: &str = "iface";
110    pub const UDP_MULTICAST_JOIN: &str = "join";
111    pub const UDP_MULTICAST_TTL: &str = "ttl";
112}
113
114pub async fn get_udp_addrs(address: Address<'_>) -> ZResult<impl Iterator<Item = SocketAddr>> {
115    let iter = tokio::net::lookup_host(address.as_str().to_string())
116        .await
117        .map_err(|e| zerror!("{}", e))?;
118    Ok(iter)
119}
120
121pub(crate) fn socket_addr_to_udp_locator(addr: &SocketAddr) -> Locator {
122    Locator::new(UDP_LOCATOR_PREFIX, addr.to_string(), "").unwrap()
123}