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