From 2b8197d0dcebdfa743073f1bc0f6c778685ff530 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 27 Jun 2023 14:05:04 +0200 Subject: [PATCH] docs: add device internal documentation Add documentation about the implementation of NMDevice. --- docs/internal/device-state-machine.drawio | 95 +++++++ docs/internal/device-state-machine.png | Bin 0 -> 51500 bytes docs/internal/device.md | 322 ++++++++++++++++++++++ 3 files changed, 417 insertions(+) create mode 100644 docs/internal/device-state-machine.drawio create mode 100644 docs/internal/device-state-machine.png create mode 100644 docs/internal/device.md diff --git a/docs/internal/device-state-machine.drawio b/docs/internal/device-state-machine.drawio new file mode 100644 index 0000000000..bc2bbdf46e --- /dev/null +++ b/docs/internal/device-state-machine.drawio @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/internal/device-state-machine.png b/docs/internal/device-state-machine.png new file mode 100644 index 0000000000000000000000000000000000000000..35eca6c4cd0702ac44be56c9c9cb10fb5621665d GIT binary patch literal 51500 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 literal 0 HcmV?d00001 diff --git a/docs/internal/device.md b/docs/internal/device.md new file mode 100644 index 0000000000..0042fdc55b --- /dev/null +++ b/docs/internal/device.md @@ -0,0 +1,322 @@ +Devices +======= + +What is a device +---------------- + +In NetworkManager, a device represents an object that allows some sort +of network configuration; it can be a regular Linux network interface +(physical as Ethernet or Wi-Fi; or virtual as a bridge or a VLAN), but +it can also be an entity that does not have a link object in the +kernel; examples of the latter kind are modems and OVS bridges/ports. + +NetworkManager automatically creates device objects at runtime based +on the device found on the system. It also creates special devices, +called *unrealized* devices that represent potential devices, +e.g. that don't exist yet but will exist when a given connection gets +activated. See the section "Unrealized devices" for more details. + +Each device has several properties; the most important are: + + - `iface`: the name of the interface. + + - `ifindex`: for devices backed by a kernel link, this is the kernel + interface index. + + - `ip_ifindex`: some devices have multiple kernel link associated. In + such case, `ifindex` is the index of the base link, while + `ip_ifindex` is the index of the link on which IP configuration + should be made. For example when activating a PPPoE connection, the + device has `ifindex` referring to the Ethernet link and + `ip_ifindex` to the PPP one. + + - `state`: the current state of the device in the device state + machine, see the next sections. + + - `l3cfg`: the L3Cfg instance takes care of configuring IP on one + ifindex. + +The device object also is exposed on D-Bus, with properties, methods +and signals, see the `interface_info_device` structure. + +Activation +---------- + +To configure a device, NetworkManager needs to activate a connection +profile on it. This happens for two reasons: + - it was requested by the user via the `ActivateConnection()` and + `AddAndActivateConnection()` API methods, which are handled by + functions `impl_manager_activate_connection()` and + `impl_manager_add_and_activate_connection()`; + - it is the result of an internal decision; in this case the + activation is handled by `nm_manager_activate_connection()`; the + reason can be: + - the connection profile is activated automatically (for example, + at startup) because it is configured to auto-connect; + - the connection profile is being activated as a dependency of + another profile; for example: + - a port profile (e.g. bridge port) always depends on the + corresponding controller profile (e.g. bridge); + - a controller profile can be configured to automatically + activate port profiles; + - during a checkpoint rollback; + - etc. + +The activation starts by first creating a `NMActiveConnection` +object. This is a abstract type used to track the state of the +activation on a specific device; it has two implementations: +`NMActRequest` for regular devices and `NMVpnConnection` for VPNs. + +Two important fields of an active connection object are: + + - the *settings-connection*: this is a pointer to the connection + being activated; it points to the connection in `NMSettings` and + always reflects the latest changes in the profile; + + - the *applied-connection*: similar to the *settings-connection*, but + this is a copy of the original *settings-connection* done at the + time the activation started. During the current activation, the + properties to configure are always read from this applied + connection because they shouldn't change even if the profile is + modified. + +Unrealized devices +------------------ + +We said that to start an activation we need a profile and a +device. What happens if the device doesn't already exist because it is +a virtual one (such as a bridge or a vlan)? + +This problem is currently solved in NetworkManager by having a special +kind of devices, *unrealized* devices. Those are 'potential' devices, +that don't exist in kernel; they are created to represent the device +for each virtual profile. Technically speaking, a device is unrealized +when the `real` flag is set to zero in the device private struct. + +When NetworkManager decides to activate a virtual profile, the +corresponding device gets realized by calling +`nm_device_create_and_realize()` and then the activation proceeds as +for physical devices. + +Object hierarchy +---------------- + +In the NetworkManager code a device is a `NMDevice` object, which has +several subclasses; each subclass represents a specific kind of device +(`NMDeviceEthernet`, `NMDeviceWifi`, `NMDeviceBridge`, etc.), and can +reimplement properties and methods of the superclass to customize the +behavior (see `struct _NMDeviceClass`). + +To show how this is used in practice, let's look at function +`nm_device_is_available()`, which indicates whether a device is ready +to be activated. The function calls the virtual method `is_available()`: + +```C +gboolean +nm_device_is_available(NMDevice *self, NMDeviceCheckDevAvailableFlags flags) +{ + ... + return NM_DEVICE_GET_CLASS(self)->is_available(self, flags); +} +``` + +`NMDevice` has a generic implementation of that method which performs +generic checks: + +```C +static gboolean +is_available(NMDevice *self, NMDeviceCheckDevAvailableFlags flags) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + if (priv->carrier || priv->ignore_carrier) + return TRUE; + ... + + return FALSE; +} +``` + +A Ethernet device needs additional checks and so it reimplements the +method to check that the MAC address is already set: + +```C +static gboolean +is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags) +{ + if (!NM_DEVICE_CLASS(nm_device_ethernet_parent_class) + ->is_available(device, flags)) + return FALSE; + + return !!nm_device_get_initial_hw_address(device); +} +``` + +Note how the function first chains up by calling the `NMDevice` +implementation and then it performs an additional check. This pattern +is heavily used in NetworkManager. + +Device states +------------- + +Each device behaves according to a state machine that looks like this: + +![Device state machine](device-state-machine.png "Device state machine") + +While the implementation of the state machine is done in different +functions in `nm-device.c`, the core part is in `_set_state_full()`. + +The states are: + + * UNMANAGED: this is the initial state and means that the device is + not managed by NetworkManager. The unmanaged state is tracked via a + bitmap of flags, and the device stays in this state while at least + one flag is set. See the section "Unmanaged flags" below for more + details. Once all unmanaged flags are cleared, the device is ready + to become UNAVAILABLE. The state transition is done in function + `_set_unmanaged_flags()`, which changes the states to UNMANAGED or + UNAVAILABLE depending on the value of the flags. Note that even if + it's not displayed in the diagram, the UNMANAGED state can be + reached by virtually every other state when a unmanaged flag + becomes set. + + * UNAVAILABLE: the device is managed by NetworkManager, but is not + available for use. Reasons may include the wireless switched off, + missing firmware, no ethernet carrier, missing supplicant or modem + manager, etc. When a device becomes available, it can transition to + DISCONNECTED; this decision is taken in various places by + scheduling a check via `nm_device_queue_recheck_available()`. + + * DISCONNECTED: the device can be activated, but is currently + idle and not connected to a network. When entering this state from + a state that belongs to the activation sequence or from + FAILED/DEACTIVATING, a cleanup of previous configuration is + done. If there is an activation queued, it's started; otherwise, + `NMPolicy` reacts to the state change and calls + `nm_policy_device_recheck_auto_activate_schedule()` to check if + there a connection that can be auto-activated on the device. + + * PREPARE: this is the first state of an activation; in this + state some initial operation are performed, such as changing the + MAC address, setting physical link properties, and anything else + required to connect to the requested network. + + This state is entered via + `nm_device_activate_schedule_stage1_device_prepare()`. When + finished, `nm_device_activate_schedule_stage2_device_config()` is + used to transition to the CONFIG state. Those functions are + re-entrant, in the sense that when a device is in a given state and + needs that an operation completes (or a condition becomes true), it + can wait and then invoke the same function again; in that way it + re-enters the same state, where all the conditions are evaluated + again and if possible the device will transition to the next state. + + * CONFIG: the device is connecting to the requested network. + This may include operations like associating with the Wi-Fi AP, + dialing the modem, connecting to the remote Bluetooth device, etc. + + * NEED_AUTH: the device requires more information to continue + connecting to the requested network. This includes secrets like + WiFi passphrases, login passwords, PIN codes, etc. + + * IP_CONFIG: this state is entered via + `nm_device_activate_schedule_stage3_ip_config()`, and is where IP + addresses and routes are assigned to the device. Function + `_dev_ip_state_check()` checks that the configuration is terminated + according the connection configuration; if so, it moves the device + to state IP_CHECK. + + * IP_CHECK: in this state, NetworkManager waits that the gateway + can be pinged successfully if the property + `connection.gateway-ping-timeout` is set. By default this step is a + no op since the property is unset. After the optional ping, the + dispatcher `pre-up` event is emitted, and the device goes to + SECONDARIES. + + * SECONDARIES: connections have a `connection.secondaries` property + that specifies a list of UUID of connections of type VPN that can + be activated automatically when the connection goes up. If there + are any secondaries, they are activated in this stage; note that + since this operation involves other devices, it is done in + `NMPolicy`, upon the emission of the state change signal handled in + `nm-policy.c:device_state_changed()`. After any secondaries are + activated, the devices transitions to state ACTIVATED. + + * ACTIVATED: the device has a network connection. Upon entering this + state the device emits the `up` dispatcher event. The device + remains is this state until the connection is deactivated or until + it fails. + + * DEACTIVATING: a disconnection from the current network + connection was requested, and the device is cleaning up resources + used for that connection. In this state the `pre-down` dispatcher + event is emitted. When finished, the devices goes again to + DISCONNECTED and the `down` dispatch event is fired. + + * FAILED: the device failed to connect to the requested network + and is cleaning up the connection request. This state can be + reached from any state belonging to the activation. When the + cleanup is done, the device goes to DISCONNECTED. If the device was + previously activated, the `down` dispatcher event is emitted. + +Unmanaged devices +----------------- + +Each device has a mask of flags representing reasons why the device is +unmanaged; when at least of of those flags is set, the device goes to +state UNMANAGED. When all flags are cleared, the device moves to state +UNAVAILABLE. The unmanaged flags are currently: + + - SLEEPING: the system is suspended, or networking is disabled + + - QUITTING: NetworkManager is shutting down. + + - PLATFORM_INIT: NetworkManager is waiting for udev to announce + the device. Note that NetworkManager can't touch the device until + then because udev might perform operations on it (such as renaming + or changing the MAC). Unrealized devices (see later) have this + flag set. + + - USER_EXPLICIT: when unmanaged by explicit user decision + (e.g. via a D-Bus command). + + - USER_SETTINGS: when unmanaged by user decision via the + settings plugin (for example `keyfile.unmanaged-devices` or + ifcfg-rh's `NM_CONTROLLED=no`). Although this is + user-configuration it cannot be overruled and is + authoritative. That is because users may depend on dropping a + ifcfg-rh file to ensure the device is unmanaged. + + - USER_CONF: when unmanaged by user decision via the + NetworkManager.conf ("unmanaged" in the [device] + section). Contrary to USER_SETTINGS, this can be overwritten via + D-Bus. + + - BY_DEFAULT: this flag is no longer used. + + - USER_UDEV: unmanaged via a udev rule. + + - EXTERNAL_DOWN: unmanaged because the interface was not created by + NetworkManager and is currently down. + +Note that the unmanaged flags are tracked via two variables +`unmanaged_mask` and `unmanaged_flags`; in this way each flag is in +practice a tri-state variable with possible values TRUE (unmanaged), +FALSE (managed) and UNSET. + +External devices and sys-iface-state +------------------------------------ + +Even if a device is managed, that doesn't mean that NetworkManager is +actively configuring it. When a device is created externally (for +example via `ip link`) and has an IP configuration, NetworkManager +creates a in-memory connection representing the configuration +parameters on the interface such as IP addresses, routes, DNS, etc.; +the connection appears as active but NetworkManager doesn't actually +touch the interface. The external status is tracked in the +`sys-iface-state` member, which can have the following values: + + - EXTERNAL: the interface is not touched by NM. + - ASSUME: this value is deprecated; it used to mean that NM should manage the device without fully reconfiguring it. Now, the interface is either managed on external. + - MANAGED: the interface is fully managed. + - REMOVED: the link was removed externally.