End-to-end UDP implementation

Port re-use still needs to be implemented to prevent exhaustion over
time, and flooding.
This commit is contained in:
Aram 🍐 2021-10-25 19:05:40 -04:00
parent 282d4f48eb
commit d975efefaf
3 changed files with 106 additions and 36 deletions

View file

@ -4,7 +4,7 @@ use std::time::Duration;
use anyhow::Context;
use boringtun::noise::{Tunn, TunnResult};
use log::Level;
use smoltcp::wire::{IpProtocol, IpVersion, Ipv4Packet, Ipv6Packet, TcpPacket};
use smoltcp::wire::{IpProtocol, IpVersion, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket};
use tokio::net::UdpSocket;
use tokio::sync::RwLock;
@ -90,17 +90,8 @@ impl WireGuardTunnel {
virtual_port: VirtualPort,
sender: tokio::sync::mpsc::Sender<Vec<u8>>,
) -> anyhow::Result<()> {
let existing = self.is_registered(virtual_port);
if existing {
Err(anyhow::anyhow!("Cannot register virtual interface with virtual port {} because it is already registered", virtual_port))
} else {
self.virtual_port_ip_tx.insert(virtual_port, sender);
Ok(())
}
}
pub fn is_registered(&self, virtual_port: VirtualPort) -> bool {
self.virtual_port_ip_tx.contains_key(&virtual_port)
self.virtual_port_ip_tx.insert(virtual_port, sender);
Ok(())
}
/// Register a virtual interface (using its assigned virtual port) with the given IP packet `Sender`.
@ -276,6 +267,7 @@ impl WireGuardTunnel {
.filter(|packet| Ipv4Addr::from(packet.dst_addr()) == self.source_peer_ip)
.map(|packet| match packet.protocol() {
IpProtocol::Tcp => Some(self.route_tcp_segment(packet.payload())),
IpProtocol::Udp => Some(self.route_udp_datagram(packet.payload())),
// Unrecognized protocol, so we cannot determine where to route
_ => Some(RouteResult::Drop),
})
@ -287,6 +279,7 @@ impl WireGuardTunnel {
.filter(|packet| Ipv6Addr::from(packet.dst_addr()) == self.source_peer_ip)
.map(|packet| match packet.next_header() {
IpProtocol::Tcp => Some(self.route_tcp_segment(packet.payload())),
IpProtocol::Udp => Some(self.route_udp_datagram(packet.payload())),
// Unrecognized protocol, so we cannot determine where to route
_ => Some(RouteResult::Drop),
})
@ -316,6 +309,24 @@ impl WireGuardTunnel {
.unwrap_or(RouteResult::Drop)
}
/// Makes a decision on the handling of an incoming UDP datagram.
fn route_udp_datagram(&self, datagram: &[u8]) -> RouteResult {
UdpPacket::new_checked(datagram)
.ok()
.map(|udp| {
if self
.virtual_port_ip_tx
.get(&VirtualPort(udp.dst_port(), PortProtocol::Udp))
.is_some()
{
RouteResult::Dispatch(VirtualPort(udp.dst_port(), PortProtocol::Udp))
} else {
RouteResult::Drop
}
})
.unwrap_or(RouteResult::Drop)
}
/// Route a packet to the IP sink interface.
async fn route_ip_sink(&self, packet: &[u8]) -> anyhow::Result<()> {
let ip_sink_tx = self.sink_ip_tx.read().await;