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 const_unwrap::const_unwrap_option;
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::{
BroadcastIpExt, CoreTimerContext, ExistsError, Inspectable, InspectableValue, Inspector,
Instant, InstantBindingsTypes, NestedIntoCoreTimerCtx, NotFoundError, ReferenceNotifiers,
TimerBindingsTypes, TimerContext, WeakDeviceIdentifier,
};
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::{
IpAddressId, IpAddressIdSpec, IpDeviceAddr, IpDeviceTimerId, Ipv4DeviceAddr, Ipv4DeviceTimerId,
Ipv6DeviceAddr, Ipv6DeviceTimerId, WeakIpAddressId,
};
use crate::internal::gmp::igmp::{IgmpGroupState, IgmpState, IgmpTimerId};
use crate::internal::gmp::mld::{MldGroupState, MldTimerId};
use crate::internal::gmp::{GmpDelayedReportTimerId, GmpState, MulticastGroupSet};
use crate::internal::types::RawMetric;
use super::dad::NonceCollection;
pub const RETRANS_TIMER_DEFAULT: NonZeroDuration =
const_unwrap_option(NonZeroDuration::from_secs(1));
const DEFAULT_HOP_LIMIT: NonZeroU8 = const_unwrap_option(NonZeroU8::new(64));
pub trait IpDeviceStateIpExt: BroadcastIpExt {
type AssignedAddress<BT: IpDeviceStateBindingsTypes>: AssignedAddress<Self::Addr> + Debug;
type GmpGroupState<I: Instant>;
type GmpProtoState<BT: IpDeviceStateBindingsTypes>;
type GmpTimerId<D: WeakDeviceIdentifier>: From<GmpDelayedReportTimerId<Self, D>>;
fn new_gmp_state<
D: WeakDeviceIdentifier,
CC: CoreTimerContext<Self::GmpTimerId<D>, BC>,
BC: IpDeviceStateBindingsTypes + TimerContext,
>(
bindings_ctx: &mut BC,
device_id: D,
) -> Self::GmpProtoState<BC>;
}
impl IpDeviceStateIpExt for Ipv4 {
type AssignedAddress<BT: IpDeviceStateBindingsTypes> = Ipv4AddressEntry<BT>;
type GmpProtoState<BT: IpDeviceStateBindingsTypes> = IgmpState<BT>;
type GmpGroupState<I: Instant> = IgmpGroupState<I>;
type GmpTimerId<D: WeakDeviceIdentifier> = IgmpTimerId<D>;
fn new_gmp_state<
D: WeakDeviceIdentifier,
CC: CoreTimerContext<Self::GmpTimerId<D>, BC>,
BC: IpDeviceStateBindingsTypes + TimerContext,
>(
bindings_ctx: &mut BC,
device_id: D,
) -> Self::GmpProtoState<BC> {
IgmpState::new::<_, CC>(bindings_ctx, device_id)
}
}
impl<BT: IpDeviceStateBindingsTypes> IpAddressId<Ipv4Addr> for StrongRc<Ipv4AddressEntry<BT>> {
type Weak = WeakRc<Ipv4AddressEntry<BT>>;
fn downgrade(&self) -> Self::Weak {
StrongRc::downgrade(self)
}
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> WeakIpAddressId<Ipv4Addr> for WeakRc<Ipv4AddressEntry<BT>> {
type Strong = StrongRc<Ipv4AddressEntry<BT>>;
fn upgrade(&self) -> Option<Self::Strong> {
self.upgrade()
}
}
impl<BT: IpDeviceStateBindingsTypes> IpAddressId<Ipv6Addr> for StrongRc<Ipv6AddressEntry<BT>> {
type Weak = WeakRc<Ipv6AddressEntry<BT>>;
fn downgrade(&self) -> Self::Weak {
StrongRc::downgrade(self)
}
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
}
}
impl<BT: IpDeviceStateBindingsTypes> WeakIpAddressId<Ipv6Addr> for WeakRc<Ipv6AddressEntry<BT>> {
type Strong = StrongRc<Ipv6AddressEntry<BT>>;
fn upgrade(&self) -> Option<Self::Strong> {
self.upgrade()
}
}
impl IpDeviceStateIpExt for Ipv6 {
type AssignedAddress<BT: IpDeviceStateBindingsTypes> = Ipv6AddressEntry<BT>;
type GmpProtoState<BT: IpDeviceStateBindingsTypes> = ();
type GmpGroupState<I: Instant> = MldGroupState<I>;
type GmpTimerId<D: WeakDeviceIdentifier> = MldTimerId<D>;
fn new_gmp_state<
D: WeakDeviceIdentifier,
CC: CoreTimerContext<Self::GmpTimerId<D>, BC>,
BC: IpDeviceStateBindingsTypes + TimerContext,
>(
_bindings_ctx: &mut BC,
_device_id: D,
) -> Self::GmpProtoState<BC> {
()
}
}
pub trait AssignedAddress<A: IpAddress> {
fn addr(&self) -> IpDeviceAddr<A>;
}
impl<BT: IpDeviceStateBindingsTypes> AssignedAddress<Ipv4Addr> for Ipv4AddressEntry<BT> {
fn addr(&self) -> IpDeviceAddr<Ipv4Addr> {
IpDeviceAddr::new_from_witness(self.addr_sub().addr())
}
}
impl<BT: IpDeviceStateBindingsTypes> AssignedAddress<Ipv6Addr> for Ipv6AddressEntry<BT> {
fn addr(&self) -> IpDeviceAddr<Ipv6Addr> {
IpDeviceAddr::new_from_ipv6_device_addr(self.addr_sub().addr())
}
}
#[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, I::GmpGroupState<BT::Instant>>,
pub gmp_proto: I::GmpProtoState<BT>,
pub gmp: GmpState<I, BT>,
}
#[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_proto: I::new_gmp_state::<_, CC, _>(bindings_ctx, device_id.clone()),
gmp: GmpState::new::<_, NestedIntoCoreTimerCtx<CC, _>>(bindings_ctx, device_id),
}),
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<PrimaryRc<I::AssignedAddress<BT>>>,
}
impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> IpDeviceAddresses<I, BT> {
pub fn iter(
&self,
) -> impl ExactSizeIterator<Item = &PrimaryRc<I::AssignedAddress<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::AssignedAddress<BT>,
) -> Result<StrongRc<I::AssignedAddress<BT>>, ExistsError> {
if self.iter().any(|a| a.addr() == addr.addr()) {
return Err(ExistsError);
}
let primary = PrimaryRc::new(addr);
let strong = PrimaryRc::clone_strong(&primary);
self.addrs.push(primary);
Ok(strong)
}
pub fn remove(
&mut self,
addr: &I::Addr,
) -> Result<PrimaryRc<I::AssignedAddress<BT>>, NotFoundError> {
let (index, _entry): (_, &PrimaryRc<I::AssignedAddress<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, PrimaryRc<I::AssignedAddress<BT>>>,
);
impl<'a, I: Ip + IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> Iterator
for AddressIdIter<'a, I, BT>
{
type Item = StrongRc<I::AssignedAddress<BT>>;
fn next(&mut self) -> Option<Self::Item> {
let Self(inner) = self;
inner.next().map(PrimaryRc::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 Ipv4DeviceConfigurationAndFlags {
pub config: Ipv4DeviceConfiguration,
pub flags: IpDeviceFlags,
}
impl AsRef<IpDeviceConfiguration> for Ipv4DeviceConfigurationAndFlags {
fn as_ref(&self) -> &IpDeviceConfiguration {
self.config.as_ref()
}
}
impl AsMut<IpDeviceConfiguration> for Ipv4DeviceConfigurationAndFlags {
fn as_mut(&mut self) -> &mut IpDeviceConfiguration {
self.config.as_mut()
}
}
impl AsRef<IpDeviceFlags> for Ipv4DeviceConfigurationAndFlags {
fn as_ref(&self) -> &IpDeviceFlags {
&self.flags
}
}
impl From<(Ipv4DeviceConfiguration, IpDeviceFlags)> for Ipv4DeviceConfigurationAndFlags {
fn from((config, flags): (Ipv4DeviceConfiguration, IpDeviceFlags)) -> Self {
Self { config, flags }
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Ipv6DeviceConfigurationAndFlags {
pub config: Ipv6DeviceConfiguration,
pub flags: IpDeviceFlags,
}
impl AsRef<IpDeviceConfiguration> for Ipv6DeviceConfigurationAndFlags {
fn as_ref(&self) -> &IpDeviceConfiguration {
self.config.as_ref()
}
}
impl AsMut<IpDeviceConfiguration> for Ipv6DeviceConfigurationAndFlags {
fn as_mut(&mut self) -> &mut IpDeviceConfiguration {
self.config.as_mut()
}
}
impl AsRef<IpDeviceFlags> for Ipv6DeviceConfigurationAndFlags {
fn as_ref(&self) -> &IpDeviceFlags {
&self.flags
}
}
impl From<(Ipv6DeviceConfiguration, IpDeviceFlags)> for Ipv6DeviceConfigurationAndFlags {
fn from((config, flags): (Ipv6DeviceConfiguration, IpDeviceFlags)) -> Self {
Self { config, flags }
}
}
#[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 = const_unwrap_option(NonZeroU8::new(3));
pub const DEFAULT_DUPLICATE_ADDRESS_DETECTION_TRANSMITS: NonZeroU16 =
const_unwrap_option(NonZeroU16::new(1));
}
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
{
}
impl<BT> IpDeviceStateBindingsTypes for BT where
BT: InstantBindingsTypes + TimerBindingsTypes + ReferenceNotifiers
{
}
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"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Ipv4AddrConfig<Instant> {
pub valid_until: Lifetime<Instant>,
}
impl<I> Default for Ipv4AddrConfig<I> {
fn default() -> Self {
Self { valid_until: Lifetime::Infinite }
}
}
#[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 { valid_until }) = config {
inspector.record_inspectable_value("ValidUntil", valid_until)
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SlaacConfig<Instant> {
Static {
valid_until: Lifetime<Instant>,
},
Temporary(TemporarySlaacConfig<Instant>),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Ipv6AddrConfig<Instant> {
Slaac(SlaacConfig<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 Ipv6AddrManualConfig<Instant> {
pub valid_until: Lifetime<Instant>,
}
impl<Instant> Default for Ipv6AddrManualConfig<Instant> {
fn default() -> Self {
Self { valid_until: Lifetime::Infinite }
}
}
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(SlaacConfig::Static { valid_until: Lifetime::Infinite });
pub fn valid_until(&self) -> Lifetime<Instant> {
match self {
Ipv6AddrConfig::Slaac(slaac_config) => match slaac_config {
SlaacConfig::Static { valid_until } => *valid_until,
SlaacConfig::Temporary(TemporarySlaacConfig {
valid_until,
desync_factor: _,
creation_time: _,
dad_counter: _,
}) => Lifetime::Finite(*valid_until),
},
Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { valid_until }) => *valid_until,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Ipv6AddressFlags {
pub deprecated: bool,
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 { deprecated, assigned }, config } = self;
inspector.record_bool("Deprecated", *deprecated);
inspector.record_bool("Assigned", *assigned);
if let Some(config) = config {
let (is_slaac, valid_until) = match config {
Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { valid_until }) => {
(false, *valid_until)
}
Ipv6AddrConfig::Slaac(SlaacConfig::Static { valid_until }) => (true, *valid_until),
Ipv6AddrConfig::Slaac(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, Lifetime::Finite(*valid_until))
}
};
inspector.record_bool("IsSlaac", is_slaac);
inspector.record_inspectable_value("ValidUntil", &valid_until);
}
}
}
#[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 { deprecated: false, 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 _: StrongRc<_> = ipv4
.add(Ipv4AddressEntry::new(
AddrSubnet::new(ADDRESS, PREFIX_LEN).unwrap(),
Ipv4AddrConfig { valid_until },
))
.unwrap();
assert_eq!(
ipv4.add(Ipv4AddressEntry::new(
AddrSubnet::new(ADDRESS, PREFIX_LEN + 1).unwrap(),
Ipv4AddrConfig { valid_until },
))
.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 _: StrongRc<_> = 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(SlaacConfig::Static { valid_until }),
))
.unwrap();
assert_eq!(
ipv6.add(Ipv6AddressEntry::new(
AddrSubnet::new(ADDRESS, PREFIX_LEN + 1).unwrap(),
Ipv6DadState::Assigned,
Ipv6AddrConfig::Manual(Ipv6AddrManualConfig { valid_until }),
))
.unwrap_err(),
ExistsError,
);
}
}