use std::io;
use std::net::SocketAddr;
use std::time::Duration;
use crate::esp::ChildSa;
use crate::transport::{DriverError, UdpTransport};
pub struct Tunnel {
sa: ChildSa,
transport: UdpTransport,
peer: SocketAddr,
}
impl Tunnel {
pub fn new(sa: ChildSa, transport: UdpTransport, peer: SocketAddr) -> Self {
Self { sa, transport, peer }
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.transport.local_addr()
}
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.transport.set_read_timeout(dur)
}
pub fn send(&mut self, inner: &[u8], next_header: u8) -> Result<(), DriverError> {
let packet = self.sa.outbound.seal(inner, next_header)?;
self.transport.send_to(&packet, self.peer)?;
Ok(())
}
pub fn recv(&mut self) -> Result<(Vec<u8>, u8), DriverError> {
let (packet, _from) = self.transport.recv_from()?;
Ok(self.sa.inbound.open(&packet)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::esp::next_header;
use crate::role::Role;
#[test]
fn tunnel_carries_packets_both_ways_over_udp() {
let sk_d = [0x55u8; 32];
let ni = [1u8; 16];
let nr = [2u8; 16];
let init_spi = 0xAAAA_AAAA;
let resp_spi = 0xBBBB_BBBB;
let a_sa = ChildSa::derive(&sk_d, &ni, &nr, Role::Initiator, init_spi, resp_spi);
let b_sa = ChildSa::derive(&sk_d, &ni, &nr, Role::Responder, resp_spi, init_spi);
let a_tp = UdpTransport::bind("127.0.0.1:0").unwrap();
let b_tp = UdpTransport::bind("127.0.0.1:0").unwrap();
a_tp.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
b_tp.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
let a_addr = a_tp.local_addr().unwrap();
let b_addr = b_tp.local_addr().unwrap();
let mut a = Tunnel::new(a_sa, a_tp, b_addr);
let mut b = Tunnel::new(b_sa, b_tp, a_addr);
a.send(b"ping from A", next_header::IPV4).unwrap();
let (got, nh) = b.recv().unwrap();
assert_eq!(got, b"ping from A");
assert_eq!(nh, next_header::IPV4);
b.send(b"pong from B", next_header::IPV4).unwrap();
let (got, _) = a.recv().unwrap();
assert_eq!(got, b"pong from B");
}
}