cni_plugin/
ip_range.rs

1//! The IpRange type and helpers for IP pools.
2
3use std::net::IpAddr;
4
5use ipnetwork::IpNetwork;
6use serde::{Deserialize, Serialize};
7
8// TODO: enforce all addresses being of the same type
9/// A range of IPs, usable for defining an IP pool.
10///
11/// The subnet is the only required field. The range can be further limited
12/// with the `range_start` and `range_end` fields, which are inclusive.
13///
14/// # Examples
15///
16/// ```json
17/// {"subnet": "10.0.0.0/8"}
18/// {"subnet": "10.0.10.0/23", "rangeStart": "10.0.11.0", "rangeEnd": "10.0.11.254"}
19/// {"subnet": "192.168.1.1/24", "gateway": "192.168.1.254"}
20/// ```
21#[derive(Clone, Debug, Deserialize, Serialize)]
22#[serde(rename_all = "camelCase")]
23pub struct IpRange {
24	/// The subnet for the range.
25	pub subnet: IpNetwork,
26
27	/// The start of the available range within the subnet, inclusive.
28	#[serde(default, skip_serializing_if = "Option::is_none")]
29	pub range_start: Option<IpAddr>,
30
31	/// The end of the available range within the subnet, inclusive.
32	#[serde(default, skip_serializing_if = "Option::is_none")]
33	pub range_end: Option<IpAddr>,
34
35	/// The gateway of the range.
36	///
37	/// Interpretation of an absent gateway is left to the implementation.
38	#[serde(default, skip_serializing_if = "Option::is_none")]
39	pub gateway: Option<IpAddr>,
40}
41
42impl IpRange {
43	/// Naive implementation of iterating the IP range.
44	///
45	/// This iterator will yield every IP available in the range, that is, every
46	/// IP in the subnet, except those lower than `range_start`, higher than
47	/// `range_end`, or the one which is the `gateway`.
48	///
49	/// The current implementation iterates through the entire range and filters
50	/// off the excluded IPs as per above. For IPv4 this will likely never be an
51	/// issue but IPv6 ranges are monstrous and could spend a long time spinning
52	/// before reaching `range_start`.
53	pub fn iter_free(&self) -> impl Iterator<Item = (IpNetwork, &Self)> {
54		let prefix = self.subnet.prefix();
55		let range_start = self.range_start;
56		let range_end = self.range_end;
57		let gateway = self.gateway;
58
59		self.subnet
60			.iter()
61			.filter(move |ip| {
62				if let Some(ref start) = range_start {
63					if ip < start {
64						// TODO: figure out how to START from there instead
65						return false;
66					}
67				}
68
69				if let Some(ref end) = range_end {
70					if ip > end {
71						// TODO: figure out how to stop the iterator there instead
72						return false;
73					}
74				}
75
76				if let Some(ref gw) = gateway {
77					if ip == gw {
78						return false;
79					}
80				}
81
82				true
83			})
84			.map(move |ip| (IpNetwork::new(ip, prefix).unwrap(), self))
85		// UNWRAP: panics on invalid prefix, but we got it from another IpNetwork
86	}
87}