| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! Socket features exposed by netstack3. |
| |
| use std::{convert::Infallible as Never, num::NonZeroU64}; |
| |
| use const_unwrap::const_unwrap_option; |
| use either::Either; |
| use fidl::endpoints::ProtocolMarker as _; |
| use fidl_fuchsia_net as fnet; |
| use fidl_fuchsia_posix::Errno; |
| use fidl_fuchsia_posix_socket as psocket; |
| use fuchsia_zircon as zx; |
| use futures::StreamExt as _; |
| use net_types::{ |
| ip::{Ip, IpAddress, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr}, |
| ScopeableAddress, SpecifiedAddr, Witness, ZonedAddr, |
| }; |
| use netstack3_core::{ |
| device::DeviceId, |
| error::{LocalAddressError, RemoteAddressError, SocketError, ZonedAddressError}, |
| ip::{IpSockCreationError, IpSockSendError, ResolveRouteError}, |
| socket::{ |
| ConnectError, NotDualStackCapableError, SetDualStackEnabledError, |
| SetMulticastMembershipError, |
| }, |
| tcp, udp, |
| }; |
| |
| use crate::bindings::{ |
| devices::{ |
| BindingId, DeviceIdAndName, DeviceSpecificInfo, Devices, DynamicCommonInfo, |
| DynamicEthernetInfo, DynamicNetdeviceInfo, |
| }, |
| util::{DeviceNotFoundError, IntoCore as _, IntoFidl as _, TryIntoCoreWithContext}, |
| Ctx, DeviceIdExt as _, |
| }; |
| |
| macro_rules! respond_not_supported { |
| ($name:expr, $responder:expr) => {{ |
| tracing::debug!("{} not supported", $name); |
| $responder |
| .send(Err(fidl_fuchsia_posix::Errno::Eopnotsupp)) |
| .unwrap_or_else(|e| tracing::error!("failed to respond: {e:?}")) |
| }}; |
| } |
| |
| pub(crate) mod datagram; |
| pub(crate) mod packet; |
| pub(crate) mod queue; |
| pub(crate) mod raw; |
| pub(crate) mod stream; |
| pub(crate) mod worker; |
| |
| const ZXSIO_SIGNAL_INCOMING: zx::Signals = |
| const_unwrap_option(zx::Signals::from_bits(psocket::SIGNAL_DATAGRAM_INCOMING)); |
| const ZXSIO_SIGNAL_OUTGOING: zx::Signals = |
| const_unwrap_option(zx::Signals::from_bits(psocket::SIGNAL_DATAGRAM_OUTGOING)); |
| const ZXSIO_SIGNAL_CONNECTED: zx::Signals = |
| const_unwrap_option(zx::Signals::from_bits(psocket::SIGNAL_STREAM_CONNECTED)); |
| |
| /// Common properties for socket workers. |
| #[derive(Debug)] |
| pub(crate) struct SocketWorkerProperties {} |
| |
| pub(crate) async fn serve( |
| mut ctx: crate::bindings::Ctx, |
| stream: psocket::ProviderRequestStream, |
| ) -> crate::bindings::util::TaskWaitGroup { |
| let (wait_group, task_spawner) = crate::bindings::util::TaskWaitGroup::new(); |
| let task_spawner: worker::ProviderScopedSpawner<_> = task_spawner.into(); |
| stream |
| .map(move |req| { |
| let req = match req { |
| Ok(req) => req, |
| Err(e) => { |
| if !e.is_closed() { |
| tracing::error!( |
| "{} request error {e:?}", |
| psocket::ProviderMarker::DEBUG_NAME |
| ); |
| } |
| return; |
| } |
| }; |
| match req { |
| psocket::ProviderRequest::InterfaceIndexToName { index, responder } => { |
| let response = { |
| let bindings_ctx = ctx.bindings_ctx(); |
| BindingId::new(index) |
| .ok_or(DeviceNotFoundError) |
| .and_then(|id| id.try_into_core_with_ctx(bindings_ctx)) |
| .map(|core_id: DeviceId<_>| core_id.bindings_id().name.clone()) |
| .map_err(|DeviceNotFoundError| zx::Status::NOT_FOUND.into_raw()) |
| }; |
| responder |
| .send(response.as_deref().map_err(|e| *e)) |
| .unwrap_or_else(|e| tracing::error!("failed to respond: {e:?}")); |
| } |
| psocket::ProviderRequest::InterfaceNameToIndex { name, responder } => { |
| let response = { |
| let bindings_ctx = ctx.bindings_ctx(); |
| let devices = AsRef::<Devices<_>>::as_ref(bindings_ctx); |
| let result = devices |
| .get_device_by_name(&name) |
| .map(|d| d.bindings_id().id.get()) |
| .ok_or(zx::Status::NOT_FOUND.into_raw()); |
| result |
| }; |
| responder |
| .send(response) |
| .unwrap_or_else(|e| tracing::error!("failed to respond: {e:?}")); |
| } |
| psocket::ProviderRequest::InterfaceNameToFlags { name, responder } => { |
| responder |
| .send(get_interface_flags(&ctx, &name)) |
| .unwrap_or_else(|e| tracing::error!("failed to respond: {e:?}")); |
| } |
| psocket::ProviderRequest::StreamSocket { domain, proto, responder } => { |
| let (client, request_stream) = create_request_stream(); |
| stream::spawn_worker(domain, proto, ctx.clone(), request_stream, &task_spawner); |
| responder |
| .send(Ok(client)) |
| .unwrap_or_else(|e| tracing::error!("failed to respond: {e:?}")); |
| } |
| psocket::ProviderRequest::DatagramSocketDeprecated { domain, proto, responder } => { |
| let (client, request_stream) = create_request_stream(); |
| let response = datagram::spawn_worker( |
| domain, |
| proto, |
| ctx.clone(), |
| request_stream, |
| SocketWorkerProperties {}, |
| &task_spawner, |
| ) |
| .map(|()| client); |
| responder |
| .send(response) |
| .unwrap_or_else(|e| tracing::error!("failed to respond: {e:?}")); |
| } |
| psocket::ProviderRequest::DatagramSocket { domain, proto, responder } => { |
| let (client, request_stream) = create_request_stream(); |
| let response = datagram::spawn_worker( |
| domain, |
| proto, |
| ctx.clone(), |
| request_stream, |
| SocketWorkerProperties {}, |
| &task_spawner, |
| ) |
| .map(|()| { |
| psocket::ProviderDatagramSocketResponse::SynchronousDatagramSocket(client) |
| }); |
| responder |
| .send(response) |
| .unwrap_or_else(|e| tracing::error!("failed to respond: {e:?}")); |
| } |
| psocket::ProviderRequest::GetInterfaceAddresses { responder } => { |
| responder |
| .send(&get_interface_addresses(&mut ctx)) |
| .unwrap_or_else(|e| tracing::error!("failed to respond: {e:?}")); |
| } |
| } |
| }) |
| .collect::<()>() |
| .await; |
| |
| wait_group |
| } |
| |
| pub(crate) fn create_request_stream<T: fidl::endpoints::ProtocolMarker>( |
| ) -> (fidl::endpoints::ClientEnd<T>, T::RequestStream) { |
| fidl::endpoints::create_request_stream().expect("can't create stream") |
| } |
| |
| fn get_interface_addresses(ctx: &mut Ctx) -> Vec<psocket::InterfaceAddresses> { |
| // Snapshot devices out so we don't hold any locks while calling into core. |
| let devices = |
| ctx.bindings_ctx().devices.with_devices(|devices| devices.cloned().collect::<Vec<_>>()); |
| devices |
| .into_iter() |
| .map(|d| { |
| let mut addresses = Vec::new(); |
| ctx.api().device_ip_any().for_each_assigned_ip_addr_subnet(&d, |a| { |
| addresses.push(fidl_fuchsia_net_ext::FromExt::from_ext(a)) |
| }); |
| |
| let DeviceIdAndName { id, name } = d.bindings_id(); |
| let info = d.external_state(); |
| let flags = flags_for_device(&info); |
| |
| psocket::InterfaceAddresses { |
| id: Some(id.get()), |
| name: Some(name.clone()), |
| addresses: Some(addresses), |
| interface_flags: Some(flags), |
| ..Default::default() |
| } |
| }) |
| .collect::<Vec<_>>() |
| } |
| |
| fn get_interface_flags( |
| ctx: &Ctx, |
| name: &str, |
| ) -> Result<psocket::InterfaceFlags, zx::sys::zx_status_t> { |
| let bindings_ctx = ctx.bindings_ctx(); |
| let device = |
| bindings_ctx.devices.get_device_by_name(name).ok_or(zx::Status::NOT_FOUND.into_raw())?; |
| Ok(flags_for_device(&device.external_state())) |
| } |
| |
| fn flags_for_device(info: &DeviceSpecificInfo<'_>) -> psocket::InterfaceFlags { |
| struct Flags { |
| physical_up: bool, |
| admin_enabled: bool, |
| loopback: bool, |
| } |
| |
| // NB: Exists to force destructuring `DynamicCommonInfo` without repetition |
| // in the match statement below. |
| struct FromDynamicInfo { |
| admin_enabled: bool, |
| } |
| impl<'a> From<&'a DynamicCommonInfo> for FromDynamicInfo { |
| fn from(value: &'a DynamicCommonInfo) -> Self { |
| let DynamicCommonInfo { |
| mtu: _, |
| admin_enabled, |
| events: _, |
| control_hook: _, |
| addresses: _, |
| } = value; |
| FromDynamicInfo { admin_enabled: *admin_enabled } |
| } |
| } |
| |
| let Flags { physical_up, admin_enabled, loopback } = match info { |
| DeviceSpecificInfo::Ethernet(info) => info.with_dynamic_info( |
| |DynamicEthernetInfo { |
| netdevice: DynamicNetdeviceInfo { common_info, phy_up }, |
| neighbor_event_sink: _, |
| }| { |
| let FromDynamicInfo { admin_enabled } = common_info.into(); |
| Flags { physical_up: *phy_up, admin_enabled, loopback: false } |
| }, |
| ), |
| DeviceSpecificInfo::Loopback(info) => info.with_dynamic_info(|common_info| { |
| let FromDynamicInfo { admin_enabled } = common_info.into(); |
| Flags { physical_up: true, admin_enabled: admin_enabled, loopback: true } |
| }), |
| DeviceSpecificInfo::PureIp(info) => { |
| info.with_dynamic_info(|DynamicNetdeviceInfo { common_info, phy_up }| { |
| let FromDynamicInfo { admin_enabled } = common_info.into(); |
| Flags { physical_up: *phy_up, admin_enabled, loopback: false } |
| }) |
| } |
| }; |
| |
| // Approximate that all interfaces support multicasting. |
| // TODO(https://fxbug.dev/42076301): Set this more precisely. |
| let multicast = true; |
| |
| // Note that the interface flags are not all intuitively named. Quotes below |
| // are from https://www.xml.com/ldd/chapter/book/ch14.html#INDEX-3,507. |
| [ |
| // IFF_UP is "on when the interface is active and ready to transfer |
| // packets". |
| (physical_up, psocket::InterfaceFlags::UP), |
| // IFF_LOOPBACK is "set only in the loopback interface". |
| (loopback, psocket::InterfaceFlags::LOOPBACK), |
| // IFF_RUNNING "indicates that the interface is up and running". |
| (admin_enabled && physical_up, psocket::InterfaceFlags::RUNNING), |
| // IFF_MULTICAST is set for "interfaces that are capable of multicast |
| // transmission". |
| (multicast, psocket::InterfaceFlags::MULTICAST), |
| ] |
| .into_iter() |
| .fold(psocket::InterfaceFlags::empty(), |mut flags, (b, flag)| { |
| flags.set(flag, b); |
| flags |
| }) |
| } |
| |
| /// A trait generalizing the data structures passed as arguments to POSIX socket |
| /// calls. |
| /// |
| /// `SockAddr` implementers are typically passed to POSIX socket calls as a blob |
| /// of bytes. It represents a type that can be parsed from a C API `struct |
| /// sockaddr`, expressed as a stream of bytes. |
| pub(crate) trait SockAddr: std::fmt::Debug + Sized + Send { |
| /// The concrete address type for this `SockAddr`. |
| type AddrType: IpAddress + ScopeableAddress; |
| /// The socket's domain. |
| const DOMAIN: psocket::Domain; |
| /// The unspecified instance of this `SockAddr`. |
| const UNSPECIFIED: Self; |
| |
| /// Creates a new `SockAddr` from the provided address and port. |
| /// |
| /// `addr` is either `Some(a)` where `a` holds a specified address an |
| /// optional zone, or `None` for the unspecified address (which can't have a |
| /// zone). |
| fn new(addr: Option<ZonedAddr<SpecifiedAddr<Self::AddrType>, NonZeroU64>>, port: u16) -> Self; |
| |
| /// Gets this `SockAddr`'s address. |
| fn addr(&self) -> Self::AddrType; |
| |
| /// Gets this `SockAddr`'s port. |
| fn port(&self) -> u16; |
| |
| /// Gets a `SpecifiedAddr` witness type for this `SockAddr`'s address. |
| fn get_specified_addr(&self) -> Option<SpecifiedAddr<Self::AddrType>> { |
| SpecifiedAddr::<Self::AddrType>::new(self.addr()) |
| } |
| |
| /// Gets this `SockAddr`'s zone identifier. |
| fn zone(&self) -> Option<NonZeroU64>; |
| |
| /// Converts this `SockAddr` into an [`fnet::SocketAddress`]. |
| fn into_sock_addr(self) -> fnet::SocketAddress; |
| |
| /// Converts an [`fnet::SocketAddress`] into a `SockAddr`. |
| fn from_sock_addr(addr: fnet::SocketAddress) -> Result<Self, Errno>; |
| } |
| |
| impl SockAddr for fnet::Ipv6SocketAddress { |
| type AddrType = Ipv6Addr; |
| const DOMAIN: psocket::Domain = psocket::Domain::Ipv6; |
| const UNSPECIFIED: Self = fnet::Ipv6SocketAddress { |
| address: fnet::Ipv6Address { addr: [0; 16] }, |
| port: 0, |
| zone_index: 0, |
| }; |
| |
| /// Creates a new `SockAddr6`. |
| fn new(addr: Option<ZonedAddr<SpecifiedAddr<Ipv6Addr>, NonZeroU64>>, port: u16) -> Self { |
| let (addr, zone_index) = addr.map_or((Ipv6::UNSPECIFIED_ADDRESS, 0), |addr| { |
| let (addr, zone) = addr.into_addr_zone(); |
| (addr.get(), zone.map_or(0, NonZeroU64::get)) |
| }); |
| fnet::Ipv6SocketAddress { address: addr.into_fidl(), port, zone_index } |
| } |
| |
| fn addr(&self) -> Ipv6Addr { |
| self.address.into_core() |
| } |
| |
| fn port(&self) -> u16 { |
| self.port |
| } |
| |
| fn zone(&self) -> Option<NonZeroU64> { |
| NonZeroU64::new(self.zone_index) |
| } |
| |
| fn into_sock_addr(self) -> fnet::SocketAddress { |
| fnet::SocketAddress::Ipv6(self) |
| } |
| |
| fn from_sock_addr(addr: fnet::SocketAddress) -> Result<Self, Errno> { |
| match addr { |
| fnet::SocketAddress::Ipv6(a) => Ok(a), |
| fnet::SocketAddress::Ipv4(_) => Err(Errno::Eafnosupport), |
| } |
| } |
| } |
| |
| impl SockAddr for fnet::Ipv4SocketAddress { |
| type AddrType = Ipv4Addr; |
| const DOMAIN: psocket::Domain = psocket::Domain::Ipv4; |
| const UNSPECIFIED: Self = |
| fnet::Ipv4SocketAddress { address: fnet::Ipv4Address { addr: [0; 4] }, port: 0 }; |
| |
| /// Creates a new `SockAddr4`. |
| fn new(addr: Option<ZonedAddr<SpecifiedAddr<Ipv4Addr>, NonZeroU64>>, port: u16) -> Self { |
| let addr = addr.map_or(Ipv4::UNSPECIFIED_ADDRESS, |zoned| zoned.into_unzoned().get()); |
| fnet::Ipv4SocketAddress { address: addr.into_fidl(), port } |
| } |
| |
| fn addr(&self) -> Ipv4Addr { |
| self.address.into_core() |
| } |
| |
| fn port(&self) -> u16 { |
| self.port |
| } |
| |
| fn zone(&self) -> Option<NonZeroU64> { |
| None |
| } |
| |
| fn into_sock_addr(self) -> fnet::SocketAddress { |
| fnet::SocketAddress::Ipv4(self) |
| } |
| |
| fn from_sock_addr(addr: fnet::SocketAddress) -> Result<Self, Errno> { |
| match addr { |
| fnet::SocketAddress::Ipv4(a) => Ok(a), |
| fnet::SocketAddress::Ipv6(_) => Err(Errno::Eafnosupport), |
| } |
| } |
| } |
| |
| /// Extension trait that associates a [`SockAddr`] and [`MulticastMembership`] |
| /// implementation to an IP version. We provide implementations for [`Ipv4`] and |
| /// [`Ipv6`]. |
| pub(crate) trait IpSockAddrExt: Ip { |
| type SocketAddress: SockAddr<AddrType = Self::Addr>; |
| } |
| |
| impl IpSockAddrExt for Ipv4 { |
| type SocketAddress = fnet::Ipv4SocketAddress; |
| } |
| |
| impl IpSockAddrExt for Ipv6 { |
| type SocketAddress = fnet::Ipv6SocketAddress; |
| } |
| |
| #[cfg(test)] |
| mod testutil { |
| use net_types::ip::{AddrSubnetEither, IpAddr}; |
| |
| use super::*; |
| |
| /// A trait that exposes common test behavior to implementers of |
| /// [`SockAddr`]. |
| pub(crate) trait TestSockAddr: SockAddr { |
| /// A different domain. |
| /// |
| /// `Ipv4SocketAddress` defines it as `Ipv6SocketAddress` and |
| /// vice-versa. |
| type DifferentDomain: TestSockAddr; |
| /// The local address used for tests. |
| const LOCAL_ADDR: Self::AddrType; |
| /// The remote address used for tests. |
| const REMOTE_ADDR: Self::AddrType; |
| /// An alternate remote address used for tests. |
| const REMOTE_ADDR_2: Self::AddrType; |
| /// An non-local address which is unreachable, used for tests. |
| const UNREACHABLE_ADDR: Self::AddrType; |
| |
| /// The default subnet prefix used for tests. |
| const DEFAULT_PREFIX: u8; |
| |
| /// Creates an [`fnet::SocketAddress`] with the given `addr` and `port`. |
| fn create(addr: Self::AddrType, port: u16) -> fnet::SocketAddress { |
| Self::new(SpecifiedAddr::new(addr).map(|a| ZonedAddr::Unzoned(a).into()), port) |
| .into_sock_addr() |
| } |
| |
| /// Gets the local address and prefix configured for the test |
| /// [`SockAddr`]. |
| fn config_addr_subnet() -> AddrSubnetEither { |
| AddrSubnetEither::new(IpAddr::from(Self::LOCAL_ADDR), Self::DEFAULT_PREFIX).unwrap() |
| } |
| |
| /// Gets the remote address and prefix to use for the test [`SockAddr`]. |
| fn config_addr_subnet_remote() -> AddrSubnetEither { |
| AddrSubnetEither::new(IpAddr::from(Self::REMOTE_ADDR), Self::DEFAULT_PREFIX).unwrap() |
| } |
| } |
| |
| impl TestSockAddr for fnet::Ipv6SocketAddress { |
| type DifferentDomain = fnet::Ipv4SocketAddress; |
| |
| const LOCAL_ADDR: Ipv6Addr = |
| Ipv6Addr::from_bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 168, 0, 1]); |
| const REMOTE_ADDR: Ipv6Addr = |
| Ipv6Addr::from_bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 168, 0, 2]); |
| const REMOTE_ADDR_2: Ipv6Addr = |
| Ipv6Addr::from_bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 168, 0, 3]); |
| const UNREACHABLE_ADDR: Ipv6Addr = |
| Ipv6Addr::from_bytes([0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 192, 168, 0, 1]); |
| const DEFAULT_PREFIX: u8 = 64; |
| } |
| |
| impl TestSockAddr for fnet::Ipv4SocketAddress { |
| type DifferentDomain = fnet::Ipv6SocketAddress; |
| |
| const LOCAL_ADDR: Ipv4Addr = Ipv4Addr::new([192, 168, 0, 1]); |
| const REMOTE_ADDR: Ipv4Addr = Ipv4Addr::new([192, 168, 0, 2]); |
| const REMOTE_ADDR_2: Ipv4Addr = Ipv4Addr::new([192, 168, 0, 3]); |
| const UNREACHABLE_ADDR: Ipv4Addr = Ipv4Addr::new([192, 168, 42, 1]); |
| const DEFAULT_PREFIX: u8 = 24; |
| } |
| } |
| |
| /// Trait expressing the conversion of error types into |
| /// [`fidl_fuchsia_posix::Errno`] errors for the POSIX-lite wrappers. |
| pub(crate) trait IntoErrno { |
| /// Returns the most equivalent POSIX error code for `self`. |
| fn into_errno(self) -> Errno; |
| } |
| |
| impl IntoErrno for Errno { |
| fn into_errno(self) -> Errno { |
| self |
| } |
| } |
| |
| impl IntoErrno for Never { |
| fn into_errno(self) -> Errno { |
| match self {} |
| } |
| } |
| |
| impl<A: IntoErrno, B: IntoErrno> IntoErrno for Either<A, B> { |
| fn into_errno(self) -> Errno { |
| match self { |
| Either::Left(a) => a.into_errno(), |
| Either::Right(b) => b.into_errno(), |
| } |
| } |
| } |
| |
| impl IntoErrno for LocalAddressError { |
| fn into_errno(self) -> Errno { |
| match self { |
| LocalAddressError::CannotBindToAddress |
| | LocalAddressError::FailedToAllocateLocalPort => Errno::Eaddrnotavail, |
| LocalAddressError::AddressMismatch => Errno::Eaddrnotavail, |
| LocalAddressError::AddressUnexpectedlyMapped => Errno::Einval, |
| LocalAddressError::AddressInUse => Errno::Eaddrinuse, |
| LocalAddressError::Zone(e) => e.into_errno(), |
| } |
| } |
| } |
| |
| impl IntoErrno for RemoteAddressError { |
| fn into_errno(self) -> Errno { |
| match self { |
| RemoteAddressError::NoRoute => Errno::Enetunreach, |
| } |
| } |
| } |
| |
| impl IntoErrno for SocketError { |
| fn into_errno(self) -> Errno { |
| match self { |
| SocketError::Remote(e) => e.into_errno(), |
| SocketError::Local(e) => e.into_errno(), |
| } |
| } |
| } |
| |
| impl IntoErrno for ResolveRouteError { |
| fn into_errno(self) -> Errno { |
| match self { |
| ResolveRouteError::NoSrcAddr => Errno::Eaddrnotavail, |
| ResolveRouteError::Unreachable => Errno::Enetunreach, |
| } |
| } |
| } |
| |
| impl IntoErrno for IpSockCreationError { |
| fn into_errno(self) -> Errno { |
| match self { |
| IpSockCreationError::Route(e) => e.into_errno(), |
| } |
| } |
| } |
| |
| impl IntoErrno for IpSockSendError { |
| fn into_errno(self) -> Errno { |
| match self { |
| IpSockSendError::Mtu => Errno::Einval, |
| IpSockSendError::Unroutable(e) => e.into_errno(), |
| } |
| } |
| } |
| |
| impl IntoErrno for udp::SendToError { |
| fn into_errno(self) -> Errno { |
| match self { |
| Self::NotWriteable => Errno::Epipe, |
| Self::CreateSock(err) => err.into_errno(), |
| Self::Zone(err) => err.into_errno(), |
| // NB: Mapping MTU to EMSGSIZE is different from the impl on |
| // `IpSockSendError` which maps to EINVAL instead. |
| Self::Send(IpSockSendError::Mtu) => Errno::Emsgsize, |
| Self::Send(IpSockSendError::Unroutable(err)) => err.into_errno(), |
| Self::RemotePortUnset => Errno::Einval, |
| Self::RemoteUnexpectedlyMapped => Errno::Enetunreach, |
| Self::RemoteUnexpectedlyNonMapped => Errno::Eafnosupport, |
| } |
| } |
| } |
| |
| impl IntoErrno for udp::SendError { |
| fn into_errno(self) -> Errno { |
| match self { |
| Self::IpSock(err) => err.into_errno(), |
| Self::NotWriteable => Errno::Epipe, |
| Self::RemotePortUnset => Errno::Edestaddrreq, |
| } |
| } |
| } |
| |
| impl IntoErrno for ConnectError { |
| fn into_errno(self) -> Errno { |
| match self { |
| Self::Ip(err) => err.into_errno(), |
| Self::Zone(err) => err.into_errno(), |
| Self::CouldNotAllocateLocalPort => Errno::Eaddrnotavail, |
| Self::SockAddrConflict => Errno::Eaddrinuse, |
| Self::RemoteUnexpectedlyMapped => Errno::Enetunreach, |
| Self::RemoteUnexpectedlyNonMapped => Errno::Eafnosupport, |
| } |
| } |
| } |
| |
| impl IntoErrno for SetMulticastMembershipError { |
| fn into_errno(self) -> Errno { |
| match self { |
| Self::AddressNotAvailable |
| | Self::DeviceDoesNotExist |
| | Self::NoDeviceWithAddress |
| | Self::NoDeviceAvailable => Errno::Enodev, |
| Self::GroupNotJoined => Errno::Eaddrnotavail, |
| Self::GroupAlreadyJoined => Errno::Eaddrinuse, |
| Self::WrongDevice => Errno::Einval, |
| } |
| } |
| } |
| |
| impl IntoErrno for ZonedAddressError { |
| fn into_errno(self) -> Errno { |
| match self { |
| Self::RequiredZoneNotProvided => Errno::Einval, |
| Self::DeviceZoneMismatch => Errno::Einval, |
| } |
| } |
| } |
| |
| impl IntoErrno for tcp::SetDeviceError { |
| fn into_errno(self) -> Errno { |
| match self { |
| Self::Conflict => Errno::Eaddrinuse, |
| Self::Unroutable => Errno::Ehostunreach, |
| Self::ZoneChange => Errno::Einval, |
| } |
| } |
| } |
| |
| impl IntoErrno for SetDualStackEnabledError { |
| fn into_errno(self) -> Errno { |
| match self { |
| SetDualStackEnabledError::SocketIsBound => Errno::Einval, |
| SetDualStackEnabledError::NotCapable => Errno::Enoprotoopt, |
| } |
| } |
| } |
| |
| impl IntoErrno for NotDualStackCapableError { |
| fn into_errno(self) -> Errno { |
| Errno::Enoprotoopt |
| } |
| } |
| |
| /// Logs the errno, tailoring the log level to the error's severity. |
| /// |
| /// # Syntax |
| /// |
| /// log_errno!(errno, fmt_str, fmt_arg1, fmt_arg2, ...); |
| /// |
| /// - errno: The error used to determine the log level. Must be an |
| /// [`fidl_fuchsia_posix::Errno`]. |
| /// - fmt_str: An `&str` format string, e.g. "Foo Op failed: {:?}". |
| /// - fmt_arg1 ... fmt_arg n: A variable length list of arguments to `fmt_st`. |
| /// |
| /// Which is expanded into the appropriate [`tracing`] macro invocation as |
| /// follows: debug!(fmt_string, fmt_arg1, fmt_arg2, ...) |
| macro_rules! log_errno { |
| ($errno:expr, $fmt_str:expr, $($arg:tt)*) => { |
| match $errno { |
| // Errnos that indicate the socket API is being called incorrectly. |
| fidl_fuchsia_posix::Errno::Einval |
| | fidl_fuchsia_posix::Errno::Eafnosupport |
| | fidl_fuchsia_posix::Errno::Enoprotoopt => tracing::warn!($fmt_str, $($arg)*), |
| // Errnos that may occur under normal operation and are quite noisy. |
| fidl_fuchsia_posix::Errno::Enetunreach |
| | fidl_fuchsia_posix::Errno::Ehostunreach |
| | fidl_fuchsia_posix::Errno::Eagain => tracing::trace!($fmt_str, $($arg)*), |
| // All other errnos. |
| _ => tracing::debug!($fmt_str, $($arg)*), |
| } |
| }; |
| } |
| pub(crate) use log_errno; |