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