nmstate/ifaces/
loopback.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use std::net::{Ipv4Addr, Ipv6Addr};
4
5use serde::{Deserialize, Serialize};
6
7use crate::{
8    BaseInterface, ErrorKind, InterfaceIpAddr, InterfaceIpv4, InterfaceIpv6,
9    InterfaceState, InterfaceType, NmstateError,
10};
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13#[non_exhaustive]
14/// Loopback interface. Only contain information of [BaseInterface].
15/// Limitations
16///  * Cannot enable DHCP or autoconf.
17///  * The [InterfaceState::Absent] can only restore the loopback configure back
18///    to default.
19///  * Ignore the request of disable IPv4 or IPv6.
20///  * Even not desired, the `127.0.0.1/8` and `::1` are always appended to
21///    static IP address list.
22///  * Require NetworkManager 1.41+ unless in kernel only mode.
23///
24/// Example yaml outpuf of `[crate::NetworkState]` with loopback interface:
25/// ```yml
26/// interfaces:
27/// - name: lo
28///   type: loopback
29///   state: up
30///   mtu: 65535
31///   ipv4:
32///     enabled: true
33///     address:
34///     - ip: 127.0.0.1
35///       prefix-length: 8
36///   ipv6:
37///     enabled: true
38///     address:
39///     - ip: ::1
40///       prefix-length: 128
41///   accept-all-mac-addresses: false
42/// ```
43pub struct LoopbackInterface {
44    #[serde(flatten)]
45    pub base: BaseInterface,
46}
47
48impl Default for LoopbackInterface {
49    fn default() -> Self {
50        let mut base = BaseInterface::new();
51        base.iface_type = InterfaceType::Loopback;
52        base.name = "lo".to_string();
53        base.state = InterfaceState::Up;
54        base.ipv4 = Some(InterfaceIpv4 {
55            enabled: true,
56            enabled_defined: true,
57            addresses: Some(vec![InterfaceIpAddr {
58                ip: Ipv4Addr::LOCALHOST.into(),
59                prefix_length: 8,
60                ..Default::default()
61            }]),
62            ..Default::default()
63        });
64        base.ipv6 = Some(InterfaceIpv6 {
65            enabled: true,
66            enabled_defined: true,
67            addresses: Some(vec![InterfaceIpAddr {
68                ip: Ipv6Addr::LOCALHOST.into(),
69                prefix_length: 128,
70                ..Default::default()
71            }]),
72            ..Default::default()
73        });
74
75        Self { base }
76    }
77}
78
79impl LoopbackInterface {
80    pub fn new() -> Self {
81        Self::default()
82    }
83
84    pub(crate) fn sanitize(
85        &self,
86        is_desired: bool,
87    ) -> Result<(), NmstateError> {
88        if is_desired {
89            if self.base.ipv4.as_ref().map(|i| i.enabled) == Some(false) {
90                return Err(NmstateError::new(
91                    ErrorKind::InvalidArgument,
92                    "Loopback interface cannot be have IPv4 disabled"
93                        .to_string(),
94                ));
95            }
96            if self.base.ipv6.as_ref().map(|i| i.enabled) == Some(false) {
97                return Err(NmstateError::new(
98                    ErrorKind::InvalidArgument,
99                    "Loopback interface cannot be have IPv6 disabled"
100                        .to_string(),
101                ));
102            }
103            if self.base.ipv4.as_ref().map(|i| i.is_auto()) == Some(true) {
104                return Err(NmstateError::new(
105                    ErrorKind::InvalidArgument,
106                    "Loopback interface cannot be have IPv4 DHCP enabled"
107                        .to_string(),
108                ));
109            }
110            if self.base.ipv6.as_ref().map(|i| i.is_auto()) == Some(true) {
111                return Err(NmstateError::new(
112                    ErrorKind::InvalidArgument,
113                    "Loopback interface cannot be have IPv6 \
114                autoconf/DHCPv6 enabled"
115                        .to_string(),
116                ));
117            }
118        }
119        Ok(())
120    }
121}