1#![allow(dead_code)]
9
10use alloc::{collections::BTreeMap, string::String, vec::Vec};
11
12use spin::RwLock;
13
14use crate::{error::KernelError, sync::once_lock::GlobalState};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum BondMode {
23 RoundRobin,
25 ActiveBackup,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
35pub enum BondError {
36 BondAlreadyExists,
38 BondNotFound,
40 SlaveAlreadyExists,
42 SlaveNotFound,
44 NoSlavesAvailable,
46 NotInitialized,
48}
49
50impl From<BondError> for KernelError {
51 fn from(_e: BondError) -> Self {
52 KernelError::InvalidArgument {
53 name: "bonding",
54 value: "operation failed",
55 }
56 }
57}
58
59#[derive(Debug, Clone)]
65pub struct BondSlave {
66 pub name: String,
68 pub mac_address: [u8; 6],
70 pub link_up: bool,
72 pub is_active: bool,
74 pub tx_packets: u64,
76 pub rx_packets: u64,
78 pub last_arp_reply_ms: u64,
80}
81
82impl BondSlave {
83 pub fn new(name: &str, mac_address: [u8; 6]) -> Self {
85 Self {
86 name: String::from(name),
87 mac_address,
88 link_up: true,
89 is_active: false,
90 tx_packets: 0,
91 rx_packets: 0,
92 last_arp_reply_ms: 0,
93 }
94 }
95}
96
97#[derive(Debug, Clone)]
103pub struct ArpMonitor {
104 pub interval_ms: u64,
106 pub targets: Vec<[u8; 4]>,
108 pub last_check: u64,
110}
111
112impl ArpMonitor {
113 pub fn new(interval_ms: u64) -> Self {
115 Self {
116 interval_ms,
117 targets: Vec::new(),
118 last_check: 0,
119 }
120 }
121
122 pub fn add_target(&mut self, ip: [u8; 4]) {
124 if !self.targets.contains(&ip) {
125 self.targets.push(ip);
126 }
127 }
128
129 pub fn tick(&mut self, now: u64) -> bool {
134 if self.interval_ms == 0 {
135 return false;
136 }
137 if now.saturating_sub(self.last_check) >= self.interval_ms {
138 self.last_check = now;
139 true
140 } else {
141 false
142 }
143 }
144}
145
146#[derive(Debug, Clone)]
152pub struct BondInterface {
153 pub name: String,
155 pub mode: BondMode,
157 pub slaves: Vec<BondSlave>,
159 pub mac_address: [u8; 6],
161 pub active_slave_index: usize,
163 pub rr_counter: usize,
165 pub arp_monitor: ArpMonitor,
167}
168
169impl BondInterface {
170 pub fn new(name: &str, mode: BondMode) -> Self {
172 Self {
173 name: String::from(name),
174 mode,
175 slaves: Vec::new(),
176 mac_address: [0u8; 6],
177 active_slave_index: 0,
178 rr_counter: 0,
179 arp_monitor: ArpMonitor::new(0),
180 }
181 }
182
183 pub fn add_slave(&mut self, slave_name: &str, mac: [u8; 6]) -> Result<(), BondError> {
185 if self.slaves.iter().any(|s| s.name == slave_name) {
187 return Err(BondError::SlaveAlreadyExists);
188 }
189
190 let mut slave = BondSlave::new(slave_name, mac);
191
192 if self.slaves.is_empty() {
194 self.mac_address = mac;
195 }
196
197 if self.mode == BondMode::ActiveBackup && !self.has_active_slave() && slave.link_up {
199 slave.is_active = true;
200 self.active_slave_index = self.slaves.len();
201 }
202
203 if self.mode == BondMode::RoundRobin {
205 slave.is_active = true;
206 }
207
208 self.slaves.push(slave);
209 Ok(())
210 }
211
212 pub fn remove_slave(&mut self, slave_name: &str) -> Result<(), BondError> {
214 let idx = self
215 .slaves
216 .iter()
217 .position(|s| s.name == slave_name)
218 .ok_or(BondError::SlaveNotFound)?;
219
220 let was_active = self.slaves[idx].is_active;
221 self.slaves.remove(idx);
222
223 if self.active_slave_index >= self.slaves.len() && !self.slaves.is_empty() {
225 self.active_slave_index = 0;
226 }
227
228 if was_active && self.mode == BondMode::ActiveBackup {
230 self.promote_next_slave();
231 }
232
233 Ok(())
234 }
235
236 pub fn select_tx_slave(&mut self) -> Option<usize> {
238 if self.slaves.is_empty() {
239 return None;
240 }
241
242 match self.mode {
243 BondMode::ActiveBackup => {
244 if self.active_slave_index < self.slaves.len()
246 && self.slaves[self.active_slave_index].link_up
247 {
248 Some(self.active_slave_index)
249 } else {
250 None
251 }
252 }
253 BondMode::RoundRobin => {
254 let count = self.slaves.len();
256 for _ in 0..count {
257 let idx = self.rr_counter % count;
258 self.rr_counter = self.rr_counter.wrapping_add(1);
259 if self.slaves[idx].link_up {
260 return Some(idx);
261 }
262 }
263 None
264 }
265 }
266 }
267
268 pub fn handle_link_change(&mut self, slave_name: &str, link_up: bool) {
270 let Some(idx) = self.slaves.iter().position(|s| s.name == slave_name) else {
271 return;
272 };
273
274 self.slaves[idx].link_up = link_up;
275
276 match self.mode {
277 BondMode::ActiveBackup => {
278 if !link_up && self.slaves[idx].is_active {
279 self.slaves[idx].is_active = false;
281 self.promote_next_slave();
282 } else if link_up && !self.has_active_slave() {
283 self.slaves[idx].is_active = true;
285 self.active_slave_index = idx;
286 }
287 }
288 BondMode::RoundRobin => {
289 self.slaves[idx].is_active = link_up;
291 }
292 }
293 }
294
295 fn has_active_slave(&self) -> bool {
297 self.slaves.iter().any(|s| s.is_active)
298 }
299
300 fn promote_next_slave(&mut self) {
302 for (i, slave) in self.slaves.iter_mut().enumerate() {
303 if slave.link_up {
304 slave.is_active = true;
305 self.active_slave_index = i;
306 return;
307 }
308 }
309 }
311
312 pub fn link_up_count(&self) -> usize {
314 self.slaves.iter().filter(|s| s.link_up).count()
315 }
316}
317
318#[derive(Default)]
324pub struct BondManager {
325 pub bonds: BTreeMap<String, BondInterface>,
327}
328
329impl BondManager {
330 pub fn new() -> Self {
332 Self::default()
333 }
334}
335
336static BOND_MANAGER: GlobalState<RwLock<BondManager>> = GlobalState::new();
338
339pub fn init() -> Result<(), KernelError> {
341 BOND_MANAGER
342 .init(RwLock::new(BondManager::new()))
343 .map_err(|_| KernelError::AlreadyExists {
344 resource: "bond_manager",
345 id: 0,
346 })?;
347 Ok(())
348}
349
350pub fn create_bond(name: &str, mode: BondMode) -> Result<(), BondError> {
352 BOND_MANAGER
353 .with(|lock| {
354 let mut mgr = lock.write();
355 if mgr.bonds.contains_key(name) {
356 return Err(BondError::BondAlreadyExists);
357 }
358 mgr.bonds
359 .insert(String::from(name), BondInterface::new(name, mode));
360 Ok(())
361 })
362 .unwrap_or(Err(BondError::NotInitialized))
363}
364
365pub fn add_slave(bond_name: &str, slave_name: &str, mac: [u8; 6]) -> Result<(), BondError> {
367 BOND_MANAGER
368 .with(|lock| {
369 let mut mgr = lock.write();
370 let bond = mgr
371 .bonds
372 .get_mut(bond_name)
373 .ok_or(BondError::BondNotFound)?;
374 bond.add_slave(slave_name, mac)
375 })
376 .unwrap_or(Err(BondError::NotInitialized))
377}
378
379pub fn remove_slave(bond_name: &str, slave_name: &str) -> Result<(), BondError> {
381 BOND_MANAGER
382 .with(|lock| {
383 let mut mgr = lock.write();
384 let bond = mgr
385 .bonds
386 .get_mut(bond_name)
387 .ok_or(BondError::BondNotFound)?;
388 bond.remove_slave(slave_name)
389 })
390 .unwrap_or(Err(BondError::NotInitialized))
391}
392
393pub fn select_tx_slave(bond_name: &str) -> Option<usize> {
395 BOND_MANAGER
396 .with(|lock| {
397 let mut mgr = lock.write();
398 let bond = mgr.bonds.get_mut(bond_name)?;
399 bond.select_tx_slave()
400 })
401 .flatten()
402}
403
404pub fn handle_link_change(slave_name: &str, link_up: bool) {
406 BOND_MANAGER.with(|lock| {
407 let mut mgr = lock.write();
408 for bond in mgr.bonds.values_mut() {
409 bond.handle_link_change(slave_name, link_up);
410 }
411 });
412}
413
414#[cfg(test)]
419mod tests {
420 use super::*;
421
422 fn make_mac(last: u8) -> [u8; 6] {
423 [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, last]
424 }
425
426 #[test]
429 fn test_create_bond_interface() {
430 let bond = BondInterface::new("bond0", BondMode::ActiveBackup);
431 assert_eq!(bond.name, "bond0");
432 assert_eq!(bond.mode, BondMode::ActiveBackup);
433 assert!(bond.slaves.is_empty());
434 assert_eq!(bond.mac_address, [0u8; 6]);
435 }
436
437 #[test]
438 fn test_add_slave_sets_mac() {
439 let mut bond = BondInterface::new("bond0", BondMode::ActiveBackup);
440 let mac = make_mac(0x01);
441 bond.add_slave("eth0", mac).unwrap();
442
443 assert_eq!(bond.slaves.len(), 1);
444 assert_eq!(bond.mac_address, mac);
445 assert!(bond.slaves[0].is_active); }
447
448 #[test]
449 fn test_add_duplicate_slave_fails() {
450 let mut bond = BondInterface::new("bond0", BondMode::RoundRobin);
451 bond.add_slave("eth0", make_mac(0x01)).unwrap();
452 let result = bond.add_slave("eth0", make_mac(0x02));
453 assert_eq!(result, Err(BondError::SlaveAlreadyExists));
454 }
455
456 #[test]
457 fn test_remove_slave() {
458 let mut bond = BondInterface::new("bond0", BondMode::RoundRobin);
459 bond.add_slave("eth0", make_mac(0x01)).unwrap();
460 bond.add_slave("eth1", make_mac(0x02)).unwrap();
461 assert_eq!(bond.slaves.len(), 2);
462
463 bond.remove_slave("eth0").unwrap();
464 assert_eq!(bond.slaves.len(), 1);
465 assert_eq!(bond.slaves[0].name, "eth1");
466 }
467
468 #[test]
469 fn test_remove_nonexistent_slave_fails() {
470 let mut bond = BondInterface::new("bond0", BondMode::ActiveBackup);
471 let result = bond.remove_slave("eth99");
472 assert_eq!(result, Err(BondError::SlaveNotFound));
473 }
474
475 #[test]
476 fn test_active_backup_failover() {
477 let mut bond = BondInterface::new("bond0", BondMode::ActiveBackup);
478 bond.add_slave("eth0", make_mac(0x01)).unwrap();
479 bond.add_slave("eth1", make_mac(0x02)).unwrap();
480
481 assert!(bond.slaves[0].is_active);
483 assert!(!bond.slaves[1].is_active);
484 assert_eq!(bond.active_slave_index, 0);
485
486 bond.handle_link_change("eth0", false);
488
489 assert!(!bond.slaves[0].is_active);
491 assert!(bond.slaves[1].is_active);
492 assert_eq!(bond.active_slave_index, 1);
493 }
494
495 #[test]
496 fn test_active_backup_select_tx() {
497 let mut bond = BondInterface::new("bond0", BondMode::ActiveBackup);
498 bond.add_slave("eth0", make_mac(0x01)).unwrap();
499 bond.add_slave("eth1", make_mac(0x02)).unwrap();
500
501 assert_eq!(bond.select_tx_slave(), Some(0));
502
503 bond.handle_link_change("eth0", false);
505 assert_eq!(bond.select_tx_slave(), Some(1));
506
507 bond.handle_link_change("eth1", false);
509 assert_eq!(bond.select_tx_slave(), None);
510 }
511
512 #[test]
513 fn test_round_robin_selection() {
514 let mut bond = BondInterface::new("bond0", BondMode::RoundRobin);
515 bond.add_slave("eth0", make_mac(0x01)).unwrap();
516 bond.add_slave("eth1", make_mac(0x02)).unwrap();
517 bond.add_slave("eth2", make_mac(0x03)).unwrap();
518
519 assert_eq!(bond.select_tx_slave(), Some(0));
521 assert_eq!(bond.select_tx_slave(), Some(1));
522 assert_eq!(bond.select_tx_slave(), Some(2));
523 assert_eq!(bond.select_tx_slave(), Some(0));
524 }
525
526 #[test]
527 fn test_round_robin_skips_down_slave() {
528 let mut bond = BondInterface::new("bond0", BondMode::RoundRobin);
529 bond.add_slave("eth0", make_mac(0x01)).unwrap();
530 bond.add_slave("eth1", make_mac(0x02)).unwrap();
531 bond.add_slave("eth2", make_mac(0x03)).unwrap();
532
533 bond.handle_link_change("eth1", false);
535
536 assert_eq!(bond.select_tx_slave(), Some(0));
538 assert_eq!(bond.select_tx_slave(), Some(2));
539 assert_eq!(bond.select_tx_slave(), Some(0));
540 }
541
542 #[test]
543 fn test_link_up_count() {
544 let mut bond = BondInterface::new("bond0", BondMode::RoundRobin);
545 bond.add_slave("eth0", make_mac(0x01)).unwrap();
546 bond.add_slave("eth1", make_mac(0x02)).unwrap();
547 assert_eq!(bond.link_up_count(), 2);
548
549 bond.handle_link_change("eth0", false);
550 assert_eq!(bond.link_up_count(), 1);
551 }
552
553 #[test]
554 fn test_arp_monitor_tick() {
555 let mut mon = ArpMonitor::new(1000);
556 mon.add_target([192, 168, 1, 1]);
557
558 assert!(!mon.tick(0));
561 assert!(!mon.tick(500));
562 assert!(mon.tick(1000));
563 assert!(!mon.tick(1500));
565 assert!(mon.tick(2000));
566 }
567
568 #[test]
569 fn test_arp_monitor_zero_interval() {
570 let mut mon = ArpMonitor::new(0);
571 assert!(!mon.tick(0));
573 assert!(!mon.tick(1000));
574 }
575
576 #[test]
577 fn test_arp_monitor_no_duplicate_targets() {
578 let mut mon = ArpMonitor::new(1000);
579 mon.add_target([10, 0, 0, 1]);
580 mon.add_target([10, 0, 0, 1]);
581 assert_eq!(mon.targets.len(), 1);
582 }
583
584 #[test]
585 fn test_remove_active_slave_promotes_next() {
586 let mut bond = BondInterface::new("bond0", BondMode::ActiveBackup);
587 bond.add_slave("eth0", make_mac(0x01)).unwrap();
588 bond.add_slave("eth1", make_mac(0x02)).unwrap();
589
590 assert!(bond.slaves[0].is_active);
592
593 bond.remove_slave("eth0").unwrap();
595 assert_eq!(bond.slaves.len(), 1);
596 assert_eq!(bond.slaves[0].name, "eth1");
597 assert!(bond.slaves[0].is_active);
598 }
599
600 #[test]
601 fn test_all_slaves_down_then_recovery() {
602 let mut bond = BondInterface::new("bond0", BondMode::ActiveBackup);
603 bond.add_slave("eth0", make_mac(0x01)).unwrap();
604 bond.add_slave("eth1", make_mac(0x02)).unwrap();
605
606 bond.handle_link_change("eth0", false);
608 bond.handle_link_change("eth1", false);
609 assert_eq!(bond.select_tx_slave(), None);
610 assert!(!bond.has_active_slave());
611
612 bond.handle_link_change("eth1", true);
614 assert!(bond.slaves[1].is_active);
615 assert_eq!(bond.select_tx_slave(), Some(1));
616 }
617}