use alloc::vec::Vec;
use either::Either;
use log::trace;
use net_types::ip::{
AddrSubnet, AddrSubnetEither, GenericOverIp, Ip, IpAddr, IpAddress, IpVersionMarker, Ipv4,
Ipv4Addr, Ipv6, Ipv6Addr,
};
use net_types::{SpecifiedAddr, Witness as _};
use netstack3_base::{
AnyDevice, ContextPair, DeviceIdContext, DeviceIdentifier as _, EventContext as _, ExistsError,
Inspector, Instant, InstantBindingsTypes, IpAddressId as _, NotFoundError, ReferenceNotifiers,
RemoveResourceResult, RemoveResourceResultWithContext,
};
use thiserror::Error;
use crate::internal::device::config::{
IpDeviceConfigurationAndFlags, IpDeviceConfigurationHandler,
PendingIpDeviceConfigurationUpdate, UpdateIpConfigurationError,
};
use crate::internal::device::state::{
CommonAddressProperties, IpDeviceConfiguration, Ipv4AddrConfig, Ipv4AddressState,
Ipv6AddrConfig, Ipv6AddrManualConfig, Ipv6AddressState,
};
use crate::internal::device::{
self, AddressRemovedReason, DelIpAddr, IpDeviceAddressContext as _, IpDeviceBindingsContext,
IpDeviceConfigurationContext, IpDeviceEvent, IpDeviceIpExt, IpDeviceStateContext as _,
};
use crate::internal::gmp::{GmpHandler as _, GmpStateContext};
use crate::internal::routing::IpRoutingDeviceContext;
use crate::internal::types::RawMetric;
pub struct DeviceIpApi<I: Ip, C>(C, IpVersionMarker<I>);
impl<I: Ip, C> DeviceIpApi<I, C> {
pub fn new(ctx: C) -> Self {
Self(ctx, IpVersionMarker::new())
}
}
impl<I, C> DeviceIpApi<I, C>
where
I: IpDeviceIpExt,
C: ContextPair,
C::CoreContext: IpDeviceConfigurationContext<I, C::BindingsContext>
+ IpDeviceConfigurationHandler<I, C::BindingsContext>
+ IpRoutingDeviceContext<I>,
C::BindingsContext:
IpDeviceBindingsContext<I, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
{
fn core_ctx(&mut self) -> &mut C::CoreContext {
let Self(pair, IpVersionMarker { .. }) = self;
pair.core_ctx()
}
fn contexts(&mut self) -> (&mut C::CoreContext, &mut C::BindingsContext) {
let Self(pair, IpVersionMarker { .. }) = self;
pair.contexts()
}
pub fn add_ip_addr_subnet(
&mut self,
device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
addr_subnet: AddrSubnet<I::Addr>,
) -> Result<(), AddIpAddrSubnetError> {
self.add_ip_addr_subnet_with_config(device, addr_subnet, Default::default())
}
pub fn add_ip_addr_subnet_with_config(
&mut self,
device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
addr_subnet: AddrSubnet<I::Addr>,
addr_config: I::ManualAddressConfig<<C::BindingsContext as InstantBindingsTypes>::Instant>,
) -> Result<(), AddIpAddrSubnetError> {
let addr_subnet = addr_subnet
.replace_witness::<I::AssignedWitness>()
.ok_or(AddIpAddrSubnetError::InvalidAddr)?;
if !device.is_loopback() && I::LOOPBACK_SUBNET.contains(&addr_subnet.addr().get()) {
return Err(AddIpAddrSubnetError::InvalidAddr);
}
let (core_ctx, bindings_ctx) = self.contexts();
core_ctx.with_ip_device_configuration(device, |config, mut core_ctx| {
device::add_ip_addr_subnet_with_config(
&mut core_ctx,
bindings_ctx,
device,
addr_subnet,
addr_config.into(),
config,
)
.map(|_address_id| ())
.map_err(|ExistsError| AddIpAddrSubnetError::Exists)
})
}
pub fn del_ip_addr(
&mut self,
device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
addr: SpecifiedAddr<I::Addr>,
) -> Result<
RemoveResourceResultWithContext<AddrSubnet<I::Addr>, C::BindingsContext>,
NotFoundError,
> {
let (core_ctx, bindings_ctx) = self.contexts();
device::del_ip_addr(
core_ctx,
bindings_ctx,
device,
DelIpAddr::SpecifiedAddr(addr),
AddressRemovedReason::Manual,
)
}
pub fn new_configuration_update<'a>(
&mut self,
device_id: &'a <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
config: I::ConfigurationUpdate,
) -> Result<
PendingIpDeviceConfigurationUpdate<
'a,
I,
<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
>,
UpdateIpConfigurationError,
> {
PendingIpDeviceConfigurationUpdate::new(config, device_id)
}
pub fn apply_configuration(
&mut self,
config: PendingIpDeviceConfigurationUpdate<
'_,
I,
<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
>,
) -> I::ConfigurationUpdate {
let (core_ctx, bindings_ctx) = self.contexts();
IpDeviceConfigurationHandler::apply_configuration(core_ctx, bindings_ctx, config)
}
pub fn update_configuration(
&mut self,
device_id: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
config: I::ConfigurationUpdate,
) -> Result<I::ConfigurationUpdate, UpdateIpConfigurationError> {
let pending = self.new_configuration_update(device_id, config)?;
Ok(self.apply_configuration(pending))
}
pub fn get_configuration(
&mut self,
device_id: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
) -> IpDeviceConfigurationAndFlags<I> {
self.core_ctx().with_ip_device_configuration(device_id, |config, mut core_ctx| {
IpDeviceConfigurationAndFlags {
config: config.clone(),
flags: core_ctx.with_ip_device_flags(device_id, |flags| flags.clone()),
gmp_mode: core_ctx.gmp_get_mode(device_id),
}
})
}
pub fn get_routing_metric(
&mut self,
device_id: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
) -> RawMetric {
self.core_ctx().get_routing_metric(device_id)
}
pub fn set_addr_properties(
&mut self,
device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
address: SpecifiedAddr<I::Addr>,
next_properties: CommonAddressProperties<
<C::BindingsContext as InstantBindingsTypes>::Instant,
>,
) -> Result<(), SetIpAddressPropertiesError> {
trace!("set_ip_addr_properties: setting {:?} for addr={:?}", next_properties, address);
let (core_ctx, bindings_ctx) = self.contexts();
let address_id = core_ctx.get_address_id(device, address)?;
core_ctx.with_ip_address_state_mut(device, &address_id, |address_state| {
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct Wrap<'a, I: IpDeviceIpExt, II: Instant>(&'a mut I::AddressState<II>);
let CommonAddressProperties { valid_until, preferred_lifetime } = I::map_ip_in(
Wrap(address_state),
|Wrap(Ipv4AddressState { config })| {
match config {
Some(Ipv4AddrConfig { common }) => Ok(common),
None => Err(NotFoundError.into()),
}
},
|Wrap(Ipv6AddressState { flags: _, config })| {
match config {
None => Err(NotFoundError.into()),
Some(Ipv6AddrConfig::Slaac(_)) => {
Err(SetIpAddressPropertiesError::NotManual)
}
Some(Ipv6AddrConfig::Manual(Ipv6AddrManualConfig {
common,
temporary: _,
})) => Ok(common),
}
},
)?;
let CommonAddressProperties {
valid_until: next_valid_until,
preferred_lifetime: next_preferred_lifetime,
} = next_properties;
let mut changed = core::mem::replace(valid_until, next_valid_until) != next_valid_until;
changed |= core::mem::replace(preferred_lifetime, next_preferred_lifetime)
!= next_preferred_lifetime;
if changed {
bindings_ctx.on_event(IpDeviceEvent::AddressPropertiesChanged {
device: device.clone(),
addr: address,
valid_until: next_valid_until,
preferred_lifetime: next_preferred_lifetime,
});
}
Ok(())
})
}
pub fn for_each_assigned_ip_addr_subnet<F: FnMut(AddrSubnet<I::Addr>)>(
&mut self,
device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
f: F,
) {
self.core_ctx().with_address_ids(device, |addrs, core_ctx| {
addrs
.filter_map(|addr| {
let assigned = core_ctx.with_ip_address_state(device, &addr, |addr_state| {
I::is_addr_assigned(addr_state)
});
assigned.then(|| addr.addr_sub().to_witness())
})
.for_each(f);
})
}
pub fn get_assigned_ip_addr_subnets(
&mut self,
device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
) -> Vec<AddrSubnet<I::Addr>> {
let mut vec = Vec::new();
self.for_each_assigned_ip_addr_subnet(device, |a| vec.push(a));
vec
}
pub fn inspect<N: Inspector>(
&mut self,
device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
inspector: &mut N,
) where
C::CoreContext: GmpStateContext<I, C::BindingsContext>,
{
inspector.record_child("Addresses", |inspector| {
self.core_ctx().with_address_ids(device, |addrs, core_ctx| {
for addr in addrs {
inspector.record_display_child(addr.addr_sub(), |inspector| {
core_ctx.with_ip_address_state(device, &addr, |addr_state| {
inspector.delegate_inspectable(addr_state)
})
});
}
})
});
inspector.record_child("Configuration", |inspector| {
self.core_ctx().with_ip_device_configuration(device, |config, _core_ctx| {
let IpDeviceConfiguration {
gmp_enabled,
unicast_forwarding_enabled,
multicast_forwarding_enabled,
} = config.as_ref();
inspector.record_bool("GmpEnabled", *gmp_enabled);
inspector.record_bool("ForwardingEnabled", *unicast_forwarding_enabled);
inspector.record_bool("MulticastForwardingEnabled", *multicast_forwarding_enabled);
})
});
inspector.record_child("GMP", |inspector| {
self.core_ctx().with_gmp_state(device, |groups, gmp_state| {
inspector.record_inspectable_value("Mode", gmp_state.mode());
inspector.record_inspectable_value("Groups", groups);
})
})
}
}
pub struct DeviceIpAnyApi<C>(C);
impl<C> DeviceIpAnyApi<C> {
pub fn new(ctx: C) -> Self {
Self(ctx)
}
}
impl<C> DeviceIpAnyApi<C>
where
C: ContextPair,
C::CoreContext: IpDeviceConfigurationContext<Ipv4, C::BindingsContext>
+ IpDeviceConfigurationHandler<Ipv4, C::BindingsContext>
+ IpRoutingDeviceContext<Ipv4>
+ IpDeviceConfigurationContext<Ipv6, C::BindingsContext>
+ IpDeviceConfigurationHandler<Ipv6, C::BindingsContext>
+ IpRoutingDeviceContext<Ipv6>,
C::BindingsContext: IpDeviceBindingsContext<Ipv4, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>
+ IpDeviceBindingsContext<Ipv6, <C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId>,
{
fn ip<I: Ip>(&mut self) -> DeviceIpApi<I, &mut C> {
let Self(pair) = self;
DeviceIpApi::new(pair)
}
pub fn add_ip_addr_subnet(
&mut self,
device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
addr_sub_and_config: impl Into<
AddrSubnetAndManualConfigEither<<C::BindingsContext as InstantBindingsTypes>::Instant>,
>,
) -> Result<(), AddIpAddrSubnetError> {
match addr_sub_and_config.into() {
AddrSubnetAndManualConfigEither::V4(addr_sub, config) => {
self.ip::<Ipv4>().add_ip_addr_subnet_with_config(device, addr_sub, config)
}
AddrSubnetAndManualConfigEither::V6(addr_sub, config) => {
self.ip::<Ipv6>().add_ip_addr_subnet_with_config(device, addr_sub, config)
}
}
}
pub fn del_ip_addr(
&mut self,
device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
addr: impl Into<SpecifiedAddr<IpAddr>>,
) -> Result<
RemoveResourceResult<
AddrSubnetEither,
Either<
<C::BindingsContext as ReferenceNotifiers>::ReferenceReceiver<AddrSubnet<Ipv4Addr>>,
<C::BindingsContext as ReferenceNotifiers>::ReferenceReceiver<AddrSubnet<Ipv6Addr>>,
>,
>,
NotFoundError,
> {
let addr = addr.into();
match addr.into() {
IpAddr::V4(addr) => self
.ip::<Ipv4>()
.del_ip_addr(device, addr)
.map(|r| r.map_removed(Into::into).map_deferred(Either::Left)),
IpAddr::V6(addr) => self
.ip::<Ipv6>()
.del_ip_addr(device, addr)
.map(|r| r.map_removed(Into::into).map_deferred(Either::Right)),
}
}
pub fn get_routing_metric(
&mut self,
device_id: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
) -> RawMetric {
let metric = self.ip::<Ipv4>().get_routing_metric(device_id);
debug_assert_eq!(metric, self.ip::<Ipv6>().get_routing_metric(device_id));
metric
}
pub fn for_each_assigned_ip_addr_subnet<F: FnMut(AddrSubnetEither)>(
&mut self,
device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
mut f: F,
) {
self.ip::<Ipv4>().for_each_assigned_ip_addr_subnet(device, |a| f(a.into()));
self.ip::<Ipv6>().for_each_assigned_ip_addr_subnet(device, |a| f(a.into()));
}
pub fn get_assigned_ip_addr_subnets(
&mut self,
device: &<C::CoreContext as DeviceIdContext<AnyDevice>>::DeviceId,
) -> Vec<AddrSubnetEither> {
let mut vec = Vec::new();
self.for_each_assigned_ip_addr_subnet(device, |a| vec.push(a));
vec
}
}
#[derive(Debug)]
pub enum AddrSubnetAndManualConfigEither<Instant> {
V4(AddrSubnet<Ipv4Addr>, Ipv4AddrConfig<Instant>),
V6(AddrSubnet<Ipv6Addr>, Ipv6AddrManualConfig<Instant>),
}
impl<Inst: Instant> AddrSubnetAndManualConfigEither<Inst> {
pub(crate) fn new<I: Ip + IpDeviceIpExt>(
addr_subnet: AddrSubnet<I::Addr>,
config: I::ManualAddressConfig<Inst>,
) -> Self {
#[derive(GenericOverIp)]
#[generic_over_ip(I, Ip)]
struct AddrSubnetAndConfig<I: IpDeviceIpExt, Inst: Instant> {
addr_subnet: AddrSubnet<I::Addr>,
config: I::ManualAddressConfig<Inst>,
}
let result = I::map_ip_in(
AddrSubnetAndConfig { addr_subnet, config },
|AddrSubnetAndConfig { addr_subnet, config }| {
AddrSubnetAndManualConfigEither::V4(addr_subnet, config)
},
|AddrSubnetAndConfig { addr_subnet, config }| {
AddrSubnetAndManualConfigEither::V6(addr_subnet, config)
},
);
result
}
pub fn addr_subnet_either(&self) -> AddrSubnetEither {
match self {
Self::V4(addr_subnet, _) => AddrSubnetEither::V4(*addr_subnet),
Self::V6(addr_subnet, _) => AddrSubnetEither::V6(*addr_subnet),
}
}
}
impl<Inst: Instant> From<AddrSubnetEither> for AddrSubnetAndManualConfigEither<Inst> {
fn from(value: AddrSubnetEither) -> Self {
match value {
AddrSubnetEither::V4(addr_subnet) => {
AddrSubnetAndManualConfigEither::new::<Ipv4>(addr_subnet, Default::default())
}
AddrSubnetEither::V6(addr_subnet) => {
AddrSubnetAndManualConfigEither::new::<Ipv6>(addr_subnet, Default::default())
}
}
}
}
impl<Inst: Instant, I: IpAddress> From<AddrSubnet<I>> for AddrSubnetAndManualConfigEither<Inst> {
fn from(value: AddrSubnet<I>) -> Self {
AddrSubnetEither::from(value).into()
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum AddIpAddrSubnetError {
Exists,
InvalidAddr,
}
#[derive(Error, Debug, PartialEq)]
pub enum SetIpAddressPropertiesError {
#[error(transparent)]
NotFound(#[from] NotFoundError),
#[error("tried to set properties on a non-manually-configured address")]
NotManual,
}