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