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