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