use alloc::collections::{HashMap, HashSet};
use alloc::vec::Vec;
use core::borrow::Borrow;
use core::convert::Infallible as Never;
use core::fmt::Debug;
use core::hash::Hash;
use core::marker::PhantomData;
use core::num::{NonZeroU16, NonZeroU8};
use core::ops::{Deref, DerefMut};
use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
use netstack3_ip::marker::OptionDelegationMarker;
use derivative::Derivative;
use either::Either;
use net_types::ip::{GenericOverIp, Ip, IpAddress, Ipv4, Ipv6, Mtu};
use net_types::{MulticastAddr, MulticastAddress as _, SpecifiedAddr, Witness, ZonedAddr};
use netstack3_base::socket::{
self, BoundSocketMap, ConnAddr, ConnInfoAddr, ConnIpAddr, DualStackConnIpAddr,
DualStackListenerIpAddr, DualStackLocalIp, DualStackRemoteIp, EitherStack, InsertError,
ListenerAddr, ListenerIpAddr, MaybeDualStack, NotDualStackCapableError, Shutdown, ShutdownType,
SocketDeviceUpdate, SocketDeviceUpdateNotAllowedError, SocketIpAddr, SocketIpExt,
SocketMapAddrSpec, SocketMapConflictPolicy, SocketMapStateSpec, SocketZonedAddrExt as _,
StrictlyZonedAddr,
};
use netstack3_base::sync::{self, RwLock};
use netstack3_base::{
AnyDevice, BidirectionalConverter, ContextPair, DeviceIdContext, DeviceIdentifier,
EitherDeviceId, ExistsError, Inspector, InspectorDeviceExt, IpDeviceAddr, LocalAddressError,
NotFoundError, OwnedOrRefsBidirectionalConverter, ReferenceNotifiers, ReferenceNotifiersExt,
RemoteAddressError, RemoveResourceResultWithContext, RngContext, SocketError,
StrongDeviceIdentifier as _, WeakDeviceIdentifier, ZonedAddressError,
};
use netstack3_filter::TransportPacketSerializer;
use netstack3_ip::socket::{
DelegatedRouteResolutionOptions, DelegatedSendOptions, IpSock, IpSockCreateAndSendError,
IpSockCreationError, IpSockSendError, IpSocketHandler, RouteResolutionOptions,
SendOneShotIpPacketError, SendOptions, SocketHopLimits,
};
use netstack3_ip::{
BaseTransportIpContext, HopLimits, Mark, MarkDomain, Marks, MulticastMembershipHandler,
ResolveRouteError, TransportIpContext,
};
use packet::BufferMut;
use packet_formats::ip::{DscpAndEcn, IpProtoExt};
use ref_cast::RefCast;
use thiserror::Error;
pub type BoundSockets<I, D, A, S> = BoundSocketMap<I, D, A, S>;
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub struct ReferenceState<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
pub(crate) state: RwLock<SocketState<I, D, S>>,
pub(crate) external_data: S::ExternalData<I>,
}
type PrimaryRc<I, D, S> = sync::PrimaryRc<ReferenceState<I, D, S>>;
pub type StrongRc<I, D, S> = sync::StrongRc<ReferenceState<I, D, S>>;
pub type WeakRc<I, D, S> = sync::WeakRc<ReferenceState<I, D, S>>;
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
OrderedLockAccess<SocketState<I, D, S>> for ReferenceState<I, D, S>
{
type Lock = RwLock<SocketState<I, D, S>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.state)
}
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> ReferenceState<I, D, S> {
pub fn external_data(&self) -> &S::ExternalData<I> {
&self.external_data
}
#[cfg(any(test, feature = "testutils"))]
pub fn state(&self) -> &RwLock<SocketState<I, D, S>> {
&self.state
}
}
#[derive(Derivative, GenericOverIp)]
#[derivative(Default(bound = ""))]
#[generic_over_ip(I, Ip)]
pub struct DatagramSocketSet<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
HashMap<StrongRc<I, D, S>, PrimaryRc<I, D, S>>,
);
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> Debug
for DatagramSocketSet<I, D, S>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let Self(rc) = self;
f.debug_list().entries(rc.keys().map(StrongRc::debug_id)).finish()
}
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> Deref
for DatagramSocketSet<I, D, S>
{
type Target = HashMap<StrongRc<I, D, S>, PrimaryRc<I, D, S>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> DerefMut
for DatagramSocketSet<I, D, S>
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub trait IpExt: netstack3_base::IpExt + DualStackIpExt + netstack3_base::IcmpIpExt {}
impl<I: netstack3_base::IpExt + DualStackIpExt + netstack3_base::IcmpIpExt> IpExt for I {}
#[derive(Derivative, GenericOverIp)]
#[generic_over_ip(I, Ip)]
#[derivative(Debug(bound = ""))]
#[allow(missing_docs)]
pub enum SocketState<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
Unbound(UnboundSocketState<I, D, S>),
Bound(BoundSocketState<I, D, S>),
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsRef<IpOptions<I, D, S>>
for SocketState<I, D, S>
{
fn as_ref(&self) -> &IpOptions<I, D, S> {
match self {
Self::Unbound(unbound) => unbound.as_ref(),
Self::Bound(bound) => bound.as_ref(),
}
}
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> SocketState<I, D, S> {
fn to_socket_info(&self) -> SocketInfo<I::Addr, D> {
match self {
Self::Unbound(_) => SocketInfo::Unbound,
Self::Bound(BoundSocketState { socket_type, original_bound_addr: _ }) => {
match socket_type {
BoundSocketStateType::Listener { state, sharing: _ } => {
let ListenerState { addr, ip_options: _ } = state;
SocketInfo::Listener(addr.clone().into())
}
BoundSocketStateType::Connected { state, sharing: _ } => {
SocketInfo::Connected(S::conn_info_from_state(&state))
}
}
}
}
}
pub fn record_common_info<N>(&self, inspector: &mut N, socket_id: &S::SocketId<I, D>)
where
N: Inspector + InspectorDeviceExt<D>,
{
inspector.record_debug_child(socket_id, |node| {
node.record_str("TransportProtocol", S::NAME);
node.record_str("NetworkProtocol", I::NAME);
let socket_info = self.to_socket_info();
let (local, remote) = match socket_info {
SocketInfo::Unbound => (None, None),
SocketInfo::Listener(ListenerInfo { local_ip, local_identifier }) => (
Some((
local_ip.map_or_else(
|| ZonedAddr::Unzoned(I::UNSPECIFIED_ADDRESS),
|addr| addr.into_inner_without_witness(),
),
local_identifier,
)),
None,
),
SocketInfo::Connected(ConnInfo {
local_ip,
local_identifier,
remote_ip,
remote_identifier,
}) => (
Some((local_ip.into_inner_without_witness(), local_identifier)),
Some((remote_ip.into_inner_without_witness(), remote_identifier)),
),
};
node.record_local_socket_addr::<N, _, _, _>(local);
node.record_remote_socket_addr::<N, _, _, _>(remote);
let IpOptions {
multicast_memberships: MulticastMemberships(multicast_memberships),
socket_options: _,
other_stack: _,
common: _,
} = self.as_ref();
node.record_child("MulticastGroupMemberships", |node| {
for (index, (multicast_addr, device)) in multicast_memberships.iter().enumerate() {
node.record_debug_child(index, |node| {
node.record_ip_addr("MulticastGroup", multicast_addr.get());
N::record_device(node, "Device", device);
})
}
});
});
}
pub fn get_options_device<'a, BC, CC: DatagramBoundStateContext<I, BC, S, WeakDeviceId = D>>(
&'a self,
core_ctx: &mut CC,
) -> (&'a IpOptions<I, CC::WeakDeviceId, S>, &'a Option<CC::WeakDeviceId>) {
match self {
SocketState::Unbound(state) => {
let UnboundSocketState { ip_options, device, sharing: _ } = state;
(ip_options, device)
}
SocketState::Bound(BoundSocketState { socket_type, original_bound_addr: _ }) => {
match socket_type {
BoundSocketStateType::Listener { state, sharing: _ } => {
let ListenerState { ip_options, addr: ListenerAddr { device, ip: _ } } =
state;
(ip_options, device)
}
BoundSocketStateType::Connected { state, sharing: _ } => {
match core_ctx.dual_stack_context() {
MaybeDualStack::DualStack(dual_stack) => {
match dual_stack.ds_converter().convert(state) {
DualStackConnState::ThisStack(state) => {
state.get_options_device()
}
DualStackConnState::OtherStack(state) => {
dual_stack.assert_dual_stack_enabled(state);
state.get_options_device()
}
}
}
MaybeDualStack::NotDualStack(not_dual_stack) => {
not_dual_stack.nds_converter().convert(state).get_options_device()
}
}
}
}
}
}
}
fn get_options_mut<'a, BC, CC: DatagramBoundStateContext<I, BC, S, WeakDeviceId = D>>(
&'a mut self,
core_ctx: &mut CC,
) -> &'a mut IpOptions<I, CC::WeakDeviceId, S> {
match self {
SocketState::Unbound(state) => {
let UnboundSocketState { ip_options, device: _, sharing: _ } = state;
ip_options
}
SocketState::Bound(BoundSocketState { socket_type, original_bound_addr: _ }) => {
match socket_type {
BoundSocketStateType::Listener { state, sharing: _ } => {
let ListenerState { ip_options, addr: _ } = state;
ip_options
}
BoundSocketStateType::Connected { state, sharing: _ } => {
match core_ctx.dual_stack_context() {
MaybeDualStack::DualStack(dual_stack) => {
match dual_stack.ds_converter().convert(state) {
DualStackConnState::ThisStack(state) => state.as_mut(),
DualStackConnState::OtherStack(state) => {
dual_stack.assert_dual_stack_enabled(state);
state.as_mut()
}
}
}
MaybeDualStack::NotDualStack(not_dual_stack) => {
not_dual_stack.nds_converter().convert(state).as_mut()
}
}
}
}
}
}
}
}
#[derive(Derivative)]
#[derivative(Debug(bound = "D: Debug"))]
pub struct BoundSocketState<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
pub socket_type: BoundSocketStateType<I, D, S>,
pub original_bound_addr: Option<S::ListenerIpAddr<I>>,
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsRef<IpOptions<I, D, S>>
for BoundSocketState<I, D, S>
{
fn as_ref(&self) -> &IpOptions<I, D, S> {
let BoundSocketState { socket_type, original_bound_addr: _ } = self;
match socket_type {
BoundSocketStateType::Listener { state, sharing: _ } => state.as_ref(),
BoundSocketStateType::Connected { state, sharing: _ } => state.as_ref(),
}
}
}
#[derive(Derivative)]
#[derivative(Debug(bound = "D: Debug"))]
#[allow(missing_docs)]
pub enum BoundSocketStateType<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
Listener { state: ListenerState<I, D, S>, sharing: S::SharingState },
Connected { state: S::ConnState<I, D>, sharing: S::SharingState },
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsRef<IpOptions<I, D, S>>
for BoundSocketStateType<I, D, S>
{
fn as_ref(&self) -> &IpOptions<I, D, S> {
match self {
Self::Listener { state, sharing: _ } => state.as_ref(),
Self::Connected { state, sharing: _ } => state.as_ref(),
}
}
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""), Default(bound = ""))]
pub struct UnboundSocketState<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
device: Option<D>,
sharing: S::SharingState,
ip_options: IpOptions<I, D, S>,
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsRef<IpOptions<I, D, S>>
for UnboundSocketState<I, D, S>
{
fn as_ref(&self) -> &IpOptions<I, D, S> {
&self.ip_options
}
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub struct ListenerState<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec + ?Sized> {
pub(crate) ip_options: IpOptions<I, D, S>,
pub(crate) addr: ListenerAddr<S::ListenerIpAddr<I>, D>,
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsRef<IpOptions<I, D, S>>
for ListenerState<I, D, S>
{
fn as_ref(&self) -> &IpOptions<I, D, S> {
&self.ip_options
}
}
#[derive(Derivative)]
#[derivative(Debug(bound = "D: Debug"))]
pub struct ConnState<
WireI: IpExt,
SocketI: IpExt,
D: WeakDeviceIdentifier,
S: DatagramSocketSpec + ?Sized,
> {
pub(crate) socket: IpSock<WireI, D>,
pub(crate) ip_options: IpOptions<SocketI, D, S>,
pub(crate) shutdown: Shutdown,
pub(crate) addr: ConnAddr<
ConnIpAddr<
WireI::Addr,
<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>,
D,
>,
pub(crate) clear_device_on_disconnect: bool,
pub(crate) extra: S::ConnStateExtra,
}
impl<WireI: IpExt, SocketI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
AsRef<IpOptions<SocketI, D, S>> for ConnState<WireI, SocketI, D, S>
{
fn as_ref(&self) -> &IpOptions<SocketI, D, S> {
&self.ip_options
}
}
impl<WireI: IpExt, SocketI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsRef<Shutdown>
for ConnState<WireI, SocketI, D, S>
{
fn as_ref(&self) -> &Shutdown {
&self.shutdown
}
}
impl<WireI: IpExt, SocketI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
AsMut<IpOptions<SocketI, D, S>> for ConnState<WireI, SocketI, D, S>
{
fn as_mut(&mut self) -> &mut IpOptions<SocketI, D, S> {
&mut self.ip_options
}
}
impl<WireI: IpExt, SocketI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsMut<Shutdown>
for ConnState<WireI, SocketI, D, S>
{
fn as_mut(&mut self) -> &mut Shutdown {
&mut self.shutdown
}
}
impl<WireI: IpExt, SocketI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
ConnState<WireI, SocketI, D, S>
{
pub fn should_receive(&self) -> bool {
let Self {
shutdown,
socket: _,
ip_options: _,
clear_device_on_disconnect: _,
addr: _,
extra: _,
} = self;
let Shutdown { receive, send: _ } = shutdown;
!*receive
}
pub fn addr(
&self,
) -> &ConnAddr<
ConnIpAddr<
WireI::Addr,
<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>,
D,
> {
&self.addr
}
pub fn extra(&self) -> &S::ConnStateExtra {
&self.extra
}
fn get_options_device(&self) -> (&IpOptions<SocketI, D, S>, &Option<D>) {
let Self { ip_options, addr: ConnAddr { device, .. }, .. } = self;
(ip_options, device)
}
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub enum DualStackConnState<
I: IpExt + DualStackIpExt,
D: WeakDeviceIdentifier,
S: DatagramSocketSpec + ?Sized,
> {
ThisStack(ConnState<I, I, D, S>),
OtherStack(ConnState<I::OtherVersion, I, D, S>),
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsRef<IpOptions<I, D, S>>
for DualStackConnState<I, D, S>
{
fn as_ref(&self) -> &IpOptions<I, D, S> {
match self {
DualStackConnState::ThisStack(state) => state.as_ref(),
DualStackConnState::OtherStack(state) => state.as_ref(),
}
}
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsMut<IpOptions<I, D, S>>
for DualStackConnState<I, D, S>
{
fn as_mut(&mut self) -> &mut IpOptions<I, D, S> {
match self {
DualStackConnState::ThisStack(state) => state.as_mut(),
DualStackConnState::OtherStack(state) => state.as_mut(),
}
}
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsRef<Shutdown>
for DualStackConnState<I, D, S>
{
fn as_ref(&self) -> &Shutdown {
match self {
DualStackConnState::ThisStack(state) => state.as_ref(),
DualStackConnState::OtherStack(state) => state.as_ref(),
}
}
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsMut<Shutdown>
for DualStackConnState<I, D, S>
{
fn as_mut(&mut self) -> &mut Shutdown {
match self {
DualStackConnState::ThisStack(state) => state.as_mut(),
DualStackConnState::OtherStack(state) => state.as_mut(),
}
}
}
#[derive(Derivative, GenericOverIp)]
#[generic_over_ip(I, Ip)]
#[derivative(Clone(bound = ""), Debug, Default(bound = ""))]
pub struct DatagramIpSpecificSocketOptions<I: IpExt, D: WeakDeviceIdentifier> {
pub hop_limits: SocketHopLimits<I>,
pub multicast_interface: Option<D>,
#[derivative(Default(value = "true"))]
pub multicast_loop: bool,
pub allow_broadcast: Option<I::BroadcastMarker>,
pub dscp_and_ecn: DscpAndEcn,
}
impl<I: IpExt, D: WeakDeviceIdentifier> SendOptions<I> for DatagramIpSpecificSocketOptions<I, D> {
fn hop_limit(&self, destination: &SpecifiedAddr<I::Addr>) -> Option<NonZeroU8> {
self.hop_limits.hop_limit_for_dst(destination)
}
fn multicast_loop(&self) -> bool {
self.multicast_loop
}
fn allow_broadcast(&self) -> Option<I::BroadcastMarker> {
self.allow_broadcast
}
fn dscp_and_ecn(&self) -> DscpAndEcn {
self.dscp_and_ecn
}
fn mtu(&self) -> Mtu {
Mtu::no_limit()
}
}
#[derive(Clone, Debug, Default)]
struct DatagramIpAgnosticOptions {
transparent: bool,
marks: Marks,
}
impl<I: Ip> RouteResolutionOptions<I> for DatagramIpAgnosticOptions {
fn transparent(&self) -> bool {
self.transparent
}
fn marks(&self) -> &Marks {
&self.marks
}
}
struct IpOptionsRef<'a, I: IpExt, D: WeakDeviceIdentifier> {
ip_specific: &'a DatagramIpSpecificSocketOptions<I, D>,
agnostic: &'a DatagramIpAgnosticOptions,
}
impl<'a, I: IpExt, D: WeakDeviceIdentifier> OptionDelegationMarker for IpOptionsRef<'a, I, D> {}
impl<'a, I: IpExt, D: WeakDeviceIdentifier> DelegatedSendOptions<I> for IpOptionsRef<'a, I, D> {
fn delegate(&self) -> &impl SendOptions<I> {
self.ip_specific
}
}
impl<'a, I: IpExt, D: WeakDeviceIdentifier> DelegatedRouteResolutionOptions<I>
for IpOptionsRef<'a, I, D>
{
fn delegate(&self) -> &impl RouteResolutionOptions<I> {
self.agnostic
}
}
#[derive(Derivative, GenericOverIp)]
#[generic_over_ip(I, Ip)]
#[derivative(Clone(bound = ""), Debug, Default(bound = ""))]
pub struct IpOptions<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec + ?Sized> {
multicast_memberships: MulticastMemberships<I::Addr, D>,
socket_options: DatagramIpSpecificSocketOptions<I, D>,
other_stack: S::OtherStackIpOptions<I, D>,
common: DatagramIpAgnosticOptions,
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> AsRef<Self> for IpOptions<I, D, S> {
fn as_ref(&self) -> &Self {
self
}
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> IpOptions<I, D, S> {
pub fn other_stack(&self) -> &S::OtherStackIpOptions<I, D> {
&self.other_stack
}
pub fn transparent(&self) -> bool {
self.common.transparent
}
fn this_stack_options_ref(&self) -> IpOptionsRef<'_, I, D> {
IpOptionsRef { ip_specific: &self.socket_options, agnostic: &self.common }
}
fn other_stack_options_ref<
'a,
BC,
CC: DualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D>,
>(
&'a self,
ctx: &CC,
) -> IpOptionsRef<'_, I::OtherVersion, D> {
IpOptionsRef { ip_specific: ctx.to_other_socket_options(self), agnostic: &self.common }
}
}
#[derive(Clone, Debug, Derivative)]
#[derivative(Default(bound = ""))]
pub(crate) struct MulticastMemberships<A, D>(HashSet<(MulticastAddr<A>, D)>);
#[cfg_attr(test, derive(Debug, PartialEq))]
pub(crate) enum MulticastMembershipChange {
Join,
Leave,
}
impl<A: Eq + Hash, D: WeakDeviceIdentifier> MulticastMemberships<A, D> {
pub(crate) fn apply_membership_change(
&mut self,
address: MulticastAddr<A>,
device: &D,
want_membership: bool,
) -> Option<MulticastMembershipChange> {
let device = device.clone();
let Self(map) = self;
if want_membership {
map.insert((address, device)).then(|| MulticastMembershipChange::Join)
} else {
map.remove(&(address, device)).then(|| MulticastMembershipChange::Leave)
}
}
}
impl<A: Eq + Hash, D: Eq + Hash> IntoIterator for MulticastMemberships<A, D> {
type Item = (MulticastAddr<A>, D);
type IntoIter = <HashSet<(MulticastAddr<A>, D)> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
let Self(memberships) = self;
memberships.into_iter()
}
}
fn leave_all_joined_groups<A: IpAddress, BC, CC: MulticastMembershipHandler<A::Version, BC>>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
memberships: MulticastMemberships<A, CC::WeakDeviceId>,
) {
for (addr, device) in memberships {
let Some(device) = device.upgrade() else {
continue;
};
core_ctx.leave_multicast_group(bindings_ctx, &device, addr)
}
}
#[derive(Hash)]
pub struct DatagramFlowId<A: IpAddress, RI> {
pub local_ip: SocketIpAddr<A>,
pub remote_ip: SocketIpAddr<A>,
pub remote_id: RI,
}
pub trait DatagramStateContext<I: IpExt, BC, S: DatagramSocketSpec>:
DeviceIdContext<AnyDevice>
{
type SocketsStateCtx<'a>: DatagramBoundStateContext<I, BC, S>
+ DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>;
fn with_all_sockets_mut<O, F: FnOnce(&mut DatagramSocketSet<I, Self::WeakDeviceId, S>) -> O>(
&mut self,
cb: F,
) -> O;
fn with_all_sockets<O, F: FnOnce(&DatagramSocketSet<I, Self::WeakDeviceId, S>) -> O>(
&mut self,
cb: F,
) -> O;
fn with_socket_state<
O,
F: FnOnce(&mut Self::SocketsStateCtx<'_>, &SocketState<I, Self::WeakDeviceId, S>) -> O,
>(
&mut self,
id: &S::SocketId<I, Self::WeakDeviceId>,
cb: F,
) -> O;
fn with_socket_state_mut<
O,
F: FnOnce(&mut Self::SocketsStateCtx<'_>, &mut SocketState<I, Self::WeakDeviceId, S>) -> O,
>(
&mut self,
id: &S::SocketId<I, Self::WeakDeviceId>,
cb: F,
) -> O;
fn for_each_socket<
F: FnMut(
&mut Self::SocketsStateCtx<'_>,
&S::SocketId<I, Self::WeakDeviceId>,
&SocketState<I, Self::WeakDeviceId, S>,
),
>(
&mut self,
cb: F,
);
}
pub(crate) type BoundSocketsFromSpec<I, CC, S> = BoundSockets<
I,
<CC as DeviceIdContext<AnyDevice>>::WeakDeviceId,
<S as DatagramSocketSpec>::AddrSpec,
<S as DatagramSocketSpec>::SocketMapSpec<I, <CC as DeviceIdContext<AnyDevice>>::WeakDeviceId>,
>;
pub trait DatagramBoundStateContext<I: IpExt + DualStackIpExt, BC, S: DatagramSocketSpec>:
DeviceIdContext<AnyDevice>
{
type IpSocketsCtx<'a>: TransportIpContext<I, BC>
+ MulticastMembershipHandler<I, BC>
+ DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>;
type DualStackContext: DualStackDatagramBoundStateContext<
I,
BC,
S,
DeviceId = Self::DeviceId,
WeakDeviceId = Self::WeakDeviceId,
>;
type NonDualStackContext: NonDualStackDatagramBoundStateContext<
I,
BC,
S,
DeviceId = Self::DeviceId,
WeakDeviceId = Self::WeakDeviceId,
>;
fn with_bound_sockets<
O,
F: FnOnce(&mut Self::IpSocketsCtx<'_>, &BoundSocketsFromSpec<I, Self, S>) -> O,
>(
&mut self,
cb: F,
) -> O;
fn with_bound_sockets_mut<
O,
F: FnOnce(&mut Self::IpSocketsCtx<'_>, &mut BoundSocketsFromSpec<I, Self, S>) -> O,
>(
&mut self,
cb: F,
) -> O;
fn dual_stack_context(
&mut self,
) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext>;
fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
&mut self,
cb: F,
) -> O;
}
pub trait DualStackConverter<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>:
'static
+ OwnedOrRefsBidirectionalConverter<
S::ListenerIpAddr<I>,
DualStackListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
>
+ OwnedOrRefsBidirectionalConverter<
S::ConnIpAddr<I>,
DualStackConnIpAddr<
I::Addr,
<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>,
>
+ OwnedOrRefsBidirectionalConverter<S::ConnState<I, D>, DualStackConnState<I, D, S>>
{
}
impl<I, D, S, O> DualStackConverter<I, D, S> for O
where
I: IpExt,
D: WeakDeviceIdentifier,
S: DatagramSocketSpec,
O: 'static
+ OwnedOrRefsBidirectionalConverter<
S::ListenerIpAddr<I>,
DualStackListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
>
+ OwnedOrRefsBidirectionalConverter<
S::ConnIpAddr<I>,
DualStackConnIpAddr<
I::Addr,
<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>,
>
+ OwnedOrRefsBidirectionalConverter<S::ConnState<I, D>, DualStackConnState<I, D, S>>,
{
}
pub trait DualStackDatagramBoundStateContext<I: IpExt, BC, S: DatagramSocketSpec>:
DeviceIdContext<AnyDevice>
{
type IpSocketsCtx<'a>: TransportIpContext<I, BC>
+ DeviceIdContext<AnyDevice, DeviceId = Self::DeviceId, WeakDeviceId = Self::WeakDeviceId>
+ TransportIpContext<I::OtherVersion, BC>;
fn dual_stack_enabled(&self, state: &impl AsRef<IpOptions<I, Self::WeakDeviceId, S>>) -> bool;
fn to_other_socket_options<'a>(
&self,
state: &'a IpOptions<I, Self::WeakDeviceId, S>,
) -> &'a DatagramIpSpecificSocketOptions<I::OtherVersion, Self::WeakDeviceId>;
fn assert_dual_stack_enabled(&self, state: &impl AsRef<IpOptions<I, Self::WeakDeviceId, S>>) {
debug_assert!(self.dual_stack_enabled(state), "socket must be dual-stack enabled")
}
fn ds_converter(&self) -> impl DualStackConverter<I, Self::WeakDeviceId, S>;
fn to_other_bound_socket_id(
&self,
id: &S::SocketId<I, Self::WeakDeviceId>,
) -> <S::SocketMapSpec<I::OtherVersion, Self::WeakDeviceId> as DatagramSocketMapSpec<
I::OtherVersion,
Self::WeakDeviceId,
S::AddrSpec,
>>::BoundSocketId;
fn with_both_bound_sockets_mut<
O,
F: FnOnce(
&mut Self::IpSocketsCtx<'_>,
&mut BoundSocketsFromSpec<I, Self, S>,
&mut BoundSocketsFromSpec<I::OtherVersion, Self, S>,
) -> O,
>(
&mut self,
cb: F,
) -> O;
fn with_other_bound_sockets_mut<
O,
F: FnOnce(
&mut Self::IpSocketsCtx<'_>,
&mut BoundSocketsFromSpec<I::OtherVersion, Self, S>,
) -> O,
>(
&mut self,
cb: F,
) -> O;
fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
&mut self,
cb: F,
) -> O;
}
pub trait NonDualStackConverter<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>:
'static
+ OwnedOrRefsBidirectionalConverter<
S::ListenerIpAddr<I>,
ListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
>
+ OwnedOrRefsBidirectionalConverter<
S::ConnIpAddr<I>,
ConnIpAddr<
I::Addr,
<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>,
>
+ OwnedOrRefsBidirectionalConverter<S::ConnState<I, D>, ConnState<I, I, D, S>>
{
}
impl<I, D, S, O> NonDualStackConverter<I, D, S> for O
where
I: IpExt,
D: WeakDeviceIdentifier,
S: DatagramSocketSpec,
O: 'static
+ OwnedOrRefsBidirectionalConverter<
S::ListenerIpAddr<I>,
ListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
>
+ OwnedOrRefsBidirectionalConverter<
S::ConnIpAddr<I>,
ConnIpAddr<
I::Addr,
<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>,
>
+ OwnedOrRefsBidirectionalConverter<S::ConnState<I, D>, ConnState<I, I, D, S>>,
{
}
pub trait NonDualStackDatagramBoundStateContext<I: IpExt, BC, S: DatagramSocketSpec>:
DeviceIdContext<AnyDevice>
{
fn nds_converter(&self) -> impl NonDualStackConverter<I, Self::WeakDeviceId, S>;
}
pub trait DatagramStateBindingsContext<I: Ip, S>: RngContext + ReferenceNotifiers {}
impl<BC: RngContext + ReferenceNotifiers, I: Ip, S> DatagramStateBindingsContext<I, S> for BC {}
pub trait DatagramSocketMapSpec<I: Ip, D: DeviceIdentifier, A: SocketMapAddrSpec>:
SocketMapStateSpec<ListenerId = Self::BoundSocketId, ConnId = Self::BoundSocketId>
+ SocketMapConflictPolicy<
ListenerAddr<ListenerIpAddr<I::Addr, A::LocalIdentifier>, D>,
<Self as SocketMapStateSpec>::ListenerSharingState,
I,
D,
A,
> + SocketMapConflictPolicy<
ConnAddr<ConnIpAddr<I::Addr, A::LocalIdentifier, A::RemoteIdentifier>, D>,
<Self as SocketMapStateSpec>::ConnSharingState,
I,
D,
A,
>
{
type BoundSocketId: Clone + Debug;
}
pub trait DualStackIpExt:
DualStackBaseIpExt + socket::DualStackIpExt<OtherVersion: DualStackBaseIpExt>
{
}
impl<I> DualStackIpExt for I where
I: DualStackBaseIpExt + socket::DualStackIpExt<OtherVersion: DualStackBaseIpExt>
{
}
pub trait DualStackBaseIpExt: socket::DualStackIpExt + SocketIpExt + netstack3_base::IpExt {
type DualStackBoundSocketId<D: WeakDeviceIdentifier, S: DatagramSocketSpec>: Clone + Debug + Eq;
type OtherStackIpOptions<State: Clone + Debug + Default + Send + Sync>: Clone
+ Debug
+ Default
+ Send
+ Sync;
type DualStackListenerIpAddr<LocalIdentifier: Clone + Debug + Send + Sync + Into<NonZeroU16>>: Clone
+ Debug
+ Send
+ Sync
+ Into<(Option<SpecifiedAddr<Self::Addr>>, NonZeroU16)>;
type DualStackConnIpAddr<S: DatagramSocketSpec>: Clone
+ Debug
+ Into<ConnInfoAddr<Self::Addr, <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier>>;
type DualStackConnState<D: WeakDeviceIdentifier, S: DatagramSocketSpec>: Debug
+ AsRef<IpOptions<Self, D, S>>
+ AsMut<IpOptions<Self, D, S>>
+ Send
+ Sync
where
Self::OtherVersion: DualStackBaseIpExt;
fn into_dual_stack_bound_socket_id<D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
id: S::SocketId<Self, D>,
) -> Self::DualStackBoundSocketId<D, S>
where
Self: IpExt;
fn conn_addr_from_state<D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
state: &Self::DualStackConnState<D, S>,
) -> ConnAddr<Self::DualStackConnIpAddr<S>, D>
where
Self::OtherVersion: DualStackBaseIpExt;
}
#[derive(Derivative)]
#[derivative(
Clone(bound = ""),
Debug(bound = ""),
Eq(bound = "S::SocketId<Ipv4, D>: Eq, S::SocketId<Ipv6, D>: Eq"),
PartialEq(bound = "S::SocketId<Ipv4, D>: PartialEq, S::SocketId<Ipv6, D>: PartialEq")
)]
#[allow(missing_docs)]
pub enum EitherIpSocket<D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
V4(S::SocketId<Ipv4, D>),
V6(S::SocketId<Ipv6, D>),
}
impl DualStackBaseIpExt for Ipv4 {
type DualStackBoundSocketId<D: WeakDeviceIdentifier, S: DatagramSocketSpec> =
EitherIpSocket<D, S>;
type OtherStackIpOptions<State: Clone + Debug + Default + Send + Sync> = ();
type DualStackListenerIpAddr<LocalIdentifier: Clone + Debug + Send + Sync + Into<NonZeroU16>> =
ListenerIpAddr<Self::Addr, LocalIdentifier>;
type DualStackConnIpAddr<S: DatagramSocketSpec> = ConnIpAddr<
Self::Addr,
<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>;
type DualStackConnState<D: WeakDeviceIdentifier, S: DatagramSocketSpec> =
ConnState<Self, Self, D, S>;
fn into_dual_stack_bound_socket_id<D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
id: S::SocketId<Self, D>,
) -> Self::DualStackBoundSocketId<D, S> {
EitherIpSocket::V4(id)
}
fn conn_addr_from_state<D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
state: &Self::DualStackConnState<D, S>,
) -> ConnAddr<Self::DualStackConnIpAddr<S>, D> {
let ConnState {
socket: _,
ip_options: _,
shutdown: _,
addr,
clear_device_on_disconnect: _,
extra: _,
} = state;
addr.clone()
}
}
impl DualStackBaseIpExt for Ipv6 {
type DualStackBoundSocketId<D: WeakDeviceIdentifier, S: DatagramSocketSpec> =
S::SocketId<Self, D>;
type OtherStackIpOptions<State: Clone + Debug + Default + Send + Sync> = State;
type DualStackListenerIpAddr<LocalIdentifier: Clone + Debug + Send + Sync + Into<NonZeroU16>> =
DualStackListenerIpAddr<Self::Addr, LocalIdentifier>;
type DualStackConnIpAddr<S: DatagramSocketSpec> = DualStackConnIpAddr<
Self::Addr,
<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>;
type DualStackConnState<D: WeakDeviceIdentifier, S: DatagramSocketSpec> =
DualStackConnState<Self, D, S>;
fn into_dual_stack_bound_socket_id<D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
id: S::SocketId<Self, D>,
) -> Self::DualStackBoundSocketId<D, S> {
id
}
fn conn_addr_from_state<D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
state: &Self::DualStackConnState<D, S>,
) -> ConnAddr<Self::DualStackConnIpAddr<S>, D> {
match state {
DualStackConnState::ThisStack(state) => {
let ConnState { addr, .. } = state;
let ConnAddr { ip, device } = addr.clone();
ConnAddr { ip: DualStackConnIpAddr::ThisStack(ip), device }
}
DualStackConnState::OtherStack(state) => {
let ConnState {
socket: _,
ip_options: _,
shutdown: _,
addr,
clear_device_on_disconnect: _,
extra: _,
} = state;
let ConnAddr { ip, device } = addr.clone();
ConnAddr { ip: DualStackConnIpAddr::OtherStack(ip), device }
}
}
}
}
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
pub struct WrapOtherStackIpOptions<
'a,
I: DualStackIpExt,
S: 'a + Clone + Debug + Default + Send + Sync,
>(pub &'a I::OtherStackIpOptions<S>);
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
pub struct WrapOtherStackIpOptionsMut<
'a,
I: DualStackIpExt,
S: 'a + Clone + Debug + Default + Send + Sync,
>(pub &'a mut I::OtherStackIpOptions<S>);
pub trait DatagramSocketSpec: Sized + 'static {
const NAME: &'static str;
type AddrSpec: SocketMapAddrSpec;
type SocketId<I: IpExt, D: WeakDeviceIdentifier>: Clone
+ Debug
+ Eq
+ Send
+ Borrow<StrongRc<I, D, Self>>
+ From<StrongRc<I, D, Self>>;
type OtherStackIpOptions<I: IpExt, D: WeakDeviceIdentifier>: Clone
+ Debug
+ Default
+ Send
+ Sync;
type ListenerIpAddr<I: IpExt>: Clone
+ Debug
+ Into<(Option<SpecifiedAddr<I::Addr>>, NonZeroU16)>
+ Send
+ Sync
+ 'static;
type SharingState: Clone + Debug + Default + Send + Sync + 'static;
type ConnIpAddr<I: IpExt>: Clone
+ Debug
+ Into<ConnInfoAddr<I::Addr, <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier>>;
type ConnState<I: IpExt, D: WeakDeviceIdentifier>: Debug
+ AsRef<IpOptions<I, D, Self>>
+ AsMut<IpOptions<I, D, Self>>
+ Send
+ Sync;
type ConnStateExtra: Debug + Send + Sync;
type SocketMapSpec<I: IpExt + DualStackIpExt, D: WeakDeviceIdentifier>: DatagramSocketMapSpec<
I,
D,
Self::AddrSpec,
ListenerSharingState = Self::SharingState,
ConnSharingState = Self::SharingState,
>;
type ExternalData<I: Ip>: Debug + Send + Sync + 'static;
fn ip_proto<I: IpProtoExt>() -> I::Proto;
fn make_bound_socket_map_id<I: IpExt, D: WeakDeviceIdentifier>(
s: &Self::SocketId<I, D>,
) -> <Self::SocketMapSpec<I, D> as DatagramSocketMapSpec<I, D, Self::AddrSpec>>::BoundSocketId;
type Serializer<I: IpExt, B: BufferMut>: TransportPacketSerializer<I, Buffer = B>;
type SerializeError;
fn make_packet<I: IpExt, B: BufferMut>(
body: B,
addr: &ConnIpAddr<
I::Addr,
<Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>,
) -> Result<Self::Serializer<I, B>, Self::SerializeError>;
fn try_alloc_listen_identifier<I: IpExt, D: WeakDeviceIdentifier>(
rng: &mut impl RngContext,
is_available: impl Fn(
<Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
) -> Result<(), InUseError>,
) -> Option<<Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>;
fn conn_info_from_state<I: IpExt, D: WeakDeviceIdentifier>(
state: &Self::ConnState<I, D>,
) -> ConnInfo<I::Addr, D>;
fn try_alloc_local_id<I: IpExt, D: WeakDeviceIdentifier, BC: RngContext>(
bound: &BoundSocketMap<I, D, Self::AddrSpec, Self::SocketMapSpec<I, D>>,
bindings_ctx: &mut BC,
flow: DatagramFlowId<I::Addr, <Self::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier>,
) -> Option<<Self::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>;
}
pub struct InUseError;
pub fn create_primary_id<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
external_data: S::ExternalData<I>,
) -> PrimaryRc<I, D, S> {
PrimaryRc::new(ReferenceState {
state: RwLock::new(SocketState::Unbound(UnboundSocketState::default())),
external_data,
})
}
#[derive(GenericOverIp, Debug, Eq, PartialEq)]
#[generic_over_ip(A, IpAddress)]
pub struct ListenerInfo<A: IpAddress, D> {
pub local_ip: Option<StrictlyZonedAddr<A, SpecifiedAddr<A>, D>>,
pub local_identifier: NonZeroU16,
}
impl<A: IpAddress, LA: Into<(Option<SpecifiedAddr<A>>, NonZeroU16)>, D> From<ListenerAddr<LA, D>>
for ListenerInfo<A, D>
{
fn from(ListenerAddr { ip, device }: ListenerAddr<LA, D>) -> Self {
let (addr, local_identifier) = ip.into();
Self {
local_ip: addr.map(|addr| {
StrictlyZonedAddr::new_with_zone(addr, || {
device.expect("device must be bound for addresses that require zones")
})
}),
local_identifier,
}
}
}
impl<A: IpAddress, D> From<NonZeroU16> for ListenerInfo<A, D> {
fn from(local_identifier: NonZeroU16) -> Self {
Self { local_ip: None, local_identifier }
}
}
#[derive(Debug, GenericOverIp, PartialEq)]
#[generic_over_ip(A, IpAddress)]
pub struct ConnInfo<A: IpAddress, D> {
pub local_ip: StrictlyZonedAddr<A, SpecifiedAddr<A>, D>,
pub local_identifier: NonZeroU16,
pub remote_ip: StrictlyZonedAddr<A, SpecifiedAddr<A>, D>,
pub remote_identifier: u16,
}
impl<A: IpAddress, D> ConnInfo<A, D> {
pub fn new(
local_ip: SpecifiedAddr<A>,
local_identifier: NonZeroU16,
remote_ip: SpecifiedAddr<A>,
remote_identifier: u16,
mut get_zone: impl FnMut() -> D,
) -> Self {
Self {
local_ip: StrictlyZonedAddr::new_with_zone(local_ip, &mut get_zone),
local_identifier,
remote_ip: StrictlyZonedAddr::new_with_zone(remote_ip, &mut get_zone),
remote_identifier,
}
}
}
#[derive(GenericOverIp, Debug, PartialEq)]
#[generic_over_ip(A, IpAddress)]
pub enum SocketInfo<A: IpAddress, D> {
Unbound,
Listener(ListenerInfo<A, D>),
Connected(ConnInfo<A, D>),
}
enum Remove<WireI: IpExt, SocketI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
Listener {
concrete_addr: ListenerAddr<
ListenerIpAddr<WireI::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
D,
>,
ip_options: IpOptions<SocketI, D, S>,
sharing: S::SharingState,
socket_id:
<S::SocketMapSpec<WireI, D> as DatagramSocketMapSpec<WireI, D, S::AddrSpec>>::BoundSocketId,
},
Connected {
concrete_addr: ConnAddr<
ConnIpAddr<
WireI::Addr,
<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>,
D,
>,
ip_options: IpOptions<SocketI, D, S>,
sharing: S::SharingState,
socket_id:
<S::SocketMapSpec<WireI, D> as DatagramSocketMapSpec<WireI, D, S::AddrSpec>>::BoundSocketId,
},
}
struct RemoveOperation<WireI: IpExt, SocketI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
Remove<WireI, SocketI, D, S>,
);
impl<WireI: IpExt, SocketI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
RemoveOperation<WireI, SocketI, D, S>
{
fn apply(
self,
sockets: &mut BoundSocketMap<WireI, D, S::AddrSpec, S::SocketMapSpec<WireI, D>>,
) -> RemoveInfo<WireI, SocketI, D, S> {
let RemoveOperation(remove) = self;
match &remove {
Remove::Listener { concrete_addr, ip_options: _, sharing: _, socket_id } => {
let ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device } =
concrete_addr;
BoundStateHandler::<_, S, _>::remove_listener(
sockets,
addr,
*identifier,
device,
socket_id,
);
}
Remove::Connected { concrete_addr, ip_options: _, sharing: _, socket_id } => {
sockets
.conns_mut()
.remove(socket_id, concrete_addr)
.expect("UDP connection not found");
}
}
RemoveInfo(remove)
}
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> RemoveOperation<I, I, D, S> {
fn new_from_state<BC, CC: NonDualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D>>(
core_ctx: &mut CC,
socket_id: &S::SocketId<I, D>,
state: &BoundSocketState<I, D, S>,
) -> Self {
let BoundSocketState { socket_type: state, original_bound_addr: _ } = state;
RemoveOperation(match state {
BoundSocketStateType::Listener {
state: ListenerState { addr: ListenerAddr { ip, device }, ip_options },
sharing,
} => Remove::Listener {
concrete_addr: ListenerAddr {
ip: core_ctx.nds_converter().convert(ip.clone()),
device: device.clone(),
},
ip_options: ip_options.clone(),
sharing: sharing.clone(),
socket_id: S::make_bound_socket_map_id(socket_id),
},
BoundSocketStateType::Connected { state, sharing } => {
let ConnState {
addr,
socket: _,
ip_options,
clear_device_on_disconnect: _,
shutdown: _,
extra: _,
} = core_ctx.nds_converter().convert(state);
Remove::Connected {
concrete_addr: addr.clone(),
ip_options: ip_options.clone(),
sharing: sharing.clone(),
socket_id: S::make_bound_socket_map_id(socket_id),
}
}
})
}
}
struct RemoveInfo<WireI: IpExt, SocketI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
Remove<WireI, SocketI, D, S>,
);
impl<WireI: IpExt, SocketI: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
RemoveInfo<WireI, SocketI, D, S>
{
fn into_options(self) -> IpOptions<SocketI, D, S> {
let RemoveInfo(remove) = self;
match remove {
Remove::Listener { concrete_addr: _, ip_options, sharing: _, socket_id: _ } => {
ip_options
}
Remove::Connected { concrete_addr: _, ip_options, sharing: _, socket_id: _ } => {
ip_options
}
}
}
fn into_options_sharing_and_device(
self,
) -> (IpOptions<SocketI, D, S>, S::SharingState, Option<D>) {
let RemoveInfo(remove) = self;
match remove {
Remove::Listener {
concrete_addr: ListenerAddr { ip: _, device },
ip_options,
sharing,
socket_id: _,
} => (ip_options, sharing, device),
Remove::Connected {
concrete_addr: ConnAddr { ip: _, device },
ip_options,
sharing,
socket_id: _,
} => (ip_options, sharing, device),
}
}
fn reinsert(
self,
sockets: &mut BoundSocketMap<WireI, D, S::AddrSpec, S::SocketMapSpec<WireI, D>>,
) {
let RemoveInfo(remove) = self;
match remove {
Remove::Listener {
concrete_addr: ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device },
ip_options: _,
sharing,
socket_id,
} => {
BoundStateHandler::<_, S, _>::try_insert_listener(
sockets, addr, identifier, device, sharing, socket_id,
)
.expect("reinserting just-removed listener failed");
}
Remove::Connected { concrete_addr, ip_options: _, sharing, socket_id } => {
let _entry = sockets
.conns_mut()
.try_insert(concrete_addr, sharing, socket_id)
.expect("reinserting just-removed connected failed");
}
}
}
}
type SingleStackRemoveOperation<I, D, S> = RemoveOperation<I, I, D, S>;
type SingleStackRemoveInfo<I, D, S> = RemoveInfo<I, I, D, S>;
enum DualStackRemove<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
CurrentStack(Remove<I, I, D, S>),
OtherStack(Remove<I::OtherVersion, I, D, S>),
ListenerBothStacks {
identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
device: Option<D>,
ip_options: IpOptions<I, D, S>,
sharing: S::SharingState,
socket_ids: PairedBoundSocketIds<I, D, S>,
},
}
struct DualStackRemoveOperation<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
DualStackRemove<I, D, S>,
);
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> DualStackRemoveOperation<I, D, S> {
fn new_from_state<BC, CC: DualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D>>(
core_ctx: &mut CC,
socket_id: &S::SocketId<I, D>,
state: &BoundSocketState<I, D, S>,
) -> Self {
let BoundSocketState { socket_type: state, original_bound_addr: _ } = state;
DualStackRemoveOperation(match state {
BoundSocketStateType::Listener {
state: ListenerState { addr, ip_options },
sharing,
} => {
let ListenerAddr { ip, device } = addr.clone();
match (
core_ctx.ds_converter().convert(ip),
core_ctx.dual_stack_enabled(&ip_options),
) {
(DualStackListenerIpAddr::BothStacks(identifier), true) => {
DualStackRemove::ListenerBothStacks {
identifier: identifier.clone(),
device,
ip_options: ip_options.clone(),
sharing: sharing.clone(),
socket_ids: PairedBoundSocketIds {
this: S::make_bound_socket_map_id(socket_id),
other: core_ctx.to_other_bound_socket_id(socket_id),
},
}
}
(DualStackListenerIpAddr::ThisStack(addr), true | false) => {
DualStackRemove::CurrentStack(Remove::Listener {
concrete_addr: ListenerAddr { ip: addr, device },
ip_options: ip_options.clone(),
sharing: sharing.clone(),
socket_id: S::make_bound_socket_map_id(socket_id),
})
}
(DualStackListenerIpAddr::OtherStack(addr), true) => {
DualStackRemove::OtherStack(Remove::Listener {
concrete_addr: ListenerAddr { ip: addr, device },
ip_options: ip_options.clone(),
sharing: sharing.clone(),
socket_id: core_ctx.to_other_bound_socket_id(socket_id),
})
}
(DualStackListenerIpAddr::OtherStack(_), false)
| (DualStackListenerIpAddr::BothStacks(_), false) => {
unreachable!("dual-stack disabled socket cannot use the other stack")
}
}
}
BoundSocketStateType::Connected { state, sharing } => {
match core_ctx.ds_converter().convert(state) {
DualStackConnState::ThisStack(state) => {
let ConnState {
addr,
socket: _,
ip_options,
clear_device_on_disconnect: _,
shutdown: _,
extra: _,
} = state;
DualStackRemove::CurrentStack(Remove::Connected {
concrete_addr: addr.clone(),
ip_options: ip_options.clone(),
sharing: sharing.clone(),
socket_id: S::make_bound_socket_map_id(socket_id),
})
}
DualStackConnState::OtherStack(state) => {
core_ctx.assert_dual_stack_enabled(&state);
let ConnState {
addr,
socket: _,
ip_options,
clear_device_on_disconnect: _,
shutdown: _,
extra: _,
} = state;
DualStackRemove::OtherStack(Remove::Connected {
concrete_addr: addr.clone(),
ip_options: ip_options.clone(),
sharing: sharing.clone(),
socket_id: core_ctx.to_other_bound_socket_id(socket_id),
})
}
}
}
})
}
fn apply(
self,
sockets: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>,
other_sockets: &mut BoundSocketMap<
I::OtherVersion,
D,
S::AddrSpec,
S::SocketMapSpec<I::OtherVersion, D>,
>,
) -> DualStackRemoveInfo<I, D, S> {
let DualStackRemoveOperation(dual_stack_remove) = self;
match dual_stack_remove {
DualStackRemove::CurrentStack(remove) => {
let RemoveInfo(remove) = RemoveOperation(remove).apply(sockets);
DualStackRemoveInfo(DualStackRemove::CurrentStack(remove))
}
DualStackRemove::OtherStack(remove) => {
let RemoveInfo(remove) = RemoveOperation(remove).apply(other_sockets);
DualStackRemoveInfo(DualStackRemove::OtherStack(remove))
}
DualStackRemove::ListenerBothStacks {
identifier,
device,
ip_options,
sharing,
socket_ids,
} => {
PairedSocketMapMut::<_, _, S> { bound: sockets, other_bound: other_sockets }
.remove_listener(&DualStackUnspecifiedAddr, identifier, &device, &socket_ids);
DualStackRemoveInfo(DualStackRemove::ListenerBothStacks {
identifier,
device,
ip_options,
sharing,
socket_ids,
})
}
}
}
}
struct DualStackRemoveInfo<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
DualStackRemove<I, D, S>,
);
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> DualStackRemoveInfo<I, D, S> {
fn into_options(self) -> IpOptions<I, D, S> {
let DualStackRemoveInfo(dual_stack_remove) = self;
match dual_stack_remove {
DualStackRemove::CurrentStack(remove) => RemoveInfo(remove).into_options(),
DualStackRemove::OtherStack(remove) => RemoveInfo(remove).into_options(),
DualStackRemove::ListenerBothStacks {
identifier: _,
device: _,
ip_options,
sharing: _,
socket_ids: _,
} => ip_options,
}
}
fn into_options_sharing_and_device(self) -> (IpOptions<I, D, S>, S::SharingState, Option<D>) {
let DualStackRemoveInfo(dual_stack_remove) = self;
match dual_stack_remove {
DualStackRemove::CurrentStack(remove) => {
RemoveInfo(remove).into_options_sharing_and_device()
}
DualStackRemove::OtherStack(remove) => {
RemoveInfo(remove).into_options_sharing_and_device()
}
DualStackRemove::ListenerBothStacks {
identifier: _,
device,
ip_options,
sharing,
socket_ids: _,
} => (ip_options, sharing, device),
}
}
fn reinsert(
self,
sockets: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>,
other_sockets: &mut BoundSocketMap<
I::OtherVersion,
D,
S::AddrSpec,
S::SocketMapSpec<I::OtherVersion, D>,
>,
) {
let DualStackRemoveInfo(dual_stack_remove) = self;
match dual_stack_remove {
DualStackRemove::CurrentStack(remove) => {
RemoveInfo(remove).reinsert(sockets);
}
DualStackRemove::OtherStack(remove) => {
RemoveInfo(remove).reinsert(other_sockets);
}
DualStackRemove::ListenerBothStacks {
identifier,
device,
ip_options: _,
sharing,
socket_ids,
} => {
let mut socket_maps_pair =
PairedSocketMapMut { bound: sockets, other_bound: other_sockets };
BoundStateHandler::<_, S, _>::try_insert_listener(
&mut socket_maps_pair,
DualStackUnspecifiedAddr,
identifier,
device,
sharing,
socket_ids,
)
.expect("reinserting just-removed listener failed")
}
}
}
}
trait BoundStateHandler<I: IpExt, S: DatagramSocketSpec, D: WeakDeviceIdentifier> {
type ListenerAddr: Clone;
type BoundSocketId;
fn is_listener_entry_available(
&self,
addr: Self::ListenerAddr,
identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
sharing_state: &S::SharingState,
) -> bool;
fn try_insert_listener(
&mut self,
addr: Self::ListenerAddr,
identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
device: Option<D>,
sharing: S::SharingState,
id: Self::BoundSocketId,
) -> Result<(), LocalAddressError>;
fn remove_listener(
&mut self,
addr: &Self::ListenerAddr,
identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
device: &Option<D>,
id: &Self::BoundSocketId,
);
}
#[derive(Copy, Clone, Debug)]
struct DualStackUnspecifiedAddr;
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> BoundStateHandler<I, S, D>
for BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>
{
type ListenerAddr = Option<SocketIpAddr<I::Addr>>;
type BoundSocketId =
<S::SocketMapSpec<I, D> as DatagramSocketMapSpec<I, D, S::AddrSpec>>::BoundSocketId;
fn is_listener_entry_available(
&self,
addr: Self::ListenerAddr,
identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
sharing: &S::SharingState,
) -> bool {
let check_addr = ListenerAddr { device: None, ip: ListenerIpAddr { identifier, addr } };
match self.listeners().could_insert(&check_addr, sharing) {
Ok(()) => true,
Err(
InsertError::Exists
| InsertError::IndirectConflict
| InsertError::ShadowAddrExists
| InsertError::ShadowerExists,
) => false,
}
}
fn try_insert_listener(
&mut self,
addr: Self::ListenerAddr,
identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
device: Option<D>,
sharing: S::SharingState,
id: Self::BoundSocketId,
) -> Result<(), LocalAddressError> {
try_insert_single_listener(self, addr, identifier, device, sharing, id).map(|_entry| ())
}
fn remove_listener(
&mut self,
addr: &Self::ListenerAddr,
identifier: <<S as DatagramSocketSpec>::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
device: &Option<D>,
id: &Self::BoundSocketId,
) {
remove_single_listener(self, addr, identifier, device, id)
}
}
struct PairedSocketMapMut<'a, I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
bound: &'a mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>,
other_bound: &'a mut BoundSocketMap<
I::OtherVersion,
D,
S::AddrSpec,
S::SocketMapSpec<I::OtherVersion, D>,
>,
}
struct PairedBoundSocketIds<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
this: <S::SocketMapSpec<I, D> as DatagramSocketMapSpec<I, D, S::AddrSpec>>::BoundSocketId,
other: <S::SocketMapSpec<I::OtherVersion, D> as DatagramSocketMapSpec<
I::OtherVersion,
D,
S::AddrSpec,
>>::BoundSocketId,
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> BoundStateHandler<I, S, D>
for PairedSocketMapMut<'_, I, D, S>
{
type ListenerAddr = DualStackUnspecifiedAddr;
type BoundSocketId = PairedBoundSocketIds<I, D, S>;
fn is_listener_entry_available(
&self,
DualStackUnspecifiedAddr: Self::ListenerAddr,
identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
sharing: &S::SharingState,
) -> bool {
let PairedSocketMapMut { bound, other_bound } = self;
BoundStateHandler::<I, S, D>::is_listener_entry_available(*bound, None, identifier, sharing)
&& BoundStateHandler::<I::OtherVersion, S, D>::is_listener_entry_available(
*other_bound,
None,
identifier,
sharing,
)
}
fn try_insert_listener(
&mut self,
DualStackUnspecifiedAddr: Self::ListenerAddr,
identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
device: Option<D>,
sharing: S::SharingState,
id: Self::BoundSocketId,
) -> Result<(), LocalAddressError> {
let PairedSocketMapMut { bound: this, other_bound: other } = self;
let PairedBoundSocketIds { this: this_id, other: other_id } = id;
try_insert_single_listener(this, None, identifier, device.clone(), sharing.clone(), this_id)
.and_then(|first_entry| {
match try_insert_single_listener(other, None, identifier, device, sharing, other_id)
{
Ok(_second_entry) => Ok(()),
Err(e) => {
first_entry.remove();
Err(e)
}
}
})
}
fn remove_listener(
&mut self,
DualStackUnspecifiedAddr: &Self::ListenerAddr,
identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
device: &Option<D>,
id: &PairedBoundSocketIds<I, D, S>,
) {
let PairedSocketMapMut { bound: this, other_bound: other } = self;
let PairedBoundSocketIds { this: this_id, other: other_id } = id;
remove_single_listener(this, &None, identifier, device, this_id);
remove_single_listener(other, &None, identifier, device, other_id);
}
}
fn try_insert_single_listener<
I: IpExt,
D: WeakDeviceIdentifier,
A: SocketMapAddrSpec,
S: DatagramSocketMapSpec<I, D, A>,
>(
bound: &mut BoundSocketMap<I, D, A, S>,
addr: Option<SocketIpAddr<I::Addr>>,
identifier: A::LocalIdentifier,
device: Option<D>,
sharing: S::ListenerSharingState,
id: S::ListenerId,
) -> Result<socket::SocketStateEntry<'_, I, D, A, S, socket::Listener>, LocalAddressError> {
bound
.listeners_mut()
.try_insert(ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device }, sharing, id)
.map_err(|e| match e {
(
InsertError::ShadowAddrExists
| InsertError::Exists
| InsertError::IndirectConflict
| InsertError::ShadowerExists,
sharing,
) => {
let _: S::ListenerSharingState = sharing;
LocalAddressError::AddressInUse
}
})
}
fn remove_single_listener<
I: IpExt,
D: WeakDeviceIdentifier,
A: SocketMapAddrSpec,
S: DatagramSocketMapSpec<I, D, A>,
>(
bound: &mut BoundSocketMap<I, D, A, S>,
addr: &Option<SocketIpAddr<I::Addr>>,
identifier: A::LocalIdentifier,
device: &Option<D>,
id: &S::ListenerId,
) {
let addr =
ListenerAddr { ip: ListenerIpAddr { addr: *addr, identifier }, device: device.clone() };
bound
.listeners_mut()
.remove(id, &addr)
.unwrap_or_else(|NotFoundError| panic!("socket ID {:?} not found for {:?}", id, addr))
}
fn try_pick_identifier<
I: IpExt,
S: DatagramSocketSpec,
D: WeakDeviceIdentifier,
BS: BoundStateHandler<I, S, D>,
BC: RngContext,
>(
addr: BS::ListenerAddr,
bound: &BS,
bindings_ctx: &mut BC,
sharing: &S::SharingState,
) -> Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier> {
S::try_alloc_listen_identifier::<I, D>(bindings_ctx, move |identifier| {
bound
.is_listener_entry_available(addr.clone(), identifier, sharing)
.then_some(())
.ok_or(InUseError)
})
}
fn try_pick_bound_address<I: IpExt, CC: TransportIpContext<I, BC>, BC, LI>(
addr: Option<ZonedAddr<SocketIpAddr<I::Addr>, CC::DeviceId>>,
device: &Option<CC::WeakDeviceId>,
core_ctx: &mut CC,
identifier: LI,
transparent: bool,
) -> Result<
(Option<SocketIpAddr<I::Addr>>, Option<EitherDeviceId<CC::DeviceId, CC::WeakDeviceId>>, LI),
LocalAddressError,
> {
let (addr, device, identifier) = match addr {
Some(addr) => {
let (addr, device) = addr.resolve_addr_with_device(device.clone())?;
if !addr.addr().is_multicast() && !transparent {
BaseTransportIpContext::<I, _>::with_devices_with_assigned_addr(
core_ctx,
addr.into(),
|mut assigned_to| {
if let Some(device) = &device {
if !assigned_to.any(|d| device == &EitherDeviceId::Strong(d)) {
return Err(LocalAddressError::AddressMismatch);
}
} else {
if !assigned_to.any(|_: CC::DeviceId| true) {
return Err(LocalAddressError::CannotBindToAddress);
}
}
Ok(())
},
)?;
}
(Some(addr), device, identifier)
}
None => (None, device.clone().map(EitherDeviceId::Weak), identifier),
};
Ok((addr, device, identifier))
}
fn listen_inner<
I: IpExt,
BC: DatagramStateBindingsContext<I, S>,
CC: DatagramBoundStateContext<I, BC, S>,
S: DatagramSocketSpec,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
state: &mut SocketState<I, CC::WeakDeviceId, S>,
id: &S::SocketId<I, CC::WeakDeviceId>,
addr: Option<ZonedAddr<SpecifiedAddr<I::Addr>, CC::DeviceId>>,
local_id: Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
) -> Result<(), Either<ExpectedUnboundError, LocalAddressError>> {
#[derive(Debug, GenericOverIp)]
#[generic_over_ip(I, Ip)]
enum BoundOperation<'a, I: IpExt, DS: DeviceIdContext<AnyDevice>, NDS> {
DualStackAnyAddr(&'a mut DS),
OnlyCurrentStack(
MaybeDualStack<&'a mut DS, &'a mut NDS>,
Option<ZonedAddr<SocketIpAddr<I::Addr>, DS::DeviceId>>,
),
OnlyOtherStack(
&'a mut DS,
Option<ZonedAddr<SocketIpAddr<<I::OtherVersion as Ip>::Addr>, DS::DeviceId>>,
),
}
let UnboundSocketState { device, sharing, ip_options } = match state {
SocketState::Unbound(state) => state,
SocketState::Bound(_) => return Err(Either::Left(ExpectedUnboundError)),
};
let dual_stack = core_ctx.dual_stack_context();
let bound_operation: BoundOperation<'_, I, _, _> = match (dual_stack, addr) {
(MaybeDualStack::DualStack(dual_stack), None) => {
match dual_stack.dual_stack_enabled(ip_options) {
true => BoundOperation::DualStackAnyAddr(dual_stack),
false => {
BoundOperation::OnlyCurrentStack(MaybeDualStack::DualStack(dual_stack), None)
}
}
}
(MaybeDualStack::DualStack(dual_stack), Some(addr)) => {
match DualStackLocalIp::<I, _>::new(addr) {
DualStackLocalIp::ThisStack(addr) => BoundOperation::OnlyCurrentStack(
MaybeDualStack::DualStack(dual_stack),
Some(addr),
),
DualStackLocalIp::OtherStack(addr) => {
match dual_stack.dual_stack_enabled(ip_options) {
true => BoundOperation::OnlyOtherStack(dual_stack, addr),
false => return Err(Either::Right(LocalAddressError::CannotBindToAddress)),
}
}
}
}
(MaybeDualStack::NotDualStack(single_stack), None) => {
BoundOperation::OnlyCurrentStack(MaybeDualStack::NotDualStack(single_stack), None)
}
(MaybeDualStack::NotDualStack(single_stack), Some(addr)) => {
match DualStackLocalIp::<I, _>::new(addr) {
DualStackLocalIp::ThisStack(addr) => BoundOperation::OnlyCurrentStack(
MaybeDualStack::NotDualStack(single_stack),
Some(addr),
),
DualStackLocalIp::OtherStack(_addr) => {
let _: Option<ZonedAddr<SocketIpAddr<<I::OtherVersion as Ip>::Addr>, _>> =
_addr;
return Err(Either::Right(LocalAddressError::CannotBindToAddress));
}
}
}
};
fn try_bind_single_stack<
I: IpExt,
S: DatagramSocketSpec,
CC: TransportIpContext<I, BC>,
BC: RngContext,
>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
bound: &mut BoundSocketMap<
I,
CC::WeakDeviceId,
S::AddrSpec,
S::SocketMapSpec<I, CC::WeakDeviceId>,
>,
addr: Option<ZonedAddr<SocketIpAddr<I::Addr>, CC::DeviceId>>,
device: &Option<CC::WeakDeviceId>,
local_id: Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
id: <S::SocketMapSpec<I, CC::WeakDeviceId> as SocketMapStateSpec>::ListenerId,
sharing: S::SharingState,
transparent: bool,
) -> Result<
ListenerAddr<
ListenerIpAddr<I::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
CC::WeakDeviceId,
>,
LocalAddressError,
> {
let identifier = match local_id {
Some(id) => Some(id),
None => try_pick_identifier::<I, S, _, _, _>(
addr.as_ref().map(ZonedAddr::addr),
bound,
bindings_ctx,
&sharing,
),
}
.ok_or(LocalAddressError::FailedToAllocateLocalPort)?;
let (addr, device, identifier) =
try_pick_bound_address::<I, _, _, _>(addr, device, core_ctx, identifier, transparent)?;
let weak_device = device.map(|d| d.as_weak().into_owned());
BoundStateHandler::<_, S, _>::try_insert_listener(
bound,
addr,
identifier,
weak_device.clone(),
sharing,
id,
)
.map(|()| ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device: weak_device })
}
let bound_addr: ListenerAddr<S::ListenerIpAddr<I>, CC::WeakDeviceId> = match bound_operation {
BoundOperation::OnlyCurrentStack(either_dual_stack, addr) => {
let converter = match either_dual_stack {
MaybeDualStack::DualStack(ds) => MaybeDualStack::DualStack(ds.ds_converter()),
MaybeDualStack::NotDualStack(nds) => {
MaybeDualStack::NotDualStack(nds.nds_converter())
}
};
core_ctx
.with_bound_sockets_mut(|core_ctx, bound| {
let id = S::make_bound_socket_map_id(id);
try_bind_single_stack::<I, S, _, _>(
core_ctx,
bindings_ctx,
bound,
addr,
device,
local_id,
id,
sharing.clone(),
ip_options.common.transparent,
)
})
.map(|ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device }| {
let ip = match converter {
MaybeDualStack::DualStack(converter) => converter.convert_back(
DualStackListenerIpAddr::ThisStack(ListenerIpAddr { addr, identifier }),
),
MaybeDualStack::NotDualStack(converter) => {
converter.convert_back(ListenerIpAddr { addr, identifier })
}
};
ListenerAddr { ip, device }
})
}
BoundOperation::OnlyOtherStack(core_ctx, addr) => {
let id = core_ctx.to_other_bound_socket_id(id);
core_ctx
.with_other_bound_sockets_mut(|core_ctx, other_bound| {
try_bind_single_stack::<_, S, _, _>(
core_ctx,
bindings_ctx,
other_bound,
addr,
device,
local_id,
id,
sharing.clone(),
ip_options.common.transparent,
)
})
.map(|ListenerAddr { ip: ListenerIpAddr { addr, identifier }, device }| {
ListenerAddr {
ip: core_ctx.ds_converter().convert_back(
DualStackListenerIpAddr::OtherStack(ListenerIpAddr {
addr,
identifier,
}),
),
device,
}
})
}
BoundOperation::DualStackAnyAddr(core_ctx) => {
let ids = PairedBoundSocketIds {
this: S::make_bound_socket_map_id(id),
other: core_ctx.to_other_bound_socket_id(id),
};
core_ctx
.with_both_bound_sockets_mut(|core_ctx, bound, other_bound| {
let mut bound_pair = PairedSocketMapMut { bound, other_bound };
let sharing = sharing.clone();
let identifier = match local_id {
Some(id) => Some(id),
None => try_pick_identifier::<I, S, _, _, _>(
DualStackUnspecifiedAddr,
&bound_pair,
bindings_ctx,
&sharing,
),
}
.ok_or(LocalAddressError::FailedToAllocateLocalPort)?;
let (_addr, device, identifier) = try_pick_bound_address::<I, _, _, _>(
None,
device,
core_ctx,
identifier,
ip_options.common.transparent,
)?;
let weak_device = device.map(|d| d.as_weak().into_owned());
BoundStateHandler::<_, S, _>::try_insert_listener(
&mut bound_pair,
DualStackUnspecifiedAddr,
identifier,
weak_device.clone(),
sharing,
ids,
)
.map(|()| (identifier, weak_device))
})
.map(|(identifier, device)| ListenerAddr {
ip: core_ctx
.ds_converter()
.convert_back(DualStackListenerIpAddr::BothStacks(identifier)),
device,
})
}
}
.map_err(Either::Right)?;
let original_bound_addr = local_id.map(|_id| {
let ListenerAddr { ip, device: _ } = &bound_addr;
ip.clone()
});
*state = SocketState::Bound(BoundSocketState {
socket_type: BoundSocketStateType::Listener {
state: ListenerState {
ip_options: ip_options.clone(),
addr: bound_addr,
},
sharing: sharing.clone(),
},
original_bound_addr,
});
Ok(())
}
#[derive(Error, Copy, Clone, Debug, Eq, PartialEq)]
pub enum ConnectError {
#[error(transparent)]
Ip(#[from] IpSockCreationError),
#[error("a local port could not be allocated")]
CouldNotAllocateLocalPort,
#[error("the socket's IP address and port conflict with an existing socket")]
SockAddrConflict,
#[error(transparent)]
Zone(#[from] ZonedAddressError),
#[error("IPv4-mapped-IPv6 addresses are not supported by this socket")]
RemoteUnexpectedlyMapped,
#[error("non IPv4-mapped-Ipv6 addresses are not supported by this socket")]
RemoteUnexpectedlyNonMapped,
}
struct ConnectParameters<
WireI: IpExt,
SocketI: IpExt,
D: WeakDeviceIdentifier,
S: DatagramSocketSpec,
> {
local_ip: Option<SocketIpAddr<WireI::Addr>>,
local_port: Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
remote_ip: ZonedAddr<SocketIpAddr<WireI::Addr>, D::Strong>,
remote_port: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
device: Option<D>,
sharing: S::SharingState,
ip_options: IpOptions<SocketI, D, S>,
socket_options: DatagramIpSpecificSocketOptions<WireI, D>,
socket_id:
<S::SocketMapSpec<WireI, D> as DatagramSocketMapSpec<WireI, D, S::AddrSpec>>::BoundSocketId,
original_shutdown: Option<Shutdown>,
extra: S::ConnStateExtra,
}
fn connect_inner<
WireI: IpExt,
SocketI: IpExt,
D: WeakDeviceIdentifier,
S: DatagramSocketSpec,
R,
BC: RngContext,
CC: IpSocketHandler<WireI, BC, WeakDeviceId = D, DeviceId = D::Strong>,
>(
connect_params: ConnectParameters<WireI, SocketI, D, S>,
core_ctx: &mut CC,
bindings_ctx: &mut BC,
sockets: &mut BoundSocketMap<WireI, D, S::AddrSpec, S::SocketMapSpec<WireI, D>>,
remove_original: impl FnOnce(
&mut BoundSocketMap<WireI, D, S::AddrSpec, S::SocketMapSpec<WireI, D>>,
) -> R,
reinsert_original: impl FnOnce(
&mut BoundSocketMap<WireI, D, S::AddrSpec, S::SocketMapSpec<WireI, D>>,
R,
),
) -> Result<ConnState<WireI, SocketI, D, S>, ConnectError> {
let ConnectParameters {
local_ip,
local_port,
remote_ip,
remote_port,
device,
sharing,
ip_options,
socket_options,
socket_id,
original_shutdown,
extra,
} = connect_params;
let device = device.or_else(|| {
remote_ip
.addr()
.addr()
.is_multicast()
.then(|| socket_options.multicast_interface.clone())
.flatten()
});
let (remote_ip, socket_device) = remote_ip.resolve_addr_with_device(device.clone())?;
let clear_device_on_disconnect = device.is_none() && socket_device.is_some();
let ip_sock = IpSocketHandler::<WireI, _>::new_ip_socket(
core_ctx,
bindings_ctx,
socket_device.as_ref().map(|d| d.as_ref()),
local_ip.and_then(IpDeviceAddr::new_from_socket_ip_addr),
remote_ip,
S::ip_proto::<WireI>(),
&ip_options.common,
)?;
let local_port = match local_port {
Some(id) => id.clone(),
None => S::try_alloc_local_id(
sockets,
bindings_ctx,
DatagramFlowId {
local_ip: SocketIpAddr::from(*ip_sock.local_ip()),
remote_ip: *ip_sock.remote_ip(),
remote_id: remote_port.clone(),
},
)
.ok_or(ConnectError::CouldNotAllocateLocalPort)?,
};
let conn_addr = ConnAddr {
ip: ConnIpAddr {
local: (SocketIpAddr::from(*ip_sock.local_ip()), local_port),
remote: (*ip_sock.remote_ip(), remote_port),
},
device: ip_sock.device().cloned(),
};
let reinsert_op = remove_original(sockets);
let bound_addr = match sockets.conns_mut().try_insert(conn_addr, sharing, socket_id) {
Ok(bound_entry) => bound_entry.get_addr().clone(),
Err((
InsertError::Exists
| InsertError::IndirectConflict
| InsertError::ShadowerExists
| InsertError::ShadowAddrExists,
_sharing,
)) => {
reinsert_original(sockets, reinsert_op);
return Err(ConnectError::SockAddrConflict);
}
};
Ok(ConnState {
socket: ip_sock,
ip_options,
clear_device_on_disconnect,
shutdown: original_shutdown.unwrap_or_else(Shutdown::default),
addr: bound_addr,
extra,
})
}
struct SingleStackConnectOperation<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec> {
params: ConnectParameters<I, I, D, S>,
remove_op: Option<SingleStackRemoveOperation<I, D, S>>,
sharing: S::SharingState,
}
impl<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
SingleStackConnectOperation<I, D, S>
{
fn new_from_state<
BC,
CC: NonDualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D, DeviceId = D::Strong>,
>(
core_ctx: &mut CC,
socket_id: &S::SocketId<I, D>,
state: &SocketState<I, D, S>,
remote_ip: ZonedAddr<SocketIpAddr<I::Addr>, D::Strong>,
remote_port: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
extra: S::ConnStateExtra,
) -> Self {
match state {
SocketState::Unbound(UnboundSocketState { device, sharing, ip_options }) => {
SingleStackConnectOperation {
params: ConnectParameters {
local_ip: None,
local_port: None,
remote_ip,
remote_port,
device: device.clone(),
sharing: sharing.clone(),
ip_options: ip_options.clone(),
socket_options: ip_options.socket_options.clone(),
socket_id: S::make_bound_socket_map_id(socket_id),
original_shutdown: None,
extra,
},
sharing: sharing.clone(),
remove_op: None,
}
}
SocketState::Bound(state) => {
let remove_op =
SingleStackRemoveOperation::new_from_state(core_ctx, socket_id, state);
let BoundSocketState { socket_type, original_bound_addr: _ } = state;
match socket_type {
BoundSocketStateType::Listener {
state: ListenerState { ip_options, addr: ListenerAddr { ip, device } },
sharing,
} => {
let ListenerIpAddr { addr, identifier } =
core_ctx.nds_converter().convert(ip);
SingleStackConnectOperation {
params: ConnectParameters {
local_ip: addr.clone(),
local_port: Some(*identifier),
remote_ip,
remote_port,
device: device.clone(),
sharing: sharing.clone(),
ip_options: ip_options.clone(),
socket_options: ip_options.socket_options.clone(),
socket_id: S::make_bound_socket_map_id(socket_id),
original_shutdown: None,
extra,
},
sharing: sharing.clone(),
remove_op: Some(remove_op),
}
}
BoundSocketStateType::Connected { state, sharing } => {
let ConnState {
socket: _,
ip_options,
shutdown,
addr:
ConnAddr {
ip: ConnIpAddr { local: (local_ip, local_id), remote: _ },
device,
},
clear_device_on_disconnect: _,
extra: _,
} = core_ctx.nds_converter().convert(state);
SingleStackConnectOperation {
params: ConnectParameters {
local_ip: Some(local_ip.clone()),
local_port: Some(*local_id),
remote_ip,
remote_port,
device: device.clone(),
sharing: sharing.clone(),
ip_options: ip_options.clone(),
socket_options: ip_options.socket_options.clone(),
socket_id: S::make_bound_socket_map_id(socket_id),
original_shutdown: Some(shutdown.clone()),
extra,
},
sharing: sharing.clone(),
remove_op: Some(remove_op),
}
}
}
}
}
}
fn apply<BC: RngContext, CC: IpSocketHandler<I, BC, WeakDeviceId = D, DeviceId = D::Strong>>(
self,
core_ctx: &mut CC,
bindings_ctx: &mut BC,
socket_map: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>,
) -> Result<(ConnState<I, I, D, S>, S::SharingState), ConnectError> {
let SingleStackConnectOperation { params, remove_op, sharing } = self;
let remove_fn =
|sockets: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>| {
remove_op.map(|remove_op| remove_op.apply(sockets))
};
let reinsert_fn =
|sockets: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>,
remove_info: Option<SingleStackRemoveInfo<I, D, S>>| {
if let Some(remove_info) = remove_info {
remove_info.reinsert(sockets)
}
};
let conn_state =
connect_inner(params, core_ctx, bindings_ctx, socket_map, remove_fn, reinsert_fn)?;
Ok((conn_state, sharing))
}
}
struct DualStackConnectOperation<I: DualStackIpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
{
params: EitherStack<ConnectParameters<I, I, D, S>, ConnectParameters<I::OtherVersion, I, D, S>>,
remove_op: Option<DualStackRemoveOperation<I, D, S>>,
sharing: S::SharingState,
}
impl<I: DualStackIpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>
DualStackConnectOperation<I, D, S>
{
fn new_from_state<
BC,
CC: DualStackDatagramBoundStateContext<I, BC, S, WeakDeviceId = D, DeviceId = D::Strong>,
>(
core_ctx: &mut CC,
socket_id: &S::SocketId<I, D>,
state: &SocketState<I, D, S>,
remote_ip: DualStackRemoteIp<I, D::Strong>,
remote_port: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
extra: S::ConnStateExtra,
) -> Result<Self, ConnectError> {
match state {
SocketState::Unbound(UnboundSocketState { device, sharing, ip_options }) => {
let params = match remote_ip {
DualStackRemoteIp::ThisStack(remote_ip) => {
EitherStack::ThisStack(ConnectParameters {
local_ip: None,
local_port: None,
remote_ip,
remote_port,
device: device.clone(),
sharing: sharing.clone(),
ip_options: ip_options.clone(),
socket_options: ip_options.socket_options.clone(),
socket_id: S::make_bound_socket_map_id(socket_id),
original_shutdown: None,
extra,
})
}
DualStackRemoteIp::OtherStack(remote_ip) => {
if !core_ctx.dual_stack_enabled(ip_options) {
return Err(ConnectError::RemoteUnexpectedlyMapped);
}
EitherStack::OtherStack(ConnectParameters {
local_ip: None,
local_port: None,
remote_ip,
remote_port,
device: device.clone(),
sharing: sharing.clone(),
ip_options: ip_options.clone(),
socket_options: core_ctx.to_other_socket_options(ip_options).clone(),
socket_id: core_ctx.to_other_bound_socket_id(socket_id),
original_shutdown: None,
extra,
})
}
};
Ok(DualStackConnectOperation { params, remove_op: None, sharing: sharing.clone() })
}
SocketState::Bound(state) => {
let remove_op =
DualStackRemoveOperation::new_from_state(core_ctx, socket_id, state);
let BoundSocketState { socket_type, original_bound_addr: _ } = state;
match socket_type {
BoundSocketStateType::Listener {
state: ListenerState { ip_options, addr: ListenerAddr { ip, device } },
sharing,
} => {
match (remote_ip, core_ctx.ds_converter().convert(ip)) {
(
DualStackRemoteIp::OtherStack(_),
DualStackListenerIpAddr::ThisStack(_),
) => Err(ConnectError::RemoteUnexpectedlyMapped),
(
DualStackRemoteIp::ThisStack(_),
DualStackListenerIpAddr::OtherStack(_),
) => Err(ConnectError::RemoteUnexpectedlyNonMapped),
(
DualStackRemoteIp::ThisStack(remote_ip),
DualStackListenerIpAddr::ThisStack(ListenerIpAddr {
addr,
identifier,
}),
) => Ok(DualStackConnectOperation {
params: EitherStack::ThisStack(ConnectParameters {
local_ip: addr.clone(),
local_port: Some(*identifier),
remote_ip,
remote_port,
device: device.clone(),
sharing: sharing.clone(),
ip_options: ip_options.clone(),
socket_options: ip_options.socket_options.clone(),
socket_id: S::make_bound_socket_map_id(socket_id),
original_shutdown: None,
extra,
}),
sharing: sharing.clone(),
remove_op: Some(remove_op),
}),
(
DualStackRemoteIp::ThisStack(remote_ip),
DualStackListenerIpAddr::BothStacks(identifier),
) => Ok(DualStackConnectOperation {
params: EitherStack::ThisStack(ConnectParameters {
local_ip: None,
local_port: Some(*identifier),
remote_ip,
remote_port,
device: device.clone(),
sharing: sharing.clone(),
ip_options: ip_options.clone(),
socket_options: ip_options.socket_options.clone(),
socket_id: S::make_bound_socket_map_id(socket_id),
original_shutdown: None,
extra,
}),
sharing: sharing.clone(),
remove_op: Some(remove_op),
}),
(
DualStackRemoteIp::OtherStack(remote_ip),
DualStackListenerIpAddr::OtherStack(ListenerIpAddr {
addr,
identifier,
}),
) => Ok(DualStackConnectOperation {
params: EitherStack::OtherStack(ConnectParameters {
local_ip: addr.clone(),
local_port: Some(*identifier),
remote_ip,
remote_port,
device: device.clone(),
sharing: sharing.clone(),
ip_options: ip_options.clone(),
socket_options: core_ctx
.to_other_socket_options(ip_options)
.clone(),
socket_id: core_ctx.to_other_bound_socket_id(socket_id),
original_shutdown: None,
extra,
}),
sharing: sharing.clone(),
remove_op: Some(remove_op),
}),
(
DualStackRemoteIp::OtherStack(remote_ip),
DualStackListenerIpAddr::BothStacks(identifier),
) => Ok(DualStackConnectOperation {
params: EitherStack::OtherStack(ConnectParameters {
local_ip: None,
local_port: Some(*identifier),
remote_ip,
remote_port,
device: device.clone(),
sharing: sharing.clone(),
ip_options: ip_options.clone(),
socket_options: core_ctx
.to_other_socket_options(ip_options)
.clone(),
socket_id: core_ctx.to_other_bound_socket_id(socket_id),
original_shutdown: None,
extra,
}),
sharing: sharing.clone(),
remove_op: Some(remove_op),
}),
}
}
BoundSocketStateType::Connected { state, sharing } => {
match (remote_ip, core_ctx.ds_converter().convert(state)) {
(
DualStackRemoteIp::OtherStack(_),
DualStackConnState::ThisStack(_),
) => Err(ConnectError::RemoteUnexpectedlyMapped),
(
DualStackRemoteIp::ThisStack(_),
DualStackConnState::OtherStack(_),
) => Err(ConnectError::RemoteUnexpectedlyNonMapped),
(
DualStackRemoteIp::ThisStack(remote_ip),
DualStackConnState::ThisStack(ConnState {
socket: _,
ip_options,
shutdown,
addr:
ConnAddr {
ip:
ConnIpAddr { local: (local_ip, local_id), remote: _ },
device,
},
clear_device_on_disconnect: _,
extra: _,
}),
) => Ok(DualStackConnectOperation {
params: EitherStack::ThisStack(ConnectParameters {
local_ip: Some(local_ip.clone()),
local_port: Some(*local_id),
remote_ip,
remote_port,
device: device.clone(),
sharing: sharing.clone(),
ip_options: ip_options.clone(),
socket_options: ip_options.socket_options.clone(),
socket_id: S::make_bound_socket_map_id(socket_id),
original_shutdown: Some(shutdown.clone()),
extra,
}),
sharing: sharing.clone(),
remove_op: Some(remove_op),
}),
(
DualStackRemoteIp::OtherStack(remote_ip),
DualStackConnState::OtherStack(ConnState {
socket: _,
ip_options,
shutdown,
addr:
ConnAddr {
ip:
ConnIpAddr { local: (local_ip, local_id), remote: _ },
device,
},
clear_device_on_disconnect: _,
extra: _,
}),
) => Ok(DualStackConnectOperation {
params: EitherStack::OtherStack(ConnectParameters {
local_ip: Some(local_ip.clone()),
local_port: Some(*local_id),
remote_ip,
remote_port,
device: device.clone(),
sharing: sharing.clone(),
ip_options: ip_options.clone(),
socket_options: core_ctx
.to_other_socket_options(ip_options)
.clone(),
socket_id: core_ctx.to_other_bound_socket_id(socket_id),
original_shutdown: Some(shutdown.clone()),
extra,
}),
sharing: sharing.clone(),
remove_op: Some(remove_op),
}),
}
}
}
}
}
}
fn apply<
BC: RngContext,
CC: IpSocketHandler<I, BC, WeakDeviceId = D, DeviceId = D::Strong>
+ IpSocketHandler<I::OtherVersion, BC, WeakDeviceId = D, DeviceId = D::Strong>,
>(
self,
core_ctx: &mut CC,
bindings_ctx: &mut BC,
socket_map: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>,
other_socket_map: &mut BoundSocketMap<
I::OtherVersion,
D,
S::AddrSpec,
S::SocketMapSpec<I::OtherVersion, D>,
>,
) -> Result<(DualStackConnState<I, D, S>, S::SharingState), ConnectError> {
let DualStackConnectOperation { params, remove_op, sharing } = self;
let conn_state = match params {
EitherStack::ThisStack(params) => {
let remove_fn =
|sockets: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>| {
remove_op.map(|remove_op| {
let remove_info = remove_op.apply(sockets, other_socket_map);
(remove_info, other_socket_map)
})
};
let reinsert_fn =
|sockets: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>,
remove_info: Option<(
DualStackRemoveInfo<I, D, S>,
&mut BoundSocketMap<
I::OtherVersion,
D,
S::AddrSpec,
S::SocketMapSpec<I::OtherVersion, D>,
>,
)>| {
if let Some((remove_info, other_sockets)) = remove_info {
remove_info.reinsert(sockets, other_sockets)
}
};
connect_inner(params, core_ctx, bindings_ctx, socket_map, remove_fn, reinsert_fn)
.map(DualStackConnState::ThisStack)
}
EitherStack::OtherStack(params) => {
let remove_fn = |other_sockets: &mut BoundSocketMap<
I::OtherVersion,
D,
S::AddrSpec,
S::SocketMapSpec<I::OtherVersion, D>,
>| {
remove_op.map(|remove_op| {
let remove_info = remove_op.apply(socket_map, other_sockets);
(remove_info, socket_map)
})
};
let reinsert_fn = |other_sockets: &mut BoundSocketMap<
I::OtherVersion,
D,
S::AddrSpec,
S::SocketMapSpec<I::OtherVersion, D>,
>,
remove_info: Option<(
DualStackRemoveInfo<I, D, S>,
&mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>,
)>| {
if let Some((remove_info, sockets)) = remove_info {
remove_info.reinsert(sockets, other_sockets)
}
};
connect_inner(
params,
core_ctx,
bindings_ctx,
other_socket_map,
remove_fn,
reinsert_fn,
)
.map(DualStackConnState::OtherStack)
}
}?;
Ok((conn_state, sharing))
}
}
#[derive(Copy, Clone, Debug, Default, Eq, GenericOverIp, PartialEq)]
#[generic_over_ip()]
pub struct ExpectedConnError;
#[derive(Copy, Clone, Debug, Default, Eq, GenericOverIp, PartialEq)]
#[generic_over_ip()]
pub struct ExpectedUnboundError;
fn disconnect_to_unbound<
I: IpExt,
BC,
CC: DatagramBoundStateContext<I, BC, S>,
S: DatagramSocketSpec,
>(
core_ctx: &mut CC,
id: &S::SocketId<I, CC::WeakDeviceId>,
clear_device_on_disconnect: bool,
socket_state: &BoundSocketState<I, CC::WeakDeviceId, S>,
) -> UnboundSocketState<I, CC::WeakDeviceId, S> {
let (ip_options, sharing, mut device) = match core_ctx.dual_stack_context() {
MaybeDualStack::NotDualStack(nds) => {
let remove_op = SingleStackRemoveOperation::new_from_state(nds, id, socket_state);
let info = core_ctx.with_bound_sockets_mut(|_core_ctx, bound| remove_op.apply(bound));
info.into_options_sharing_and_device()
}
MaybeDualStack::DualStack(ds) => {
let remove_op = DualStackRemoveOperation::new_from_state(ds, id, socket_state);
let info = ds.with_both_bound_sockets_mut(|_core_ctx, bound, other_bound| {
remove_op.apply(bound, other_bound)
});
info.into_options_sharing_and_device()
}
};
if clear_device_on_disconnect {
device = None
}
UnboundSocketState { device, sharing, ip_options }
}
fn disconnect_to_listener<
I: IpExt,
BC,
CC: DatagramBoundStateContext<I, BC, S>,
S: DatagramSocketSpec,
>(
core_ctx: &mut CC,
id: &S::SocketId<I, CC::WeakDeviceId>,
listener_ip: S::ListenerIpAddr<I>,
clear_device_on_disconnect: bool,
socket_state: &BoundSocketState<I, CC::WeakDeviceId, S>,
) -> BoundSocketState<I, CC::WeakDeviceId, S> {
let (ip_options, sharing, device) = match core_ctx.dual_stack_context() {
MaybeDualStack::NotDualStack(nds) => {
let ListenerIpAddr { addr, identifier } =
nds.nds_converter().convert(listener_ip.clone());
let remove_op = SingleStackRemoveOperation::new_from_state(nds, id, socket_state);
core_ctx.with_bound_sockets_mut(|_core_ctx, bound| {
let (ip_options, sharing, mut device) =
remove_op.apply(bound).into_options_sharing_and_device();
if clear_device_on_disconnect {
device = None;
}
BoundStateHandler::<_, S, _>::try_insert_listener(
bound,
addr,
identifier,
device.clone(),
sharing.clone(),
S::make_bound_socket_map_id(id),
)
.expect("inserting listener for disconnected socket should succeed");
(ip_options, sharing, device)
})
}
MaybeDualStack::DualStack(ds) => {
let remove_op = DualStackRemoveOperation::new_from_state(ds, id, socket_state);
let other_id = ds.to_other_bound_socket_id(id);
let id = S::make_bound_socket_map_id(id);
let converter = ds.ds_converter();
ds.with_both_bound_sockets_mut(|_core_ctx, bound, other_bound| {
let (ip_options, sharing, mut device) =
remove_op.apply(bound, other_bound).into_options_sharing_and_device();
if clear_device_on_disconnect {
device = None;
}
match converter.convert(listener_ip.clone()) {
DualStackListenerIpAddr::ThisStack(ListenerIpAddr { addr, identifier }) => {
BoundStateHandler::<_, S, _>::try_insert_listener(
bound,
addr,
identifier,
device.clone(),
sharing.clone(),
id,
)
}
DualStackListenerIpAddr::OtherStack(ListenerIpAddr { addr, identifier }) => {
BoundStateHandler::<_, S, _>::try_insert_listener(
other_bound,
addr,
identifier,
device.clone(),
sharing.clone(),
other_id,
)
}
DualStackListenerIpAddr::BothStacks(identifier) => {
let ids = PairedBoundSocketIds { this: id, other: other_id };
let mut bound_pair = PairedSocketMapMut { bound, other_bound };
BoundStateHandler::<_, S, _>::try_insert_listener(
&mut bound_pair,
DualStackUnspecifiedAddr,
identifier,
device.clone(),
sharing.clone(),
ids,
)
}
}
.expect("inserting listener for disconnected socket should succeed");
(ip_options, sharing, device)
})
}
};
BoundSocketState {
original_bound_addr: Some(listener_ip.clone()),
socket_type: BoundSocketStateType::Listener {
state: ListenerState { ip_options, addr: ListenerAddr { ip: listener_ip, device } },
sharing,
},
}
}
#[derive(Debug, GenericOverIp)]
#[generic_over_ip()]
pub enum SendError<SE> {
NotConnected,
NotWriteable,
IpSock(IpSockSendError),
SerializeError(SE),
}
#[derive(Debug)]
pub enum SendToError<SE> {
NotWriteable,
Zone(ZonedAddressError),
CreateAndSend(IpSockCreateAndSendError),
RemoteUnexpectedlyMapped,
RemoteUnexpectedlyNonMapped,
SerializeError(SE),
}
struct SendOneshotParameters<'a, I: IpExt, S: DatagramSocketSpec, D: WeakDeviceIdentifier> {
local_ip: Option<SocketIpAddr<I::Addr>>,
local_id: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
remote_ip: ZonedAddr<SocketIpAddr<I::Addr>, D::Strong>,
remote_id: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
device: &'a Option<D>,
options: IpOptionsRef<'a, I, D>,
}
fn send_oneshot<I: IpExt, S: DatagramSocketSpec, CC: IpSocketHandler<I, BC>, BC, B: BufferMut>(
core_ctx: &mut CC,
bindings_ctx: &mut BC,
params: SendOneshotParameters<'_, I, S, CC::WeakDeviceId>,
body: B,
) -> Result<(), SendToError<S::SerializeError>> {
let SendOneshotParameters { local_ip, local_id, remote_ip, remote_id, device, options } =
params;
let device = device.clone().or_else(|| {
remote_ip
.addr()
.addr()
.is_multicast()
.then(|| options.ip_specific.multicast_interface.clone())
.flatten()
});
let (remote_ip, device) = match remote_ip.resolve_addr_with_device(device) {
Ok(addr) => addr,
Err(e) => return Err(SendToError::Zone(e)),
};
core_ctx
.send_oneshot_ip_packet_with_fallible_serializer(
bindings_ctx,
device.as_ref().map(|d| d.as_ref()),
local_ip.and_then(IpDeviceAddr::new_from_socket_ip_addr),
remote_ip,
S::ip_proto::<I>(),
&options,
|local_ip| {
S::make_packet::<I, _>(
body,
&ConnIpAddr {
local: (local_ip.into(), local_id),
remote: (remote_ip, remote_id),
},
)
},
)
.map_err(|err| match err {
SendOneShotIpPacketError::CreateAndSendError { err } => SendToError::CreateAndSend(err),
SendOneShotIpPacketError::SerializeError(err) => SendToError::SerializeError(err),
})
}
enum SetBoundDeviceParameters<
'a,
WireI: IpExt,
SocketI: IpExt,
D: WeakDeviceIdentifier,
S: DatagramSocketSpec,
> {
Listener {
ip: &'a ListenerIpAddr<WireI::Addr, <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
device: &'a mut Option<D>,
},
Connected(&'a mut ConnState<WireI, SocketI, D, S>),
}
fn set_bound_device_single_stack<
'a,
WireI: IpExt,
SocketI: IpExt,
D: WeakDeviceIdentifier,
S: DatagramSocketSpec,
BC,
CC: IpSocketHandler<WireI, BC, WeakDeviceId = D, DeviceId = D::Strong>,
>(
bindings_ctx: &mut BC,
core_ctx: &mut CC,
params: SetBoundDeviceParameters<'a, WireI, SocketI, D, S>,
sockets: &mut BoundSocketMap<WireI, D, S::AddrSpec, S::SocketMapSpec<WireI, D>>,
socket_id: &<
S::SocketMapSpec<WireI, D> as DatagramSocketMapSpec<WireI, D, S::AddrSpec>
>::BoundSocketId,
new_device: Option<&D::Strong>,
) -> Result<(), SocketError> {
let (local_ip, remote_ip, old_device) = match ¶ms {
SetBoundDeviceParameters::Listener {
ip: ListenerIpAddr { addr, identifier: _ },
device,
} => (addr.as_ref(), None, device.as_ref()),
SetBoundDeviceParameters::Connected(ConnState {
socket: _,
ip_options: _,
addr:
ConnAddr {
ip: ConnIpAddr { local: (local_ip, _local_id), remote: (remote_ip, _remote_id) },
device,
},
shutdown: _,
clear_device_on_disconnect: _,
extra: _,
}) => (Some(local_ip), Some(remote_ip), device.as_ref()),
};
let device_update = SocketDeviceUpdate {
local_ip: local_ip.map(AsRef::<SpecifiedAddr<WireI::Addr>>::as_ref),
remote_ip: remote_ip.map(AsRef::<SpecifiedAddr<WireI::Addr>>::as_ref),
old_device,
};
match device_update.check_update(new_device) {
Ok(()) => (),
Err(SocketDeviceUpdateNotAllowedError) => {
return Err(SocketError::Local(LocalAddressError::Zone(
ZonedAddressError::DeviceZoneMismatch,
)));
}
};
match params {
SetBoundDeviceParameters::Listener { ip, device } => {
let new_device = new_device.map(|d| d.downgrade());
let old_addr = ListenerAddr { ip: ip.clone(), device: device.clone() };
let new_addr = ListenerAddr { ip: ip.clone(), device: new_device.clone() };
let entry = sockets
.listeners_mut()
.entry(socket_id, &old_addr)
.unwrap_or_else(|| panic!("invalid listener ID {:?}", socket_id));
let _entry = entry
.try_update_addr(new_addr)
.map_err(|(ExistsError {}, _entry)| LocalAddressError::AddressInUse)?;
*device = new_device
}
SetBoundDeviceParameters::Connected(ConnState {
socket,
ip_options,
addr,
shutdown: _,
clear_device_on_disconnect,
extra: _,
}) => {
let ConnAddr { ip, device } = addr;
let ConnIpAddr { local: (local_ip, _local_id), remote: (remote_ip, _remote_id) } = ip;
let new_socket = core_ctx
.new_ip_socket(
bindings_ctx,
new_device.map(EitherDeviceId::Strong),
IpDeviceAddr::new_from_socket_ip_addr(local_ip.clone()),
remote_ip.clone(),
socket.proto(),
&ip_options.common,
)
.map_err(|_: IpSockCreationError| {
SocketError::Remote(RemoteAddressError::NoRoute)
})?;
let new_device = new_socket.device().cloned();
let old_addr = ConnAddr { ip: ip.clone(), device: device.clone() };
let entry = sockets
.conns_mut()
.entry(socket_id, &old_addr)
.unwrap_or_else(|| panic!("invalid conn id {:?}", socket_id));
let new_addr = ConnAddr { ip: ip.clone(), device: new_device.clone() };
let entry = entry
.try_update_addr(new_addr)
.map_err(|(ExistsError {}, _entry)| LocalAddressError::AddressInUse)?;
*socket = new_socket;
if new_device.is_some() {
*clear_device_on_disconnect = false;
}
*addr = entry.get_addr().clone()
}
}
Ok(())
}
fn set_bound_device_listener_both_stacks<
'a,
I: IpExt,
D: WeakDeviceIdentifier,
S: DatagramSocketSpec,
>(
old_device: &mut Option<D>,
local_id: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
PairedSocketMapMut { bound: sockets, other_bound: other_sockets }: PairedSocketMapMut<
'a,
I,
D,
S,
>,
PairedBoundSocketIds { this: socket_id, other: other_socket_id }: PairedBoundSocketIds<I, D, S>,
new_device: Option<D>,
) -> Result<(), SocketError> {
fn try_update_entry<I: IpExt, D: WeakDeviceIdentifier, S: DatagramSocketSpec>(
old_device: Option<D>,
new_device: Option<D>,
sockets: &mut BoundSocketMap<I, D, S::AddrSpec, S::SocketMapSpec<I, D>>,
socket_id: &<
S::SocketMapSpec<I, D> as DatagramSocketMapSpec<I, D, S::AddrSpec>
>::BoundSocketId,
local_id: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
) -> Result<(), SocketError> {
let old_addr = ListenerAddr {
ip: ListenerIpAddr { addr: None, identifier: local_id },
device: old_device,
};
let entry = sockets
.listeners_mut()
.entry(socket_id, &old_addr)
.unwrap_or_else(|| panic!("invalid listener ID {:?}", socket_id));
let new_addr = ListenerAddr {
ip: ListenerIpAddr { addr: None, identifier: local_id },
device: new_device,
};
let _entry = entry
.try_update_addr(new_addr)
.map_err(|(ExistsError {}, _entry)| LocalAddressError::AddressInUse)?;
return Ok(());
}
try_update_entry::<_, _, S>(
old_device.clone(),
new_device.clone(),
sockets,
&socket_id,
local_id,
)?;
let result = try_update_entry::<_, _, S>(
old_device.clone(),
new_device.clone(),
other_sockets,
&other_socket_id,
local_id,
);
if let Err(e) = result {
try_update_entry::<_, _, S>(new_device, old_device.clone(), sockets, &socket_id, local_id)
.expect("failed to rollback listener in this stack to it's original device");
return Err(e);
}
*old_device = new_device;
return Ok(());
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SetMulticastMembershipError {
AddressNotAvailable,
DeviceDoesNotExist,
NoDeviceWithAddress,
NoDeviceAvailable,
GroupAlreadyJoined,
GroupNotJoined,
WrongDevice,
}
fn pick_interface_for_addr<
A: IpAddress,
S: DatagramSocketSpec,
BC: DatagramStateBindingsContext<A::Version, S>,
CC: DatagramBoundStateContext<A::Version, BC, S>,
>(
core_ctx: &mut CC,
remote_addr: MulticastAddr<A>,
source_addr: Option<SpecifiedAddr<A>>,
marks: &Marks,
) -> Result<CC::DeviceId, SetMulticastMembershipError>
where
A::Version: IpExt,
{
core_ctx.with_transport_context(|core_ctx| match source_addr {
Some(source_addr) => {
BaseTransportIpContext::<A::Version, _>::with_devices_with_assigned_addr(
core_ctx,
source_addr,
|mut devices| {
if let Some(d) = devices.next() {
if devices.next() == None {
return Ok(d);
}
}
Err(SetMulticastMembershipError::NoDeviceAvailable)
},
)
}
None => {
let device = MulticastMembershipHandler::select_device_for_multicast_group(
core_ctx,
remote_addr,
marks,
)
.map_err(|e| match e {
ResolveRouteError::NoSrcAddr | ResolveRouteError::Unreachable => {
SetMulticastMembershipError::NoDeviceAvailable
}
})?;
Ok(device)
}
})
}
#[derive(Copy, Clone, Debug, Eq, GenericOverIp, PartialEq)]
#[generic_over_ip(A, IpAddress)]
pub enum MulticastInterfaceSelector<A: IpAddress, D> {
LocalAddress(SpecifiedAddr<A>),
Interface(D),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, GenericOverIp)]
#[generic_over_ip(A, IpAddress)]
pub enum MulticastMembershipInterfaceSelector<A: IpAddress, D> {
Specified(MulticastInterfaceSelector<A, D>),
AnyInterfaceWithRoute,
}
impl<A: IpAddress, D> From<MulticastInterfaceSelector<A, D>>
for MulticastMembershipInterfaceSelector<A, D>
{
fn from(selector: MulticastInterfaceSelector<A, D>) -> Self {
Self::Specified(selector)
}
}
#[derive(RefCast)]
#[repr(transparent)]
pub struct DatagramApi<I, C, S>(C, PhantomData<(S, I)>);
impl<I, C, S> DatagramApi<I, C, S> {
pub fn new(ctx: C) -> Self {
Self(ctx, PhantomData)
}
pub fn wrap(ctx: &mut C) -> &mut Self {
Self::ref_cast_mut(ctx)
}
}
type DatagramApiSocketId<I, C, S> = <S as DatagramSocketSpec>::SocketId<
I,
<<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId,
>;
type DatagramApiDeviceId<C> =
<<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId;
type DatagramApiWeakDeviceId<C> =
<<C as ContextPair>::CoreContext as DeviceIdContext<AnyDevice>>::WeakDeviceId;
impl<I, C, S> DatagramApi<I, C, S>
where
I: IpExt,
C: ContextPair,
C::BindingsContext: DatagramStateBindingsContext<I, S>,
C::CoreContext: DatagramStateContext<I, C::BindingsContext, S>,
S: DatagramSocketSpec,
{
fn core_ctx(&mut self) -> &mut C::CoreContext {
let Self(pair, PhantomData) = self;
pair.core_ctx()
}
fn contexts(&mut self) -> (&mut C::CoreContext, &mut C::BindingsContext) {
let Self(pair, PhantomData) = self;
pair.contexts()
}
pub fn create(
&mut self,
external_data: S::ExternalData<I>,
) -> S::SocketId<I, DatagramApiWeakDeviceId<C>> {
let primary = create_primary_id(external_data);
let strong = PrimaryRc::clone_strong(&primary);
self.core_ctx().with_all_sockets_mut(move |socket_set| {
let strong = PrimaryRc::clone_strong(&primary);
assert_matches::assert_matches!(socket_set.insert(strong, primary), None);
});
strong.into()
}
pub fn collect_all_sockets(&mut self) -> Vec<S::SocketId<I, DatagramApiWeakDeviceId<C>>> {
self.core_ctx()
.with_all_sockets(|socket_set| socket_set.keys().map(|s| s.clone().into()).collect())
}
pub fn close(
&mut self,
id: DatagramApiSocketId<I, C, S>,
) -> RemoveResourceResultWithContext<S::ExternalData<I>, C::BindingsContext> {
let (core_ctx, bindings_ctx) = self.contexts();
let primary = core_ctx.with_all_sockets_mut(|all_sockets| {
all_sockets.remove(id.borrow()).expect("socket already closed")
});
core_ctx.with_socket_state(&id, |core_ctx, state| {
let ip_options = match state {
SocketState::Unbound(UnboundSocketState { device: _, sharing: _, ip_options }) => {
ip_options.clone()
}
SocketState::Bound(state) => match core_ctx.dual_stack_context() {
MaybeDualStack::DualStack(dual_stack) => {
let op = DualStackRemoveOperation::new_from_state(dual_stack, &id, state);
dual_stack
.with_both_bound_sockets_mut(|_core_ctx, sockets, other_sockets| {
op.apply(sockets, other_sockets)
})
.into_options()
}
MaybeDualStack::NotDualStack(not_dual_stack) => {
let op =
SingleStackRemoveOperation::new_from_state(not_dual_stack, &id, state);
core_ctx
.with_bound_sockets_mut(|_core_ctx, sockets| op.apply(sockets))
.into_options()
}
},
};
DatagramBoundStateContext::<I, _, _>::with_transport_context(core_ctx, |core_ctx| {
leave_all_joined_groups(core_ctx, bindings_ctx, ip_options.multicast_memberships)
});
});
core::mem::drop(id);
<C::BindingsContext as ReferenceNotifiersExt>::unwrap_or_notify_with_new_reference_notifier(
primary,
|ReferenceState { state: _, external_data }| external_data,
)
}
pub fn get_info(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
) -> SocketInfo<I::Addr, DatagramApiWeakDeviceId<C>> {
self.core_ctx().with_socket_state(id, |_core_ctx, state| state.to_socket_info())
}
pub fn listen(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
addr: Option<ZonedAddr<SpecifiedAddr<I::Addr>, DatagramApiDeviceId<C>>>,
local_id: Option<<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
) -> Result<(), Either<ExpectedUnboundError, LocalAddressError>> {
let (core_ctx, bindings_ctx) = self.contexts();
core_ctx.with_socket_state_mut(id, |core_ctx, state| {
listen_inner::<_, _, _, S>(core_ctx, bindings_ctx, state, id, addr, local_id)
})
}
pub fn connect(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
remote_ip: Option<ZonedAddr<SpecifiedAddr<I::Addr>, DatagramApiDeviceId<C>>>,
remote_id: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
extra: S::ConnStateExtra,
) -> Result<(), ConnectError> {
let (core_ctx, bindings_ctx) = self.contexts();
core_ctx.with_socket_state_mut(id, |core_ctx, state| {
let (conn_state, sharing) = match (
core_ctx.dual_stack_context(),
DualStackRemoteIp::<I, _>::new(remote_ip.clone()),
) {
(MaybeDualStack::DualStack(ds), remote_ip) => {
let connect_op = DualStackConnectOperation::new_from_state(
ds, id, state, remote_ip, remote_id, extra,
)?;
let converter = ds.ds_converter();
let (conn_state, sharing) =
ds.with_both_bound_sockets_mut(|core_ctx, bound, other_bound| {
connect_op.apply(core_ctx, bindings_ctx, bound, other_bound)
})?;
Ok((converter.convert_back(conn_state), sharing))
}
(MaybeDualStack::NotDualStack(nds), DualStackRemoteIp::ThisStack(remote_ip)) => {
let connect_op = SingleStackConnectOperation::new_from_state(
nds, id, state, remote_ip, remote_id, extra,
);
let converter = nds.nds_converter();
let (conn_state, sharing) =
core_ctx.with_bound_sockets_mut(|core_ctx, bound| {
connect_op.apply(core_ctx, bindings_ctx, bound)
})?;
Ok((converter.convert_back(conn_state), sharing))
}
(MaybeDualStack::NotDualStack(_), DualStackRemoteIp::OtherStack(_)) => {
Err(ConnectError::RemoteUnexpectedlyMapped)
}
}?;
let original_bound_addr = match state {
SocketState::Unbound(_) => None,
SocketState::Bound(BoundSocketState { socket_type: _, original_bound_addr }) => {
original_bound_addr.clone()
}
};
*state = SocketState::Bound(BoundSocketState {
socket_type: BoundSocketStateType::Connected { state: conn_state, sharing },
original_bound_addr,
});
Ok(())
})
}
pub fn disconnect_connected(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
) -> Result<(), ExpectedConnError> {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
let inner_state = match state {
SocketState::Unbound(_) => return Err(ExpectedConnError),
SocketState::Bound(state) => state,
};
let BoundSocketState { socket_type, original_bound_addr } = inner_state;
let conn_state = match socket_type {
BoundSocketStateType::Listener { state: _, sharing: _ } => {
return Err(ExpectedConnError)
}
BoundSocketStateType::Connected { state, sharing: _ } => state,
};
let clear_device_on_disconnect = match core_ctx.dual_stack_context() {
MaybeDualStack::DualStack(dual_stack) => {
match dual_stack.ds_converter().convert(conn_state) {
DualStackConnState::ThisStack(conn_state) => {
conn_state.clear_device_on_disconnect
}
DualStackConnState::OtherStack(conn_state) => {
conn_state.clear_device_on_disconnect
}
}
}
MaybeDualStack::NotDualStack(not_dual_stack) => {
not_dual_stack.nds_converter().convert(conn_state).clear_device_on_disconnect
}
};
*state = match original_bound_addr {
None => SocketState::Unbound(disconnect_to_unbound(
core_ctx,
id,
clear_device_on_disconnect,
inner_state,
)),
Some(original_bound_addr) => SocketState::Bound(disconnect_to_listener(
core_ctx,
id,
original_bound_addr.clone(),
clear_device_on_disconnect,
inner_state,
)),
};
Ok(())
})
}
pub fn get_shutdown_connected(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
) -> Option<ShutdownType> {
self.core_ctx().with_socket_state(id, |core_ctx, state| {
let state = match state {
SocketState::Unbound(_) => return None,
SocketState::Bound(BoundSocketState { socket_type, original_bound_addr: _ }) => {
match socket_type {
BoundSocketStateType::Listener { state: _, sharing: _ } => return None,
BoundSocketStateType::Connected { state, sharing: _ } => state,
}
}
};
let Shutdown { send, receive } = match core_ctx.dual_stack_context() {
MaybeDualStack::DualStack(ds) => ds.ds_converter().convert(state).as_ref(),
MaybeDualStack::NotDualStack(nds) => nds.nds_converter().convert(state).as_ref(),
};
ShutdownType::from_send_receive(*send, *receive)
})
}
pub fn shutdown_connected(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
which: ShutdownType,
) -> Result<(), ExpectedConnError> {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
let state = match state {
SocketState::Unbound(_) => return Err(ExpectedConnError),
SocketState::Bound(BoundSocketState { socket_type, original_bound_addr: _ }) => {
match socket_type {
BoundSocketStateType::Listener { state: _, sharing: _ } => {
return Err(ExpectedConnError)
}
BoundSocketStateType::Connected { state, sharing: _ } => state,
}
}
};
let (shutdown_send, shutdown_receive) = which.to_send_receive();
let Shutdown { send, receive } = match core_ctx.dual_stack_context() {
MaybeDualStack::DualStack(ds) => ds.ds_converter().convert(state).as_mut(),
MaybeDualStack::NotDualStack(nds) => nds.nds_converter().convert(state).as_mut(),
};
*send |= shutdown_send;
*receive |= shutdown_receive;
Ok(())
})
}
pub fn send_conn<B: BufferMut>(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
body: B,
) -> Result<(), SendError<S::SerializeError>> {
let (core_ctx, bindings_ctx) = self.contexts();
core_ctx.with_socket_state(id, |core_ctx, state| {
let state = match state {
SocketState::Unbound(_) => return Err(SendError::NotConnected),
SocketState::Bound(BoundSocketState { socket_type, original_bound_addr: _ }) => {
match socket_type {
BoundSocketStateType::Listener { state: _, sharing: _ } => {
return Err(SendError::NotConnected)
}
BoundSocketStateType::Connected { state, sharing: _ } => state,
}
}
};
struct SendParams<
'a,
I: IpExt,
S: DatagramSocketSpec,
D: WeakDeviceIdentifier,
O: SendOptions<I> + RouteResolutionOptions<I>,
> {
socket: &'a IpSock<I, D>,
ip: &'a ConnIpAddr<
I::Addr,
<S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>,
options: O,
}
enum Operation<
'a,
I: DualStackIpExt,
S: DatagramSocketSpec,
D: WeakDeviceIdentifier,
BC: DatagramStateBindingsContext<I, S>,
DualStackSC: DualStackDatagramBoundStateContext<I, BC, S>,
CC: DatagramBoundStateContext<I, BC, S>,
O: SendOptions<I> + RouteResolutionOptions<I>,
OtherO: SendOptions<I::OtherVersion> + RouteResolutionOptions<I::OtherVersion>,
> {
SendToThisStack((SendParams<'a, I, S, D, O>, &'a mut CC)),
SendToOtherStack(
(SendParams<'a, I::OtherVersion, S, D, OtherO>, &'a mut DualStackSC),
),
_Phantom((Never, PhantomData<BC>)),
}
let (shutdown, operation) = match core_ctx.dual_stack_context() {
MaybeDualStack::DualStack(dual_stack) => {
match dual_stack.ds_converter().convert(state) {
DualStackConnState::ThisStack(ConnState {
socket,
ip_options,
clear_device_on_disconnect: _,
shutdown,
addr: ConnAddr { ip, device: _ },
extra: _,
}) => (
shutdown,
Operation::SendToThisStack((
SendParams {
socket,
ip,
options: ip_options.this_stack_options_ref(),
},
core_ctx,
)),
),
DualStackConnState::OtherStack(ConnState {
socket,
ip_options,
clear_device_on_disconnect: _,
shutdown,
addr: ConnAddr { ip, device: _ },
extra: _,
}) => (
shutdown,
Operation::SendToOtherStack((
SendParams {
socket,
ip,
options: ip_options.other_stack_options_ref(dual_stack),
},
dual_stack,
)),
),
}
}
MaybeDualStack::NotDualStack(not_dual_stack) => {
let ConnState {
socket,
ip_options,
clear_device_on_disconnect: _,
shutdown,
addr: ConnAddr { ip, device: _ },
extra: _,
} = not_dual_stack.nds_converter().convert(state);
(
shutdown,
Operation::SendToThisStack((
SendParams { socket, ip, options: ip_options.this_stack_options_ref() },
core_ctx,
)),
)
}
};
let Shutdown { send: shutdown_send, receive: _ } = shutdown;
if *shutdown_send {
return Err(SendError::NotWriteable);
}
match operation {
Operation::SendToThisStack((SendParams { socket, ip, options }, core_ctx)) => {
let packet =
S::make_packet::<I, _>(body, &ip).map_err(SendError::SerializeError)?;
DatagramBoundStateContext::with_transport_context(core_ctx, |core_ctx| {
core_ctx
.send_ip_packet(bindings_ctx, &socket, packet, &options)
.map_err(|send_error| SendError::IpSock(send_error))
})
}
Operation::SendToOtherStack((SendParams { socket, ip, options }, dual_stack)) => {
let packet = S::make_packet::<I::OtherVersion, _>(body, &ip)
.map_err(SendError::SerializeError)?;
DualStackDatagramBoundStateContext::with_transport_context::<_, _>(
dual_stack,
|core_ctx| {
core_ctx
.send_ip_packet(bindings_ctx, &socket, packet, &options)
.map_err(|send_error| SendError::IpSock(send_error))
},
)
}
}
})
}
pub fn send_to<B: BufferMut>(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
remote_ip: Option<ZonedAddr<SpecifiedAddr<I::Addr>, DatagramApiDeviceId<C>>>,
remote_identifier: <S::AddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
body: B,
) -> Result<(), Either<LocalAddressError, SendToError<S::SerializeError>>> {
let (core_ctx, bindings_ctx) = self.contexts();
core_ctx.with_socket_state_mut(id, |core_ctx, state| {
match listen_inner(core_ctx, bindings_ctx, state, id, None, None) {
Ok(()) | Err(Either::Left(ExpectedUnboundError)) => (),
Err(Either::Right(e)) => return Err(Either::Left(e)),
};
let state = match state {
SocketState::Unbound(_) => panic!("expected bound socket"),
SocketState::Bound(BoundSocketState {
socket_type: state,
original_bound_addr: _,
}) => state,
};
enum Operation<
'a,
I: DualStackIpExt,
S: DatagramSocketSpec,
D: WeakDeviceIdentifier,
BC: DatagramStateBindingsContext<I, S>,
DualStackSC: DualStackDatagramBoundStateContext<I, BC, S>,
CC: DatagramBoundStateContext<I, BC, S>,
> {
SendToThisStack((SendOneshotParameters<'a, I, S, D>, &'a mut CC)),
SendToOtherStack(
(SendOneshotParameters<'a, I::OtherVersion, S, D>, &'a mut DualStackSC),
),
_Phantom((Never, PhantomData<BC>)),
}
let (operation, shutdown) = match (
core_ctx.dual_stack_context(),
DualStackRemoteIp::<I, _>::new(remote_ip.clone()),
) {
(MaybeDualStack::NotDualStack(_), DualStackRemoteIp::OtherStack(_)) => {
return Err(Either::Right(SendToError::RemoteUnexpectedlyMapped))
}
(MaybeDualStack::NotDualStack(nds), DualStackRemoteIp::ThisStack(remote_ip)) => {
match state {
BoundSocketStateType::Listener {
state: ListenerState { ip_options, addr: ListenerAddr { ip, device } },
sharing: _,
} => {
let ListenerIpAddr { addr, identifier } =
nds.nds_converter().convert(ip.clone());
(
Operation::SendToThisStack((
SendOneshotParameters {
local_ip: addr,
local_id: identifier,
remote_ip,
remote_id: remote_identifier,
device,
options: ip_options.this_stack_options_ref(),
},
core_ctx,
)),
None,
)
}
BoundSocketStateType::Connected { state, sharing: _ } => {
let ConnState {
socket: _,
ip_options,
clear_device_on_disconnect: _,
shutdown,
addr:
ConnAddr {
ip: ConnIpAddr { local: (local_ip, local_id), remote: _ },
device,
},
extra: _,
} = nds.nds_converter().convert(state);
(
Operation::SendToThisStack((
SendOneshotParameters {
local_ip: Some(*local_ip),
local_id: *local_id,
remote_ip,
remote_id: remote_identifier,
device,
options: ip_options.this_stack_options_ref(),
},
core_ctx,
)),
Some(shutdown),
)
}
}
}
(MaybeDualStack::DualStack(ds), remote_ip) => match state {
BoundSocketStateType::Listener {
state: ListenerState { ip_options, addr: ListenerAddr { ip, device } },
sharing: _,
} => match (ds.ds_converter().convert(ip), remote_ip) {
(
DualStackListenerIpAddr::ThisStack(_),
DualStackRemoteIp::OtherStack(_),
) => return Err(Either::Right(SendToError::RemoteUnexpectedlyMapped)),
(
DualStackListenerIpAddr::OtherStack(_),
DualStackRemoteIp::ThisStack(_),
) => return Err(Either::Right(SendToError::RemoteUnexpectedlyNonMapped)),
(
DualStackListenerIpAddr::ThisStack(ListenerIpAddr { addr, identifier }),
DualStackRemoteIp::ThisStack(remote_ip),
) => (
Operation::SendToThisStack((
SendOneshotParameters {
local_ip: *addr,
local_id: *identifier,
remote_ip,
remote_id: remote_identifier,
device,
options: ip_options.this_stack_options_ref(),
},
core_ctx,
)),
None,
),
(
DualStackListenerIpAddr::BothStacks(identifier),
DualStackRemoteIp::ThisStack(remote_ip),
) => (
Operation::SendToThisStack((
SendOneshotParameters {
local_ip: None,
local_id: *identifier,
remote_ip,
remote_id: remote_identifier,
device,
options: ip_options.this_stack_options_ref(),
},
core_ctx,
)),
None,
),
(
DualStackListenerIpAddr::OtherStack(ListenerIpAddr {
addr,
identifier,
}),
DualStackRemoteIp::OtherStack(remote_ip),
) => (
Operation::SendToOtherStack((
SendOneshotParameters {
local_ip: *addr,
local_id: *identifier,
remote_ip,
remote_id: remote_identifier,
device,
options: ip_options.other_stack_options_ref(ds),
},
ds,
)),
None,
),
(
DualStackListenerIpAddr::BothStacks(identifier),
DualStackRemoteIp::OtherStack(remote_ip),
) => (
Operation::SendToOtherStack((
SendOneshotParameters {
local_ip: None,
local_id: *identifier,
remote_ip,
remote_id: remote_identifier,
device,
options: ip_options.other_stack_options_ref(ds),
},
ds,
)),
None,
),
},
BoundSocketStateType::Connected { state, sharing: _ } => {
match (ds.ds_converter().convert(state), remote_ip) {
(
DualStackConnState::ThisStack(_),
DualStackRemoteIp::OtherStack(_),
) => return Err(Either::Right(SendToError::RemoteUnexpectedlyMapped)),
(
DualStackConnState::OtherStack(_),
DualStackRemoteIp::ThisStack(_),
) => {
return Err(Either::Right(SendToError::RemoteUnexpectedlyNonMapped))
}
(
DualStackConnState::ThisStack(state),
DualStackRemoteIp::ThisStack(remote_ip),
) => {
let ConnState {
socket: _,
ip_options,
clear_device_on_disconnect: _,
shutdown,
addr,
extra: _,
} = state;
let ConnAddr {
ip: ConnIpAddr { local: (local_ip, local_id), remote: _ },
device,
} = addr;
(
Operation::SendToThisStack((
SendOneshotParameters {
local_ip: Some(*local_ip),
local_id: *local_id,
remote_ip,
remote_id: remote_identifier,
device,
options: ip_options.this_stack_options_ref(),
},
core_ctx,
)),
Some(shutdown),
)
}
(
DualStackConnState::OtherStack(state),
DualStackRemoteIp::OtherStack(remote_ip),
) => {
let ConnState {
socket: _,
ip_options,
clear_device_on_disconnect: _,
shutdown,
addr,
extra: _,
} = state;
let ConnAddr {
ip: ConnIpAddr { local: (local_ip, local_id), .. },
device,
} = addr;
(
Operation::SendToOtherStack((
SendOneshotParameters {
local_ip: Some(*local_ip),
local_id: *local_id,
remote_ip,
remote_id: remote_identifier,
device,
options: ip_options.other_stack_options_ref(ds),
},
ds,
)),
Some(shutdown),
)
}
}
}
},
};
if let Some(Shutdown { send: shutdown_write, receive: _ }) = shutdown {
if *shutdown_write {
return Err(Either::Right(SendToError::NotWriteable));
}
}
match operation {
Operation::SendToThisStack((params, core_ctx)) => {
DatagramBoundStateContext::with_transport_context(core_ctx, |core_ctx| {
send_oneshot::<_, S, _, _, _>(core_ctx, bindings_ctx, params, body)
})
}
Operation::SendToOtherStack((params, core_ctx)) => {
DualStackDatagramBoundStateContext::with_transport_context::<_, _>(
core_ctx,
|core_ctx| {
send_oneshot::<_, S, _, _, _>(core_ctx, bindings_ctx, params, body)
},
)
}
}
.map_err(Either::Right)
})
}
pub fn get_bound_device(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
) -> Option<DatagramApiWeakDeviceId<C>> {
self.core_ctx().with_socket_state(id, |core_ctx, state| {
let (_, device): (&IpOptions<_, _, _>, _) = state.get_options_device(core_ctx);
device.clone()
})
}
pub fn set_device(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
new_device: Option<&DatagramApiDeviceId<C>>,
) -> Result<(), SocketError> {
let (core_ctx, bindings_ctx) = self.contexts();
core_ctx.with_socket_state_mut(id, |core_ctx, state| {
match state {
SocketState::Unbound(state) => {
let UnboundSocketState { ref mut device, sharing: _, ip_options: _ } = state;
*device = new_device.map(|d| d.downgrade());
Ok(())
}
SocketState::Bound(BoundSocketState { socket_type, original_bound_addr: _ }) => {
enum Operation<
'a,
I: IpExt,
D: WeakDeviceIdentifier,
S: DatagramSocketSpec,
CC,
DualStackSC,
> {
ThisStack {
params: SetBoundDeviceParameters<'a, I, I, D, S>,
core_ctx: CC,
},
OtherStack {
params: SetBoundDeviceParameters<'a, I::OtherVersion, I, D, S>,
core_ctx: DualStackSC,
},
ListenerBothStacks {
identifier: <S::AddrSpec as SocketMapAddrSpec>::LocalIdentifier,
device: &'a mut Option<D>,
core_ctx: DualStackSC,
},
}
let op = match core_ctx.dual_stack_context() {
MaybeDualStack::DualStack(ds) => match socket_type {
BoundSocketStateType::Listener { state, sharing: _ } => {
let ListenerState {
ip_options: _,
addr: ListenerAddr { ip, device },
} = state;
match ds.ds_converter().convert(ip) {
DualStackListenerIpAddr::ThisStack(ip) => {
Operation::ThisStack {
params: SetBoundDeviceParameters::Listener {
ip,
device,
},
core_ctx,
}
}
DualStackListenerIpAddr::OtherStack(ip) => {
Operation::OtherStack {
params: SetBoundDeviceParameters::Listener {
ip,
device,
},
core_ctx: ds,
}
}
DualStackListenerIpAddr::BothStacks(identifier) => {
Operation::ListenerBothStacks {
identifier: *identifier,
device,
core_ctx: ds,
}
}
}
}
BoundSocketStateType::Connected { state, sharing: _ } => {
match ds.ds_converter().convert(state) {
DualStackConnState::ThisStack(state) => Operation::ThisStack {
params: SetBoundDeviceParameters::Connected(state),
core_ctx,
},
DualStackConnState::OtherStack(state) => {
Operation::OtherStack {
params: SetBoundDeviceParameters::Connected(state),
core_ctx: ds,
}
}
}
}
},
MaybeDualStack::NotDualStack(nds) => match socket_type {
BoundSocketStateType::Listener { state, sharing: _ } => {
let ListenerState {
ip_options: _,
addr: ListenerAddr { ip, device },
} = state;
Operation::ThisStack {
params: SetBoundDeviceParameters::Listener {
ip: nds.nds_converter().convert(ip),
device,
},
core_ctx,
}
}
BoundSocketStateType::Connected { state, sharing: _ } => {
Operation::ThisStack {
params: SetBoundDeviceParameters::Connected(
nds.nds_converter().convert(state),
),
core_ctx,
}
}
},
};
match op {
Operation::ThisStack { params, core_ctx } => {
let socket_id = S::make_bound_socket_map_id(id);
DatagramBoundStateContext::<I, _, _>::with_bound_sockets_mut(
core_ctx,
|core_ctx, bound| {
set_bound_device_single_stack(
bindings_ctx,
core_ctx,
params,
bound,
&socket_id,
new_device,
)
},
)
}
Operation::OtherStack { params, core_ctx } => {
let socket_id = core_ctx.to_other_bound_socket_id(id);
core_ctx.with_other_bound_sockets_mut(|core_ctx, bound| {
set_bound_device_single_stack(
bindings_ctx,
core_ctx,
params,
bound,
&socket_id,
new_device,
)
})
}
Operation::ListenerBothStacks { identifier, device, core_ctx } => {
let socket_id = PairedBoundSocketIds::<_, _, S> {
this: S::make_bound_socket_map_id(id),
other: core_ctx.to_other_bound_socket_id(id),
};
core_ctx.with_both_bound_sockets_mut(|_core_ctx, bound, other_bound| {
set_bound_device_listener_both_stacks(
device,
identifier,
PairedSocketMapMut { bound, other_bound },
socket_id,
new_device.map(|d| d.downgrade()),
)
})
}
}
}
}
})
}
pub fn set_multicast_membership(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
multicast_group: MulticastAddr<I::Addr>,
interface: MulticastMembershipInterfaceSelector<I::Addr, DatagramApiDeviceId<C>>,
want_membership: bool,
) -> Result<(), SetMulticastMembershipError> {
let (core_ctx, bindings_ctx) = self.contexts();
core_ctx.with_socket_state_mut(id, |core_ctx, state| {
let (ip_options, bound_device) = state.get_options_device(core_ctx);
let interface = match interface {
MulticastMembershipInterfaceSelector::Specified(selector) => match selector {
MulticastInterfaceSelector::Interface(device) => {
if bound_device.as_ref().is_some_and(|d| d != &device) {
return Err(SetMulticastMembershipError::WrongDevice);
} else {
EitherDeviceId::Strong(device)
}
}
MulticastInterfaceSelector::LocalAddress(addr) => {
EitherDeviceId::Strong(pick_interface_for_addr(
core_ctx,
multicast_group,
Some(addr),
&ip_options.common.marks,
)?)
}
},
MulticastMembershipInterfaceSelector::AnyInterfaceWithRoute => {
if let Some(bound_device) = bound_device.as_ref() {
EitherDeviceId::Weak(bound_device.clone())
} else {
EitherDeviceId::Strong(pick_interface_for_addr(
core_ctx,
multicast_group,
None,
&ip_options.common.marks,
)?)
}
}
};
let ip_options = state.get_options_mut(core_ctx);
let Some(strong_interface) = interface.as_strong() else {
return Err(SetMulticastMembershipError::DeviceDoesNotExist);
};
let change = ip_options
.multicast_memberships
.apply_membership_change(multicast_group, &interface.as_weak(), want_membership)
.ok_or(if want_membership {
SetMulticastMembershipError::GroupAlreadyJoined
} else {
SetMulticastMembershipError::GroupNotJoined
})?;
DatagramBoundStateContext::<I, _, _>::with_transport_context(core_ctx, |core_ctx| {
match change {
MulticastMembershipChange::Join => {
MulticastMembershipHandler::<I, _>::join_multicast_group(
core_ctx,
bindings_ctx,
&strong_interface,
multicast_group,
)
}
MulticastMembershipChange::Leave => {
MulticastMembershipHandler::<I, _>::leave_multicast_group(
core_ctx,
bindings_ctx,
&strong_interface,
multicast_group,
)
}
}
});
Ok(())
})
}
pub fn update_ip_hop_limit(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
update: impl FnOnce(&mut SocketHopLimits<I>),
) {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
let options = state.get_options_mut(core_ctx);
update(&mut options.socket_options.hop_limits)
})
}
pub fn get_ip_hop_limits(&mut self, id: &DatagramApiSocketId<I, C, S>) -> HopLimits {
self.core_ctx().with_socket_state(id, |core_ctx, state| {
let (options, device) = state.get_options_device(core_ctx);
let device = device.as_ref().and_then(|d| d.upgrade());
DatagramBoundStateContext::<I, _, _>::with_transport_context(core_ctx, |core_ctx| {
options.socket_options.hop_limits.get_limits_with_defaults(
&BaseTransportIpContext::<I, _>::get_default_hop_limits(
core_ctx,
device.as_ref(),
),
)
})
})
}
pub fn with_other_stack_ip_options_mut_if_unbound<R>(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
cb: impl FnOnce(&mut S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>) -> R,
) -> Result<R, ExpectedUnboundError> {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
let is_unbound = match state {
SocketState::Unbound(_) => true,
SocketState::Bound(_) => false,
};
if is_unbound {
let options = state.get_options_mut(core_ctx);
Ok(cb(&mut options.other_stack))
} else {
Err(ExpectedUnboundError)
}
})
}
pub fn with_other_stack_ip_options_mut<R>(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
cb: impl FnOnce(&mut S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>) -> R,
) -> R {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
let options = state.get_options_mut(core_ctx);
cb(&mut options.other_stack)
})
}
pub fn with_other_stack_ip_options<R>(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
cb: impl FnOnce(&S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>) -> R,
) -> R {
self.core_ctx().with_socket_state(id, |core_ctx, state| {
let (options, _device) = state.get_options_device(core_ctx);
cb(&options.other_stack)
})
}
pub fn with_other_stack_ip_options_and_default_hop_limits<R>(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
cb: impl FnOnce(&S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>, HopLimits) -> R,
) -> Result<R, NotDualStackCapableError> {
self.core_ctx().with_socket_state(id, |core_ctx, state| {
let (options, device) = state.get_options_device(core_ctx);
let device = device.as_ref().and_then(|d| d.upgrade());
match DatagramBoundStateContext::<I, _, _>::dual_stack_context(core_ctx) {
MaybeDualStack::NotDualStack(_) => Err(NotDualStackCapableError),
MaybeDualStack::DualStack(ds) => {
let default_hop_limits =
DualStackDatagramBoundStateContext::<I, _, _>::with_transport_context(
ds,
|sync_ctx| {
BaseTransportIpContext::<I, _>::get_default_hop_limits(
sync_ctx,
device.as_ref(),
)
},
);
Ok(cb(&options.other_stack, default_hop_limits))
}
}
})
}
pub fn with_both_stacks_ip_options_mut<R>(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
cb: impl FnOnce(
&mut DatagramIpSpecificSocketOptions<I, DatagramApiWeakDeviceId<C>>,
&mut S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>,
) -> R,
) -> R {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
let options = state.get_options_mut(core_ctx);
cb(&mut options.socket_options, &mut options.other_stack)
})
}
pub fn with_both_stacks_ip_options<R>(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
cb: impl FnOnce(
&DatagramIpSpecificSocketOptions<I, DatagramApiWeakDeviceId<C>>,
&S::OtherStackIpOptions<I, DatagramApiWeakDeviceId<C>>,
) -> R,
) -> R {
self.core_ctx().with_socket_state(id, |core_ctx, state| {
let (options, _device) = state.get_options_device(core_ctx);
cb(&options.socket_options, &options.other_stack)
})
}
pub fn update_sharing(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
f: impl FnOnce(&mut S::SharingState),
) -> Result<(), ExpectedUnboundError> {
self.core_ctx().with_socket_state_mut(id, |_core_ctx, state| {
let state = match state {
SocketState::Bound(_) => return Err(ExpectedUnboundError),
SocketState::Unbound(state) => state,
};
f(&mut state.sharing);
Ok(())
})
}
pub fn get_sharing(&mut self, id: &DatagramApiSocketId<I, C, S>) -> S::SharingState {
self.core_ctx().with_socket_state(id, |_core_ctx, state| {
match state {
SocketState::Unbound(state) => {
let UnboundSocketState { device: _, sharing, ip_options: _ } = state;
sharing
}
SocketState::Bound(BoundSocketState { socket_type, original_bound_addr: _ }) => {
match socket_type {
BoundSocketStateType::Listener { state: _, sharing } => sharing,
BoundSocketStateType::Connected { state: _, sharing } => sharing,
}
}
}
.clone()
})
}
pub fn set_ip_transparent(&mut self, id: &DatagramApiSocketId<I, C, S>, value: bool) {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
state.get_options_mut(core_ctx).common.transparent = value;
})
}
pub fn get_ip_transparent(&mut self, id: &DatagramApiSocketId<I, C, S>) -> bool {
self.core_ctx().with_socket_state(id, |core_ctx, state| {
let (options, _device) = state.get_options_device(core_ctx);
options.common.transparent
})
}
pub fn set_mark(&mut self, id: &DatagramApiSocketId<I, C, S>, domain: MarkDomain, mark: Mark) {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
*state.get_options_mut(core_ctx).common.marks.get_mut(domain) = mark;
})
}
pub fn get_mark(&mut self, id: &DatagramApiSocketId<I, C, S>, domain: MarkDomain) -> Mark {
self.core_ctx().with_socket_state(id, |core_ctx, state| {
let (options, _device) = state.get_options_device(core_ctx);
*options.common.marks.get(domain)
})
}
pub fn set_broadcast(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
value: Option<I::BroadcastMarker>,
) {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
state.get_options_mut(core_ctx).socket_options.allow_broadcast = value;
})
}
pub fn get_broadcast(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
) -> Option<I::BroadcastMarker> {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
let (options, _device) = state.get_options_device(core_ctx);
options.socket_options.allow_broadcast
})
}
pub fn set_multicast_interface(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
value: Option<&DatagramApiDeviceId<C>>,
) {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
state.get_options_mut(core_ctx).socket_options.multicast_interface =
value.map(|v| v.downgrade());
})
}
pub fn get_multicast_interface(
&mut self,
id: &DatagramApiSocketId<I, C, S>,
) -> Option<DatagramApiWeakDeviceId<C>> {
self.core_ctx().with_socket_state(id, |core_ctx, state| {
let (options, _device) = state.get_options_device(core_ctx);
options.socket_options.multicast_interface.clone()
})
}
pub fn set_multicast_loop(&mut self, id: &DatagramApiSocketId<I, C, S>, value: bool) {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
state.get_options_mut(core_ctx).socket_options.multicast_loop = value;
})
}
pub fn get_multicast_loop(&mut self, id: &DatagramApiSocketId<I, C, S>) -> bool {
self.core_ctx().with_socket_state(id, |core_ctx, state| {
let (options, _device) = state.get_options_device(core_ctx);
options.socket_options.multicast_loop
})
}
pub fn set_dscp_and_ecn(&mut self, id: &DatagramApiSocketId<I, C, S>, value: DscpAndEcn) {
self.core_ctx().with_socket_state_mut(id, |core_ctx, state| {
state.get_options_mut(core_ctx).socket_options.dscp_and_ecn = value;
})
}
pub fn get_dscp_and_ecn(&mut self, id: &DatagramApiSocketId<I, C, S>) -> DscpAndEcn {
self.core_ctx().with_socket_state(id, |core_ctx, state| {
let (options, _device) = state.get_options_device(core_ctx);
options.socket_options.dscp_and_ecn
})
}
}
#[cfg(any(test, feature = "testutils"))]
pub(crate) mod testutil {
use super::*;
use alloc::vec;
use net_types::ip::IpAddr;
use net_types::Witness;
use netstack3_base::testutil::{FakeStrongDeviceId, TestIpExt};
use netstack3_base::CtxPair;
use netstack3_ip::socket::testutil::FakeDeviceConfig;
pub fn setup_fake_ctx_with_dualstack_conn_addrs<CC, BC: Default, D: FakeStrongDeviceId>(
local_ip: IpAddr,
remote_ip: SpecifiedAddr<IpAddr>,
devices: impl IntoIterator<Item = D>,
core_ctx_builder: impl FnOnce(Vec<FakeDeviceConfig<D, SpecifiedAddr<IpAddr>>>) -> CC,
) -> CtxPair<CC, BC> {
fn unmap_ip(addr: IpAddr) -> IpAddr {
match addr {
IpAddr::V4(v4) => IpAddr::V4(v4),
IpAddr::V6(v6) => match v6.to_ipv4_mapped() {
Some(v4) => IpAddr::V4(v4),
None => IpAddr::V6(v6),
},
}
}
let local_ip = unmap_ip(local_ip);
let remote_ip = unmap_ip(remote_ip.get());
let local_ip = SpecifiedAddr::new(local_ip).unwrap_or_else(|| match remote_ip {
IpAddr::V4(_) => Ipv4::TEST_ADDRS.local_ip.into(),
IpAddr::V6(_) => Ipv6::TEST_ADDRS.local_ip.into(),
});
let remote_ip = SpecifiedAddr::new(remote_ip).expect("remote-ip should be specified");
CtxPair::with_core_ctx(core_ctx_builder(
devices
.into_iter()
.map(|device| FakeDeviceConfig {
device,
local_ips: vec![local_ip],
remote_ips: vec![remote_ip],
})
.collect(),
))
}
}
#[cfg(test)]
mod test {
use core::convert::Infallible as Never;
use alloc::vec;
use assert_matches::assert_matches;
use const_unwrap::const_unwrap_option;
use derivative::Derivative;
use ip_test_macro::ip_test;
use net_declare::{net_ip_v4, net_ip_v6};
use net_types::ip::{IpVersionMarker, Ipv4Addr, Ipv6Addr};
use net_types::Witness;
use netstack3_base::socket::{
AddrVec, Bound, IncompatibleError, ListenerAddrInfo, RemoveResult, SocketMapAddrStateSpec,
};
use netstack3_base::socketmap::SocketMap;
use netstack3_base::testutil::{
FakeDeviceId, FakeReferencyDeviceId, FakeStrongDeviceId, FakeWeakDeviceId,
MultipleDevicesId, TestIpExt,
};
use netstack3_base::{ContextProvider, CtxPair, UninstantiableWrapper};
use netstack3_ip::device::IpDeviceStateIpExt;
use netstack3_ip::socket::testutil::{
FakeDeviceConfig, FakeDualStackIpSocketCtx, FakeIpSocketCtx,
};
use netstack3_ip::testutil::DualStackSendIpPacketMeta;
use netstack3_ip::DEFAULT_HOP_LIMITS;
use packet::{Buf, Serializer as _};
use packet_formats::ip::{Ipv4Proto, Ipv6Proto};
use test_case::test_case;
use super::*;
use crate::internal::spec_context;
trait DatagramIpExt<D: FakeStrongDeviceId>:
IpExt + IpDeviceStateIpExt + TestIpExt + DualStackIpExt + DualStackContextsIpExt<D>
{
}
impl<
D: FakeStrongDeviceId,
I: Ip
+ IpExt
+ IpDeviceStateIpExt
+ TestIpExt
+ DualStackIpExt
+ DualStackContextsIpExt<D>,
> DatagramIpExt<D> for I
{
}
#[derive(Debug)]
enum FakeAddrSpec {}
impl SocketMapAddrSpec for FakeAddrSpec {
type LocalIdentifier = NonZeroU16;
type RemoteIdentifier = u16;
}
#[derive(Debug)]
enum FakeStateSpec {}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct Tag;
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
enum Sharing {
#[default]
NoConflicts,
ConnectionConflicts {
remote_port: u16,
},
}
#[derive(Clone, Debug, Derivative)]
#[derivative(Eq(bound = ""), PartialEq(bound = ""))]
struct Id<I: IpExt, D: WeakDeviceIdentifier>(StrongRc<I, D, FakeStateSpec>);
impl<I: IpExt, D: WeakDeviceIdentifier> Id<I, D> {
fn get(&self) -> impl Deref<Target = SocketState<I, D, FakeStateSpec>> + '_ {
let Self(rc) = self;
rc.state.read()
}
fn get_mut(&self) -> impl DerefMut<Target = SocketState<I, D, FakeStateSpec>> + '_ {
let Self(rc) = self;
rc.state.write()
}
}
impl<I: IpExt, D: WeakDeviceIdentifier> From<StrongRc<I, D, FakeStateSpec>> for Id<I, D> {
fn from(value: StrongRc<I, D, FakeStateSpec>) -> Self {
Self(value)
}
}
impl<I: IpExt, D: WeakDeviceIdentifier> Borrow<StrongRc<I, D, FakeStateSpec>> for Id<I, D> {
fn borrow(&self) -> &StrongRc<I, D, FakeStateSpec> {
let Self(rc) = self;
rc
}
}
#[derive(Debug)]
struct AddrState<T>(T);
struct FakeSocketMapStateSpec<I, D>(PhantomData<(I, D)>, Never);
impl<I: IpExt, D: WeakDeviceIdentifier> SocketMapStateSpec for FakeSocketMapStateSpec<I, D> {
type AddrVecTag = Tag;
type ConnAddrState = AddrState<Self::ConnId>;
type ConnId = I::DualStackBoundSocketId<D, FakeStateSpec>;
type ConnSharingState = Sharing;
type ListenerAddrState = AddrState<Self::ListenerId>;
type ListenerId = I::DualStackBoundSocketId<D, FakeStateSpec>;
type ListenerSharingState = Sharing;
fn listener_tag(_: ListenerAddrInfo, _state: &Self::ListenerAddrState) -> Self::AddrVecTag {
Tag
}
fn connected_tag(_has_device: bool, _state: &Self::ConnAddrState) -> Self::AddrVecTag {
Tag
}
}
const FAKE_DATAGRAM_IPV4_PROTOCOL: Ipv4Proto = Ipv4Proto::Other(253);
const FAKE_DATAGRAM_IPV6_PROTOCOL: Ipv6Proto = Ipv6Proto::Other(254);
impl DatagramSocketSpec for FakeStateSpec {
const NAME: &'static str = "FAKE";
type AddrSpec = FakeAddrSpec;
type SocketId<I: IpExt, D: WeakDeviceIdentifier> = Id<I, D>;
type OtherStackIpOptions<I: IpExt, D: WeakDeviceIdentifier> =
DatagramIpSpecificSocketOptions<I::OtherVersion, D>;
type SocketMapSpec<I: IpExt, D: WeakDeviceIdentifier> = FakeSocketMapStateSpec<I, D>;
type SharingState = Sharing;
type ListenerIpAddr<I: IpExt> =
I::DualStackListenerIpAddr<<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier>;
type ConnIpAddr<I: IpExt> = I::DualStackConnIpAddr<Self>;
type ConnStateExtra = ();
type ConnState<I: IpExt, D: WeakDeviceIdentifier> = I::DualStackConnState<D, Self>;
type ExternalData<I: Ip> = ();
fn ip_proto<I: IpProtoExt>() -> I::Proto {
I::map_ip((), |()| FAKE_DATAGRAM_IPV4_PROTOCOL, |()| FAKE_DATAGRAM_IPV6_PROTOCOL)
}
fn make_bound_socket_map_id<I: IpExt, D: WeakDeviceIdentifier>(
s: &Self::SocketId<I, D>,
) -> <Self::SocketMapSpec<I, D> as DatagramSocketMapSpec<I, D, Self::AddrSpec>>::BoundSocketId
{
I::into_dual_stack_bound_socket_id(s.clone())
}
type Serializer<I: IpExt, B: BufferMut> = packet::Nested<B, ()>;
type SerializeError = Never;
fn make_packet<I: IpExt, B: BufferMut>(
body: B,
_addr: &ConnIpAddr<
I::Addr,
<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<FakeAddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>,
) -> Result<Self::Serializer<I, B>, Never> {
Ok(body.encapsulate(()))
}
fn try_alloc_listen_identifier<I: Ip, D: WeakDeviceIdentifier>(
_bindings_ctx: &mut impl RngContext,
is_available: impl Fn(
<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier,
) -> Result<(), InUseError>,
) -> Option<<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier> {
(1..=u16::MAX).map(|i| NonZeroU16::new(i).unwrap()).find(|i| is_available(*i).is_ok())
}
fn conn_info_from_state<I: IpExt, D: WeakDeviceIdentifier>(
state: &Self::ConnState<I, D>,
) -> ConnInfo<I::Addr, D> {
let ConnAddr { ip, device } = I::conn_addr_from_state(state);
let ConnInfoAddr { local: (local_ip, local_port), remote: (remote_ip, remote_port) } =
ip.into();
ConnInfo::new(local_ip, local_port, remote_ip, remote_port, || {
device.clone().expect("device must be bound for addresses that require zones")
})
}
fn try_alloc_local_id<I: IpExt, D: WeakDeviceIdentifier, BC: RngContext>(
bound: &BoundSocketMap<I, D, FakeAddrSpec, FakeSocketMapStateSpec<I, D>>,
_bindings_ctx: &mut BC,
_flow: DatagramFlowId<I::Addr, <FakeAddrSpec as SocketMapAddrSpec>::RemoteIdentifier>,
) -> Option<<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier> {
(1..u16::MAX).find_map(|identifier| {
let identifier = NonZeroU16::new(identifier).unwrap();
bound
.listeners()
.could_insert(
&ListenerAddr {
device: None,
ip: ListenerIpAddr { addr: None, identifier },
},
&Default::default(),
)
.is_ok()
.then_some(identifier)
})
}
}
impl<I: IpExt, D: WeakDeviceIdentifier> DatagramSocketMapSpec<I, D, FakeAddrSpec>
for FakeSocketMapStateSpec<I, D>
{
type BoundSocketId = I::DualStackBoundSocketId<D, FakeStateSpec>;
}
impl<I: IpExt, D: WeakDeviceIdentifier>
SocketMapConflictPolicy<
ConnAddr<
ConnIpAddr<
I::Addr,
<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<FakeAddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>,
D,
>,
Sharing,
I,
D,
FakeAddrSpec,
> for FakeSocketMapStateSpec<I, D>
{
fn check_insert_conflicts(
sharing: &Sharing,
addr: &ConnAddr<
ConnIpAddr<
I::Addr,
<FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier,
<FakeAddrSpec as SocketMapAddrSpec>::RemoteIdentifier,
>,
D,
>,
_socketmap: &SocketMap<AddrVec<I, D, FakeAddrSpec>, Bound<Self>>,
) -> Result<(), InsertError> {
let ConnAddr { ip: ConnIpAddr { local: _, remote: (_remote_ip, port) }, device: _ } =
addr;
match sharing {
Sharing::NoConflicts => Ok(()),
Sharing::ConnectionConflicts { remote_port } => {
if remote_port == port {
Err(InsertError::Exists)
} else {
Ok(())
}
}
}
}
}
impl<I: IpExt, D: WeakDeviceIdentifier>
SocketMapConflictPolicy<
ListenerAddr<
ListenerIpAddr<I::Addr, <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
D,
>,
Sharing,
I,
D,
FakeAddrSpec,
> for FakeSocketMapStateSpec<I, D>
{
fn check_insert_conflicts(
sharing: &Sharing,
_addr: &ListenerAddr<
ListenerIpAddr<I::Addr, <FakeAddrSpec as SocketMapAddrSpec>::LocalIdentifier>,
D,
>,
_socketmap: &SocketMap<AddrVec<I, D, FakeAddrSpec>, Bound<Self>>,
) -> Result<(), InsertError> {
match sharing {
Sharing::NoConflicts => Ok(()),
Sharing::ConnectionConflicts { remote_port: _ } => Ok(()),
}
}
}
impl<T: Eq> SocketMapAddrStateSpec for AddrState<T> {
type Id = T;
type SharingState = Sharing;
type Inserter<'a>
= Never
where
Self: 'a;
fn new(_sharing: &Self::SharingState, id: Self::Id) -> Self {
AddrState(id)
}
fn contains_id(&self, id: &Self::Id) -> bool {
let Self(inner) = self;
inner == id
}
fn try_get_inserter<'a, 'b>(
&'b mut self,
_new_sharing_state: &'a Self::SharingState,
) -> Result<Self::Inserter<'b>, IncompatibleError> {
Err(IncompatibleError)
}
fn could_insert(
&self,
_new_sharing_state: &Self::SharingState,
) -> Result<(), IncompatibleError> {
Err(IncompatibleError)
}
fn remove_by_id(&mut self, _id: Self::Id) -> RemoveResult {
RemoveResult::IsLast
}
}
#[derive(Derivative, GenericOverIp)]
#[derivative(Default(bound = ""))]
#[generic_over_ip()]
struct FakeBoundSockets<D: FakeStrongDeviceId> {
v4: BoundSockets<
Ipv4,
FakeWeakDeviceId<D>,
FakeAddrSpec,
FakeSocketMapStateSpec<Ipv4, FakeWeakDeviceId<D>>,
>,
v6: BoundSockets<
Ipv6,
FakeWeakDeviceId<D>,
FakeAddrSpec,
FakeSocketMapStateSpec<Ipv6, FakeWeakDeviceId<D>>,
>,
}
impl<D: FakeStrongDeviceId, I: IpExt>
AsRef<
BoundSockets<
I,
FakeWeakDeviceId<D>,
FakeAddrSpec,
FakeSocketMapStateSpec<I, FakeWeakDeviceId<D>>,
>,
> for FakeBoundSockets<D>
{
fn as_ref(
&self,
) -> &BoundSockets<
I,
FakeWeakDeviceId<D>,
FakeAddrSpec,
FakeSocketMapStateSpec<I, FakeWeakDeviceId<D>>,
> {
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct Wrap<'a, I: IpExt, D: FakeStrongDeviceId>(
&'a BoundSockets<
I,
FakeWeakDeviceId<D>,
FakeAddrSpec,
FakeSocketMapStateSpec<I, FakeWeakDeviceId<D>>,
>,
);
let Wrap(state) = I::map_ip(self, |state| Wrap(&state.v4), |state| Wrap(&state.v6));
state
}
}
impl<D: FakeStrongDeviceId, I: IpExt>
AsMut<
BoundSockets<
I,
FakeWeakDeviceId<D>,
FakeAddrSpec,
FakeSocketMapStateSpec<I, FakeWeakDeviceId<D>>,
>,
> for FakeBoundSockets<D>
{
fn as_mut(
&mut self,
) -> &mut BoundSockets<
I,
FakeWeakDeviceId<D>,
FakeAddrSpec,
FakeSocketMapStateSpec<I, FakeWeakDeviceId<D>>,
> {
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct Wrap<'a, I: IpExt, D: FakeStrongDeviceId>(
&'a mut BoundSockets<
I,
FakeWeakDeviceId<D>,
FakeAddrSpec,
FakeSocketMapStateSpec<I, FakeWeakDeviceId<D>>,
>,
);
let Wrap(state) =
I::map_ip(self, |state| Wrap(&mut state.v4), |state| Wrap(&mut state.v6));
state
}
}
type FakeBindingsCtx = netstack3_base::testutil::FakeBindingsCtx<(), (), (), ()>;
type FakeCtx<I, D> = CtxPair<FakeCoreCtx<I, D>, FakeBindingsCtx>;
type FakeSocketSet<I, D> = DatagramSocketSet<I, FakeWeakDeviceId<D>, FakeStateSpec>;
type InnerIpSocketCtx<D> = netstack3_base::testutil::FakeCoreCtx<
FakeDualStackIpSocketCtx<D>,
DualStackSendIpPacketMeta<D>,
D,
>;
trait DatagramApiExt: ContextPair + Sized {
fn datagram_api<I: Ip>(&mut self) -> DatagramApi<I, &mut Self, FakeStateSpec> {
DatagramApi::new(self)
}
}
impl<O> DatagramApiExt for O where O: ContextPair + Sized {}
struct FakeDualStackCoreCtx<D: FakeStrongDeviceId> {
bound_sockets: FakeBoundSockets<D>,
ip_socket_ctx: InnerIpSocketCtx<D>,
}
struct FakeCoreCtx<I: IpExt, D: FakeStrongDeviceId> {
dual_stack: FakeDualStackCoreCtx<D>,
socket_set: FakeSocketSet<I, D>,
}
impl<I: IpExt, D: FakeStrongDeviceId> ContextProvider for FakeCoreCtx<I, D> {
type Context = Self;
fn context(&mut self) -> &mut Self::Context {
self
}
}
impl<I: IpExt, D: FakeStrongDeviceId> FakeCoreCtx<I, D> {
fn new() -> Self {
Self::new_with_sockets(Default::default(), Default::default())
}
fn new_with_sockets(
socket_set: FakeSocketSet<I, D>,
bound_sockets: FakeBoundSockets<D>,
) -> Self {
Self {
socket_set,
dual_stack: FakeDualStackCoreCtx {
bound_sockets,
ip_socket_ctx: Default::default(),
},
}
}
fn new_with_ip_socket_ctx(ip_socket_ctx: FakeDualStackIpSocketCtx<D>) -> Self {
Self {
socket_set: Default::default(),
dual_stack: FakeDualStackCoreCtx {
bound_sockets: Default::default(),
ip_socket_ctx: InnerIpSocketCtx::with_state(ip_socket_ctx),
},
}
}
}
impl<I: IpExt, D: FakeStrongDeviceId> DeviceIdContext<AnyDevice> for FakeCoreCtx<I, D> {
type DeviceId = D;
type WeakDeviceId = FakeWeakDeviceId<D>;
}
impl<D: FakeStrongDeviceId> DeviceIdContext<AnyDevice> for FakeDualStackCoreCtx<D> {
type DeviceId = D;
type WeakDeviceId = FakeWeakDeviceId<D>;
}
impl<D: FakeStrongDeviceId, I: DatagramIpExt<D>>
spec_context::DatagramSpecStateContext<I, FakeCoreCtx<I, D>, FakeBindingsCtx>
for FakeStateSpec
{
type SocketsStateCtx<'a> = FakeDualStackCoreCtx<D>;
fn with_all_sockets_mut<
O,
F: FnOnce(&mut DatagramSocketSet<I, FakeWeakDeviceId<D>, FakeStateSpec>) -> O,
>(
core_ctx: &mut FakeCoreCtx<I, D>,
cb: F,
) -> O {
cb(&mut core_ctx.socket_set)
}
fn with_all_sockets<
O,
F: FnOnce(&DatagramSocketSet<I, FakeWeakDeviceId<D>, FakeStateSpec>) -> O,
>(
core_ctx: &mut FakeCoreCtx<I, D>,
cb: F,
) -> O {
cb(&core_ctx.socket_set)
}
fn with_socket_state<
O,
F: FnOnce(
&mut Self::SocketsStateCtx<'_>,
&SocketState<I, FakeWeakDeviceId<D>, FakeStateSpec>,
) -> O,
>(
core_ctx: &mut FakeCoreCtx<I, D>,
id: &Id<I, FakeWeakDeviceId<D>>,
cb: F,
) -> O {
cb(&mut core_ctx.dual_stack, &id.get())
}
fn with_socket_state_mut<
O,
F: FnOnce(
&mut Self::SocketsStateCtx<'_>,
&mut SocketState<I, FakeWeakDeviceId<D>, FakeStateSpec>,
) -> O,
>(
core_ctx: &mut FakeCoreCtx<I, D>,
id: &Id<I, FakeWeakDeviceId<D>>,
cb: F,
) -> O {
cb(&mut core_ctx.dual_stack, &mut id.get_mut())
}
fn for_each_socket<
F: FnMut(
&mut Self::SocketsStateCtx<'_>,
&Id<I, FakeWeakDeviceId<D>>,
&SocketState<I, FakeWeakDeviceId<D>, FakeStateSpec>,
),
>(
core_ctx: &mut FakeCoreCtx<I, D>,
mut cb: F,
) {
core_ctx.socket_set.keys().for_each(|id| {
let id = Id::from(id.clone());
cb(&mut core_ctx.dual_stack, &id, &id.get());
})
}
}
trait DualStackContextsIpExt<D: FakeStrongDeviceId>: IpExt {
type DualStackContext: DualStackDatagramBoundStateContext<
Self,
FakeBindingsCtx,
FakeStateSpec,
DeviceId = D,
WeakDeviceId = FakeWeakDeviceId<D>,
>;
type NonDualStackContext: NonDualStackDatagramBoundStateContext<
Self,
FakeBindingsCtx,
FakeStateSpec,
DeviceId = D,
WeakDeviceId = FakeWeakDeviceId<D>,
>;
fn dual_stack_context(
core_ctx: &mut FakeDualStackCoreCtx<D>,
) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext>;
}
impl<D: FakeStrongDeviceId> DualStackContextsIpExt<D> for Ipv4 {
type DualStackContext = UninstantiableWrapper<FakeDualStackCoreCtx<D>>;
type NonDualStackContext = FakeDualStackCoreCtx<D>;
fn dual_stack_context(
core_ctx: &mut FakeDualStackCoreCtx<D>,
) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
MaybeDualStack::NotDualStack(core_ctx)
}
}
impl<D: FakeStrongDeviceId> DualStackContextsIpExt<D> for Ipv6 {
type DualStackContext = FakeDualStackCoreCtx<D>;
type NonDualStackContext = UninstantiableWrapper<FakeDualStackCoreCtx<D>>;
fn dual_stack_context(
core_ctx: &mut FakeDualStackCoreCtx<D>,
) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
MaybeDualStack::DualStack(core_ctx)
}
}
impl<D: FakeStrongDeviceId, I: DualStackContextsIpExt<D>>
spec_context::DatagramSpecBoundStateContext<I, FakeDualStackCoreCtx<D>, FakeBindingsCtx>
for FakeStateSpec
{
type IpSocketsCtx<'a> = InnerIpSocketCtx<D>;
type DualStackContext = I::DualStackContext;
type NonDualStackContext = I::NonDualStackContext;
fn with_bound_sockets<
O,
F: FnOnce(
&mut Self::IpSocketsCtx<'_>,
&BoundSockets<
I,
FakeWeakDeviceId<D>,
FakeAddrSpec,
FakeSocketMapStateSpec<I, FakeWeakDeviceId<D>>,
>,
) -> O,
>(
core_ctx: &mut FakeDualStackCoreCtx<D>,
cb: F,
) -> O {
let FakeDualStackCoreCtx { bound_sockets, ip_socket_ctx } = core_ctx;
cb(ip_socket_ctx, bound_sockets.as_ref())
}
fn with_bound_sockets_mut<
O,
F: FnOnce(
&mut Self::IpSocketsCtx<'_>,
&mut BoundSockets<
I,
FakeWeakDeviceId<D>,
FakeAddrSpec,
FakeSocketMapStateSpec<I, FakeWeakDeviceId<D>>,
>,
) -> O,
>(
core_ctx: &mut FakeDualStackCoreCtx<D>,
cb: F,
) -> O {
let FakeDualStackCoreCtx { bound_sockets, ip_socket_ctx } = core_ctx;
cb(ip_socket_ctx, bound_sockets.as_mut())
}
fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
core_ctx: &mut FakeDualStackCoreCtx<D>,
cb: F,
) -> O {
cb(&mut core_ctx.ip_socket_ctx)
}
fn dual_stack_context(
core_ctx: &mut FakeDualStackCoreCtx<D>,
) -> MaybeDualStack<&mut Self::DualStackContext, &mut Self::NonDualStackContext> {
I::dual_stack_context(core_ctx)
}
}
impl<D: FakeStrongDeviceId>
spec_context::NonDualStackDatagramSpecBoundStateContext<
Ipv4,
FakeDualStackCoreCtx<D>,
FakeBindingsCtx,
> for FakeStateSpec
{
fn nds_converter(
_core_ctx: &FakeDualStackCoreCtx<D>,
) -> impl NonDualStackConverter<Ipv4, FakeWeakDeviceId<D>, Self> {
()
}
}
impl<D: FakeStrongDeviceId>
spec_context::DualStackDatagramSpecBoundStateContext<
Ipv6,
FakeDualStackCoreCtx<D>,
FakeBindingsCtx,
> for FakeStateSpec
{
type IpSocketsCtx<'a> = InnerIpSocketCtx<D>;
fn dual_stack_enabled(
_core_ctx: &FakeDualStackCoreCtx<D>,
_state: &impl AsRef<IpOptions<Ipv6, FakeWeakDeviceId<D>, FakeStateSpec>>,
) -> bool {
true
}
fn to_other_socket_options<'a>(
_core_ctx: &FakeDualStackCoreCtx<D>,
state: &'a IpOptions<Ipv6, FakeWeakDeviceId<D>, FakeStateSpec>,
) -> &'a DatagramIpSpecificSocketOptions<Ipv4, FakeWeakDeviceId<D>> {
let IpOptions { other_stack, .. } = state;
other_stack
}
fn ds_converter(
_core_ctx: &FakeDualStackCoreCtx<D>,
) -> impl DualStackConverter<Ipv6, FakeWeakDeviceId<D>, Self> {
()
}
fn to_other_bound_socket_id(
_core_ctx: &FakeDualStackCoreCtx<D>,
id: &Id<Ipv6, D::Weak>,
) -> EitherIpSocket<D::Weak, FakeStateSpec> {
EitherIpSocket::V6(id.clone())
}
fn with_both_bound_sockets_mut<
O,
F: FnOnce(
&mut Self::IpSocketsCtx<'_>,
&mut BoundSocketsFromSpec<Ipv6, FakeDualStackCoreCtx<D>, FakeStateSpec>,
&mut BoundSocketsFromSpec<Ipv4, FakeDualStackCoreCtx<D>, FakeStateSpec>,
) -> O,
>(
core_ctx: &mut FakeDualStackCoreCtx<D>,
cb: F,
) -> O {
let FakeDualStackCoreCtx { bound_sockets: FakeBoundSockets { v4, v6 }, ip_socket_ctx } =
core_ctx;
cb(ip_socket_ctx, v6, v4)
}
fn with_other_bound_sockets_mut<
O,
F: FnOnce(
&mut Self::IpSocketsCtx<'_>,
&mut BoundSocketsFromSpec<Ipv4, FakeDualStackCoreCtx<D>, FakeStateSpec>,
) -> O,
>(
core_ctx: &mut FakeDualStackCoreCtx<D>,
cb: F,
) -> O {
let FakeDualStackCoreCtx { bound_sockets, ip_socket_ctx } = core_ctx;
cb(ip_socket_ctx, bound_sockets.as_mut())
}
fn with_transport_context<O, F: FnOnce(&mut Self::IpSocketsCtx<'_>) -> O>(
core_ctx: &mut FakeDualStackCoreCtx<D>,
cb: F,
) -> O {
cb(&mut core_ctx.ip_socket_ctx)
}
}
#[ip_test(I)]
fn set_get_hop_limits<I: DatagramIpExt<FakeDeviceId>>() {
let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, FakeDeviceId>::new());
let mut api = ctx.datagram_api::<I>();
let unbound = api.create(());
const EXPECTED_HOP_LIMITS: HopLimits = HopLimits {
unicast: const_unwrap_option(NonZeroU8::new(45)),
multicast: const_unwrap_option(NonZeroU8::new(23)),
};
api.update_ip_hop_limit(&unbound, |limits| {
*limits = SocketHopLimits {
unicast: Some(EXPECTED_HOP_LIMITS.unicast),
multicast: Some(EXPECTED_HOP_LIMITS.multicast),
version: IpVersionMarker::default(),
}
});
assert_eq!(api.get_ip_hop_limits(&unbound), EXPECTED_HOP_LIMITS);
}
#[ip_test(I)]
fn set_get_device_hop_limits<I: DatagramIpExt<FakeReferencyDeviceId>>() {
let device = FakeReferencyDeviceId::default();
let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(
FakeDualStackIpSocketCtx::new([FakeDeviceConfig::<_, SpecifiedAddr<I::Addr>> {
device: device.clone(),
local_ips: Default::default(),
remote_ips: Default::default(),
}]),
));
let mut api = ctx.datagram_api::<I>();
let unbound = api.create(());
api.set_device(&unbound, Some(&device)).unwrap();
let HopLimits { mut unicast, multicast } = DEFAULT_HOP_LIMITS;
unicast = unicast.checked_add(1).unwrap();
{
let device_state =
api.core_ctx().dual_stack.ip_socket_ctx.state.get_device_state_mut::<I>(&device);
assert_ne!(device_state.default_hop_limit, unicast);
device_state.default_hop_limit = unicast;
}
assert_eq!(api.get_ip_hop_limits(&unbound), HopLimits { unicast, multicast });
device.mark_removed();
assert_eq!(api.get_ip_hop_limits(&unbound), DEFAULT_HOP_LIMITS);
}
#[ip_test(I)]
fn default_hop_limits<I: DatagramIpExt<FakeDeviceId>>() {
let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, FakeDeviceId>::new());
let mut api = ctx.datagram_api::<I>();
let unbound = api.create(());
assert_eq!(api.get_ip_hop_limits(&unbound), DEFAULT_HOP_LIMITS);
api.update_ip_hop_limit(&unbound, |limits| {
*limits = SocketHopLimits {
unicast: Some(const_unwrap_option(NonZeroU8::new(1))),
multicast: Some(const_unwrap_option(NonZeroU8::new(1))),
version: IpVersionMarker::default(),
}
});
assert_ne!(api.get_ip_hop_limits(&unbound), DEFAULT_HOP_LIMITS);
api.update_ip_hop_limit(&unbound, |limits| *limits = Default::default());
assert_eq!(api.get_ip_hop_limits(&unbound), DEFAULT_HOP_LIMITS);
}
#[ip_test(I)]
fn bind_device_unbound<I: DatagramIpExt<FakeDeviceId>>() {
let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, FakeDeviceId>::new());
let mut api = ctx.datagram_api::<I>();
let unbound = api.create(());
api.set_device(&unbound, Some(&FakeDeviceId)).unwrap();
assert_eq!(api.get_bound_device(&unbound), Some(FakeWeakDeviceId(FakeDeviceId)));
api.set_device(&unbound, None).unwrap();
assert_eq!(api.get_bound_device(&unbound), None);
}
#[ip_test(I)]
fn send_to_binds_unbound<I: DatagramIpExt<FakeDeviceId>>() {
let mut ctx =
FakeCtx::with_core_ctx(FakeCoreCtx::<I, FakeDeviceId>::new_with_ip_socket_ctx(
FakeDualStackIpSocketCtx::new([FakeDeviceConfig {
device: FakeDeviceId,
local_ips: vec![I::TEST_ADDRS.local_ip],
remote_ips: vec![I::TEST_ADDRS.remote_ip],
}]),
));
let mut api = ctx.datagram_api::<I>();
let socket = api.create(());
let body = Buf::new(Vec::new(), ..);
api.send_to(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), 1234, body)
.expect("succeeds");
assert_matches!(api.get_info(&socket), SocketInfo::Listener(_));
}
#[ip_test(I)]
fn send_to_no_route_still_binds<I: DatagramIpExt<FakeDeviceId>>() {
let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(
FakeDualStackIpSocketCtx::new([FakeDeviceConfig {
device: FakeDeviceId,
local_ips: vec![I::TEST_ADDRS.local_ip],
remote_ips: vec![],
}]),
));
let mut api = ctx.datagram_api::<I>();
let socket = api.create(());
let body = Buf::new(Vec::new(), ..);
assert_matches!(
api.send_to(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)), 1234, body,),
Err(Either::Right(SendToError::CreateAndSend(_)))
);
assert_matches!(api.get_info(&socket), SocketInfo::Listener(_));
}
#[ip_test(I)]
#[test_case(true; "remove device b")]
#[test_case(false; "dont remove device b")]
fn multicast_membership_changes<I: DatagramIpExt<FakeReferencyDeviceId> + TestIpExt>(
remove_device_b: bool,
) {
let device_a = FakeReferencyDeviceId::default();
let device_b = FakeReferencyDeviceId::default();
let mut core_ctx = FakeIpSocketCtx::<I, FakeReferencyDeviceId>::new(
[device_a.clone(), device_b.clone()].into_iter().map(|device| FakeDeviceConfig {
device,
local_ips: Default::default(),
remote_ips: Default::default(),
}),
);
let mut bindings_ctx = FakeBindingsCtx::default();
let multicast_addr1 = I::get_multicast_addr(1);
let mut memberships = MulticastMemberships::default();
assert_eq!(
memberships.apply_membership_change(
multicast_addr1,
&FakeWeakDeviceId(device_a.clone()),
true ),
Some(MulticastMembershipChange::Join),
);
core_ctx.join_multicast_group(&mut bindings_ctx, &device_a, multicast_addr1);
let multicast_addr2 = I::get_multicast_addr(2);
assert_eq!(
memberships.apply_membership_change(
multicast_addr2,
&FakeWeakDeviceId(device_b.clone()),
true ),
Some(MulticastMembershipChange::Join),
);
core_ctx.join_multicast_group(&mut bindings_ctx, &device_b, multicast_addr2);
for (device, addr, expected) in [
(&device_a, multicast_addr1, true),
(&device_a, multicast_addr2, false),
(&device_b, multicast_addr1, false),
(&device_b, multicast_addr2, true),
] {
assert_eq!(
core_ctx.get_device_state(device).is_in_multicast_group(&addr),
expected,
"device={:?}, addr={}",
device,
addr,
);
}
if remove_device_b {
device_b.mark_removed();
}
leave_all_joined_groups(&mut core_ctx, &mut bindings_ctx, memberships);
for (device, addr, expected) in [
(&device_a, multicast_addr1, false),
(&device_a, multicast_addr2, false),
(&device_b, multicast_addr1, false),
(&device_b, multicast_addr2, remove_device_b),
] {
assert_eq!(
core_ctx.get_device_state(device).is_in_multicast_group(&addr),
expected,
"device={:?}, addr={}",
device,
addr,
);
}
}
#[ip_test(I)]
fn set_get_transparent<I: DatagramIpExt<FakeDeviceId>>() {
let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(
FakeDualStackIpSocketCtx::new([FakeDeviceConfig::<_, SpecifiedAddr<I::Addr>> {
device: FakeDeviceId,
local_ips: Default::default(),
remote_ips: Default::default(),
}]),
));
let mut api = ctx.datagram_api::<I>();
let unbound = api.create(());
assert!(!api.get_ip_transparent(&unbound));
api.set_ip_transparent(&unbound, true);
assert!(api.get_ip_transparent(&unbound));
api.set_ip_transparent(&unbound, false);
assert!(!api.get_ip_transparent(&unbound));
}
#[ip_test(I)]
fn transparent_bind_connect_non_local_src_addr<I: DatagramIpExt<FakeDeviceId>>() {
let mut ctx = FakeCtx::with_core_ctx(FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(
FakeDualStackIpSocketCtx::new([FakeDeviceConfig {
device: FakeDeviceId,
local_ips: vec![],
remote_ips: vec![I::TEST_ADDRS.remote_ip],
}]),
));
let mut api = ctx.datagram_api::<I>();
let socket = api.create(());
api.set_ip_transparent(&socket, true);
const LOCAL_PORT: NonZeroU16 = const_unwrap_option(NonZeroU16::new(10));
const REMOTE_PORT: u16 = 1234;
api.listen(&socket, Some(ZonedAddr::Unzoned(I::TEST_ADDRS.local_ip)), Some(LOCAL_PORT))
.expect("listen should succeed");
api.connect(
&socket,
Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
REMOTE_PORT,
Default::default(),
)
.expect("connect should succeed");
api.send_to(
&socket,
Some(ZonedAddr::Unzoned(I::TEST_ADDRS.remote_ip)),
REMOTE_PORT,
Buf::new(Vec::new(), ..),
)
.expect("send_to should succeed");
}
#[derive(Eq, PartialEq)]
enum OriginalSocketState {
Unbound,
Listener,
Connected,
}
#[ip_test(I)]
#[test_case(OriginalSocketState::Unbound; "reinsert_unbound")]
#[test_case(OriginalSocketState::Listener; "reinsert_listener")]
#[test_case(OriginalSocketState::Connected; "reinsert_connected")]
fn connect_reinserts_on_failure_single_stack<I: DatagramIpExt<FakeDeviceId>>(
original: OriginalSocketState,
) {
connect_reinserts_on_failure_inner::<I>(
original,
I::TEST_ADDRS.local_ip.get(),
I::TEST_ADDRS.remote_ip,
);
}
#[test_case(OriginalSocketState::Listener, net_ip_v6!("::FFFF:192.0.2.1"),
net_ip_v4!("192.0.2.2"); "reinsert_listener_other_stack")]
#[test_case(OriginalSocketState::Listener, net_ip_v6!("::"),
net_ip_v4!("192.0.2.2"); "reinsert_listener_both_stacks")]
#[test_case(OriginalSocketState::Connected, net_ip_v6!("::FFFF:192.0.2.1"),
net_ip_v4!("192.0.2.2"); "reinsert_connected_other_stack")]
fn connect_reinserts_on_failure_dual_stack(
original: OriginalSocketState,
local_ip: Ipv6Addr,
remote_ip: Ipv4Addr,
) {
let remote_ip = remote_ip.to_ipv6_mapped();
connect_reinserts_on_failure_inner::<Ipv6>(original, local_ip, remote_ip);
}
fn connect_reinserts_on_failure_inner<I: DatagramIpExt<FakeDeviceId>>(
original: OriginalSocketState,
local_ip: I::Addr,
remote_ip: SpecifiedAddr<I::Addr>,
) {
let mut ctx = testutil::setup_fake_ctx_with_dualstack_conn_addrs::<_, FakeBindingsCtx, _>(
local_ip.to_ip_addr(),
remote_ip.into(),
[FakeDeviceId {}],
|device_configs| {
FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(FakeDualStackIpSocketCtx::new(
device_configs,
))
},
);
let mut api = ctx.datagram_api::<I>();
let socket = api.create(());
const LOCAL_PORT: NonZeroU16 = const_unwrap_option(NonZeroU16::new(10));
const ORIGINAL_REMOTE_PORT: u16 = 1234;
const NEW_REMOTE_PORT: u16 = 5678;
match original {
OriginalSocketState::Unbound => {}
OriginalSocketState::Listener => api
.listen(
&socket,
SpecifiedAddr::new(local_ip).map(ZonedAddr::Unzoned),
Some(LOCAL_PORT),
)
.expect("listen should succeed"),
OriginalSocketState::Connected => api
.connect(
&socket,
Some(ZonedAddr::Unzoned(remote_ip)),
ORIGINAL_REMOTE_PORT,
Default::default(),
)
.expect("connect should succeed"),
}
api.core_ctx().with_socket_state_mut(
&socket,
|_core_ctx, state: &mut SocketState<I, _, FakeStateSpec>| {
let sharing = match state {
SocketState::Unbound(UnboundSocketState {
device: _,
sharing,
ip_options: _,
}) => sharing,
SocketState::Bound(BoundSocketState {
socket_type,
original_bound_addr: _,
}) => match socket_type {
BoundSocketStateType::Connected { state: _, sharing } => sharing,
BoundSocketStateType::Listener { state: _, sharing } => sharing,
},
};
*sharing = Sharing::ConnectionConflicts { remote_port: NEW_REMOTE_PORT };
},
);
assert_matches!(
api.connect(
&socket,
Some(ZonedAddr::Unzoned(remote_ip)),
NEW_REMOTE_PORT,
Default::default(),
),
Err(ConnectError::SockAddrConflict)
);
let info = api.get_info(&socket);
match original {
OriginalSocketState::Unbound => assert_matches!(info, SocketInfo::Unbound),
OriginalSocketState::Listener => {
let local_port = assert_matches!(
info,
SocketInfo::Listener(ListenerInfo {
local_ip: _,
local_identifier,
}) => local_identifier
);
assert_eq!(LOCAL_PORT, local_port);
}
OriginalSocketState::Connected => {
let remote_port = assert_matches!(
info,
SocketInfo::Connected(ConnInfo {
local_ip: _,
local_identifier: _,
remote_ip: _,
remote_identifier,
}) => remote_identifier
);
assert_eq!(ORIGINAL_REMOTE_PORT, remote_port);
}
}
}
#[test_case(net_ip_v6!("::a:b:c:d"), ShutdownType::Send; "this_stack_send")]
#[test_case(net_ip_v6!("::a:b:c:d"), ShutdownType::Receive; "this_stack_receive")]
#[test_case(net_ip_v6!("::a:b:c:d"), ShutdownType::SendAndReceive; "this_stack_send_and_receive")]
#[test_case(net_ip_v6!("::FFFF:192.0.2.1"), ShutdownType::Send; "other_stack_send")]
#[test_case(net_ip_v6!("::FFFF:192.0.2.1"), ShutdownType::Receive; "other_stack_receive")]
#[test_case(net_ip_v6!("::FFFF:192.0.2.1"), ShutdownType::SendAndReceive; "other_stack_send_and_receive")]
fn set_get_shutdown_dualstack(remote_ip: Ipv6Addr, shutdown: ShutdownType) {
let remote_ip = SpecifiedAddr::new(remote_ip).expect("remote_ip should be specified");
let mut ctx = testutil::setup_fake_ctx_with_dualstack_conn_addrs::<_, FakeBindingsCtx, _>(
Ipv6::UNSPECIFIED_ADDRESS.into(),
remote_ip.into(),
[FakeDeviceId {}],
|device_configs| {
FakeCoreCtx::<Ipv6, _>::new_with_ip_socket_ctx(FakeDualStackIpSocketCtx::new(
device_configs,
))
},
);
let mut api = ctx.datagram_api::<Ipv6>();
const REMOTE_PORT: u16 = 1234;
let socket = api.create(());
api.connect(&socket, Some(ZonedAddr::Unzoned(remote_ip)), REMOTE_PORT, Default::default())
.expect("connect should succeed");
assert_eq!(api.get_shutdown_connected(&socket), None);
api.shutdown_connected(&socket, shutdown).expect("shutdown should succeed");
assert_eq!(api.get_shutdown_connected(&socket), Some(shutdown));
}
#[ip_test(I)]
#[test_case(OriginalSocketState::Unbound; "unbound")]
#[test_case(OriginalSocketState::Listener; "listener")]
#[test_case(OriginalSocketState::Connected; "connected")]
fn set_get_device_single_stack<I: DatagramIpExt<MultipleDevicesId>>(
original: OriginalSocketState,
) {
set_get_device_inner::<I>(original, I::TEST_ADDRS.local_ip.get(), I::TEST_ADDRS.remote_ip);
}
#[test_case(OriginalSocketState::Listener, net_ip_v6!("::FFFF:192.0.2.1"),
net_ip_v4!("192.0.2.2"); "listener_other_stack")]
#[test_case(OriginalSocketState::Listener, net_ip_v6!("::"),
net_ip_v4!("192.0.2.2"); "listener_both_stacks")]
#[test_case(OriginalSocketState::Connected, net_ip_v6!("::FFFF:192.0.2.1"),
net_ip_v4!("192.0.2.2"); "connected_other_stack")]
fn set_get_device_dual_stack(
original: OriginalSocketState,
local_ip: Ipv6Addr,
remote_ip: Ipv4Addr,
) {
let remote_ip = remote_ip.to_ipv6_mapped();
set_get_device_inner::<Ipv6>(original, local_ip, remote_ip);
}
fn set_get_device_inner<I: DatagramIpExt<MultipleDevicesId>>(
original: OriginalSocketState,
local_ip: I::Addr,
remote_ip: SpecifiedAddr<I::Addr>,
) {
const DEVICE_ID1: MultipleDevicesId = MultipleDevicesId::A;
const DEVICE_ID2: MultipleDevicesId = MultipleDevicesId::B;
let mut ctx = testutil::setup_fake_ctx_with_dualstack_conn_addrs::<_, FakeBindingsCtx, _>(
local_ip.to_ip_addr(),
remote_ip.into(),
[DEVICE_ID1, DEVICE_ID2],
|device_configs| {
FakeCoreCtx::<I, _>::new_with_ip_socket_ctx(FakeDualStackIpSocketCtx::new(
device_configs,
))
},
);
const LOCAL_PORT: NonZeroU16 = const_unwrap_option(NonZeroU16::new(10));
const REMOTE_PORT: u16 = 1234;
let mut api = ctx.datagram_api::<I>();
let socket1 = api.create(());
let socket2 = api.create(());
for (socket, device_id) in [(&socket1, DEVICE_ID1), (&socket2, DEVICE_ID2)] {
match original {
OriginalSocketState::Unbound => {}
OriginalSocketState::Listener => api
.listen(
&socket,
SpecifiedAddr::new(local_ip).map(ZonedAddr::Unzoned),
Some(LOCAL_PORT),
)
.expect("listen should succeed"),
OriginalSocketState::Connected => api
.connect(
&socket,
Some(ZonedAddr::Unzoned(remote_ip)),
REMOTE_PORT,
Default::default(),
)
.expect("connect should succeed"),
}
assert_eq!(api.get_bound_device(socket), None);
api.set_device(socket, Some(&device_id)).expect("set device should succeed");
assert_eq!(api.get_bound_device(socket), Some(FakeWeakDeviceId(device_id)));
}
if original != OriginalSocketState::Unbound {
assert_eq!(
api.set_device(&socket2, Some(&DEVICE_ID1)),
Err(SocketError::Local(LocalAddressError::AddressInUse))
);
assert_eq!(api.get_bound_device(&socket1), Some(FakeWeakDeviceId(DEVICE_ID1)));
assert_eq!(api.get_bound_device(&socket2), Some(FakeWeakDeviceId(DEVICE_ID2)));
}
api.close(socket2).into_removed();
api.set_device(&socket1, None).expect("set device should succeed");
assert_eq!(api.get_bound_device(&socket1), None,);
}
}