1use serde::{Deserialize, Deserializer, Serialize};
4
5use crate::{BaseInterface, InterfaceType, NetworkState};
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29#[non_exhaustive]
30pub struct IpsecInterface {
31 #[serde(flatten)]
32 pub base: BaseInterface,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub libreswan: Option<LibreswanConfig>,
35}
36
37impl Default for IpsecInterface {
38 fn default() -> Self {
39 Self {
40 base: BaseInterface {
41 iface_type: InterfaceType::Ipsec,
42 ..Default::default()
43 },
44 libreswan: None,
45 }
46 }
47}
48
49impl IpsecInterface {
50 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub(crate) fn hide_secrets(&mut self) {
55 if let Some(c) = self.libreswan.as_mut() {
56 if c.psk.is_some() {
57 c.psk = Some(NetworkState::PASSWORD_HID_BY_NMSTATE.to_string());
58 }
59 }
60 }
61
62 pub(crate) fn sanitize(&mut self, is_desired: bool) {
65 if let Some(ipv4_conf) = self.base.ipv4.as_mut() {
66 if ipv4_conf.dhcp == Some(false)
67 && ipv4_conf.enabled
68 && ipv4_conf.enabled_defined
69 {
70 if is_desired {
71 log::info!(
72 "Treating IPv4 `dhcp: false` for IPSec interface {} \
73 as IPv4 disabled",
74 self.base.name.as_str()
75 );
76 }
77 ipv4_conf.enabled = false;
78 ipv4_conf.dhcp = None;
79 }
80 }
81 }
82}
83
84#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
85#[serde(deny_unknown_fields)]
86#[non_exhaustive]
87pub struct LibreswanConfig {
88 pub right: String,
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub rightid: Option<String>,
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub rightrsasigkey: Option<String>,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub rightcert: Option<String>,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub left: Option<String>,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub leftid: Option<String>,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub leftrsasigkey: Option<String>,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub leftcert: Option<String>,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub ikev2: Option<String>,
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub psk: Option<String>,
108 #[serde(skip_serializing_if = "Option::is_none")]
109 pub ikelifetime: Option<String>,
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub salifetime: Option<String>,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub ike: Option<String>,
114 #[serde(skip_serializing_if = "Option::is_none")]
115 pub esp: Option<String>,
116 #[serde(
117 skip_serializing_if = "Option::is_none",
118 default,
119 deserialize_with = "crate::deserializer::option_u64_or_string"
120 )]
121 pub dpddelay: Option<u64>,
122 #[serde(
123 skip_serializing_if = "Option::is_none",
124 default,
125 deserialize_with = "crate::deserializer::option_u64_or_string"
126 )]
127 pub dpdtimeout: Option<u64>,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 pub dpdaction: Option<String>,
130 #[serde(
131 skip_serializing_if = "Option::is_none",
132 rename = "ipsec-interface",
133 default,
134 deserialize_with = "parse_ipsec_iface"
135 )]
136 pub ipsec_interface: Option<String>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub authby: Option<String>,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub rightsubnet: Option<String>,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub leftsubnet: Option<String>,
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub leftmodecfgclient: Option<bool>,
145 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
146 pub kind: Option<LibreswanConnectionType>,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub hostaddrfamily: Option<LibreswanAddressFamily>,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub clientaddrfamily: Option<LibreswanAddressFamily>,
151 #[serde(
152 skip_serializing_if = "Option::is_none",
153 rename = "require-id-on-certificate"
154 )]
155 pub require_id_on_certificate: Option<bool>,
156}
157
158impl LibreswanConfig {
159 pub fn new() -> Self {
160 Self::default()
161 }
162}
163
164impl std::fmt::Debug for LibreswanConfig {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 f.debug_struct("LibreswanConfig")
167 .field("right", &self.right)
168 .field("rightid", &self.rightid)
169 .field("rightrsasigkey", &self.rightrsasigkey)
170 .field("rightcert", &self.rightcert)
171 .field("left", &self.left)
172 .field("leftid", &self.leftid)
173 .field("leftrsasigkey", &self.leftrsasigkey)
174 .field("leftcert", &self.leftcert)
175 .field("ikev2", &self.ikev2)
176 .field(
177 "psk",
178 &Some(NetworkState::PASSWORD_HID_BY_NMSTATE.to_string()),
179 )
180 .field("ikelifetime", &self.ikelifetime)
181 .field("salifetime", &self.salifetime)
182 .field("ike", &self.ike)
183 .field("esp", &self.esp)
184 .field("dpddelay", &self.dpddelay)
185 .field("dpdtimeout", &self.dpdtimeout)
186 .field("dpdaction", &self.dpdaction)
187 .field("ipsec_interface", &self.ipsec_interface)
188 .field("authby", &self.authby)
189 .field("rightsubnet", &self.rightsubnet)
190 .field("leftsubnet", &self.leftsubnet)
191 .field("leftmodecfgclient", &self.leftmodecfgclient)
192 .field("kind", &self.kind)
193 .field("hostaddrfamily", &self.hostaddrfamily)
194 .field("clientaddrfamily", &self.clientaddrfamily)
195 .field("require_id_on_certificate", &self.require_id_on_certificate)
196 .finish()
197 }
198}
199
200fn parse_ipsec_iface<'de, D>(
201 deserializer: D,
202) -> Result<Option<String>, D::Error>
203where
204 D: Deserializer<'de>,
205{
206 let v = serde_json::Value::deserialize(deserializer)?;
207
208 match v {
209 serde_json::Value::Number(d) => {
210 if let Some(d) = d.as_u64() {
211 Ok(Some(d.to_string()))
212 } else {
213 Err(serde::de::Error::custom(
214 "Invalid ipsec-interface value, should be unsigned \
215 integer, string 'yes' or 'no'",
216 ))
217 }
218 }
219 serde_json::Value::String(s) => match s.as_str() {
220 "yes" | "no" => Ok(Some(s)),
221 _ => {
222 if s.parse::<u32>().is_ok() {
223 Ok(Some(s))
224 } else {
225 Err(serde::de::Error::custom(
226 "Invalid ipsec-interface value, should be unsigned \
227 integer, string 'yes' or 'no'",
228 ))
229 }
230 }
231 },
232 _ => Err(serde::de::Error::custom(
233 "Invalid ipsec-interface value, should be unsigned integer, \
234 string 'yes' or 'no'",
235 )),
236 }
237}
238
239#[derive(
240 Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default,
241)]
242#[non_exhaustive]
243#[serde(rename_all = "lowercase")]
244pub enum LibreswanConnectionType {
245 #[default]
246 Tunnel,
247 Transport,
248}
249
250impl std::fmt::Display for LibreswanConnectionType {
251 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252 write!(
253 f,
254 "{}",
255 match self {
256 Self::Tunnel => "tunnel",
257 Self::Transport => "transport",
258 }
259 )
260 }
261}
262
263#[derive(
264 Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default,
265)]
266#[non_exhaustive]
267#[serde(rename_all = "lowercase")]
268pub enum LibreswanAddressFamily {
269 #[default]
270 Ipv4,
271 Ipv6,
272}
273
274impl std::fmt::Display for LibreswanAddressFamily {
275 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276 write!(
277 f,
278 "{}",
279 match self {
280 Self::Ipv4 => "ipv4",
281 Self::Ipv6 => "ipv6",
282 }
283 )
284 }
285}