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}