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, 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.gen_conf_mode
196                    && !veth_peers.contains(&eth_iface.base.name.as_str())
197                {
198                    return Err(NmstateError::new(
199                        ErrorKind::InvalidArgument,
200                        format!(
201                            "Ethernet interface {} does not exists",
202                            eth_iface.base.name.as_str()
203                        ),
204                    ));
205                }
206            }
207        }
208
209        let mut pending_deletions: Vec<String> = Vec::new();
210
211        for iface in self.iter().filter(|i| {
212            i.merged.iface_type() == InterfaceType::Ethernet
213                && i.is_desired()
214                && i.merged.is_up()
215                && i.current.is_some()
216        }) {
217            if let (
218                Some(Interface::Ethernet(des_eth_iface)),
219                Some(Interface::Ethernet(cur_eth_iface)),
220            ) = (iface.desired.as_ref(), iface.current.as_ref())
221            {
222                if let (Some(veth_conf), Some(cur_veth_conf)) =
223                    (des_eth_iface.veth.as_ref(), cur_eth_iface.veth.as_ref())
224                {
225                    if veth_conf.peer != cur_veth_conf.peer {
226                        pending_deletions.push(cur_veth_conf.peer.to_string());
227                    }
228                }
229            }
230        }
231
232        for iface in self.iter().filter(|i| {
233            i.merged.iface_type() == InterfaceType::Ethernet
234                && i.is_desired()
235                && i.merged.is_absent()
236                && i.current.is_some()
237        }) {
238            if let Some(Interface::Ethernet(cur_eth_iface)) =
239                iface.current.as_ref()
240            {
241                if let Some(veth_conf) = cur_eth_iface.veth.as_ref() {
242                    pending_deletions.push(veth_conf.peer.to_string());
243                }
244            }
245        }
246
247        for del_peer in pending_deletions {
248            if let Some(iface) = self.kernel_ifaces.get_mut(&del_peer) {
249                iface.mark_as_absent();
250            }
251        }
252        Ok(())
253    }
254}
255
256impl Interfaces {
257    // Not allowing changing veth peer away from ignored peer unless previous
258    // peer changed from ignore to managed
259    pub(crate) fn validate_change_veth_ignored_peer(
260        &self,
261        current: &Self,
262        ignored_ifaces: &[(String, InterfaceType)],
263    ) -> Result<(), NmstateError> {
264        let ignored_veth_ifaces: Vec<&String> = ignored_ifaces
265            .iter()
266            .filter_map(|(n, t)| {
267                if t == &InterfaceType::Ethernet {
268                    Some(n)
269                } else {
270                    None
271                }
272            })
273            .collect();
274
275        for iface in self.kernel_ifaces.values().filter(|i| {
276            if let Interface::Ethernet(i) = i {
277                i.veth.is_some()
278            } else {
279                false
280            }
281        }) {
282            if let (
283                Interface::Ethernet(des_iface),
284                Some(Interface::Ethernet(cur_iface)),
285            ) = (iface, current.get_iface(iface.name(), InterfaceType::Veth))
286            {
287                if let (Some(des_peer), cur_peer) = (
288                    des_iface.veth.as_ref().map(|v| v.peer.as_str()),
289                    cur_iface.veth.as_ref().map(|v| v.peer.as_str()),
290                ) {
291                    let cur_peer = if let Some(c) = cur_peer {
292                        c
293                    } else {
294                        // The veth peer is in another namespace.
295                        let e = NmstateError::new(
296                            ErrorKind::InvalidArgument,
297                            format!(
298                                "Veth interface {} is currently holding peer \
299                                 assigned to other namespace Please remove \
300                                 this veth pair before changing veth peer to \
301                                 {des_peer}",
302                                iface.name(),
303                            ),
304                        );
305                        log::error!("{e}");
306                        return Err(e);
307                    };
308
309                    if des_peer != cur_peer
310                        && ignored_veth_ifaces.contains(&&cur_peer.to_string())
311                    {
312                        let e = NmstateError::new(
313                            ErrorKind::InvalidArgument,
314                            format!(
315                                "Veth interface {} is currently holding peer \
316                                 {} which is marked as ignored. Hence not \
317                                 allowing changing its peer to {}. Please \
318                                 remove this veth pair before changing veth \
319                                 peer",
320                                iface.name(),
321                                cur_peer,
322                                des_peer
323                            ),
324                        );
325                        log::error!("{e}");
326                        return Err(e);
327                    }
328                }
329            }
330        }
331        Ok(())
332    }
333
334    pub(crate) fn validate_new_veth_without_peer(
335        &self,
336        current: &Self,
337    ) -> Result<(), NmstateError> {
338        for iface in self.kernel_ifaces.values().filter(|i| {
339            i.is_up()
340                && i.iface_type() == InterfaceType::Veth
341                && !current.kernel_ifaces.contains_key(i.name())
342        }) {
343            if let Interface::Ethernet(eth_iface) = iface {
344                if eth_iface.veth.is_none() {
345                    return Err(NmstateError::new(
346                        ErrorKind::InvalidArgument,
347                        format!(
348                            "Veth interface {} does not exist, peer name is \
349                             required for creating it",
350                            iface.name()
351                        ),
352                    ));
353                }
354            }
355        }
356        Ok(())
357    }
358}