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