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