use alloc::vec::Vec;
use core::fmt::Debug;
use core::hash::Hash;
use core::num::{NonZeroU16, NonZeroU8};
use core::ops::{Deref, DerefMut};
use core::time::Duration;
use derivative::Derivative;
use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
use net_types::ip::{
AddrSubnet, GenericOverIp, Ip, IpAddress, IpMarked, IpVersionMarker, Ipv4, Ipv4Addr, Ipv6,
Ipv6Addr,
};
use netstack3_base::sync::{Mutex, PrimaryRc, RwLock, StrongRc, WeakRc};
use netstack3_base::{
AssignedAddrIpExt, BroadcastIpExt, CoreTimerContext, ExistsError, Inspectable,
InspectableValue, Inspector, Instant, InstantBindingsTypes, IpAddressId,
NestedIntoCoreTimerCtx, NotFoundError, ReferenceNotifiers, TimerBindingsTypes, TimerContext,
WeakDeviceIdentifier,
};
use packet_formats::icmp::ndp::NonZeroNdpLifetime;
use packet_formats::utils::NonZeroDuration;
use crate::internal::device::dad::DadBindingsTypes;
use crate::internal::device::route_discovery::Ipv6RouteDiscoveryState;
use crate::internal::device::router_solicitation::RsState;
use crate::internal::device::slaac::{SlaacConfiguration, SlaacState};
use crate::internal::device::{
IpAddressIdSpec, IpDeviceAddr, IpDeviceTimerId, Ipv4DeviceAddr, Ipv4DeviceTimerId,
Ipv6DeviceAddr, Ipv6DeviceTimerId, WeakIpAddressId,
};
use crate::internal::gmp::igmp::{IgmpConfig, IgmpTimerId, IgmpTypeLayout};
use crate::internal::gmp::mld::{MldConfig, MldTimerId, MldTypeLayout};
use crate::internal::gmp::{GmpGroupState, GmpState, GmpTimerId, GmpTypeLayout, MulticastGroupSet};
use crate::internal::types::RawMetric;
use super::dad::NonceCollection;
pub const RETRANS_TIMER_DEFAULT: NonZeroDuration = NonZeroDuration::from_secs(1).unwrap();
const DEFAULT_HOP_LIMIT: NonZeroU8 = NonZeroU8::new(64).unwrap();
pub trait IpDeviceStateIpExt: BroadcastIpExt {
type AssignedAddressState<BT: IpDeviceStateBindingsTypes>: AssignedAddressState<Address = Self::Addr>
+ Debug;
type GmpProtoConfig: Default;
type GmpTypeLayout<BT: IpDeviceStateBindingsTypes>: GmpTypeLayout<Self, BT>;
type GmpTimerId<D: WeakDeviceIdentifier>: From<GmpTimerId<Self, D>>;
}
impl IpDeviceStateIpExt for Ipv4 {
type AssignedAddressState<BT: IpDeviceStateBindingsTypes> = Ipv4AddressEntry<BT>;
type GmpTimerId<D: WeakDeviceIdentifier> = IgmpTimerId<D>;
type GmpProtoConfig = IgmpConfig;
type GmpTypeLayout<BT: IpDeviceStateBindingsTypes> = IgmpTypeLayout;
}
impl IpDeviceStateIpExt for Ipv6 {
type AssignedAddressState<BT: IpDeviceStateBindingsTypes> = Ipv6AddressEntry<BT>;
type GmpTimerId<D: WeakDeviceIdentifier> = MldTimerId<D>;
type GmpProtoConfig = MldConfig;
type GmpTypeLayout<BT: IpDeviceStateBindingsTypes> = MldTypeLayout;
}
pub trait AssignedAddressState: Debug + Send + Sync + 'static {
type Address: IpAddress<Version: AssignedAddrIpExt>;
fn addr(&self) -> IpDeviceAddr<Self::Address>;
fn addr_sub(
&self,
) -> AddrSubnet<
Self::Address,
<<Self::Address as IpAddress>::Version as AssignedAddrIpExt>::AssignedWitness,
>;
}
impl<BT: IpDeviceStateBindingsTypes> AssignedAddressState for Ipv4AddressEntry<BT> {
type Address = Ipv4Addr;
fn addr(&self) -> IpDeviceAddr<Ipv4Addr> {
IpDeviceAddr::new_from_witness(self.addr_sub().addr())
}
fn addr_sub(&self) -> AddrSubnet<Ipv4Addr, Ipv4DeviceAddr> {
*self.addr_sub()
}
}
impl<BT: IpDeviceStateBindingsTypes> AssignedAddressState for Ipv6AddressEntry<BT> {
type Address = Ipv6Addr;
fn addr(&self) -> IpDeviceAddr<Ipv6Addr> {
IpDeviceAddr::new_from_ipv6_device_addr(self.addr_sub().addr())
}
fn addr_sub(&self) -> AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
*self.addr_sub()
}
}
pub struct PrimaryAddressId<S>(PrimaryRc<S>);
impl<S: AssignedAddressState> Debug for PrimaryAddressId<S> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let Self(rc) = self;
write!(f, "PrimaryAddressId({:?} => {})", rc.debug_id(), self.addr_sub())
}
}
impl<S> Deref for PrimaryAddressId<S> {
type Target = S;
fn deref(&self) -> &Self::Target {
let Self(inner) = self;
inner.deref()
}
}
impl<S> PrimaryAddressId<S> {
fn new_with_strong_clone(addr: S) -> (Self, AddressId<S>) {
let primary = PrimaryRc::new(addr);
let strong = PrimaryRc::clone_strong(&primary);
(Self(primary), AddressId(strong))
}
pub fn clone_strong(&self) -> AddressId<S> {
let Self(inner) = self;
AddressId(PrimaryRc::clone_strong(inner))
}
pub fn ptr_eq(&self, other: &AddressId<S>) -> bool {
let Self(inner) = self;
let AddressId(other) = other;
PrimaryRc::ptr_eq(inner, other)
}
pub fn into_inner(self) -> PrimaryRc<S> {
self.0
}
}
impl<S: AssignedAddressState> AssignedAddressState for PrimaryAddressId<S> {
type Address = S::Address;
fn addr(&self) -> IpDeviceAddr<S::Address> {
let Self(inner) = self;
inner.addr()
}
fn addr_sub(
&self,
) -> AddrSubnet<
S::Address,
<<S::Address as IpAddress>::Version as AssignedAddrIpExt>::AssignedWitness,
> {
let Self(inner) = self;
inner.addr_sub()
}
}
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Eq(bound = ""), Hash(bound = ""), PartialEq(bound = ""))]
pub struct AddressId<S>(StrongRc<S>);
impl<S: AssignedAddressState> Debug for AddressId<S> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let Self(rc) = self;
write!(f, "AddressId({:?} => {})", rc.debug_id(), self.addr_sub())
}
}
impl<S> Deref for AddressId<S> {
type Target = S;
fn deref(&self) -> &Self::Target {
let Self(inner) = self;
inner.deref()
}
}
impl<A, S> IpAddressId<A> for AddressId<S>
where
A: IpAddress<Version: AssignedAddrIpExt>,
S: AssignedAddressState<Address = A>,
{
type Weak = WeakAddressId<S>;
fn downgrade(&self) -> Self::Weak {
let Self(inner) = self;
WeakAddressId(StrongRc::downgrade(inner))
}
fn addr(&self) -> IpDeviceAddr<A> {
let Self(inner) = self;
inner.addr()
}
fn addr_sub(&self) -> AddrSubnet<A, <A::Version as AssignedAddrIpExt>::AssignedWitness> {
let Self(inner) = self;
inner.addr_sub()
}
}
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Eq(bound = ""), Hash(bound = ""), PartialEq(bound = ""))]
pub struct WeakAddressId<S>(WeakRc<S>);
impl<S: AssignedAddressState> Debug for WeakAddressId<S> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let Self(rc) = self;
if let Some(id) = self.upgrade() {
write!(f, "WeakAddressId({:?} => {})", rc.debug_id(), id.addr_sub())
} else {
write!(
f,
"WeakAddressId({:?} => {})",
rc.debug_id(),
<S::Address as IpAddress>::Version::NAME
)
}
}
}
impl<S: AssignedAddressState> InspectableValue for WeakAddressId<S> {
fn record<I: Inspector>(&self, name: &str, inspector: &mut I) {
inspector.record_debug(name, self);
}
}
impl<A, S> WeakIpAddressId<A> for WeakAddressId<S>
where
A: IpAddress<Version: AssignedAddrIpExt>,
S: AssignedAddressState<Address = A>,
{
type Strong = AddressId<S>;
fn upgrade(&self) -> Option<Self::Strong> {
let Self(inner) = self;
inner.upgrade().map(AddressId)
}
fn is_assigned(&self) -> bool {
let Self(inner) = self;
inner.strong_count() != 0
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct IpDeviceFlags {
pub ip_enabled: bool,
}
pub struct IpDeviceMulticastGroups<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> {
pub groups: MulticastGroupSet<I::Addr, GmpGroupState<I, BT>>,
pub gmp: GmpState<I, I::GmpTypeLayout<BT>, BT>,
pub gmp_config: I::GmpProtoConfig,
}
#[derive(Copy, Clone, Debug)]
pub struct DefaultHopLimit<I: Ip>(NonZeroU8, IpVersionMarker<I>);
impl<I: Ip> Deref for DefaultHopLimit<I> {
type Target = NonZeroU8;
fn deref(&self) -> &NonZeroU8 {
let Self(value, IpVersionMarker { .. }) = self;
value
}
}
impl<I: Ip> DerefMut for DefaultHopLimit<I> {
fn deref_mut(&mut self) -> &mut NonZeroU8 {
let Self(value, IpVersionMarker { .. }) = self;
value
}
}
impl<I: Ip> Default for DefaultHopLimit<I> {
fn default() -> Self {
Self(DEFAULT_HOP_LIMIT, IpVersionMarker::new())
}
}
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
pub struct IpDeviceState<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> {
addrs: RwLock<IpDeviceAddresses<I, BT>>,
multicast_groups: RwLock<IpDeviceMulticastGroups<I, BT>>,
default_hop_limit: RwLock<DefaultHopLimit<I>>,
flags: Mutex<IpMarked<I, IpDeviceFlags>>,
}
impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>
OrderedLockAccess<IpDeviceAddresses<I, BT>> for DualStackIpDeviceState<BT>
{
type Lock = RwLock<IpDeviceAddresses<I, BT>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.ip_state::<I>().addrs)
}
}
impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>
OrderedLockAccess<IpDeviceMulticastGroups<I, BT>> for DualStackIpDeviceState<BT>
{
type Lock = RwLock<IpDeviceMulticastGroups<I, BT>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.ip_state::<I>().multicast_groups)
}
}
impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> OrderedLockAccess<DefaultHopLimit<I>>
for DualStackIpDeviceState<BT>
{
type Lock = RwLock<DefaultHopLimit<I>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.ip_state::<I>().default_hop_limit)
}
}
impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>
OrderedLockAccess<IpMarked<I, IpDeviceFlags>> for DualStackIpDeviceState<BT>
{
type Lock = Mutex<IpMarked<I, IpDeviceFlags>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.ip_state::<I>().flags)
}
}
impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<SlaacState<BT>>
for DualStackIpDeviceState<BT>
{
type Lock = Mutex<SlaacState<BT>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.ipv6.slaac_state)
}
}
impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> IpDeviceState<I, BT> {
#[cfg(any(test, feature = "testutils"))]
pub fn addrs(&self) -> &RwLock<IpDeviceAddresses<I, BT>> {
&self.addrs
}
}
impl<I: IpDeviceStateIpExt, BC: IpDeviceStateBindingsTypes + TimerContext> IpDeviceState<I, BC> {
fn new<D: WeakDeviceIdentifier, CC: CoreTimerContext<I::GmpTimerId<D>, BC>>(
bindings_ctx: &mut BC,
device_id: D,
) -> IpDeviceState<I, BC> {
IpDeviceState {
addrs: Default::default(),
multicast_groups: RwLock::new(IpDeviceMulticastGroups {
groups: Default::default(),
gmp: GmpState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(bindings_ctx, device_id),
gmp_config: Default::default(),
}),
default_hop_limit: Default::default(),
flags: Default::default(),
}
}
}
#[derive(Derivative)]
#[derivative(Default(bound = ""))]
#[cfg_attr(test, derive(Debug))]
pub struct IpDeviceAddresses<I: Ip + IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> {
addrs: Vec<PrimaryAddressId<I::AssignedAddressState<BT>>>,
}
impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> IpDeviceAddresses<I, BT> {
pub fn iter(
&self,
) -> impl ExactSizeIterator<Item = &PrimaryAddressId<I::AssignedAddressState<BT>>>
+ ExactSizeIterator
+ Clone {
self.addrs.iter()
}
pub fn strong_iter(&self) -> AddressIdIter<'_, I, BT> {
AddressIdIter(self.addrs.iter())
}
pub fn add(
&mut self,
addr: I::AssignedAddressState<BT>,
) -> Result<AddressId<I::AssignedAddressState<BT>>, ExistsError> {
if self.iter().any(|a| a.addr() == addr.addr()) {
return Err(ExistsError);
}
let (primary, strong) = PrimaryAddressId::new_with_strong_clone(addr);
self.addrs.push(primary);
Ok(strong)
}
pub fn remove(
&mut self,
addr: &I::Addr,
) -> Result<PrimaryAddressId<I::AssignedAddressState<BT>>, NotFoundError> {
let (index, _entry): (_, &PrimaryAddressId<I::AssignedAddressState<BT>>) = self
.addrs
.iter()
.enumerate()
.find(|(_, entry)| &entry.addr().addr() == addr)
.ok_or(NotFoundError)?;
Ok(self.addrs.remove(index))
}
}
pub struct AddressIdIter<'a, I: Ip + IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes>(
core::slice::Iter<'a, PrimaryAddressId<I::AssignedAddressState<BT>>>,
);
impl<'a, I: Ip + IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Iterator
for AddressIdIter<'a, I, BT>
{
type Item = AddressId<I::AssignedAddressState<BT>>;
fn next(&mut self) -> Option<Self::Item> {
let Self(inner) = self;
inner.next().map(|addr| addr.clone_strong())
}
}
pub struct Ipv4DeviceState<BT: IpDeviceStateBindingsTypes> {
ip_state: IpDeviceState<Ipv4, BT>,
config: RwLock<Ipv4DeviceConfiguration>,
}
impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv4DeviceConfiguration>
for DualStackIpDeviceState<BT>
{
type Lock = RwLock<Ipv4DeviceConfiguration>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.ipv4.config)
}
}
impl<BC: IpDeviceStateBindingsTypes + TimerContext> Ipv4DeviceState<BC> {
fn new<D: WeakDeviceIdentifier, CC: CoreTimerContext<Ipv4DeviceTimerId<D>, BC>>(
bindings_ctx: &mut BC,
device_id: D,
) -> Ipv4DeviceState<BC> {
Ipv4DeviceState {
ip_state: IpDeviceState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(
bindings_ctx,
device_id,
),
config: Default::default(),
}
}
}
impl<BT: IpDeviceStateBindingsTypes> AsRef<IpDeviceState<Ipv4, BT>> for Ipv4DeviceState<BT> {
fn as_ref(&self) -> &IpDeviceState<Ipv4, BT> {
&self.ip_state
}
}
impl<BT: IpDeviceStateBindingsTypes> AsMut<IpDeviceState<Ipv4, BT>> for Ipv4DeviceState<BT> {
fn as_mut(&mut self) -> &mut IpDeviceState<Ipv4, BT> {
&mut self.ip_state
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct IpDeviceConfiguration {
pub gmp_enabled: bool,
pub unicast_forwarding_enabled: bool,
pub multicast_forwarding_enabled: bool,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Ipv4DeviceConfiguration {
pub ip_config: IpDeviceConfiguration,
}
impl AsRef<IpDeviceConfiguration> for Ipv4DeviceConfiguration {
fn as_ref(&self) -> &IpDeviceConfiguration {
&self.ip_config
}
}
impl AsMut<IpDeviceConfiguration> for Ipv4DeviceConfiguration {
fn as_mut(&mut self) -> &mut IpDeviceConfiguration {
&mut self.ip_config
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Ipv6DeviceConfiguration {
pub dad_transmits: Option<NonZeroU16>,
pub max_router_solicitations: Option<NonZeroU8>,
pub slaac_config: SlaacConfiguration,
pub ip_config: IpDeviceConfiguration,
}
impl Ipv6DeviceConfiguration {
pub const DEFAULT_MAX_RTR_SOLICITATIONS: NonZeroU8 = NonZeroU8::new(3).unwrap();
pub const DEFAULT_DUPLICATE_ADDRESS_DETECTION_TRANSMITS: NonZeroU16 =
NonZeroU16::new(1).unwrap();
}
impl AsRef<IpDeviceConfiguration> for Ipv6DeviceConfiguration {
fn as_ref(&self) -> &IpDeviceConfiguration {
&self.ip_config
}
}
impl AsMut<IpDeviceConfiguration> for Ipv6DeviceConfiguration {
fn as_mut(&mut self) -> &mut IpDeviceConfiguration {
&mut self.ip_config
}
}
impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv6NetworkLearnedParameters>
for DualStackIpDeviceState<BT>
{
type Lock = RwLock<Ipv6NetworkLearnedParameters>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.ipv6.learned_params)
}
}
impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv6RouteDiscoveryState<BT>>
for DualStackIpDeviceState<BT>
{
type Lock = Mutex<Ipv6RouteDiscoveryState<BT>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.ipv6.route_discovery)
}
}
impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<RsState<BT>> for DualStackIpDeviceState<BT> {
type Lock = Mutex<RsState<BT>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.ipv6.router_solicitations)
}
}
impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv6DeviceConfiguration>
for DualStackIpDeviceState<BT>
{
type Lock = RwLock<Ipv6DeviceConfiguration>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.ipv6.config)
}
}
#[derive(Default)]
pub struct Ipv6NetworkLearnedParameters {
pub retrans_timer: Option<NonZeroDuration>,
}
impl Ipv6NetworkLearnedParameters {
pub fn retrans_timer_or_default(&self) -> NonZeroDuration {
self.retrans_timer.clone().unwrap_or(RETRANS_TIMER_DEFAULT)
}
}
pub struct Ipv6DeviceState<BT: IpDeviceStateBindingsTypes> {
learned_params: RwLock<Ipv6NetworkLearnedParameters>,
route_discovery: Mutex<Ipv6RouteDiscoveryState<BT>>,
router_solicitations: Mutex<RsState<BT>>,
ip_state: IpDeviceState<Ipv6, BT>,
config: RwLock<Ipv6DeviceConfiguration>,
slaac_state: Mutex<SlaacState<BT>>,
}
impl<BC: IpDeviceStateBindingsTypes + TimerContext> Ipv6DeviceState<BC> {
pub fn new<
D: WeakDeviceIdentifier,
A: WeakIpAddressId<Ipv6Addr>,
CC: CoreTimerContext<Ipv6DeviceTimerId<D, A>, BC>,
>(
bindings_ctx: &mut BC,
device_id: D,
) -> Self {
Ipv6DeviceState {
learned_params: Default::default(),
route_discovery: Mutex::new(Ipv6RouteDiscoveryState::new::<
_,
NestedIntoCoreTimerCtx<CC, _>,
>(bindings_ctx, device_id.clone())),
router_solicitations: Default::default(),
ip_state: IpDeviceState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(
bindings_ctx,
device_id.clone(),
),
config: Default::default(),
slaac_state: Mutex::new(SlaacState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(
bindings_ctx,
device_id,
)),
}
}
}
impl<BT: IpDeviceStateBindingsTypes> AsRef<IpDeviceState<Ipv6, BT>> for Ipv6DeviceState<BT> {
fn as_ref(&self) -> &IpDeviceState<Ipv6, BT> {
&self.ip_state
}
}
impl<BT: IpDeviceStateBindingsTypes> AsMut<IpDeviceState<Ipv6, BT>> for Ipv6DeviceState<BT> {
fn as_mut(&mut self) -> &mut IpDeviceState<Ipv6, BT> {
&mut self.ip_state
}
}
pub trait IpDeviceStateBindingsTypes:
InstantBindingsTypes + TimerBindingsTypes + ReferenceNotifiers + 'static
{
}
impl<BT> IpDeviceStateBindingsTypes for BT where
BT: InstantBindingsTypes + TimerBindingsTypes + ReferenceNotifiers + 'static
{
}
pub struct DualStackIpDeviceState<BT: IpDeviceStateBindingsTypes> {
ipv4: Ipv4DeviceState<BT>,
ipv6: Ipv6DeviceState<BT>,
metric: RawMetric,
}
impl<BC: IpDeviceStateBindingsTypes + TimerContext> DualStackIpDeviceState<BC> {
pub fn new<
D: WeakDeviceIdentifier,
A: IpAddressIdSpec,
CC: CoreTimerContext<IpDeviceTimerId<Ipv6, D, A>, BC>
+ CoreTimerContext<IpDeviceTimerId<Ipv4, D, A>, BC>,
>(
bindings_ctx: &mut BC,
device_id: D,
metric: RawMetric,
) -> Self {
Self {
ipv4: Ipv4DeviceState::new::<D, NestedIntoCoreTimerCtx<CC, IpDeviceTimerId<Ipv4, D, A>>>(
bindings_ctx,
device_id.clone(),
),
ipv6: Ipv6DeviceState::new::<
D,
A::WeakV6,
NestedIntoCoreTimerCtx<CC, IpDeviceTimerId<Ipv6, D, A>>,
>(bindings_ctx, device_id),
metric,
}
}
}
impl<BT: IpDeviceStateBindingsTypes> DualStackIpDeviceState<BT> {
pub fn metric(&self) -> &RawMetric {
&self.metric
}
pub fn ip_state<I: IpDeviceStateIpExt>(&self) -> &IpDeviceState<I, BT> {
I::map_ip_out(
self,
|dual_stack| &dual_stack.ipv4.ip_state,
|dual_stack| &dual_stack.ipv6.ip_state,
)
}
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub enum Ipv6DadState<BT: DadBindingsTypes> {
Assigned,
#[allow(missing_docs)]
Tentative {
dad_transmits_remaining: Option<NonZeroU16>,
timer: BT::Timer,
nonces: NonceCollection,
added_extra_transmits_after_detecting_looped_back_ns: bool,
},
Uninitialized,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TemporarySlaacConfig<Instant> {
pub valid_until: Instant,
pub desync_factor: Duration,
pub creation_time: Instant,
pub dad_counter: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Lifetime<I> {
Finite(I),
Infinite,
}
impl<I: Instant> InspectableValue for Lifetime<I> {
fn record<N: Inspector>(&self, name: &str, inspector: &mut N) {
match self {
Self::Finite(instant) => inspector.record_inspectable_value(name, instant),
Self::Infinite => inspector.record_str(name, "infinite"),
}
}
}
impl<I: Instant> Lifetime<I> {
pub fn from_ndp(now: I, duration: NonZeroNdpLifetime) -> Self {
match duration {
NonZeroNdpLifetime::Finite(d) => Self::after(now, d.get()),
NonZeroNdpLifetime::Infinite => Self::Infinite,
}
}
pub fn after(now: I, duration: Duration) -> Self {
match now.checked_add(duration) {
Some(i) => Self::Finite(i),
None => Self::Infinite,
}
}
}
impl<Instant> Lifetime<Instant> {
pub fn map_instant<N, F: FnOnce(Instant) -> N>(self, f: F) -> Lifetime<N> {
match self {
Self::Infinite => Lifetime::Infinite,
Self::Finite(i) => Lifetime::Finite(f(i)),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PreferredLifetime<Instant> {
Preferred(Lifetime<Instant>),
Deprecated,
}
impl<Instant> PreferredLifetime<Instant> {
pub const fn preferred_until(instant: Instant) -> Self {
Self::Preferred(Lifetime::Finite(instant))
}
pub const fn preferred_forever() -> Self {
Self::Preferred(Lifetime::Infinite)
}
pub const fn is_deprecated(&self) -> bool {
match self {
Self::Preferred(_) => false,
Self::Deprecated => true,
}
}
pub fn map_instant<N, F: FnOnce(Instant) -> N>(self, f: F) -> PreferredLifetime<N> {
match self {
Self::Deprecated => PreferredLifetime::Deprecated,
Self::Preferred(l) => PreferredLifetime::Preferred(l.map_instant(f)),
}
}
}
impl<I: Instant> PreferredLifetime<I> {
pub fn preferred_for(now: I, duration: NonZeroNdpLifetime) -> Self {
Self::Preferred(Lifetime::from_ndp(now, duration))
}
pub fn maybe_preferred_for(now: I, duration: Option<NonZeroNdpLifetime>) -> Self {
match duration {
Some(d) => Self::preferred_for(now, d),
None => Self::Deprecated,
}
}
}
impl<Instant> Default for PreferredLifetime<Instant> {
fn default() -> Self {
Self::Preferred(Lifetime::Infinite)
}
}
impl<I: Instant> InspectableValue for PreferredLifetime<I> {
fn record<N: Inspector>(&self, name: &str, inspector: &mut N) {
match self {
Self::Deprecated => inspector.record_str(name, "deprecated"),
Self::Preferred(lifetime) => inspector.record_inspectable_value(name, lifetime),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct CommonAddressProperties<Instant> {
pub valid_until: Lifetime<Instant>,
pub preferred_lifetime: PreferredLifetime<Instant>,
}
impl<I> Default for CommonAddressProperties<I> {
fn default() -> Self {
Self {
valid_until: Lifetime::Infinite,
preferred_lifetime: PreferredLifetime::preferred_forever(),
}
}
}
impl<Inst: Instant> Inspectable for CommonAddressProperties<Inst> {
fn record<I: Inspector>(&self, inspector: &mut I) {
let Self { valid_until, preferred_lifetime } = self;
inspector.record_inspectable_value("ValidUntil", valid_until);
inspector.record_inspectable_value("PreferredLifetime", preferred_lifetime);
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Ipv4AddrConfig<Instant> {
pub common: CommonAddressProperties<Instant>,
}
impl<I> Default for Ipv4AddrConfig<I> {
fn default() -> Self {
Self { common: Default::default() }
}
}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Ipv4AddressEntry<BT: IpDeviceStateBindingsTypes> {
addr_sub: AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>,
state: RwLock<Ipv4AddressState<BT::Instant>>,
}
impl<BT: IpDeviceStateBindingsTypes> Ipv4AddressEntry<BT> {
pub fn new(
addr_sub: AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>,
config: Ipv4AddrConfig<BT::Instant>,
) -> Self {
Self { addr_sub, state: RwLock::new(Ipv4AddressState { config: Some(config) }) }
}
pub fn addr_sub(&self) -> &AddrSubnet<Ipv4Addr, Ipv4DeviceAddr> {
&self.addr_sub
}
}
impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv4AddressState<BT::Instant>>
for Ipv4AddressEntry<BT>
{
type Lock = RwLock<Ipv4AddressState<BT::Instant>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.state)
}
}
#[derive(Debug)]
pub struct Ipv4AddressState<Instant> {
pub(crate) config: Option<Ipv4AddrConfig<Instant>>,
}
impl<Inst: Instant> Inspectable for Ipv4AddressState<Inst> {
fn record<I: Inspector>(&self, inspector: &mut I) {
let Self { config } = self;
if let Some(Ipv4AddrConfig { common }) = config {
inspector.delegate_inspectable(common);
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SlaacConfig<Instant> {
Static {
valid_until: Lifetime<Instant>,
},
Temporary(TemporarySlaacConfig<Instant>),
}
impl<Instant: Copy> SlaacConfig<Instant> {
pub fn valid_until(&self) -> Lifetime<Instant> {
match self {
SlaacConfig::Static { valid_until } => *valid_until,
SlaacConfig::Temporary(TemporarySlaacConfig {
valid_until,
desync_factor: _,
creation_time: _,
dad_counter: _,
}) => Lifetime::Finite(*valid_until),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Ipv6AddrConfig<Instant> {
Slaac(Ipv6AddrSlaacConfig<Instant>),
Manual(Ipv6AddrManualConfig<Instant>),
}
impl<Instant> Default for Ipv6AddrConfig<Instant> {
fn default() -> Self {
Self::Manual(Default::default())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Ipv6AddrSlaacConfig<Instant> {
pub inner: SlaacConfig<Instant>,
pub preferred_lifetime: PreferredLifetime<Instant>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Ipv6AddrManualConfig<Instant> {
pub common: CommonAddressProperties<Instant>,
pub temporary: bool,
}
impl<Instant> Default for Ipv6AddrManualConfig<Instant> {
fn default() -> Self {
Self { common: Default::default(), temporary: false }
}
}
impl<Instant> From<Ipv6AddrManualConfig<Instant>> for Ipv6AddrConfig<Instant> {
fn from(value: Ipv6AddrManualConfig<Instant>) -> Self {
Self::Manual(value)
}
}
impl<Instant: Copy> Ipv6AddrConfig<Instant> {
pub(crate) const SLAAC_LINK_LOCAL: Self = Self::Slaac(Ipv6AddrSlaacConfig {
inner: SlaacConfig::Static { valid_until: Lifetime::Infinite },
preferred_lifetime: PreferredLifetime::Preferred(Lifetime::Infinite),
});
pub fn valid_until(&self) -> Lifetime<Instant> {
match self {
Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { inner, .. }) => inner.valid_until(),
Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { common, .. }) => common.valid_until,
}
}
pub fn preferred_lifetime(&self) -> PreferredLifetime<Instant> {
match self {
Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { common, .. }) => {
common.preferred_lifetime
}
Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { preferred_lifetime, .. }) => {
*preferred_lifetime
}
}
}
pub fn is_deprecated(&self) -> bool {
self.preferred_lifetime().is_deprecated()
}
pub fn is_temporary(&self) -> bool {
match self {
Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { inner, .. }) => match inner {
SlaacConfig::Static { .. } => false,
SlaacConfig::Temporary(_) => true,
},
Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { temporary, .. }) => *temporary,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Ipv6AddressFlags {
pub assigned: bool,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Ipv6AddressState<Instant> {
pub flags: Ipv6AddressFlags,
pub config: Option<Ipv6AddrConfig<Instant>>,
}
impl<Inst: Instant> Inspectable for Ipv6AddressState<Inst> {
fn record<I: Inspector>(&self, inspector: &mut I) {
let Self { flags: Ipv6AddressFlags { assigned }, config } = self;
inspector.record_bool("Assigned", *assigned);
if let Some(config) = config {
let is_slaac = match config {
Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { .. }) => false,
Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig { inner, preferred_lifetime: _ }) => {
match inner {
SlaacConfig::Static { valid_until: _ } => {}
SlaacConfig::Temporary(TemporarySlaacConfig {
valid_until: _,
desync_factor,
creation_time,
dad_counter,
}) => {
inspector
.record_double("DesyncFactorSecs", desync_factor.as_secs_f64());
inspector.record_uint("DadCounter", *dad_counter);
inspector.record_inspectable_value("CreationTime", creation_time);
}
}
true
}
};
inspector.record_bool("IsSlaac", is_slaac);
inspector.record_inspectable_value("ValidUntil", &config.valid_until());
inspector.record_inspectable_value("PreferredLifetime", &config.preferred_lifetime());
inspector.record_bool("Temporary", config.is_temporary());
}
}
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub struct Ipv6AddressEntry<BT: IpDeviceStateBindingsTypes> {
addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
dad_state: Mutex<Ipv6DadState<BT>>,
state: RwLock<Ipv6AddressState<BT::Instant>>,
}
impl<BT: IpDeviceStateBindingsTypes> Ipv6AddressEntry<BT> {
pub fn new(
addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
dad_state: Ipv6DadState<BT>,
config: Ipv6AddrConfig<BT::Instant>,
) -> Self {
let assigned = match dad_state {
Ipv6DadState::Assigned => true,
Ipv6DadState::Tentative { .. } | Ipv6DadState::Uninitialized => false,
};
Self {
addr_sub,
dad_state: Mutex::new(dad_state),
state: RwLock::new(Ipv6AddressState {
config: Some(config),
flags: Ipv6AddressFlags { assigned },
}),
}
}
pub fn addr_sub(&self) -> &AddrSubnet<Ipv6Addr, Ipv6DeviceAddr> {
&self.addr_sub
}
}
impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv6DadState<BT>> for Ipv6AddressEntry<BT> {
type Lock = Mutex<Ipv6DadState<BT>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.dad_state)
}
}
impl<BT: IpDeviceStateBindingsTypes> OrderedLockAccess<Ipv6AddressState<BT::Instant>>
for Ipv6AddressEntry<BT>
{
type Lock = RwLock<Ipv6AddressState<BT::Instant>>;
fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
OrderedLockRef::new(&self.state)
}
}
#[cfg(test)]
mod tests {
use super::*;
use netstack3_base::testutil::{FakeBindingsCtx, FakeInstant};
use test_case::test_case;
type FakeBindingsCtxImpl = FakeBindingsCtx<(), (), (), ()>;
#[test_case(Lifetime::Infinite ; "with infinite valid_until")]
#[test_case(Lifetime::Finite(FakeInstant::from(Duration::from_secs(1))); "with finite valid_until")]
fn test_add_addr_ipv4(valid_until: Lifetime<FakeInstant>) {
const ADDRESS: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
const PREFIX_LEN: u8 = 8;
let mut ipv4 = IpDeviceAddresses::<Ipv4, FakeBindingsCtxImpl>::default();
let config = Ipv4AddrConfig {
common: CommonAddressProperties { valid_until, ..Default::default() },
};
let _: AddressId<_> = ipv4
.add(Ipv4AddressEntry::new(AddrSubnet::new(ADDRESS, PREFIX_LEN).unwrap(), config))
.unwrap();
assert_eq!(
ipv4.add(Ipv4AddressEntry::new(
AddrSubnet::new(ADDRESS, PREFIX_LEN + 1).unwrap(),
config,
))
.unwrap_err(),
ExistsError
);
}
#[test_case(Lifetime::Infinite ; "with infinite valid_until")]
#[test_case(Lifetime::Finite(FakeInstant::from(Duration::from_secs(1))); "with finite valid_until")]
fn test_add_addr_ipv6(valid_until: Lifetime<FakeInstant>) {
const ADDRESS: Ipv6Addr =
Ipv6Addr::from_bytes([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]);
const PREFIX_LEN: u8 = 8;
let mut ipv6 = IpDeviceAddresses::<Ipv6, FakeBindingsCtxImpl>::default();
let mut bindings_ctx = FakeBindingsCtxImpl::default();
let _: AddressId<_> = ipv6
.add(Ipv6AddressEntry::new(
AddrSubnet::new(ADDRESS, PREFIX_LEN).unwrap(),
Ipv6DadState::Tentative {
dad_transmits_remaining: None,
timer: bindings_ctx.new_timer(()),
nonces: Default::default(),
added_extra_transmits_after_detecting_looped_back_ns: false,
},
Ipv6AddrConfig::Slaac(Ipv6AddrSlaacConfig {
inner: SlaacConfig::Static { valid_until },
preferred_lifetime: PreferredLifetime::Preferred(Lifetime::Infinite),
}),
))
.unwrap();
assert_eq!(
ipv6.add(Ipv6AddressEntry::new(
AddrSubnet::new(ADDRESS, PREFIX_LEN + 1).unwrap(),
Ipv6DadState::Assigned,
Ipv6AddrConfig::Manual(Ipv6AddrManualConfig {
common: CommonAddressProperties { valid_until, ..Default::default() },
..Default::default()
}),
))
.unwrap_err(),
ExistsError,
);
}
}