Skip to main content

nmstate/
mptcp.rs

1// SPDX-License-Identifier: Apache-2.0
2
3// The document string for MptcpAddressFlag is copy from manpage of
4// `IP-MPTCP(8)` which is licensed under GPLv2.0+
5
6use serde::{Deserialize, Serialize};
7
8use crate::{BaseInterface, ErrorKind, MergedInterface, NmstateError};
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
11#[serde(rename_all = "kebab-case", deny_unknown_fields)]
12#[non_exhaustive]
13pub struct MptcpConfig {
14    #[serde(skip_serializing_if = "Option::is_none")]
15    /// Automatically assign MPTCP flags to all valid IP addresses of this
16    /// interface including both static and dynamic ones.
17    pub address_flags: Option<Vec<MptcpAddressFlag>>,
18}
19
20#[derive(
21    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize,
22)]
23#[serde(rename_all = "kebab-case")]
24#[non_exhaustive]
25pub enum MptcpAddressFlag {
26    /// The endpoint will be announced/signaled to each peer via an MPTCP
27    /// ADD_ADDR sub-option. Upon reception of an ADD_ADDR sub-option, the
28    /// peer can try to create additional subflows. Cannot used along with
29    /// MptcpAddressFlag::Fullmesh as Linux kernel enforced.
30    Signal,
31    /// If additional subflow creation is allowed by the MPTCP limits, the
32    /// MPTCP path manager will try to create an additional subflow using
33    /// this endpoint as the source address after the MPTCP connection is
34    /// established.
35    Subflow,
36    /// If this is a subflow endpoint, the subflows created using this endpoint
37    /// will have the backup flag set during the connection process. This flag
38    /// instructs the peer to only send data on a given subflow when all
39    /// non-backup subflows are unavailable. This does not affect outgoing
40    /// data, where subflow priority is determined by the backup/non-backup
41    /// flag received from the peer.
42    Backup,
43    /// If this is a subflow endpoint and additional subflow creation is
44    /// allowed by the MPTCP limits, the MPTCP path manager will try to
45    /// create an additional subflow for each known peer address, using
46    /// this endpoint as the source address. This will occur after the
47    /// MPTCP connection is established. If the peer did not announce any
48    /// additional addresses using the MPTCP ADD_ADDR sub-option, this will
49    /// behave the same as a plain subflow endpoint.  When the peer does
50    /// announce addresses, each received ADD_ADDR sub-option will trigger
51    /// creation of an additional subflow to generate a full mesh topology.
52    Fullmesh,
53}
54
55impl MergedInterface {
56    pub(crate) fn post_inter_ifaces_process_mptcp(
57        &self,
58    ) -> Result<(), NmstateError> {
59        if let Some(iface) = self.for_apply.as_ref().map(|i| i.base_iface()) {
60            if let Some(iface_flags) =
61                iface.mptcp.as_ref().and_then(|m| m.address_flags.as_ref())
62                && iface_flags.contains(&MptcpAddressFlag::Signal)
63                && iface_flags.contains(&MptcpAddressFlag::Fullmesh)
64            {
65                let e = NmstateError::new(
66                    ErrorKind::InvalidArgument,
67                    "MPTCP flags mustn't have both signal and fullmesh"
68                        .to_string(),
69                );
70                log::error!("{e}");
71                return Err(e);
72            }
73            validate_iface_mptcp_and_addr_mptcp_flags(iface);
74        }
75
76        Ok(())
77    }
78}
79
80fn validate_iface_mptcp_and_addr_mptcp_flags(iface: &BaseInterface) {
81    let mut iface_flags =
82        match iface.mptcp.as_ref().and_then(|m| m.address_flags.as_ref()) {
83            Some(f) => f.clone(),
84            None => Vec::new(),
85        };
86    iface_flags.sort_unstable();
87
88    let empty_ip_addrs = Vec::new();
89
90    for ip_addr in iface
91        .ipv4
92        .as_ref()
93        .and_then(|i| i.addresses.as_ref())
94        .unwrap_or(&empty_ip_addrs)
95        .iter()
96        .chain(
97            iface
98                .ipv6
99                .as_ref()
100                .and_then(|i| i.addresses.as_ref())
101                .unwrap_or(&empty_ip_addrs)
102                .iter(),
103        )
104    {
105        if let Some(mut addr_flags) = ip_addr.mptcp_flags.as_ref().cloned() {
106            addr_flags.sort_unstable();
107            if iface_flags != addr_flags {
108                log::warn!(
109                    "Nmstate does not support setting different MPTCP flags \
110                     within the interface. Ignoring MPTCP flags {:?} of IP \
111                     address {}/{} as it is different from interface level \
112                     MPTCP flags {:?}",
113                    addr_flags,
114                    ip_addr.ip,
115                    ip_addr.prefix_length,
116                    iface_flags
117                );
118            }
119        }
120    }
121}