nmstate/ifaces/
ethernet.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    BaseInterface, ErrorKind, Interface, InterfaceType, Interfaces,
7    MergedInterfaces, NetworkStateMode, NmstateError, SrIovConfig,
8};
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11#[non_exhaustive]
12/// Ethernet(IEEE 802.3) interface.
13/// Besides [BaseInterface], optionally could hold [EthernetConfig] and/or
14/// [VethConfig].
15/// The yaml output of [crate::NetworkState] containing ethernet interface would
16/// be:
17/// ```yml
18/// interfaces:
19/// - name: ens3
20///   type: ethernet
21///   state: up
22///   mac-address: 00:11:22:33:44:FF
23///   mtu: 1500
24///   min-mtu: 68
25///   max-mtu: 65535
26///   wait-ip: ipv4
27///   ipv4:
28///     enabled: true
29///     dhcp: false
30///     address:
31///     - ip: 192.0.2.9
32///       prefix-length: 24
33///   ipv6:
34///     enabled: false
35///   mptcp:
36///     address-flags: []
37///   accept-all-mac-addresses: false
38///   lldp:
39///     enabled: false
40///   ethtool:
41///     feature:
42///       tx-tcp-ecn-segmentation: true
43///       tx-tcp-mangleid-segmentation: false
44///       tx-tcp6-segmentation: true
45///       tx-tcp-segmentation: true
46///       rx-gro-list: false
47///       rx-udp-gro-forwarding: false
48///       rx-gro-hw: true
49///       tx-checksum-ip-generic: true
50///       tx-generic-segmentation: true
51///       rx-gro: true
52///       tx-nocache-copy: false
53///     coalesce:
54///       rx-frames: 1
55///       tx-frames: 1
56///     ring:
57///       rx: 256
58///       rx-max: 256
59///       tx: 256
60///       tx-max: 256
61///   ethernet:
62///     auto-negotiation: false
63/// ```
64pub struct EthernetInterface {
65    #[serde(flatten)]
66    pub base: BaseInterface,
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub ethernet: Option<EthernetConfig>,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    /// When applying, the [VethConfig] is only valid when
71    /// [BaseInterface.iface_type] is set to [InterfaceType::Veth] explicitly.
72    pub veth: Option<VethConfig>,
73}
74
75impl Default for EthernetInterface {
76    fn default() -> Self {
77        let mut base = BaseInterface::new();
78        base.iface_type = InterfaceType::Ethernet;
79        Self {
80            base,
81            ethernet: None,
82            veth: None,
83        }
84    }
85}
86
87impl EthernetInterface {
88    pub(crate) fn sanitize(&mut self) -> Result<(), NmstateError> {
89        // Always set interface type to ethernet for verifying and applying
90        self.base.iface_type = InterfaceType::Ethernet;
91
92        if let Some(sriov_conf) =
93            self.ethernet.as_mut().and_then(|e| e.sr_iov.as_mut())
94        {
95            sriov_conf.sanitize()?
96        }
97
98        Ok(())
99    }
100
101    pub fn new() -> Self {
102        Self::default()
103    }
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
107#[serde(rename_all = "kebab-case")]
108#[non_exhaustive]
109pub enum EthernetDuplex {
110    /// Deserialize and serialize from/to `full`.
111    Full,
112    /// Deserialize and serialize from/to `half`.
113    Half,
114}
115
116impl std::fmt::Display for EthernetDuplex {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        write!(
119            f,
120            "{}",
121            match self {
122                Self::Full => "full",
123                Self::Half => "half",
124            }
125        )
126    }
127}
128
129#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
130#[serde(rename_all = "kebab-case", deny_unknown_fields)]
131#[non_exhaustive]
132pub struct EthernetConfig {
133    #[serde(skip_serializing_if = "Option::is_none")]
134    /// Single Root I/O Virtualization(SRIOV) configuration.
135    /// Deserialize and serialize from/to `sr-iov`.
136    pub sr_iov: Option<SrIovConfig>,
137    #[serde(
138        skip_serializing_if = "Option::is_none",
139        rename = "auto-negotiation",
140        default,
141        deserialize_with = "crate::deserializer::option_bool_or_string"
142    )]
143    /// Deserialize and serialize from/to `auto-negotiation`.
144    pub auto_neg: Option<bool>,
145    #[serde(
146        skip_serializing_if = "Option::is_none",
147        default,
148        deserialize_with = "crate::deserializer::option_u32_or_string"
149    )]
150    pub speed: Option<u32>,
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub duplex: Option<EthernetDuplex>,
153}
154
155impl EthernetConfig {
156    pub fn new() -> Self {
157        Self::default()
158    }
159}
160
161#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
162#[non_exhaustive]
163pub struct VethConfig {
164    /// The name of veth peer.
165    pub peer: String,
166}
167
168impl MergedInterfaces {
169    // Raise error if new veth interface has no peer defined.
170    // Mark old veth peer as absent when veth changed its peer.
171    // Mark veth peer as absent also when veth is marked as absent.
172    pub(crate) fn process_veth_peer_changes(
173        &mut self,
174    ) -> Result<(), NmstateError> {
175        let mut veth_peers: Vec<&str> = Vec::new();
176        for iface in self.iter().filter(|i| {
177            i.merged.iface_type() == InterfaceType::Ethernet && i.merged.is_up()
178        }) {
179            if let Interface::Ethernet(eth_iface) = &iface.merged {
180                if let Some(v) =
181                    eth_iface.veth.as_ref().map(|v| v.peer.as_str())
182                {
183                    veth_peers.push(v);
184                }
185            }
186        }
187        for iface in self.iter().filter(|i| {
188            i.merged.iface_type() == InterfaceType::Ethernet
189                && i.is_desired()
190                && i.current.is_none()
191                && i.merged.is_up()
192        }) {
193            if let Some(Interface::Ethernet(eth_iface)) = &iface.desired {
194                if eth_iface.veth.is_none()
195                    && self.mode != NetworkStateMode::GenerateConfig
196                    && !veth_peers.contains(&eth_iface.base.name.as_str())
197                    // The `resolve_sriov_reference()` will raise error if
198                    // SRIOV VF not found, so we skip check on them here.
199                    && !eth_iface.base.name.starts_with("sriov:")
200                {
201                    return Err(NmstateError::new(
202                        ErrorKind::InvalidArgument,
203                        format!(
204                            "Ethernet interface {} does not exists",
205                            eth_iface.base.name.as_str()
206                        ),
207                    ));
208                }
209            }
210        }
211
212        let mut pending_deletions: Vec<String> = Vec::new();
213
214        for iface in self.iter().filter(|i| {
215            i.merged.iface_type() == InterfaceType::Ethernet
216                && i.is_desired()
217                && i.merged.is_up()
218                && i.current.is_some()
219        }) {
220            if let (
221                Some(Interface::Ethernet(des_eth_iface)),
222                Some(Interface::Ethernet(cur_eth_iface)),
223            ) = (iface.desired.as_ref(), iface.current.as_ref())
224            {
225                if let (Some(veth_conf), Some(cur_veth_conf)) =
226                    (des_eth_iface.veth.as_ref(), cur_eth_iface.veth.as_ref())
227                {
228                    if veth_conf.peer != cur_veth_conf.peer {
229                        pending_deletions.push(cur_veth_conf.peer.to_string());
230                    }
231                }
232            }
233        }
234
235        for iface in self.iter().filter(|i| {
236            i.merged.iface_type() == InterfaceType::Ethernet
237                && i.is_desired()
238                && i.merged.is_absent()
239                && i.current.is_some()
240        }) {
241            if let Some(Interface::Ethernet(cur_eth_iface)) =
242                iface.current.as_ref()
243            {
244                if let Some(veth_conf) = cur_eth_iface.veth.as_ref() {
245                    pending_deletions.push(veth_conf.peer.to_string());
246                }
247            }
248        }
249
250        for del_peer in pending_deletions {
251            if let Some(iface) = self.kernel_ifaces.get_mut(&del_peer) {
252                iface.mark_as_absent();
253            }
254        }
255        Ok(())
256    }
257}
258
259impl Interfaces {
260    // Not allowing changing veth peer away from ignored peer unless previous
261    // peer changed from ignore to managed
262    pub(crate) fn validate_change_veth_ignored_peer(
263        &self,
264        current: &Self,
265        ignored_ifaces: &[(String, InterfaceType)],
266    ) -> Result<(), NmstateError> {
267        let ignored_veth_ifaces: Vec<&String> = ignored_ifaces
268            .iter()
269            .filter_map(|(n, t)| {
270                if t == &InterfaceType::Ethernet {
271                    Some(n)
272                } else {
273                    None
274                }
275            })
276            .collect();
277
278        for iface in self.kernel_ifaces.values().filter(|i| {
279            if let Interface::Ethernet(i) = i {
280                i.veth.is_some()
281            } else {
282                false
283            }
284        }) {
285            if let (
286                Interface::Ethernet(des_iface),
287                Some(Interface::Ethernet(cur_iface)),
288            ) = (iface, current.get_iface(iface.name(), InterfaceType::Veth))
289            {
290                if let (Some(des_peer), cur_peer) = (
291                    des_iface.veth.as_ref().map(|v| v.peer.as_str()),
292                    cur_iface.veth.as_ref().map(|v| v.peer.as_str()),
293                ) {
294                    let cur_peer = if let Some(c) = cur_peer {
295                        c
296                    } else {
297                        // The veth peer is in another namespace.
298                        let e = NmstateError::new(
299                            ErrorKind::InvalidArgument,
300                            format!(
301                                "Veth interface {} is currently holding peer \
302                                 assigned to other namespace Please remove \
303                                 this veth pair before changing veth peer to \
304                                 {des_peer}",
305                                iface.name(),
306                            ),
307                        );
308                        log::error!("{e}");
309                        return Err(e);
310                    };
311
312                    if des_peer != cur_peer
313                        && ignored_veth_ifaces.contains(&&cur_peer.to_string())
314                    {
315                        let e = NmstateError::new(
316                            ErrorKind::InvalidArgument,
317                            format!(
318                                "Veth interface {} is currently holding peer \
319                                 {} which is marked as ignored. Hence not \
320                                 allowing changing its peer to {}. Please \
321                                 remove this veth pair before changing veth \
322                                 peer",
323                                iface.name(),
324                                cur_peer,
325                                des_peer
326                            ),
327                        );
328                        log::error!("{e}");
329                        return Err(e);
330                    }
331                }
332            }
333        }
334        Ok(())
335    }
336
337    pub(crate) fn validate_new_veth_without_peer(
338        &self,
339        current: &Self,
340    ) -> Result<(), NmstateError> {
341        for iface in self.kernel_ifaces.values().filter(|i| {
342            i.is_up()
343                && i.iface_type() == InterfaceType::Veth
344                && !current.kernel_ifaces.contains_key(i.name())
345        }) {
346            if let Interface::Ethernet(eth_iface) = iface {
347                if eth_iface.veth.is_none() {
348                    return Err(NmstateError::new(
349                        ErrorKind::InvalidArgument,
350                        format!(
351                            "Veth interface {} does not exist, peer name is \
352                             required for creating it",
353                            iface.name()
354                        ),
355                    ));
356                }
357            }
358        }
359        Ok(())
360    }
361}