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