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}