1use log::{debug, error, info, trace, warn};
3use serde::{Deserialize, Serialize};
4
5use super::CNIError;
6
7use super::exec::{Exec, ExecArgs, RawExec};
8use crate::libcni::result::result100;
9use crate::libcni::result::{APIResult, ResultCNI};
10use crate::libcni::types::NetworkConfig;
11use std::collections::HashMap;
12use std::fs;
13use std::io::Write;
14use std::path::Path;
15
16pub trait CNI {
17 fn add_network_list(
18 &self,
19 net: NetworkConfigList,
20 rt: RuntimeConf,
21 ) -> ResultCNI<Box<dyn APIResult>>;
22
23 fn check_network_list(&self, net: NetworkConfigList, rt: RuntimeConf) -> ResultCNI<()>;
24
25 fn delete_network_list(&self, net: NetworkConfigList, rt: RuntimeConf) -> ResultCNI<()>;
26
27 fn get_network_list_cached_result(
28 &self,
29 net: NetworkConfigList,
30 rt: RuntimeConf,
31 ) -> ResultCNI<Box<dyn APIResult>>;
32
33 fn add_network(
34 &self,
35 name: String,
36 cni_version: String,
37 net: NetworkConfig,
38 prev_result: Option<Box<dyn APIResult>>,
39 rt: RuntimeConf,
40 ) -> ResultCNI<Box<dyn APIResult>>;
41
42 fn check_network(
43 &self,
44 name: String,
45 cni_version: String,
46 prev_result: Option<Box<dyn APIResult>>,
47 net: NetworkConfig,
48 rt: RuntimeConf,
49 ) -> ResultCNI<()>;
50
51 fn delete_network(
52 &self,
53 name: String,
54 cni_version: String,
55 net: NetworkConfig,
56 rt: RuntimeConf,
57 ) -> ResultCNI<()>;
58
59 fn get_network_cached_result(
60 &self,
61 net: NetworkConfig,
62 rt: RuntimeConf,
63 ) -> ResultCNI<Box<dyn APIResult>>;
64
65 fn get_network_cached_config(
66 &self,
67 net: NetworkConfig,
68 rt: RuntimeConf,
69 ) -> ResultCNI<(Vec<u8>, RuntimeConf)>;
70
71 fn validate_network_list(&self, net: NetworkConfigList) -> ResultCNI<Vec<String>>;
72
73 fn validate_network(&self, net: NetworkConfig) -> ResultCNI<Vec<String>>;
74}
75
76#[derive(Default, Clone, Serialize, Deserialize)]
77pub struct NetworkConfigList {
78 pub name: String,
79 pub cni_version: String,
80 pub disable_check: bool,
81 pub plugins: Vec<NetworkConfig>,
82 pub bytes: Vec<u8>,
83}
84
85impl NetworkConfigList {
86 pub fn validate(&self) -> Result<(), String> {
87 if self.name.is_empty() {
88 return Err("Network name cannot be empty".to_string());
89 }
90 if self.cni_version.is_empty() {
91 return Err("CNI version cannot be empty".to_string());
92 }
93 if self.plugins.is_empty() {
94 return Err("At least one plugin is required".to_string());
95 }
96 Ok(())
97 }
98}
99
100#[derive(Clone, Default, Serialize, Deserialize)]
101pub struct RuntimeConf {
102 pub container_id: String,
103 pub net_ns: String,
104 pub if_name: String,
105 pub args: Vec<[String; 2]>,
106 pub capability_args: HashMap<String, String>,
107 pub cache_dir: String,
108}
109
110impl RuntimeConf {
111 pub fn get_cache_key(&self) -> String {
112 let id_part = if self.container_id.len() > 12 {
113 &self.container_id[..12]
114 } else {
115 &self.container_id
116 };
117
118 format!("{}-{}", id_part, self.if_name)
119 }
120}
121
122#[derive(Default)]
123pub struct CNIConfig {
124 pub path: Vec<String>,
125 pub exec: RawExec,
126 pub cache_dir: String,
127}
128
129impl CNIConfig {
130 fn get_cache_dir(&self, netname: &str) -> std::path::PathBuf {
131 let cache_dir = if self.cache_dir.is_empty() {
132 "/var/lib/cni/cache".to_string()
133 } else {
134 self.cache_dir.clone()
135 };
136
137 let path = Path::new(&cache_dir).join(netname);
138 if !path.exists() {
139 if let Err(e) = std::fs::create_dir_all(&path) {
140 warn!("Failed to create cache directory {}: {}", path.display(), e);
141 }
142 }
143 path
144 }
145
146 fn cache_network_config(
147 &self,
148 network_name: &str,
149 rt: &RuntimeConf,
150 config_bytes: &[u8],
151 ) -> ResultCNI<()> {
152 let cache_dir = self.get_cache_dir(network_name);
153 let key = rt.get_cache_key();
154 let config_path = cache_dir.join(format!("{}.config", key));
155
156 trace!("Caching network config to {}", config_path.display());
157
158 let mut file =
159 fs::File::create(config_path).map_err(|e| Box::new(CNIError::Io(Box::new(e))))?;
160
161 file.write_all(config_bytes)
162 .map_err(|e| Box::new(CNIError::Io(Box::new(e))))?;
163
164 Ok(())
165 }
166
167 fn cache_network_result(
168 &self,
169 network_name: &str,
170 rt: &RuntimeConf,
171 result: &dyn APIResult,
172 ) -> ResultCNI<()> {
173 let cache_dir = self.get_cache_dir(network_name);
174 let key = rt.get_cache_key();
175 let result_path = cache_dir.join(format!("{}.result", key));
176
177 trace!("Caching network result to {}", result_path.display());
178
179 let result_json = result.get_json();
180 let result_bytes = result_json.dump().as_bytes().to_vec();
181
182 let mut file =
183 fs::File::create(result_path).map_err(|e| Box::new(CNIError::Io(Box::new(e))))?;
184
185 file.write_all(&result_bytes)
186 .map_err(|e| Box::new(CNIError::Io(Box::new(e))))?;
187
188 Ok(())
189 }
190
191 #[allow(clippy::type_complexity)]
192 fn read_cached_network(
193 &self,
194 netname: &str,
195 rt: &RuntimeConf,
196 ) -> Result<(Box<dyn APIResult>, Vec<u8>, RuntimeConf), String> {
197 trace!("Reading cached network {} config", netname);
198 let cache_dir = self.get_cache_dir(netname);
199 let key = rt.get_cache_key();
200 let result_path = cache_dir.join(format!("{}.result", key));
201 let config_path = cache_dir.join(format!("{}.config", key));
202
203 if !result_path.exists() || !config_path.exists() {
204 return Err("Cache files do not exist".to_string());
205 }
206
207 let result_bytes = fs::read(&result_path).map_err(|e| e.to_string())?;
209 let config_bytes = fs::read(&config_path).map_err(|e| e.to_string())?;
210
211 let _: serde_json::Value = serde_json::from_slice(&result_bytes)
213 .map_err(|e| format!("Failed to parse result cache: {}", e))?;
214
215 let result = result100::Result {
217 cni_version: Some(
218 rt.args
219 .iter()
220 .find(|arg| arg[0] == "cniVersion")
221 .map(|arg| arg[1].clone())
222 .unwrap_or_else(|| "0.3.1".to_string()),
223 ),
224 ..Default::default()
225 };
226
227 let result = Box::new(result) as Box<dyn APIResult>;
228
229 Ok((result, config_bytes, rt.clone()))
230 }
231
232 fn build_new_config(
233 &self,
234 name: String,
235 cni_version: String,
236 orig: &NetworkConfig,
237 prev_result: Option<Box<dyn APIResult>>,
238 _rt: &RuntimeConf,
239 ) -> Result<NetworkConfig, String> {
240 debug!("Building new network config for {}", name);
241
242 let mut json_object = match json::parse(String::from_utf8_lossy(&orig.bytes).as_ref()) {
243 Ok(obj) => obj,
244 Err(e) => return Err(format!("Failed to parse network config: {}", e)),
245 };
246
247 if let Err(e) = json_object.insert("name", name) {
249 return Err(format!("Failed to insert name: {}", e));
250 }
251
252 if let Err(e) = json_object.insert("cniVersion", cni_version) {
253 return Err(format!("Failed to insert cniVersion: {}", e));
254 }
255
256 if let Some(prev_result) = prev_result {
258 let prev_json = prev_result.get_json();
259 debug!("Adding prevResult to config: {}", prev_json.dump());
260 if let Err(e) = json_object.insert("prevResult", prev_json) {
261 return Err(format!("Failed to insert prevResult: {}", e));
262 }
263 }
264
265 let new_bytes = json_object.dump().as_bytes().to_vec();
266 trace!("Built new config: {}", String::from_utf8_lossy(&new_bytes));
267
268 let mut new_conf = orig.clone();
270 new_conf.bytes = new_bytes;
271
272 Ok(new_conf)
273 }
274}
275
276impl CNI for CNIConfig {
277 fn add_network_list(
278 &self,
279 net: NetworkConfigList,
280 rt: RuntimeConf,
281 ) -> ResultCNI<Box<dyn APIResult>> {
282 info!("Adding network list: {}", net.name);
283
284 self.validate_network_list(net.clone())?;
286
287 let mut prev_result: Option<Box<dyn APIResult>> = None;
288
289 for (i, plugin) in net.plugins.iter().enumerate() {
291 debug!(
292 "Executing plugin {}/{}: {}",
293 i + 1,
294 net.plugins.len(),
295 plugin.network._type
296 );
297
298 let result = self.add_network(
300 net.name.clone(),
301 net.cni_version.clone(),
302 plugin.clone(),
303 prev_result,
304 rt.clone(),
305 )?;
306
307 prev_result = Some(result);
309 }
310
311 if let Some(result) = &prev_result {
313 if let Err(e) = self.cache_network_result(&net.name, &rt, result.as_ref()) {
314 warn!("Failed to cache network result: {}", e);
315 }
316 }
317
318 info!("Successfully added network list: {}", net.name);
319
320 Ok(prev_result.unwrap_or_else(|| Box::<result100::Result>::default()))
322 }
323
324 fn check_network_list(&self, net: NetworkConfigList, rt: RuntimeConf) -> ResultCNI<()> {
325 info!("Checking network list: {}", net.name);
326
327 if net.disable_check {
329 debug!("Network check is disabled for {}", net.name);
330 return Ok(());
331 }
332
333 let (prev_result, _, _) = match self.read_cached_network(&net.name, &rt) {
335 Ok(data) => data,
336 Err(e) => {
337 warn!("No cached result found for network {}: {}", net.name, e);
338 (
339 Box::<result100::Result>::default() as Box<dyn APIResult>,
340 Vec::new(),
341 rt.clone(),
342 )
343 }
344 };
345
346 for (i, plugin) in net.plugins.iter().enumerate() {
348 debug!(
349 "Checking plugin {}/{}: {}",
350 i + 1,
351 net.plugins.len(),
352 plugin.network._type
353 );
354
355 self.check_network(
357 net.name.clone(),
358 net.cni_version.clone(),
359 Some(prev_result.clone_box()),
360 plugin.clone(),
361 rt.clone(),
362 )?;
363 }
364
365 info!("Network list check passed: {}", net.name);
366 Ok(())
367 }
368
369 fn delete_network_list(&self, net: NetworkConfigList, rt: RuntimeConf) -> ResultCNI<()> {
370 info!("Deleting network list: {}", net.name);
371
372 for (i, plugin) in net.plugins.iter().enumerate().rev() {
374 debug!(
375 "Deleting plugin {}/{}: {}",
376 net.plugins.len() - i,
377 net.plugins.len(),
378 plugin.network._type
379 );
380
381 if let Err(e) = self.delete_network(
383 net.name.clone(),
384 net.cni_version.clone(),
385 plugin.clone(),
386 rt.clone(),
387 ) {
388 warn!("Error deleting plugin {}: {}", plugin.network._type, e);
389 }
391 }
392
393 let cache_dir = self.get_cache_dir(&net.name);
395 let key = rt.get_cache_key();
396 let result_path = cache_dir.join(format!("{}.result", key));
397 let config_path = cache_dir.join(format!("{}.config", key));
398
399 if result_path.exists() {
400 if let Err(e) = fs::remove_file(&result_path) {
401 warn!("Failed to remove cached result: {}", e);
402 }
403 }
404
405 if config_path.exists() {
406 if let Err(e) = fs::remove_file(&config_path) {
407 warn!("Failed to remove cached config: {}", e);
408 }
409 }
410
411 info!("Successfully deleted network list: {}", net.name);
412 Ok(())
413 }
414
415 fn get_network_list_cached_result(
416 &self,
417 net: NetworkConfigList,
418 rt: RuntimeConf,
419 ) -> ResultCNI<Box<dyn APIResult>> {
420 debug!("Getting cached result for network list: {}", net.name);
421
422 match self.read_cached_network(&net.name, &rt) {
423 Ok((result, _, _)) => {
424 debug!("Found cached result for network {}", net.name);
425 Ok(result)
426 }
427 Err(e) => {
428 let err_msg = format!("No cached result for network {}: {}", net.name, e);
429 error!("{}", err_msg);
430 Err(Box::new(CNIError::NotFound(net.name, err_msg)))
431 }
432 }
433 }
434
435 fn add_network(
436 &self,
437 name: String,
438 cni_version: String,
439 net: NetworkConfig,
440 prev_result: Option<Box<dyn APIResult>>,
441 rt: RuntimeConf,
442 ) -> ResultCNI<Box<dyn APIResult>> {
443 debug!("Adding network {} with plugin {}", name, net.network._type);
444
445 let plugin_path = self
447 .exec
448 .find_in_path(net.network._type.clone(), self.path.clone())?;
449
450 let environ = ExecArgs {
452 command: "ADD".to_string(),
453 containerd_id: rt.container_id.clone(),
454 netns: rt.net_ns.clone(),
455 plugin_args: rt.args.clone(),
456 plugin_args_str: rt
457 .args
458 .iter()
459 .map(|arg| format!("{}={}", arg[0], arg[1]))
460 .collect::<Vec<_>>()
461 .join(";"),
462 ifname: rt.if_name.clone(),
463 path: self.path[0].clone(),
464 };
465
466 let new_conf = match self.build_new_config(
468 name.clone(),
469 cni_version.clone(),
470 &net,
471 prev_result,
472 &rt,
473 ) {
474 Ok(conf) => conf,
475 Err(e) => return Err(Box::new(CNIError::Config(e))),
476 };
477
478 if let Err(e) = self.cache_network_config(&name, &rt, &new_conf.bytes) {
480 warn!("Failed to cache network config: {}", e);
481 }
482
483 let result_bytes =
485 self.exec
486 .exec_plugins(plugin_path, &new_conf.bytes, environ.to_env())?;
487
488 let mut result: result100::Result = match serde_json::from_slice(&result_bytes) {
490 Ok(r) => r,
491 Err(e) => {
492 debug!(
494 "Failed to directly deserialize result: {}, creating minimal result",
495 e
496 );
497 result100::Result {
498 cni_version: Some(cni_version.clone()),
499 ..Default::default()
500 }
501 }
502 };
503
504 if result.cni_version.is_none() {
506 result.cni_version = Some(cni_version);
507 }
508
509 debug!("Successfully added network {}", name);
510 Ok(Box::new(result))
511 }
512
513 fn check_network(
514 &self,
515 name: String,
516 cni_version: String,
517 prev_result: Option<Box<dyn APIResult>>,
518 net: NetworkConfig,
519 rt: RuntimeConf,
520 ) -> ResultCNI<()> {
521 debug!(
522 "Checking network {} with plugin {}",
523 name, net.network._type
524 );
525
526 let plugin_path = self
528 .exec
529 .find_in_path(net.network._type.clone(), self.path.clone())?;
530
531 let environ = ExecArgs {
533 command: "CHECK".to_string(),
534 containerd_id: rt.container_id.clone(),
535 netns: rt.net_ns.clone(),
536 plugin_args: rt.args.clone(),
537 plugin_args_str: rt
538 .args
539 .iter()
540 .map(|arg| format!("{}={}", arg[0], arg[1]))
541 .collect::<Vec<_>>()
542 .join(";"),
543 ifname: rt.if_name.clone(),
544 path: self.path[0].clone(),
545 };
546
547 let new_conf =
549 match self.build_new_config(name.clone(), cni_version, &net, prev_result, &rt) {
550 Ok(conf) => conf,
551 Err(e) => return Err(Box::new(CNIError::Config(e))),
552 };
553
554 self.exec
556 .exec_plugins(plugin_path, &new_conf.bytes, environ.to_env())?;
557
558 debug!("Network check passed for {}", name);
559 Ok(())
560 }
561
562 fn delete_network(
563 &self,
564 name: String,
565 cni_version: String,
566 net: NetworkConfig,
567 rt: RuntimeConf,
568 ) -> ResultCNI<()> {
569 debug!(
570 "Deleting network {} with plugin {}",
571 name, net.network._type
572 );
573
574 let plugin_path = self
576 .exec
577 .find_in_path(net.network._type.clone(), self.path.clone())?;
578
579 let environ = ExecArgs {
581 command: "DEL".to_string(),
582 containerd_id: rt.container_id.clone(),
583 netns: rt.net_ns.clone(),
584 plugin_args: rt.args.clone(),
585 plugin_args_str: rt
586 .args
587 .iter()
588 .map(|arg| format!("{}={}", arg[0], arg[1]))
589 .collect::<Vec<_>>()
590 .join(";"),
591 ifname: rt.if_name.clone(),
592 path: self.path[0].clone(),
593 };
594
595 let new_conf = match self.build_new_config(name.clone(), cni_version, &net, None, &rt) {
597 Ok(conf) => conf,
598 Err(e) => return Err(Box::new(CNIError::Config(e))),
599 };
600
601 self.exec
603 .exec_plugins(plugin_path, &new_conf.bytes, environ.to_env())?;
604
605 debug!("Successfully deleted network {}", name);
606 Ok(())
607 }
608
609 fn get_network_cached_result(
610 &self,
611 net: NetworkConfig,
612 rt: RuntimeConf,
613 ) -> ResultCNI<Box<dyn APIResult>> {
614 debug!("Getting cached result for network {}", net.network.name);
615
616 match self.read_cached_network(&net.network.name, &rt) {
617 Ok((result, _, _)) => {
618 debug!("Found cached result for network {}", net.network.name);
619 Ok(result)
620 }
621 Err(e) => {
622 let err_msg = format!("No cached result for network {}: {}", net.network.name, e);
623 error!("{}", err_msg);
624 Err(Box::new(CNIError::NotFound(net.network.name, err_msg)))
625 }
626 }
627 }
628
629 fn get_network_cached_config(
630 &self,
631 net: NetworkConfig,
632 rt: RuntimeConf,
633 ) -> ResultCNI<(Vec<u8>, RuntimeConf)> {
634 debug!("Getting cached config for network {}", net.network.name);
635
636 match self.read_cached_network(&net.network.name, &rt) {
637 Ok((_, config_bytes, cached_rt)) => {
638 debug!("Found cached config for network {}", net.network.name);
639 Ok((config_bytes, cached_rt))
640 }
641 Err(e) => {
642 let err_msg = format!("No cached config for network {}: {}", net.network.name, e);
643 error!("{}", err_msg);
644 Err(Box::new(CNIError::NotFound(net.network.name, err_msg)))
645 }
646 }
647 }
648
649 fn validate_network_list(&self, net: NetworkConfigList) -> ResultCNI<Vec<String>> {
650 debug!("Validating network list: {}", net.name);
651
652 if let Err(e) = net.validate() {
654 return Err(Box::new(CNIError::Config(e)));
655 }
656
657 let mut plugin_types = Vec::new();
659 for plugin in &net.plugins {
660 let types = self.validate_network(plugin.clone())?;
661 plugin_types.extend(types);
662 }
663
664 debug!("Network list validation passed for {}", net.name);
665 Ok(plugin_types)
666 }
667
668 fn validate_network(&self, net: NetworkConfig) -> ResultCNI<Vec<String>> {
669 debug!("Validating network: {}", net.network.name);
670
671 if net.network._type.is_empty() {
673 return Err(Box::new(CNIError::Config(
674 "Plugin type cannot be empty".to_string(),
675 )));
676 }
677
678 let plugin_path = self
680 .exec
681 .find_in_path(net.network._type.clone(), self.path.clone())?;
682
683 let environ = ExecArgs {
685 command: "VERSION".to_string(),
686 containerd_id: "".to_string(),
687 netns: "".to_string(),
688 plugin_args: Vec::new(),
689 plugin_args_str: "".to_string(),
690 ifname: "".to_string(),
691 path: self.path[0].clone(),
692 };
693
694 match self.exec.exec_plugins(plugin_path, &[], environ.to_env()) {
696 Ok(version_bytes) => {
697 match serde_json::from_slice::<serde_json::Value>(&version_bytes) {
699 Ok(version_info) => {
700 if let Some(supported_versions) = version_info.get("supportedVersions") {
701 if let Some(versions_array) = supported_versions.as_array() {
702 let versions: Vec<String> = versions_array
703 .iter()
704 .filter_map(|v| v.as_str().map(|s| s.to_string()))
705 .collect();
706
707 debug!(
708 "Plugin {} supports versions: {:?}",
709 net.network._type, versions
710 );
711 return Ok(versions);
712 }
713 }
714
715 warn!(
716 "Plugin {} did not return supported versions",
717 net.network._type
718 );
719 Ok(vec![])
720 }
721 Err(e) => {
722 warn!(
723 "Failed to parse version info from plugin {}: {}",
724 net.network._type, e
725 );
726 Ok(vec![])
727 }
728 }
729 }
730 Err(e) => {
731 warn!(
732 "Failed to get version info from plugin {}: {}",
733 net.network._type, e
734 );
735 Ok(vec![])
736 }
737 }
738 }
739}