1pub(crate) mod api;
8pub(crate) mod config;
9pub(crate) mod dad;
10pub(crate) mod nud;
11pub(crate) mod opaque_iid;
12pub(crate) mod route_discovery;
13pub(crate) mod router_solicitation;
14pub(crate) mod slaac;
15pub(crate) mod state;
16
17use alloc::vec::Vec;
18use core::fmt::{Debug, Display};
19use core::hash::Hash;
20use core::num::NonZeroU8;
21
22use derivative::Derivative;
23use log::{debug, info};
24use net_types::ip::{
25 AddrSubnet, GenericOverIp, Ip, IpAddress, Ipv4, Ipv4Addr, Ipv4SourceAddr, Ipv6, Ipv6Addr,
26 Ipv6SourceAddr, Mtu, Subnet,
27};
28use net_types::{LinkLocalAddress as _, MulticastAddr, SpecifiedAddr, Witness};
29use netstack3_base::{
30 AnyDevice, AssignedAddrIpExt, Counter, CounterCollectionSpec, DeferredResourceRemovalContext,
31 DeviceIdContext, EventContext, ExistsError, HandleableTimer, Instant, InstantBindingsTypes,
32 InstantContext, IpAddressId, IpDeviceAddr, IpDeviceAddressIdContext, IpExt, Ipv4DeviceAddr,
33 Ipv6DeviceAddr, NotFoundError, RemoveResourceResultWithContext, ResourceCounterContext,
34 RngContext, SendFrameError, StrongDeviceIdentifier, TimerContext, TimerHandler,
35 TxMetadataBindingsTypes, WeakDeviceIdentifier, WeakIpAddressId,
36};
37use netstack3_filter::ProofOfEgressCheck;
38use packet::{BufferMut, Serializer};
39use packet_formats::icmp::mld::MldPacket;
40use packet_formats::icmp::ndp::NonZeroNdpLifetime;
41use packet_formats::utils::NonZeroDuration;
42use zerocopy::SplitByteSlice;
43
44use crate::device::CommonAddressProperties;
45use crate::internal::base::{DeviceIpLayerMetadata, IpDeviceMtuContext, IpPacketDestination};
46use crate::internal::counters::IpCounters;
47use crate::internal::device::config::{
48 IpDeviceConfigurationUpdate, Ipv4DeviceConfigurationUpdate, Ipv6DeviceConfigurationUpdate,
49};
50use crate::internal::device::dad::{
51 DadHandler, DadIncomingPacketResult, DadIpExt, DadTimerId, Ipv4DadAddressInfo,
52 Ipv6PacketResultMetadata, NeedsDad,
53};
54use crate::internal::device::nud::NudIpHandler;
55use crate::internal::device::route_discovery::{
56 Ipv6DiscoveredRoute, Ipv6DiscoveredRouteTimerId, RouteDiscoveryHandler,
57};
58use crate::internal::device::router_solicitation::{RsHandler, RsTimerId};
59use crate::internal::device::slaac::{SlaacHandler, SlaacTimerId};
60use crate::internal::device::state::{
61 IpAddressData, IpAddressFlags, IpDeviceConfiguration, IpDeviceFlags, IpDeviceState,
62 IpDeviceStateBindingsTypes, IpDeviceStateIpExt, Ipv4AddrConfig, Ipv4DeviceConfiguration,
63 Ipv4DeviceState, Ipv6AddrConfig, Ipv6AddrManualConfig, Ipv6DeviceConfiguration,
64 Ipv6DeviceState, Ipv6NetworkLearnedParameters, Lifetime, PreferredLifetime, WeakAddressId,
65};
66use crate::internal::gmp::igmp::{IgmpPacketHandler, IgmpTimerId};
67use crate::internal::gmp::mld::{MldPacketHandler, MldTimerId};
68use crate::internal::gmp::{self, GmpHandler, GroupJoinResult, GroupLeaveResult};
69use crate::internal::local_delivery::{IpHeaderInfo, LocalDeliveryPacketInfo};
70
71#[derive(Derivative, GenericOverIp)]
78#[derivative(
79 Clone(bound = ""),
80 Eq(bound = ""),
81 PartialEq(bound = ""),
82 Hash(bound = ""),
83 Debug(bound = "")
84)]
85#[generic_over_ip(I, Ip)]
86pub struct IpDeviceTimerId<
87 I: IpDeviceIpExt,
88 D: WeakDeviceIdentifier,
89 BT: IpDeviceStateBindingsTypes,
90>(I::Timer<D, BT>);
91
92#[derive(Derivative)]
94#[derivative(
95 Clone(bound = ""),
96 Debug(bound = ""),
97 Eq(bound = ""),
98 Hash(bound = ""),
99 PartialEq(bound = "")
100)]
101pub enum Ipv4DeviceTimerId<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> {
102 Igmp(IgmpTimerId<D>),
104 Dad(DadTimerId<Ipv4, D, WeakAddressId<Ipv4, BT>>),
106}
107
108impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> Ipv4DeviceTimerId<D, BT> {
109 fn device_id(&self) -> Option<D::Strong> {
111 match self {
112 Ipv4DeviceTimerId::Igmp(igmp) => igmp.device_id().upgrade(),
113 Ipv4DeviceTimerId::Dad(dad) => dad.device_id().upgrade(),
114 }
115 }
116
117 pub fn into_common(self) -> IpDeviceTimerId<Ipv4, D, BT> {
119 self.into()
120 }
121}
122
123impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<IpDeviceTimerId<Ipv4, D, BT>>
124 for Ipv4DeviceTimerId<D, BT>
125{
126 fn from(IpDeviceTimerId(inner): IpDeviceTimerId<Ipv4, D, BT>) -> Self {
127 inner
128 }
129}
130
131impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<Ipv4DeviceTimerId<D, BT>>
132 for IpDeviceTimerId<Ipv4, D, BT>
133{
134 fn from(value: Ipv4DeviceTimerId<D, BT>) -> Self {
135 Self(value)
136 }
137}
138
139impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<IgmpTimerId<D>>
140 for Ipv4DeviceTimerId<D, BT>
141{
142 fn from(id: IgmpTimerId<D>) -> Ipv4DeviceTimerId<D, BT> {
143 Ipv4DeviceTimerId::Igmp(id)
144 }
145}
146
147impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes>
148 From<DadTimerId<Ipv4, D, WeakAddressId<Ipv4, BT>>> for Ipv4DeviceTimerId<D, BT>
149{
150 fn from(id: DadTimerId<Ipv4, D, WeakAddressId<Ipv4, BT>>) -> Ipv4DeviceTimerId<D, BT> {
151 Ipv4DeviceTimerId::Dad(id)
152 }
153}
154
155impl<
156 D: WeakDeviceIdentifier,
157 BC: IpDeviceStateBindingsTypes,
158 CC: TimerHandler<BC, IgmpTimerId<D>>
159 + TimerHandler<BC, DadTimerId<Ipv4, D, WeakAddressId<Ipv4, BC>>>,
160> HandleableTimer<CC, BC> for Ipv4DeviceTimerId<D, BC>
161{
162 fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer: BC::UniqueTimerId) {
163 match self {
164 Ipv4DeviceTimerId::Igmp(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
165 Ipv4DeviceTimerId::Dad(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
166 }
167 }
168}
169
170impl<I, CC, BC> HandleableTimer<CC, BC> for IpDeviceTimerId<I, CC::WeakDeviceId, BC>
171where
172 I: IpDeviceIpExt,
173 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
174 CC: IpDeviceConfigurationContext<I, BC>,
175 for<'a> CC::WithIpDeviceConfigurationInnerCtx<'a>:
176 TimerHandler<BC, I::Timer<CC::WeakDeviceId, BC>>,
177{
178 fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer: BC::UniqueTimerId) {
179 let Self(id) = self;
180 let Some(device_id) = I::timer_device_id(&id) else {
181 return;
182 };
183 core_ctx.with_ip_device_configuration(&device_id, |_state, mut core_ctx| {
184 TimerHandler::handle_timer(&mut core_ctx, bindings_ctx, id, timer)
185 })
186 }
187}
188
189#[derive(Derivative)]
191#[derivative(
192 Clone(bound = ""),
193 Debug(bound = ""),
194 Eq(bound = ""),
195 Hash(bound = ""),
196 PartialEq(bound = "")
197)]
198#[allow(missing_docs)]
199pub enum Ipv6DeviceTimerId<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> {
200 Mld(MldTimerId<D>),
201 Dad(DadTimerId<Ipv6, D, WeakAddressId<Ipv6, BT>>),
202 Rs(RsTimerId<D>),
203 RouteDiscovery(Ipv6DiscoveredRouteTimerId<D>),
204 Slaac(SlaacTimerId<D>),
205}
206
207impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<IpDeviceTimerId<Ipv6, D, BT>>
208 for Ipv6DeviceTimerId<D, BT>
209{
210 fn from(IpDeviceTimerId(inner): IpDeviceTimerId<Ipv6, D, BT>) -> Self {
211 inner
212 }
213}
214
215impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<Ipv6DeviceTimerId<D, BT>>
216 for IpDeviceTimerId<Ipv6, D, BT>
217{
218 fn from(value: Ipv6DeviceTimerId<D, BT>) -> Self {
219 Self(value)
220 }
221}
222
223impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> Ipv6DeviceTimerId<D, BT> {
224 fn device_id(&self) -> Option<D::Strong> {
226 match self {
227 Self::Mld(id) => id.device_id(),
228 Self::Dad(id) => id.device_id(),
229 Self::Rs(id) => id.device_id(),
230 Self::RouteDiscovery(id) => id.device_id(),
231 Self::Slaac(id) => id.device_id(),
232 }
233 .upgrade()
234 }
235
236 pub fn into_common(self) -> IpDeviceTimerId<Ipv6, D, BT> {
238 self.into()
239 }
240}
241
242impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<MldTimerId<D>>
243 for Ipv6DeviceTimerId<D, BT>
244{
245 fn from(id: MldTimerId<D>) -> Ipv6DeviceTimerId<D, BT> {
246 Ipv6DeviceTimerId::Mld(id)
247 }
248}
249
250impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes>
251 From<DadTimerId<Ipv6, D, WeakAddressId<Ipv6, BT>>> for Ipv6DeviceTimerId<D, BT>
252{
253 fn from(id: DadTimerId<Ipv6, D, WeakAddressId<Ipv6, BT>>) -> Ipv6DeviceTimerId<D, BT> {
254 Ipv6DeviceTimerId::Dad(id)
255 }
256}
257
258impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<RsTimerId<D>>
259 for Ipv6DeviceTimerId<D, BT>
260{
261 fn from(id: RsTimerId<D>) -> Ipv6DeviceTimerId<D, BT> {
262 Ipv6DeviceTimerId::Rs(id)
263 }
264}
265
266impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<Ipv6DiscoveredRouteTimerId<D>>
267 for Ipv6DeviceTimerId<D, BT>
268{
269 fn from(id: Ipv6DiscoveredRouteTimerId<D>) -> Ipv6DeviceTimerId<D, BT> {
270 Ipv6DeviceTimerId::RouteDiscovery(id)
271 }
272}
273
274impl<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> From<SlaacTimerId<D>>
275 for Ipv6DeviceTimerId<D, BT>
276{
277 fn from(id: SlaacTimerId<D>) -> Ipv6DeviceTimerId<D, BT> {
278 Ipv6DeviceTimerId::Slaac(id)
279 }
280}
281
282impl<
283 D: WeakDeviceIdentifier,
284 BC: IpDeviceStateBindingsTypes,
285 CC: TimerHandler<BC, RsTimerId<D>>
286 + TimerHandler<BC, Ipv6DiscoveredRouteTimerId<D>>
287 + TimerHandler<BC, MldTimerId<D>>
288 + TimerHandler<BC, SlaacTimerId<D>>
289 + TimerHandler<BC, DadTimerId<Ipv6, D, WeakAddressId<Ipv6, BC>>>,
290> HandleableTimer<CC, BC> for Ipv6DeviceTimerId<D, BC>
291{
292 fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, timer: BC::UniqueTimerId) {
293 match self {
294 Ipv6DeviceTimerId::Mld(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
295 Ipv6DeviceTimerId::Dad(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
296 Ipv6DeviceTimerId::Rs(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
297 Ipv6DeviceTimerId::RouteDiscovery(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
298 Ipv6DeviceTimerId::Slaac(id) => core_ctx.handle_timer(bindings_ctx, id, timer),
299 }
300 }
301}
302
303pub trait IpDeviceIpExt: IpDeviceStateIpExt + AssignedAddrIpExt + gmp::IpExt + DadIpExt {
305 type State<BT: IpDeviceStateBindingsTypes>: AsRef<IpDeviceState<Self, BT>>
307 + AsMut<IpDeviceState<Self, BT>>;
308 type Configuration: AsRef<IpDeviceConfiguration>
310 + AsMut<IpDeviceConfiguration>
311 + Clone
312 + Debug
313 + Eq
314 + PartialEq;
315 type Timer<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes>: Into<IpDeviceTimerId<Self, D, BT>>
317 + From<IpDeviceTimerId<Self, D, BT>>
318 + Clone
319 + Eq
320 + PartialEq
321 + Debug
322 + Hash;
323 type ManualAddressConfig<I: Instant>: Default + Debug + Into<Self::AddressConfig<I>>;
325 type ConfigurationUpdate: From<IpDeviceConfigurationUpdate>
327 + AsRef<IpDeviceConfigurationUpdate>
328 + Debug;
329
330 fn get_common_props<I: Instant>(config: &Self::AddressConfig<I>) -> CommonAddressProperties<I>;
332
333 fn timer_device_id<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes>(
335 timer: &Self::Timer<D, BT>,
336 ) -> Option<D::Strong>;
337}
338
339impl IpDeviceIpExt for Ipv4 {
340 type State<BT: IpDeviceStateBindingsTypes> = Ipv4DeviceState<BT>;
341 type Configuration = Ipv4DeviceConfiguration;
342 type Timer<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> = Ipv4DeviceTimerId<D, BT>;
343 type ManualAddressConfig<I: Instant> = Ipv4AddrConfig<I>;
344 type ConfigurationUpdate = Ipv4DeviceConfigurationUpdate;
345
346 fn get_common_props<I: Instant>(config: &Self::AddressConfig<I>) -> CommonAddressProperties<I> {
347 config.properties
348 }
349
350 fn timer_device_id<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes>(
351 timer: &Self::Timer<D, BT>,
352 ) -> Option<D::Strong> {
353 timer.device_id()
354 }
355}
356
357impl IpDeviceIpExt for Ipv6 {
358 type State<BT: IpDeviceStateBindingsTypes> = Ipv6DeviceState<BT>;
359 type Configuration = Ipv6DeviceConfiguration;
360 type Timer<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes> = Ipv6DeviceTimerId<D, BT>;
361 type ManualAddressConfig<I: Instant> = Ipv6AddrManualConfig<I>;
362 type ConfigurationUpdate = Ipv6DeviceConfigurationUpdate;
363
364 fn get_common_props<I: Instant>(config: &Self::AddressConfig<I>) -> CommonAddressProperties<I> {
365 CommonAddressProperties {
366 valid_until: config.valid_until(),
367 preferred_lifetime: config.preferred_lifetime(),
368 }
369 }
370
371 fn timer_device_id<D: WeakDeviceIdentifier, BT: IpDeviceStateBindingsTypes>(
372 timer: &Self::Timer<D, BT>,
373 ) -> Option<D::Strong> {
374 timer.device_id()
375 }
376}
377#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
379pub enum IpAddressState {
380 Unavailable,
382 Assigned,
385 Tentative,
389}
390
391#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
393pub enum AddressRemovedReason {
394 Manual,
396 DadFailed,
398 Forfeited,
401}
402
403#[derive(Debug, Eq, Hash, PartialEq, GenericOverIp)]
404#[generic_over_ip(I, Ip)]
405pub enum IpDeviceEvent<DeviceId, I: Ip, Instant> {
407 AddressAdded {
409 device: DeviceId,
411 addr: AddrSubnet<I::Addr>,
413 state: IpAddressState,
415 valid_until: Lifetime<Instant>,
417 preferred_lifetime: PreferredLifetime<Instant>,
419 },
420 AddressRemoved {
422 device: DeviceId,
424 addr: SpecifiedAddr<I::Addr>,
426 reason: AddressRemovedReason,
428 },
429 AddressStateChanged {
431 device: DeviceId,
433 addr: SpecifiedAddr<I::Addr>,
435 state: IpAddressState,
437 },
438 AddressPropertiesChanged {
440 device: DeviceId,
442 addr: SpecifiedAddr<I::Addr>,
444 valid_until: Lifetime<Instant>,
446 preferred_lifetime: PreferredLifetime<Instant>,
448 },
449 EnabledChanged {
451 device: DeviceId,
453 ip_enabled: bool,
455 },
456}
457
458impl<DeviceId, I: Ip, Instant> IpDeviceEvent<DeviceId, I, Instant> {
459 pub fn map_device<N, F: FnOnce(DeviceId) -> N>(self, map: F) -> IpDeviceEvent<N, I, Instant> {
461 match self {
462 IpDeviceEvent::AddressAdded {
463 device,
464 addr,
465 state,
466 valid_until,
467 preferred_lifetime,
468 } => IpDeviceEvent::AddressAdded {
469 device: map(device),
470 addr,
471 state,
472 valid_until,
473 preferred_lifetime,
474 },
475 IpDeviceEvent::AddressRemoved { device, addr, reason } => {
476 IpDeviceEvent::AddressRemoved { device: map(device), addr, reason }
477 }
478 IpDeviceEvent::AddressStateChanged { device, addr, state } => {
479 IpDeviceEvent::AddressStateChanged { device: map(device), addr, state }
480 }
481 IpDeviceEvent::EnabledChanged { device, ip_enabled } => {
482 IpDeviceEvent::EnabledChanged { device: map(device), ip_enabled }
483 }
484 IpDeviceEvent::AddressPropertiesChanged {
485 device,
486 addr,
487 valid_until,
488 preferred_lifetime,
489 } => IpDeviceEvent::AddressPropertiesChanged {
490 device: map(device),
491 addr,
492 valid_until,
493 preferred_lifetime,
494 },
495 }
496 }
497}
498
499pub trait IpDeviceBindingsContext<I: IpDeviceIpExt, D: StrongDeviceIdentifier>:
501 IpDeviceStateBindingsTypes
502 + DeferredResourceRemovalContext
503 + TimerContext
504 + RngContext
505 + EventContext<IpDeviceEvent<D, I, <Self as InstantBindingsTypes>::Instant>>
506{
507}
508impl<
509 D: StrongDeviceIdentifier,
510 I: IpDeviceIpExt,
511 BC: IpDeviceStateBindingsTypes
512 + DeferredResourceRemovalContext
513 + TimerContext
514 + RngContext
515 + EventContext<IpDeviceEvent<D, I, <Self as InstantBindingsTypes>::Instant>>,
516> IpDeviceBindingsContext<I, D> for BC
517{
518}
519
520pub trait IpDeviceAddressContext<I: IpDeviceIpExt, BT: InstantBindingsTypes>:
522 IpDeviceAddressIdContext<I>
523{
524 fn with_ip_address_data<O, F: FnOnce(&IpAddressData<I, BT::Instant>) -> O>(
527 &mut self,
528 device_id: &Self::DeviceId,
529 addr_id: &Self::AddressId,
530 cb: F,
531 ) -> O;
532
533 fn with_ip_address_data_mut<O, F: FnOnce(&mut IpAddressData<I, BT::Instant>) -> O>(
536 &mut self,
537 device_id: &Self::DeviceId,
538 addr_id: &Self::AddressId,
539 cb: F,
540 ) -> O;
541}
542
543pub trait IpDeviceStateContext<I: IpDeviceIpExt, BT: IpDeviceStateBindingsTypes>:
545 IpDeviceAddressContext<I, BT>
546{
547 type IpDeviceAddressCtx<'a>: IpDeviceAddressContext<I, BT, DeviceId = Self::DeviceId, AddressId = Self::AddressId>;
549
550 fn with_ip_device_flags<O, F: FnOnce(&IpDeviceFlags) -> O>(
557 &mut self,
558 device_id: &Self::DeviceId,
559 cb: F,
560 ) -> O;
561
562 fn add_ip_address(
564 &mut self,
565 device_id: &Self::DeviceId,
566 addr: AddrSubnet<I::Addr, I::AssignedWitness>,
567 config: I::AddressConfig<BT::Instant>,
568 ) -> Result<Self::AddressId, ExistsError>;
569
570 fn remove_ip_address(
572 &mut self,
573 device_id: &Self::DeviceId,
574 addr: Self::AddressId,
575 ) -> RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BT>;
576
577 fn get_address_id(
579 &mut self,
580 device_id: &Self::DeviceId,
581 addr: SpecifiedAddr<I::Addr>,
582 ) -> Result<Self::AddressId, NotFoundError>;
583
584 type AddressIdsIter<'a>: Iterator<Item = Self::AddressId> + 'a;
586
587 fn with_address_ids<
590 O,
591 F: FnOnce(Self::AddressIdsIter<'_>, &mut Self::IpDeviceAddressCtx<'_>) -> O,
592 >(
593 &mut self,
594 device_id: &Self::DeviceId,
595 cb: F,
596 ) -> O;
597
598 fn with_default_hop_limit<O, F: FnOnce(&NonZeroU8) -> O>(
601 &mut self,
602 device_id: &Self::DeviceId,
603 cb: F,
604 ) -> O;
605
606 fn with_default_hop_limit_mut<O, F: FnOnce(&mut NonZeroU8) -> O>(
609 &mut self,
610 device_id: &Self::DeviceId,
611 cb: F,
612 ) -> O;
613
614 fn join_link_multicast_group(
617 &mut self,
618 bindings_ctx: &mut BT,
619 device_id: &Self::DeviceId,
620 multicast_addr: MulticastAddr<I::Addr>,
621 );
622
623 fn leave_link_multicast_group(
626 &mut self,
627 bindings_ctx: &mut BT,
628 device_id: &Self::DeviceId,
629 multicast_addr: MulticastAddr<I::Addr>,
630 );
631}
632
633pub trait WithIpDeviceConfigurationMutInner<I: IpDeviceIpExt, BT: IpDeviceStateBindingsTypes>:
636 DeviceIdContext<AnyDevice>
637{
638 type IpDeviceStateCtx<'s>: IpDeviceStateContext<I, BT, DeviceId = Self::DeviceId>
640 + GmpHandler<I, BT>
641 + NudIpHandler<I, BT>
642 + DadHandler<I, BT>
643 + 's
644 where
645 Self: 's;
646
647 fn ip_device_configuration_and_ctx(
650 &mut self,
651 ) -> (&I::Configuration, Self::IpDeviceStateCtx<'_>);
652
653 fn with_configuration_and_flags_mut<
656 O,
657 F: FnOnce(&mut I::Configuration, &mut IpDeviceFlags) -> O,
658 >(
659 &mut self,
660 device_id: &Self::DeviceId,
661 cb: F,
662 ) -> O;
663}
664
665pub trait IpDeviceConfigurationContext<
667 I: IpDeviceIpExt,
668 BC: IpDeviceBindingsContext<I, Self::DeviceId>,
669>: IpDeviceStateContext<I, BC> + IpDeviceMtuContext<I> + DeviceIdContext<AnyDevice>
670{
671 type DevicesIter<'s>: Iterator<Item = Self::DeviceId> + 's;
673 type WithIpDeviceConfigurationInnerCtx<'s>: IpDeviceStateContext<I, BC, DeviceId = Self::DeviceId, AddressId = Self::AddressId>
675 + GmpHandler<I, BC>
676 + NudIpHandler<I, BC>
677 + DadHandler<I, BC>
678 + IpAddressRemovalHandler<I, BC>
679 + IpDeviceMtuContext<I>
680 + 's;
681 type WithIpDeviceConfigurationMutInner<'s>: WithIpDeviceConfigurationMutInner<I, BC, DeviceId = Self::DeviceId>
683 + 's;
684 type DeviceAddressAndGroupsAccessor<'s>: IpDeviceStateContext<I, BC, DeviceId = Self::DeviceId>
686 + 's;
687
688 fn with_ip_device_configuration<
691 O,
692 F: FnOnce(&I::Configuration, Self::WithIpDeviceConfigurationInnerCtx<'_>) -> O,
693 >(
694 &mut self,
695 device_id: &Self::DeviceId,
696 cb: F,
697 ) -> O;
698
699 fn with_ip_device_configuration_mut<
701 O,
702 F: FnOnce(Self::WithIpDeviceConfigurationMutInner<'_>) -> O,
703 >(
704 &mut self,
705 device_id: &Self::DeviceId,
706 cb: F,
707 ) -> O;
708
709 fn with_devices_and_state<
712 O,
713 F: FnOnce(Self::DevicesIter<'_>, Self::DeviceAddressAndGroupsAccessor<'_>) -> O,
714 >(
715 &mut self,
716 cb: F,
717 ) -> O;
718
719 fn loopback_id(&mut self) -> Option<Self::DeviceId>;
722}
723
724pub trait WithIpv6DeviceConfigurationMutInner<BC: IpDeviceBindingsContext<Ipv6, Self::DeviceId>>:
727 WithIpDeviceConfigurationMutInner<Ipv6, BC>
728{
729 type Ipv6DeviceStateCtx<'s>: Ipv6DeviceContext<BC, DeviceId = Self::DeviceId>
731 + GmpHandler<Ipv6, BC>
732 + NudIpHandler<Ipv6, BC>
733 + DadHandler<Ipv6, BC>
734 + RsHandler<BC>
735 + SlaacHandler<BC>
736 + RouteDiscoveryHandler<BC>
737 + 's
738 where
739 Self: 's;
740
741 fn ipv6_device_configuration_and_ctx(
744 &mut self,
745 ) -> (&Ipv6DeviceConfiguration, Self::Ipv6DeviceStateCtx<'_>);
746}
747
748pub trait Ipv6DeviceConfigurationContext<BC: IpDeviceBindingsContext<Ipv6, Self::DeviceId>>:
750 IpDeviceConfigurationContext<Ipv6, BC>
751{
752 type Ipv6DeviceStateCtx<'s>: Ipv6DeviceContext<BC, DeviceId = Self::DeviceId, AddressId = Self::AddressId>
754 + GmpHandler<Ipv6, BC>
755 + MldPacketHandler<BC, Self::DeviceId>
756 + NudIpHandler<Ipv6, BC>
757 + DadHandler<Ipv6, BC>
758 + RsHandler<BC>
759 + SlaacHandler<BC>
760 + RouteDiscoveryHandler<BC>
761 + 's;
762 type WithIpv6DeviceConfigurationMutInner<'s>: WithIpv6DeviceConfigurationMutInner<BC, DeviceId = Self::DeviceId>
764 + 's;
765
766 fn with_ipv6_device_configuration<
769 O,
770 F: FnOnce(&Ipv6DeviceConfiguration, Self::Ipv6DeviceStateCtx<'_>) -> O,
771 >(
772 &mut self,
773 device_id: &Self::DeviceId,
774 cb: F,
775 ) -> O;
776
777 fn with_ipv6_device_configuration_mut<
779 O,
780 F: FnOnce(Self::WithIpv6DeviceConfigurationMutInner<'_>) -> O,
781 >(
782 &mut self,
783 device_id: &Self::DeviceId,
784 cb: F,
785 ) -> O;
786}
787
788pub trait Ipv6LinkLayerAddr {
790 fn as_bytes(&self) -> &[u8];
792
793 fn eui64_iid(&self) -> [u8; 8];
795}
796
797pub trait Ipv6DeviceContext<BC: IpDeviceBindingsContext<Ipv6, Self::DeviceId>>:
799 IpDeviceStateContext<Ipv6, BC>
800{
801 type LinkLayerAddr: Ipv6LinkLayerAddr;
803
804 fn get_link_layer_addr(&mut self, device_id: &Self::DeviceId) -> Option<Self::LinkLayerAddr>;
807
808 fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu);
810
811 fn with_network_learned_parameters<O, F: FnOnce(&Ipv6NetworkLearnedParameters) -> O>(
813 &mut self,
814 device_id: &Self::DeviceId,
815 cb: F,
816 ) -> O;
817
818 fn with_network_learned_parameters_mut<O, F: FnOnce(&mut Ipv6NetworkLearnedParameters) -> O>(
820 &mut self,
821 device_id: &Self::DeviceId,
822 cb: F,
823 ) -> O;
824}
825
826pub trait IpDeviceHandler<I: IpDeviceIpExt, BC>: DeviceIdContext<AnyDevice> {
828 fn is_router_device(&mut self, device_id: &Self::DeviceId) -> bool;
830
831 fn set_default_hop_limit(&mut self, device_id: &Self::DeviceId, hop_limit: NonZeroU8);
833
834 fn handle_received_dad_packet(
847 &mut self,
848 bindings_ctx: &mut BC,
849 device_id: &Self::DeviceId,
850 addr: SpecifiedAddr<I::Addr>,
851 packet_data: I::ReceivedPacketData<'_>,
852 ) -> IpAddressState;
853}
854
855impl<
856 I: IpDeviceIpExt,
857 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
858 CC: IpDeviceConfigurationContext<I, BC> + ResourceCounterContext<CC::DeviceId, IpCounters<I>>,
859> IpDeviceHandler<I, BC> for CC
860{
861 fn is_router_device(&mut self, device_id: &Self::DeviceId) -> bool {
862 is_ip_unicast_forwarding_enabled(self, device_id)
863 }
864
865 fn set_default_hop_limit(&mut self, device_id: &Self::DeviceId, hop_limit: NonZeroU8) {
866 self.with_default_hop_limit_mut(device_id, |default_hop_limit| {
867 *default_hop_limit = hop_limit
868 })
869 }
870
871 fn handle_received_dad_packet(
872 &mut self,
873 bindings_ctx: &mut BC,
874 device_id: &Self::DeviceId,
875 addr: SpecifiedAddr<I::Addr>,
876 packet_data: I::ReceivedPacketData<'_>,
877 ) -> IpAddressState {
878 let addr_id = match self.get_address_id(device_id, addr) {
879 Ok(o) => o,
880 Err(NotFoundError) => return IpAddressState::Unavailable,
881 };
882
883 let orig_state =
884 match self.with_ip_device_configuration(device_id, |_config, mut core_ctx| {
885 core_ctx.handle_incoming_packet(bindings_ctx, device_id, &addr_id, packet_data)
886 }) {
887 DadIncomingPacketResult::Assigned { should_remove } => {
888 if !should_remove {
889 return IpAddressState::Assigned;
891 } else {
892 IpAddressState::Assigned
903 }
904 }
905 DadIncomingPacketResult::Tentative { meta } => {
906 #[derive(GenericOverIp)]
907 #[generic_over_ip(I, Ip)]
908 struct Wrapped<I: IpDeviceIpExt>(I::IncomingPacketResultMeta);
909 let is_looped_back = I::map_ip_in(
910 Wrapped(meta),
911 |Wrapped(())| false,
914 |Wrapped(Ipv6PacketResultMetadata { matched_nonce })| matched_nonce,
927 );
928
929 if is_looped_back {
930 self.increment_both(device_id, |c| {
932 #[derive(GenericOverIp)]
933 #[generic_over_ip(I, Ip)]
934 struct InCounters<'a, I: IpDeviceIpExt>(
935 &'a <I::RxCounters as CounterCollectionSpec>::CounterCollection<
936 Counter,
937 >,
938 );
939 I::map_ip_in::<_, _>(
940 InCounters(&c.version_rx),
941 |_counters| {
942 unreachable!("Looped back ARP probes are dropped in ARP")
943 },
944 |InCounters(counters)| &counters.drop_looped_back_dad_probe,
945 )
946 });
947
948 return IpAddressState::Tentative;
951 } else {
952 IpAddressState::Tentative
954 }
955 }
956 DadIncomingPacketResult::Uninitialized => IpAddressState::Unavailable,
957 };
958
959 let removal_reason = match orig_state {
962 IpAddressState::Assigned => AddressRemovedReason::Forfeited,
965 IpAddressState::Tentative | IpAddressState::Unavailable => {
968 AddressRemovedReason::DadFailed
969 }
970 };
971 match del_ip_addr(
972 self,
973 bindings_ctx,
974 device_id,
975 DelIpAddr::AddressId(addr_id),
976 removal_reason,
977 ) {
978 Ok(result) => {
979 bindings_ctx.defer_removal_result(result);
980 orig_state
981 }
982 Err(NotFoundError) => {
983 IpAddressState::Unavailable
985 }
986 }
987 }
988}
989
990pub fn receive_igmp_packet<CC, BC, B, H>(
992 core_ctx: &mut CC,
993 bindings_ctx: &mut BC,
994 device: &CC::DeviceId,
995 src_ip: Ipv4SourceAddr,
996 dst_ip: SpecifiedAddr<Ipv4Addr>,
997 buffer: B,
998 info: &LocalDeliveryPacketInfo<Ipv4, H>,
999) where
1000 CC: IpDeviceConfigurationContext<Ipv4, BC>,
1001 BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1002 for<'a> CC::WithIpDeviceConfigurationInnerCtx<'a>: IpDeviceStateContext<Ipv4, BC, DeviceId = CC::DeviceId>
1003 + IgmpPacketHandler<BC, CC::DeviceId>,
1004 B: BufferMut,
1005 H: IpHeaderInfo<Ipv4>,
1006{
1007 core_ctx.with_ip_device_configuration(device, |_config, mut core_ctx| {
1008 IgmpPacketHandler::receive_igmp_packet(
1009 &mut core_ctx,
1010 bindings_ctx,
1011 device,
1012 src_ip,
1013 dst_ip,
1014 buffer,
1015 info,
1016 )
1017 })
1018}
1019
1020pub trait Ipv6DeviceHandler<BC>: IpDeviceHandler<Ipv6, BC> {
1022 type LinkLayerAddr: Ipv6LinkLayerAddr;
1024
1025 fn get_link_layer_addr(&mut self, device_id: &Self::DeviceId) -> Option<Self::LinkLayerAddr>;
1028
1029 fn set_discovered_retrans_timer(
1031 &mut self,
1032 bindings_ctx: &mut BC,
1033 device_id: &Self::DeviceId,
1034 retrans_timer: NonZeroDuration,
1035 );
1036
1037 fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu);
1039
1040 fn update_discovered_ipv6_route(
1042 &mut self,
1043 bindings_ctx: &mut BC,
1044 device_id: &Self::DeviceId,
1045 route: Ipv6DiscoveredRoute,
1046 lifetime: Option<NonZeroNdpLifetime>,
1047 );
1048
1049 fn apply_slaac_update(
1051 &mut self,
1052 bindings_ctx: &mut BC,
1053 device_id: &Self::DeviceId,
1054 prefix: Subnet<Ipv6Addr>,
1055 preferred_lifetime: Option<NonZeroNdpLifetime>,
1056 valid_lifetime: Option<NonZeroNdpLifetime>,
1057 );
1058
1059 fn receive_mld_packet<B: SplitByteSlice, H: IpHeaderInfo<Ipv6>>(
1061 &mut self,
1062 bindings_ctx: &mut BC,
1063 device: &Self::DeviceId,
1064 src_ip: Ipv6SourceAddr,
1065 dst_ip: SpecifiedAddr<Ipv6Addr>,
1066 packet: MldPacket<B>,
1067 header_info: &H,
1068 );
1069}
1070
1071impl<
1072 BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1073 CC: Ipv6DeviceContext<BC>
1074 + Ipv6DeviceConfigurationContext<BC>
1075 + ResourceCounterContext<CC::DeviceId, IpCounters<Ipv6>>,
1076> Ipv6DeviceHandler<BC> for CC
1077{
1078 type LinkLayerAddr = CC::LinkLayerAddr;
1079
1080 fn get_link_layer_addr(&mut self, device_id: &Self::DeviceId) -> Option<CC::LinkLayerAddr> {
1081 Ipv6DeviceContext::get_link_layer_addr(self, device_id)
1082 }
1083
1084 fn set_discovered_retrans_timer(
1085 &mut self,
1086 _bindings_ctx: &mut BC,
1087 device_id: &Self::DeviceId,
1088 retrans_timer: NonZeroDuration,
1089 ) {
1090 self.with_network_learned_parameters_mut(device_id, |state| {
1091 state.retrans_timer = Some(retrans_timer)
1092 })
1093 }
1094
1095 fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu) {
1096 Ipv6DeviceContext::set_link_mtu(self, device_id, mtu)
1097 }
1098
1099 fn update_discovered_ipv6_route(
1100 &mut self,
1101 bindings_ctx: &mut BC,
1102 device_id: &Self::DeviceId,
1103 route: Ipv6DiscoveredRoute,
1104 lifetime: Option<NonZeroNdpLifetime>,
1105 ) {
1106 self.with_ipv6_device_configuration(device_id, |_config, mut core_ctx| {
1107 RouteDiscoveryHandler::update_route(
1108 &mut core_ctx,
1109 bindings_ctx,
1110 device_id,
1111 route,
1112 lifetime,
1113 )
1114 })
1115 }
1116
1117 fn apply_slaac_update(
1118 &mut self,
1119 bindings_ctx: &mut BC,
1120 device_id: &Self::DeviceId,
1121 prefix: Subnet<Ipv6Addr>,
1122 preferred_lifetime: Option<NonZeroNdpLifetime>,
1123 valid_lifetime: Option<NonZeroNdpLifetime>,
1124 ) {
1125 self.with_ipv6_device_configuration(device_id, |_config, mut core_ctx| {
1126 SlaacHandler::apply_slaac_update(
1127 &mut core_ctx,
1128 bindings_ctx,
1129 device_id,
1130 prefix,
1131 preferred_lifetime,
1132 valid_lifetime,
1133 )
1134 })
1135 }
1136
1137 fn receive_mld_packet<B: SplitByteSlice, H: IpHeaderInfo<Ipv6>>(
1138 &mut self,
1139 bindings_ctx: &mut BC,
1140 device: &Self::DeviceId,
1141 src_ip: Ipv6SourceAddr,
1142 dst_ip: SpecifiedAddr<Ipv6Addr>,
1143 packet: MldPacket<B>,
1144 header_info: &H,
1145 ) {
1146 self.with_ipv6_device_configuration(device, |_config, mut core_ctx| {
1147 MldPacketHandler::receive_mld_packet(
1148 &mut core_ctx,
1149 bindings_ctx,
1150 device,
1151 src_ip,
1152 dst_ip,
1153 packet,
1154 header_info,
1155 )
1156 })
1157 }
1158}
1159
1160pub trait IpDeviceSendContext<I: IpExt, BC: TxMetadataBindingsTypes>:
1162 DeviceIdContext<AnyDevice>
1163{
1164 fn send_ip_frame<S>(
1166 &mut self,
1167 bindings_ctx: &mut BC,
1168 device_id: &Self::DeviceId,
1169 destination: IpPacketDestination<I, &Self::DeviceId>,
1170 ip_layer_metadata: DeviceIpLayerMetadata<BC>,
1171 body: S,
1172 egress_proof: ProofOfEgressCheck,
1173 ) -> Result<(), SendFrameError<S>>
1174 where
1175 S: Serializer,
1176 S::Buffer: BufferMut;
1177}
1178
1179fn enable_ipv6_device_with_config<
1180 BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1181 CC: Ipv6DeviceContext<BC>
1182 + GmpHandler<Ipv6, BC>
1183 + RsHandler<BC>
1184 + DadHandler<Ipv6, BC>
1185 + SlaacHandler<BC>,
1186>(
1187 core_ctx: &mut CC,
1188 bindings_ctx: &mut BC,
1189 device_id: &CC::DeviceId,
1190 config: &Ipv6DeviceConfiguration,
1191) {
1192 join_ip_multicast_with_config(
1194 core_ctx,
1195 bindings_ctx,
1196 device_id,
1197 Ipv6::ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS,
1198 config,
1199 );
1200 GmpHandler::gmp_handle_maybe_enabled(core_ctx, bindings_ctx, device_id);
1201
1202 core_ctx
1209 .with_address_ids(device_id, |addrs, _core_ctx| addrs.collect::<Vec<_>>())
1210 .into_iter()
1211 .for_each(|addr_id| {
1212 let needs_dad = DadHandler::initialize_duplicate_address_detection(
1213 core_ctx,
1214 bindings_ctx,
1215 device_id,
1216 &addr_id,
1217 |state| IpDeviceEvent::AddressStateChanged {
1218 device: device_id.clone(),
1219 addr: addr_id.addr().into(),
1220 state,
1221 },
1222 );
1223 match needs_dad {
1224 NeedsDad::Yes(token) => {
1225 core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1226 }
1227 NeedsDad::No => {}
1228 }
1229 });
1230
1231 if core_ctx.get_link_layer_addr(device_id).is_some() {
1234 SlaacHandler::generate_link_local_address(core_ctx, bindings_ctx, device_id);
1235 }
1236
1237 RsHandler::start_router_solicitation(core_ctx, bindings_ctx, device_id);
1238}
1239
1240fn disable_ipv6_device_with_config<
1241 BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1242 CC: Ipv6DeviceContext<BC>
1243 + GmpHandler<Ipv6, BC>
1244 + RsHandler<BC>
1245 + DadHandler<Ipv6, BC>
1246 + RouteDiscoveryHandler<BC>
1247 + SlaacHandler<BC>
1248 + NudIpHandler<Ipv6, BC>,
1249>(
1250 core_ctx: &mut CC,
1251 bindings_ctx: &mut BC,
1252 device_id: &CC::DeviceId,
1253 device_config: &Ipv6DeviceConfiguration,
1254) {
1255 NudIpHandler::flush_neighbor_table(core_ctx, bindings_ctx, device_id);
1256
1257 SlaacHandler::remove_all_slaac_addresses(core_ctx, bindings_ctx, device_id);
1258
1259 RouteDiscoveryHandler::invalidate_routes(core_ctx, bindings_ctx, device_id);
1260
1261 RsHandler::stop_router_solicitation(core_ctx, bindings_ctx, device_id);
1262
1263 core_ctx.with_network_learned_parameters_mut(device_id, |params| params.reset());
1266
1267 core_ctx
1270 .with_address_ids(device_id, |addrs, core_ctx| {
1271 addrs
1272 .map(|addr_id| {
1273 core_ctx.with_ip_address_data(
1274 device_id,
1275 &addr_id,
1276 |IpAddressData { flags: _, config }| (addr_id.clone(), *config),
1277 )
1278 })
1279 .collect::<Vec<_>>()
1280 })
1281 .into_iter()
1282 .for_each(|(addr_id, config)| {
1283 if config
1284 .is_some_and(|config| config.is_slaac() && addr_id.addr().addr().is_link_local())
1285 {
1286 del_ip_addr_inner_and_notify_handler(
1287 core_ctx,
1288 bindings_ctx,
1289 device_id,
1290 DelIpAddr::AddressId(addr_id),
1291 AddressRemovedReason::Manual,
1292 device_config,
1293 )
1294 .map(|remove_result| {
1295 bindings_ctx.defer_removal_result(remove_result);
1296 })
1297 .unwrap_or_else(|NotFoundError| {
1298 })
1302 } else {
1303 DadHandler::stop_duplicate_address_detection(
1304 core_ctx,
1305 bindings_ctx,
1306 device_id,
1307 &addr_id,
1308 );
1309 bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
1310 device: device_id.clone(),
1311 addr: addr_id.addr().into(),
1312 state: IpAddressState::Unavailable,
1313 });
1314 }
1315 });
1316
1317 GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id);
1318 leave_ip_multicast_with_config(
1319 core_ctx,
1320 bindings_ctx,
1321 device_id,
1322 Ipv6::ALL_NODES_LINK_LOCAL_MULTICAST_ADDRESS,
1323 device_config,
1324 );
1325}
1326
1327fn enable_ipv4_device_with_config<
1328 BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1329 CC: IpDeviceStateContext<Ipv4, BC> + GmpHandler<Ipv4, BC> + DadHandler<Ipv4, BC>,
1330>(
1331 core_ctx: &mut CC,
1332 bindings_ctx: &mut BC,
1333 device_id: &CC::DeviceId,
1334 config: &Ipv4DeviceConfiguration,
1335) {
1336 join_ip_multicast_with_config(
1338 core_ctx,
1339 bindings_ctx,
1340 device_id,
1341 Ipv4::ALL_SYSTEMS_MULTICAST_ADDRESS,
1342 config,
1343 );
1344 GmpHandler::gmp_handle_maybe_enabled(core_ctx, bindings_ctx, device_id);
1345 core_ctx
1346 .with_address_ids(device_id, |addrs, _core_ctx| addrs.collect::<Vec<_>>())
1347 .into_iter()
1348 .for_each(|addr_id| {
1349 let needs_dad = DadHandler::initialize_duplicate_address_detection(
1350 core_ctx,
1351 bindings_ctx,
1352 device_id,
1353 &addr_id,
1354 |state| IpDeviceEvent::AddressStateChanged {
1355 device: device_id.clone(),
1356 addr: addr_id.addr().into(),
1357 state,
1358 },
1359 );
1360 match needs_dad {
1361 NeedsDad::Yes(token) => {
1362 core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1363 }
1364 NeedsDad::No => {}
1365 }
1366 })
1367}
1368
1369fn disable_ipv4_device_with_config<
1370 BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1371 CC: IpDeviceStateContext<Ipv4, BC> + GmpHandler<Ipv4, BC> + NudIpHandler<Ipv4, BC>,
1372>(
1373 core_ctx: &mut CC,
1374 bindings_ctx: &mut BC,
1375 device_id: &CC::DeviceId,
1376 config: &Ipv4DeviceConfiguration,
1377) {
1378 NudIpHandler::flush_neighbor_table(core_ctx, bindings_ctx, device_id);
1379 GmpHandler::gmp_handle_disabled(core_ctx, bindings_ctx, device_id);
1380 leave_ip_multicast_with_config(
1381 core_ctx,
1382 bindings_ctx,
1383 device_id,
1384 Ipv4::ALL_SYSTEMS_MULTICAST_ADDRESS,
1385 config,
1386 );
1387 core_ctx.with_address_ids(device_id, |addrs, _core_ctx| {
1388 addrs.for_each(|addr| {
1389 bindings_ctx.on_event(IpDeviceEvent::AddressStateChanged {
1390 device: device_id.clone(),
1391 addr: addr.addr().into(),
1392 state: IpAddressState::Unavailable,
1393 });
1394 })
1395 })
1396}
1397
1398pub fn get_ipv4_addr_subnet<BT: IpDeviceStateBindingsTypes, CC: IpDeviceStateContext<Ipv4, BT>>(
1400 core_ctx: &mut CC,
1401 device_id: &CC::DeviceId,
1402) -> Option<AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>> {
1403 core_ctx.with_address_ids(device_id, |mut addrs, _core_ctx| addrs.next().map(|a| a.addr_sub()))
1404}
1405
1406pub fn get_ipv6_hop_limit<BT: IpDeviceStateBindingsTypes, CC: IpDeviceStateContext<Ipv6, BT>>(
1408 core_ctx: &mut CC,
1409 device: &CC::DeviceId,
1410) -> NonZeroU8 {
1411 core_ctx.with_default_hop_limit(device, Clone::clone)
1412}
1413
1414pub fn is_ip_unicast_forwarding_enabled<
1416 I: IpDeviceIpExt,
1417 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1418 CC: IpDeviceConfigurationContext<I, BC>,
1419>(
1420 core_ctx: &mut CC,
1421 device_id: &CC::DeviceId,
1422) -> bool {
1423 core_ctx.with_ip_device_configuration(device_id, |state, _ctx| {
1424 AsRef::<IpDeviceConfiguration>::as_ref(state).unicast_forwarding_enabled
1425 })
1426}
1427
1428pub fn is_ip_multicast_forwarding_enabled<
1430 I: IpDeviceIpExt,
1431 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1432 CC: IpDeviceConfigurationContext<I, BC>,
1433>(
1434 core_ctx: &mut CC,
1435 device_id: &CC::DeviceId,
1436) -> bool {
1437 core_ctx.with_ip_device_configuration(device_id, |state, _ctx| {
1438 AsRef::<IpDeviceConfiguration>::as_ref(state).multicast_forwarding_enabled
1439 })
1440}
1441
1442pub fn join_ip_multicast_with_config<
1449 I: IpDeviceIpExt,
1450 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1451 CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC>,
1452>(
1453 core_ctx: &mut CC,
1454 bindings_ctx: &mut BC,
1455 device_id: &CC::DeviceId,
1456 multicast_addr: MulticastAddr<I::Addr>,
1457 _config: &I::Configuration,
1458) {
1459 match core_ctx.gmp_join_group(bindings_ctx, device_id, multicast_addr) {
1460 GroupJoinResult::Joined(()) => {
1461 core_ctx.join_link_multicast_group(bindings_ctx, device_id, multicast_addr)
1462 }
1463 GroupJoinResult::AlreadyMember => {}
1464 }
1465}
1466
1467pub fn join_ip_multicast<
1478 I: IpDeviceIpExt,
1479 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1480 CC: IpDeviceConfigurationContext<I, BC>,
1481>(
1482 core_ctx: &mut CC,
1483 bindings_ctx: &mut BC,
1484 device_id: &CC::DeviceId,
1485 multicast_addr: MulticastAddr<I::Addr>,
1486) {
1487 core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
1488 join_ip_multicast_with_config(
1489 &mut core_ctx,
1490 bindings_ctx,
1491 device_id,
1492 multicast_addr,
1493 config,
1494 )
1495 })
1496}
1497
1498pub fn leave_ip_multicast_with_config<
1505 I: IpDeviceIpExt,
1506 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1507 CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC>,
1508>(
1509 core_ctx: &mut CC,
1510 bindings_ctx: &mut BC,
1511 device_id: &CC::DeviceId,
1512 multicast_addr: MulticastAddr<I::Addr>,
1513 _config: &I::Configuration,
1514) {
1515 match core_ctx.gmp_leave_group(bindings_ctx, device_id, multicast_addr) {
1516 GroupLeaveResult::Left(()) => {
1517 core_ctx.leave_link_multicast_group(bindings_ctx, device_id, multicast_addr)
1518 }
1519 GroupLeaveResult::StillMember => {}
1520 GroupLeaveResult::NotMember => panic!(
1521 "attempted to leave IP multicast group we were not a member of: {}",
1522 multicast_addr,
1523 ),
1524 }
1525}
1526
1527pub fn leave_ip_multicast<
1542 I: IpDeviceIpExt,
1543 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1544 CC: IpDeviceConfigurationContext<I, BC>,
1545>(
1546 core_ctx: &mut CC,
1547 bindings_ctx: &mut BC,
1548 device_id: &CC::DeviceId,
1549 multicast_addr: MulticastAddr<I::Addr>,
1550) {
1551 core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
1552 leave_ip_multicast_with_config(
1553 &mut core_ctx,
1554 bindings_ctx,
1555 device_id,
1556 multicast_addr,
1557 config,
1558 )
1559 })
1560}
1561
1562pub fn add_ip_addr_subnet_with_config<
1569 I: IpDeviceIpExt,
1570 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1571 CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC> + DadHandler<I, BC>,
1572>(
1573 core_ctx: &mut CC,
1574 bindings_ctx: &mut BC,
1575 device_id: &CC::DeviceId,
1576 addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
1577 addr_config: I::AddressConfig<BC::Instant>,
1578 _device_config: &I::Configuration,
1579) -> Result<CC::AddressId, ExistsError> {
1580 info!("adding addr {addr_sub:?} config {addr_config:?} to device {device_id:?}");
1581 let CommonAddressProperties { valid_until, preferred_lifetime } =
1582 I::get_common_props(&addr_config);
1583 let addr_id = core_ctx.add_ip_address(device_id, addr_sub, addr_config)?;
1584 assert_eq!(addr_id.addr().addr(), addr_sub.addr().get());
1585
1586 let ip_enabled =
1587 core_ctx.with_ip_device_flags(device_id, |IpDeviceFlags { ip_enabled }| *ip_enabled);
1588
1589 let needs_dad = if ip_enabled {
1590 DadHandler::initialize_duplicate_address_detection(
1591 core_ctx,
1592 bindings_ctx,
1593 device_id,
1594 &addr_id,
1595 |state| IpDeviceEvent::AddressAdded {
1596 device: device_id.clone(),
1597 addr: addr_sub.to_witness(),
1598 state,
1599 valid_until,
1600 preferred_lifetime,
1601 },
1602 )
1603 } else {
1604 bindings_ctx.on_event(IpDeviceEvent::AddressAdded {
1611 device: device_id.clone(),
1612 addr: addr_sub.to_witness(),
1613 state: IpAddressState::Unavailable,
1614 valid_until,
1615 preferred_lifetime,
1616 });
1617 NeedsDad::No
1618 };
1619
1620 match needs_dad {
1621 NeedsDad::Yes(token) => {
1622 core_ctx.start_duplicate_address_detection(bindings_ctx, token);
1623 }
1624 NeedsDad::No => {}
1625 }
1626
1627 Ok(addr_id)
1628}
1629
1630pub trait IpAddressRemovalHandler<I: IpDeviceIpExt, BC: InstantBindingsTypes>:
1632 DeviceIdContext<AnyDevice>
1633{
1634 fn on_address_removed(
1637 &mut self,
1638 bindings_ctx: &mut BC,
1639 device_id: &Self::DeviceId,
1640 addr_sub: AddrSubnet<I::Addr, I::AssignedWitness>,
1641 config: I::AddressConfig<BC::Instant>,
1642 reason: AddressRemovedReason,
1643 );
1644}
1645
1646impl<CC: DeviceIdContext<AnyDevice>, BC: InstantBindingsTypes> IpAddressRemovalHandler<Ipv4, BC>
1648 for CC
1649{
1650 fn on_address_removed(
1651 &mut self,
1652 _bindings_ctx: &mut BC,
1653 _device_id: &Self::DeviceId,
1654 _addr_sub: AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>,
1655 _config: Ipv4AddrConfig<BC::Instant>,
1656 _reason: AddressRemovedReason,
1657 ) {
1658 }
1660}
1661
1662impl<CC: SlaacHandler<BC>, BC: InstantContext> IpAddressRemovalHandler<Ipv6, BC> for CC {
1664 fn on_address_removed(
1665 &mut self,
1666 bindings_ctx: &mut BC,
1667 device_id: &Self::DeviceId,
1668 addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
1669 config: Ipv6AddrConfig<BC::Instant>,
1670 reason: AddressRemovedReason,
1671 ) {
1672 match config {
1673 Ipv6AddrConfig::Slaac(config) => SlaacHandler::on_address_removed(
1674 self,
1675 bindings_ctx,
1676 device_id,
1677 addr_sub,
1678 config,
1679 reason,
1680 ),
1681 Ipv6AddrConfig::Manual(_manual_config) => (),
1682 }
1683 }
1684}
1685
1686#[allow(missing_docs)]
1688pub enum DelIpAddr<Id, A> {
1689 SpecifiedAddr(SpecifiedAddr<A>),
1690 AddressId(Id),
1691}
1692
1693impl<Id: IpAddressId<A>, A: IpAddress<Version: AssignedAddrIpExt>> Display for DelIpAddr<Id, A> {
1694 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1695 match self {
1696 DelIpAddr::SpecifiedAddr(addr) => write!(f, "{}", *addr),
1697 DelIpAddr::AddressId(id) => write!(f, "{}", id.addr()),
1698 }
1699 }
1700}
1701
1702pub fn del_ip_addr_inner<
1705 I: IpDeviceIpExt,
1706 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1707 CC: IpDeviceStateContext<I, BC> + GmpHandler<I, BC> + DadHandler<I, BC>,
1708>(
1709 core_ctx: &mut CC,
1710 bindings_ctx: &mut BC,
1711 device_id: &CC::DeviceId,
1712 addr: DelIpAddr<CC::AddressId, I::Addr>,
1713 reason: AddressRemovedReason,
1714 _config: &I::Configuration,
1716) -> Result<
1717 (
1718 AddrSubnet<I::Addr, I::AssignedWitness>,
1719 I::AddressConfig<BC::Instant>,
1720 RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>,
1721 ),
1722 NotFoundError,
1723> {
1724 let addr_id = match addr {
1725 DelIpAddr::SpecifiedAddr(addr) => core_ctx.get_address_id(device_id, addr)?,
1726 DelIpAddr::AddressId(id) => id,
1727 };
1728 DadHandler::stop_duplicate_address_detection(core_ctx, bindings_ctx, device_id, &addr_id);
1729 let addr_config = core_ctx
1733 .with_ip_address_data_mut(device_id, &addr_id, |addr_data| addr_data.config.take())
1734 .ok_or(NotFoundError)?;
1735
1736 let addr_sub = addr_id.addr_sub();
1737 let result = core_ctx.remove_ip_address(device_id, addr_id);
1738
1739 bindings_ctx.on_event(IpDeviceEvent::AddressRemoved {
1740 device: device_id.clone(),
1741 addr: addr_sub.addr().into(),
1742 reason,
1743 });
1744
1745 Ok((addr_sub, addr_config, result))
1746}
1747
1748fn del_ip_addr<
1750 I: IpDeviceIpExt,
1751 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1752 CC: IpDeviceConfigurationContext<I, BC>,
1753>(
1754 core_ctx: &mut CC,
1755 bindings_ctx: &mut BC,
1756 device_id: &CC::DeviceId,
1757 addr: DelIpAddr<CC::AddressId, I::Addr>,
1758 reason: AddressRemovedReason,
1759) -> Result<RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>, NotFoundError> {
1760 info!("removing addr {addr} from device {device_id:?}");
1761 core_ctx.with_ip_device_configuration(device_id, |config, mut core_ctx| {
1762 del_ip_addr_inner_and_notify_handler(
1763 &mut core_ctx,
1764 bindings_ctx,
1765 device_id,
1766 addr,
1767 reason,
1768 config,
1769 )
1770 })
1771}
1772
1773fn del_ip_addr_inner_and_notify_handler<
1776 I: IpDeviceIpExt,
1777 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1778 CC: IpDeviceStateContext<I, BC>
1779 + GmpHandler<I, BC>
1780 + DadHandler<I, BC>
1781 + IpAddressRemovalHandler<I, BC>,
1782>(
1783 core_ctx: &mut CC,
1784 bindings_ctx: &mut BC,
1785 device_id: &CC::DeviceId,
1786 addr: DelIpAddr<CC::AddressId, I::Addr>,
1787 reason: AddressRemovedReason,
1788 config: &I::Configuration,
1789) -> Result<RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC>, NotFoundError> {
1790 del_ip_addr_inner(core_ctx, bindings_ctx, device_id, addr, reason, config).map(
1791 |(addr_sub, config, result)| {
1792 core_ctx.on_address_removed(bindings_ctx, device_id, addr_sub, config, reason);
1793 result
1794 },
1795 )
1796}
1797
1798pub fn is_ip_device_enabled<
1800 I: IpDeviceIpExt,
1801 BC: IpDeviceBindingsContext<I, CC::DeviceId>,
1802 CC: IpDeviceStateContext<I, BC>,
1803>(
1804 core_ctx: &mut CC,
1805 device_id: &CC::DeviceId,
1806) -> bool {
1807 core_ctx.with_ip_device_flags(device_id, |flags| flags.ip_enabled)
1808}
1809
1810pub fn clear_ipv4_device_state<
1812 BC: IpDeviceBindingsContext<Ipv4, CC::DeviceId>,
1813 CC: IpDeviceConfigurationContext<Ipv4, BC>,
1814>(
1815 core_ctx: &mut CC,
1816 bindings_ctx: &mut BC,
1817 device_id: &CC::DeviceId,
1818) {
1819 core_ctx.with_ip_device_configuration_mut(device_id, |mut core_ctx| {
1820 let ip_enabled = core_ctx.with_configuration_and_flags_mut(device_id, |_config, flags| {
1821 let IpDeviceFlags { ip_enabled } = flags;
1824 core::mem::replace(ip_enabled, false)
1825 });
1826
1827 let (config, mut core_ctx) = core_ctx.ip_device_configuration_and_ctx();
1828 let core_ctx = &mut core_ctx;
1829 if ip_enabled {
1830 disable_ipv4_device_with_config(core_ctx, bindings_ctx, device_id, config);
1831 }
1832 })
1833}
1834
1835pub fn clear_ipv6_device_state<
1837 BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1838 CC: Ipv6DeviceConfigurationContext<BC>,
1839>(
1840 core_ctx: &mut CC,
1841 bindings_ctx: &mut BC,
1842 device_id: &CC::DeviceId,
1843) {
1844 core_ctx.with_ipv6_device_configuration_mut(device_id, |mut core_ctx| {
1845 let ip_enabled = core_ctx.with_configuration_and_flags_mut(device_id, |_config, flags| {
1846 let IpDeviceFlags { ip_enabled } = flags;
1849 core::mem::replace(ip_enabled, false)
1850 });
1851
1852 let (config, mut core_ctx) = core_ctx.ipv6_device_configuration_and_ctx();
1853 let core_ctx = &mut core_ctx;
1854 if ip_enabled {
1855 disable_ipv6_device_with_config(core_ctx, bindings_ctx, device_id, config);
1856 }
1857 })
1858}
1859
1860pub fn on_arp_packet<CC, BC>(
1864 core_ctx: &mut CC,
1865 bindings_ctx: &mut BC,
1866 device_id: &CC::DeviceId,
1867 sender_addr: Ipv4Addr,
1868 target_addr: Ipv4Addr,
1869 is_arp_probe: bool,
1870) -> bool
1871where
1872 CC: IpDeviceHandler<Ipv4, BC> + IpDeviceStateContext<Ipv4, BC>,
1873 BC: IpDeviceStateBindingsTypes,
1874{
1875 if let Some(sender_addr) = SpecifiedAddr::new(sender_addr) {
1881 let sender_addr_state = IpDeviceHandler::<Ipv4, _>::handle_received_dad_packet(
1882 core_ctx,
1883 bindings_ctx,
1884 &device_id,
1885 sender_addr,
1886 Ipv4DadAddressInfo::SourceAddr,
1887 );
1888 match sender_addr_state {
1889 IpAddressState::Assigned => {
1898 info!("DAD received conflicting ARP packet for assigned addr=({sender_addr})");
1899 }
1900 s @ IpAddressState::Tentative | s @ IpAddressState::Unavailable => {
1901 debug!("DAD received conflicting ARP packet for addr=({sender_addr}), state={s:?}");
1902 }
1903 }
1904 }
1905
1906 let Some(target_addr) = SpecifiedAddr::new(target_addr) else {
1907 return false;
1908 };
1909
1910 if is_arp_probe {
1916 let target_addr_state = IpDeviceHandler::<Ipv4, _>::handle_received_dad_packet(
1917 core_ctx,
1918 bindings_ctx,
1919 &device_id,
1920 target_addr,
1921 Ipv4DadAddressInfo::TargetAddr,
1922 );
1923 let assigned = match target_addr_state {
1924 IpAddressState::Assigned => true,
1927 s @ IpAddressState::Tentative | s @ IpAddressState::Unavailable => {
1928 debug!("DAD received conflicting ARP packet for addr=({target_addr}), state={s:?}");
1929 false
1930 }
1931 };
1932 assigned
1933 } else {
1934 let addr_id = match core_ctx.get_address_id(device_id, target_addr) {
1937 Ok(o) => o,
1938 Err(NotFoundError) => return false,
1939 };
1940
1941 core_ctx.with_ip_address_data(
1942 device_id,
1943 &addr_id,
1944 |IpAddressData { flags: IpAddressFlags { assigned }, config: _ }| *assigned,
1945 )
1946 }
1947}
1948
1949#[cfg(any(test, feature = "testutils"))]
1950pub(crate) mod testutil {
1951 use alloc::boxed::Box;
1952
1953 use crate::device::IpAddressFlags;
1954
1955 use super::*;
1956
1957 pub fn with_assigned_ipv4_addr_subnets<
1960 BT: IpDeviceStateBindingsTypes,
1961 CC: IpDeviceStateContext<Ipv4, BT>,
1962 O,
1963 F: FnOnce(Box<dyn Iterator<Item = AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>> + '_>) -> O,
1964 >(
1965 core_ctx: &mut CC,
1966 device_id: &CC::DeviceId,
1967 cb: F,
1968 ) -> O {
1969 core_ctx.with_address_ids(device_id, |addrs, _core_ctx| {
1970 cb(Box::new(addrs.map(|a| a.addr_sub())))
1971 })
1972 }
1973
1974 pub fn with_assigned_ipv6_addr_subnets<
1986 BC: IpDeviceBindingsContext<Ipv6, CC::DeviceId>,
1987 CC: Ipv6DeviceContext<BC>,
1988 O,
1989 F: FnOnce(Box<dyn Iterator<Item = AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>> + '_>) -> O,
1990 >(
1991 core_ctx: &mut CC,
1992 device_id: &CC::DeviceId,
1993 cb: F,
1994 ) -> O {
1995 core_ctx.with_address_ids(device_id, |addrs, core_ctx| {
1996 cb(Box::new(addrs.filter_map(|addr_id| {
1997 core_ctx
1998 .with_ip_address_data(
1999 device_id,
2000 &addr_id,
2001 |IpAddressData { flags: IpAddressFlags { assigned }, config: _ }| *assigned,
2002 )
2003 .then(|| addr_id.addr_sub())
2004 })))
2005 })
2006 }
2007}