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