1use alloc::sync::Arc;
8use core::fmt::{self, Debug};
9use core::hash::Hash;
10use core::num::NonZeroU64;
11
12use derivative::Derivative;
13use netstack3_base::sync::{DynDebugReferences, PrimaryRc, StrongRc};
14use netstack3_base::{
15 Device, DeviceIdentifier, InterfaceProperties, StrongDeviceIdentifier, WeakDeviceIdentifier,
16};
17
18use crate::blackhole::{BlackholeDevice, BlackholeDeviceId, BlackholeWeakDeviceId};
19use crate::internal::base::{
20 DeviceClassMatcher as _, DeviceIdAndNameMatcher as _, DeviceLayerTypes, OriginTracker,
21};
22use crate::internal::ethernet::EthernetLinkDevice;
23use crate::internal::loopback::{LoopbackDevice, LoopbackDeviceId, LoopbackWeakDeviceId};
24use crate::internal::pure_ip::{PureIpDevice, PureIpDeviceId, PureIpWeakDeviceId};
25use crate::internal::state::{BaseDeviceState, DeviceStateSpec, IpLinkDeviceState, WeakCookie};
26
27#[derive(Derivative)]
33#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Hash(bound = ""))]
34#[allow(missing_docs)]
35pub enum WeakDeviceId<BT: DeviceLayerTypes> {
36 Ethernet(EthernetWeakDeviceId<BT>),
37 Loopback(LoopbackWeakDeviceId<BT>),
38 PureIp(PureIpWeakDeviceId<BT>),
39 Blackhole(BlackholeWeakDeviceId<BT>),
40}
41
42impl<BT: DeviceLayerTypes> PartialEq<DeviceId<BT>> for WeakDeviceId<BT> {
43 fn eq(&self, other: &DeviceId<BT>) -> bool {
44 <DeviceId<BT> as PartialEq<WeakDeviceId<BT>>>::eq(other, self)
45 }
46}
47
48impl<BT: DeviceLayerTypes> From<EthernetWeakDeviceId<BT>> for WeakDeviceId<BT> {
49 fn from(id: EthernetWeakDeviceId<BT>) -> WeakDeviceId<BT> {
50 WeakDeviceId::Ethernet(id)
51 }
52}
53
54impl<BT: DeviceLayerTypes> From<LoopbackWeakDeviceId<BT>> for WeakDeviceId<BT> {
55 fn from(id: LoopbackWeakDeviceId<BT>) -> WeakDeviceId<BT> {
56 WeakDeviceId::Loopback(id)
57 }
58}
59
60impl<BT: DeviceLayerTypes> From<PureIpWeakDeviceId<BT>> for WeakDeviceId<BT> {
61 fn from(id: PureIpWeakDeviceId<BT>) -> WeakDeviceId<BT> {
62 WeakDeviceId::PureIp(id)
63 }
64}
65
66impl<BT: DeviceLayerTypes> From<BlackholeWeakDeviceId<BT>> for WeakDeviceId<BT> {
67 fn from(id: BlackholeWeakDeviceId<BT>) -> WeakDeviceId<BT> {
68 WeakDeviceId::Blackhole(id)
69 }
70}
71
72impl<BT: DeviceLayerTypes> WeakDeviceId<BT> {
73 pub fn upgrade(&self) -> Option<DeviceId<BT>> {
75 for_any_device_id!(WeakDeviceId, self, id => id.upgrade().map(Into::into))
76 }
77
78 pub fn debug_references(&self) -> DynDebugReferences {
80 for_any_device_id!(
81 WeakDeviceId,
82 self,
83 BaseWeakDeviceId { cookie } => cookie.weak_ref.debug_references().into_dyn()
84 )
85 }
86
87 pub fn bindings_id(&self) -> &BT::DeviceIdentifier {
89 for_any_device_id!(WeakDeviceId, self, id => id.bindings_id())
90 }
91}
92
93impl<BT: DeviceLayerTypes> DeviceIdentifier for WeakDeviceId<BT> {
94 fn is_loopback(&self) -> bool {
95 match self {
96 WeakDeviceId::Loopback(_) => true,
97 WeakDeviceId::Ethernet(_) | WeakDeviceId::PureIp(_) | WeakDeviceId::Blackhole(_) => {
98 false
99 }
100 }
101 }
102}
103
104impl<BT: DeviceLayerTypes> WeakDeviceIdentifier for WeakDeviceId<BT> {
105 type Strong = DeviceId<BT>;
106
107 fn upgrade(&self) -> Option<Self::Strong> {
108 self.upgrade()
109 }
110}
111
112impl<BT: DeviceLayerTypes> Debug for WeakDeviceId<BT> {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 for_any_device_id!(WeakDeviceId, self, id => Debug::fmt(id, f))
115 }
116}
117
118#[derive(Derivative)]
125#[derivative(Eq(bound = ""), PartialEq(bound = ""), Hash(bound = ""))]
126#[allow(missing_docs)]
127pub enum DeviceId<BT: DeviceLayerTypes> {
128 Ethernet(EthernetDeviceId<BT>),
129 Loopback(LoopbackDeviceId<BT>),
130 PureIp(PureIpDeviceId<BT>),
131 Blackhole(BlackholeDeviceId<BT>),
132}
133
134#[macro_export]
168macro_rules! for_any_device_id {
169 ($device_id_enum_type:ident, $device_id:expr, $variable:pat => $expression:expr) => {
171 match $device_id {
172 $device_id_enum_type::Loopback($variable) => $expression,
173 $device_id_enum_type::Ethernet($variable) => $expression,
174 $device_id_enum_type::PureIp($variable) => $expression,
175 $device_id_enum_type::Blackhole($variable) => $expression,
176 }
177 };
178 (
180 $device_id_enum_type:ident,
181 $provider_trait:ident,
182 $type_param:ident,
183 $device_id:expr, $variable:pat => $expression:expr) => {
184 match $device_id {
185 $device_id_enum_type::Loopback($variable) => {
186 type $type_param = <() as $provider_trait>::Loopback;
187 $expression
188 }
189 $device_id_enum_type::Ethernet($variable) => {
190 type $type_param = <() as $provider_trait>::Ethernet;
191 $expression
192 }
193 $device_id_enum_type::PureIp($variable) => {
194 type $type_param = <() as $provider_trait>::PureIp;
195 $expression
196 }
197 $device_id_enum_type::Blackhole($variable) => {
198 type $type_param = <() as $provider_trait>::Blackhole;
199 $expression
200 }
201 }
202 };
203}
204pub(crate) use crate::for_any_device_id;
205
206pub trait DeviceProvider {
208 type Ethernet: Device;
210 type Loopback: Device;
212 type PureIp: Device;
214 type Blackhole: Device;
216}
217
218impl DeviceProvider for () {
220 type Ethernet = EthernetLinkDevice;
221 type Loopback = LoopbackDevice;
222 type PureIp = PureIpDevice;
223 type Blackhole = BlackholeDevice;
224}
225
226impl<BT: DeviceLayerTypes> Clone for DeviceId<BT> {
227 #[cfg_attr(feature = "instrumented", track_caller)]
228 fn clone(&self) -> Self {
229 for_any_device_id!(DeviceId, self, id => id.clone().into())
230 }
231}
232
233impl<BT: DeviceLayerTypes> PartialEq<WeakDeviceId<BT>> for DeviceId<BT> {
234 fn eq(&self, other: &WeakDeviceId<BT>) -> bool {
235 match (self, other) {
236 (DeviceId::Ethernet(strong), WeakDeviceId::Ethernet(weak)) => strong == weak,
237 (DeviceId::Loopback(strong), WeakDeviceId::Loopback(weak)) => strong == weak,
238 (DeviceId::PureIp(strong), WeakDeviceId::PureIp(weak)) => strong == weak,
239 (DeviceId::Blackhole(strong), WeakDeviceId::Blackhole(weak)) => strong == weak,
240 (DeviceId::Ethernet(_), _)
241 | (DeviceId::Loopback(_), _)
242 | (DeviceId::PureIp(_), _)
243 | (DeviceId::Blackhole(_), _) => false,
244 }
245 }
246}
247
248impl<BT: DeviceLayerTypes> PartialOrd for DeviceId<BT> {
249 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
250 Some(self.cmp(other))
251 }
252}
253
254impl<BT: DeviceLayerTypes> Ord for DeviceId<BT> {
255 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
256 fn discriminant<BT: DeviceLayerTypes>(d: &DeviceId<BT>) -> u8 {
259 match d {
260 DeviceId::Ethernet(_) => 0,
262 DeviceId::Loopback(_) => 1,
263 DeviceId::PureIp(_) => 2,
264 DeviceId::Blackhole(_) => 3,
265 }
266 }
267 match (self, other) {
268 (DeviceId::Ethernet(me), DeviceId::Ethernet(other)) => me.cmp(other),
269 (DeviceId::Loopback(me), DeviceId::Loopback(other)) => me.cmp(other),
270 (DeviceId::PureIp(me), DeviceId::PureIp(other)) => me.cmp(other),
271 (DeviceId::Blackhole(me), DeviceId::Blackhole(other)) => me.cmp(other),
272 (me @ DeviceId::Ethernet(_), other)
273 | (me @ DeviceId::Loopback(_), other)
274 | (me @ DeviceId::PureIp(_), other)
275 | (me @ DeviceId::Blackhole(_), other) => discriminant(me).cmp(&discriminant(other)),
276 }
277 }
278}
279
280impl<BT: DeviceLayerTypes> From<EthernetDeviceId<BT>> for DeviceId<BT> {
281 fn from(id: EthernetDeviceId<BT>) -> DeviceId<BT> {
282 DeviceId::Ethernet(id)
283 }
284}
285
286impl<BT: DeviceLayerTypes> From<LoopbackDeviceId<BT>> for DeviceId<BT> {
287 fn from(id: LoopbackDeviceId<BT>) -> DeviceId<BT> {
288 DeviceId::Loopback(id)
289 }
290}
291
292impl<BT: DeviceLayerTypes> From<PureIpDeviceId<BT>> for DeviceId<BT> {
293 fn from(id: PureIpDeviceId<BT>) -> DeviceId<BT> {
294 DeviceId::PureIp(id)
295 }
296}
297
298impl<BT: DeviceLayerTypes> From<BlackholeDeviceId<BT>> for DeviceId<BT> {
299 fn from(id: BlackholeDeviceId<BT>) -> DeviceId<BT> {
300 DeviceId::Blackhole(id)
301 }
302}
303
304impl<BT: DeviceLayerTypes> DeviceId<BT> {
305 pub fn downgrade(&self) -> WeakDeviceId<BT> {
307 for_any_device_id!(DeviceId, self, id => id.downgrade().into())
308 }
309
310 pub fn bindings_id(&self) -> &BT::DeviceIdentifier {
312 for_any_device_id!(DeviceId, self, id => id.bindings_id())
313 }
314}
315
316impl<BT: DeviceLayerTypes> DeviceIdentifier for DeviceId<BT> {
317 fn is_loopback(&self) -> bool {
318 match self {
319 DeviceId::Loopback(_) => true,
320 DeviceId::Ethernet(_) | DeviceId::PureIp(_) | DeviceId::Blackhole(_) => false,
321 }
322 }
323}
324
325impl<BT: DeviceLayerTypes> StrongDeviceIdentifier for DeviceId<BT> {
326 type Weak = WeakDeviceId<BT>;
327
328 fn downgrade(&self) -> Self::Weak {
329 self.downgrade()
330 }
331}
332
333impl<BT: DeviceLayerTypes> Debug for DeviceId<BT> {
334 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335 for_any_device_id!(DeviceId, self, id => Debug::fmt(id, f))
336 }
337}
338
339impl<BT: DeviceLayerTypes> InterfaceProperties<BT::DeviceClass> for DeviceId<BT> {
340 fn id_matches(&self, id: &NonZeroU64) -> bool {
341 self.bindings_id().id_matches(id)
342 }
343
344 fn name_matches(&self, name: &str) -> bool {
345 self.bindings_id().name_matches(name)
346 }
347
348 fn device_class_matches(&self, device_class: &BT::DeviceClass) -> bool {
349 for_any_device_id!(
350 DeviceId,
351 self,
352 id => id.external_state().device_class_matches(device_class)
353 )
354 }
355}
356
357#[derive(Derivative)]
362#[derivative(Clone(bound = ""))]
363pub struct BaseWeakDeviceId<T: DeviceStateSpec, BT: DeviceLayerTypes> {
364 cookie: Arc<WeakCookie<T, BT>>,
367}
368
369impl<T: DeviceStateSpec, BT: DeviceLayerTypes> PartialEq for BaseWeakDeviceId<T, BT> {
370 fn eq(&self, other: &Self) -> bool {
371 self.cookie.weak_ref.ptr_eq(&other.cookie.weak_ref)
372 }
373}
374
375impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Eq for BaseWeakDeviceId<T, BT> {}
376
377impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Hash for BaseWeakDeviceId<T, BT> {
378 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
379 self.cookie.weak_ref.hash(state)
380 }
381}
382
383impl<T: DeviceStateSpec, BT: DeviceLayerTypes> PartialEq<BaseDeviceId<T, BT>>
384 for BaseWeakDeviceId<T, BT>
385{
386 fn eq(&self, other: &BaseDeviceId<T, BT>) -> bool {
387 <BaseDeviceId<T, BT> as PartialEq<BaseWeakDeviceId<T, BT>>>::eq(other, self)
388 }
389}
390
391impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Debug for BaseWeakDeviceId<T, BT> {
392 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393 let Self { cookie } = self;
394 write!(f, "Weak{}({:?})", T::DEBUG_TYPE, &cookie.bindings_id)
395 }
396}
397
398impl<T: DeviceStateSpec, BT: DeviceLayerTypes> DeviceIdentifier for BaseWeakDeviceId<T, BT> {
399 fn is_loopback(&self) -> bool {
400 T::IS_LOOPBACK
401 }
402}
403
404impl<T: DeviceStateSpec, BT: DeviceLayerTypes> WeakDeviceIdentifier for BaseWeakDeviceId<T, BT> {
405 type Strong = BaseDeviceId<T, BT>;
406
407 fn upgrade(&self) -> Option<Self::Strong> {
408 self.upgrade()
409 }
410}
411
412impl<T: DeviceStateSpec, BT: DeviceLayerTypes> BaseWeakDeviceId<T, BT> {
413 pub fn upgrade(&self) -> Option<BaseDeviceId<T, BT>> {
416 let Self { cookie } = self;
417 cookie.weak_ref.upgrade().map(|rc| BaseDeviceId { rc })
418 }
419
420 pub fn bindings_id(&self) -> &BT::DeviceIdentifier {
422 &self.cookie.bindings_id
423 }
424}
425
426#[derive(Derivative)]
431#[derivative(Hash(bound = ""), Eq(bound = ""), PartialEq(bound = ""))]
432pub struct BaseDeviceId<T: DeviceStateSpec, BT: DeviceLayerTypes> {
433 rc: StrongRc<BaseDeviceState<T, BT>>,
436}
437
438impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Clone for BaseDeviceId<T, BT> {
439 #[cfg_attr(feature = "instrumented", track_caller)]
440 fn clone(&self) -> Self {
441 let Self { rc } = self;
442 Self { rc: StrongRc::clone(rc) }
443 }
444}
445
446impl<T: DeviceStateSpec, BT: DeviceLayerTypes> PartialEq<BaseWeakDeviceId<T, BT>>
447 for BaseDeviceId<T, BT>
448{
449 fn eq(&self, BaseWeakDeviceId { cookie }: &BaseWeakDeviceId<T, BT>) -> bool {
450 let Self { rc: me_rc } = self;
451 StrongRc::weak_ptr_eq(me_rc, &cookie.weak_ref)
452 }
453}
454
455impl<T: DeviceStateSpec, BT: DeviceLayerTypes> PartialEq<BasePrimaryDeviceId<T, BT>>
456 for BaseDeviceId<T, BT>
457{
458 fn eq(&self, BasePrimaryDeviceId { rc: other_rc }: &BasePrimaryDeviceId<T, BT>) -> bool {
459 let Self { rc: me_rc } = self;
460 PrimaryRc::ptr_eq(other_rc, me_rc)
461 }
462}
463
464impl<T: DeviceStateSpec, BT: DeviceLayerTypes> PartialOrd for BaseDeviceId<T, BT> {
465 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
466 Some(self.cmp(other))
467 }
468}
469
470impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Ord for BaseDeviceId<T, BT> {
471 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
472 let Self { rc: me } = self;
473 let Self { rc: other } = other;
474
475 StrongRc::ptr_cmp(me, other)
476 }
477}
478
479impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Debug for BaseDeviceId<T, BT> {
480 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
481 let Self { rc } = self;
482 write!(f, "{}({:?})", T::DEBUG_TYPE, &rc.weak_cookie.bindings_id)
483 }
484}
485
486impl<T: DeviceStateSpec, BT: DeviceLayerTypes> DeviceIdentifier for BaseDeviceId<T, BT> {
487 fn is_loopback(&self) -> bool {
488 T::IS_LOOPBACK
489 }
490}
491
492impl<T: DeviceStateSpec, BT: DeviceLayerTypes> StrongDeviceIdentifier for BaseDeviceId<T, BT> {
493 type Weak = BaseWeakDeviceId<T, BT>;
494
495 fn downgrade(&self) -> Self::Weak {
496 self.downgrade()
497 }
498}
499
500impl<T: DeviceStateSpec, BT: DeviceLayerTypes> BaseDeviceId<T, BT> {
501 pub fn device_state(&self, tracker: &OriginTracker) -> &IpLinkDeviceState<T, BT> {
506 debug_assert_eq!(tracker, &self.rc.ip.origin);
507 &self.rc.ip
508 }
509
510 pub fn external_state(&self) -> &T::External<BT> {
512 &self.rc.external_state
513 }
514
515 pub fn bindings_id(&self) -> &BT::DeviceIdentifier {
517 &self.rc.weak_cookie.bindings_id
518 }
519
520 pub fn downgrade(&self) -> BaseWeakDeviceId<T, BT> {
522 let Self { rc } = self;
523 BaseWeakDeviceId { cookie: Arc::clone(&rc.weak_cookie) }
524 }
525}
526
527pub struct BasePrimaryDeviceId<T: DeviceStateSpec, BT: DeviceLayerTypes> {
529 rc: PrimaryRc<BaseDeviceState<T, BT>>,
532}
533
534impl<T: DeviceStateSpec, BT: DeviceLayerTypes> Debug for BasePrimaryDeviceId<T, BT> {
535 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
536 let Self { rc } = self;
537 write!(f, "Primary{}({:?})", T::DEBUG_TYPE, &rc.weak_cookie.bindings_id)
538 }
539}
540
541impl<T: DeviceStateSpec, BT: DeviceLayerTypes> BasePrimaryDeviceId<T, BT> {
542 #[cfg_attr(feature = "instrumented", track_caller)]
544 pub fn clone_strong(&self) -> BaseDeviceId<T, BT> {
545 let Self { rc } = self;
546 BaseDeviceId { rc: PrimaryRc::clone_strong(rc) }
547 }
548
549 pub(crate) fn new<F: FnOnce(BaseWeakDeviceId<T, BT>) -> IpLinkDeviceState<T, BT>>(
550 ip: F,
551 external_state: T::External<BT>,
552 bindings_id: BT::DeviceIdentifier,
553 ) -> Self {
554 Self {
555 rc: PrimaryRc::new_cyclic(move |weak_ref| {
556 let weak_cookie = Arc::new(WeakCookie { bindings_id, weak_ref });
557 let ip = ip(BaseWeakDeviceId { cookie: Arc::clone(&weak_cookie) });
558 BaseDeviceState { ip, external_state, weak_cookie }
559 }),
560 }
561 }
562
563 pub(crate) fn into_inner(self) -> PrimaryRc<BaseDeviceState<T, BT>> {
564 self.rc
565 }
566}
567
568pub type EthernetDeviceId<BT> = BaseDeviceId<EthernetLinkDevice, BT>;
572pub type EthernetWeakDeviceId<BT> = BaseWeakDeviceId<EthernetLinkDevice, BT>;
574pub type EthernetPrimaryDeviceId<BT> = BasePrimaryDeviceId<EthernetLinkDevice, BT>;
576
577#[cfg(any(test, feature = "testutils"))]
578mod testutil {
579 use super::*;
580
581 impl<BT: DeviceLayerTypes> TryFrom<DeviceId<BT>> for EthernetDeviceId<BT> {
582 type Error = DeviceId<BT>;
583 fn try_from(id: DeviceId<BT>) -> Result<EthernetDeviceId<BT>, DeviceId<BT>> {
584 match id {
585 DeviceId::Ethernet(id) => Ok(id),
586 DeviceId::Loopback(_) | DeviceId::PureIp(_) | DeviceId::Blackhole(_) => Err(id),
587 }
588 }
589 }
590
591 impl<BT: DeviceLayerTypes> DeviceId<BT> {
592 pub fn unwrap_ethernet(self) -> EthernetDeviceId<BT> {
594 assert_matches::assert_matches!(self, DeviceId::Ethernet(e) => e)
595 }
596 }
597}