Skip to main content

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