use std::io::Read;
use std::net::{Ipv4Addr, TcpStream};
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
const ETH_IP_TCP_HDR_LEN: usize = 54;
pub const TOTAL_HDR_LEN: usize = 12 + ETH_IP_TCP_HDR_LEN;
pub struct InlineConn {
pub stream: TcpStream,
pub remote_ip: Ipv4Addr,
pub guest_ip: Ipv4Addr,
pub remote_port: u16,
pub guest_port: u16,
pub our_seq: Arc<AtomicU32>,
pub last_ack: Arc<AtomicU32>,
pub guest_acked: Arc<AtomicU32>,
pub guest_window: Arc<AtomicU32>,
pub gw_mac: [u8; 6],
pub guest_mac: [u8; 6],
pub host_eof: bool,
pub dead: Arc<std::sync::atomic::AtomicBool>,
}
unsafe impl Send for InlineConn {}
const HONORED_WINDOW_CAP: u32 = 256 * 1024;
impl InlineConn {
pub fn send_budget(&self) -> u32 {
let sent = self.our_seq.load(Ordering::Relaxed);
let acked = self.guest_acked.load(Ordering::Relaxed);
let window = self
.guest_window
.load(Ordering::Relaxed)
.min(HONORED_WINDOW_CAP);
let in_flight = sent.wrapping_sub(acked);
if in_flight >= 0x8000_0000 {
return window;
}
window.saturating_sub(in_flight)
}
}
pub fn write_inline_headers(
buf: &mut [u8],
conn: &InlineConn,
payload_len: usize,
num_buffers: u16,
) -> usize {
if buf.len() < TOTAL_HDR_LEN {
return 0;
}
let last_ack = conn.last_ack.load(Ordering::Relaxed);
let tcp_total_len = 20 + payload_len;
let ip_total_len = 20 + tcp_total_len;
let eth_total_len = 14 + ip_total_len;
buf[0..12].fill(0);
buf[0] = 1; if eth_total_len > 1500 {
buf[1] = 1; buf[2..4].copy_from_slice(&54u16.to_le_bytes());
buf[4..6].copy_from_slice(&1460u16.to_le_bytes()); }
buf[6..8].copy_from_slice(&34u16.to_le_bytes()); buf[8..10].copy_from_slice(&16u16.to_le_bytes()); buf[10..12].copy_from_slice(&num_buffers.to_le_bytes());
let eth = 12;
buf[eth..eth + 6].copy_from_slice(&conn.guest_mac);
buf[eth + 6..eth + 12].copy_from_slice(&conn.gw_mac);
buf[eth + 12..eth + 14].copy_from_slice(&0x0800u16.to_be_bytes());
let ip = eth + 14;
buf[ip] = 0x45; buf[ip + 1] = 0;
buf[ip + 2..ip + 4].copy_from_slice(&(ip_total_len as u16).to_be_bytes());
buf[ip + 4..ip + 6].fill(0); buf[ip + 6..ip + 8].copy_from_slice(&0x4000u16.to_be_bytes()); buf[ip + 8] = 64; buf[ip + 9] = 6; buf[ip + 10..ip + 12].fill(0); buf[ip + 12..ip + 16].copy_from_slice(&conn.remote_ip.octets());
buf[ip + 16..ip + 20].copy_from_slice(&conn.guest_ip.octets());
let ip_cksum = ipv4_header_checksum(&buf[ip..ip + 20]);
buf[ip + 10..ip + 12].copy_from_slice(&ip_cksum.to_be_bytes());
let tcp = ip + 20;
let our_seq = conn.our_seq.load(Ordering::Relaxed);
buf[tcp..tcp + 2].copy_from_slice(&conn.remote_port.to_be_bytes());
buf[tcp + 2..tcp + 4].copy_from_slice(&conn.guest_port.to_be_bytes());
buf[tcp + 4..tcp + 8].copy_from_slice(&our_seq.to_be_bytes());
buf[tcp + 8..tcp + 12].copy_from_slice(&last_ack.to_be_bytes());
buf[tcp + 12] = 0x50; buf[tcp + 13] = 0x18; buf[tcp + 14..tcp + 16].copy_from_slice(&65535u16.to_be_bytes()); buf[tcp + 16..tcp + 18].fill(0); buf[tcp + 18..tcp + 20].fill(0);
let pseudo_cksum = tcp_pseudo_header_checksum(conn.remote_ip, conn.guest_ip, tcp_total_len);
buf[tcp + 16..tcp + 18].copy_from_slice(&pseudo_cksum.to_be_bytes());
TOTAL_HDR_LEN
}
pub fn read_payload_to_guest(conn: &mut InlineConn, buf: &mut [u8]) -> std::io::Result<usize> {
conn.stream.read(buf)
}
pub fn write_fin_headers(buf: &mut [u8], conn: &InlineConn) -> usize {
write_ctrl_headers(buf, conn, 0x11, 65535) }
pub fn write_rst_headers(buf: &mut [u8], conn: &InlineConn) -> usize {
write_ctrl_headers(buf, conn, 0x14, 0) }
fn write_ctrl_headers(buf: &mut [u8], conn: &InlineConn, tcp_flags: u8, window: u16) -> usize {
if buf.len() < TOTAL_HDR_LEN {
return 0;
}
let last_ack = conn.last_ack.load(Ordering::Relaxed);
let tcp_total_len = 20; let ip_total_len = 20 + tcp_total_len;
buf[0..12].fill(0);
buf[0] = 1; buf[6..8].copy_from_slice(&34u16.to_le_bytes()); buf[8..10].copy_from_slice(&16u16.to_le_bytes()); buf[10..12].copy_from_slice(&1u16.to_le_bytes());
let eth = 12;
buf[eth..eth + 6].copy_from_slice(&conn.guest_mac);
buf[eth + 6..eth + 12].copy_from_slice(&conn.gw_mac);
buf[eth + 12..eth + 14].copy_from_slice(&0x0800u16.to_be_bytes());
let ip = eth + 14;
buf[ip] = 0x45;
buf[ip + 1] = 0;
buf[ip + 2..ip + 4].copy_from_slice(&(ip_total_len as u16).to_be_bytes());
buf[ip + 4..ip + 6].fill(0);
buf[ip + 6..ip + 8].copy_from_slice(&0x4000u16.to_be_bytes());
buf[ip + 8] = 64;
buf[ip + 9] = 6;
buf[ip + 10..ip + 12].fill(0);
buf[ip + 12..ip + 16].copy_from_slice(&conn.remote_ip.octets());
buf[ip + 16..ip + 20].copy_from_slice(&conn.guest_ip.octets());
let ip_cksum = ipv4_header_checksum(&buf[ip..ip + 20]);
buf[ip + 10..ip + 12].copy_from_slice(&ip_cksum.to_be_bytes());
let tcp = ip + 20;
let our_seq = conn.our_seq.load(Ordering::Relaxed);
buf[tcp..tcp + 2].copy_from_slice(&conn.remote_port.to_be_bytes());
buf[tcp + 2..tcp + 4].copy_from_slice(&conn.guest_port.to_be_bytes());
buf[tcp + 4..tcp + 8].copy_from_slice(&our_seq.to_be_bytes());
buf[tcp + 8..tcp + 12].copy_from_slice(&last_ack.to_be_bytes());
buf[tcp + 12] = 0x50; buf[tcp + 13] = tcp_flags;
buf[tcp + 14..tcp + 16].copy_from_slice(&window.to_be_bytes());
buf[tcp + 16..tcp + 18].fill(0);
buf[tcp + 18..tcp + 20].fill(0);
let pseudo_cksum = tcp_pseudo_header_checksum(conn.remote_ip, conn.guest_ip, tcp_total_len);
buf[tcp + 16..tcp + 18].copy_from_slice(&pseudo_cksum.to_be_bytes());
TOTAL_HDR_LEN
}
fn ipv4_header_checksum(header: &[u8]) -> u16 {
let mut sum: u32 = 0;
let mut i = 0;
while i + 1 < header.len() {
if i != 10 {
sum += u32::from(u16::from_be_bytes([header[i], header[i + 1]]));
}
i += 2;
}
while sum > 0xFFFF {
sum = (sum & 0xFFFF) + (sum >> 16);
}
!sum as u16
}
fn tcp_pseudo_header_checksum(src_ip: Ipv4Addr, dst_ip: Ipv4Addr, tcp_len: usize) -> u16 {
let mut sum: u32 = 0;
let src = src_ip.octets();
let dst = dst_ip.octets();
sum += u32::from(u16::from_be_bytes([src[0], src[1]]));
sum += u32::from(u16::from_be_bytes([src[2], src[3]]));
sum += u32::from(u16::from_be_bytes([dst[0], dst[1]]));
sum += u32::from(u16::from_be_bytes([dst[2], dst[3]]));
sum += 6u32; sum += tcp_len as u32;
while sum > 0xFFFF {
sum = (sum & 0xFFFF) + (sum >> 16);
}
!sum as u16
}