1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! State maintained by the device layer.

use alloc::sync::Arc;
use core::fmt::Debug;

use lock_order::lock::{OrderedLockAccess, OrderedLockRef};
use net_types::ip::{Ipv4, Ipv6};
use netstack3_base::sync::{RwLock, WeakRc};
use netstack3_base::{
    CoreTimerContext, Device, DeviceIdContext, Inspectable, TimerContext, WeakDeviceIdentifier,
};
use netstack3_ip::device::{DualStackIpDeviceState, IpAddressIdSpec, IpDeviceTimerId};
use netstack3_ip::RawMetric;

use crate::internal::base::{DeviceCounters, DeviceLayerTypes, OriginTracker};
use crate::internal::socket::HeldDeviceSockets;

/// Provides the specifications for device state held by [`BaseDeviceId`] in
/// [`BaseDeviceState`].
pub trait DeviceStateSpec: Device + Sized + Send + Sync + 'static {
    /// The link state.
    type Link<BT: DeviceLayerTypes>: Send + Sync;
    /// The external (bindings) state.
    type External<BT: DeviceLayerTypes>: Send + Sync;
    /// Properties given to device creation.
    type CreationProperties: Debug;
    /// Device-specific counters.
    type Counters: Inspectable;
    /// The timer identifier required by this device state.
    type TimerId<D: WeakDeviceIdentifier>;

    /// Creates a new link state from the given properties.
    fn new_link_state<
        CC: CoreTimerContext<Self::TimerId<CC::WeakDeviceId>, BC> + DeviceIdContext<Self>,
        BC: DeviceLayerTypes + TimerContext,
    >(
        bindings_ctx: &mut BC,
        self_id: CC::WeakDeviceId,
        properties: Self::CreationProperties,
    ) -> Self::Link<BC>;

    /// Marker for loopback devices.
    const IS_LOOPBACK: bool;
    /// Marker used to print debug information for device identifiers.
    const DEBUG_TYPE: &'static str;
}

/// Groups state kept by weak device references.
///
/// A weak device reference must be able to carry the bindings identifier
/// infallibly. The `WeakCookie` is kept inside [`BaseDeviceState`] in an `Arc`
/// to group all the information that is cloned out to support weak device
/// references.
pub(crate) struct WeakCookie<T: DeviceStateSpec, BT: DeviceLayerTypes> {
    pub(crate) bindings_id: BT::DeviceIdentifier,
    pub(crate) weak_ref: WeakRc<BaseDeviceState<T, BT>>,
}

pub(crate) struct BaseDeviceState<T: DeviceStateSpec, BT: DeviceLayerTypes> {
    pub(crate) ip: IpLinkDeviceState<T, BT>,
    pub(crate) external_state: T::External<BT>,
    pub(crate) weak_cookie: Arc<WeakCookie<T, BT>>,
}

/// A convenience wrapper around `IpLinkDeviceStateInner` that uses
/// `DeviceStateSpec` to extract the link state type and make type signatures
/// shorter.
pub type IpLinkDeviceState<T, BT> = IpLinkDeviceStateInner<<T as DeviceStateSpec>::Link<BT>, BT>;

/// State for a link-device that is also an IP device.
///
/// `D` is the link-specific state.
pub struct IpLinkDeviceStateInner<T, BT: DeviceLayerTypes> {
    /// The device's IP state.
    pub ip: DualStackIpDeviceState<BT>,
    /// The device's link state.
    pub link: T,
    pub(crate) origin: OriginTracker,
    pub(super) sockets: RwLock<HeldDeviceSockets<BT>>,
    /// Common device counters.
    pub counters: DeviceCounters,
}

impl<T, BC: DeviceLayerTypes + TimerContext> IpLinkDeviceStateInner<T, BC> {
    /// Create a new `IpLinkDeviceState` with a link-specific state `link`.
    pub fn new<
        D: WeakDeviceIdentifier,
        A: IpAddressIdSpec,
        CC: CoreTimerContext<IpDeviceTimerId<Ipv6, D, A>, BC>
            + CoreTimerContext<IpDeviceTimerId<Ipv4, D, A>, BC>,
    >(
        bindings_ctx: &mut BC,
        device_id: D,
        link: T,
        metric: RawMetric,
        origin: OriginTracker,
    ) -> Self {
        Self {
            ip: DualStackIpDeviceState::new::<D, A, CC>(bindings_ctx, device_id, metric),
            link,
            origin,
            sockets: RwLock::new(HeldDeviceSockets::default()),
            counters: DeviceCounters::default(),
        }
    }
}

impl<T, BT: DeviceLayerTypes> AsRef<DualStackIpDeviceState<BT>> for IpLinkDeviceStateInner<T, BT> {
    fn as_ref(&self) -> &DualStackIpDeviceState<BT> {
        &self.ip
    }
}

impl<T, BT: DeviceLayerTypes> OrderedLockAccess<HeldDeviceSockets<BT>>
    for IpLinkDeviceStateInner<T, BT>
{
    type Lock = RwLock<HeldDeviceSockets<BT>>;
    fn ordered_lock_access(&self) -> OrderedLockRef<'_, Self::Lock> {
        OrderedLockRef::new(&self.sockets)
    }
}