netstack3_core/device/
base.rs

1// Copyright 2023 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//! Implementations of device layer traits for [`CoreCtx`].
6
7use core::fmt::Debug;
8use core::num::NonZeroU8;
9use core::ops::Deref as _;
10
11use lock_order::lock::{DelegatedOrderedLockAccess, LockLevelFor, UnlockedAccess};
12use lock_order::relation::LockBefore;
13use log::debug;
14use net_types::ethernet::Mac;
15use net_types::ip::{
16    AddrSubnet, Ip, IpAddress, IpInvariant, IpVersion, IpVersionMarker, Ipv4, Ipv4Addr, Ipv6,
17    Ipv6Addr, Mtu,
18};
19use net_types::{map_ip_twice, MulticastAddr, SpecifiedAddr, Witness as _};
20use netstack3_base::{
21    AnyDevice, BroadcastIpExt, CounterContext, DeviceIdContext, ExistsError,
22    IpDeviceAddressIdContext, Ipv4DeviceAddr, Ipv6DeviceAddr, NotFoundError, ReceivableFrameMeta,
23    RecvIpFrameMeta, ReferenceNotifiersExt, RemoveResourceResultWithContext,
24    ResourceCounterContext, SendFrameError, WeakDeviceIdentifier,
25};
26use netstack3_device::blackhole::{BlackholeDeviceCounters, BlackholeDeviceId};
27use netstack3_device::ethernet::{
28    self, EthernetDeviceCounters, EthernetDeviceId, EthernetIpLinkDeviceDynamicStateContext,
29    EthernetLinkDevice, EthernetPrimaryDeviceId, EthernetWeakDeviceId,
30};
31use netstack3_device::loopback::{self, LoopbackDevice, LoopbackDeviceId, LoopbackPrimaryDeviceId};
32use netstack3_device::pure_ip::{self, PureIpDeviceCounters, PureIpDeviceId};
33use netstack3_device::queue::TransmitQueueHandler;
34use netstack3_device::socket::{DeviceSocketCounters, DeviceSocketId, HeldDeviceSockets};
35use netstack3_device::{
36    for_any_device_id, ArpCounters, BaseDeviceId, DeviceCollectionContext,
37    DeviceConfigurationContext, DeviceCounters, DeviceId, DeviceLayerState, DeviceStateSpec,
38    Devices, DevicesIter, IpLinkDeviceState, IpLinkDeviceStateInner, Ipv6DeviceLinkLayerAddr,
39    OriginTracker, OriginTrackerContext, WeakDeviceId,
40};
41use netstack3_filter::ProofOfEgressCheck;
42use netstack3_ip::device::{
43    AddressId, AddressIdIter, AssignedAddressState as _, DualStackIpDeviceState,
44    IpDeviceAddressContext, IpDeviceConfigurationContext, IpDeviceFlags, IpDeviceIpExt,
45    IpDeviceSendContext, IpDeviceStateContext, Ipv4AddressEntry, Ipv4AddressState,
46    Ipv4DeviceConfiguration, Ipv6AddressEntry, Ipv6AddressState, Ipv6DadState,
47    Ipv6DeviceConfiguration, Ipv6DeviceConfigurationContext, Ipv6DeviceContext,
48    Ipv6NetworkLearnedParameters, PrimaryAddressId, WeakAddressId,
49};
50use netstack3_ip::nud::{
51    ConfirmationFlags, DynamicNeighborUpdateSource, NudHandler, NudIpHandler, NudUserConfig,
52};
53use netstack3_ip::{
54    self as ip, DeviceIpLayerMetadata, IpPacketDestination, IpRoutingDeviceContext, RawMetric,
55};
56use packet::{BufferMut, Serializer};
57use packet_formats::ethernet::EthernetIpExt;
58
59use crate::context::prelude::*;
60use crate::context::{CoreCtxAndResource, Locked, WrapLockLevel};
61use crate::ip::integration::CoreCtxWithIpDeviceConfiguration;
62use crate::{BindingsContext, BindingsTypes, CoreCtx, StackState};
63
64fn bytes_to_mac(b: &[u8]) -> Option<Mac> {
65    (b.len() >= Mac::BYTES).then(|| {
66        Mac::new({
67            let mut bytes = [0; Mac::BYTES];
68            bytes.copy_from_slice(&b[..Mac::BYTES]);
69            bytes
70        })
71    })
72}
73
74impl<
75        I: Ip,
76        BC: BindingsContext,
77        L: LockBefore<crate::lock_ordering::EthernetIpv4Arp>
78            + LockBefore<crate::lock_ordering::EthernetIpv6Nud>,
79    > NudIpHandler<I, BC> for CoreCtx<'_, BC, L>
80where
81    Self: NudHandler<I, EthernetLinkDevice, BC>
82        + DeviceIdContext<EthernetLinkDevice, DeviceId = EthernetDeviceId<BC>>,
83{
84    fn handle_neighbor_probe(
85        &mut self,
86        bindings_ctx: &mut BC,
87        device_id: &DeviceId<BC>,
88        neighbor: SpecifiedAddr<I::Addr>,
89        link_addr: &[u8],
90    ) {
91        match device_id {
92            DeviceId::Ethernet(id) => {
93                if let Some(link_addr) = bytes_to_mac(link_addr) {
94                    NudHandler::<I, EthernetLinkDevice, _>::handle_neighbor_update(
95                        self,
96                        bindings_ctx,
97                        &id,
98                        neighbor,
99                        link_addr,
100                        DynamicNeighborUpdateSource::Probe,
101                    )
102                }
103            }
104            // NUD is not supported on Loopback, Blackhole, and Pure IP devices.
105            DeviceId::Loopback(LoopbackDeviceId { .. })
106            | DeviceId::Blackhole(BlackholeDeviceId { .. })
107            | DeviceId::PureIp(PureIpDeviceId { .. }) => {}
108        }
109    }
110
111    fn handle_neighbor_confirmation(
112        &mut self,
113        bindings_ctx: &mut BC,
114        device_id: &DeviceId<BC>,
115        neighbor: SpecifiedAddr<I::Addr>,
116        link_addr: &[u8],
117        flags: ConfirmationFlags,
118    ) {
119        match device_id {
120            DeviceId::Ethernet(id) => {
121                if let Some(link_addr) = bytes_to_mac(link_addr) {
122                    NudHandler::<I, EthernetLinkDevice, _>::handle_neighbor_update(
123                        self,
124                        bindings_ctx,
125                        &id,
126                        neighbor,
127                        link_addr,
128                        DynamicNeighborUpdateSource::Confirmation(flags),
129                    )
130                }
131            }
132            // NUD is not supported on Loopback, Blackhole, and Pure IP devices.
133            DeviceId::Loopback(LoopbackDeviceId { .. })
134            | DeviceId::Blackhole(BlackholeDeviceId { .. })
135            | DeviceId::PureIp(PureIpDeviceId { .. }) => {}
136        }
137    }
138
139    fn flush_neighbor_table(&mut self, bindings_ctx: &mut BC, device_id: &DeviceId<BC>) {
140        match device_id {
141            DeviceId::Ethernet(id) => {
142                NudHandler::<I, EthernetLinkDevice, _>::flush(self, bindings_ctx, &id)
143            }
144            // NUD is not supported on Loopback, Blackhole, and Pure IP devices.
145            DeviceId::Loopback(LoopbackDeviceId { .. })
146            | DeviceId::Blackhole(BlackholeDeviceId { .. })
147            | DeviceId::PureIp(PureIpDeviceId { .. }) => {}
148        }
149    }
150}
151
152impl<I, D, L, BC> ReceivableFrameMeta<CoreCtx<'_, BC, L>, BC>
153    for RecvIpFrameMeta<D, DeviceIpLayerMetadata<BC>, I>
154where
155    BC: BindingsContext,
156    D: Into<DeviceId<BC>>,
157    L: LockBefore<crate::lock_ordering::IcmpAllSocketsSet<Ipv4>>,
158    I: Ip,
159{
160    fn receive_meta<B: BufferMut + Debug>(
161        self,
162        core_ctx: &mut CoreCtx<'_, BC, L>,
163        bindings_ctx: &mut BC,
164        frame: B,
165    ) {
166        let RecvIpFrameMeta {
167            device,
168            frame_dst,
169            ip_layer_metadata,
170            marker: IpVersionMarker { .. },
171        } = self;
172        let device = device.into();
173        match I::VERSION {
174            IpVersion::V4 => ip::receive_ipv4_packet(
175                core_ctx,
176                bindings_ctx,
177                &device,
178                frame_dst,
179                ip_layer_metadata,
180                frame,
181            ),
182            IpVersion::V6 => ip::receive_ipv6_packet(
183                core_ctx,
184                bindings_ctx,
185                &device,
186                frame_dst,
187                ip_layer_metadata,
188                frame,
189            ),
190        }
191    }
192}
193
194#[netstack3_macros::instantiate_ip_impl_block(I)]
195impl<
196        I: BroadcastIpExt,
197        BC: BindingsContext,
198        L: LockBefore<crate::lock_ordering::FilterState<I>>,
199    > IpDeviceSendContext<I, BC> for CoreCtx<'_, BC, L>
200{
201    fn send_ip_frame<S>(
202        &mut self,
203        bindings_ctx: &mut BC,
204        device: &DeviceId<BC>,
205        destination: IpPacketDestination<I, &DeviceId<BC>>,
206        ip_layer_metadata: DeviceIpLayerMetadata<BC>,
207        body: S,
208        ProofOfEgressCheck { .. }: ProofOfEgressCheck,
209    ) -> Result<(), SendFrameError<S>>
210    where
211        S: Serializer,
212        S::Buffer: BufferMut,
213    {
214        send_ip_frame(self, bindings_ctx, device, destination, ip_layer_metadata, body)
215    }
216}
217
218#[netstack3_macros::instantiate_ip_impl_block(I)]
219impl<
220        I: BroadcastIpExt,
221        Config,
222        BC: BindingsContext,
223        L: LockBefore<crate::lock_ordering::FilterState<I>>,
224    > IpDeviceSendContext<I, BC> for CoreCtxWithIpDeviceConfiguration<'_, Config, L, BC>
225{
226    fn send_ip_frame<S>(
227        &mut self,
228        bindings_ctx: &mut BC,
229        device: &DeviceId<BC>,
230        destination: IpPacketDestination<I, &DeviceId<BC>>,
231        ip_layer_metadata: DeviceIpLayerMetadata<BC>,
232        body: S,
233        ProofOfEgressCheck { .. }: ProofOfEgressCheck,
234    ) -> Result<(), SendFrameError<S>>
235    where
236        S: Serializer,
237        S::Buffer: BufferMut,
238    {
239        let Self { config: _, core_ctx } = self;
240        send_ip_frame(core_ctx, bindings_ctx, device, destination, ip_layer_metadata, body)
241    }
242}
243
244impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceConfiguration<Ipv4>>>
245    IpDeviceConfigurationContext<Ipv4, BC> for CoreCtx<'_, BC, L>
246{
247    type DevicesIter<'s> = DevicesIter<'s, BC>;
248    type WithIpDeviceConfigurationInnerCtx<'s> = CoreCtxWithIpDeviceConfiguration<
249        's,
250        &'s Ipv4DeviceConfiguration,
251        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv4>>,
252        BC,
253    >;
254    type WithIpDeviceConfigurationMutInner<'s> = CoreCtxWithIpDeviceConfiguration<
255        's,
256        &'s mut Ipv4DeviceConfiguration,
257        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv4>>,
258        BC,
259    >;
260    type DeviceAddressAndGroupsAccessor<'s> =
261        CoreCtx<'s, BC, WrapLockLevel<crate::lock_ordering::DeviceLayerState>>;
262
263    fn with_ip_device_configuration<
264        O,
265        F: FnOnce(&Ipv4DeviceConfiguration, Self::WithIpDeviceConfigurationInnerCtx<'_>) -> O,
266    >(
267        &mut self,
268        device_id: &Self::DeviceId,
269        cb: F,
270    ) -> O {
271        let mut core_ctx_and_resource = ip_device_state_and_core_ctx(self, device_id);
272        let (state, mut locked) = core_ctx_and_resource
273            .read_lock_with_and::<crate::lock_ordering::IpDeviceConfiguration<Ipv4>, _>(|c| {
274                c.right()
275            });
276        cb(
277            &state,
278            CoreCtxWithIpDeviceConfiguration { config: &state, core_ctx: locked.cast_core_ctx() },
279        )
280    }
281
282    fn with_ip_device_configuration_mut<
283        O,
284        F: FnOnce(Self::WithIpDeviceConfigurationMutInner<'_>) -> O,
285    >(
286        &mut self,
287        device_id: &Self::DeviceId,
288        cb: F,
289    ) -> O {
290        let mut core_ctx_and_resource = ip_device_state_and_core_ctx(self, device_id);
291        let (mut state, mut locked) = core_ctx_and_resource
292            .write_lock_with_and::<crate::lock_ordering::IpDeviceConfiguration<Ipv4>, _>(
293            |c| c.right(),
294        );
295        cb(CoreCtxWithIpDeviceConfiguration {
296            config: &mut state,
297            core_ctx: locked.cast_core_ctx(),
298        })
299    }
300
301    fn with_devices_and_state<
302        O,
303        F: FnOnce(Self::DevicesIter<'_>, Self::DeviceAddressAndGroupsAccessor<'_>) -> O,
304    >(
305        &mut self,
306        cb: F,
307    ) -> O {
308        let (devices, locked) = self.read_lock_and::<crate::lock_ordering::DeviceLayerState>();
309        cb(devices.iter(), locked)
310    }
311
312    fn loopback_id(&mut self) -> Option<Self::DeviceId> {
313        let devices = &*self.read_lock::<crate::lock_ordering::DeviceLayerState>();
314        devices.loopback.as_ref().map(|primary| DeviceId::Loopback(primary.clone_strong()))
315    }
316}
317
318impl<BC: BindingsContext, L> IpDeviceAddressIdContext<Ipv4> for CoreCtx<'_, BC, L> {
319    type AddressId = AddressId<Ipv4AddressEntry<BC>>;
320    type WeakAddressId = WeakAddressId<Ipv4AddressEntry<BC>>;
321}
322
323impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::Ipv4DeviceAddressState>>
324    IpDeviceAddressContext<Ipv4, BC> for CoreCtx<'_, BC, L>
325{
326    fn with_ip_address_state<O, F: FnOnce(&Ipv4AddressState<BC::Instant>) -> O>(
327        &mut self,
328        _: &Self::DeviceId,
329        addr_id: &Self::AddressId,
330        cb: F,
331    ) -> O {
332        let mut locked = self.adopt(addr_id.deref());
333        let let_binding_needed_for_lifetimes =
334            cb(&locked
335                .read_lock_with::<crate::lock_ordering::Ipv4DeviceAddressState, _>(|c| c.right()));
336        let_binding_needed_for_lifetimes
337    }
338
339    fn with_ip_address_state_mut<O, F: FnOnce(&mut Ipv4AddressState<BC::Instant>) -> O>(
340        &mut self,
341        _: &Self::DeviceId,
342        addr_id: &Self::AddressId,
343        cb: F,
344    ) -> O {
345        let mut locked = self.adopt(addr_id.deref());
346        let let_binding_needed_for_lifetimes =
347            cb(&mut locked
348                .write_lock_with::<crate::lock_ordering::Ipv4DeviceAddressState, _>(|c| c.right()));
349        let_binding_needed_for_lifetimes
350    }
351}
352
353impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceAddresses<Ipv4>>>
354    IpDeviceStateContext<Ipv4, BC> for CoreCtx<'_, BC, L>
355{
356    type IpDeviceAddressCtx<'a> =
357        CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::IpDeviceAddresses<Ipv4>>>;
358
359    fn with_ip_device_flags<O, F: FnOnce(&IpDeviceFlags) -> O>(
360        &mut self,
361        device_id: &Self::DeviceId,
362        cb: F,
363    ) -> O {
364        let mut state = ip_device_state(self, device_id);
365        let flags = &*state.lock::<crate::lock_ordering::IpDeviceFlags<Ipv4>>();
366        cb(flags)
367    }
368
369    fn add_ip_address(
370        &mut self,
371        device_id: &Self::DeviceId,
372        addr: AddrSubnet<Ipv4Addr, Ipv4DeviceAddr>,
373        config: <Ipv4 as IpDeviceIpExt>::AddressConfig<BC::Instant>,
374    ) -> Result<Self::AddressId, ExistsError> {
375        let mut state = ip_device_state(self, device_id);
376        let addr_id = state
377            .write_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv4>>()
378            .add(Ipv4AddressEntry::new(addr, config));
379        addr_id
380    }
381
382    fn remove_ip_address(
383        &mut self,
384        device_id: &Self::DeviceId,
385        addr: Self::AddressId,
386    ) -> RemoveResourceResultWithContext<AddrSubnet<Ipv4Addr>, BC> {
387        let mut state = ip_device_state(self, device_id);
388        let primary = state
389            .write_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv4>>()
390            .remove(&addr.addr().addr())
391            .expect("should exist when address ID exists");
392        assert!(PrimaryAddressId::ptr_eq(&primary, &addr));
393        core::mem::drop(addr);
394
395        BC::unwrap_or_notify_with_new_reference_notifier(primary.into_inner(), |entry| {
396            entry.addr_sub().to_witness::<SpecifiedAddr<_>>()
397        })
398    }
399
400    fn get_address_id(
401        &mut self,
402        device_id: &Self::DeviceId,
403        addr: SpecifiedAddr<Ipv4Addr>,
404    ) -> Result<Self::AddressId, NotFoundError> {
405        let mut state = ip_device_state(self, device_id);
406        let addr_id = state
407            .read_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv4>>()
408            .iter()
409            .find(|a| a.addr().addr() == *addr)
410            .map(PrimaryAddressId::clone_strong)
411            .ok_or(NotFoundError);
412        addr_id
413    }
414
415    type AddressIdsIter<'a> = AddressIdIter<'a, Ipv4, BC>;
416    fn with_address_ids<
417        O,
418        F: FnOnce(Self::AddressIdsIter<'_>, &mut Self::IpDeviceAddressCtx<'_>) -> O,
419    >(
420        &mut self,
421        device_id: &Self::DeviceId,
422        cb: F,
423    ) -> O {
424        let mut core_ctx_and_resource = ip_device_state_and_core_ctx(self, device_id);
425        let (state, mut locked) = core_ctx_and_resource
426            .read_lock_with_and::<crate::lock_ordering::IpDeviceAddresses<Ipv4>, _>(|c| c.right());
427        cb(state.strong_iter(), &mut locked.cast_core_ctx())
428    }
429
430    fn with_default_hop_limit<O, F: FnOnce(&NonZeroU8) -> O>(
431        &mut self,
432        device_id: &Self::DeviceId,
433        cb: F,
434    ) -> O {
435        let mut state = ip_device_state(self, device_id);
436        let mut state = state.read_lock::<crate::lock_ordering::IpDeviceDefaultHopLimit<Ipv4>>();
437        cb(&mut state)
438    }
439
440    fn with_default_hop_limit_mut<O, F: FnOnce(&mut NonZeroU8) -> O>(
441        &mut self,
442        device_id: &Self::DeviceId,
443        cb: F,
444    ) -> O {
445        let mut state = ip_device_state(self, device_id);
446        let mut state = state.write_lock::<crate::lock_ordering::IpDeviceDefaultHopLimit<Ipv4>>();
447        cb(&mut state)
448    }
449
450    fn join_link_multicast_group(
451        &mut self,
452        bindings_ctx: &mut BC,
453        device_id: &Self::DeviceId,
454        multicast_addr: MulticastAddr<Ipv4Addr>,
455    ) {
456        join_link_multicast_group(self, bindings_ctx, device_id, multicast_addr)
457    }
458
459    fn leave_link_multicast_group(
460        &mut self,
461        bindings_ctx: &mut BC,
462        device_id: &Self::DeviceId,
463        multicast_addr: MulticastAddr<Ipv4Addr>,
464    ) {
465        leave_link_multicast_group(self, bindings_ctx, device_id, multicast_addr)
466    }
467}
468
469impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>>
470    Ipv6DeviceConfigurationContext<BC> for CoreCtx<'_, BC, L>
471{
472    type Ipv6DeviceStateCtx<'s> = CoreCtxWithIpDeviceConfiguration<
473        's,
474        &'s Ipv6DeviceConfiguration,
475        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
476        BC,
477    >;
478    type WithIpv6DeviceConfigurationMutInner<'s> = CoreCtxWithIpDeviceConfiguration<
479        's,
480        &'s mut Ipv6DeviceConfiguration,
481        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
482        BC,
483    >;
484
485    fn with_ipv6_device_configuration<
486        O,
487        F: FnOnce(&Ipv6DeviceConfiguration, Self::Ipv6DeviceStateCtx<'_>) -> O,
488    >(
489        &mut self,
490        device_id: &Self::DeviceId,
491        cb: F,
492    ) -> O {
493        IpDeviceConfigurationContext::<Ipv6, _>::with_ip_device_configuration(self, device_id, cb)
494    }
495
496    fn with_ipv6_device_configuration_mut<
497        O,
498        F: FnOnce(Self::WithIpv6DeviceConfigurationMutInner<'_>) -> O,
499    >(
500        &mut self,
501        device_id: &Self::DeviceId,
502        cb: F,
503    ) -> O {
504        IpDeviceConfigurationContext::<Ipv6, _>::with_ip_device_configuration_mut(
505            self, device_id, cb,
506        )
507    }
508}
509
510impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>>
511    IpDeviceConfigurationContext<Ipv6, BC> for CoreCtx<'_, BC, L>
512{
513    type DevicesIter<'s> = DevicesIter<'s, BC>;
514    type WithIpDeviceConfigurationInnerCtx<'s> = CoreCtxWithIpDeviceConfiguration<
515        's,
516        &'s Ipv6DeviceConfiguration,
517        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
518        BC,
519    >;
520    type WithIpDeviceConfigurationMutInner<'s> = CoreCtxWithIpDeviceConfiguration<
521        's,
522        &'s mut Ipv6DeviceConfiguration,
523        WrapLockLevel<crate::lock_ordering::IpDeviceConfiguration<Ipv6>>,
524        BC,
525    >;
526    type DeviceAddressAndGroupsAccessor<'s> =
527        CoreCtx<'s, BC, WrapLockLevel<crate::lock_ordering::DeviceLayerState>>;
528
529    fn with_ip_device_configuration<
530        O,
531        F: FnOnce(&Ipv6DeviceConfiguration, Self::WithIpDeviceConfigurationInnerCtx<'_>) -> O,
532    >(
533        &mut self,
534        device_id: &Self::DeviceId,
535        cb: F,
536    ) -> O {
537        let mut core_ctx_and_resource = ip_device_state_and_core_ctx(self, device_id);
538        let (state, mut locked) = core_ctx_and_resource
539            .read_lock_with_and::<crate::lock_ordering::IpDeviceConfiguration<Ipv6>, _>(|c| {
540                c.right()
541            });
542        cb(
543            &state,
544            CoreCtxWithIpDeviceConfiguration { config: &state, core_ctx: locked.cast_core_ctx() },
545        )
546    }
547
548    fn with_ip_device_configuration_mut<
549        O,
550        F: FnOnce(Self::WithIpDeviceConfigurationMutInner<'_>) -> O,
551    >(
552        &mut self,
553        device_id: &Self::DeviceId,
554        cb: F,
555    ) -> O {
556        let mut core_ctx_and_resource = ip_device_state_and_core_ctx(self, device_id);
557        let (mut state, mut locked) = core_ctx_and_resource
558            .write_lock_with_and::<crate::lock_ordering::IpDeviceConfiguration<Ipv6>, _>(
559            |c| c.right(),
560        );
561        cb(CoreCtxWithIpDeviceConfiguration {
562            config: &mut state,
563            core_ctx: locked.cast_core_ctx(),
564        })
565    }
566
567    fn with_devices_and_state<
568        O,
569        F: FnOnce(Self::DevicesIter<'_>, Self::DeviceAddressAndGroupsAccessor<'_>) -> O,
570    >(
571        &mut self,
572        cb: F,
573    ) -> O {
574        let (devices, locked) = self.read_lock_and::<crate::lock_ordering::DeviceLayerState>();
575        cb(devices.iter(), locked)
576    }
577
578    fn loopback_id(&mut self) -> Option<Self::DeviceId> {
579        let devices = &*self.read_lock::<crate::lock_ordering::DeviceLayerState>();
580        devices.loopback.as_ref().map(|primary| DeviceId::Loopback(primary.clone_strong()))
581    }
582}
583
584impl<BC: BindingsContext, L> IpDeviceAddressIdContext<Ipv6> for CoreCtx<'_, BC, L> {
585    type AddressId = AddressId<Ipv6AddressEntry<BC>>;
586    type WeakAddressId = WeakAddressId<Ipv6AddressEntry<BC>>;
587}
588
589impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::Ipv6DeviceAddressState>>
590    IpDeviceAddressContext<Ipv6, BC> for CoreCtx<'_, BC, L>
591{
592    fn with_ip_address_state<O, F: FnOnce(&Ipv6AddressState<BC::Instant>) -> O>(
593        &mut self,
594        _device_id: &Self::DeviceId,
595        addr_id: &Self::AddressId,
596        cb: F,
597    ) -> O {
598        let mut locked = self.adopt(addr_id.deref());
599        let x = cb(&locked
600            .read_lock_with::<crate::lock_ordering::Ipv6DeviceAddressState, _>(|c| c.right()));
601        x
602    }
603
604    fn with_ip_address_state_mut<O, F: FnOnce(&mut Ipv6AddressState<BC::Instant>) -> O>(
605        &mut self,
606        _device_id: &Self::DeviceId,
607        addr_id: &Self::AddressId,
608        cb: F,
609    ) -> O {
610        let mut locked = self.adopt(addr_id.deref());
611        let x = cb(&mut locked
612            .write_lock_with::<crate::lock_ordering::Ipv6DeviceAddressState, _>(|c| c.right()));
613        x
614    }
615}
616
617impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceAddresses<Ipv6>>>
618    IpDeviceStateContext<Ipv6, BC> for CoreCtx<'_, BC, L>
619{
620    type IpDeviceAddressCtx<'a> =
621        CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::IpDeviceAddresses<Ipv6>>>;
622
623    fn with_ip_device_flags<O, F: FnOnce(&IpDeviceFlags) -> O>(
624        &mut self,
625        device_id: &Self::DeviceId,
626        cb: F,
627    ) -> O {
628        let mut state = ip_device_state(self, device_id);
629        let flags = &*state.lock::<crate::lock_ordering::IpDeviceFlags<Ipv6>>();
630        cb(flags)
631    }
632
633    fn add_ip_address(
634        &mut self,
635        device_id: &Self::DeviceId,
636        addr: AddrSubnet<Ipv6Addr, Ipv6DeviceAddr>,
637        config: <Ipv6 as IpDeviceIpExt>::AddressConfig<BC::Instant>,
638    ) -> Result<Self::AddressId, ExistsError> {
639        let mut state = ip_device_state(self, device_id);
640        let addr_id = state
641            .write_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv6>>()
642            .add(Ipv6AddressEntry::new(addr, Ipv6DadState::Uninitialized, config));
643        addr_id
644    }
645
646    fn remove_ip_address(
647        &mut self,
648        device_id: &Self::DeviceId,
649        addr: Self::AddressId,
650    ) -> RemoveResourceResultWithContext<AddrSubnet<Ipv6Addr>, BC> {
651        let mut state = ip_device_state(self, device_id);
652        let primary = state
653            .write_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv6>>()
654            .remove(&addr.addr().addr())
655            .expect("should exist when address ID exists");
656        assert!(PrimaryAddressId::ptr_eq(&primary, &addr));
657        core::mem::drop(addr);
658
659        BC::unwrap_or_notify_with_new_reference_notifier(primary.into_inner(), |entry| {
660            entry.addr_sub().to_witness::<SpecifiedAddr<_>>()
661        })
662    }
663
664    fn get_address_id(
665        &mut self,
666        device_id: &Self::DeviceId,
667        addr: SpecifiedAddr<Ipv6Addr>,
668    ) -> Result<Self::AddressId, NotFoundError> {
669        let mut state = ip_device_state(self, device_id);
670        let addr_id = state
671            .read_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv6>>()
672            .iter()
673            .find_map(|a| (a.addr().addr() == *addr).then(|| PrimaryAddressId::clone_strong(a)))
674            .ok_or(NotFoundError);
675        addr_id
676    }
677
678    type AddressIdsIter<'a> = AddressIdIter<'a, Ipv6, BC>;
679    fn with_address_ids<
680        O,
681        F: FnOnce(Self::AddressIdsIter<'_>, &mut Self::IpDeviceAddressCtx<'_>) -> O,
682    >(
683        &mut self,
684        device_id: &Self::DeviceId,
685        cb: F,
686    ) -> O {
687        let mut core_ctx_and_resource = ip_device_state_and_core_ctx(self, device_id);
688        let (state, mut core_ctx) = core_ctx_and_resource
689            .read_lock_with_and::<crate::lock_ordering::IpDeviceAddresses<Ipv6>, _>(|c| c.right());
690        cb(state.strong_iter(), &mut core_ctx.cast_core_ctx())
691    }
692
693    fn with_default_hop_limit<O, F: FnOnce(&NonZeroU8) -> O>(
694        &mut self,
695        device_id: &Self::DeviceId,
696        cb: F,
697    ) -> O {
698        let mut state = ip_device_state(self, device_id);
699        let mut state = state.read_lock::<crate::lock_ordering::IpDeviceDefaultHopLimit<Ipv6>>();
700        cb(&mut state)
701    }
702
703    fn with_default_hop_limit_mut<O, F: FnOnce(&mut NonZeroU8) -> O>(
704        &mut self,
705        device_id: &Self::DeviceId,
706        cb: F,
707    ) -> O {
708        let mut state = ip_device_state(self, device_id);
709        let mut state = state.write_lock::<crate::lock_ordering::IpDeviceDefaultHopLimit<Ipv6>>();
710        cb(&mut state)
711    }
712
713    fn join_link_multicast_group(
714        &mut self,
715        bindings_ctx: &mut BC,
716        device_id: &Self::DeviceId,
717        multicast_addr: MulticastAddr<Ipv6Addr>,
718    ) {
719        join_link_multicast_group(self, bindings_ctx, device_id, multicast_addr)
720    }
721
722    fn leave_link_multicast_group(
723        &mut self,
724        bindings_ctx: &mut BC,
725        device_id: &Self::DeviceId,
726        multicast_addr: MulticastAddr<Ipv6Addr>,
727    ) {
728        leave_link_multicast_group(self, bindings_ctx, device_id, multicast_addr)
729    }
730}
731
732impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceAddresses<Ipv6>>>
733    Ipv6DeviceContext<BC> for CoreCtx<'_, BC, L>
734{
735    type LinkLayerAddr = Ipv6DeviceLinkLayerAddr;
736
737    fn get_link_layer_addr(
738        &mut self,
739        device_id: &Self::DeviceId,
740    ) -> Option<Ipv6DeviceLinkLayerAddr> {
741        match device_id {
742            DeviceId::Ethernet(id) => {
743                Some(Ipv6DeviceLinkLayerAddr::Mac(ethernet::get_mac(self, &id).get()))
744            }
745            DeviceId::Loopback(LoopbackDeviceId { .. })
746            | DeviceId::Blackhole(BlackholeDeviceId { .. })
747            | DeviceId::PureIp(PureIpDeviceId { .. }) => None,
748        }
749    }
750
751    fn set_link_mtu(&mut self, device_id: &Self::DeviceId, mtu: Mtu) {
752        if mtu < Ipv6::MINIMUM_LINK_MTU {
753            return;
754        }
755
756        match device_id {
757            DeviceId::Ethernet(id) => ethernet::set_mtu(self, &id, mtu),
758            DeviceId::Loopback(LoopbackDeviceId { .. }) => {}
759            DeviceId::PureIp(id) => pure_ip::set_mtu(self, &id, mtu),
760            DeviceId::Blackhole(BlackholeDeviceId { .. }) => {}
761        }
762    }
763
764    fn with_network_learned_parameters<O, F: FnOnce(&Ipv6NetworkLearnedParameters) -> O>(
765        &mut self,
766        device_id: &Self::DeviceId,
767        cb: F,
768    ) -> O {
769        let mut state = ip_device_state(self, device_id);
770        let state = state.read_lock::<crate::lock_ordering::Ipv6DeviceLearnedParams>();
771        cb(&state)
772    }
773
774    fn with_network_learned_parameters_mut<O, F: FnOnce(&mut Ipv6NetworkLearnedParameters) -> O>(
775        &mut self,
776        device_id: &Self::DeviceId,
777        cb: F,
778    ) -> O {
779        let mut state = ip_device_state(self, device_id);
780        let mut state = state.write_lock::<crate::lock_ordering::Ipv6DeviceLearnedParams>();
781        cb(&mut state)
782    }
783}
784
785impl<BT: BindingsTypes, L> DeviceIdContext<EthernetLinkDevice> for CoreCtx<'_, BT, L> {
786    type DeviceId = EthernetDeviceId<BT>;
787    type WeakDeviceId = EthernetWeakDeviceId<BT>;
788}
789
790impl<BT: BindingsTypes> DelegatedOrderedLockAccess<Devices<BT>> for StackState<BT> {
791    type Inner = DeviceLayerState<BT>;
792    fn delegate_ordered_lock_access(&self) -> &Self::Inner {
793        &self.device
794    }
795}
796
797impl<BT: BindingsTypes> LockLevelFor<StackState<BT>> for crate::lock_ordering::DeviceLayerState {
798    type Data = Devices<BT>;
799}
800
801impl<BT: BindingsTypes, L> DeviceIdContext<AnyDevice> for CoreCtx<'_, BT, L> {
802    type DeviceId = DeviceId<BT>;
803    type WeakDeviceId = WeakDeviceId<BT>;
804}
805
806/// It is safe to provide unlocked access to [`IpLinkDeviceStateInner`] itself
807/// here because care has been taken to avoid exposing publicly to the core
808/// integration crate any state that is held by a lock, as opposed to read-only
809/// state that can be accessed safely at any lock level, e.g. state with no
810/// interior mutability or atomics.
811///
812/// Access to state held by locks *must* be mediated using the global lock
813/// ordering declared in [`crate::lock_ordering`].
814impl<T, BT: BindingsTypes> UnlockedAccess<crate::lock_ordering::UnlockedState>
815    for IpLinkDeviceStateInner<T, BT>
816{
817    type Data = IpLinkDeviceStateInner<T, BT>;
818    type Guard<'l>
819        = &'l IpLinkDeviceStateInner<T, BT>
820    where
821        Self: 'l;
822
823    fn access(&self) -> Self::Guard<'_> {
824        &self
825    }
826}
827
828pub(crate) fn device_state<'a, BT: BindingsTypes, L, D: DeviceStateSpec>(
829    core_ctx: &'a mut CoreCtx<'_, BT, L>,
830    device_id: &'a BaseDeviceId<D, BT>,
831) -> Locked<&'a IpLinkDeviceState<D, BT>, L> {
832    let state = device_id.device_state(
833        &core_ctx.unlocked_access::<crate::lock_ordering::UnlockedState>().device.origin,
834    );
835    core_ctx.replace(state)
836}
837
838pub(crate) fn device_state_and_core_ctx<'a, BT: BindingsTypes, L, D: DeviceStateSpec>(
839    core_ctx: &'a mut CoreCtx<'_, BT, L>,
840    id: &'a BaseDeviceId<D, BT>,
841) -> CoreCtxAndResource<'a, BT, IpLinkDeviceState<D, BT>, L> {
842    let state = id.device_state(
843        &core_ctx.unlocked_access::<crate::lock_ordering::UnlockedState>().device.origin,
844    );
845    core_ctx.adopt(state)
846}
847
848pub(crate) fn ip_device_state<'a, BC: BindingsContext, L>(
849    core_ctx: &'a mut CoreCtx<'_, BC, L>,
850    device: &'a DeviceId<BC>,
851) -> Locked<&'a DualStackIpDeviceState<BC>, L> {
852    for_any_device_id!(
853        DeviceId,
854        device,
855        id => {
856            let state = id.device_state(
857                &core_ctx.unlocked_access::<crate::lock_ordering::UnlockedState>().device.origin
858            );
859            core_ctx.replace(state.as_ref())
860        }
861    )
862}
863
864pub(crate) fn ip_device_state_and_core_ctx<'a, BC: BindingsContext, L>(
865    core_ctx: &'a mut CoreCtx<'_, BC, L>,
866    device: &'a DeviceId<BC>,
867) -> CoreCtxAndResource<'a, BC, DualStackIpDeviceState<BC>, L> {
868    for_any_device_id!(
869        DeviceId,
870        device,
871        id => {
872            let state = id.device_state(
873                &core_ctx.unlocked_access::<crate::lock_ordering::UnlockedState>().device.origin
874            );
875            core_ctx.adopt(state.as_ref())
876        }
877    )
878}
879
880pub(crate) fn get_mtu<
881    BC: BindingsContext,
882    L: LockBefore<crate::lock_ordering::EthernetDeviceDynamicState>,
883>(
884    core_ctx: &mut CoreCtx<'_, BC, L>,
885    device: &DeviceId<BC>,
886) -> Mtu {
887    match device {
888        DeviceId::Ethernet(id) => ethernet::get_mtu(core_ctx, &id),
889        DeviceId::Loopback(id) => device_state(core_ctx, id).cast_with(|s| &s.link.mtu).copied(),
890        DeviceId::PureIp(id) => pure_ip::get_mtu(core_ctx, &id),
891        DeviceId::Blackhole(_id) => Mtu::no_limit(),
892    }
893}
894
895fn join_link_multicast_group<
896    BC: BindingsContext,
897    A: IpAddress,
898    L: LockBefore<crate::lock_ordering::EthernetDeviceDynamicState>,
899>(
900    core_ctx: &mut CoreCtx<'_, BC, L>,
901    bindings_ctx: &mut BC,
902    device_id: &DeviceId<BC>,
903    multicast_addr: MulticastAddr<A>,
904) {
905    match device_id {
906        DeviceId::Ethernet(id) => ethernet::join_link_multicast(
907            core_ctx,
908            bindings_ctx,
909            &id,
910            MulticastAddr::from(&multicast_addr),
911        ),
912        DeviceId::Loopback(LoopbackDeviceId { .. })
913        | DeviceId::PureIp(PureIpDeviceId { .. })
914        | DeviceId::Blackhole(BlackholeDeviceId { .. }) => {}
915    }
916}
917
918fn leave_link_multicast_group<
919    BC: BindingsContext,
920    A: IpAddress,
921    L: LockBefore<crate::lock_ordering::EthernetDeviceDynamicState>,
922>(
923    core_ctx: &mut CoreCtx<'_, BC, L>,
924    bindings_ctx: &mut BC,
925    device_id: &DeviceId<BC>,
926    multicast_addr: MulticastAddr<A>,
927) {
928    match device_id {
929        DeviceId::Ethernet(id) => ethernet::leave_link_multicast(
930            core_ctx,
931            bindings_ctx,
932            &id,
933            MulticastAddr::from(&multicast_addr),
934        ),
935        DeviceId::Loopback(LoopbackDeviceId { .. })
936        | DeviceId::PureIp(PureIpDeviceId { .. })
937        | DeviceId::Blackhole(BlackholeDeviceId { .. }) => {}
938    }
939}
940
941fn send_ip_frame<BC, S, I, L>(
942    core_ctx: &mut CoreCtx<'_, BC, L>,
943    bindings_ctx: &mut BC,
944    device: &DeviceId<BC>,
945    destination: IpPacketDestination<I, &DeviceId<BC>>,
946    ip_layer_metadata: DeviceIpLayerMetadata<BC>,
947    body: S,
948) -> Result<(), SendFrameError<S>>
949where
950    BC: BindingsContext,
951    S: Serializer,
952    S::Buffer: BufferMut,
953    I: EthernetIpExt + BroadcastIpExt,
954    L: LockBefore<crate::lock_ordering::IpState<I>>
955        + LockBefore<crate::lock_ordering::LoopbackTxQueue>
956        + LockBefore<crate::lock_ordering::PureIpDeviceTxQueue>,
957    for<'a> CoreCtx<'a, BC, L>: EthernetIpLinkDeviceDynamicStateContext<BC, DeviceId = EthernetDeviceId<BC>>
958        + NudHandler<I, EthernetLinkDevice, BC>
959        + TransmitQueueHandler<EthernetLinkDevice, BC, Meta = BC::TxMetadata>,
960{
961    match device {
962        DeviceId::Ethernet(id) => ethernet::send_ip_frame(
963            core_ctx,
964            bindings_ctx,
965            id,
966            destination,
967            body,
968            ip_layer_metadata.into_tx_metadata(),
969        ),
970        DeviceId::Loopback(id) => loopback::send_ip_frame(
971            core_ctx,
972            bindings_ctx,
973            id,
974            destination,
975            ip_layer_metadata,
976            body,
977        ),
978        DeviceId::PureIp(id) => pure_ip::send_ip_frame(
979            core_ctx,
980            bindings_ctx,
981            id,
982            destination,
983            body,
984            ip_layer_metadata.into_tx_metadata(),
985        ),
986        DeviceId::Blackhole(id) => {
987            // Just drop the frame.
988            debug!("dropping frame in send_ip_frame on blackhole device {id:?}");
989            core_ctx.increment_both(id, DeviceCounters::send_frame::<I>);
990            Ok(())
991        }
992    }
993}
994
995impl<'a, BT, L> DeviceCollectionContext<EthernetLinkDevice, BT> for CoreCtx<'a, BT, L>
996where
997    BT: BindingsTypes,
998    L: LockBefore<crate::lock_ordering::DeviceLayerState>,
999{
1000    fn insert(&mut self, device: EthernetPrimaryDeviceId<BT>) {
1001        let mut devices = self.write_lock::<crate::lock_ordering::DeviceLayerState>();
1002        let strong = device.clone_strong();
1003        assert!(devices.ethernet.insert(strong, device).is_none());
1004    }
1005
1006    fn remove(&mut self, device: &EthernetDeviceId<BT>) -> Option<EthernetPrimaryDeviceId<BT>> {
1007        let mut devices = self.write_lock::<crate::lock_ordering::DeviceLayerState>();
1008        devices.ethernet.remove(device)
1009    }
1010}
1011
1012impl<'a, BT, L> DeviceCollectionContext<LoopbackDevice, BT> for CoreCtx<'a, BT, L>
1013where
1014    BT: BindingsTypes,
1015    L: LockBefore<crate::lock_ordering::DeviceLayerState>,
1016{
1017    fn insert(&mut self, device: LoopbackPrimaryDeviceId<BT>) {
1018        let mut devices = self.write_lock::<crate::lock_ordering::DeviceLayerState>();
1019        let prev = devices.loopback.replace(device);
1020        // NB: At a previous version we returned an error when bindings tried to
1021        // install the loopback device twice. Turns out that all callers
1022        // panicked on that error so might as well panic here and simplify the
1023        // API code.
1024        assert!(prev.is_none(), "can't install loopback device more than once");
1025    }
1026
1027    fn remove(&mut self, device: &LoopbackDeviceId<BT>) -> Option<LoopbackPrimaryDeviceId<BT>> {
1028        // We assert here because there's an invariant that only one loopback
1029        // device exists. So if we're calling this function with a loopback
1030        // device ID then it *must* exist and it *must* be the same as the
1031        // currently installed device.
1032        let mut devices = self.write_lock::<crate::lock_ordering::DeviceLayerState>();
1033        let primary = devices.loopback.take().expect("loopback device not installed");
1034        assert_eq!(device, &primary);
1035        Some(primary)
1036    }
1037}
1038
1039impl<'a, BT: BindingsTypes, L> OriginTrackerContext for CoreCtx<'a, BT, L> {
1040    fn origin_tracker(&mut self) -> OriginTracker {
1041        self.unlocked_access::<crate::lock_ordering::UnlockedState>().device.origin.clone()
1042    }
1043}
1044
1045impl<'a, BT, L> DeviceConfigurationContext<EthernetLinkDevice> for CoreCtx<'a, BT, L>
1046where
1047    L: LockBefore<crate::lock_ordering::NudConfig<Ipv4>>
1048        + LockBefore<crate::lock_ordering::NudConfig<Ipv6>>,
1049    BT: BindingsTypes,
1050{
1051    fn with_nud_config<I: Ip, O, F: FnOnce(Option<&NudUserConfig>) -> O>(
1052        &mut self,
1053        device_id: &Self::DeviceId,
1054        f: F,
1055    ) -> O {
1056        let state = device_state(self, device_id);
1057        // NB: We need map_ip here because we can't write a lock ordering
1058        // restriction for all IP versions.
1059        let IpInvariant(o) =
1060            map_ip_twice!(I, IpInvariant((state, f)), |IpInvariant((mut state, f))| {
1061                IpInvariant(f(Some(&*state.read_lock::<crate::lock_ordering::NudConfig<I>>())))
1062            });
1063        o
1064    }
1065
1066    fn with_nud_config_mut<I: Ip, O, F: FnOnce(Option<&mut NudUserConfig>) -> O>(
1067        &mut self,
1068        device_id: &Self::DeviceId,
1069        f: F,
1070    ) -> O {
1071        let state = device_state(self, device_id);
1072        // NB: We need map_ip here because we can't write a lock ordering
1073        // restriction for all IP versions.
1074        let IpInvariant(o) =
1075            map_ip_twice!(I, IpInvariant((state, f)), |IpInvariant((mut state, f))| {
1076                IpInvariant(f(Some(&mut *state.write_lock::<crate::lock_ordering::NudConfig<I>>())))
1077            });
1078        o
1079    }
1080}
1081
1082impl<'a, BT, L> DeviceConfigurationContext<LoopbackDevice> for CoreCtx<'a, BT, L>
1083where
1084    BT: BindingsTypes,
1085{
1086    fn with_nud_config<I: Ip, O, F: FnOnce(Option<&NudUserConfig>) -> O>(
1087        &mut self,
1088        _device_id: &Self::DeviceId,
1089        f: F,
1090    ) -> O {
1091        // Loopback doesn't support NUD.
1092        f(None)
1093    }
1094
1095    fn with_nud_config_mut<I: Ip, O, F: FnOnce(Option<&mut NudUserConfig>) -> O>(
1096        &mut self,
1097        _device_id: &Self::DeviceId,
1098        f: F,
1099    ) -> O {
1100        // Loopback doesn't support NUD.
1101        f(None)
1102    }
1103}
1104
1105impl<BC: BindingsContext, L> CounterContext<EthernetDeviceCounters> for CoreCtx<'_, BC, L> {
1106    fn counters(&self) -> &EthernetDeviceCounters {
1107        &self.unlocked_access::<crate::lock_ordering::UnlockedState>().device.ethernet_counters
1108    }
1109}
1110
1111impl<BC: BindingsContext, L> CounterContext<DeviceSocketCounters> for CoreCtx<'_, BC, L> {
1112    fn counters(&self) -> &DeviceSocketCounters {
1113        &self.unlocked_access::<crate::lock_ordering::UnlockedState>().device.device_socket_counters
1114    }
1115}
1116
1117impl<BC: BindingsContext, L> CounterContext<PureIpDeviceCounters> for CoreCtx<'_, BC, L> {
1118    fn counters(&self) -> &PureIpDeviceCounters {
1119        &self.unlocked_access::<crate::lock_ordering::UnlockedState>().device.pure_ip_counters
1120    }
1121}
1122
1123impl<'a, BC: BindingsContext, L> ResourceCounterContext<DeviceId<BC>, DeviceCounters>
1124    for CoreCtx<'a, BC, L>
1125{
1126    fn per_resource_counters<'b>(&'b self, device_id: &'b DeviceId<BC>) -> &'b DeviceCounters {
1127        for_any_device_id!(DeviceId, device_id, id => {
1128            let state = id.device_state(
1129                &self.unlocked_access::<crate::lock_ordering::UnlockedState>().device.origin,
1130            );
1131            &state.counters
1132        })
1133    }
1134}
1135
1136impl<'a, BC: BindingsContext, D: DeviceStateSpec, L>
1137    ResourceCounterContext<BaseDeviceId<D, BC>, DeviceCounters> for CoreCtx<'a, BC, L>
1138{
1139    fn per_resource_counters<'b>(
1140        &'b self,
1141        device_id: &'b BaseDeviceId<D, BC>,
1142    ) -> &'b DeviceCounters {
1143        let state = device_id.device_state(
1144            &self.unlocked_access::<crate::lock_ordering::UnlockedState>().device.origin,
1145        );
1146        &state.counters
1147    }
1148}
1149
1150impl<'a, BC: BindingsContext, L, D: WeakDeviceIdentifier>
1151    ResourceCounterContext<DeviceSocketId<D, BC>, DeviceSocketCounters> for CoreCtx<'a, BC, L>
1152{
1153    fn per_resource_counters<'b>(
1154        &'b self,
1155        socket_id: &'b DeviceSocketId<D, BC>,
1156    ) -> &'b DeviceSocketCounters {
1157        socket_id.counters()
1158    }
1159}
1160
1161impl<'a, BC: BindingsContext, L>
1162    ResourceCounterContext<EthernetDeviceId<BC>, EthernetDeviceCounters> for CoreCtx<'a, BC, L>
1163{
1164    fn per_resource_counters<'b>(
1165        &'b self,
1166        device_id: &'b EthernetDeviceId<BC>,
1167    ) -> &'b EthernetDeviceCounters {
1168        let state = device_id.device_state(
1169            &self.unlocked_access::<crate::lock_ordering::UnlockedState>().device.origin,
1170        );
1171        &state.link.counters
1172    }
1173}
1174
1175impl<'a, BC: BindingsContext, L>
1176    ResourceCounterContext<LoopbackDeviceId<BC>, EthernetDeviceCounters> for CoreCtx<'a, BC, L>
1177{
1178    fn per_resource_counters<'b>(
1179        &'b self,
1180        device_id: &'b LoopbackDeviceId<BC>,
1181    ) -> &'b EthernetDeviceCounters {
1182        let state = device_id.device_state(
1183            &self.unlocked_access::<crate::lock_ordering::UnlockedState>().device.origin,
1184        );
1185        &state.link.counters
1186    }
1187}
1188
1189impl<'a, BC: BindingsContext, L> ResourceCounterContext<PureIpDeviceId<BC>, PureIpDeviceCounters>
1190    for CoreCtx<'a, BC, L>
1191{
1192    fn per_resource_counters<'b>(
1193        &'b self,
1194        device_id: &'b PureIpDeviceId<BC>,
1195    ) -> &'b PureIpDeviceCounters {
1196        let state = device_id.device_state(
1197            &self.unlocked_access::<crate::lock_ordering::UnlockedState>().device.origin,
1198        );
1199        &state.link.counters
1200    }
1201}
1202
1203// Blackhole devices have no device-specific counters.
1204impl<'a, BC: BindingsContext, L> CounterContext<BlackholeDeviceCounters> for CoreCtx<'a, BC, L> {
1205    fn counters(&self) -> &BlackholeDeviceCounters {
1206        &BlackholeDeviceCounters
1207    }
1208}
1209
1210impl<'a, BC: BindingsContext, L>
1211    ResourceCounterContext<BlackholeDeviceId<BC>, BlackholeDeviceCounters> for CoreCtx<'a, BC, L>
1212{
1213    fn per_resource_counters<'b>(
1214        &'b self,
1215        _device_id: &'b BlackholeDeviceId<BC>,
1216    ) -> &'b BlackholeDeviceCounters {
1217        &BlackholeDeviceCounters
1218    }
1219}
1220
1221impl<T, BT: BindingsTypes> LockLevelFor<IpLinkDeviceStateInner<T, BT>>
1222    for crate::lock_ordering::DeviceSockets
1223{
1224    type Data = HeldDeviceSockets<BT>;
1225}
1226
1227impl<BT: BindingsTypes, L> CounterContext<DeviceCounters> for CoreCtx<'_, BT, L> {
1228    fn counters(&self) -> &DeviceCounters {
1229        &self.unlocked_access::<crate::lock_ordering::UnlockedState>().device.counters
1230    }
1231}
1232
1233impl<I: IpDeviceIpExt, BC: BindingsContext, L> IpRoutingDeviceContext<I> for CoreCtx<'_, BC, L>
1234where
1235    Self: IpDeviceStateContext<I, BC, DeviceId = DeviceId<BC>>,
1236{
1237    fn get_routing_metric(&mut self, device_id: &Self::DeviceId) -> RawMetric {
1238        let state = ip_device_state(self, device_id);
1239        *state.unlocked_access::<crate::lock_ordering::UnlockedState>().metric()
1240    }
1241
1242    fn is_ip_device_enabled(&mut self, device_id: &Self::DeviceId) -> bool {
1243        IpDeviceStateContext::<I, _>::with_ip_device_flags(
1244            self,
1245            device_id,
1246            |IpDeviceFlags { ip_enabled }| *ip_enabled,
1247        )
1248    }
1249}
1250
1251impl<BT: BindingsTypes, L> CounterContext<ArpCounters> for CoreCtx<'_, BT, L> {
1252    fn counters(&self) -> &ArpCounters {
1253        &self.unlocked_access::<crate::lock_ordering::UnlockedState>().device.arp_counters
1254    }
1255}