diff --git a/edge-nal-embassy/Cargo.toml b/edge-nal-embassy/Cargo.toml index 6d403b6..ce3c402 100644 --- a/edge-nal-embassy/Cargo.toml +++ b/edge-nal-embassy/Cargo.toml @@ -15,7 +15,16 @@ categories = [ ] [features] +default = ["proto-ipv4", "proto-ipv6", "medium-ethernet", "dns", "udp", "tcp", "multicast"] defmt = ["dep:defmt", "heapless/defmt-03", "embassy-net/defmt"] +proto-ipv4 = ["embassy-net/proto-ipv4"] +proto-ipv6 = ["embassy-net/proto-ipv6"] +medium-ethernet = ["embassy-net/medium-ethernet"] +medium-ip = ["embassy-net/medium-ip"] +dns = ["embassy-net/dns"] +udp = ["embassy-net/udp"] +tcp = ["embassy-net/tcp"] +multicast = ["embassy-net/multicast"] [dependencies] log = { version = "0.4", default-features = false, optional = true } @@ -23,14 +32,5 @@ defmt = { version = "0.3", optional = true } embedded-io-async = { workspace = true } edge-nal = { workspace = true } heapless = { workspace = true } -# Do not require these features and conditionalize the code instead -embassy-net = { version = "0.6", features = [ - "tcp", - "udp", - "dns", - "proto-ipv6", - "medium-ethernet", - "proto-ipv4", - "multicast", -] } +embassy-net = "0.6" embassy-futures = { workspace = true } diff --git a/edge-nal-embassy/src/lib.rs b/edge-nal-embassy/src/lib.rs index 8f3aced..7b492fa 100644 --- a/edge-nal-embassy/src/lib.rs +++ b/edge-nal-embassy/src/lib.rs @@ -4,20 +4,26 @@ use core::cell::{Cell, UnsafeCell}; use core::mem::MaybeUninit; -use core::net::SocketAddr; +use core::net::{IpAddr, SocketAddr}; use core::ptr::NonNull; -use embassy_net::{IpEndpoint, IpListenEndpoint}; +use embassy_net::{IpAddress, IpEndpoint, IpListenEndpoint}; +#[cfg(feature = "dns")] pub use dns::*; +#[cfg(feature = "tcp")] pub use tcp::*; +#[cfg(feature = "udp")] pub use udp::*; // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +#[cfg(feature = "dns")] mod dns; +#[cfg(feature = "tcp")] mod tcp; +#[cfg(feature = "udp")] mod udp; pub(crate) struct Pool { @@ -66,26 +72,33 @@ pub(crate) fn to_net_socket(socket: IpEndpoint) -> SocketAddr { SocketAddr::new(socket.addr.into(), socket.port) } -// pub(crate) fn to_net_socket2(socket: IpListenEndpoint) -> SocketAddr { -// SocketAddr::new( -// socket -// .addr -// .map(to_net_addr) -// .unwrap_or(IpAddr::V6(Ipv6Addr::UNSPECIFIED)), -// socket.port, -// ) -// } - -pub(crate) fn to_emb_socket(socket: SocketAddr) -> IpEndpoint { - IpEndpoint { - addr: socket.ip().into(), +pub(crate) fn to_emb_socket(socket: SocketAddr) -> Option { + Some(IpEndpoint { + addr: to_emb_addr(socket.ip())?, port: socket.port(), - } + }) } -pub(crate) fn to_emb_bind_socket(socket: SocketAddr) -> IpListenEndpoint { - IpListenEndpoint { - addr: (!socket.ip().is_unspecified()).then(|| socket.ip().into()), +pub(crate) fn to_emb_bind_socket(socket: SocketAddr) -> Option { + let addr = if socket.ip().is_unspecified() { + None + } else { + Some(to_emb_addr(socket.ip())?) + }; + + Some(IpListenEndpoint { + addr, port: socket.port(), + }) +} + +pub(crate) fn to_emb_addr(addr: IpAddr) -> Option { + match addr { + #[cfg(feature = "proto-ipv4")] + IpAddr::V4(addr) => Some(addr.into()), + #[cfg(feature = "proto-ipv6")] + IpAddr::V6(addr) => Some(addr.into()), + #[allow(unreachable_patterns)] + _ => None, } } diff --git a/edge-nal-embassy/src/tcp.rs b/edge-nal-embassy/src/tcp.rs index d169d64..3c7f359 100644 --- a/edge-nal-embassy/src/tcp.rs +++ b/edge-nal-embassy/src/tcp.rs @@ -44,7 +44,10 @@ impl TcpConnect async fn connect(&self, remote: SocketAddr) -> Result, Self::Error> { let mut socket = TcpSocket::new(self.stack, self.buffers)?; - socket.socket.connect(to_emb_socket(remote)).await?; + socket + .socket + .connect(to_emb_socket(remote).ok_or(TcpError::UnsupportedProto)?) + .await?; Ok(socket) } @@ -82,7 +85,10 @@ impl edge_nal::TcpAccept async fn accept(&self) -> Result<(SocketAddr, Self::Socket<'_>), Self::Error> { let mut socket = TcpSocket::new(self.stack.stack, self.stack.buffers)?; - socket.socket.accept(to_emb_bind_socket(self.local)).await?; + socket + .socket + .accept(to_emb_bind_socket(self.local).ok_or(TcpError::UnsupportedProto)?) + .await?; let local_endpoint = unwrap!(socket.socket.local_endpoint()); @@ -286,6 +292,7 @@ pub enum TcpError { Connect(ConnectError), Accept(AcceptError), NoBuffers, + UnsupportedProto, } impl From for TcpError { @@ -314,6 +321,7 @@ impl embedded_io_async::Error for TcpError { TcpError::Connect(_) => ErrorKind::Other, TcpError::Accept(_) => ErrorKind::Other, TcpError::NoBuffers => ErrorKind::OutOfMemory, + TcpError::UnsupportedProto => ErrorKind::InvalidInput, } } } diff --git a/edge-nal-embassy/src/udp.rs b/edge-nal-embassy/src/udp.rs index a269555..ae36f08 100644 --- a/edge-nal-embassy/src/udp.rs +++ b/edge-nal-embassy/src/udp.rs @@ -1,10 +1,10 @@ -use core::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use core::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; use core::ptr::NonNull; use edge_nal::{MulticastV4, MulticastV6, Readable, UdpBind, UdpReceive, UdpSend, UdpSplit}; use embassy_net::udp::{BindError, PacketMetadata, RecvError, SendError}; -use embassy_net::{MulticastError, Stack}; +use embassy_net::Stack; use embedded_io_async::{ErrorKind, ErrorType}; @@ -49,7 +49,9 @@ impl Udp async fn bind(&self, local: SocketAddr) -> Result, Self::Error> { let mut socket = UdpSocket::new(self.stack, self.buffers)?; - socket.socket.bind(to_emb_bind_socket(local))?; + socket + .socket + .bind(to_emb_bind_socket(local).ok_or(UdpError::UnsupportedProto)?)?; Ok(socket) } @@ -58,6 +60,7 @@ impl Udp /// A UDP socket /// Implements the `UdpReceive` `UdpSend` and `UdpSplit` traits from `edge-nal` pub struct UdpSocket<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize, const M: usize> { + #[allow(unused)] stack: embassy_net::Stack<'d>, socket: embassy_net::udp::UdpSocket<'d>, stack_buffers: &'d UdpBuffers, @@ -125,7 +128,12 @@ impl Udp for UdpSocket<'_, N, TX_SZ, RX_SZ, M> { async fn send(&mut self, remote: SocketAddr, data: &[u8]) -> Result<(), Self::Error> { - self.socket.send_to(data, to_emb_socket(remote)).await?; + self.socket + .send_to( + data, + to_emb_socket(remote).ok_or(UdpError::UnsupportedProto)?, + ) + .await?; Ok(()) } @@ -151,7 +159,12 @@ impl Udp for &UdpSocket<'_, N, TX_SZ, RX_SZ, M> { async fn send(&mut self, remote: SocketAddr, data: &[u8]) -> Result<(), Self::Error> { - self.socket.send_to(data, remote).await?; + self.socket + .send_to( + data, + to_emb_socket(remote).ok_or(UdpError::UnsupportedProto)?, + ) + .await?; Ok(()) } @@ -189,22 +202,42 @@ impl Mul { async fn join_v4( &mut self, - multicast_addr: Ipv4Addr, + #[allow(unused)] multicast_addr: Ipv4Addr, _interface: Ipv4Addr, ) -> Result<(), Self::Error> { - self.stack - .join_multicast_group(IpAddr::V4(multicast_addr))?; + #[cfg(feature = "multicast")] + { + self.stack.join_multicast_group( + crate::to_emb_addr(core::net::IpAddr::V4(multicast_addr)) + .ok_or(UdpError::UnsupportedProto)?, + )?; + } + + #[cfg(not(feature = "multicast"))] + { + Err(UdpError::UnsupportedProto)?; + } Ok(()) } async fn leave_v4( &mut self, - multicast_addr: Ipv4Addr, + #[allow(unused)] multicast_addr: Ipv4Addr, _interface: Ipv4Addr, ) -> Result<(), Self::Error> { - self.stack - .leave_multicast_group(IpAddr::V4(multicast_addr))?; + #[cfg(feature = "multicast")] + { + self.stack.leave_multicast_group( + crate::to_emb_addr(core::net::IpAddr::V4(multicast_addr)) + .ok_or(UdpError::UnsupportedProto)?, + )?; + } + + #[cfg(not(feature = "multicast"))] + { + Err(UdpError::UnsupportedProto)?; + } Ok(()) } @@ -215,22 +248,42 @@ impl Mul { async fn join_v6( &mut self, - multicast_addr: Ipv6Addr, + #[allow(unused)] multicast_addr: Ipv6Addr, _interface: u32, ) -> Result<(), Self::Error> { - self.stack - .join_multicast_group(IpAddr::V6(multicast_addr))?; + #[cfg(feature = "multicast")] + { + self.stack.join_multicast_group( + crate::to_emb_addr(core::net::IpAddr::V6(multicast_addr)) + .ok_or(UdpError::UnsupportedProto)?, + )?; + } + + #[cfg(not(feature = "multicast"))] + { + Err(UdpError::UnsupportedProto)?; + } Ok(()) } async fn leave_v6( &mut self, - multicast_addr: Ipv6Addr, + #[allow(unused)] multicast_addr: Ipv6Addr, _interface: u32, ) -> Result<(), Self::Error> { - self.stack - .leave_multicast_group(IpAddr::V6(multicast_addr))?; + #[cfg(feature = "multicast")] + { + self.stack.leave_multicast_group( + crate::to_emb_addr(core::net::IpAddr::V6(multicast_addr)) + .ok_or(UdpError::UnsupportedProto)?, + )?; + } + + #[cfg(not(feature = "multicast"))] + { + Err(UdpError::UnsupportedProto)?; + } Ok(()) } @@ -252,8 +305,12 @@ pub enum UdpError { Recv(RecvError), Send(SendError), Bind(BindError), - Multicast(MulticastError), + /// The table of joined multicast groups is already full. + MulticastGroupTableFull, + /// Cannot join/leave the given multicast group. + MulticastUnaddressable, NoBuffers, + UnsupportedProto, } impl From for UdpError { @@ -274,9 +331,16 @@ impl From for UdpError { } } -impl From for UdpError { - fn from(e: MulticastError) -> Self { - UdpError::Multicast(e) +#[cfg(all( + feature = "multicast", + any(feature = "proto-ipv4", feature = "proto-ipv6") +))] +impl From for UdpError { + fn from(e: embassy_net::MulticastError) -> Self { + match e { + embassy_net::MulticastError::GroupTableFull => UdpError::MulticastGroupTableFull, + embassy_net::MulticastError::Unaddressable => UdpError::MulticastUnaddressable, + } } } @@ -287,8 +351,10 @@ impl embedded_io_async::Error for UdpError { UdpError::Recv(_) => ErrorKind::Other, UdpError::Send(_) => ErrorKind::Other, UdpError::Bind(_) => ErrorKind::Other, - UdpError::Multicast(_) => ErrorKind::Other, + UdpError::MulticastGroupTableFull => ErrorKind::Other, + UdpError::MulticastUnaddressable => ErrorKind::Other, UdpError::NoBuffers => ErrorKind::OutOfMemory, + UdpError::UnsupportedProto => ErrorKind::InvalidInput, } } }