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, 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::ipv4::Ipv4FragmentType;
45use packet_formats::utils::NonZeroDuration;
46
47use crate::context::WrapLockLevel;
48use crate::context::prelude::*;
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            icmp::NdpMessage::NeighborSolicitation {
217                message: NeighborSolicitation::new(lookup_addr.get()),
218                code: IcmpZeroCode,
219            },
220        );
221    }
222}
223
224impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IcmpAllSocketsSet<Ipv6>>>
225    NudIcmpContext<Ipv6, EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
226{
227    fn send_icmp_dest_unreachable(
228        &mut self,
229        bindings_ctx: &mut BC,
230        frame: Buf<Vec<u8>>,
231        device_id: Option<&Self::DeviceId>,
232        original_src_ip: SocketIpAddr<Ipv6Addr>,
233        original_dst_ip: SocketIpAddr<Ipv6Addr>,
234        _: (),
235    ) {
236        icmp::send_icmpv6_address_unreachable(
237            self,
238            bindings_ctx,
239            device_id.map(|device_id| device_id.clone().into()).as_ref(),
240            // NB: link layer address resolution only happens for packets destined for
241            // a unicast address, so passing `None` as `FrameDestination` here is always
242            // correct since there's never a need to not send the ICMP error due to
243            // a multicast/broadcast destination.
244            None,
245            original_src_ip,
246            original_dst_ip,
247            frame,
248            // TODO(https://fxbug.dev/400977853): The pending frame this ICMP message is
249            // responding to can either be generated from ourselves or being forwarded.
250            // In the former case, the marks are irrelevant because this message will end
251            // up being delivered locally. For the later case, we need to make sure the
252            // marks are stored with the pending frames.
253            &Marks::default(),
254        );
255    }
256}
257
258impl<'a, BC: BindingsContext, L: LockBefore<crate::lock_ordering::Ipv6DeviceLearnedParams>>
259    NudConfigContext<Ipv6> for CoreCtxWithDeviceId<'a, CoreCtx<'a, BC, L>>
260{
261    fn retransmit_timeout(&mut self) -> NonZeroDuration {
262        let Self { device_id, core_ctx } = self;
263        let mut state = integration::device_state(core_ctx, device_id);
264        let mut state = state.cast();
265        // NB: This assignment is satisfying borrow checking on state.
266        let x = state
267            .read_lock::<crate::lock_ordering::Ipv6DeviceLearnedParams>()
268            .retrans_timer_or_default();
269        x
270    }
271
272    fn with_nud_user_config<O, F: FnOnce(&NudUserConfig) -> O>(&mut self, cb: F) -> O {
273        let Self { device_id, core_ctx } = self;
274        let mut state = integration::device_state(core_ctx, device_id);
275        let x = state.read_lock::<crate::lock_ordering::NudConfig<Ipv6>>();
276        cb(&*x)
277    }
278}
279
280impl<'a, BC: BindingsContext, L: LockBefore<crate::lock_ordering::AllDeviceSockets>>
281    NudSenderContext<Ipv6, EthernetLinkDevice, BC> for CoreCtxWithDeviceId<'a, CoreCtx<'a, BC, L>>
282{
283    fn send_ip_packet_to_neighbor_link_addr<S>(
284        &mut self,
285        bindings_ctx: &mut BC,
286        dst_mac: Mac,
287        body: S,
288        meta: BC::TxMetadata,
289    ) -> Result<(), SendFrameError<S>>
290    where
291        S: Serializer,
292        S::Buffer: BufferMut,
293    {
294        let Self { device_id, core_ctx } = self;
295        ethernet::send_as_ethernet_frame_to_dst(
296            *core_ctx,
297            bindings_ctx,
298            device_id,
299            dst_mac,
300            body,
301            EtherType::Ipv6,
302            meta,
303        )
304    }
305}
306
307impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpState<Ipv4>>>
308    ArpContext<EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
309{
310    type ConfigCtx<'a> = CoreCtxWithDeviceId<
311        'a,
312        CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::EthernetIpv4Arp>>,
313    >;
314
315    type ArpSenderCtx<'a> = CoreCtxWithDeviceId<
316        'a,
317        CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::EthernetIpv4Arp>>,
318    >;
319
320    fn with_arp_state_mut_and_sender_ctx<
321        O,
322        F: FnOnce(&mut ArpState<EthernetLinkDevice, BC>, &mut Self::ArpSenderCtx<'_>) -> O,
323    >(
324        &mut self,
325        device_id: &EthernetDeviceId<BC>,
326        cb: F,
327    ) -> O {
328        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
329        let (mut arp, mut locked) = core_ctx_and_resource
330            .lock_with_and::<crate::lock_ordering::EthernetIpv4Arp, _>(|c| c.right());
331        let mut locked = CoreCtxWithDeviceId { device_id, core_ctx: &mut locked.cast_core_ctx() };
332        cb(&mut arp, &mut locked)
333    }
334
335    fn get_protocol_addr(&mut self, device_id: &EthernetDeviceId<BC>) -> Option<Ipv4Addr> {
336        let mut state = integration::device_state(self, device_id);
337        let mut state = state.cast();
338        let ipv4 = state.read_lock::<crate::lock_ordering::IpDeviceAddresses<Ipv4>>();
339        // NB: This assignment is satisfying borrow checking on state.
340        let x = ipv4.iter().next().map(|addr| addr.addr().get());
341        x
342    }
343
344    fn get_hardware_addr(
345        &mut self,
346        _bindings_ctx: &mut BC,
347        device_id: &EthernetDeviceId<BC>,
348    ) -> UnicastAddr<Mac> {
349        ethernet::get_mac(self, device_id)
350    }
351
352    fn with_arp_state_mut<
353        O,
354        F: FnOnce(&mut ArpState<EthernetLinkDevice, BC>, &mut Self::ConfigCtx<'_>) -> O,
355    >(
356        &mut self,
357        device_id: &EthernetDeviceId<BC>,
358        cb: F,
359    ) -> O {
360        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
361        let (mut arp, mut locked) = core_ctx_and_resource
362            .lock_with_and::<crate::lock_ordering::EthernetIpv4Arp, _>(|c| c.right());
363        let mut locked = CoreCtxWithDeviceId { device_id, core_ctx: &mut locked.cast_core_ctx() };
364        cb(&mut arp, &mut locked)
365    }
366
367    fn with_arp_state<O, F: FnOnce(&ArpState<EthernetLinkDevice, BC>) -> O>(
368        &mut self,
369        device_id: &EthernetDeviceId<BC>,
370        cb: F,
371    ) -> O {
372        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
373        let arp = core_ctx_and_resource
374            .lock_with::<crate::lock_ordering::EthernetIpv4Arp, _>(|c| c.right());
375        cb(&arp)
376    }
377}
378
379impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpDeviceConfiguration<Ipv4>>>
380    ArpIpLayerContext<EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
381{
382    fn on_arp_packet(
383        &mut self,
384        bindings_ctx: &mut BC,
385        device_id: &EthernetDeviceId<BC>,
386        sender_addr: Ipv4Addr,
387        target_addr: Ipv4Addr,
388        is_arp_probe: bool,
389    ) -> bool {
390        let device_id = DeviceId::Ethernet(device_id.clone());
391        netstack3_ip::device::on_arp_packet(
392            self,
393            bindings_ctx,
394            &device_id,
395            sender_addr,
396            target_addr,
397            is_arp_probe,
398        )
399    }
400}
401
402impl<BT: BindingsTypes, L> UseDelegateNudContext for CoreCtx<'_, BT, L> {}
403impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IpState<Ipv4>>>
404    DelegateNudContext<Ipv4> for CoreCtx<'_, BC, L>
405{
406    type Delegate<T> = ArpNudCtx<T>;
407}
408
409impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::IcmpAllSocketsSet<Ipv4>>>
410    NudIcmpContext<Ipv4, EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
411{
412    fn send_icmp_dest_unreachable(
413        &mut self,
414        bindings_ctx: &mut BC,
415        frame: Buf<Vec<u8>>,
416        device_id: Option<&Self::DeviceId>,
417        original_src_ip: SocketIpAddr<Ipv4Addr>,
418        original_dst_ip: SocketIpAddr<Ipv4Addr>,
419        (header_len, fragment_type): (usize, Ipv4FragmentType),
420    ) {
421        icmp::send_icmpv4_host_unreachable(
422            self,
423            bindings_ctx,
424            device_id.map(|device_id| device_id.clone().into()).as_ref(),
425            // NB: link layer address resolution only happens for packets destined for
426            // a unicast address, so passing `None` as `FrameDestination` here is always
427            // correct since there's never a need to not send the ICMP error due to
428            // a multicast/broadcast destination.
429            None,
430            original_src_ip,
431            original_dst_ip,
432            frame,
433            header_len,
434            fragment_type,
435            // TODO(https://fxbug.dev/400977853): The pending frame this ICMP message is
436            // responding to can either be generated from ourselves or being forwarded.
437            // In the former case, the marks are irrelevant because this message will end
438            // up being delivered locally. For the later case, we need to make sure the
439            // marks are stored with the pending frames.
440            &Marks::default(),
441        );
442    }
443}
444
445impl<'a, BC: BindingsContext, L: LockBefore<crate::lock_ordering::NudConfig<Ipv4>>> ArpConfigContext
446    for CoreCtxWithDeviceId<'a, CoreCtx<'a, BC, L>>
447{
448    fn with_nud_user_config<O, F: FnOnce(&NudUserConfig) -> O>(&mut self, cb: F) -> O {
449        let Self { device_id, core_ctx } = self;
450        let mut state = integration::device_state(core_ctx, device_id);
451        let x = state.read_lock::<crate::lock_ordering::NudConfig<Ipv4>>();
452        cb(&*x)
453    }
454}
455
456impl<'a, BC: BindingsContext, L: LockBefore<crate::lock_ordering::AllDeviceSockets>>
457    ArpSenderContext<EthernetLinkDevice, BC> for CoreCtxWithDeviceId<'a, CoreCtx<'a, BC, L>>
458{
459    fn send_ip_packet_to_neighbor_link_addr<S>(
460        &mut self,
461        bindings_ctx: &mut BC,
462        dst_mac: Mac,
463        body: S,
464        meta: BC::TxMetadata,
465    ) -> Result<(), SendFrameError<S>>
466    where
467        S: Serializer,
468        S::Buffer: BufferMut,
469    {
470        let Self { device_id, core_ctx } = self;
471        ethernet::send_as_ethernet_frame_to_dst(
472            *core_ctx,
473            bindings_ctx,
474            device_id,
475            dst_mac,
476            body,
477            EtherType::Ipv4,
478            meta,
479        )
480    }
481}
482
483impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::EthernetTxQueue>>
484    TransmitQueueCommon<EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
485{
486    type Meta = BC::TxMetadata;
487    type Allocator = BufVecU8Allocator;
488    type Buffer = Buf<Vec<u8>>;
489    type DequeueContext = BC::DequeueContext;
490
491    fn parse_outgoing_frame<'a, 'b>(
492        buf: &'a [u8],
493        _meta: &'b Self::Meta,
494    ) -> Result<SentFrame<&'a [u8]>, ParseSentFrameError> {
495        SentFrame::try_parse_as_ethernet(buf)
496    }
497}
498
499impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::EthernetTxQueue>>
500    TransmitQueueContext<EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
501{
502    fn with_transmit_queue_mut<
503        O,
504        F: FnOnce(&mut TransmitQueueState<Self::Meta, Self::Buffer, Self::Allocator>) -> O,
505    >(
506        &mut self,
507        device_id: &EthernetDeviceId<BC>,
508        cb: F,
509    ) -> O {
510        let mut state = integration::device_state(self, device_id);
511        let mut x = state.lock::<crate::lock_ordering::EthernetTxQueue>();
512        cb(&mut x)
513    }
514
515    fn with_transmit_queue<
516        O,
517        F: FnOnce(&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 x = state.lock::<crate::lock_ordering::EthernetTxQueue>();
525        cb(&x)
526    }
527
528    fn send_frame(
529        &mut self,
530        bindings_ctx: &mut BC,
531        device_id: &Self::DeviceId,
532        dequeue_context: Option<&mut BC::DequeueContext>,
533        _meta: Self::Meta,
534        buf: Self::Buffer,
535    ) -> Result<(), DeviceSendFrameError> {
536        DeviceLayerEventDispatcher::send_ethernet_frame(
537            bindings_ctx,
538            device_id,
539            buf,
540            dequeue_context,
541        )
542    }
543}
544
545impl<BC: BindingsContext, L: LockBefore<crate::lock_ordering::EthernetTxDequeue>>
546    TransmitDequeueContext<EthernetLinkDevice, BC> for CoreCtx<'_, BC, L>
547{
548    type TransmitQueueCtx<'a> =
549        CoreCtx<'a, BC, WrapLockLevel<crate::lock_ordering::EthernetTxDequeue>>;
550
551    fn with_dequed_packets_and_tx_queue_ctx<
552        O,
553        F: FnOnce(&mut DequeueState<Self::Meta, Self::Buffer>, &mut Self::TransmitQueueCtx<'_>) -> O,
554    >(
555        &mut self,
556        device_id: &Self::DeviceId,
557        cb: F,
558    ) -> O {
559        let mut core_ctx_and_resource = integration::device_state_and_core_ctx(self, device_id);
560        let (mut x, mut locked) = core_ctx_and_resource
561            .lock_with_and::<crate::lock_ordering::EthernetTxDequeue, _>(|c| c.right());
562        cb(&mut x, &mut locked.cast_core_ctx())
563    }
564}
565
566impl<I: Ip, BT: BindingsTypes> LockLevelFor<IpLinkDeviceState<EthernetLinkDevice, BT>>
567    for crate::lock_ordering::NudConfig<I>
568{
569    type Data = IpMarked<I, NudUserConfig>;
570}
571
572impl<BT: BindingsTypes> LockLevelFor<IpLinkDeviceState<EthernetLinkDevice, BT>>
573    for crate::lock_ordering::EthernetDeviceDynamicState
574{
575    type Data = DynamicEthernetDeviceState;
576}
577
578impl<BT: BindingsTypes> LockLevelFor<IpLinkDeviceState<EthernetLinkDevice, BT>>
579    for crate::lock_ordering::EthernetIpv6Nud
580{
581    type Data = NudState<Ipv6, EthernetLinkDevice, BT>;
582}
583
584impl<BT: BindingsTypes> LockLevelFor<IpLinkDeviceState<EthernetLinkDevice, BT>>
585    for crate::lock_ordering::EthernetIpv4Arp
586{
587    type Data = ArpState<EthernetLinkDevice, BT>;
588}
589
590impl<BT: BindingsTypes> LockLevelFor<IpLinkDeviceState<EthernetLinkDevice, BT>>
591    for crate::lock_ordering::EthernetTxQueue
592{
593    type Data = TransmitQueueState<BT::TxMetadata, Buf<Vec<u8>>, BufVecU8Allocator>;
594}
595
596impl<BT: BindingsTypes> LockLevelFor<IpLinkDeviceState<EthernetLinkDevice, BT>>
597    for crate::lock_ordering::EthernetTxDequeue
598{
599    type Data = DequeueState<BT::TxMetadata, Buf<Vec<u8>>>;
600}