Skip to main content

netstack3_core/device/
ethernet.rs

1// Copyright 2024 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 traits defined in foreign modules for the types defined
6//! in the ethernet module.
7
8use alloc::vec::Vec;
9use lock_order::lock::LockLevelFor;
10use lock_order::relation::LockBefore;
11
12use log::debug;
13use net_types::ethernet::Mac;
14use net_types::ip::{Ip, IpMarked, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
15use net_types::{SpecifiedAddr, UnicastAddr, Witness};
16use netstack3_base::socket::SocketIpAddr;
17use netstack3_base::{CoreTimerContext, CounterContext, DeviceIdContext, Marks, SendFrameError};
18use netstack3_device::ethernet::{
19    self, DynamicEthernetDeviceState, EthernetDeviceId, EthernetIpLinkDeviceDynamicStateContext,
20    EthernetIpLinkDeviceStaticStateContext, EthernetLinkDevice, EthernetTimerId,
21    EthernetWeakDeviceId, StaticEthernetDeviceState,
22};
23use netstack3_device::queue::{
24    BufVecU8Allocator, DequeueState, TransmitDequeueContext, TransmitQueueCommon,
25    TransmitQueueContext, TransmitQueueState,
26};
27use netstack3_device::socket::{ParseSentFrameError, SentFrame};
28use netstack3_device::{
29    ArpConfigContext, ArpContext, ArpIpLayerContext, ArpNudCtx, ArpSenderContext, ArpState,
30    DeviceId, DeviceLayerEventDispatcher, DeviceLayerTimerId, DeviceSendFrameError,
31    IpLinkDeviceState,
32};
33use netstack3_ip::IpDeviceEgressStateContext;
34use netstack3_ip::icmp::{self, IcmpErrorHandler, Icmpv4Error, Icmpv6Error, NdpCounters};
35use netstack3_ip::nud::{
36    DelegateNudContext, NudConfigContext, NudContext, NudIcmpContext, NudSenderContext, NudState,
37    NudUserConfig, UseDelegateNudContext,
38};
39use packet::{Buf, BufferMut, InnerPacketBuilder as _, Serializer};
40use packet_formats::ethernet::EtherType;
41use packet_formats::icmp::IcmpZeroCode;
42use packet_formats::icmp::ndp::options::NdpOptionBuilder;
43use packet_formats::icmp::ndp::{NeighborSolicitation, OptionSequenceBuilder};
44use packet_formats::ip::{Ipv4Proto, Ipv6Proto};
45use packet_formats::ipv4::Ipv4FragmentType;
46use packet_formats::utils::NonZeroDuration;
47
48use crate::context::WrapLockLevel;
49use crate::context::prelude::*;
50use crate::device::integration;
51use crate::{BindingsContext, BindingsTypes, CoreCtx};
52
53pub struct CoreCtxWithDeviceId<'a, CC: DeviceIdContext<EthernetLinkDevice>> {
54    core_ctx: &'a mut CC,
55    device_id: &'a CC::DeviceId,
56}
57
58impl<'a, CC: DeviceIdContext<EthernetLinkDevice>> DeviceIdContext<EthernetLinkDevice>
59    for CoreCtxWithDeviceId<'a, CC>
60{
61    type DeviceId = CC::DeviceId;
62    type WeakDeviceId = CC::WeakDeviceId;
63}
64
65impl<BC: BindingsContext, L> EthernetIpLinkDeviceStaticStateContext for CoreCtx<'_, BC, L> {
66    fn with_static_ethernet_device_state<O, F: FnOnce(&StaticEthernetDeviceState) -> O>(
67        &mut self,
68        device_id: &EthernetDeviceId<BC>,
69        cb: F,
70    ) -> O {
71        let state = integration::device_state(self, device_id);
72        cb(&state.unlocked_access::<crate::lock_ordering::UnlockedState>().link.static_state)
73    }
74}
75
76impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::EthernetDeviceDynamicState>>
77    EthernetIpLinkDeviceDynamicStateContext<BC> for CoreCtx<'_, BC, L>
78{
79    fn with_ethernet_state<
80        O,
81        F: FnOnce(&StaticEthernetDeviceState, &DynamicEthernetDeviceState) -> O,
82    >(
83        &mut self,
84        device_id: &EthernetDeviceId<BC>,
85        cb: F,
86    ) -> O {
87        let mut state = integration::device_state(self, device_id);
88        let (dynamic_state, locked) =
89            state.read_lock_and::<crate::lock_ordering::EthernetDeviceDynamicState>();
90        cb(
91            &locked.unlocked_access::<crate::lock_ordering::UnlockedState>().link.static_state,
92            &dynamic_state,
93        )
94    }
95
96    fn with_ethernet_state_mut<
97        O,
98        F: FnOnce(&StaticEthernetDeviceState, &mut DynamicEthernetDeviceState) -> O,
99    >(
100        &mut self,
101        device_id: &EthernetDeviceId<BC>,
102        cb: F,
103    ) -> O {
104        let mut state = integration::device_state(self, device_id);
105        let (mut dynamic_state, locked) =
106            state.write_lock_and::<crate::lock_ordering::EthernetDeviceDynamicState>();
107        cb(
108            &locked.unlocked_access::<crate::lock_ordering::UnlockedState>().link.static_state,
109            &mut dynamic_state,
110        )
111    }
112}
113
114impl<BT: BindingsTypes, L> CoreTimerContext<EthernetTimerId<EthernetWeakDeviceId<BT>>, BT>
115    for CoreCtx<'_, BT, L>
116{
117    fn convert_timer(dispatch_id: EthernetTimerId<EthernetWeakDeviceId<BT>>) -> BT::DispatchId {
118        DeviceLayerTimerId::from(dispatch_id).into()
119    }
120}
121
122impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::FilterState<Ipv6>>>
123    NudContext<Ipv6, EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
124{
125    type ConfigCtx<'a> = CoreCtxWithDeviceId<
126        'a,
127        CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::EthernetIpv6Nud>>,
128    >;
129
130    type SenderCtx<'a> = CoreCtxWithDeviceId<
131        'a,
132        CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::EthernetIpv6Nud>>,
133    >;
134
135    fn with_nud_state_mut_and_sender_ctx<
136        O,
137        F: FnOnce(&mut NudState<Ipv6, EthernetLinkDevice, BC>, &mut Self::SenderCtx<'_>) -> O,
138    >(
139        &mut self,
140        device_id: &EthernetDeviceId<BC>,
141        cb: F,
142    ) -> O {
143        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
144        let (mut nud, mut locked) = core_ctx_and_resource
145            .lock_with_and::<crate::lock_ordering::EthernetIpv6Nud, _>(|c| c.right());
146        let mut locked = CoreCtxWithDeviceId { device_id, core_ctx: &mut locked.cast_core_ctx() };
147        cb(&mut nud, &mut locked)
148    }
149
150    fn with_nud_state_mut<
151        O,
152        F: FnOnce(&mut NudState<Ipv6, EthernetLinkDevice, BC>, &mut Self::ConfigCtx<'_>) -> O,
153    >(
154        &mut self,
155        device_id: &EthernetDeviceId<BC>,
156        cb: F,
157    ) -> O {
158        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
159        let (mut nud, mut locked) = core_ctx_and_resource
160            .lock_with_and::<crate::lock_ordering::EthernetIpv6Nud, _>(|c| c.right());
161        let mut locked = CoreCtxWithDeviceId { device_id, core_ctx: &mut locked.cast_core_ctx() };
162        cb(&mut nud, &mut locked)
163    }
164
165    fn with_nud_state<O, F: FnOnce(&NudState<Ipv6, EthernetLinkDevice, BC>) -> O>(
166        &mut self,
167        device_id: &EthernetDeviceId<BC>,
168        cb: F,
169    ) -> O {
170        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
171        let nud = core_ctx_and_resource
172            .lock_with::<crate::lock_ordering::EthernetIpv6Nud, _>(|c| c.right());
173        cb(&nud)
174    }
175
176    fn send_neighbor_solicitation(
177        &mut self,
178        bindings_ctx: &mut BC,
179        device_id: &EthernetDeviceId<BC>,
180        lookup_addr: SpecifiedAddr<Ipv6Addr>,
181        remote_link_addr: Option<Mac>,
182    ) {
183        let dst_ip = match remote_link_addr {
184            // TODO(https://fxbug.dev/42081683): once `send_ndp_packet` does not go through
185            // the normal IP egress flow, using the NUD table to resolve the link address,
186            // use the specified link address to determine where to unicast the
187            // solicitation.
188            Some(_) => lookup_addr,
189            None => lookup_addr.to_solicited_node_address().into_specified(),
190        };
191        let src_ip = IpDeviceEgressStateContext::<Ipv6>::get_local_addr_for_remote(
192            self,
193            &device_id.clone().into(),
194            Some(dst_ip),
195        );
196        let src_ip = match src_ip {
197            Some(s) => s,
198            None => return,
199        };
200
201        let mac = ethernet::get_mac(self, device_id);
202
203        CounterContext::<NdpCounters>::counters(self).tx.neighbor_solicitation.increment();
204        debug!("sending NDP solicitation for {lookup_addr} to {dst_ip}");
205        // TODO(https://fxbug.dev/42165912): Either panic or guarantee that this error
206        // can't happen statically.
207        let _: Result<(), _> = icmp::send_ndp_packet(
208            self,
209            bindings_ctx,
210            &device_id.clone().into(),
211            Some(src_ip.into()),
212            dst_ip,
213            OptionSequenceBuilder::<_>::new(
214                [NdpOptionBuilder::SourceLinkLayerAddress(mac.bytes().as_ref())].iter(),
215            )
216            .into_serializer(),
217            icmp::NdpMessage::NeighborSolicitation {
218                message: NeighborSolicitation::new(lookup_addr.get()),
219                code: IcmpZeroCode,
220            },
221        );
222    }
223}
224
225impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IcmpAllSocketsSet<Ipv6>>>
226    NudIcmpContext<Ipv6, EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
227{
228    fn send_icmp_dest_unreachable(
229        &mut self,
230        bindings_ctx: &mut BC,
231        frame: Buf<Vec<u8>>,
232        device_id: Option<&Self::DeviceId>,
233        original_src_ip: SocketIpAddr<Ipv6Addr>,
234        original_dst_ip: SocketIpAddr<Ipv6Addr>,
235        header_len: usize,
236        proto: Ipv6Proto,
237        _metadata: (),
238    ) {
239        <Self as IcmpErrorHandler<Ipv6, BC>>::send_icmp_error_message(
240            self,
241            bindings_ctx,
242            device_id.map(|device_id| device_id.clone().into()).as_ref(), // This is Option<&CC::DeviceId>
243            // NB: link layer address resolution only happens for packets destined for
244            // a unicast address, so passing `None` as `FrameDestination` here is always
245            // correct since there's never a need to not send the ICMP error due to
246            // a multicast/broadcast destination.
247            None,
248            original_src_ip,
249            original_dst_ip,
250            frame,
251            Icmpv6Error::AddressUnreachable,
252            header_len,
253            proto,
254            // TODO(https://fxbug.dev/400977853): The pending frame this ICMP message is
255            // responding to can either be generated from ourselves or being forwarded.
256            // In the former case, the marks are irrelevant because this message will end
257            // up being delivered locally. For the later case, we need to make sure the
258            // marks are stored with the pending frames.
259            &Marks::default(),
260        );
261    }
262}
263
264impl<'a, BC: BindingsContext, L: LockBefore<crate::lock_ordering::Ipv6DeviceLearnedParams>>
265    NudConfigContext<Ipv6> for CoreCtxWithDeviceId<'a, CoreCtx<'a, BC, L>>
266{
267    fn retransmit_timeout(&mut self) -> NonZeroDuration {
268        let Self { device_id, core_ctx } = self;
269        let mut state = integration::device_state(core_ctx, device_id);
270        let mut state = state.cast();
271        // NB: This assignment is satisfying borrow checking on state.
272        let x = state.read_lock::<crate::lock_ordering::Ipv6DeviceLearnedParams>().retrans_timer();
273        x.unwrap_or_else(|| {
274            NudConfigContext::<Ipv6>::with_nud_user_config(self, |c| c.retrans_timer)
275        })
276    }
277
278    fn with_nud_user_config<O, F: FnOnce(&NudUserConfig) -> O>(&mut self, cb: F) -> O {
279        let Self { device_id, core_ctx } = self;
280        let mut state = integration::device_state(core_ctx, device_id);
281        let x = state.read_lock::<crate::lock_ordering::NudConfig<Ipv6>>();
282        cb(&*x)
283    }
284}
285
286impl<'a, BC: BindingsContext, L: LockBefore<crate::lock_ordering::AllDeviceSockets>>
287    NudSenderContext<Ipv6, EthernetLinkDevice, BC> for CoreCtxWithDeviceId<'a, CoreCtx<'a, BC, L>>
288{
289    fn send_ip_packet_to_neighbor_link_addr<S>(
290        &mut self,
291        bindings_ctx: &mut BC,
292        dst_mac: Mac,
293        body: S,
294        meta: BC::TxMetadata,
295    ) -> Result<(), SendFrameError<S>>
296    where
297        S: Serializer,
298        S::Buffer: BufferMut,
299    {
300        let Self { device_id, core_ctx } = self;
301        ethernet::send_as_ethernet_frame_to_dst(
302            *core_ctx,
303            bindings_ctx,
304            device_id,
305            dst_mac,
306            body,
307            EtherType::Ipv6,
308            meta,
309        )
310    }
311}
312
313impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpState<Ipv4>>>
314    ArpContext<EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
315{
316    type ConfigCtx<'a> = CoreCtxWithDeviceId<
317        'a,
318        CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::EthernetIpv4Arp>>,
319    >;
320
321    type ArpSenderCtx<'a> = CoreCtxWithDeviceId<
322        'a,
323        CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::EthernetIpv4Arp>>,
324    >;
325
326    fn with_arp_state_mut_and_sender_ctx<
327        O,
328        F: FnOnce(&mut ArpState<EthernetLinkDevice, BC>, &mut Self::ArpSenderCtx<'_>) -> O,
329    >(
330        &mut self,
331        device_id: &EthernetDeviceId<BC>,
332        cb: F,
333    ) -> O {
334        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
335        let (mut arp, mut locked) = core_ctx_and_resource
336            .lock_with_and::<crate::lock_ordering::EthernetIpv4Arp, _>(|c| c.right());
337        let mut locked = CoreCtxWithDeviceId { device_id, core_ctx: &mut locked.cast_core_ctx() };
338        cb(&mut arp, &mut locked)
339    }
340
341    fn get_protocol_addr(&mut self, device_id: &EthernetDeviceId<BC>) -> Option<Ipv4Addr> {
342        let mut state = integration::device_state(self, device_id);
343        let mut state = state.cast();
344        let ipv4 = state.read_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv4>>();
345        // NB: This assignment is satisfying borrow checking on state.
346        let x = ipv4.iter().next().map(|addr| addr.addr().get());
347        x
348    }
349
350    fn get_hardware_addr(
351        &mut self,
352        _bindings_ctx: &mut BC,
353        device_id: &EthernetDeviceId<BC>,
354    ) -> UnicastAddr<Mac> {
355        ethernet::get_mac(self, device_id)
356    }
357
358    fn with_arp_state_mut<
359        O,
360        F: FnOnce(&mut ArpState<EthernetLinkDevice, BC>, &mut Self::ConfigCtx<'_>) -> O,
361    >(
362        &mut self,
363        device_id: &EthernetDeviceId<BC>,
364        cb: F,
365    ) -> O {
366        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
367        let (mut arp, mut locked) = core_ctx_and_resource
368            .lock_with_and::<crate::lock_ordering::EthernetIpv4Arp, _>(|c| c.right());
369        let mut locked = CoreCtxWithDeviceId { device_id, core_ctx: &mut locked.cast_core_ctx() };
370        cb(&mut arp, &mut locked)
371    }
372
373    fn with_arp_state<O, F: FnOnce(&ArpState<EthernetLinkDevice, BC>) -> O>(
374        &mut self,
375        device_id: &EthernetDeviceId<BC>,
376        cb: F,
377    ) -> O {
378        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
379        let arp = core_ctx_and_resource
380            .lock_with::<crate::lock_ordering::EthernetIpv4Arp, _>(|c| c.right());
381        cb(&arp)
382    }
383}
384
385impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceConfiguration<Ipv4>>>
386    ArpIpLayerContext<EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
387{
388    fn on_arp_packet(
389        &mut self,
390        bindings_ctx: &mut BC,
391        device_id: &EthernetDeviceId<BC>,
392        sender_addr: Ipv4Addr,
393        target_addr: Ipv4Addr,
394        is_arp_probe: bool,
395    ) -> bool {
396        let device_id = DeviceId::Ethernet(device_id.clone());
397        netstack3_ip::device::on_arp_packet(
398            self,
399            bindings_ctx,
400            &device_id,
401            sender_addr,
402            target_addr,
403            is_arp_probe,
404        )
405    }
406}
407
408impl<BT: BindingsTypes, L> UseDelegateNudContext for CoreCtx<'_, BT, L> {}
409impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpState<Ipv4>>>
410    DelegateNudContext<Ipv4> for CoreCtx<'_, BC, L>
411{
412    type Delegate<T> = ArpNudCtx<T>;
413}
414
415impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IcmpAllSocketsSet<Ipv4>>>
416    NudIcmpContext<Ipv4, EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
417{
418    fn send_icmp_dest_unreachable(
419        &mut self,
420        bindings_ctx: &mut BC,
421        frame: Buf<Vec<u8>>,
422        device_id: Option<&Self::DeviceId>,
423        original_src_ip: SocketIpAddr<Ipv4Addr>,
424        original_dst_ip: SocketIpAddr<Ipv4Addr>,
425        header_len: usize,
426        proto: Ipv4Proto,
427        fragment_type: Ipv4FragmentType,
428    ) {
429        if fragment_type != Ipv4FragmentType::InitialFragment {
430            return;
431        }
432
433        <Self as IcmpErrorHandler<Ipv4, BC>>::send_icmp_error_message(
434            self,
435            bindings_ctx,
436            device_id.map(|device_id| device_id.clone().into()).as_ref(), // This is Option<&CC::DeviceId>
437            // NB: link layer address resolution only happens for packets destined for
438            // a unicast address, so passing `None` as `FrameDestination` here is always
439            // correct since there's never a need to not send the ICMP error due to
440            // a multicast/broadcast destination.
441            None,
442            original_src_ip,
443            original_dst_ip,
444            frame,
445            Icmpv4Error::HostUnreachable,
446            header_len,
447            proto,
448            // TODO(https://fxbug.dev/400977853): The pending frame this ICMP message is
449            // responding to can either be generated from ourselves or being forwarded.
450            // In the former case, the marks are irrelevant because this message will end
451            // up being delivered locally. For the later case, we need to make sure the
452            // marks are stored with the pending frames.
453            &Marks::default(),
454        );
455    }
456}
457
458impl<'a, BC: BindingsContext, L: LockBefore<crate::lock_ordering::NudConfig<Ipv4>>> ArpConfigContext
459    for CoreCtxWithDeviceId<'a, CoreCtx<'a, BC, L>>
460{
461    fn with_nud_user_config<O, F: FnOnce(&NudUserConfig) -> O>(&mut self, cb: F) -> O {
462        let Self { device_id, core_ctx } = self;
463        let mut state = integration::device_state(core_ctx, device_id);
464        let x = state.read_lock::<crate::lock_ordering::NudConfig<Ipv4>>();
465        cb(&*x)
466    }
467}
468
469impl<'a, BC: BindingsContext, L: LockBefore<crate::lock_ordering::AllDeviceSockets>>
470    ArpSenderContext<EthernetLinkDevice, BC> for CoreCtxWithDeviceId<'a, CoreCtx<'a, BC, L>>
471{
472    fn send_ip_packet_to_neighbor_link_addr<S>(
473        &mut self,
474        bindings_ctx: &mut BC,
475        dst_mac: Mac,
476        body: S,
477        meta: BC::TxMetadata,
478    ) -> Result<(), SendFrameError<S>>
479    where
480        S: Serializer,
481        S::Buffer: BufferMut,
482    {
483        let Self { device_id, core_ctx } = self;
484        ethernet::send_as_ethernet_frame_to_dst(
485            *core_ctx,
486            bindings_ctx,
487            device_id,
488            dst_mac,
489            body,
490            EtherType::Ipv4,
491            meta,
492        )
493    }
494}
495
496impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::EthernetTxQueue>>
497    TransmitQueueCommon<EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
498{
499    type Meta = BC::TxMetadata;
500    type Allocator = BufVecU8Allocator;
501    type Buffer = Buf<Vec<u8>>;
502    type DequeueContext = BC::DequeueContext;
503
504    fn parse_outgoing_frame<'a, 'b>(
505        buf: &'a [u8],
506        _meta: &'b Self::Meta,
507    ) -> Result<SentFrame<&'a [u8]>, ParseSentFrameError> {
508        SentFrame::try_parse_as_ethernet(buf)
509    }
510}
511
512impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::EthernetTxQueue>>
513    TransmitQueueContext<EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
514{
515    fn with_transmit_queue_mut<
516        O,
517        F: FnOnce(&mut TransmitQueueState<Self::Meta, Self::Buffer, Self::Allocator>) -> O,
518    >(
519        &mut self,
520        device_id: &EthernetDeviceId<BC>,
521        cb: F,
522    ) -> O {
523        let mut state = integration::device_state(self, device_id);
524        let mut x = state.lock::<crate::lock_ordering::EthernetTxQueue>();
525        cb(&mut x)
526    }
527
528    fn with_transmit_queue<
529        O,
530        F: FnOnce(&TransmitQueueState<Self::Meta, Self::Buffer, Self::Allocator>) -> O,
531    >(
532        &mut self,
533        device_id: &EthernetDeviceId<BC>,
534        cb: F,
535    ) -> O {
536        let mut state = integration::device_state(self, device_id);
537        let x = state.lock::<crate::lock_ordering::EthernetTxQueue>();
538        cb(&x)
539    }
540
541    fn send_frame(
542        &mut self,
543        bindings_ctx: &mut BC,
544        device_id: &Self::DeviceId,
545        dequeue_context: Option<&mut BC::DequeueContext>,
546        _meta: Self::Meta,
547        buf: Self::Buffer,
548    ) -> Result<(), DeviceSendFrameError> {
549        DeviceLayerEventDispatcher::send_ethernet_frame(
550            bindings_ctx,
551            device_id,
552            buf,
553            dequeue_context,
554        )
555    }
556}
557
558impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::EthernetTxDequeue>>
559    TransmitDequeueContext<EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
560{
561    type TransmitQueueCtx<'a> =
562        CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::EthernetTxDequeue>>;
563
564    fn with_dequed_packets_and_tx_queue_ctx<
565        O,
566        F: FnOnce(&mut DequeueState<Self::Meta, Self::Buffer>, &mut Self::TransmitQueueCtx<'_>) -> O,
567    >(
568        &mut self,
569        device_id: &Self::DeviceId,
570        cb: F,
571    ) -> O {
572        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
573        let (mut x, mut locked) = core_ctx_and_resource
574            .lock_with_and::<crate::lock_ordering::EthernetTxDequeue, _>(|c| c.right());
575        cb(&mut x, &mut locked.cast_core_ctx())
576    }
577}
578
579impl<I: Ip, BT: BindingsTypes> LockLevelFor<IpLinkDeviceState<EthernetLinkDevice, BT>>
580    for crate::lock_ordering::NudConfig<I>
581{
582    type Data = IpMarked<I, NudUserConfig>;
583}
584
585impl<BT: BindingsTypes> LockLevelFor<IpLinkDeviceState<EthernetLinkDevice, BT>>
586    for crate::lock_ordering::EthernetDeviceDynamicState
587{
588    type Data = DynamicEthernetDeviceState;
589}
590
591impl<BT: BindingsTypes> LockLevelFor<IpLinkDeviceState<EthernetLinkDevice, BT>>
592    for crate::lock_ordering::EthernetIpv6Nud
593{
594    type Data = NudState<Ipv6, EthernetLinkDevice, BT>;
595}
596
597impl<BT: BindingsTypes> LockLevelFor<IpLinkDeviceState<EthernetLinkDevice, BT>>
598    for crate::lock_ordering::EthernetIpv4Arp
599{
600    type Data = ArpState<EthernetLinkDevice, BT>;
601}
602
603impl<BT: BindingsTypes> LockLevelFor<IpLinkDeviceState<EthernetLinkDevice, BT>>
604    for crate::lock_ordering::EthernetTxQueue
605{
606    type Data = TransmitQueueState<BT::TxMetadata, Buf<Vec<u8>>, BufVecU8Allocator>;
607}
608
609impl<BT: BindingsTypes> LockLevelFor<IpLinkDeviceState<EthernetLinkDevice, BT>>
610    for crate::lock_ordering::EthernetTxDequeue
611{
612    type Data = DequeueState<BT::TxMetadata, Buf<Vec<u8>>>;
613}