feat: gateway
untested
This commit is contained in:
parent
4ffe38221b
commit
70b06cb6ef
6 changed files with 99 additions and 38 deletions
|
@ -5,7 +5,7 @@ To use vxlan in isp, we need to implement a VTEP for vault01.
|
|||
## Roadmap
|
||||
|
||||
1. [x] Basic router which forward to every connected VTEP -> bad idea without mac learning
|
||||
2. Special case for when we are the target (internet, DNS)
|
||||
2. [x] Special case for when we are the target (internet, DNS)
|
||||
3. [x] Mac learning
|
||||
4. DHCP, which removes user prefixes, (comes with ARP snooping ?)
|
||||
5. Reverse path checking, filter auto ip attribution
|
||||
|
|
26
src/complex_reply.rs
Normal file
26
src/complex_reply.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use pnet::packet::{
|
||||
arp::{ArpOperations, MutableArpPacket},
|
||||
ethernet::MutableEthernetPacket,
|
||||
vxlan::MutableVxlanPacket,
|
||||
MutablePacket,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{global::Env, packet::PacketReading};
|
||||
|
||||
pub async fn reply_arp(env: Arc<Env>, reading: PacketReading, mut raw: Vec<u8>) -> Option<()> {
|
||||
let mut vxlan = MutableVxlanPacket::new(&mut raw[..]).unwrap();
|
||||
let mut eth = MutableEthernetPacket::new(vxlan.payload_mut()).unwrap();
|
||||
eth.set_destination(eth.get_source());
|
||||
eth.set_source(env.gateway_mac);
|
||||
let mut arp = MutableArpPacket::new(eth.payload_mut()).unwrap();
|
||||
arp.set_operation(ArpOperations::Reply);
|
||||
arp.set_target_hw_addr(arp.get_sender_hw_addr());
|
||||
arp.set_sender_hw_addr(env.gateway_mac);
|
||||
let target_addr = arp.get_target_proto_addr();
|
||||
arp.set_target_proto_addr(arp.get_sender_proto_addr());
|
||||
arp.set_sender_proto_addr(target_addr);
|
||||
|
||||
env.send(&raw, reading.vtep).await;
|
||||
Some(())
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
use core::net::{IpAddr, SocketAddr};
|
||||
use ipnet::IpNet;
|
||||
use pnet::util::MacAddr;
|
||||
use std::sync::Arc;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use crate::redirect::MacTable;
|
||||
|
||||
pub struct Env {
|
||||
pub socket: UdpSocket,
|
||||
pub used_vnis: (u32, u32),
|
||||
// pub local_nets: Vec<IpNet>,
|
||||
pub reserved_nets: Vec<IpNet>,
|
||||
// pub gateway: IpAddr,
|
||||
|
@ -29,4 +29,7 @@ impl Env {
|
|||
Err(err) => eprintln!("Encountered {} while sending packet to {}", err, addr),
|
||||
}
|
||||
}
|
||||
pub async fn mac_learn(self: Arc<Self>, mac: MacAddr, vni: u32, vtep: IpAddr) {
|
||||
self.mac_table.learn(mac, vni, vtep).await
|
||||
}
|
||||
}
|
||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -4,6 +4,7 @@ use pnet::util::MacAddr;
|
|||
use std::{io, str::FromStr, sync::Arc};
|
||||
use tokio::{net::UdpSocket, select, task};
|
||||
|
||||
mod complex_reply;
|
||||
mod global;
|
||||
mod packet;
|
||||
mod redirect;
|
||||
|
@ -24,9 +25,9 @@ async fn treat_packet(env: Arc<Env>, raw: Vec<u8>, src_vtep: SocketAddr) -> Opti
|
|||
match reading.action {
|
||||
InternalTransmit(dst) => redirect::send(env, reading, dst, raw).await,
|
||||
InternalBroadcast => redirect::broadcast(env, reading, raw).await,
|
||||
GatewayArp => todo!(),
|
||||
GatewayTransmit => todo!(),
|
||||
GatewayEnter => todo!(),
|
||||
GatewayArp => complex_reply::reply_arp(env, reading, raw).await,
|
||||
GatewayTransmit => redirect::gateway_send(env, raw).await,
|
||||
GatewayEnter(dst) => redirect::gateway_enter(env, dst, raw).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +51,6 @@ async fn main() -> io::Result<()> {
|
|||
let env = Arc::new(Env {
|
||||
socket: UdpSocket::bind(SocketAddr::new(addr_parsing(addr), 4789)).await?,
|
||||
// local_nets: local_nets.into_iter().map(net_parsing).collect(),
|
||||
used_vnis: vni_range,
|
||||
reserved_nets: reserved_nets.into_iter().map(net_parsing).collect(),
|
||||
// gateway: addr_parsing(gateway),
|
||||
gateway_vtep: addr_parsing(gateway_vtep),
|
||||
|
@ -58,7 +58,7 @@ async fn main() -> io::Result<()> {
|
|||
gateway_mac: MacAddr::from_str(gateway_mac)
|
||||
.unwrap_or_else(|e| panic!("Error parsing {:?}: {}", gateway_mac, e)),
|
||||
|
||||
mac_table: redirect::new_mactable(vni_range),
|
||||
mac_table: redirect::MacTable::new(vni_range),
|
||||
});
|
||||
|
||||
let mut buf = vec![0u8; 2000];
|
||||
|
|
|
@ -11,7 +11,6 @@ use pnet::{
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::global::Env;
|
||||
use crate::redirect::mac_learn;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
/// Action to be taken for a packet
|
||||
|
@ -29,7 +28,7 @@ pub enum Action {
|
|||
GatewayTransmit,
|
||||
|
||||
/// Forward the packet bypassing VNIs
|
||||
GatewayEnter,
|
||||
GatewayEnter(MacAddr),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -47,14 +46,12 @@ pub fn read_packet(env: &Arc<Env>, payload: &[u8], src: SocketAddr) -> Option<Pa
|
|||
|
||||
let mut action = InternalTransmit(eth.get_destination());
|
||||
if vxlan.get_vni() == env.gateway_vni {
|
||||
action = GatewayEnter;
|
||||
action = GatewayEnter(eth.get_destination());
|
||||
} else {
|
||||
tokio::spawn(mac_learn(
|
||||
env.clone(),
|
||||
eth.get_source(),
|
||||
vxlan.get_vni(),
|
||||
src.ip(),
|
||||
));
|
||||
tokio::spawn(
|
||||
env.clone()
|
||||
.mac_learn(eth.get_source(), vxlan.get_vni(), src.ip()),
|
||||
);
|
||||
if eth.get_destination().is_broadcast() {
|
||||
action = InternalBroadcast;
|
||||
if eth.get_ethertype() == EtherTypes::Arp {
|
||||
|
|
|
@ -1,33 +1,48 @@
|
|||
use pnet::util::MacAddr;
|
||||
use pnet::{packet::vxlan::MutableVxlanPacket, util::MacAddr};
|
||||
use std::{collections::HashMap, net::IpAddr, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::global::Env;
|
||||
use crate::packet::PacketReading;
|
||||
|
||||
pub type MacTable = Vec<RwLock<HashMap<MacAddr, IpAddr>>>;
|
||||
|
||||
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 struct MacTable {
|
||||
vni_range: (u32, u32),
|
||||
per_vni: Vec<RwLock<HashMap<MacAddr, IpAddr>>>,
|
||||
// TODO: HashMap<(MacAddr, IpAddr), (u32, IpAddr)>
|
||||
per_mac: RwLock<HashMap<MacAddr, (u32, IpAddr)>>,
|
||||
}
|
||||
|
||||
fn mac_table(env: &Arc<Env>, vni: u32) -> Option<&RwLock<HashMap<MacAddr, IpAddr>>> {
|
||||
if env.used_vnis.0 <= vni && vni < env.used_vnis.1 {
|
||||
Some(&env.mac_table[(vni - env.used_vnis.0) as usize])
|
||||
} else {
|
||||
None
|
||||
impl MacTable {
|
||||
pub fn new(vni_range: (u32, u32)) -> Self {
|
||||
let mut per_vni = Vec::new();
|
||||
per_vni.resize_with((vni_range.1 - vni_range.0) as usize, || {
|
||||
RwLock::new(HashMap::new())
|
||||
});
|
||||
Self {
|
||||
vni_range,
|
||||
per_vni,
|
||||
per_mac: RwLock::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
fn vni_table(&self, vni: u32) -> Option<&RwLock<HashMap<MacAddr, IpAddr>>> {
|
||||
if self.vni_range.0 <= vni && vni < self.vni_range.1 {
|
||||
Some(&self.per_vni[(vni - self.vni_range.0) as usize])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn learn(&self, mac: MacAddr, vni: u32, vtep: IpAddr) {
|
||||
// we avoid taking a write lock when unnecessary
|
||||
|
||||
if let Some(vni_table) = self.vni_table(vni) {
|
||||
if vni_table.read().await.get(&mac) != Some(&vtep) {
|
||||
vni_table.write().await.insert(mac, vtep);
|
||||
}
|
||||
}
|
||||
if self.per_mac.read().await.get(&mac) != Some(&(vni, vtep)) {
|
||||
self.per_mac.write().await.insert(mac, (vni, vtep));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +53,12 @@ pub async fn send(
|
|||
dst: MacAddr,
|
||||
packet: Vec<u8>,
|
||||
) -> Option<()> {
|
||||
let vtep = *mac_table(&env, reading.vni)?.read().await.get(&dst)?;
|
||||
let vtep = *env
|
||||
.mac_table
|
||||
.vni_table(reading.vni)?
|
||||
.read()
|
||||
.await
|
||||
.get(&dst)?;
|
||||
if vtep != reading.vtep {
|
||||
env.send(&packet, vtep).await;
|
||||
}
|
||||
|
@ -46,7 +66,7 @@ pub async fn send(
|
|||
}
|
||||
|
||||
pub async fn broadcast(env: Arc<Env>, reading: PacketReading, packet: Vec<u8>) -> Option<()> {
|
||||
let table = mac_table(&env, reading.vni)?.read().await;
|
||||
let table = env.mac_table.vni_table(reading.vni)?.read().await;
|
||||
for vtep in table.values() {
|
||||
if *vtep != reading.vtep {
|
||||
// TODO: parallele send
|
||||
|
@ -55,3 +75,18 @@ pub async fn broadcast(env: Arc<Env>, reading: PacketReading, packet: Vec<u8>) -
|
|||
}
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub async fn gateway_send(env: Arc<Env>, mut packet: Vec<u8>) -> Option<()> {
|
||||
let mut vxlan = MutableVxlanPacket::new(&mut packet[..]).unwrap();
|
||||
vxlan.set_vni(env.gateway_vni);
|
||||
env.send(&packet, env.gateway_vtep).await;
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub async fn gateway_enter(env: Arc<Env>, dst: MacAddr, mut packet: Vec<u8>) -> Option<()> {
|
||||
let infos = env.mac_table.per_mac.read().await.get(&dst).copied()?;
|
||||
let mut vxlan = MutableVxlanPacket::new(&mut packet[..]).unwrap();
|
||||
vxlan.set_vni(infos.0);
|
||||
env.send(&packet, infos.1).await;
|
||||
Some(())
|
||||
}
|
||||
|
|
Reference in a new issue