Files
RedBear-OS/netstack/src/link/mod.rs
T
Red Bear OS c899b42d36 arp: static ARP entry add/delete via netcfg (ip neigh add/del)
LinkDevice trait gains:
- add_static_neighbor(ip, mac) → Result<(), String>
- remove_neighbor(ip) → bool (true if removed)

EthernetLink implements both with permanent entries (expires_at = i64::MAX/2).

netcfg/ifaces/eth0/arp/ is now:
  list      → read (existing)
  stats     → read (existing)
  flush     → write (existing)
  add       → write  NEW
  del       → write  NEW

Usage:
  echo '192.168.1.1 00:11:22:33:44:55' > /scheme/netcfg/ifaces/eth0/arp/add
  echo '192.168.1.1' > /scheme/netcfg/ifaces/eth0/arp/del
  echo '10.0.0.0/24 gateway-01 11:22:33:44:55:66' > /scheme/netcfg/ifaces/eth0/arp/add

Mirrors Linux 'ip neigh add' / 'ip neigh del'.
MAC format accepts both colon (00:11:22:33:44:55) and dash (00-11-22-33-44-55).
Unicast MAC validation rejects broadcast/multicast addresses.
2026-07-08 18:49:40 +03:00

133 lines
3.3 KiB
Rust

pub mod bond;
pub mod bridge;
pub mod ethernet;
pub mod gre;
pub mod ipip;
pub mod loopback;
pub mod qdisc;
pub mod stp;
pub mod tun;
pub mod vlan;
pub mod vxlan;
use std::rc::Rc;
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
/// Represent a link layer device (eth0, loopback...)
pub trait LinkDevice {
/// Send the given packet to the machine with the `next_hop` ip address
/// This method cannot fail so it's the implementor responsability
/// to buffer packets which can't be sent immediatly or decide to
/// drop them if necessary
fn send(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant);
/// Returns None if nothing is received.
/// Returns an Ip packet otherwise
fn recv(&mut self, now: Instant) -> Option<&[u8]>;
/// Returns the LinkDevice display name used to refer to it and for lookups
fn name(&self) -> &Rc<str>;
/// Returns wether this device have packets pending
fn can_recv(&self) -> bool;
fn mac_address(&self) -> Option<EthernetAddress>;
fn set_mac_address(&mut self, addr: EthernetAddress);
fn ip_address(&self) -> Option<IpCidr>;
fn set_ip_address(&mut self, addr: IpCidr);
fn arp_table(&self) -> String {
String::new()
}
fn flush_arp(&mut self) {}
fn arp_stats(&self) -> String {
String::new()
}
fn qdisc_info(&self) -> String {
"none\n".to_string()
}
fn set_qdisc(&mut self, _kind: &str) -> Result<(), String> {
Err("qdisc configuration not supported on this device\n".to_string())
}
fn add_static_neighbor(&mut self, _ip: IpAddress, _mac: [u8; 6]) -> Result<(), String> {
Err("static ARP entry not supported on this device\n".to_string())
}
fn remove_neighbor(&mut self, _ip: IpAddress) -> bool {
false
}
fn mtu(&self) -> usize {
1500
}
fn set_mtu(&mut self, _mtu: usize) {}
fn is_enabled(&self) -> bool {
true
}
fn set_enabled(&mut self, _enabled: bool) {}
fn link_state(&self) -> &'static str {
"unknown"
}
fn statistics(&self) -> Stats {
Stats::default()
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Stats {
pub rx_bytes: u64,
pub rx_packets: u64,
pub tx_bytes: u64,
pub tx_packets: u64,
pub rx_errors: u64,
pub tx_errors: u64,
pub rx_dropped: u64,
pub tx_dropped: u64,
}
#[derive(Default)]
pub struct DeviceList {
inner: Vec<Box<dyn LinkDevice>>,
}
impl DeviceList {
pub fn push<T: LinkDevice + 'static>(&mut self, dev: T) {
self.inner.push(Box::new(dev))
}
pub fn get(&self, device_name: &str) -> Option<&dyn LinkDevice> {
self.inner
.iter()
.find(|dev| dev.name().as_ref() == device_name)
.map(|device| device.as_ref())
}
pub fn get_mut(&mut self, device_name: &str) -> Option<&mut (dyn LinkDevice + 'static)> {
self.inner
.iter_mut()
.find(|dev| dev.name().as_ref() == device_name)
.map(|device| device.as_mut())
}
pub fn iter(&self) -> impl Iterator<Item = &(dyn LinkDevice + 'static)> {
self.inner.iter().map(|b| b.as_ref())
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (dyn LinkDevice + 'static)> {
self.inner.iter_mut().map(|b| b.as_mut())
}
}