Skip to main content

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