feat: wip! wip! gateway
This commit is contained in:
parent
12639556a2
commit
4ffe38221b
5 changed files with 79 additions and 43 deletions
|
@ -4,6 +4,6 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "net"] }
|
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "net", "sync"] }
|
||||||
pnet = "0.35.0"
|
pnet = "0.35.0"
|
||||||
ipnet = { version = "2", features = ["serde"] }
|
ipnet = { version = "2", features = ["serde"] }
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
use core::net::{IpAddr, SocketAddr};
|
use core::net::{IpAddr, SocketAddr};
|
||||||
use ipnet::IpNet;
|
use ipnet::IpNet;
|
||||||
use pnet::util::MacAddr;
|
use pnet::util::MacAddr;
|
||||||
use std::{collections::HashMap, sync::Mutex};
|
|
||||||
use tokio::net::UdpSocket;
|
use tokio::net::UdpSocket;
|
||||||
|
|
||||||
|
use crate::redirect::MacTable;
|
||||||
|
|
||||||
pub struct Env {
|
pub struct Env {
|
||||||
pub socket: UdpSocket,
|
pub socket: UdpSocket,
|
||||||
pub vteps: Vec<IpAddr>,
|
pub used_vnis: (u32, u32),
|
||||||
pub mac_table: Mutex<HashMap<(MacAddr, u32), IpAddr>>,
|
|
||||||
// pub local_nets: Vec<IpNet>,
|
// pub local_nets: Vec<IpNet>,
|
||||||
pub reserved_nets: Vec<IpNet>,
|
pub reserved_nets: Vec<IpNet>,
|
||||||
// pub gateway: IpAddr,
|
// pub gateway: IpAddr,
|
||||||
pub gateway_mac: MacAddr,
|
pub gateway_mac: MacAddr,
|
||||||
pub gateway_vtep: IpAddr,
|
pub gateway_vtep: IpAddr,
|
||||||
pub gateway_vni: u32,
|
pub gateway_vni: u32,
|
||||||
|
|
||||||
|
pub mac_table: MacTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Env {
|
impl Env {
|
||||||
|
|
23
src/main.rs
23
src/main.rs
|
@ -1,12 +1,7 @@
|
||||||
use core::net::{IpAddr, SocketAddr};
|
use core::net::{IpAddr, SocketAddr};
|
||||||
use ipnet::IpNet;
|
use ipnet::IpNet;
|
||||||
use pnet::util::MacAddr;
|
use pnet::util::MacAddr;
|
||||||
use std::{
|
use std::{io, str::FromStr, sync::Arc};
|
||||||
collections::HashMap,
|
|
||||||
io,
|
|
||||||
str::FromStr,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
use tokio::{net::UdpSocket, select, task};
|
use tokio::{net::UdpSocket, select, task};
|
||||||
|
|
||||||
mod global;
|
mod global;
|
||||||
|
@ -24,16 +19,21 @@ fn net_parsing(net: &str) -> IpNet {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn treat_packet(env: Arc<Env>, raw: Vec<u8>, src_vtep: SocketAddr) -> Option<()> {
|
async fn treat_packet(env: Arc<Env>, raw: Vec<u8>, src_vtep: SocketAddr) -> Option<()> {
|
||||||
|
use packet::Action::*;
|
||||||
let reading = packet::read_packet(&env, raw.as_slice(), src_vtep)?;
|
let reading = packet::read_packet(&env, raw.as_slice(), src_vtep)?;
|
||||||
redirect::send(env, reading, raw).await;
|
match reading.action {
|
||||||
Some(())
|
InternalTransmit(dst) => redirect::send(env, reading, dst, raw).await,
|
||||||
|
InternalBroadcast => redirect::broadcast(env, reading, raw).await,
|
||||||
|
GatewayArp => todo!(),
|
||||||
|
GatewayTransmit => todo!(),
|
||||||
|
GatewayEnter => todo!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> io::Result<()> {
|
async fn main() -> io::Result<()> {
|
||||||
// TODO: config file
|
// TODO: config file
|
||||||
let addr = "10.0.0.1";
|
let addr = "10.0.0.1";
|
||||||
let vteps = ["10.0.0.2", "10.0.0.3"]; // will probably end in vtep learning
|
|
||||||
let reserved_nets = [
|
let reserved_nets = [
|
||||||
"10.0.0.0/27",
|
"10.0.0.0/27",
|
||||||
"10.0.253.0/24",
|
"10.0.253.0/24",
|
||||||
|
@ -45,11 +45,12 @@ async fn main() -> io::Result<()> {
|
||||||
// let gateway = "10.0.0.5";
|
// let gateway = "10.0.0.5";
|
||||||
let gateway_vtep = "10.0.0.5";
|
let gateway_vtep = "10.0.0.5";
|
||||||
let gateway_vni = 2;
|
let gateway_vni = 2;
|
||||||
|
let vni_range: (u32, u32) = (1000, 10000);
|
||||||
|
|
||||||
let env = Arc::new(Env {
|
let env = Arc::new(Env {
|
||||||
socket: UdpSocket::bind(SocketAddr::new(addr_parsing(addr), 4789)).await?,
|
socket: UdpSocket::bind(SocketAddr::new(addr_parsing(addr), 4789)).await?,
|
||||||
vteps: vteps.into_iter().map(addr_parsing).collect(),
|
|
||||||
// local_nets: local_nets.into_iter().map(net_parsing).collect(),
|
// local_nets: local_nets.into_iter().map(net_parsing).collect(),
|
||||||
|
used_vnis: vni_range,
|
||||||
reserved_nets: reserved_nets.into_iter().map(net_parsing).collect(),
|
reserved_nets: reserved_nets.into_iter().map(net_parsing).collect(),
|
||||||
// gateway: addr_parsing(gateway),
|
// gateway: addr_parsing(gateway),
|
||||||
gateway_vtep: addr_parsing(gateway_vtep),
|
gateway_vtep: addr_parsing(gateway_vtep),
|
||||||
|
@ -57,7 +58,7 @@ async fn main() -> io::Result<()> {
|
||||||
gateway_mac: MacAddr::from_str(gateway_mac)
|
gateway_mac: MacAddr::from_str(gateway_mac)
|
||||||
.unwrap_or_else(|e| panic!("Error parsing {:?}: {}", gateway_mac, e)),
|
.unwrap_or_else(|e| panic!("Error parsing {:?}: {}", gateway_mac, e)),
|
||||||
|
|
||||||
mac_table: Mutex::new(HashMap::new()),
|
mac_table: redirect::new_mactable(vni_range),
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut buf = vec![0u8; 2000];
|
let mut buf = vec![0u8; 2000];
|
||||||
|
|
|
@ -17,7 +17,7 @@ use crate::redirect::mac_learn;
|
||||||
/// Action to be taken for a packet
|
/// Action to be taken for a packet
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
/// Transmit for the non-broadcast destination MAC
|
/// Transmit for the non-broadcast destination MAC
|
||||||
InternalTransmit,
|
InternalTransmit(MacAddr),
|
||||||
|
|
||||||
/// Transmit to all known device on this VNI
|
/// Transmit to all known device on this VNI
|
||||||
InternalBroadcast,
|
InternalBroadcast,
|
||||||
|
@ -36,8 +36,6 @@ pub enum Action {
|
||||||
pub struct PacketReading {
|
pub struct PacketReading {
|
||||||
pub vtep: IpAddr,
|
pub vtep: IpAddr,
|
||||||
pub vni: u32,
|
pub vni: u32,
|
||||||
pub src: MacAddr,
|
|
||||||
pub dst: MacAddr,
|
|
||||||
pub action: Action,
|
pub action: Action,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,11 +45,16 @@ pub fn read_packet(env: &Arc<Env>, payload: &[u8], src: SocketAddr) -> Option<Pa
|
||||||
let vxlan = VxlanPacket::new(payload)?;
|
let vxlan = VxlanPacket::new(payload)?;
|
||||||
let eth = EthernetPacket::new(vxlan.payload())?;
|
let eth = EthernetPacket::new(vxlan.payload())?;
|
||||||
|
|
||||||
let mut action = InternalTransmit;
|
let mut action = InternalTransmit(eth.get_destination());
|
||||||
if vxlan.get_vni() == env.gateway_vni {
|
if vxlan.get_vni() == env.gateway_vni {
|
||||||
action = GatewayEnter;
|
action = GatewayEnter;
|
||||||
} else {
|
} else {
|
||||||
mac_learn(env, eth.get_source(), vxlan.get_vni(), src.ip());
|
tokio::spawn(mac_learn(
|
||||||
|
env.clone(),
|
||||||
|
eth.get_source(),
|
||||||
|
vxlan.get_vni(),
|
||||||
|
src.ip(),
|
||||||
|
));
|
||||||
if eth.get_destination().is_broadcast() {
|
if eth.get_destination().is_broadcast() {
|
||||||
action = InternalBroadcast;
|
action = InternalBroadcast;
|
||||||
if eth.get_ethertype() == EtherTypes::Arp {
|
if eth.get_ethertype() == EtherTypes::Arp {
|
||||||
|
@ -78,8 +81,6 @@ pub fn read_packet(env: &Arc<Env>, payload: &[u8], src: SocketAddr) -> Option<Pa
|
||||||
Some(PacketReading {
|
Some(PacketReading {
|
||||||
vtep: src.ip(),
|
vtep: src.ip(),
|
||||||
vni: vxlan.get_vni(),
|
vni: vxlan.get_vni(),
|
||||||
src: eth.get_source(),
|
|
||||||
dst: eth.get_destination(),
|
|
||||||
action,
|
action,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,57 @@
|
||||||
use crate::global::Env;
|
|
||||||
use pnet::util::MacAddr;
|
use pnet::util::MacAddr;
|
||||||
use std::{net::IpAddr, sync::Arc};
|
use std::{collections::HashMap, net::IpAddr, sync::Arc};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
use crate::global::Env;
|
||||||
use crate::packet::PacketReading;
|
use crate::packet::PacketReading;
|
||||||
|
|
||||||
pub fn mac_learn(env: &Arc<Env>, mac: MacAddr, vni: u32, vtep: IpAddr) {
|
pub type MacTable = Vec<RwLock<HashMap<MacAddr, IpAddr>>>;
|
||||||
env.mac_table.lock().unwrap().insert((mac, vni), vtep);
|
|
||||||
|
pub fn new_mactable(vni_range: (u32, u32)) -> MacTable {
|
||||||
|
let mut mac_table = Vec::new();
|
||||||
|
mac_table.resize_with((vni_range.1 - vni_range.0) as usize, || {
|
||||||
|
RwLock::new(HashMap::new())
|
||||||
|
});
|
||||||
|
mac_table
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(env: Arc<Env>, reading: PacketReading, packet: Vec<u8>) {
|
fn mac_table(env: &Arc<Env>, vni: u32) -> Option<&RwLock<HashMap<MacAddr, IpAddr>>> {
|
||||||
let svtep = env
|
if env.used_vnis.0 <= vni && vni < env.used_vnis.1 {
|
||||||
.mac_table
|
Some(&env.mac_table[(vni - env.used_vnis.0) as usize])
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.get(&(reading.dst, reading.vni))
|
|
||||||
.copied();
|
|
||||||
if let Some(vtep) = svtep {
|
|
||||||
env.send(&packet, vtep.clone()).await;
|
|
||||||
} else {
|
} else {
|
||||||
for vtep in env.vteps.iter().filter(|vtep| **vtep != reading.vtep) {
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn mac_learn(env: Arc<Env>, mac: MacAddr, vni: u32, vtep: IpAddr) {
|
||||||
|
if let Some(vni_table) = mac_table(&env, vni) {
|
||||||
|
// avoid taking a write lock when unnecessary
|
||||||
|
if vni_table.read().await.get(&mac) != Some(&vtep) {
|
||||||
|
vni_table.write().await.insert(mac, vtep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send(
|
||||||
|
env: Arc<Env>,
|
||||||
|
reading: PacketReading,
|
||||||
|
dst: MacAddr,
|
||||||
|
packet: Vec<u8>,
|
||||||
|
) -> Option<()> {
|
||||||
|
let vtep = *mac_table(&env, reading.vni)?.read().await.get(&dst)?;
|
||||||
|
if vtep != reading.vtep {
|
||||||
|
env.send(&packet, vtep).await;
|
||||||
|
}
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn broadcast(env: Arc<Env>, reading: PacketReading, packet: Vec<u8>) -> Option<()> {
|
||||||
|
let table = mac_table(&env, reading.vni)?.read().await;
|
||||||
|
for vtep in table.values() {
|
||||||
|
if *vtep != reading.vtep {
|
||||||
|
// TODO: parallele send
|
||||||
env.send(&packet, *vtep).await;
|
env.send(&packet, *vtep).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue