1mod cgroups;
20mod image;
21mod networking;
22mod oci;
23mod overlay;
24mod seccomp;
25
26pub use self::{cgroups::*, image::*, networking::*, oci::*, overlay::*, seccomp::*};
28
29pub(crate) fn parse_u32(s: &str) -> Option<u32> {
35 let mut result: u32 = 0;
36 for b in s.bytes() {
37 if b.is_ascii_digit() {
38 result = result.checked_mul(10)?;
39 result = result.checked_add((b - b'0') as u32)?;
40 } else {
41 return None;
42 }
43 }
44 Some(result)
45}
46
47pub(crate) fn parse_u64(s: &str) -> Option<u64> {
49 let mut result: u64 = 0;
50 for b in s.bytes() {
51 if b.is_ascii_digit() {
52 result = result.checked_mul(10)?;
53 result = result.checked_add((b - b'0') as u64)?;
54 } else {
55 return None;
56 }
57 }
58 Some(result)
59}
60
61pub(crate) fn simple_sha256(data: &[u8]) -> [u8; 32] {
64 const K: [u32; 64] = [
65 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
66 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
67 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
68 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
69 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
70 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
71 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
72 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
73 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
74 0xc67178f2,
75 ];
76
77 let mut h: [u32; 8] = [
78 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
79 0x5be0cd19,
80 ];
81
82 let original_len_bits = (data.len() as u64).saturating_mul(8);
83
84 let padded_len = (data.len() + 9).div_ceil(64) * 64;
86 let mut padded = [0u8; 128]; let use_stack = padded_len <= 128;
89
90 #[cfg(feature = "alloc")]
91 let mut heap_padded: alloc::vec::Vec<u8>;
92 #[cfg(not(feature = "alloc"))]
93 let heap_padded: [u8; 0] = [];
94
95 let buf: &mut [u8] = if use_stack {
96 padded[..data.len()].copy_from_slice(data);
97 padded[data.len()] = 0x80;
98 let len_offset = padded_len - 8;
99 padded[len_offset..len_offset + 8].copy_from_slice(&original_len_bits.to_be_bytes());
100 &mut padded[..padded_len]
101 } else {
102 #[cfg(feature = "alloc")]
103 {
104 heap_padded = alloc::vec![0u8; padded_len];
105 heap_padded[..data.len()].copy_from_slice(data);
106 heap_padded[data.len()] = 0x80;
107 let len_offset = padded_len - 8;
108 heap_padded[len_offset..len_offset + 8]
109 .copy_from_slice(&original_len_bits.to_be_bytes());
110 &mut heap_padded[..]
111 }
112 #[cfg(not(feature = "alloc"))]
113 {
114 return [0u8; 32];
117 }
118 };
119
120 let mut w = [0u32; 64];
122 let mut block_offset = 0;
123 while block_offset < buf.len() {
124 let block = &buf[block_offset..block_offset + 64];
125 for i in 0..16 {
126 w[i] = u32::from_be_bytes([
127 block[i * 4],
128 block[i * 4 + 1],
129 block[i * 4 + 2],
130 block[i * 4 + 3],
131 ]);
132 }
133 for i in 16..64 {
134 let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
135 let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
136 w[i] = w[i - 16]
137 .wrapping_add(s0)
138 .wrapping_add(w[i - 7])
139 .wrapping_add(s1);
140 }
141
142 let mut a = h[0];
143 let mut b = h[1];
144 let mut c = h[2];
145 let mut d = h[3];
146 let mut e = h[4];
147 let mut f = h[5];
148 let mut g = h[6];
149 let mut hh = h[7];
150
151 for i in 0..64 {
152 let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
153 let ch = (e & f) ^ ((!e) & g);
154 let temp1 = hh
155 .wrapping_add(s1)
156 .wrapping_add(ch)
157 .wrapping_add(K[i])
158 .wrapping_add(w[i]);
159 let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
160 let maj = (a & b) ^ (a & c) ^ (b & c);
161 let temp2 = s0.wrapping_add(maj);
162
163 hh = g;
164 g = f;
165 f = e;
166 e = d.wrapping_add(temp1);
167 d = c;
168 c = b;
169 b = a;
170 a = temp1.wrapping_add(temp2);
171 }
172
173 h[0] = h[0].wrapping_add(a);
174 h[1] = h[1].wrapping_add(b);
175 h[2] = h[2].wrapping_add(c);
176 h[3] = h[3].wrapping_add(d);
177 h[4] = h[4].wrapping_add(e);
178 h[5] = h[5].wrapping_add(f);
179 h[6] = h[6].wrapping_add(g);
180 h[7] = h[7].wrapping_add(hh);
181
182 block_offset += 64;
183 }
184
185 let mut out = [0u8; 32];
186 for (i, &val) in h.iter().enumerate() {
187 out[i * 4..i * 4 + 4].copy_from_slice(&val.to_be_bytes());
188 }
189 out
190}
191
192#[cfg(test)]
197mod tests {
198 #[allow(unused_imports)]
199 use alloc::vec;
200
201 use super::*;
202
203 #[test]
206 fn test_oci_lifecycle_state_display() {
207 assert_eq!(
208 alloc::format!("{}", OciLifecycleState::Creating),
209 "creating"
210 );
211 assert_eq!(alloc::format!("{}", OciLifecycleState::Created), "created");
212 assert_eq!(alloc::format!("{}", OciLifecycleState::Running), "running");
213 assert_eq!(alloc::format!("{}", OciLifecycleState::Stopped), "stopped");
214 }
215
216 #[test]
217 fn test_oci_namespace_kind_parse() {
218 assert_eq!(
219 OciNamespaceKind::from_str_kind("pid"),
220 Some(OciNamespaceKind::Pid)
221 );
222 assert_eq!(
223 OciNamespaceKind::from_str_kind("network"),
224 Some(OciNamespaceKind::Network)
225 );
226 assert_eq!(OciNamespaceKind::from_str_kind("invalid"), None);
227 }
228
229 #[test]
230 fn test_oci_config_parse_basic() {
231 let input = concat!(
232 "oci_version=1.0.2\n",
233 "root_path=/rootfs\n",
234 "root_readonly=true\n",
235 "hostname=mycontainer\n",
236 "process_cwd=/app\n",
237 "process_uid=1000\n",
238 "process_gid=1000\n",
239 "process_terminal=true\n",
240 "process_arg=/bin/sh\n",
241 "process_arg=-c\n",
242 "process_env=PATH=/usr/bin\n",
243 "namespace=pid\n",
244 "namespace=network:/proc/123/ns/net\n",
245 "cgroups_path=/sys/fs/cgroup/mycontainer\n",
246 "memory_limit=67108864\n",
247 "cpu_shares=512\n",
248 "cpu_quota=50000\n",
249 "cpu_period=100000\n",
250 "hook_prestart=/usr/bin/hook:5\n",
251 "mount=/proc:proc:proc:nosuid,noexec\n",
252 );
253 let config = OciConfig::parse(input).unwrap();
254 assert_eq!(config.oci_version, "1.0.2");
255 assert_eq!(config.root.path, "/rootfs");
256 assert!(config.root.readonly);
257 assert_eq!(config.hostname, "mycontainer");
258 assert_eq!(config.process.cwd, "/app");
259 assert_eq!(config.process.uid, 1000);
260 assert_eq!(config.process.gid, 1000);
261 assert!(config.process.terminal);
262 assert_eq!(config.process.args.len(), 2);
263 assert_eq!(config.process.args[0], "/bin/sh");
264 assert_eq!(config.process.env.len(), 1);
265 assert_eq!(config.linux.namespaces.len(), 2);
266 assert_eq!(config.linux.namespaces[0].kind, OciNamespaceKind::Pid);
267 assert!(config.linux.namespaces[0].path.is_none());
268 assert_eq!(config.linux.namespaces[1].kind, OciNamespaceKind::Network);
269 assert!(config.linux.namespaces[1].path.is_some());
270 assert_eq!(config.linux.memory_limit, 67108864);
271 assert_eq!(config.linux.cpu_shares, 512);
272 assert_eq!(config.linux.cpu_quota, 50000);
273 assert_eq!(config.hooks.prestart.len(), 1);
274 assert_eq!(config.hooks.prestart[0].timeout_secs, 5);
275 assert_eq!(config.mounts.len(), 1);
276 assert_eq!(config.mounts[0].options.len(), 2);
277 }
278
279 #[test]
280 fn test_oci_config_validate_empty_args() {
281 let input = "root_path=/rootfs\n";
282 let config = OciConfig::parse(input).unwrap();
283 assert!(config.validate().is_err());
284 }
285
286 #[test]
287 fn test_oci_container_lifecycle() {
288 let input = "root_path=/rootfs\nprocess_arg=/bin/sh\nprocess_cwd=/\n";
289 let config = OciConfig::parse(input).unwrap();
290 let mut container = OciContainer::new("test1", "/bundles/test1", config).unwrap();
291 assert_eq!(container.state, OciLifecycleState::Creating);
292
293 container.mark_created().unwrap();
294 assert_eq!(container.state, OciLifecycleState::Created);
295
296 container.start(42).unwrap();
297 assert_eq!(container.state, OciLifecycleState::Running);
298 assert_eq!(container.pid, 42);
299
300 container.stop().unwrap();
301 assert_eq!(container.state, OciLifecycleState::Stopped);
302 }
303
304 #[test]
305 fn test_oci_container_invalid_transition() {
306 let input = "root_path=/rootfs\nprocess_arg=/bin/sh\nprocess_cwd=/\n";
307 let config = OciConfig::parse(input).unwrap();
308 let mut container = OciContainer::new("test1", "/bundles/test1", config).unwrap();
309 assert!(container.start(1).is_err());
311 }
312
313 #[test]
314 fn test_oci_container_pivot_root() {
315 let input = "root_path=/rootfs\nprocess_arg=/bin/sh\nprocess_cwd=/\n";
316 let config = OciConfig::parse(input).unwrap();
317 let container = OciContainer::new("test1", "/bundles/test1", config).unwrap();
318 let (old, new) = container.pivot_root().unwrap();
319 assert_eq!(old, "/.pivot_root");
320 assert_eq!(new, "/rootfs");
321 }
322
323 #[test]
326 fn test_layer_digest_compute() {
327 let digest = LayerDigest::compute(b"hello");
328 assert_eq!(digest.bytes[0], 0x2c);
331 assert_eq!(digest.bytes[1], 0xf2);
332 }
333
334 #[test]
335 fn test_layer_digest_hex() {
336 let digest = LayerDigest::compute(b"");
337 let hex = digest.to_hex();
338 assert_eq!(hex.len(), 64);
339 assert!(hex.starts_with("e3b0c442"));
342 }
343
344 #[test]
345 fn test_is_gzip() {
346 assert!(is_gzip(&[0x1f, 0x8b, 0x08]));
347 assert!(!is_gzip(&[0x00, 0x00]));
348 assert!(!is_gzip(&[0x1f]));
349 }
350
351 #[test]
352 fn test_tar_size_parse() {
353 let mut header = [0u8; 512];
354 header[124] = b'0';
356 header[125] = b'0';
357 header[126] = b'0';
358 header[127] = b'0';
359 header[128] = b'6';
360 header[129] = b'4';
361 header[130] = b'4';
362 assert_eq!(parse_tar_size(&header), 0o644);
363 }
364
365 #[test]
366 fn test_container_image_compose() {
367 let config = b"config data";
368 let layer1 = b"layer 1 data";
369 let layer2 = b"layer 2 data";
370 let image = ContainerImage::compose("test:latest", config, &[layer1, layer2]);
371 assert_eq!(image.name, "test:latest");
372 assert_eq!(image.layers.len(), 2);
373 assert_eq!(image.manifest.layer_digests.len(), 2);
374 assert_eq!(image.manifest.schema_version, 2);
375 assert_eq!(image.image_id, image.manifest.config_digest);
376 }
377
378 #[test]
379 fn test_layer_cache_operations() {
380 let mut cache = LayerCache::new(2);
381 let digest = LayerDigest::compute(b"test");
382 let hex = digest.to_hex();
383 let layer = CachedLayer {
384 digest: digest.clone(),
385 extracted_path: String::from("/layers/test"),
386 size_bytes: 1024,
387 reference_count: 1,
388 };
389 assert!(cache.insert(layer));
390 assert_eq!(cache.entry_count(), 1);
391 assert!(cache.get(&hex).is_some());
392 assert!(cache.add_ref(&hex));
393 assert_eq!(cache.get(&hex).unwrap().reference_count, 2);
394 assert!(cache.release(&hex));
395 assert_eq!(cache.get(&hex).unwrap().reference_count, 1);
396 assert!(cache.release(&hex)); assert_eq!(cache.entry_count(), 0);
398 }
399
400 #[test]
401 fn test_layer_cache_full() {
402 let mut cache = LayerCache::new(1);
403 let l1 = CachedLayer {
404 digest: LayerDigest::compute(b"a"),
405 extracted_path: String::from("/a"),
406 size_bytes: 100,
407 reference_count: 1,
408 };
409 let l2 = CachedLayer {
410 digest: LayerDigest::compute(b"b"),
411 extracted_path: String::from("/b"),
412 size_bytes: 200,
413 reference_count: 1,
414 };
415 assert!(cache.insert(l1));
416 assert!(cache.is_full());
417 assert!(!cache.insert(l2));
418 }
419
420 #[test]
423 fn test_cgroup_memory_basic() {
424 let mut mem = CgroupMemoryController::new(1);
425 mem.set_hard_limit(4096).unwrap();
426 assert!(mem.charge(2048).is_ok());
427 assert_eq!(mem.usage_current, 2048);
428 assert_eq!(mem.usage_peak, 2048);
429 mem.uncharge(1024);
430 assert_eq!(mem.usage_current, 1024);
431 assert_eq!(mem.usage_peak, 2048); }
433
434 #[test]
435 fn test_cgroup_memory_oom() {
436 let mut mem = CgroupMemoryController::new(1);
437 mem.set_hard_limit(1024).unwrap();
438 mem.charge(512).unwrap();
439 let result = mem.charge(1024);
440 assert!(result.is_err());
441 assert_eq!(mem.oom.oom_kill_count, 1);
442 assert!(mem.oom.under_oom);
443 }
444
445 #[test]
446 fn test_cgroup_memory_soft_limit() {
447 let mut mem = CgroupMemoryController::new(1);
448 mem.set_soft_limit(512);
449 mem.charge(256).unwrap();
450 assert!(!mem.soft_limit_exceeded());
451 mem.set_hard_limit(4096).unwrap();
453 mem.charge(512).unwrap();
454 assert!(mem.soft_limit_exceeded());
455 }
456
457 #[test]
458 fn test_cgroup_memory_reclaim_from_cache() {
459 let mut mem = CgroupMemoryController::new(1);
460 mem.set_hard_limit(2048).unwrap();
461 mem.charge(1024).unwrap();
462 mem.add_cache(512);
463 assert!(mem.charge(1024).is_ok());
466 }
467
468 #[test]
469 fn test_memory_stat_total() {
470 let stat = MemoryStat {
471 rss: 1000,
472 cache: 500,
473 mapped_file: 200,
474 anon: 300,
475 swap: 0,
476 };
477 assert_eq!(stat.total(), 1500);
478 }
479
480 #[test]
483 fn test_cgroup_cpu_shares() {
484 let mut cpu = CgroupCpuController::new(1);
485 assert_eq!(cpu.shares, 1024);
486 cpu.set_shares(2048).unwrap();
487 assert_eq!(cpu.shares, 2048);
488 assert!(cpu.set_shares(1).is_err()); assert!(cpu.set_shares(300000).is_err()); }
491
492 #[test]
493 fn test_cgroup_cpu_bandwidth() {
494 let mut cpu = CgroupCpuController::new(1);
495 cpu.set_bandwidth(50000, 100000).unwrap();
496 assert_eq!(cpu.quota_us, 50000);
497 assert_eq!(cpu.period_us, 100000);
498 assert_eq!(cpu.effective_cpu_percent_x100(), 5000);
500 }
501
502 #[test]
503 fn test_cgroup_cpu_throttle() {
504 let mut cpu = CgroupCpuController::new(1);
505 cpu.set_bandwidth(10000, 100000).unwrap(); assert!(!cpu.consume_runtime(5_000_000)); assert!(cpu.consume_runtime(6_000_000)); assert!(cpu.throttled);
510 assert_eq!(cpu.stats.nr_throttled, 1);
511 }
512
513 #[test]
514 fn test_cgroup_cpu_period_reset() {
515 let mut cpu = CgroupCpuController::new(1);
516 cpu.set_bandwidth(10000, 100000).unwrap();
517 cpu.consume_runtime(10_000_000);
518 cpu.new_period();
519 assert!(!cpu.throttled);
520 assert_eq!(cpu.stats.nr_periods, 1);
521 }
522
523 #[test]
524 fn test_cgroup_cpu_burst() {
525 let mut cpu = CgroupCpuController::new(1);
526 cpu.set_bandwidth(10000, 100000).unwrap(); cpu.set_burst(5000); cpu.consume_runtime(5_000_000);
530 cpu.new_period(); assert!(cpu.burst_budget_ns > 0);
532 assert!(!cpu.consume_runtime(14_000_000));
534 }
535
536 #[test]
537 fn test_cgroup_cpu_proportional() {
538 let cpu = CgroupCpuController::new(1);
539 let ns = cpu.proportional_runtime_ns(2048);
541 assert_eq!(ns, 50_000_000);
543 }
544
545 #[test]
548 fn test_overlay_basic_lookup() {
549 let mut lower = OverlayLayer::new(true);
550 lower.entries.insert(
552 String::from("etc/passwd"),
553 OverlayEntry {
554 path: String::from("etc/passwd"),
555 kind: OverlayEntryKind::File,
556 content: b"root:x:0:0".to_vec(),
557 mode: 0o644,
558 },
559 );
560
561 let mut fs = OverlayFs::new("/tmp/work");
562 fs.add_lower_layer(lower);
563 let entry = fs.lookup("etc/passwd");
564 assert!(entry.is_some());
565 assert_eq!(entry.unwrap().content, b"root:x:0:0");
566 }
567
568 #[test]
569 fn test_overlay_upper_takes_precedence() {
570 let mut lower = OverlayLayer::new(true);
571 lower.entries.insert(
572 String::from("etc/hostname"),
573 OverlayEntry {
574 path: String::from("etc/hostname"),
575 kind: OverlayEntryKind::File,
576 content: b"oldhost".to_vec(),
577 mode: 0o644,
578 },
579 );
580 let mut fs = OverlayFs::new("/tmp/work");
581 fs.add_lower_layer(lower);
582 fs.write_file("etc/hostname", b"newhost".to_vec(), 0o644)
583 .unwrap();
584 let entry = fs.lookup("etc/hostname").unwrap();
585 assert_eq!(entry.content, b"newhost");
586 }
587
588 #[test]
589 fn test_overlay_whiteout() {
590 let mut lower = OverlayLayer::new(true);
591 lower.entries.insert(
592 String::from("etc/shadow"),
593 OverlayEntry {
594 path: String::from("etc/shadow"),
595 kind: OverlayEntryKind::File,
596 content: b"secret".to_vec(),
597 mode: 0o600,
598 },
599 );
600 let mut fs = OverlayFs::new("/tmp/work");
601 fs.add_lower_layer(lower);
602 assert!(fs.lookup("etc/shadow").is_some());
603 fs.delete_file("etc/shadow").unwrap();
604 assert!(fs.lookup("etc/shadow").is_none());
605 }
606
607 #[test]
608 fn test_overlay_opaque_dir() {
609 let mut lower = OverlayLayer::new(true);
610 lower.entries.insert(
611 String::from("etc/conf.d/old.conf"),
612 OverlayEntry {
613 path: String::from("etc/conf.d/old.conf"),
614 kind: OverlayEntryKind::File,
615 content: b"old".to_vec(),
616 mode: 0o644,
617 },
618 );
619 let mut fs = OverlayFs::new("/tmp/work");
620 fs.add_lower_layer(lower);
621 fs.make_opaque_dir("etc/conf.d").unwrap();
622 let listing = fs.list_dir("etc/conf.d");
624 assert!(listing.is_empty());
625 }
626
627 #[test]
628 fn test_overlay_readonly_layer() {
629 let mut lower = OverlayLayer::new(true);
630 let result = lower.add_entry(OverlayEntry {
631 path: String::from("test"),
632 kind: OverlayEntryKind::File,
633 content: Vec::new(),
634 mode: 0o644,
635 });
636 assert!(result.is_err());
637 }
638
639 #[test]
642 fn test_veth_pair_creation() {
643 let pair = create_veth_pair("veth0", "eth0", 42);
644 assert_eq!(pair.host.name, "veth0");
645 assert_eq!(pair.container.name, "eth0");
646 assert_eq!(pair.host.peer_name, "eth0");
647 assert_eq!(pair.container.peer_name, "veth0");
648 assert_eq!(pair.host.namespace_id, 0);
649 assert_eq!(pair.container.namespace_id, 42);
650 assert_eq!(pair.host.mtu, 1500);
651 assert_ne!(pair.host.mac, pair.container.mac);
653 }
654
655 #[test]
656 fn test_veth_mac_generation() {
657 let mac1 = generate_veth_mac(1);
658 let mac2 = generate_veth_mac(2);
659 assert_eq!(mac1[0], 0x02); assert_ne!(mac1, mac2);
661 }
662
663 #[test]
664 fn test_nat_table() {
665 let mut nat = NatTable::new(0xC0A80001); nat.enable_masquerade();
667 assert!(nat.masquerade_enabled);
668
669 let mapping = NatPortMapping {
670 external_port: 8080,
671 internal_port: 80,
672 protocol: 6, container_ip: 0x0A000002, };
675 nat.add_port_mapping(mapping).unwrap();
676 assert_eq!(nat.port_mappings.len(), 1);
677
678 let dup = NatPortMapping {
680 external_port: 8080,
681 internal_port: 8080,
682 protocol: 6,
683 container_ip: 0x0A000003,
684 };
685 assert!(nat.add_port_mapping(dup).is_err());
686
687 let found = nat.lookup_inbound(8080, 6).unwrap();
689 assert_eq!(found.internal_port, 80);
690 assert_eq!(found.container_ip, 0x0A000002);
691
692 let rewritten = nat.snat_rewrite(0x0A000002);
694 assert_eq!(rewritten, Some(0xC0A80001));
695
696 assert!(nat.remove_port_mapping(8080, 6));
698 assert!(nat.lookup_inbound(8080, 6).is_none());
699 }
700
701 #[test]
702 fn test_veth_bridge() {
703 let mut bridge = VethBridge::new("br0", 0x0A000001, 0xFFFFFF00);
704 bridge.attach("veth0");
705 bridge.attach("veth1");
706 assert_eq!(bridge.attached_count(), 2);
707 assert!(bridge.in_subnet(0x0A0000FE)); assert!(!bridge.in_subnet(0x0B000001)); bridge.add_arp_proxy(ArpProxyEntry {
711 ip: 0x0A000002,
712 mac: [0x02, 0x42, 0x00, 0x00, 0x00, 0x01],
713 });
714 assert!(bridge.arp_lookup(0x0A000002).is_some());
715 assert!(bridge.arp_lookup(0x0A000003).is_none());
716
717 bridge.detach("veth0");
718 assert_eq!(bridge.attached_count(), 1);
719 }
720
721 #[test]
724 fn test_seccomp_data_as_bytes() {
725 let data = SeccompData::new(1, audit_arch::X86_64, [0; 6]);
726 let bytes = data.as_bytes();
727 assert_eq!(bytes.len(), 64);
728 assert_eq!(
730 u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
731 1
732 );
733 assert_eq!(
735 u32::from_ne_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
736 audit_arch::X86_64
737 );
738 }
739
740 #[test]
741 fn test_seccomp_filter_validate() {
742 let mut filter = SeccompFilter::new();
743 assert!(filter.validate().is_err()); filter.push(BpfInstruction::ret(SeccompAction::Allow as u32));
746 assert!(filter.validate().is_ok());
747 }
748
749 #[test]
750 fn test_seccomp_filter_deny_syscalls() {
751 let filter = SeccompFilter::deny_syscalls(
752 audit_arch::X86_64,
753 &[59], 1, );
756 assert!(filter.validate().is_ok());
757
758 let data_execve = SeccompData::new(59, audit_arch::X86_64, [0; 6]);
760 let action = filter.evaluate(&data_execve);
761 assert_eq!(action, SeccompAction::errno(1));
762
763 let data_read = SeccompData::new(0, audit_arch::X86_64, [0; 6]);
765 let action = filter.evaluate(&data_read);
766 assert_eq!(action, SeccompAction::Allow as u32);
767 }
768
769 #[test]
770 fn test_seccomp_filter_allow_syscalls() {
771 let filter = SeccompFilter::allow_syscalls(
772 audit_arch::X86_64,
773 &[0, 1, 60], );
775 assert!(filter.validate().is_ok());
776
777 let data_read = SeccompData::new(0, audit_arch::X86_64, [0; 6]);
778 assert_eq!(filter.evaluate(&data_read), SeccompAction::Allow as u32);
779
780 let data_execve = SeccompData::new(59, audit_arch::X86_64, [0; 6]);
781 assert_eq!(
782 filter.evaluate(&data_execve),
783 SeccompAction::KillProcess as u32
784 );
785 }
786
787 #[test]
788 fn test_seccomp_wrong_arch_killed() {
789 let filter = SeccompFilter::deny_syscalls(audit_arch::X86_64, &[59], 1);
790 let data = SeccompData::new(59, audit_arch::AARCH64, [0; 6]);
791 assert_eq!(filter.evaluate(&data), SeccompAction::KillProcess as u32);
792 }
793
794 #[test]
795 fn test_seccomp_state_disabled() {
796 let state = SeccompState::new();
797 let data = SeccompData::new(59, audit_arch::X86_64, [0; 6]);
798 assert_eq!(state.evaluate(&data), SeccompAction::Allow as u32);
799 }
800
801 #[test]
802 fn test_seccomp_state_strict() {
803 let mut state = SeccompState::new();
804 state.mode = SeccompMode::Strict;
805 let data = SeccompData::new(0, audit_arch::X86_64, [0; 6]);
807 assert_eq!(state.evaluate(&data), SeccompAction::Allow as u32);
808 let data2 = SeccompData::new(59, audit_arch::X86_64, [0; 6]);
810 assert_eq!(state.evaluate(&data2), SeccompAction::KillThread as u32);
811 }
812
813 #[test]
814 fn test_seccomp_state_filter_install() {
815 let mut state = SeccompState::new();
816 let filter = SeccompFilter::deny_syscalls(audit_arch::X86_64, &[59], 1);
817 state.install_filter(filter).unwrap();
818 assert_eq!(state.mode, SeccompMode::Filter);
819 assert_eq!(state.filter_count(), 1);
820 }
821
822 #[test]
823 fn test_seccomp_fork_inherit() {
824 let mut state = SeccompState::new();
825 let mut f1 = SeccompFilter::deny_syscalls(audit_arch::X86_64, &[59], 1);
826 f1.inherit_on_fork = true;
827 let mut f2 = SeccompFilter::deny_syscalls(audit_arch::X86_64, &[60], 1);
828 f2.inherit_on_fork = false;
829 state.install_filter(f1).unwrap();
830 state.install_filter(f2).unwrap();
831 let child = state.fork_inherit();
832 assert_eq!(child.filter_count(), 1); }
834
835 #[test]
836 fn test_bpf_instruction_constructors() {
837 let load = BpfInstruction::load_word(4);
838 assert_eq!(load.code, BpfOpcode::LdAbsW as u16);
839 assert_eq!(load.k, 4);
840
841 let jeq = BpfInstruction::jump_eq(42, 1, 0);
842 assert_eq!(jeq.code, BpfOpcode::JmpJeqK as u16);
843 assert_eq!(jeq.k, 42);
844 assert_eq!(jeq.jt, 1);
845 assert_eq!(jeq.jf, 0);
846
847 let ret = BpfInstruction::ret(SeccompAction::Allow as u32);
848 assert_eq!(ret.code, BpfOpcode::Ret as u16);
849 }
850
851 #[test]
852 fn test_seccomp_errno_action() {
853 let action = SeccompAction::errno(13); assert_eq!(action, 0x0005_000D);
855 }
856
857 #[test]
860 fn test_parse_u32() {
861 assert_eq!(parse_u32("12345"), Some(12345));
862 assert_eq!(parse_u32("0"), Some(0));
863 assert_eq!(parse_u32("abc"), None);
864 assert_eq!(parse_u32(""), Some(0));
865 }
866
867 #[test]
868 fn test_parse_u64() {
869 assert_eq!(parse_u64("123456789"), Some(123456789));
870 assert_eq!(parse_u64("0"), Some(0));
871 }
872
873 #[test]
874 fn test_sha256_empty() {
875 let hash = simple_sha256(b"");
876 assert_eq!(hash[0], 0xe3);
879 assert_eq!(hash[1], 0xb0);
880 assert_eq!(hash[2], 0xc4);
881 assert_eq!(hash[3], 0x42);
882 }
883
884 #[test]
885 fn test_sha256_hello() {
886 let hash = simple_sha256(b"hello");
887 assert_eq!(hash[0], 0x2c);
890 assert_eq!(hash[1], 0xf2);
891 assert_eq!(hash[2], 0x4d);
892 assert_eq!(hash[3], 0xba);
893 }
894}