use crate::slirp::packet::{build_packet4, build_packet6};
use crate::Result;
use std::io::{Read, Write};
use std::net::{Ipv4Addr, Ipv6Addr, Shutdown, TcpStream};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, Mutex};
use std::thread;
pub(crate) const FLAG_FIN: u8 = 0x01;
pub(crate) const FLAG_SYN: u8 = 0x02;
pub(crate) const FLAG_RST: u8 = 0x04;
pub(crate) const FLAG_PSH: u8 = 0x08;
pub(crate) const FLAG_ACK: u8 = 0x10;
const RECV_WINDOW: u16 = 32768;
const MSS_V4: usize = 1460;
const MSS_V6: usize = 1440;
pub(crate) enum SendBack {
V4 {
local_ip: Ipv4Addr,
remote_ip: Ipv4Addr,
send: Arc<dyn Fn(&[u8]) -> Result<()> + Send + Sync>,
},
V6 {
local_ip: Ipv6Addr,
remote_ip: Ipv6Addr,
send: Arc<dyn Fn(&[u8]) -> Result<()> + Send + Sync>,
},
}
impl SendBack {
fn write_seg(&self, seg: &[u8]) -> Result<()> {
match self {
SendBack::V4 {
local_ip,
remote_ip,
send,
} => {
let pkt = build_packet4(*local_ip, *remote_ip, seg);
send(&pkt)
}
SendBack::V6 {
local_ip,
remote_ip,
send,
} => {
let pkt = build_packet6(*local_ip, *remote_ip, seg);
send(&pkt)
}
}
}
fn mss(&self) -> usize {
match self {
SendBack::V4 { .. } => MSS_V4,
SendBack::V6 { .. } => MSS_V6,
}
}
}
struct State {
snd_nxt: AtomicU32,
snd_iss: AtomicU32,
rcv_nxt: AtomicU32,
fin_sent: AtomicBool,
fin_recv: AtomicBool,
closed: AtomicBool,
remote_write: Mutex<Option<TcpStream>>,
}
pub(crate) struct TcpNatConn {
state: Arc<State>,
src_port: u16, dst_port: u16, send_back: SendBack,
}
impl TcpNatConn {
pub(crate) fn accept_syn(
client_src_port: u16,
client_dst_port: u16,
client_seq: u32,
remote: TcpStream,
send_back: SendBack,
) -> Result<Arc<TcpNatConn>> {
let remote_read = remote.try_clone()?;
let iss = crate::rand::u32();
let state = Arc::new(State {
snd_nxt: AtomicU32::new(iss.wrapping_add(1)),
snd_iss: AtomicU32::new(iss),
rcv_nxt: AtomicU32::new(client_seq.wrapping_add(1)),
fin_sent: AtomicBool::new(false),
fin_recv: AtomicBool::new(false),
closed: AtomicBool::new(false),
remote_write: Mutex::new(Some(remote)),
});
let conn = Arc::new(TcpNatConn {
state: state.clone(),
src_port: client_src_port,
dst_port: client_dst_port,
send_back,
});
conn.send_control(FLAG_SYN | FLAG_ACK, iss, client_seq.wrapping_add(1))?;
let conn2 = conn.clone();
thread::spawn(move || conn2.read_loop(remote_read));
Ok(conn)
}
pub(crate) fn handle_segment(&self, tcp: &[u8]) -> Result<()> {
if tcp.len() < 20 {
return Ok(());
}
let seq = u32::from_be_bytes([tcp[4], tcp[5], tcp[6], tcp[7]]);
let ack = u32::from_be_bytes([tcp[8], tcp[9], tcp[10], tcp[11]]);
let data_off = ((tcp[12] >> 4) as usize) * 4;
let flags = tcp[13];
if data_off < 20 || data_off > tcp.len() {
return Ok(());
}
let payload = &tcp[data_off..];
if flags & FLAG_RST != 0 {
self.shutdown_remote_both();
self.state.closed.store(true, Ordering::Release);
return Ok(());
}
if flags & FLAG_SYN != 0
&& self.state.snd_nxt.load(Ordering::Acquire)
== self.state.snd_iss.load(Ordering::Acquire).wrapping_add(1)
&& !self.state.fin_sent.load(Ordering::Acquire)
{
let iss = self.state.snd_iss.load(Ordering::Acquire);
self.send_control(FLAG_SYN | FLAG_ACK, iss, seq.wrapping_add(1))?;
return Ok(());
}
let _ = ack;
let mut consumed = self.state.rcv_nxt.load(Ordering::Acquire);
if seq == consumed && !payload.is_empty() {
let mut guard = self.state.remote_write.lock().expect("poisoned");
let write_err = match guard.as_mut() {
Some(s) => s.write_all(payload).err(),
None => None,
};
drop(guard);
if write_err.is_some() {
let _ = self.send_rst_keepalive(seq.wrapping_add(payload.len() as u32));
self.shutdown_remote_both();
self.state.closed.store(true, Ordering::Release);
return Ok(());
}
consumed = consumed.wrapping_add(payload.len() as u32);
self.state.rcv_nxt.store(consumed, Ordering::Release);
let snd_nxt = self.state.snd_nxt.load(Ordering::Acquire);
self.send_control(FLAG_ACK, snd_nxt, consumed)?;
} else if seq == consumed && payload.is_empty() {
} else if (seq.wrapping_add(payload.len() as u32)).wrapping_sub(consumed) as i32 <= 0 {
let snd_nxt = self.state.snd_nxt.load(Ordering::Acquire);
self.send_control(FLAG_ACK, snd_nxt, consumed)?;
} else {
let snd_nxt = self.state.snd_nxt.load(Ordering::Acquire);
self.send_control(FLAG_ACK, snd_nxt, consumed)?;
}
if flags & FLAG_FIN != 0 && !self.state.fin_recv.load(Ordering::Acquire) {
self.state.fin_recv.store(true, Ordering::Release);
let new_rcv = consumed.wrapping_add(1);
self.state.rcv_nxt.store(new_rcv, Ordering::Release);
if let Ok(g) = self.state.remote_write.lock() {
if let Some(s) = g.as_ref() {
let _ = s.shutdown(Shutdown::Write);
}
}
let snd_nxt = self.state.snd_nxt.load(Ordering::Acquire);
self.send_control(FLAG_ACK, snd_nxt, new_rcv)?;
if self.state.fin_sent.load(Ordering::Acquire) {
self.state.closed.store(true, Ordering::Release);
}
}
Ok(())
}
fn read_loop(self: Arc<Self>, mut remote_read: TcpStream) {
let mss = self.send_back.mss();
let mut buf = vec![0u8; mss];
loop {
if self.state.closed.load(Ordering::Acquire) {
return;
}
let n = remote_read.read(&mut buf).unwrap_or_default();
if n == 0 {
if !self.state.fin_sent.swap(true, Ordering::AcqRel) {
let snd_nxt = self.state.snd_nxt.load(Ordering::Acquire);
let rcv_nxt = self.state.rcv_nxt.load(Ordering::Acquire);
let _ = self.send_control(FLAG_FIN | FLAG_ACK, snd_nxt, rcv_nxt);
self.state
.snd_nxt
.store(snd_nxt.wrapping_add(1), Ordering::Release);
}
self.state.closed.store(true, Ordering::Release);
return;
}
let snd_nxt = self.state.snd_nxt.load(Ordering::Acquire);
let rcv_nxt = self.state.rcv_nxt.load(Ordering::Acquire);
let _ = self.send_data(snd_nxt, rcv_nxt, &buf[..n]);
self.state
.snd_nxt
.store(snd_nxt.wrapping_add(n as u32), Ordering::Release);
}
}
fn send_control(&self, flags: u8, seq: u32, ack: u32) -> Result<()> {
let mut hdr = vec![0u8; 20];
self.fill_hdr(&mut hdr, flags, seq, ack);
self.send_back.write_seg(&hdr)
}
fn send_data(&self, seq: u32, ack: u32, payload: &[u8]) -> Result<()> {
let mut buf = vec![0u8; 20 + payload.len()];
self.fill_hdr(&mut buf, FLAG_PSH | FLAG_ACK, seq, ack);
buf[20..].copy_from_slice(payload);
self.send_back.write_seg(&buf)
}
fn send_rst_keepalive(&self, ack: u32) -> Result<()> {
let snd_nxt = self.state.snd_nxt.load(Ordering::Acquire);
self.send_control(FLAG_RST | FLAG_ACK, snd_nxt, ack)
}
fn fill_hdr(&self, hdr: &mut [u8], flags: u8, seq: u32, ack: u32) {
hdr[0..2].copy_from_slice(&self.dst_port.to_be_bytes());
hdr[2..4].copy_from_slice(&self.src_port.to_be_bytes());
hdr[4..8].copy_from_slice(&seq.to_be_bytes());
hdr[8..12].copy_from_slice(&ack.to_be_bytes());
hdr[12] = 5 << 4; hdr[13] = flags;
hdr[14..16].copy_from_slice(&RECV_WINDOW.to_be_bytes());
}
fn shutdown_remote_both(&self) {
if let Ok(g) = self.state.remote_write.lock() {
if let Some(s) = g.as_ref() {
let _ = s.shutdown(Shutdown::Both);
}
}
}
pub(crate) fn close(&self) {
self.shutdown_remote_both();
self.state.closed.store(true, Ordering::Release);
let snd_nxt = self.state.snd_nxt.load(Ordering::Acquire);
let rcv_nxt = self.state.rcv_nxt.load(Ordering::Acquire);
let _ = self.send_control(FLAG_RST | FLAG_ACK, snd_nxt, rcv_nxt);
}
pub(crate) fn is_closed(&self) -> bool {
self.state.closed.load(Ordering::Acquire)
}
}
impl Drop for TcpNatConn {
fn drop(&mut self) {
self.shutdown_remote_both();
self.state.closed.store(true, Ordering::Release);
}
}
pub(crate) fn build_rst_for_stray(tcp: &[u8], dst_port: u16, src_port: u16) -> Option<Vec<u8>> {
if tcp.len() < 20 {
return None;
}
let flags = tcp[13];
let mut hdr = vec![0u8; 20];
hdr[0..2].copy_from_slice(&dst_port.to_be_bytes());
hdr[2..4].copy_from_slice(&src_port.to_be_bytes());
hdr[12] = 5 << 4;
hdr[14..16].copy_from_slice(&RECV_WINDOW.to_be_bytes());
if (flags & FLAG_ACK) != 0 {
let seg_ack = u32::from_be_bytes([tcp[8], tcp[9], tcp[10], tcp[11]]);
hdr[4..8].copy_from_slice(&seg_ack.to_be_bytes());
hdr[13] = FLAG_RST;
} else {
let seg_seq = u32::from_be_bytes([tcp[4], tcp[5], tcp[6], tcp[7]]);
let data_off = ((tcp[12] >> 4) as usize) * 4;
let data_off = data_off.min(tcp.len());
let mut data_len = (tcp.len() - data_off) as u32;
if (tcp[13] & FLAG_FIN) != 0 {
data_len = data_len.wrapping_add(1);
}
hdr[4..8].copy_from_slice(&0u32.to_be_bytes());
hdr[8..12].copy_from_slice(&seg_seq.wrapping_add(data_len).to_be_bytes());
hdr[13] = FLAG_RST | FLAG_ACK;
}
Some(hdr)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_rst_with_ack() {
let mut tcp = vec![0u8; 20];
tcp[12] = 5 << 4;
tcp[13] = FLAG_ACK;
tcp[8..12].copy_from_slice(&12345u32.to_be_bytes());
let rst = build_rst_for_stray(&tcp, 80, 5000).unwrap();
assert_eq!(rst[13], FLAG_RST);
let seq = u32::from_be_bytes([rst[4], rst[5], rst[6], rst[7]]);
assert_eq!(seq, 12345);
}
#[test]
fn build_rst_without_ack() {
let mut tcp = vec![0u8; 20];
tcp[12] = 5 << 4;
tcp[13] = FLAG_SYN;
tcp[4..8].copy_from_slice(&100u32.to_be_bytes());
let rst = build_rst_for_stray(&tcp, 80, 5000).unwrap();
assert_eq!(rst[13], FLAG_RST | FLAG_ACK);
let ack = u32::from_be_bytes([rst[8], rst[9], rst[10], rst[11]]);
assert_eq!(ack, 100);
}
}