blob: 6489abae243c7a2ca76a5356eb577187939377d0 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! The integrations for protocols built on top of an IP device.
use alloc::boxed::Box;
use core::{num::NonZeroU8, time::Duration};
use net_types::{
ip::{AddrSubnet, Ip as _, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr},
LinkLocalUnicastAddr, MulticastAddr, SpecifiedAddr, UnicastAddr, Witness as _,
};
use packet::{BufferMut, EmptyBuf, Serializer};
use packet_formats::{
icmp::{
ndp::{NeighborSolicitation, RouterSolicitation},
IcmpMessage, IcmpPacketBuilder, IcmpUnusedCode,
},
ip::Ipv6Proto,
};
use crate::{
context::{CounterContext, FrameContext},
error::ExistsError,
ip::{
self,
device::{
self, add_ipv6_addr_subnet,
dad::{DadHandler, Ipv6DeviceDadContext, Ipv6LayerDadContext},
del_ipv6_addr_with_reason, get_ipv4_addr_subnet, get_ipv4_device_state,
get_ipv6_device_state, get_ipv6_hop_limit, is_ip_device_enabled,
is_ipv4_routing_enabled, is_ipv6_routing_enabled,
route_discovery::{Ipv6RouteDiscoveryState, Ipv6RouteDiscoveryStateContext},
router_solicitation::{Ipv6DeviceRsContext, Ipv6LayerRsContext},
send_ip_frame,
slaac::{
SlaacAddressEntry, SlaacAddressEntryMut, SlaacConfiguration, SlaacStateContext,
},
state::{
AddrConfig, AddressState, DelIpv6AddrReason, IpDeviceConfiguration,
Ipv4DeviceConfiguration, Ipv6AddressEntry, Ipv6DeviceConfiguration, SlaacConfig,
},
IpDeviceIpExt, IpDeviceNonSyncContext,
},
gmp::{
igmp::{IgmpContext, IgmpGroupState, IgmpPacketMetadata},
mld::{MldContext, MldFrameMetadata, MldGroupState},
GmpHandler, MulticastGroupSet,
},
send_ipv6_packet_from_device, AddressStatus, IpLayerIpExt, IpLayerNonSyncContext,
Ipv4PresentAddressStatus, Ipv6PresentAddressStatus, SendIpPacketMeta, DEFAULT_TTL,
},
};
/// The IP packet hop limit for all NDP packets.
///
/// See [RFC 4861 section 4.1], [RFC 4861 section 4.2], [RFC 4861 section 4.2],
/// [RFC 4861 section 4.3], [RFC 4861 section 4.4], and [RFC 4861 section 4.5]
/// for more information.
///
/// [RFC 4861 section 4.1]: https://tools.ietf.org/html/rfc4861#section-4.1
/// [RFC 4861 section 4.2]: https://tools.ietf.org/html/rfc4861#section-4.2
/// [RFC 4861 section 4.3]: https://tools.ietf.org/html/rfc4861#section-4.3
/// [RFC 4861 section 4.4]: https://tools.ietf.org/html/rfc4861#section-4.4
/// [RFC 4861 section 4.5]: https://tools.ietf.org/html/rfc4861#section-4.5
pub(super) const REQUIRED_NDP_IP_PACKET_HOP_LIMIT: u8 = 255;
impl<
C: IpDeviceNonSyncContext<Ipv6, SC::DeviceId>,
SC: device::Ipv6DeviceContext<C> + GmpHandler<Ipv6, C> + DadHandler<C> + CounterContext,
> SlaacStateContext<C> for SC
{
fn get_config(&self, device_id: Self::DeviceId) -> SlaacConfiguration {
let Ipv6DeviceConfiguration {
dad_transmits: _,
max_router_solicitations: _,
slaac_config,
ip_config: _,
} = SC::get_ip_device_state(self, device_id).config;
slaac_config
}
fn dad_transmits(&self, device_id: Self::DeviceId) -> Option<NonZeroU8> {
let Ipv6DeviceConfiguration {
dad_transmits,
max_router_solicitations: _,
slaac_config: _,
ip_config: _,
} = SC::get_ip_device_state(self, device_id).config;
dad_transmits
}
fn retrans_timer(&self, device_id: SC::DeviceId) -> Duration {
SC::get_ip_device_state(self, device_id).retrans_timer.get()
}
fn get_interface_identifier(&self, device_id: Self::DeviceId) -> [u8; 8] {
SC::get_eui64_iid(self, device_id).unwrap_or_else(Default::default)
}
fn iter_slaac_addrs(
&self,
device_id: Self::DeviceId,
) -> Box<dyn Iterator<Item = SlaacAddressEntry<C::Instant>> + '_> {
Box::new(
SC::get_ip_device_state(self, device_id).ip_state.iter_addrs().cloned().filter_map(
|Ipv6AddressEntry { addr_sub, state: _, config, deprecated }| match config {
AddrConfig::Slaac(config) => {
Some(SlaacAddressEntry { addr_sub, config, deprecated })
}
AddrConfig::Manual => None,
},
),
)
}
fn iter_slaac_addrs_mut(
&mut self,
device_id: Self::DeviceId,
) -> Box<dyn Iterator<Item = SlaacAddressEntryMut<'_, C::Instant>> + '_> {
Box::new(SC::get_ip_device_state_mut(self, device_id).ip_state.iter_addrs_mut().filter_map(
|Ipv6AddressEntry { addr_sub, state: _, config, deprecated }| match config {
AddrConfig::Slaac(config) => {
Some(SlaacAddressEntryMut { addr_sub: *addr_sub, config, deprecated })
}
AddrConfig::Manual => None,
},
))
}
fn add_slaac_addr_sub(
&mut self,
ctx: &mut C,
device_id: Self::DeviceId,
addr_sub: AddrSubnet<Ipv6Addr, UnicastAddr<Ipv6Addr>>,
slaac_config: SlaacConfig<C::Instant>,
) -> Result<(), ExistsError> {
add_ipv6_addr_subnet(
self,
ctx,
device_id,
addr_sub.to_witness(),
AddrConfig::Slaac(slaac_config),
)
}
fn remove_slaac_addr(
&mut self,
ctx: &mut C,
device_id: Self::DeviceId,
addr: &UnicastAddr<Ipv6Addr>,
) {
del_ipv6_addr_with_reason(
self,
ctx,
device_id,
&addr.into_specified(),
DelIpv6AddrReason::ManualAction,
)
.unwrap()
}
}
impl<C: IpDeviceNonSyncContext<Ipv6, SC::DeviceId>, SC: device::IpDeviceContext<Ipv6, C>>
Ipv6RouteDiscoveryStateContext<C> for SC
{
fn get_discovered_routes_mut(
&mut self,
device_id: SC::DeviceId,
) -> &mut Ipv6RouteDiscoveryState {
&mut SC::get_ip_device_state_mut(self, device_id).route_discovery
}
}
impl<
C: IpDeviceNonSyncContext<Ipv4, SC::DeviceId>,
SC: device::BufferIpDeviceContext<Ipv4, C, EmptyBuf>,
> IgmpContext<C> for SC
{
fn get_ip_addr_subnet(&self, device: SC::DeviceId) -> Option<AddrSubnet<Ipv4Addr>> {
get_ipv4_addr_subnet(self, device)
}
fn igmp_enabled(&self, device: SC::DeviceId) -> bool {
let Ipv4DeviceConfiguration {
ip_config: IpDeviceConfiguration { ip_enabled, gmp_enabled },
} = SC::get_ip_device_state(self, device).config;
ip_enabled && gmp_enabled
}
fn get_state_mut(
&mut self,
device: SC::DeviceId,
) -> &mut MulticastGroupSet<Ipv4Addr, IgmpGroupState<C::Instant>> {
&mut self.get_ip_device_state_mut(device).ip_state.multicast_groups
}
fn get_state(
&self,
device: SC::DeviceId,
) -> &MulticastGroupSet<Ipv4Addr, IgmpGroupState<C::Instant>> {
&self.get_ip_device_state(device).ip_state.multicast_groups
}
}
impl<
C: IpDeviceNonSyncContext<Ipv4, SC::DeviceId>,
SC: device::BufferIpDeviceContext<Ipv4, C, EmptyBuf>,
> FrameContext<C, EmptyBuf, IgmpPacketMetadata<SC::DeviceId>> for SC
{
fn send_frame<S: Serializer<Buffer = EmptyBuf>>(
&mut self,
ctx: &mut C,
meta: IgmpPacketMetadata<SC::DeviceId>,
body: S,
) -> Result<(), S> {
SC::send_ip_frame(self, ctx, meta.device, meta.dst_ip.into_specified(), body)
}
}
impl<
C: IpDeviceNonSyncContext<Ipv6, SC::DeviceId>,
SC: device::BufferIpDeviceContext<Ipv6, C, EmptyBuf>,
> MldContext<C> for SC
{
fn get_ipv6_link_local_addr(
&self,
device: SC::DeviceId,
) -> Option<LinkLocalUnicastAddr<Ipv6Addr>> {
get_ipv6_device_state(self, device).iter_addrs().find_map(|a| {
if a.state.is_assigned() {
LinkLocalUnicastAddr::new(a.addr_sub().addr())
} else {
None
}
})
}
fn mld_enabled(&self, device: SC::DeviceId) -> bool {
let Ipv6DeviceConfiguration {
dad_transmits: _,
max_router_solicitations: _,
slaac_config: _,
ip_config: IpDeviceConfiguration { ip_enabled, gmp_enabled },
} = SC::get_ip_device_state(self, device).config;
ip_enabled && gmp_enabled
}
fn get_state_mut(
&mut self,
device: SC::DeviceId,
) -> &mut MulticastGroupSet<Ipv6Addr, MldGroupState<C::Instant>> {
&mut self.get_ip_device_state_mut(device).ip_state.multicast_groups
}
fn get_state(
&self,
device: SC::DeviceId,
) -> &MulticastGroupSet<Ipv6Addr, MldGroupState<C::Instant>> {
&self.get_ip_device_state(device).ip_state.multicast_groups
}
}
impl<C: IpDeviceNonSyncContext<Ipv6, SC::DeviceId>, SC: device::Ipv6DeviceContext<C>>
Ipv6DeviceDadContext<C> for SC
{
fn get_address_state_mut(
&mut self,
device_id: SC::DeviceId,
addr: UnicastAddr<Ipv6Addr>,
) -> Option<&mut AddressState> {
self.get_ip_device_state_mut(device_id)
.ip_state
.find_addr_mut(&addr)
.map(|Ipv6AddressEntry { addr_sub: _, state, config: _, deprecated: _ }| state)
}
fn retrans_timer(&self, device_id: SC::DeviceId) -> Duration {
SC::get_ip_device_state(self, device_id).retrans_timer.get()
}
}
fn send_ndp_packet<
C: IpLayerNonSyncContext<Ipv6, SC::DeviceId>,
SC: ip::BufferIpDeviceContext<Ipv6, C, EmptyBuf>,
S: Serializer<Buffer = EmptyBuf>,
M: IcmpMessage<Ipv6, &'static [u8]>,
>(
sync_ctx: &mut SC,
ctx: &mut C,
device_id: SC::DeviceId,
src_ip: Ipv6Addr,
dst_ip: SpecifiedAddr<Ipv6Addr>,
body: S,
code: M::Code,
message: M,
) -> Result<(), S> {
// TODO(https://fxbug.dev/95359): Send through ICMPv6 send path.
send_ipv6_packet_from_device(
sync_ctx,
ctx,
SendIpPacketMeta {
device: device_id,
src_ip: SpecifiedAddr::new(src_ip),
dst_ip,
next_hop: dst_ip,
ttl: NonZeroU8::new(REQUIRED_NDP_IP_PACKET_HOP_LIMIT),
proto: Ipv6Proto::Icmpv6,
mtu: None,
},
body.encapsulate(IcmpPacketBuilder::<Ipv6, &[u8], _>::new(
src_ip,
dst_ip.get(),
code,
message,
)),
)
.map_err(|s| s.into_inner())
}
impl<
C: IpLayerNonSyncContext<Ipv6, SC::DeviceId>,
SC: ip::BufferIpDeviceContext<Ipv6, C, EmptyBuf>,
> Ipv6LayerDadContext<C> for SC
{
fn send_dad_packet(
&mut self,
ctx: &mut C,
device_id: Self::DeviceId,
dst_ip: MulticastAddr<Ipv6Addr>,
message: NeighborSolicitation,
) -> Result<(), ()> {
send_ndp_packet(
self,
ctx,
device_id,
Ipv6::UNSPECIFIED_ADDRESS,
dst_ip.into_specified(),
EmptyBuf,
IcmpUnusedCode,
message,
)
.map_err(|EmptyBuf| ())
}
}
impl<C: IpDeviceNonSyncContext<Ipv6, SC::DeviceId>, SC: device::Ipv6DeviceContext<C>>
Ipv6DeviceRsContext<C> for SC
{
fn get_max_router_solicitations(&self, device_id: SC::DeviceId) -> Option<NonZeroU8> {
self.get_ip_device_state(device_id).config.max_router_solicitations
}
fn get_router_soliciations_remaining_mut(
&mut self,
device_id: SC::DeviceId,
) -> &mut Option<NonZeroU8> {
&mut self.get_ip_device_state_mut(device_id).router_soliciations_remaining
}
fn get_link_layer_addr_bytes(&self, device_id: SC::DeviceId) -> Option<&[u8]> {
device::Ipv6DeviceContext::get_link_layer_addr_bytes(self, device_id)
}
}
impl<
C: IpDeviceNonSyncContext<Ipv6, SC::DeviceId> + IpLayerNonSyncContext<Ipv6, SC::DeviceId>,
SC: ip::BufferIpDeviceContext<Ipv6, C, EmptyBuf> + device::Ipv6DeviceContext<C>,
> Ipv6LayerRsContext<C> for SC
{
fn send_rs_packet<
S: Serializer<Buffer = EmptyBuf>,
F: FnOnce(Option<UnicastAddr<Ipv6Addr>>) -> S,
>(
&mut self,
ctx: &mut C,
device_id: Self::DeviceId,
message: RouterSolicitation,
body: F,
) -> Result<(), S> {
let dst_ip = Ipv6::ALL_ROUTERS_LINK_LOCAL_MULTICAST_ADDRESS.into_specified();
let src_ip = crate::ip::socket::ipv6_source_address_selection::select_ipv6_source_address(
dst_ip,
device_id,
get_ipv6_device_state(self, device_id).iter_addrs().map(move |a| (a, device_id)),
);
send_ndp_packet(
self,
ctx,
device_id,
src_ip.map_or(Ipv6::UNSPECIFIED_ADDRESS, |a| a.get()),
dst_ip,
body(src_ip),
IcmpUnusedCode,
message,
)
}
}
impl<C: IpDeviceNonSyncContext<Ipv4, SC::DeviceId>, SC: device::IpDeviceContext<Ipv4, C>>
ip::IpDeviceContext<Ipv4, C> for SC
{
fn is_ip_device_enabled(&self, device_id: SC::DeviceId) -> bool {
is_ip_device_enabled(self, device_id)
}
fn address_status(
&self,
dst_ip: SpecifiedAddr<Ipv4Addr>,
) -> AddressStatus<(SC::DeviceId, Ipv4PresentAddressStatus)> {
self.iter_devices()
.find_map(|device_id| match self.address_status_for_device(dst_ip, device_id) {
AddressStatus::Present(a) => Some(AddressStatus::Present((device_id, a))),
AddressStatus::Unassigned => None,
})
.unwrap_or(AddressStatus::Unassigned)
}
fn address_status_for_device(
&self,
dst_ip: SpecifiedAddr<Ipv4Addr>,
device_id: Self::DeviceId,
) -> AddressStatus<Ipv4PresentAddressStatus> {
let dev_state = get_ipv4_device_state(self, device_id);
if dst_ip.is_limited_broadcast() {
return AddressStatus::Present(Ipv4PresentAddressStatus::LimitedBroadcast);
}
if MulticastAddr::new(dst_ip.get())
.map_or(false, |addr| dev_state.multicast_groups.contains(&addr))
{
return AddressStatus::Present(Ipv4PresentAddressStatus::Multicast);
}
dev_state
.iter_addrs()
.find_map(|addr| {
let (addr, subnet) = addr.addr_subnet();
if addr == dst_ip {
Some(AddressStatus::Present(Ipv4PresentAddressStatus::Unicast))
} else if dst_ip.get() == subnet.broadcast() {
Some(AddressStatus::Present(Ipv4PresentAddressStatus::SubnetBroadcast))
} else {
None
}
})
.unwrap_or(AddressStatus::Unassigned)
}
fn is_device_routing_enabled(&self, device_id: SC::DeviceId) -> bool {
is_ipv4_routing_enabled(self, device_id)
}
fn get_hop_limit(&self, _device_id: SC::DeviceId) -> NonZeroU8 {
DEFAULT_TTL
}
fn get_mtu(&self, device_id: Self::DeviceId) -> u32 {
self.get_mtu(device_id)
}
}
impl<C: IpDeviceNonSyncContext<Ipv6, SC::DeviceId>, SC: device::IpDeviceContext<Ipv6, C>>
ip::IpDeviceContext<Ipv6, C> for SC
{
fn is_ip_device_enabled(&self, device_id: SC::DeviceId) -> bool {
is_ip_device_enabled(self, device_id)
}
fn address_status(
&self,
addr: SpecifiedAddr<Ipv6Addr>,
) -> AddressStatus<(SC::DeviceId, Ipv6PresentAddressStatus)> {
self.iter_devices()
.find_map(|device_id| match self.address_status_for_device(addr, device_id) {
AddressStatus::Present(a) => Some(AddressStatus::Present((device_id, a))),
AddressStatus::Unassigned => None,
})
.unwrap_or(AddressStatus::Unassigned)
}
fn address_status_for_device(
&self,
addr: SpecifiedAddr<Ipv6Addr>,
device_id: Self::DeviceId,
) -> AddressStatus<<Ipv6 as IpLayerIpExt>::AddressStatus> {
let dev_state = get_ipv6_device_state(self, device_id);
if MulticastAddr::new(addr.get())
.map_or(false, |addr| dev_state.multicast_groups.contains(&addr))
{
AddressStatus::Present(Ipv6PresentAddressStatus::Multicast)
} else {
dev_state.find_addr(&addr).map(|addr| addr.state).map_or(
AddressStatus::Unassigned,
|state| match state {
AddressState::Assigned => {
AddressStatus::Present(Ipv6PresentAddressStatus::UnicastAssigned)
}
AddressState::Tentative { dad_transmits_remaining: _ } => {
AddressStatus::Present(Ipv6PresentAddressStatus::UnicastTentative)
}
},
)
}
}
fn is_device_routing_enabled(&self, device_id: SC::DeviceId) -> bool {
is_ipv6_routing_enabled(self, device_id)
}
fn get_hop_limit(&self, device_id: SC::DeviceId) -> NonZeroU8 {
get_ipv6_hop_limit(self, device_id)
}
fn get_mtu(&self, device_id: Self::DeviceId) -> u32 {
self.get_mtu(device_id)
}
}
impl<
C: IpDeviceNonSyncContext<Ipv6, SC::DeviceId>,
SC: device::BufferIpDeviceContext<Ipv6, C, EmptyBuf>,
> FrameContext<C, EmptyBuf, MldFrameMetadata<SC::DeviceId>> for SC
{
fn send_frame<S: Serializer<Buffer = EmptyBuf>>(
&mut self,
ctx: &mut C,
meta: MldFrameMetadata<SC::DeviceId>,
body: S,
) -> Result<(), S> {
SC::send_ip_frame(self, ctx, meta.device, meta.dst_ip.into_specified(), body)
}
}
impl<
I: IpLayerIpExt + IpDeviceIpExt<C::Instant, SC::DeviceId>,
B: BufferMut,
C: IpDeviceNonSyncContext<I, SC::DeviceId>,
SC: device::BufferIpDeviceContext<I, C, B> + ip::IpDeviceContext<I, C>,
> ip::BufferIpDeviceContext<I, C, B> for SC
{
fn send_ip_frame<S: Serializer<Buffer = B>>(
&mut self,
ctx: &mut C,
device_id: SC::DeviceId,
next_hop: SpecifiedAddr<I::Addr>,
packet: S,
) -> Result<(), S> {
send_ip_frame(self, ctx, device_id, next_hop, packet)
}
}