netstack3_core/ip/
device.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! The integrations for protocols built on top of an IP device.
6
7use core::borrow::Borrow;
8use core::convert::Infallible as Never;
9use core::marker::PhantomData;
10use core::num::NonZeroU8;
11use core::ops::{Deref as _, DerefMut as _};
12use core::sync::atomic::AtomicU16;
13
14use lock_order::lock::{LockLevelFor, UnlockedAccess};
15use lock_order::relation::LockBefore;
16use log::debug;
17use net_types::ip::{AddrSubnet, Ip, IpMarked, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr, Mtu};
18use net_types::{LinkLocalUnicastAddr, MulticastAddr, SpecifiedAddr, UnicastAddr, Witness as _};
19use netstack3_base::{
20    AnyDevice, CoreEventContext, CoreTimerContext, CounterContext, DeviceIdContext, ExistsError,
21    IpDeviceAddr, IpDeviceAddressIdContext, Ipv4DeviceAddr, Ipv6DeviceAddr, NotFoundError,
22    RemoveResourceResultWithContext, ResourceCounterContext,
23};
24use netstack3_device::{DeviceId, WeakDeviceId};
25use netstack3_filter::FilterImpl;
26use netstack3_ip::device::{
27    self, add_ip_addr_subnet_with_config, del_ip_addr_inner, get_ipv6_hop_limit,
28    is_ip_device_enabled, is_ip_multicast_forwarding_enabled, is_ip_unicast_forwarding_enabled,
29    join_ip_multicast_with_config, leave_ip_multicast_with_config, AddressRemovedReason,
30    DadAddressContext, DadAddressStateRef, DadContext, DadEvent, DadStateRef, DadTimerId,
31    DefaultHopLimit, DelIpAddr, DualStackIpDeviceState, IpAddressIdSpec, IpAddressIdSpecContext,
32    IpAddressState, IpDeviceAddresses, IpDeviceConfiguration, IpDeviceEvent, IpDeviceFlags,
33    IpDeviceIpExt, IpDeviceMulticastGroups, IpDeviceStateBindingsTypes, IpDeviceStateContext,
34    IpDeviceStateIpExt, IpDeviceTimerId, Ipv4AddressEntry, Ipv4AddressState,
35    Ipv4DeviceConfiguration, Ipv6AddrConfig, Ipv6AddrSlaacConfig, Ipv6AddressEntry,
36    Ipv6AddressFlags, Ipv6AddressState, Ipv6DadState, Ipv6DeviceConfiguration, Ipv6DeviceTimerId,
37    Ipv6DiscoveredRoute, Ipv6DiscoveredRoutesContext, Ipv6NetworkLearnedParameters,
38    Ipv6RouteDiscoveryContext, Ipv6RouteDiscoveryState, RsContext, RsState, RsTimerId,
39    SlaacAddressEntry, SlaacAddressEntryMut, SlaacAddresses, SlaacConfigAndState, SlaacContext,
40    SlaacCounters, SlaacState, WeakAddressId,
41};
42use netstack3_ip::gmp::{
43    GmpGroupState, GmpState, GmpStateRef, IgmpContext, IgmpContextMarker, IgmpSendContext,
44    IgmpStateContext, IgmpTypeLayout, MldContext, MldContextMarker, MldSendContext,
45    MldStateContext, MldTypeLayout, MulticastGroupSet,
46};
47use netstack3_ip::nud::{self, ConfirmationFlags, NudCounters, NudIpHandler};
48use netstack3_ip::{
49    self as ip, AddableMetric, AddressStatus, FilterHandlerProvider, IpDeviceContext,
50    IpDeviceEgressStateContext, IpDeviceIngressStateContext, IpLayerIpExt, IpSasHandler,
51    IpSendFrameError, Ipv4PresentAddressStatus, DEFAULT_TTL,
52};
53use packet::{EmptyBuf, InnerPacketBuilder, Serializer};
54use packet_formats::icmp::ndp::options::{NdpNonce, NdpOptionBuilder};
55use packet_formats::icmp::ndp::{NeighborSolicitation, OptionSequenceBuilder, RouterSolicitation};
56use packet_formats::icmp::IcmpZeroCode;
57
58use crate::context::prelude::*;
59use crate::context::WrapLockLevel;
60use crate::{BindingsContext, BindingsTypes, CoreCtx, IpExt};
61
62pub struct SlaacAddrs<'a, BC: BindingsContext> {
63    pub(crate) core_ctx: CoreCtxWithIpDeviceConfiguration<
64        'a,
65        &'a Ipv6DeviceConfiguration,
66        WrapLockLevel<crate::lock_ordering::Ipv6DeviceSlaac>,
67        BC,
68    >,
69    pub(crate) device_id: DeviceId<BC>,
70    pub(crate) config: &'a Ipv6DeviceConfiguration,
71}
72
73/// Provides an Iterator for `SlaacAddrs` to implement `SlaacAddresses`.
74///
75/// Note that we use concrete types here instead of going through traits because
76/// it's the only way to satisfy the GAT bounds on `SlaacAddresses`' associated
77/// type.
78pub struct SlaacAddrsIter<'x, BC: BindingsContext> {
79    core_ctx: CoreCtx<'x, BC, WrapLockLevel<crate::lock_ordering::IpDeviceAddresses<Ipv6>>>,
80    addrs: ip::device::AddressIdIter<'x, Ipv6, BC>,
81    device_id: &'x DeviceId<BC>,
82}
83
84impl<'x, BC> Iterator for SlaacAddrsIter<'x, BC>
85where
86    BC: BindingsContext,
87{
88    type Item = SlaacAddressEntry<BC::Instant>;
89    fn next(&mut self) -> Option<Self::Item> {
90        let Self { core_ctx, addrs, device_id } = self;
91        // NB: This form is equivalent to using the `filter_map` combinator but
92        // keeps the type signature simple.
93        addrs.by_ref().find_map(|addr_id| {
94            device::IpDeviceAddressContext::<Ipv6, _>::with_ip_address_state(
95                core_ctx,
96                device_id,
97                &addr_id,
98                |Ipv6AddressState { flags: Ipv6AddressFlags { assigned: _ }, config }| {
99                    let addr_sub = addr_id.addr_sub();
100                    match config {
101                        Some(Ipv6AddrConfig::Slaac(config)) => {
102                            Some(SlaacAddressEntry { addr_sub: *addr_sub, config: *config })
103                        }
104                        None | Some(Ipv6AddrConfig::Manual(_)) => None,
105                    }
106                },
107            )
108        })
109    }
110}
111
112impl<'a, BC: BindingsContext> CounterContext<SlaacCounters> for SlaacAddrs<'a, BC> {
113    fn counters(&self) -> &SlaacCounters {
114        &self
115            .core_ctx
116            .core_ctx
117            .unlocked_access::<crate::lock_ordering::UnlockedState>()
118            .ipv6
119            .slaac_counters
120    }
121}
122
123impl<'a, BC: BindingsContext> SlaacAddresses<BC> for SlaacAddrs<'a, BC> {
124    fn for_each_addr_mut<F: FnMut(SlaacAddressEntryMut<'_, BC::Instant>)>(&mut self, mut cb: F) {
125        let SlaacAddrs { core_ctx, device_id, config: _ } = self;
126        let CoreCtxWithIpDeviceConfiguration { config: _, core_ctx } = core_ctx;
127        let mut state = crate::device::integration::ip_device_state(core_ctx, device_id);
128        let (addrs, mut locked) =
129            state.read_lock_and::<crate::lock_ordering::IpDeviceAddresses<Ipv6>>();
130        addrs.iter().for_each(|entry| {
131            let addr_sub = *entry.addr_sub();
132            let mut locked = locked.adopt(&**entry);
133            let mut state = locked
134                .write_lock_with::<crate::lock_ordering::Ipv6DeviceAddressState, _>(|c| c.right());
135            let Ipv6AddressState { config, flags: Ipv6AddressFlags { assigned: _ } } = &mut *state;
136
137            match config {
138                Some(Ipv6AddrConfig::Slaac(config)) => {
139                    cb(SlaacAddressEntryMut { addr_sub, config })
140                }
141                None | Some(Ipv6AddrConfig::Manual(_)) => {}
142            }
143        })
144    }
145
146    type AddrsIter<'x> = SlaacAddrsIter<'x, BC>;
147
148    fn with_addrs<O, F: FnOnce(Self::AddrsIter<'_>) -> O>(&mut self, cb: F) -> O {
149        let SlaacAddrs { core_ctx, device_id, config: _ } = self;
150        device::IpDeviceStateContext::<Ipv6, BC>::with_address_ids(
151            core_ctx,
152            device_id,
153            |addrs, core_ctx| {
154                cb(SlaacAddrsIter { core_ctx: core_ctx.as_owned(), addrs, device_id })
155            },
156        )
157    }
158
159    fn add_addr_sub_and_then<O, F: FnOnce(SlaacAddressEntryMut<'_, BC::Instant>, &mut BC) -> O>(
160        &mut self,
161        bindings_ctx: &mut BC,
162        add_addr_sub: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
163        slaac_config: Ipv6AddrSlaacConfig<BC::Instant>,
164        and_then: F,
165    ) -> Result<O, ExistsError> {
166        let SlaacAddrs { core_ctx, device_id, config } = self;
167
168        add_ip_addr_subnet_with_config::<Ipv6, _, _>(
169            core_ctx,
170            bindings_ctx,
171            device_id,
172            add_addr_sub.to_witness(),
173            Ipv6AddrConfig::Slaac(slaac_config),
174            config,
175        )
176        .map(|entry| {
177            let addr_sub = entry.addr_sub();
178            let mut locked = core_ctx.core_ctx.adopt(entry.deref());
179            let mut state = locked
180                .write_lock_with::<crate::lock_ordering::Ipv6DeviceAddressState, _>(|c| c.right());
181            let Ipv6AddressState { config, flags: _ } = &mut *state;
182            let config = assert_matches::assert_matches!(
183                config,
184                Some(Ipv6AddrConfig::Slaac(c)) => c
185            );
186            and_then(SlaacAddressEntryMut { addr_sub: *addr_sub, config }, bindings_ctx)
187        })
188    }
189
190    fn remove_addr(
191        &mut self,
192        bindings_ctx: &mut BC,
193        addr: &Ipv6DeviceAddr,
194    ) -> Result<
195        (AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>, Ipv6AddrSlaacConfig<BC::Instant>),
196        NotFoundError,
197    > {
198        let SlaacAddrs { core_ctx, device_id, config } = self;
199        del_ip_addr_inner::<Ipv6, _, _>(
200            core_ctx,
201            bindings_ctx,
202            device_id,
203            DelIpAddr::SpecifiedAddr(addr.into_specified()),
204            AddressRemovedReason::Manual,
205            config,
206        )
207        .map(|(addr_sub, config, result)| {
208            assert_eq!(&addr_sub.addr(), addr);
209            bindings_ctx.defer_removal_result(result);
210            match config {
211                Ipv6AddrConfig::Slaac(config) => (addr_sub, config),
212                Ipv6AddrConfig::Manual(_manual_config) => {
213                    unreachable!(
214                        "address {addr_sub} on device {device_id:?} should have been a SLAAC \
215                        address; config = {config:?}",
216                    );
217                }
218            }
219        })
220    }
221}
222
223impl<BT: BindingsTypes, L> IgmpContextMarker for CoreCtx<'_, BT, L> {}
224
225impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceGmp<Ipv4>>>
226    IgmpStateContext<BC> for CoreCtx<'_, BC, L>
227{
228    fn with_igmp_state<
229        O,
230        F: FnOnce(
231            &MulticastGroupSet<Ipv4Addr, GmpGroupState<Ipv4, BC>>,
232            &GmpState<Ipv4, IgmpTypeLayout, BC>,
233        ) -> O,
234    >(
235        &mut self,
236        device: &Self::DeviceId,
237        cb: F,
238    ) -> O {
239        let mut state = crate::device::integration::ip_device_state(self, device);
240        let state = state.read_lock::<crate::lock_ordering::IpDeviceGmp<Ipv4>>();
241        let IpDeviceMulticastGroups { groups, gmp, .. } = &*state;
242        cb(groups, gmp)
243    }
244}
245
246impl<BT: BindingsTypes, L> MldContextMarker for CoreCtx<'_, BT, L> {}
247
248impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceGmp<Ipv6>>>
249    MldStateContext<BC> for CoreCtx<'_, BC, L>
250{
251    fn with_mld_state<
252        O,
253        F: FnOnce(
254            &MulticastGroupSet<Ipv6Addr, GmpGroupState<Ipv6, BC>>,
255            &GmpState<Ipv6, MldTypeLayout, BC>,
256        ) -> O,
257    >(
258        &mut self,
259        device: &Self::DeviceId,
260        cb: F,
261    ) -> O {
262        let mut state = crate::device::integration::ip_device_state(self, device);
263        let state = state.read_lock::<crate::lock_ordering::IpDeviceGmp<Ipv6>>();
264        let IpDeviceMulticastGroups { groups, gmp, .. } = &*state;
265        cb(groups, gmp)
266    }
267}
268
269/// Iterator over a device and its status for an address.
270///
271/// This is functionally identical to using `Iterator::filter_map` on the
272/// provided devices and yielding devices with the address assigned (and the
273/// status), but is named so that it can be used as an associated type.
274pub struct FilterPresentWithDevices<
275    I: IpLayerIpExt,
276    Devices: Iterator<Item = Accessor::DeviceId>,
277    Accessor: DeviceIdContext<AnyDevice>,
278    BT,
279> {
280    devices: Devices,
281    addr: SpecifiedAddr<I::Addr>,
282    state_accessor: Accessor,
283    assignment_state: fn(
284        &mut Accessor,
285        &Accessor::DeviceId,
286        SpecifiedAddr<I::Addr>,
287    ) -> AddressStatus<I::AddressStatus>,
288    _marker: PhantomData<BT>,
289}
290
291impl<
292        I: IpLayerIpExt,
293        Devices: Iterator<Item = Accessor::DeviceId>,
294        Accessor: DeviceIdContext<AnyDevice>,
295        BT,
296    > FilterPresentWithDevices<I, Devices, Accessor, BT>
297{
298    fn new(
299        devices: Devices,
300        state_accessor: Accessor,
301        assignment_state: fn(
302            &mut Accessor,
303            &Accessor::DeviceId,
304            SpecifiedAddr<I::Addr>,
305        ) -> AddressStatus<I::AddressStatus>,
306        addr: SpecifiedAddr<I::Addr>,
307    ) -> Self {
308        Self { devices, addr, state_accessor, assignment_state, _marker: PhantomData }
309    }
310}
311
312impl<
313        's,
314        BT: IpDeviceStateBindingsTypes,
315        I: Ip + IpLayerIpExt + IpDeviceIpExt,
316        Devices: Iterator<Item = Accessor::DeviceId>,
317        Accessor: IpDeviceStateContext<I, BT>,
318    > Iterator for FilterPresentWithDevices<I, Devices, Accessor, BT>
319where
320    <I as IpDeviceIpExt>::State<BT>: 's,
321{
322    type Item = (Accessor::DeviceId, I::AddressStatus);
323    fn next(&mut self) -> Option<Self::Item> {
324        let Self { devices, addr, state_accessor, assignment_state, _marker } = self;
325        devices
326            .filter_map(|d| match assignment_state(state_accessor, &d, *addr) {
327                AddressStatus::Present(status) => Some((d, status)),
328                AddressStatus::Unassigned => None,
329            })
330            .next()
331    }
332}
333
334impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpState<Ipv4>>>
335    IpDeviceEgressStateContext<Ipv4> for CoreCtx<'_, BC, L>
336{
337    fn with_next_packet_id<O, F: FnOnce(&AtomicU16) -> O>(&self, cb: F) -> O {
338        cb(&self.unlocked_access::<crate::lock_ordering::UnlockedState>().ipv4.next_packet_id)
339    }
340
341    fn get_local_addr_for_remote(
342        &mut self,
343        device_id: &Self::DeviceId,
344        remote: Option<SpecifiedAddr<Ipv4Addr>>,
345    ) -> Option<IpDeviceAddr<Ipv4Addr>> {
346        IpSasHandler::<Ipv4, _>::get_local_addr_for_remote(self, device_id, remote)
347    }
348
349    fn get_hop_limit(&mut self, _device_id: &Self::DeviceId) -> NonZeroU8 {
350        DEFAULT_TTL
351    }
352}
353
354impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceGmp<Ipv4>>>
355    IpDeviceIngressStateContext<Ipv4> for CoreCtx<'_, BC, L>
356{
357    fn address_status_for_device(
358        &mut self,
359        dst_ip: SpecifiedAddr<Ipv4Addr>,
360        device_id: &Self::DeviceId,
361    ) -> AddressStatus<Ipv4PresentAddressStatus> {
362        AddressStatus::from_context_addr_v4(self, device_id, dst_ip)
363    }
364}
365
366impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceConfiguration<Ipv4>>>
367    IpDeviceContext<Ipv4> for CoreCtx<'_, BC, L>
368{
369    fn is_ip_device_enabled(&mut self, device_id: &Self::DeviceId) -> bool {
370        is_ip_device_enabled::<Ipv4, _, _>(self, device_id)
371    }
372
373    type DeviceAndAddressStatusIter<'a> = FilterPresentWithDevices<
374        Ipv4,
375        <Self as device::IpDeviceConfigurationContext<Ipv4, BC>>::DevicesIter<'a>,
376        <Self as device::IpDeviceConfigurationContext<Ipv4, BC>>::DeviceAddressAndGroupsAccessor<
377            'a,
378        >,
379        BC,
380    >;
381
382    fn with_address_statuses<F: FnOnce(Self::DeviceAndAddressStatusIter<'_>) -> R, R>(
383        &mut self,
384        addr: SpecifiedAddr<Ipv4Addr>,
385        cb: F,
386    ) -> R {
387        device::IpDeviceConfigurationContext::<Ipv4, _>::with_devices_and_state(
388            self,
389            |devices, state| {
390                cb(FilterPresentWithDevices::new(
391                    devices,
392                    state,
393                    AddressStatus::from_context_addr_v4,
394                    addr,
395                ))
396            },
397        )
398    }
399
400    fn is_device_unicast_forwarding_enabled(&mut self, device_id: &Self::DeviceId) -> bool {
401        is_ip_unicast_forwarding_enabled::<Ipv4, _, _>(self, device_id)
402    }
403}
404
405impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpState<Ipv6>>>
406    IpDeviceEgressStateContext<Ipv6> for CoreCtx<'_, BC, L>
407{
408    fn with_next_packet_id<O, F: FnOnce(&()) -> O>(&self, cb: F) -> O {
409        cb(&())
410    }
411
412    fn get_local_addr_for_remote(
413        &mut self,
414        device_id: &Self::DeviceId,
415        remote: Option<SpecifiedAddr<Ipv6Addr>>,
416    ) -> Option<IpDeviceAddr<Ipv6Addr>> {
417        ip::IpSasHandler::<Ipv6, _>::get_local_addr_for_remote(self, device_id, remote)
418    }
419
420    fn get_hop_limit(&mut self, device_id: &Self::DeviceId) -> NonZeroU8 {
421        get_ipv6_hop_limit(self, device_id)
422    }
423}
424
425impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceGmp<Ipv6>>>
426    IpDeviceIngressStateContext<Ipv6> for CoreCtx<'_, BC, L>
427{
428    fn address_status_for_device(
429        &mut self,
430        addr: SpecifiedAddr<Ipv6Addr>,
431        device_id: &Self::DeviceId,
432    ) -> AddressStatus<<Ipv6 as IpLayerIpExt>::AddressStatus> {
433        AddressStatus::from_context_addr_v6(self, device_id, addr)
434    }
435}
436
437impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>>
438    ip::IpDeviceContext<Ipv6> for CoreCtx<'_, BC, L>
439{
440    fn is_ip_device_enabled(&mut self, device_id: &Self::DeviceId) -> bool {
441        is_ip_device_enabled::<Ipv6, _, _>(self, device_id)
442    }
443
444    type DeviceAndAddressStatusIter<'a> = FilterPresentWithDevices<
445        Ipv6,
446        <Self as device::IpDeviceConfigurationContext<Ipv6, BC>>::DevicesIter<'a>,
447        <Self as device::IpDeviceConfigurationContext<Ipv6, BC>>::DeviceAddressAndGroupsAccessor<
448            'a,
449        >,
450        BC,
451    >;
452
453    fn with_address_statuses<F: FnOnce(Self::DeviceAndAddressStatusIter<'_>) -> R, R>(
454        &mut self,
455        addr: SpecifiedAddr<Ipv6Addr>,
456        cb: F,
457    ) -> R {
458        device::IpDeviceConfigurationContext::<Ipv6, _>::with_devices_and_state(
459            self,
460            |devices, state| {
461                cb(FilterPresentWithDevices::new(
462                    devices,
463                    state,
464                    AddressStatus::from_context_addr_v6,
465                    addr,
466                ))
467            },
468        )
469    }
470
471    fn is_device_unicast_forwarding_enabled(&mut self, device_id: &Self::DeviceId) -> bool {
472        is_ip_unicast_forwarding_enabled::<Ipv6, _, _>(self, device_id)
473    }
474}
475
476#[netstack3_macros::instantiate_ip_impl_block(I)]
477impl<
478        I: IpExt,
479        BC: BindingsContext,
480        L: LockBefore<crate::lock_ordering::IpDeviceConfiguration<I>>,
481    > ip::IpDeviceConfirmReachableContext<I, BC> for CoreCtx<'_, BC, L>
482{
483    fn confirm_reachable(
484        &mut self,
485        bindings_ctx: &mut BC,
486        device: &Self::DeviceId,
487        neighbor: SpecifiedAddr<<I as Ip>::Addr>,
488    ) {
489        match device {
490            DeviceId::Ethernet(id) => {
491                nud::confirm_reachable::<I, _, _, _>(self, bindings_ctx, id, neighbor)
492            }
493            // NUD is not supported on Loopback, pure IP, or blackhole devices.
494            DeviceId::Loopback(_) | DeviceId::PureIp(_) | DeviceId::Blackhole(_) => {}
495        }
496    }
497}
498
499impl<
500        I: IpExt,
501        BC: BindingsContext,
502        L: LockBefore<crate::lock_ordering::EthernetDeviceDynamicState>,
503    > ip::IpDeviceMtuContext<I> for CoreCtx<'_, BC, L>
504{
505    fn get_mtu(&mut self, device_id: &Self::DeviceId) -> Mtu {
506        crate::device::integration::get_mtu(self, device_id)
507    }
508}
509
510#[netstack3_macros::instantiate_ip_impl_block(I)]
511impl<
512        I: IpExt,
513        BC: BindingsContext,
514        L: LockBefore<crate::lock_ordering::IpDeviceConfiguration<I>>,
515    > ip::multicast_forwarding::MulticastForwardingDeviceContext<I> for CoreCtx<'_, BC, L>
516{
517    fn is_device_multicast_forwarding_enabled(&mut self, device_id: &Self::DeviceId) -> bool {
518        is_ip_multicast_forwarding_enabled::<I, _, _>(self, device_id)
519    }
520}
521
522pub struct CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC: BindingsContext> {
523    pub config: Config,
524    pub core_ctx: CoreCtx<'a, BC, L>,
525}
526
527impl<'a, Config, L, BC: BindingsContext, T> CounterContext<T>
528    for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
529where
530    CoreCtx<'a, BC, L>: CounterContext<T>,
531{
532    fn counters(&self) -> &T {
533        self.core_ctx.counters()
534    }
535}
536
537impl<'a, Config, L, BC: BindingsContext, R, T> ResourceCounterContext<R, T>
538    for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
539where
540    CoreCtx<'a, BC, L>: ResourceCounterContext<R, T>,
541{
542    fn per_resource_counters<'b>(&'b self, resource: &'b R) -> &'b T {
543        self.core_ctx.per_resource_counters(resource)
544    }
545}
546
547impl<'a, Config, L, BC: BindingsContext, T> CoreTimerContext<T, BC>
548    for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
549where
550    CoreCtx<'a, BC, L>: CoreTimerContext<T, BC>,
551{
552    fn convert_timer(dispatch_id: T) -> BC::DispatchId {
553        <CoreCtx<'a, BC, L> as CoreTimerContext<T, BC>>::convert_timer(dispatch_id)
554    }
555}
556
557#[netstack3_macros::instantiate_ip_impl_block(I)]
558impl<'a, I: gmp::IpExt + IpDeviceIpExt, BC: BindingsContext>
559    device::WithIpDeviceConfigurationMutInner<I, BC>
560    for CoreCtxWithIpDeviceConfiguration<
561        'a,
562        &mut <I as IpDeviceIpExt>::Configuration,
563        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<I>>,
564        BC,
565    >
566{
567    type IpDeviceStateCtx<'s>
568        = CoreCtxWithIpDeviceConfiguration<
569        's,
570        &'s <I as IpDeviceIpExt>::Configuration,
571        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<I>>,
572        BC,
573    >
574    where
575        Self: 's;
576
577    fn ip_device_configuration_and_ctx(
578        &mut self,
579    ) -> (&<I as IpDeviceIpExt>::Configuration, Self::IpDeviceStateCtx<'_>) {
580        let Self { config, core_ctx } = self;
581        let config = &**config;
582        (config, CoreCtxWithIpDeviceConfiguration { config, core_ctx: core_ctx.as_owned() })
583    }
584
585    fn with_configuration_and_flags_mut<
586        O,
587        F: FnOnce(&mut <I as IpDeviceIpExt>::Configuration, &mut IpDeviceFlags) -> O,
588    >(
589        &mut self,
590        device_id: &Self::DeviceId,
591        cb: F,
592    ) -> O {
593        let Self { config, core_ctx } = self;
594        let mut state = crate::device::integration::ip_device_state(core_ctx, device_id);
595        let mut flags = state.lock::<crate::lock_ordering::IpDeviceFlags<I>>();
596        cb(*config, &mut *flags)
597    }
598}
599
600impl<'a, BC: BindingsContext> device::WithIpv6DeviceConfigurationMutInner<BC>
601    for CoreCtxWithIpDeviceConfiguration<
602        'a,
603        &mut Ipv6DeviceConfiguration,
604        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
605        BC,
606    >
607{
608    type Ipv6DeviceStateCtx<'s>
609        = CoreCtxWithIpDeviceConfiguration<
610        's,
611        &'s Ipv6DeviceConfiguration,
612        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
613        BC,
614    >
615    where
616        Self: 's;
617
618    fn ipv6_device_configuration_and_ctx(
619        &mut self,
620    ) -> (&Ipv6DeviceConfiguration, Self::Ipv6DeviceStateCtx<'_>) {
621        let Self { config, core_ctx } = self;
622        let config = &**config;
623        (config, CoreCtxWithIpDeviceConfiguration { config, core_ctx: core_ctx.as_owned() })
624    }
625}
626
627impl<'a, Config, BC: BindingsContext, L> DeviceIdContext<AnyDevice>
628    for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
629{
630    type DeviceId = <CoreCtx<'a, BC, L> as DeviceIdContext<AnyDevice>>::DeviceId;
631    type WeakDeviceId = <CoreCtx<'a, BC, L> as DeviceIdContext<AnyDevice>>::WeakDeviceId;
632}
633
634impl<'a, Config: Borrow<Ipv6DeviceConfiguration>, BC: BindingsContext> SlaacContext<BC>
635    for CoreCtxWithIpDeviceConfiguration<
636        'a,
637        Config,
638        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
639        BC,
640    >
641{
642    type LinkLayerAddr = <Self as device::Ipv6DeviceContext<BC>>::LinkLayerAddr;
643
644    type SlaacAddrs<'s> = SlaacAddrs<'s, BC>;
645
646    fn with_slaac_addrs_mut_and_configs<
647        O,
648        F: FnOnce(
649            &mut Self::SlaacAddrs<'_>,
650            SlaacConfigAndState<Self::LinkLayerAddr, BC>,
651            &mut SlaacState<BC>,
652        ) -> O,
653    >(
654        &mut self,
655        device_id: &Self::DeviceId,
656        cb: F,
657    ) -> O {
658        let Self { config, core_ctx } = self;
659        let retrans_timer = device::Ipv6DeviceContext::with_network_learned_parameters(
660            core_ctx,
661            device_id,
662            |params| {
663                // NB: We currently only change the retransmission timer from
664                // learning it from the network. We might need to consider user
665                // settings once we allow users to override the value.
666                params.retrans_timer_or_default().get()
667            },
668        );
669        // We use the link-layer address to derive opaque IIDs for the interface, rather
670        // than the interface ID or name, because it is more stable. This does imply
671        // that we do not generate SLAAC addresses for interfaces without a link-layer
672        // address (e.g. loopback and pure IP devices); we could revisit this in the
673        // future if desired.
674        let link_layer_addr = device::Ipv6DeviceContext::get_link_layer_addr(core_ctx, device_id);
675
676        let config = Borrow::borrow(config);
677        let Ipv6DeviceConfiguration {
678            dad_transmits,
679            max_router_solicitations: _,
680            slaac_config,
681            ip_config: _,
682        } = *config;
683
684        let ipv6_state = &core_ctx.unlocked_access::<crate::lock_ordering::UnlockedState>().ipv6;
685        let stable_secret_key = ipv6_state.slaac_stable_secret_key;
686        let temp_secret_key = ipv6_state.slaac_temp_secret_key;
687        let mut core_ctx_and_resource =
688            crate::device::integration::ip_device_state_and_core_ctx(core_ctx, device_id);
689        let (mut state, mut locked) = core_ctx_and_resource
690            .lock_with_and::<crate::lock_ordering::Ipv6DeviceSlaac, _>(|x| x.right());
691        let core_ctx =
692            CoreCtxWithIpDeviceConfiguration { config, core_ctx: locked.cast_core_ctx() };
693
694        let mut addrs = SlaacAddrs { core_ctx, device_id: device_id.clone(), config };
695
696        cb(
697            &mut addrs,
698            SlaacConfigAndState {
699                config: slaac_config,
700                dad_transmits,
701                retrans_timer,
702                link_layer_addr,
703                temp_secret_key,
704                stable_secret_key,
705                _marker: PhantomData,
706            },
707            &mut state,
708        )
709    }
710}
711
712impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceGmp<Ipv6>>>
713    DadAddressContext<BC>
714    for CoreCtxWithIpDeviceConfiguration<'_, &'_ Ipv6DeviceConfiguration, L, BC>
715{
716    fn with_address_assigned<O, F: FnOnce(&mut bool) -> O>(
717        &mut self,
718        _: &Self::DeviceId,
719        addr: &Self::AddressId,
720        cb: F,
721    ) -> O {
722        let mut locked = self.core_ctx.adopt(addr.deref());
723        let mut state = locked
724            .write_lock_with::<crate::lock_ordering::Ipv6DeviceAddressState, _>(|c| c.right());
725        let Ipv6AddressState { flags: Ipv6AddressFlags { assigned }, config: _ } = &mut *state;
726
727        cb(assigned)
728    }
729
730    fn join_multicast_group(
731        &mut self,
732        bindings_ctx: &mut BC,
733        device_id: &Self::DeviceId,
734        multicast_addr: MulticastAddr<Ipv6Addr>,
735    ) {
736        let Self { config, core_ctx } = self;
737        let config = Borrow::borrow(&*config);
738        join_ip_multicast_with_config(
739            &mut CoreCtxWithIpDeviceConfiguration { config, core_ctx: core_ctx.as_owned() },
740            bindings_ctx,
741            device_id,
742            multicast_addr,
743            config,
744        )
745    }
746
747    fn leave_multicast_group(
748        &mut self,
749        bindings_ctx: &mut BC,
750        device_id: &Self::DeviceId,
751        multicast_addr: MulticastAddr<Ipv6Addr>,
752    ) {
753        let Self { config, core_ctx } = self;
754        let config = Borrow::borrow(&*config);
755        leave_ip_multicast_with_config(
756            &mut CoreCtxWithIpDeviceConfiguration { config, core_ctx: core_ctx.as_owned() },
757            bindings_ctx,
758            device_id,
759            multicast_addr,
760            config,
761        )
762    }
763}
764
765impl<'a, Config, BC, L> CoreEventContext<DadEvent<DeviceId<BC>>>
766    for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
767where
768    BC: BindingsContext,
769{
770    type OuterEvent = IpDeviceEvent<DeviceId<BC>, Ipv6, BC::Instant>;
771
772    fn convert_event(event: DadEvent<DeviceId<BC>>) -> Self::OuterEvent {
773        match event {
774            DadEvent::AddressAssigned { device, addr } => IpDeviceEvent::AddressStateChanged {
775                device,
776                addr: addr.into_specified(),
777                state: IpAddressState::Assigned,
778            },
779        }
780    }
781}
782
783impl<
784        'a,
785        Config: Borrow<Ipv6DeviceConfiguration>,
786        BC: BindingsContext,
787        L: LockBefore<crate::lock_ordering::Ipv6DeviceAddressDad>,
788    > DadContext<BC> for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
789{
790    type DadAddressCtx<'b> = CoreCtxWithIpDeviceConfiguration<
791        'b,
792        &'b Ipv6DeviceConfiguration,
793        WrapLockLevel<crate::lock_ordering::Ipv6DeviceAddressDad>,
794        BC,
795    >;
796
797    fn with_dad_state<O, F: FnOnce(DadStateRef<'_, Self::DadAddressCtx<'_>, BC>) -> O>(
798        &mut self,
799        device_id: &Self::DeviceId,
800        addr: &Self::AddressId,
801        cb: F,
802    ) -> O {
803        let Self { config, core_ctx } = self;
804        let retrans_timer = device::Ipv6DeviceContext::<BC>::with_network_learned_parameters(
805            core_ctx,
806            device_id,
807            |p| {
808                // NB: We currently only change the retransmission timer from
809                // learning it from the network. We might need to consider user
810                // settings once we allow users to override the value.
811                p.retrans_timer_or_default()
812            },
813        );
814
815        let mut core_ctx = core_ctx.adopt(addr.deref());
816        let config = Borrow::borrow(&*config);
817
818        let (mut dad_state, mut locked) =
819            core_ctx.lock_with_and::<crate::lock_ordering::Ipv6DeviceAddressDad, _>(|c| c.right());
820        let mut core_ctx =
821            CoreCtxWithIpDeviceConfiguration { config, core_ctx: locked.cast_core_ctx() };
822
823        cb(DadStateRef {
824            state: DadAddressStateRef { dad_state: dad_state.deref_mut(), core_ctx: &mut core_ctx },
825            retrans_timer: &retrans_timer,
826            max_dad_transmits: &config.dad_transmits,
827        })
828    }
829
830    fn send_dad_packet(
831        &mut self,
832        bindings_ctx: &mut BC,
833        device_id: &Self::DeviceId,
834        dst_ip: MulticastAddr<Ipv6Addr>,
835        message: NeighborSolicitation,
836        nonce: NdpNonce<&[u8]>,
837    ) -> Result<(), ()> {
838        let options = [NdpOptionBuilder::Nonce(nonce)];
839        ip::icmp::send_ndp_packet(
840            self,
841            bindings_ctx,
842            device_id,
843            None,
844            dst_ip.into_specified(),
845            OptionSequenceBuilder::new(options.iter()).into_serializer(),
846            IcmpZeroCode,
847            message,
848        )
849        .map_err(|IpSendFrameError { serializer: _, error }| {
850            debug!("error sending DAD packet: {error:?}")
851        })
852    }
853}
854
855impl<'a, Config: Borrow<Ipv6DeviceConfiguration>, BC: BindingsContext> RsContext<BC>
856    for CoreCtxWithIpDeviceConfiguration<
857        'a,
858        Config,
859        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
860        BC,
861    >
862{
863    type LinkLayerAddr = <CoreCtx<
864        'a,
865        BC,
866        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
867    > as device::Ipv6DeviceContext<BC>>::LinkLayerAddr;
868
869    fn with_rs_state_mut_and_max<O, F: FnOnce(&mut RsState<BC>, Option<NonZeroU8>) -> O>(
870        &mut self,
871        device_id: &Self::DeviceId,
872        cb: F,
873    ) -> O {
874        let Self { config, core_ctx } = self;
875        let mut state = crate::device::integration::ip_device_state(core_ctx, device_id);
876        let mut state = state.lock::<crate::lock_ordering::Ipv6DeviceRouterSolicitations>();
877        cb(&mut state, Borrow::borrow(&*config).max_router_solicitations)
878    }
879
880    fn get_link_layer_addr(&mut self, device_id: &Self::DeviceId) -> Option<Self::LinkLayerAddr> {
881        let Self { config: _, core_ctx } = self;
882        device::Ipv6DeviceContext::get_link_layer_addr(core_ctx, device_id)
883    }
884
885    fn send_rs_packet<
886        S: Serializer<Buffer = EmptyBuf>,
887        F: FnOnce(Option<UnicastAddr<Ipv6Addr>>) -> S,
888    >(
889        &mut self,
890        bindings_ctx: &mut BC,
891        device_id: &Self::DeviceId,
892        message: RouterSolicitation,
893        body: F,
894    ) -> Result<(), IpSendFrameError<S>> {
895        let Self { config: _, core_ctx } = self;
896
897        let dst_ip = Ipv6::ALL_ROUTERS_LINK_LOCAL_MULTICAST_ADDRESS.into_specified();
898        let src_ip = ip::IpSasHandler::<Ipv6, _>::get_local_addr_for_remote(
899            core_ctx,
900            device_id,
901            Some(dst_ip),
902        )
903        .and_then(|addr| UnicastAddr::new(addr.addr()));
904        ip::icmp::send_ndp_packet(
905            core_ctx,
906            bindings_ctx,
907            device_id,
908            src_ip.map(UnicastAddr::into_specified),
909            dst_ip,
910            body(src_ip),
911            IcmpZeroCode,
912            message,
913        )
914    }
915}
916
917impl<
918        I: IpExt,
919        Config,
920        BC: BindingsContext,
921        L: LockBefore<crate::lock_ordering::EthernetDeviceDynamicState>,
922    > ip::IpDeviceMtuContext<I> for CoreCtxWithIpDeviceConfiguration<'_, Config, L, BC>
923{
924    fn get_mtu(&mut self, device_id: &Self::DeviceId) -> Mtu {
925        ip::IpDeviceMtuContext::<I>::get_mtu(&mut self.core_ctx, device_id)
926    }
927}
928
929impl<L, BT: BindingsTypes> CoreTimerContext<RsTimerId<WeakDeviceId<BT>>, BT>
930    for CoreCtx<'_, BT, L>
931{
932    fn convert_timer(dispatch_id: RsTimerId<WeakDeviceId<BT>>) -> BT::DispatchId {
933        IpDeviceTimerId::<Ipv6, _, _>::from(Ipv6DeviceTimerId::from(dispatch_id)).into()
934    }
935}
936
937impl<BC: BindingsContext> Ipv6DiscoveredRoutesContext<BC>
938    for CoreCtx<'_, BC, WrapLockLevel<crate::lock_ordering::Ipv6DeviceRouteDiscovery>>
939{
940    fn add_discovered_ipv6_route(
941        &mut self,
942        bindings_ctx: &mut BC,
943        device_id: &Self::DeviceId,
944        Ipv6DiscoveredRoute { subnet, gateway }: Ipv6DiscoveredRoute,
945    ) {
946        let device_id = device_id.clone();
947        let entry = ip::AddableEntry {
948            subnet,
949            device: device_id,
950            gateway: gateway.map(|g| (*g).into_specified()),
951            metric: AddableMetric::MetricTracksInterface,
952        };
953
954        ip::request_context_add_route::<Ipv6, _, _>(bindings_ctx, entry);
955    }
956
957    fn del_discovered_ipv6_route(
958        &mut self,
959        bindings_ctx: &mut BC,
960        device_id: &Self::DeviceId,
961        Ipv6DiscoveredRoute { subnet, gateway }: Ipv6DiscoveredRoute,
962    ) {
963        ip::request_context_del_routes::<Ipv6, _, _>(
964            bindings_ctx,
965            subnet,
966            device_id.clone(),
967            gateway.map(|g| (*g).into_specified()),
968        );
969    }
970}
971
972impl<'a, Config, BC: BindingsContext> Ipv6RouteDiscoveryContext<BC>
973    for CoreCtxWithIpDeviceConfiguration<
974        'a,
975        Config,
976        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
977        BC,
978    >
979{
980    type WithDiscoveredRoutesMutCtx<'b> =
981        CoreCtx<'b, BC, WrapLockLevel<crate::lock_ordering::Ipv6DeviceRouteDiscovery>>;
982
983    fn with_discovered_routes_mut<
984        O,
985        F: FnOnce(&mut Ipv6RouteDiscoveryState<BC>, &mut Self::WithDiscoveredRoutesMutCtx<'_>) -> O,
986    >(
987        &mut self,
988        device_id: &Self::DeviceId,
989        cb: F,
990    ) -> O {
991        let Self { config: _, core_ctx } = self;
992        let mut core_ctx_and_resource =
993            crate::device::integration::ip_device_state_and_core_ctx(core_ctx, device_id);
994
995        let (mut state, mut locked) =
996            core_ctx_and_resource
997                .lock_with_and::<crate::lock_ordering::Ipv6DeviceRouteDiscovery, _>(|x| x.right());
998        cb(&mut state, &mut locked.cast_core_ctx())
999    }
1000}
1001
1002impl<'a, Config, BC: BindingsContext> device::Ipv6DeviceContext<BC>
1003    for CoreCtxWithIpDeviceConfiguration<
1004        'a,
1005        Config,
1006        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
1007        BC,
1008    >
1009{
1010    type LinkLayerAddr = <CoreCtx<
1011        'a,
1012        BC,
1013        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
1014    > as device::Ipv6DeviceContext<BC>>::LinkLayerAddr;
1015
1016    fn get_link_layer_addr(&mut self, device_id: &Self::DeviceId) -> Option<Self::LinkLayerAddr> {
1017        let Self { config: _, core_ctx } = self;
1018        device::Ipv6DeviceContext::get_link_layer_addr(core_ctx, device_id)
1019    }
1020
1021    fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu) {
1022        let Self { config: _, core_ctx } = self;
1023        device::Ipv6DeviceContext::set_link_mtu(core_ctx, device_id, mtu)
1024    }
1025
1026    fn with_network_learned_parameters<O, F: FnOnce(&Ipv6NetworkLearnedParameters) -> O>(
1027        &mut self,
1028        device_id: &Self::DeviceId,
1029        cb: F,
1030    ) -> O {
1031        let Self { config: _, core_ctx } = self;
1032        device::Ipv6DeviceContext::with_network_learned_parameters(core_ctx, device_id, cb)
1033    }
1034
1035    fn with_network_learned_parameters_mut<O, F: FnOnce(&mut Ipv6NetworkLearnedParameters) -> O>(
1036        &mut self,
1037        device_id: &Self::DeviceId,
1038        cb: F,
1039    ) -> O {
1040        let Self { config: _, core_ctx } = self;
1041        device::Ipv6DeviceContext::with_network_learned_parameters_mut(core_ctx, device_id, cb)
1042    }
1043}
1044
1045impl<'a, Config, I: IpDeviceIpExt, L, BC: BindingsContext> IpDeviceAddressIdContext<I>
1046    for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
1047where
1048    CoreCtx<'a, BC, L>: IpDeviceAddressIdContext<I>,
1049{
1050    type AddressId = <CoreCtx<'a, BC, L> as IpDeviceAddressIdContext<I>>::AddressId;
1051    type WeakAddressId = <CoreCtx<'a, BC, L> as IpDeviceAddressIdContext<I>>::WeakAddressId;
1052}
1053
1054impl<'a, Config, I: IpDeviceIpExt, BC: BindingsContext, L> device::IpDeviceAddressContext<I, BC>
1055    for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
1056where
1057    CoreCtx<'a, BC, L>: device::IpDeviceAddressContext<I, BC>,
1058{
1059    fn with_ip_address_state<O, F: FnOnce(&I::AddressState<BC::Instant>) -> O>(
1060        &mut self,
1061        device_id: &Self::DeviceId,
1062        addr_id: &Self::AddressId,
1063        cb: F,
1064    ) -> O {
1065        let Self { config: _, core_ctx } = self;
1066        device::IpDeviceAddressContext::<I, BC>::with_ip_address_state(
1067            core_ctx, device_id, addr_id, cb,
1068        )
1069    }
1070
1071    fn with_ip_address_state_mut<O, F: FnOnce(&mut I::AddressState<BC::Instant>) -> O>(
1072        &mut self,
1073        device_id: &Self::DeviceId,
1074        addr_id: &Self::AddressId,
1075        cb: F,
1076    ) -> O {
1077        let Self { config: _, core_ctx } = self;
1078        device::IpDeviceAddressContext::<I, BC>::with_ip_address_state_mut(
1079            core_ctx, device_id, addr_id, cb,
1080        )
1081    }
1082}
1083
1084impl<'a, Config, I: IpDeviceIpExt, BC: BindingsContext, L> device::IpDeviceStateContext<I, BC>
1085    for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
1086where
1087    CoreCtx<'a, BC, L>: device::IpDeviceStateContext<I, BC>,
1088{
1089    type IpDeviceAddressCtx<'b> =
1090        <CoreCtx<'a, BC, L> as device::IpDeviceStateContext<I, BC>>::IpDeviceAddressCtx<'b>;
1091
1092    fn with_ip_device_flags<O, F: FnOnce(&IpDeviceFlags) -> O>(
1093        &mut self,
1094        device_id: &Self::DeviceId,
1095        cb: F,
1096    ) -> O {
1097        let Self { config: _, core_ctx } = self;
1098        device::IpDeviceStateContext::<I, BC>::with_ip_device_flags(core_ctx, device_id, cb)
1099    }
1100
1101    fn add_ip_address(
1102        &mut self,
1103        device_id: &Self::DeviceId,
1104        addr: AddrSubnet<I::Addr, I::AssignedWitness>,
1105        config: I::AddressConfig<BC::Instant>,
1106    ) -> Result<Self::AddressId, ExistsError> {
1107        let Self { config: _, core_ctx } = self;
1108        device::IpDeviceStateContext::<I, BC>::add_ip_address(core_ctx, device_id, addr, config)
1109    }
1110
1111    fn remove_ip_address(
1112        &mut self,
1113        device_id: &Self::DeviceId,
1114        addr: Self::AddressId,
1115    ) -> RemoveResourceResultWithContext<AddrSubnet<I::Addr>, BC> {
1116        let Self { config: _, core_ctx } = self;
1117        device::IpDeviceStateContext::<I, BC>::remove_ip_address(core_ctx, device_id, addr)
1118    }
1119
1120    fn get_address_id(
1121        &mut self,
1122        device_id: &Self::DeviceId,
1123        addr: SpecifiedAddr<I::Addr>,
1124    ) -> Result<Self::AddressId, NotFoundError> {
1125        let Self { config: _, core_ctx } = self;
1126        device::IpDeviceStateContext::<I, BC>::get_address_id(core_ctx, device_id, addr)
1127    }
1128
1129    type AddressIdsIter<'b> =
1130        <CoreCtx<'a, BC, L> as device::IpDeviceStateContext<I, BC>>::AddressIdsIter<'b>;
1131    fn with_address_ids<
1132        O,
1133        F: FnOnce(Self::AddressIdsIter<'_>, &mut Self::IpDeviceAddressCtx<'_>) -> O,
1134    >(
1135        &mut self,
1136        device_id: &Self::DeviceId,
1137        cb: F,
1138    ) -> O {
1139        let Self { config: _, core_ctx } = self;
1140        device::IpDeviceStateContext::<I, BC>::with_address_ids(core_ctx, device_id, cb)
1141    }
1142
1143    fn with_default_hop_limit<O, F: FnOnce(&NonZeroU8) -> O>(
1144        &mut self,
1145        device_id: &Self::DeviceId,
1146        cb: F,
1147    ) -> O {
1148        let Self { config: _, core_ctx } = self;
1149        device::IpDeviceStateContext::<I, BC>::with_default_hop_limit(core_ctx, device_id, cb)
1150    }
1151
1152    fn with_default_hop_limit_mut<O, F: FnOnce(&mut NonZeroU8) -> O>(
1153        &mut self,
1154        device_id: &Self::DeviceId,
1155        cb: F,
1156    ) -> O {
1157        let Self { config: _, core_ctx } = self;
1158        device::IpDeviceStateContext::<I, BC>::with_default_hop_limit_mut(core_ctx, device_id, cb)
1159    }
1160
1161    fn join_link_multicast_group(
1162        &mut self,
1163        bindings_ctx: &mut BC,
1164        device_id: &Self::DeviceId,
1165        multicast_addr: MulticastAddr<I::Addr>,
1166    ) {
1167        let Self { config: _, core_ctx } = self;
1168        device::IpDeviceStateContext::<I, BC>::join_link_multicast_group(
1169            core_ctx,
1170            bindings_ctx,
1171            device_id,
1172            multicast_addr,
1173        )
1174    }
1175
1176    fn leave_link_multicast_group(
1177        &mut self,
1178        bindings_ctx: &mut BC,
1179        device_id: &Self::DeviceId,
1180        multicast_addr: MulticastAddr<I::Addr>,
1181    ) {
1182        let Self { config: _, core_ctx } = self;
1183        device::IpDeviceStateContext::<I, BC>::leave_link_multicast_group(
1184            core_ctx,
1185            bindings_ctx,
1186            device_id,
1187            multicast_addr,
1188        )
1189    }
1190}
1191
1192impl<BC: BindingsContext, Config, L> IgmpContextMarker
1193    for CoreCtxWithIpDeviceConfiguration<'_, Config, L, BC>
1194{
1195}
1196
1197impl<'a, Config: Borrow<Ipv4DeviceConfiguration>, BC: BindingsContext> IgmpContext<BC>
1198    for CoreCtxWithIpDeviceConfiguration<
1199        'a,
1200        Config,
1201        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv4>>,
1202        BC,
1203    >
1204{
1205    type SendContext<'b> = CoreCtx<'b, BC, WrapLockLevel<crate::lock_ordering::IpDeviceGmp<Ipv4>>>;
1206
1207    /// Calls the function with a mutable reference to the device's IGMP state
1208    /// and whether or not IGMP is enabled for the `device`.
1209    fn with_igmp_state_mut<
1210        O,
1211        F: for<'b> FnOnce(Self::SendContext<'b>, GmpStateRef<'b, Ipv4, IgmpTypeLayout, BC>) -> O,
1212    >(
1213        &mut self,
1214        device: &Self::DeviceId,
1215        cb: F,
1216    ) -> O {
1217        let Self { config, core_ctx } = self;
1218        let Ipv4DeviceConfiguration { ip_config: IpDeviceConfiguration { gmp_enabled, .. } } =
1219            Borrow::borrow(&*config);
1220
1221        let mut state = crate::device::integration::ip_device_state_and_core_ctx(core_ctx, device);
1222        // Note that changes to `ip_enabled` is not possible in this context
1223        // since IP enabled changes are only performed while the IP device
1224        // configuration lock is held exclusively. Since we have access to
1225        // the IP device configuration here (`config`), we know changes to
1226        // IP enabled are not possible.
1227        let ip_enabled = state
1228            .lock_with::<crate::lock_ordering::IpDeviceFlags<Ipv4>, _>(|x| x.right())
1229            .ip_enabled;
1230        let (mut state, mut locked) =
1231            state.write_lock_with_and::<crate::lock_ordering::IpDeviceGmp<Ipv4>, _>(|x| x.right());
1232        let IpDeviceMulticastGroups { groups, gmp, gmp_config } = &mut *state;
1233        let enabled = ip_enabled && *gmp_enabled;
1234        cb(locked.cast_core_ctx(), GmpStateRef { enabled, groups, gmp, config: gmp_config })
1235    }
1236}
1237
1238impl<'a, BC: BindingsContext> IgmpSendContext<BC>
1239    for CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::IpDeviceGmp<Ipv4>>>
1240{
1241    fn get_ip_addr_subnet(
1242        &mut self,
1243        device: &Self::DeviceId,
1244    ) -> Option<AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>> {
1245        ip::device::get_ipv4_addr_subnet(self, device)
1246    }
1247}
1248
1249impl<BC: BindingsContext, Config, L> MldContextMarker
1250    for CoreCtxWithIpDeviceConfiguration<'_, Config, L, BC>
1251{
1252}
1253
1254impl<
1255        'a,
1256        Config: Borrow<Ipv6DeviceConfiguration>,
1257        BC: BindingsContext,
1258        L: LockBefore<crate::lock_ordering::IpDeviceGmp<Ipv6>>,
1259    > MldContext<BC> for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
1260{
1261    type SendContext<'b> = CoreCtx<'b, BC, WrapLockLevel<crate::lock_ordering::IpDeviceGmp<Ipv6>>>;
1262
1263    fn with_mld_state_mut<
1264        O,
1265        F: FnOnce(Self::SendContext<'_>, GmpStateRef<'_, Ipv6, MldTypeLayout, BC>) -> O,
1266    >(
1267        &mut self,
1268        device: &Self::DeviceId,
1269        cb: F,
1270    ) -> O {
1271        let Self { config, core_ctx } = self;
1272        let Ipv6DeviceConfiguration {
1273            dad_transmits: _,
1274            max_router_solicitations: _,
1275            slaac_config: _,
1276            ip_config: IpDeviceConfiguration { gmp_enabled, .. },
1277        } = Borrow::borrow(&*config);
1278
1279        let mut state = crate::device::integration::ip_device_state_and_core_ctx(core_ctx, device);
1280        let ip_enabled = state
1281            .lock_with::<crate::lock_ordering::IpDeviceFlags<Ipv6>, _>(|x| x.right())
1282            .ip_enabled;
1283        let (mut state, mut locked) =
1284            state.write_lock_with_and::<crate::lock_ordering::IpDeviceGmp<Ipv6>, _>(|x| x.right());
1285        let IpDeviceMulticastGroups { groups, gmp, gmp_config } = &mut *state;
1286        let enabled = ip_enabled && *gmp_enabled;
1287        cb(locked.cast_core_ctx(), GmpStateRef { enabled, groups, gmp, config: gmp_config })
1288    }
1289}
1290
1291impl<'a, BC: BindingsContext> MldSendContext<BC>
1292    for CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::IpDeviceGmp<Ipv6>>>
1293{
1294    fn get_ipv6_link_local_addr(
1295        &mut self,
1296        device: &Self::DeviceId,
1297    ) -> Option<LinkLocalUnicastAddr<Ipv6Addr>> {
1298        device::IpDeviceStateContext::<Ipv6, BC>::with_address_ids(
1299            self,
1300            device,
1301            |mut addrs, core_ctx| {
1302                addrs.find_map(|addr_id| {
1303                    device::IpDeviceAddressContext::<Ipv6, _>::with_ip_address_state(
1304                        core_ctx,
1305                        device,
1306                        &addr_id,
1307                        |Ipv6AddressState { flags: Ipv6AddressFlags { assigned }, config: _ }| {
1308                            if *assigned {
1309                                LinkLocalUnicastAddr::new(addr_id.addr_sub().addr().get())
1310                            } else {
1311                                None
1312                            }
1313                        },
1314                    )
1315                })
1316            },
1317        )
1318    }
1319}
1320
1321impl<'a, Config, I: IpDeviceIpExt, BC: BindingsContext, L> NudIpHandler<I, BC>
1322    for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
1323where
1324    CoreCtx<'a, BC, L>: NudIpHandler<I, BC>,
1325{
1326    fn handle_neighbor_probe(
1327        &mut self,
1328        bindings_ctx: &mut BC,
1329        device_id: &Self::DeviceId,
1330        neighbor: SpecifiedAddr<I::Addr>,
1331        link_addr: &[u8],
1332    ) {
1333        let Self { config: _, core_ctx } = self;
1334        NudIpHandler::<I, BC>::handle_neighbor_probe(
1335            core_ctx,
1336            bindings_ctx,
1337            device_id,
1338            neighbor,
1339            link_addr,
1340        )
1341    }
1342
1343    fn handle_neighbor_confirmation(
1344        &mut self,
1345        bindings_ctx: &mut BC,
1346        device_id: &Self::DeviceId,
1347        neighbor: SpecifiedAddr<I::Addr>,
1348        link_addr: &[u8],
1349        flags: ConfirmationFlags,
1350    ) {
1351        let Self { config: _, core_ctx } = self;
1352        NudIpHandler::<I, BC>::handle_neighbor_confirmation(
1353            core_ctx,
1354            bindings_ctx,
1355            device_id,
1356            neighbor,
1357            link_addr,
1358            flags,
1359        )
1360    }
1361
1362    fn flush_neighbor_table(&mut self, bindings_ctx: &mut BC, device_id: &Self::DeviceId) {
1363        let Self { config: _, core_ctx } = self;
1364        NudIpHandler::<I, BC>::flush_neighbor_table(core_ctx, bindings_ctx, device_id)
1365    }
1366}
1367
1368#[netstack3_macros::instantiate_ip_impl_block(I)]
1369impl<
1370        'a,
1371        I: IpExt,
1372        Config,
1373        BC: BindingsContext,
1374        L: LockBefore<crate::lock_ordering::FilterState<I>>,
1375    > FilterHandlerProvider<I, BC> for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
1376{
1377    type Handler<'b>
1378        = FilterImpl<'b, CoreCtx<'a, BC, L>>
1379    where
1380        Self: 'b;
1381
1382    fn filter_handler(&mut self) -> Self::Handler<'_> {
1383        let Self { config: _, core_ctx } = self;
1384        FilterHandlerProvider::<I, BC>::filter_handler(core_ctx)
1385    }
1386}
1387
1388#[netstack3_macros::instantiate_ip_impl_block(I)]
1389impl<
1390        'a,
1391        I: IpLayerIpExt,
1392        Config,
1393        BC: BindingsContext,
1394        L: LockBefore<crate::lock_ordering::IpDeviceGmp<I>>,
1395    > IpDeviceEgressStateContext<I> for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
1396{
1397    fn with_next_packet_id<O, F: FnOnce(&<I as IpLayerIpExt>::PacketIdState) -> O>(
1398        &self,
1399        cb: F,
1400    ) -> O {
1401        let Self { config: _, core_ctx } = self;
1402        IpDeviceEgressStateContext::<I>::with_next_packet_id(core_ctx, cb)
1403    }
1404
1405    fn get_local_addr_for_remote(
1406        &mut self,
1407        device_id: &Self::DeviceId,
1408        remote: Option<SpecifiedAddr<<I as Ip>::Addr>>,
1409    ) -> Option<IpDeviceAddr<<I as Ip>::Addr>> {
1410        let Self { config: _, core_ctx } = self;
1411        IpDeviceEgressStateContext::<I>::get_local_addr_for_remote(core_ctx, device_id, remote)
1412    }
1413
1414    fn get_hop_limit(&mut self, device_id: &Self::DeviceId) -> NonZeroU8 {
1415        let Self { config: _, core_ctx } = self;
1416        IpDeviceEgressStateContext::<I>::get_hop_limit(core_ctx, device_id)
1417    }
1418}
1419
1420#[netstack3_macros::instantiate_ip_impl_block(I)]
1421impl<
1422        'a,
1423        I: IpLayerIpExt,
1424        Config,
1425        BC: BindingsContext,
1426        L: LockBefore<crate::lock_ordering::IpDeviceGmp<I>>,
1427    > IpDeviceIngressStateContext<I> for CoreCtxWithIpDeviceConfiguration<'a, Config, L, BC>
1428{
1429    fn address_status_for_device(
1430        &mut self,
1431        dst_ip: SpecifiedAddr<<I as Ip>::Addr>,
1432        device_id: &Self::DeviceId,
1433    ) -> AddressStatus<<I as IpLayerIpExt>::AddressStatus> {
1434        let Self { config: _, core_ctx } = self;
1435        IpDeviceIngressStateContext::<I>::address_status_for_device(core_ctx, dst_ip, device_id)
1436    }
1437}
1438
1439impl<BC: BindingsContext, I: Ip, L> CounterContext<NudCounters<I>> for CoreCtx<'_, BC, L> {
1440    fn counters(&self) -> &NudCounters<I> {
1441        self.unlocked_access::<crate::lock_ordering::UnlockedState>().device.nud_counters::<I>()
1442    }
1443}
1444
1445pub struct IpAddrCtxSpec<BT>(Never, PhantomData<BT>);
1446
1447impl<BT: BindingsTypes> IpAddressIdSpec for IpAddrCtxSpec<BT> {
1448    type WeakV4 = WeakAddressId<Ipv4AddressEntry<BT>>;
1449    type WeakV6 = WeakAddressId<Ipv6AddressEntry<BT>>;
1450}
1451
1452impl<BC: BindingsContext, L> IpAddressIdSpecContext for CoreCtx<'_, BC, L> {
1453    type AddressIdSpec = IpAddrCtxSpec<BC>;
1454}
1455
1456impl<L, BT: BindingsTypes>
1457    CoreTimerContext<DadTimerId<WeakDeviceId<BT>, WeakAddressId<Ipv6AddressEntry<BT>>>, BT>
1458    for CoreCtx<'_, BT, L>
1459{
1460    fn convert_timer(
1461        dispatch_id: DadTimerId<WeakDeviceId<BT>, WeakAddressId<Ipv6AddressEntry<BT>>>,
1462    ) -> BT::DispatchId {
1463        IpDeviceTimerId::<Ipv6, _, _>::from(Ipv6DeviceTimerId::from(dispatch_id)).into()
1464    }
1465}
1466
1467impl<I: IpDeviceIpExt, BT: BindingsTypes, L>
1468    CoreTimerContext<IpDeviceTimerId<I, WeakDeviceId<BT>, IpAddrCtxSpec<BT>>, BT>
1469    for CoreCtx<'_, BT, L>
1470{
1471    fn convert_timer(
1472        dispatch_id: IpDeviceTimerId<I, WeakDeviceId<BT>, IpAddrCtxSpec<BT>>,
1473    ) -> BT::DispatchId {
1474        dispatch_id.into()
1475    }
1476}
1477
1478impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> LockLevelFor<DualStackIpDeviceState<BT>>
1479    for crate::lock_ordering::IpDeviceAddresses<I>
1480{
1481    type Data = IpDeviceAddresses<I, BT>;
1482}
1483
1484impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> LockLevelFor<DualStackIpDeviceState<BT>>
1485    for crate::lock_ordering::IpDeviceGmp<I>
1486{
1487    type Data = IpDeviceMulticastGroups<I, BT>;
1488}
1489
1490impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> LockLevelFor<DualStackIpDeviceState<BT>>
1491    for crate::lock_ordering::IpDeviceDefaultHopLimit<I>
1492{
1493    type Data = DefaultHopLimit<I>;
1494}
1495
1496impl<I: IpDeviceStateIpExt, BT: IpDeviceStateBindingsTypes> LockLevelFor<DualStackIpDeviceState<BT>>
1497    for crate::lock_ordering::IpDeviceFlags<I>
1498{
1499    type Data = IpMarked<I, IpDeviceFlags>;
1500}
1501
1502impl<BT: IpDeviceStateBindingsTypes> LockLevelFor<DualStackIpDeviceState<BT>>
1503    for crate::lock_ordering::Ipv6DeviceSlaac
1504{
1505    type Data = SlaacState<BT>;
1506}
1507
1508/// It is safe to provide unlocked access to [`DualStackIpDeviceState`] itself
1509/// here because care has been taken to avoid exposing publicly to the core
1510/// integration crate any state that is held by a lock, as opposed to read-only
1511/// state that can be accessed safely at any lock level, e.g. state with no
1512/// interior mutability or atomics.
1513///
1514/// Access to state held by locks *must* be mediated using the global lock
1515/// ordering declared in [`crate::lock_ordering`].
1516impl<BT: IpDeviceStateBindingsTypes> UnlockedAccess<crate::lock_ordering::UnlockedState>
1517    for DualStackIpDeviceState<BT>
1518{
1519    type Data = DualStackIpDeviceState<BT>;
1520    type Guard<'l>
1521        = &'l DualStackIpDeviceState<BT>
1522    where
1523        Self: 'l;
1524
1525    fn access(&self) -> Self::Guard<'_> {
1526        &self
1527    }
1528}
1529
1530impl<BT: IpDeviceStateBindingsTypes> LockLevelFor<DualStackIpDeviceState<BT>>
1531    for crate::lock_ordering::IpDeviceConfiguration<Ipv4>
1532{
1533    type Data = Ipv4DeviceConfiguration;
1534}
1535
1536impl<BT: IpDeviceStateBindingsTypes> LockLevelFor<DualStackIpDeviceState<BT>>
1537    for crate::lock_ordering::Ipv6DeviceLearnedParams
1538{
1539    type Data = Ipv6NetworkLearnedParameters;
1540}
1541
1542impl<BT: IpDeviceStateBindingsTypes> LockLevelFor<DualStackIpDeviceState<BT>>
1543    for crate::lock_ordering::Ipv6DeviceRouteDiscovery
1544{
1545    type Data = Ipv6RouteDiscoveryState<BT>;
1546}
1547
1548impl<BT: IpDeviceStateBindingsTypes> LockLevelFor<DualStackIpDeviceState<BT>>
1549    for crate::lock_ordering::Ipv6DeviceRouterSolicitations
1550{
1551    type Data = RsState<BT>;
1552}
1553
1554impl<BT: IpDeviceStateBindingsTypes> LockLevelFor<DualStackIpDeviceState<BT>>
1555    for crate::lock_ordering::IpDeviceConfiguration<Ipv6>
1556{
1557    type Data = Ipv6DeviceConfiguration;
1558}
1559
1560impl<BT: IpDeviceStateBindingsTypes> LockLevelFor<Ipv4AddressEntry<BT>>
1561    for crate::lock_ordering::Ipv4DeviceAddressState
1562{
1563    type Data = Ipv4AddressState<BT::Instant>;
1564}
1565
1566impl<BT: IpDeviceStateBindingsTypes> LockLevelFor<Ipv6AddressEntry<BT>>
1567    for crate::lock_ordering::Ipv6DeviceAddressDad
1568{
1569    type Data = Ipv6DadState<BT>;
1570}
1571
1572impl<BT: IpDeviceStateBindingsTypes> LockLevelFor<Ipv6AddressEntry<BT>>
1573    for crate::lock_ordering::Ipv6DeviceAddressState
1574{
1575    type Data = Ipv6AddressState<BT::Instant>;
1576}
1577
1578impl<BT: BindingsTypes, L> CounterContext<SlaacCounters> for CoreCtx<'_, BT, L> {
1579    fn counters(&self) -> &SlaacCounters {
1580        &self.unlocked_access::<crate::lock_ordering::UnlockedState>().ipv6.slaac_counters
1581    }
1582}