421 lines
12 KiB
Rust
Executable File
421 lines
12 KiB
Rust
Executable File
#![allow(unused)]
|
|
|
|
use std::{
|
|
collections::HashMap,
|
|
sync::atomic::{AtomicU8, Ordering},
|
|
time::Duration,
|
|
};
|
|
|
|
use tracing::error;
|
|
|
|
use once_cell::sync::OnceCell;
|
|
use sdlan_sn_rs::utils::{BROADCAST_MAC, MULTICAST_MAC, Mac, ip_to_string, net_bit_len_to_mask};
|
|
use tokio::sync::{
|
|
mpsc::{channel, Receiver, Sender},
|
|
oneshot,
|
|
};
|
|
|
|
use super::{get_edge, get_route_table, init_arp_wait_list, init_route};
|
|
|
|
static GLOBAL_ARP: OnceCell<ArpActor> = OnceCell::new();
|
|
pub fn init_arp() {
|
|
init_route();
|
|
init_arp_wait_list();
|
|
let actor = ArpActor::new();
|
|
GLOBAL_ARP.set(actor).unwrap();
|
|
}
|
|
|
|
pub fn get_arp() -> &'static ArpActor {
|
|
GLOBAL_ARP.get().unwrap()
|
|
}
|
|
|
|
const ETHER_TYPE_ARP: u16 = 0x0806;
|
|
const ETHER_TYPE_IP: u16 = 0x0800;
|
|
#[allow(unused)]
|
|
const ETHER_TYPE_IP6: u16 = 0x86dd;
|
|
|
|
const ARP_MAX_AGE: u8 = 128;
|
|
|
|
const ARP_HWTYPE_ETH: u16 = 1;
|
|
pub const ARP_REQUEST: u16 = 1;
|
|
pub const ARP_REPLY: u16 = 2;
|
|
|
|
#[repr(C)]
|
|
pub struct EthHdr {
|
|
pub dest: [u8; 6],
|
|
pub src: [u8; 6],
|
|
pub eth_type: u16,
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct ArpHdr {
|
|
pub ethhdr: EthHdr,
|
|
pub hwtype: u16,
|
|
pub protocol: u16,
|
|
pub hwlen: u8,
|
|
pub protolen: u8,
|
|
pub opcode: u16,
|
|
pub shwaddr: [u8; 6],
|
|
pub sipaddr: [u16; 2],
|
|
pub dhwaddr: [u8; 6],
|
|
pub dipaddr: [u16; 2],
|
|
}
|
|
|
|
impl ArpHdr {
|
|
pub fn from_slice(data: &[u8]) -> Self {
|
|
if data.len() < 42 {
|
|
panic!("data size error");
|
|
}
|
|
Self {
|
|
ethhdr: EthHdr {
|
|
dest: data[0..6].try_into().unwrap(),
|
|
src: data[6..12].try_into().unwrap(),
|
|
eth_type: u16::from_be_bytes(data[12..14].try_into().unwrap()),
|
|
},
|
|
hwtype: u16::from_be_bytes(data[14..16].try_into().unwrap()),
|
|
protocol: u16::from_be_bytes(data[16..18].try_into().unwrap()),
|
|
hwlen: data[18],
|
|
protolen: data[19],
|
|
opcode: u16::from_be_bytes(data[20..22].try_into().unwrap()),
|
|
shwaddr: data[22..28].try_into().unwrap(),
|
|
sipaddr: [
|
|
u16::from_be_bytes(data[28..30].try_into().unwrap()),
|
|
u16::from_be_bytes(data[30..32].try_into().unwrap()),
|
|
],
|
|
dhwaddr: data[32..38].try_into().unwrap(),
|
|
dipaddr: [
|
|
u16::from_be_bytes(data[38..40].try_into().unwrap()),
|
|
u16::from_be_bytes(data[40..42].try_into().unwrap()),
|
|
],
|
|
}
|
|
}
|
|
|
|
pub fn marshal_to_bytes(&self) -> Vec<u8> {
|
|
let mut result = Vec::with_capacity(64);
|
|
result.extend_from_slice(&self.ethhdr.dest);
|
|
result.extend_from_slice(&self.ethhdr.src);
|
|
result.extend_from_slice(&self.ethhdr.eth_type.to_be_bytes());
|
|
result.extend_from_slice(&self.hwtype.to_be_bytes());
|
|
result.extend_from_slice(&self.protocol.to_be_bytes());
|
|
result.push(self.hwlen);
|
|
result.push(self.protolen);
|
|
result.extend_from_slice(&self.opcode.to_be_bytes());
|
|
result.extend_from_slice(&self.shwaddr);
|
|
result.extend_from_slice(&self.sipaddr[0].to_be_bytes());
|
|
result.extend_from_slice(&self.sipaddr[1].to_be_bytes());
|
|
|
|
result.extend_from_slice(&self.dhwaddr);
|
|
result.extend_from_slice(&self.dipaddr[0].to_be_bytes());
|
|
result.extend_from_slice(&self.dipaddr[1].to_be_bytes());
|
|
// result.extend_from_slice(&[0; 18]);
|
|
|
|
// let crc = CRC_HASH.checksum(&result).to_be_bytes();
|
|
// result.extend_from_slice(&crc);
|
|
|
|
result
|
|
}
|
|
|
|
pub fn new() -> Self {
|
|
Self {
|
|
ethhdr: EthHdr {
|
|
dest: [0; 6],
|
|
src: [0; 6],
|
|
eth_type: ETHER_TYPE_ARP,
|
|
},
|
|
hwtype: ARP_HWTYPE_ETH,
|
|
protocol: ETHER_TYPE_IP,
|
|
hwlen: 6,
|
|
protolen: 4,
|
|
opcode: ARP_REQUEST,
|
|
shwaddr: [0; 6],
|
|
sipaddr: [0; 2],
|
|
dhwaddr: [0; 6],
|
|
dipaddr: [0; 2],
|
|
}
|
|
}
|
|
}
|
|
|
|
// const ARP_TABLE_SIZE: usize = 8;
|
|
static ARPTIME: AtomicU8 = AtomicU8::new(0);
|
|
const BROADCAST_IPADDR: u32 = 0xffffffff;
|
|
|
|
#[derive(Debug)]
|
|
#[allow(unused)]
|
|
pub struct ArpEntry {
|
|
ip_addr: u32,
|
|
arptime: u8,
|
|
hw_addr: [u8; 6],
|
|
}
|
|
|
|
/*
|
|
impl ArpEntry {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
ip_addr: 0,
|
|
arptime: 0,
|
|
hw_addr: [0; 6],
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
pub struct ArpInfo {
|
|
// host_ip: AtomicU32,
|
|
// ip representation of mask
|
|
// host_netmask: AtomicU32,
|
|
entry: HashMap<u32, ArpEntry>,
|
|
// entry: [ArpEntry; ARP_TABLE_SIZE],
|
|
}
|
|
|
|
impl ArpInfo {
|
|
fn lookup_ip_mac(&self, ip: u32) -> Option<([u8; 6], u32, bool)> {
|
|
if ip == BROADCAST_IPADDR {
|
|
return Some((BROADCAST_MAC, ip, false));
|
|
}
|
|
let edge = get_edge();
|
|
let netbit = edge.device_config.get_net_bit();
|
|
let host_netmask = net_bit_len_to_mask(netbit);
|
|
let host_netmask_reverse = !host_netmask;
|
|
|
|
if (ip & host_netmask_reverse) == host_netmask_reverse {
|
|
return Some((BROADCAST_MAC, ip, false));
|
|
}
|
|
|
|
let first_ip = (ip >> 24) as u8 & 0xff;
|
|
if first_ip >= 224 && first_ip <= 239 {
|
|
let mut multi = MULTICAST_MAC;
|
|
multi[3] = (ip >> 16) as u8 & 0x7f;
|
|
multi[4] = (ip >> 8) as u8 & 0xff;
|
|
multi[5] = (ip) as u8 & 0xff;
|
|
return Some((multi, ip, false));
|
|
}
|
|
let mut target_ip = 0;
|
|
|
|
let host_ip = edge.device_config.get_ip();
|
|
|
|
if (ip & host_netmask) == (host_ip & host_netmask) {
|
|
target_ip = ip;
|
|
}
|
|
|
|
if target_ip == 0 {
|
|
let route_table = get_route_table();
|
|
if let Some(gateway_ip) = route_table.get_gateway_ip(ip) {
|
|
target_ip = gateway_ip;
|
|
}
|
|
}
|
|
if target_ip == 0 {
|
|
error!("target should not route to me: ip = {}", ip_to_string(&ip));
|
|
return None;
|
|
}
|
|
if let Some(entry) = self.entry.get(&target_ip) {
|
|
return Some((entry.hw_addr, target_ip, false));
|
|
}
|
|
/*
|
|
for i in 0..ARP_TABLE_SIZE {
|
|
let item = &self.entry[i];
|
|
if item.ip_addr == target_ip {
|
|
return (item.hw_addr, target_ip, false);
|
|
}
|
|
}
|
|
*/
|
|
return Some((BROADCAST_MAC, target_ip, true));
|
|
}
|
|
|
|
fn set_arp(&mut self, mac: [u8; 6], ip: u32) {
|
|
// println!("setting ip: {:?} is at {:?}", ip.to_be_bytes(), mac);
|
|
let nowage = ARPTIME.load(Ordering::Relaxed);
|
|
self.entry.insert(
|
|
ip,
|
|
ArpEntry {
|
|
ip_addr: ip,
|
|
hw_addr: mac,
|
|
arptime: nowage,
|
|
},
|
|
);
|
|
/*
|
|
for i in 0..ARP_TABLE_SIZE {
|
|
let item = &mut self.entry[i];
|
|
if item.ip_addr != 0 {
|
|
if item.ip_addr == ip {
|
|
item.hw_addr = mac;
|
|
let nowage = ARPTIME.load(Ordering::Relaxed);
|
|
item.arptime = nowage;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
/*
|
|
println!("set_arp 1");
|
|
|
|
let mut itemindex = ARP_TABLE_SIZE;
|
|
for i in 0..ARP_TABLE_SIZE {
|
|
let temp = &self.entry[i];
|
|
println!("set arp 2");
|
|
if temp.ip_addr == 0 {
|
|
itemindex = i;
|
|
println!("set arp 3: itemindex = {}", itemindex);
|
|
break;
|
|
}
|
|
}
|
|
let arptime = ARPTIME.load(Ordering::Relaxed);
|
|
if itemindex == ARP_TABLE_SIZE {
|
|
println!("set_arp 4");
|
|
let mut tmpage = 0;
|
|
let mut idx = 0;
|
|
for i in 0..ARP_TABLE_SIZE {
|
|
let temp = &self.entry[i];
|
|
if (arptime - temp.arptime) > tmpage {
|
|
tmpage = arptime - temp.arptime;
|
|
idx = i;
|
|
}
|
|
}
|
|
itemindex = idx;
|
|
}
|
|
println!("set_arp 5");
|
|
let temp = &mut self.entry[itemindex];
|
|
temp.arptime = arptime;
|
|
temp.ip_addr = ip;
|
|
temp.hw_addr = mac;
|
|
println!("set arp 6: idx={} => {:?}", itemindex, temp);
|
|
*/
|
|
}
|
|
|
|
fn timer(&mut self) {
|
|
let timer = ARPTIME.fetch_add(1, Ordering::Relaxed);
|
|
let mut todelete = vec![];
|
|
for (ip, entry) in &self.entry {
|
|
if (timer - entry.arptime) >= ARP_MAX_AGE {
|
|
todelete.push(*ip);
|
|
}
|
|
}
|
|
for ip in todelete {
|
|
self.entry.remove(&ip);
|
|
}
|
|
/*
|
|
for i in 0..ARP_TABLE_SIZE {
|
|
let item = &mut self.entry[i];
|
|
if item.ip_addr != 0 && (timer - item.arptime) >= ARP_MAX_AGE {
|
|
item.ip_addr = 0;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
pub enum ArpRequestInfo {
|
|
Lookup { ip: u32 },
|
|
Set { ip: u32, mac: Mac },
|
|
}
|
|
|
|
pub struct ArpRequest {
|
|
req: ArpRequestInfo,
|
|
tx: oneshot::Sender<ArpResponse>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
#[allow(unused)]
|
|
pub enum ArpResponse {
|
|
LookupResp {
|
|
mac: Mac,
|
|
ip: u32,
|
|
do_arp_request: bool,
|
|
},
|
|
SetResp {
|
|
ok: bool,
|
|
},
|
|
ArpRespError {
|
|
msg: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ArpActor {
|
|
pub tx: Sender<ArpRequest>,
|
|
}
|
|
|
|
impl ArpActor {
|
|
pub fn new() -> Self {
|
|
let (tx, rx) = channel(20);
|
|
tokio::spawn(loop_arp_info(rx));
|
|
Self { tx }
|
|
}
|
|
}
|
|
|
|
async fn loop_arp_info(mut rx: Receiver<ArpRequest>) {
|
|
let mut arp = ArpInfo {
|
|
// entry: array::from_fn(|_i| ArpEntry::new()),
|
|
entry: HashMap::new(),
|
|
};
|
|
loop {
|
|
tokio::select! {
|
|
data = rx.recv() => {
|
|
if let Some(d) = data {
|
|
match d.req {
|
|
ArpRequestInfo::Lookup{ip} => {
|
|
let Some(mac) = arp.lookup_ip_mac(ip) else {
|
|
continue;
|
|
};
|
|
if let Err(e) = d.tx.send(ArpResponse::LookupResp {
|
|
mac: mac.0, ip: mac.1, do_arp_request: mac.2 }
|
|
) {
|
|
error!("failed to send back arp lookup feedback: {:?}", e);
|
|
};
|
|
}
|
|
ArpRequestInfo::Set{ip, mac} => {
|
|
arp.set_arp(mac, ip);
|
|
if let Err(e) = d.tx.send(ArpResponse::SetResp { ok: true }) {
|
|
error!("failed to send back arp set feedback: {:?}", e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ = tokio::time::sleep(Duration::from_secs(10)) => {
|
|
arp.timer();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn send_arp_request(data: ArpRequestInfo) -> ArpResponse {
|
|
let (tx, rx) = oneshot::channel();
|
|
let arp = get_arp();
|
|
if let Err(e) = arp.tx.send(ArpRequest { tx, req: data }).await {
|
|
error!("failed to send arp request: {}", e);
|
|
return ArpResponse::ArpRespError {
|
|
msg: "failed to send arp request".to_owned(),
|
|
};
|
|
}
|
|
match rx.await {
|
|
Ok(res) => res,
|
|
Err(e) => ArpResponse::ArpRespError { msg: e.to_string() },
|
|
}
|
|
}
|
|
|
|
pub fn generate_arp_request(srcmac: [u8; 6], dstip: u32, srcip: u32) -> Vec<u8> {
|
|
let mut arphdr = ArpHdr::new();
|
|
arphdr.ethhdr.dest = [0xff; 6];
|
|
arphdr.dhwaddr = [0; 6];
|
|
arphdr.ethhdr.src = srcmac;
|
|
arphdr.shwaddr = srcmac;
|
|
|
|
arphdr.dipaddr = [(dstip >> 16) as u16, (dstip & 0xffff) as u16];
|
|
arphdr.sipaddr = [(srcip >> 16) as u16, (srcip & 0xffff) as u16];
|
|
|
|
/*
|
|
println!(
|
|
"arphdr.sipaddr: {:?}, {:?}",
|
|
arphdr.sipaddr[0].to_be_bytes(),
|
|
arphdr.sipaddr[1].to_be_bytes()
|
|
);
|
|
println!(
|
|
"arphdr.dipaddr: {:?}, {:?}",
|
|
arphdr.dipaddr[0].to_be_bytes(),
|
|
arphdr.dipaddr[1].to_be_bytes()
|
|
);
|
|
*/
|
|
|
|
arphdr.marshal_to_bytes()
|
|
}
|