1use serde::{Deserialize, Serialize};
4
5use super::common::{DataSource, EntityOrigin};
6use super::entity_id::EntityId;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
9pub enum FirewallAction {
10 Allow,
11 Block,
12 Reject,
13}
14
15impl<'de> Deserialize<'de> for FirewallAction {
16 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
17 where
18 D: serde::Deserializer<'de>,
19 {
20 let s = String::deserialize(deserializer)?;
21 match s.to_lowercase().as_str() {
22 "allow" => Ok(Self::Allow),
23 "block" => Ok(Self::Block),
24 "reject" => Ok(Self::Reject),
25 _ => Err(serde::de::Error::unknown_variant(
26 &s,
27 &["allow", "block", "reject"],
28 )),
29 }
30 }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
34pub enum IpVersion {
35 Ipv4,
36 Ipv6,
37 Both,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct FirewallZone {
43 pub id: EntityId,
44 pub name: String,
45 pub network_ids: Vec<EntityId>,
46 pub origin: Option<EntityOrigin>,
47
48 #[serde(skip)]
49 #[allow(dead_code)]
50 pub(crate) source: DataSource,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct PolicyEndpoint {
58 pub zone_id: Option<EntityId>,
59 pub filter: Option<TrafficFilter>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64#[serde(tag = "kind", rename_all = "snake_case")]
65pub enum TrafficFilter {
66 Network {
67 network_ids: Vec<EntityId>,
68 match_opposite: bool,
69 #[serde(default, skip_serializing_if = "Vec::is_empty")]
70 mac_addresses: Vec<String>,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
72 ports: Option<PortSpec>,
73 },
74 IpAddress {
75 addresses: Vec<IpSpec>,
76 match_opposite: bool,
77 #[serde(default, skip_serializing_if = "Vec::is_empty")]
78 mac_addresses: Vec<String>,
79 #[serde(default, skip_serializing_if = "Option::is_none")]
80 ports: Option<PortSpec>,
81 },
82 MacAddress {
83 mac_addresses: Vec<String>,
84 #[serde(default, skip_serializing_if = "Option::is_none")]
85 ports: Option<PortSpec>,
86 },
87 Port {
88 ports: PortSpec,
89 },
90 Region {
91 regions: Vec<String>,
92 #[serde(default, skip_serializing_if = "Option::is_none")]
93 ports: Option<PortSpec>,
94 },
95 Application {
96 application_ids: Vec<i64>,
97 #[serde(default, skip_serializing_if = "Option::is_none")]
98 ports: Option<PortSpec>,
99 },
100 ApplicationCategory {
101 category_ids: Vec<i64>,
102 #[serde(default, skip_serializing_if = "Option::is_none")]
103 ports: Option<PortSpec>,
104 },
105 Domain {
106 domains: Vec<String>,
107 #[serde(default, skip_serializing_if = "Option::is_none")]
108 ports: Option<PortSpec>,
109 },
110 Other {
112 raw_type: String,
113 },
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118#[serde(tag = "kind", rename_all = "snake_case")]
119pub enum PortSpec {
120 Values {
121 items: Vec<String>,
122 match_opposite: bool,
123 },
124 MatchingList {
125 list_id: EntityId,
126 match_opposite: bool,
127 },
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132#[serde(tag = "kind", rename_all = "snake_case")]
133pub enum IpSpec {
134 Address { value: String },
135 Range { start: String, stop: String },
136 Subnet { value: String },
137 MatchingList { list_id: EntityId },
138}
139
140impl TrafficFilter {
141 pub fn summary(&self) -> String {
143 match self {
144 Self::Network {
145 network_ids,
146 match_opposite,
147 ..
148 } => {
149 let prefix = if *match_opposite { "NOT " } else { "" };
150 format!("{prefix}net({} networks)", network_ids.len())
151 }
152 Self::IpAddress {
153 addresses,
154 match_opposite,
155 ..
156 } => {
157 let prefix = if *match_opposite { "NOT " } else { "" };
158 let items: Vec<String> = addresses
159 .iter()
160 .map(|a| match a {
161 IpSpec::Address { value } | IpSpec::Subnet { value } => value.clone(),
162 IpSpec::Range { start, stop } => format!("{start}-{stop}"),
163 IpSpec::MatchingList { list_id } => format!("list:{list_id}"),
164 })
165 .collect();
166 let display = if items.len() <= 2 {
167 items.join(", ")
168 } else {
169 format!("{}, {} +{} more", items[0], items[1], items.len() - 2)
170 };
171 format!("{prefix}ip({display})")
172 }
173 Self::MacAddress { mac_addresses, .. } => {
174 format!("mac({})", mac_addresses.len())
175 }
176 Self::Port { ports } => summarize_ports(ports),
177 Self::Region { regions, .. } => format!("region({})", regions.join(",")),
178 Self::Application {
179 application_ids, ..
180 } => {
181 format!("app({} apps)", application_ids.len())
182 }
183 Self::ApplicationCategory { category_ids, .. } => {
184 format!("cat({} categories)", category_ids.len())
185 }
186 Self::Domain { domains, .. } => {
187 if domains.len() <= 2 {
188 format!("domain({})", domains.join(", "))
189 } else {
190 format!("domain({} +{} more)", domains[0], domains.len() - 1)
191 }
192 }
193 Self::Other { raw_type } => format!("({raw_type})"),
194 }
195 }
196}
197
198fn summarize_ports(spec: &PortSpec) -> String {
199 match spec {
200 PortSpec::Values {
201 items,
202 match_opposite,
203 } => {
204 let prefix = if *match_opposite { "NOT " } else { "" };
205 format!("{prefix}port({})", items.join(","))
206 }
207 PortSpec::MatchingList {
208 list_id,
209 match_opposite,
210 } => {
211 let prefix = if *match_opposite { "NOT " } else { "" };
212 format!("{prefix}port(list:{list_id})")
213 }
214 }
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct FirewallPolicy {
220 pub id: EntityId,
221 pub name: String,
222 pub description: Option<String>,
223 pub enabled: bool,
224 pub index: Option<i32>,
225
226 pub action: FirewallAction,
227 pub ip_version: IpVersion,
228
229 pub source: PolicyEndpoint,
231 pub destination: PolicyEndpoint,
232
233 pub source_summary: Option<String>,
235 pub destination_summary: Option<String>,
236
237 pub protocol_summary: Option<String>,
239 pub schedule: Option<String>,
240 pub ipsec_mode: Option<String>,
241
242 pub connection_states: Vec<String>,
243 pub logging_enabled: bool,
244
245 pub origin: Option<EntityOrigin>,
246
247 #[serde(skip)]
248 #[allow(dead_code)]
249 pub(crate) data_source: DataSource,
250}
251
252#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
256pub enum NatType {
257 Masquerade,
258 Source,
259 Destination,
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct NatPolicy {
265 pub id: EntityId,
266 pub name: String,
267 pub description: Option<String>,
268 pub enabled: bool,
269 pub nat_type: NatType,
270 pub interface_id: Option<EntityId>,
271 pub protocol: Option<String>,
272 pub src_address: Option<String>,
273 pub src_port: Option<String>,
274 pub dst_address: Option<String>,
275 pub dst_port: Option<String>,
276 pub translated_address: Option<String>,
277 pub translated_port: Option<String>,
278 pub origin: Option<EntityOrigin>,
279
280 #[serde(skip)]
281 #[allow(dead_code)]
282 pub(crate) data_source: DataSource,
283}
284
285#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
287pub enum AclAction {
288 Allow,
289 Block,
290}
291
292#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
294pub enum AclRuleType {
295 Ipv4,
296 Mac,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct AclRule {
302 pub id: EntityId,
303 pub name: String,
304 pub enabled: bool,
305 pub rule_type: AclRuleType,
306 pub action: AclAction,
307 pub source_summary: Option<String>,
308 pub destination_summary: Option<String>,
309 pub origin: Option<EntityOrigin>,
310
311 #[serde(skip)]
312 #[allow(dead_code)]
313 pub(crate) source: DataSource,
314}