cni_plugin/config.rs
1//! Configuration structures.
2//!
3//! You’ll want to start with [`NetworkConfig`].
4
5use std::collections::HashMap;
6
7use ipnetwork::IpNetwork;
8use semver::Version;
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12use crate::{ip_range::IpRange, macaddr::MacAddr};
13
14pub use crate::dns::Dns;
15
16/// Top-level network configuration.
17///
18/// This is the structure that is provided to plugins by CNI, not the structure
19/// that administrators write to configure CNI. As such, some fields defined in
20/// the spec to only exist in the administrative schema are not included here.
21///
22/// This struct’s members include all fields described by the spec, as well as a
23/// `specific` field which is a map of [`String`]s to [`Value`]s, and will catch
24/// any custom fields present at the top level of the configuration. There are
25/// other `specific` fields in _some_ of the structs that are in fields below.
26///
27/// In general, this structure will only ever be read or modified by a plugin,
28/// but all fields are public to allow construction if necessary.
29#[derive(Clone, Debug, Deserialize, Serialize)]
30#[serde(rename_all = "camelCase")]
31pub struct NetworkConfig {
32 /// Version of the CNI spec to which this configuration conforms.
33 ///
34 /// This is a [Semantic Version 2.0](https://semver.org/) version number,
35 /// and that is enforced here by being a [`Version`], not a string.
36 ///
37 /// This version must be used when creating [replies][crate::reply], which
38 /// include a similar field. The spec does not currently cover the case
39 /// where an [`ErrorReply`][crate::reply::ErrorReply] must be created
40 /// _before_ the config is parsed, or in cases of unparseable config; this
41 /// is [under discussion](https://github.com/containernetworking/cni/issues/827).
42 #[serde(deserialize_with = "crate::version::deserialize_version")]
43 #[serde(serialize_with = "crate::version::serialize_version")]
44 pub cni_version: Version,
45
46 /// Name of the network configuration.
47 ///
48 /// This is unique across all network configurations on a host (or other
49 /// administrative domain). There are format restrictions but as this field
50 /// will always be provided by the CNI runtime and is not to be created or
51 /// altered by plugins, those are not checked here.
52 pub name: String,
53
54 /// Name of the top-level plugin binary on disk.
55 ///
56 /// This is called `type` in the JSON.
57 ///
58 /// The “top-level” distinction is because a config may include an IPAM
59 /// section, which contains its own `plugin` field, and this full
60 /// configuration is provided to all sub plugins (via delegation), so a
61 /// plugin may receive a configuration where this field doesn’t match its
62 /// own name.
63 #[serde(rename = "type")]
64 pub plugin: String,
65
66 /// Arbitrary arguments passed by the runtime.
67 ///
68 /// This is a map of arbitrary arguments passed by the runtime, which might
69 /// be on their own or on behalf of the user/operator. Plugins are free to
70 /// ignore it if they’re not expecting anything within.
71 ///
72 /// This replaces the older and deprecated `CNI_ARGS` environment variable,
73 /// which this library doesn’t read (you may do so yourself if needed).
74 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
75 pub args: HashMap<String, Value>,
76
77 /// Set up an IP masquerade on the host for this network.
78 ///
79 /// This is an optional, “well-known” configuration field.
80 ///
81 /// If `true`, and if the plugin supports it, an IP masquerade must be set
82 /// up on the host for this network.
83 #[serde(default)]
84 pub ip_masq: bool,
85
86 /// IP Address Management sub-config.
87 ///
88 /// This is an optional, “well-known” configuration field.
89 ///
90 /// If present, and if the plugin supports it, the IPAM plugin specified by
91 /// the `plugin` field of [`IpamConfig`] must be invoked via delegation.
92 #[serde(default, skip_serializing_if = "Option::is_none")]
93 pub ipam: Option<IpamConfig>,
94
95 /// DNS sub-config.
96 ///
97 /// This is an optional, “well-known” configuration field.
98 ///
99 /// If present, and if the plugin supports it, the DNS settings specified
100 /// must be configured for this network.
101 ///
102 /// Note that this section is sourced from the administrative configuration.
103 /// There is another field for runtime-provided DNS settings when supported,
104 /// see [`RuntimeConfig`].
105 #[serde(default, skip_serializing_if = "Option::is_none")]
106 pub dns: Option<Dns>,
107
108 /// Dynamic information provided by the runtime.
109 ///
110 /// This is an optional, “well-known” configuration field, named
111 /// `runtimeConfig` in the spec, which is derived from the `capabilities`
112 /// field only present in the administrative configuration.
113 ///
114 /// Plugins can request that the runtime insert this dynamic configuration
115 /// by explicitly listing their capabilities in the administrative
116 /// configuration. Unlike the `args` field, plugins are expected to act on
117 /// the data provided, and should not ignore it if they can’t.
118 #[serde(
119 default,
120 rename = "runtimeConfig",
121 skip_serializing_if = "Option::is_none"
122 )]
123 pub runtime: Option<RuntimeConfig>,
124
125 /// The result of the previous plugin in a chain.
126 ///
127 /// This is the `prevResult` field in the spec.
128 ///
129 /// This field may contain anything, but most likely contains a
130 /// [`SuccessReply`][crate::reply::SuccessReply] or
131 /// [`IpamSuccessReply`][crate::reply::IpamSuccessReply]. You should use
132 /// [`serde_json::from_value`] to reinterpret it as whatever you expect it
133 /// to be.
134 ///
135 /// Plugins provided a `prev_result` as part of their input configuration
136 /// must per spec output it as their result, with any possible modifications
137 /// made by that plugin included. If a plugin makes no changes that would be
138 /// reflected in the success reply, then it must output a reply equivalent
139 /// to the provided `prev_result`.
140 ///
141 /// In a `CHECK` operation, the plugin must consult the `prev_result` to
142 /// determine the expected interfaces and addresses.
143 #[serde(default, skip_serializing_if = "Option::is_none")]
144 pub prev_result: Option<Value>,
145
146 /// Custom top-level fields.
147 ///
148 /// This is a [`serde(flatten)`](https://serde.rs/field-attrs.html#flatten)
149 /// field which aggregates any and all additional custom fields not covered
150 /// above.
151 ///
152 /// Plugins may use this for custom configuration.
153 #[serde(flatten)]
154 pub specific: HashMap<String, Value>,
155}
156
157/// IP Address Management configuration.
158///
159/// IPAM plugins will be invoked with the full [`NetworkConfig`] as input, but
160/// should take their configuration from this section only.
161#[derive(Clone, Debug, Deserialize, Serialize)]
162#[serde(rename_all = "camelCase")]
163pub struct IpamConfig {
164 /// Name of the IPAM plugin binary on disk.
165 ///
166 /// This is called `type` in the JSON.
167 #[serde(rename = "type")]
168 pub plugin: String,
169
170 /// All other IPAM fields.
171 ///
172 /// This is a [`serde(flatten)`](https://serde.rs/field-attrs.html#flatten)
173 /// field which aggregates any and all additional fields apart from the
174 /// `plugin` field above.
175 ///
176 /// The spec describes nothing in particular for this section, so it is
177 /// entirely up to plugins to interpret it as required.
178 #[serde(flatten)]
179 pub specific: HashMap<String, Value>,
180}
181
182/// Dynamic information provided by the runtime.
183///
184/// These are generated by the runtime. Note that not all runtimes implement all
185/// of these. Also note that all fields below except for `specific` are for
186/// “well-known” configs as documented in [CONVENTIONS.md], and those that are
187/// not implemented here will appear in the `specific` map.
188///
189/// Finally, note this struct is marked non-exhaustive: new fields may be added
190/// to hoist new “well-known” configs out of the `specific` map.
191///
192/// [CONVENTIONS.md]: https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md
193#[derive(Clone, Debug, Default, Deserialize, Serialize)]
194#[serde(rename_all = "camelCase")]
195#[non_exhaustive]
196pub struct RuntimeConfig {
197 /// List of port mappings from host to namespace to set up.
198 #[serde(default, skip_serializing_if = "Vec::is_empty")]
199 pub port_mappings: Vec<PortMapping>,
200
201 /// List of pools to use for IPAM.
202 ///
203 /// An IP pool is a list of IP ranges, hence this is this a list of lists of
204 /// IP ranges. The outer list defines how many IP addresses to allocate,
205 /// with each inner pool defining where to allocate from.
206 ///
207 /// The [`IpRange`] type has methods to help with allocation.
208 #[serde(default, skip_serializing_if = "Vec::is_empty")]
209 pub ips_ranges: Vec<Vec<IpRange>>,
210
211 /// Bandwidth limits to set.
212 #[serde(default, skip_serializing_if = "Option::is_none")]
213 pub bandwidth: Option<BandwidthLimits>,
214
215 /// DNS configuration.
216 ///
217 /// Note that this section is set by the runtime. There is another field for
218 /// DNS in sourced in the administrative config, see [`NetworkConfig`].
219 #[serde(default, skip_serializing_if = "Option::is_none")]
220 pub dns: Option<Dns>,
221
222 /// List of static IPs to use for IPAM.
223 #[serde(default, skip_serializing_if = "Vec::is_empty")]
224 pub ips: Vec<IpNetwork>,
225
226 /// MAC address to use for the interface.
227 #[serde(default, skip_serializing_if = "Option::is_none")]
228 pub mac: Option<MacAddr>,
229
230 /// List of names mapped to the IPs assigned to this interface.
231 #[serde(default, skip_serializing_if = "Vec::is_empty")]
232 pub aliases: Vec<String>,
233
234 // TODO: infinibandGUID (behind feature)
235 // TODO: (PCI) deviceID (behind feature)
236 /// Custom runtime fields.
237 ///
238 /// This is a [`serde(flatten)`](https://serde.rs/field-attrs.html#flatten)
239 /// field which aggregates any and all additional custom fields not covered
240 /// above.
241 ///
242 /// Take note of the caveats in the struct documentation.
243 #[serde(flatten)]
244 pub specific: HashMap<String, Value>,
245}
246
247/// Port mapping entry.
248///
249/// This defines a single mapping (forwarding) of a port from the host to the
250/// container namespace.
251///
252/// It is up to the implementation what to do if the `protocol` is left `None`.
253#[derive(Clone, Debug, Deserialize, Serialize)]
254#[serde(rename_all = "camelCase")]
255pub struct PortMapping {
256 /// Port on the host.
257 pub host_port: u16,
258
259 /// Port in the namespace.
260 pub container_port: u16,
261
262 /// Protocol to forward.
263 #[serde(default, skip_serializing_if = "Option::is_none")]
264 pub protocol: Option<PortProtocol>,
265}
266
267/// Protocol for a port.
268///
269/// This is non-exhaustive as more protocols may be added.
270#[derive(Clone, Debug, Deserialize, Serialize)]
271#[serde(rename_all = "snake_case")]
272#[non_exhaustive]
273pub enum PortProtocol {
274 /// The TCP protocol.
275 Tcp,
276
277 /// The UDP protocol.
278 Udp,
279}
280
281/// Bandwidth limits to set on the interface.
282#[derive(Clone, Debug, Default, Deserialize, Serialize)]
283#[serde(rename_all = "camelCase")]
284pub struct BandwidthLimits {
285 /// Rate limit for incoming traffic in bits per second.
286 #[serde(default, skip_serializing_if = "Option::is_none")]
287 pub ingress_rate: Option<usize>,
288
289 /// Burst limit for incoming traffic in bits.
290 #[serde(default, skip_serializing_if = "Option::is_none")]
291 pub ingress_burst: Option<usize>,
292
293 /// Rate limit for outgoing traffic in bits per second.
294 #[serde(default, skip_serializing_if = "Option::is_none")]
295 pub egress_rate: Option<usize>,
296
297 /// Burst limit for outgoing traffic in bits.
298 #[serde(default, skip_serializing_if = "Option::is_none")]
299 pub egress_burst: Option<usize>,
300}