use std::net::Ipv4Addr;
use super::{
ETH_HEADER_LEN,
checksum::{ipv4_header_checksum, tcp_checksum},
};
#[derive(Debug, Clone, Copy)]
pub struct TcpFrameParams {
pub src_ip: Ipv4Addr,
pub dst_ip: Ipv4Addr,
pub src_port: u16,
pub dst_port: u16,
pub seq: u32,
pub ack: u32,
pub window: u16,
pub src_mac: [u8; 6],
pub dst_mac: [u8; 6],
}
#[must_use]
pub fn build_tcp_ack_frame(p: &TcpFrameParams) -> Vec<u8> {
let TcpFrameParams {
src_ip,
dst_ip,
src_port,
dst_port,
seq,
ack,
window,
src_mac,
dst_mac,
} = *p;
let tcp_hdr_len = 20;
let ip_total_len = 20 + tcp_hdr_len;
let frame_len = ETH_HEADER_LEN + ip_total_len;
let mut frame = vec![0u8; frame_len];
frame[0..6].copy_from_slice(&dst_mac);
frame[6..12].copy_from_slice(&src_mac);
frame[12..14].copy_from_slice(&0x0800u16.to_be_bytes());
let ip = ETH_HEADER_LEN;
frame[ip] = 0x45; frame[ip + 2..ip + 4].copy_from_slice(&(ip_total_len as u16).to_be_bytes());
frame[ip + 6..ip + 8].copy_from_slice(&0x4000u16.to_be_bytes()); frame[ip + 8] = 64; frame[ip + 9] = 6; frame[ip + 12..ip + 16].copy_from_slice(&src_ip.octets());
frame[ip + 16..ip + 20].copy_from_slice(&dst_ip.octets());
let ip_cksum = ipv4_header_checksum(&frame[ip..ip + 20]);
frame[ip + 10..ip + 12].copy_from_slice(&ip_cksum.to_be_bytes());
let tcp = ip + 20;
frame[tcp..tcp + 2].copy_from_slice(&src_port.to_be_bytes());
frame[tcp + 2..tcp + 4].copy_from_slice(&dst_port.to_be_bytes());
frame[tcp + 4..tcp + 8].copy_from_slice(&seq.to_be_bytes());
frame[tcp + 8..tcp + 12].copy_from_slice(&ack.to_be_bytes());
frame[tcp + 12] = 0x50; frame[tcp + 13] = 0x10; frame[tcp + 14..tcp + 16].copy_from_slice(&window.to_be_bytes());
let tcp_cksum = tcp_checksum(src_ip, dst_ip, &frame[tcp..]);
frame[tcp + 16..tcp + 18].copy_from_slice(&tcp_cksum.to_be_bytes());
frame
}
#[must_use]
pub fn build_tcp_data_frame(p: &TcpFrameParams, payload: &[u8]) -> Vec<u8> {
let TcpFrameParams {
src_ip,
dst_ip,
src_port,
dst_port,
seq,
ack,
window,
src_mac,
dst_mac,
} = *p;
let tcp_hdr_len = 20;
let tcp_total_len = tcp_hdr_len + payload.len();
let ip_total_len = 20 + tcp_total_len;
assert!(
u16::try_from(ip_total_len).is_ok(),
"build_tcp_data_frame: ip_total_len={ip_total_len} overflows u16"
);
let frame_len = ETH_HEADER_LEN + ip_total_len;
let mut frame = vec![0u8; frame_len];
frame[0..6].copy_from_slice(&dst_mac);
frame[6..12].copy_from_slice(&src_mac);
frame[12..14].copy_from_slice(&0x0800u16.to_be_bytes());
let ip = ETH_HEADER_LEN;
frame[ip] = 0x45;
frame[ip + 2..ip + 4].copy_from_slice(&(ip_total_len as u16).to_be_bytes());
frame[ip + 6..ip + 8].copy_from_slice(&0x4000u16.to_be_bytes()); frame[ip + 8] = 64; frame[ip + 9] = 6; frame[ip + 12..ip + 16].copy_from_slice(&src_ip.octets());
frame[ip + 16..ip + 20].copy_from_slice(&dst_ip.octets());
let ip_cksum = ipv4_header_checksum(&frame[ip..ip + 20]);
frame[ip + 10..ip + 12].copy_from_slice(&ip_cksum.to_be_bytes());
let tcp = ip + 20;
frame[tcp..tcp + 2].copy_from_slice(&src_port.to_be_bytes());
frame[tcp + 2..tcp + 4].copy_from_slice(&dst_port.to_be_bytes());
frame[tcp + 4..tcp + 8].copy_from_slice(&seq.to_be_bytes());
frame[tcp + 8..tcp + 12].copy_from_slice(&ack.to_be_bytes());
frame[tcp + 12] = 0x50; frame[tcp + 13] = 0x18; frame[tcp + 14..tcp + 16].copy_from_slice(&window.to_be_bytes());
frame[tcp + 20..].copy_from_slice(payload);
let tcp_cksum = tcp_checksum(src_ip, dst_ip, &frame[tcp..]);
frame[tcp + 16..tcp + 18].copy_from_slice(&tcp_cksum.to_be_bytes());
frame
}
#[must_use]
pub fn build_tcp_data_frame_partial_csum(p: &TcpFrameParams, payload: &[u8]) -> Vec<u8> {
let TcpFrameParams {
src_ip,
dst_ip,
src_port,
dst_port,
seq,
ack,
window,
src_mac,
dst_mac,
} = *p;
let tcp_hdr_len = 20;
let tcp_total_len = tcp_hdr_len + payload.len();
let ip_total_len = 20 + tcp_total_len;
assert!(
u16::try_from(ip_total_len).is_ok(),
"build_tcp_data_frame_partial_csum: ip_total_len={ip_total_len} overflows u16"
);
let frame_len = ETH_HEADER_LEN + ip_total_len;
let mut frame = vec![0u8; frame_len];
frame[0..6].copy_from_slice(&dst_mac);
frame[6..12].copy_from_slice(&src_mac);
frame[12..14].copy_from_slice(&0x0800u16.to_be_bytes());
let ip = ETH_HEADER_LEN;
frame[ip] = 0x45;
frame[ip + 2..ip + 4].copy_from_slice(&(ip_total_len as u16).to_be_bytes());
frame[ip + 6..ip + 8].copy_from_slice(&0x4000u16.to_be_bytes()); frame[ip + 8] = 64; frame[ip + 9] = 6; frame[ip + 12..ip + 16].copy_from_slice(&src_ip.octets());
frame[ip + 16..ip + 20].copy_from_slice(&dst_ip.octets());
let ip_cksum = ipv4_header_checksum(&frame[ip..ip + 20]);
frame[ip + 10..ip + 12].copy_from_slice(&ip_cksum.to_be_bytes());
let tcp = ip + 20;
frame[tcp..tcp + 2].copy_from_slice(&src_port.to_be_bytes());
frame[tcp + 2..tcp + 4].copy_from_slice(&dst_port.to_be_bytes());
frame[tcp + 4..tcp + 8].copy_from_slice(&seq.to_be_bytes());
frame[tcp + 8..tcp + 12].copy_from_slice(&ack.to_be_bytes());
frame[tcp + 12] = 0x50; frame[tcp + 13] = 0x18; frame[tcp + 14..tcp + 16].copy_from_slice(&window.to_be_bytes());
frame[tcp + 20..].copy_from_slice(payload);
let pseudo_cksum = tcp_pseudo_header_checksum(src_ip, dst_ip, tcp_total_len);
frame[tcp + 16..tcp + 18].copy_from_slice(&pseudo_cksum.to_be_bytes());
frame
}
#[must_use]
pub 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
}
#[must_use]
pub fn build_tcp_fin_frame(p: &TcpFrameParams) -> Vec<u8> {
let mut ack_params = *p;
ack_params.window = 65535;
let mut frame = build_tcp_ack_frame(&ack_params);
let tcp = ETH_HEADER_LEN + 20;
frame[tcp + 13] = 0x11; frame[tcp + 16..tcp + 18].copy_from_slice(&[0, 0]);
let tcp_cksum = tcp_checksum(p.src_ip, p.dst_ip, &frame[tcp..]);
frame[tcp + 16..tcp + 18].copy_from_slice(&tcp_cksum.to_be_bytes());
frame
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct TcpSynOptions {
pub mss: Option<u16>,
pub wscale: Option<u8>,
pub sack_permitted: bool,
pub timestamps: bool,
}
#[must_use]
pub fn parse_tcp_syn_options(tcp_segment: &[u8]) -> TcpSynOptions {
let mut opts = TcpSynOptions::default();
if tcp_segment.len() < 20 {
return opts;
}
let data_offset = usize::from(tcp_segment[12] >> 4) * 4;
if data_offset < 20 || data_offset > tcp_segment.len() {
return opts;
}
let options = &tcp_segment[20..data_offset];
let mut i = 0;
while i < options.len() {
let kind = options[i];
match kind {
0 => break, 1 => {
i += 1;
} 2 => {
if i + 4 > options.len() || options[i + 1] != 4 {
break;
}
opts.mss = Some(u16::from_be_bytes([options[i + 2], options[i + 3]]));
i += 4;
}
3 => {
if i + 3 > options.len() || options[i + 1] != 3 {
break;
}
opts.wscale = Some(options[i + 2]);
i += 3;
}
4 => {
if i + 2 > options.len() || options[i + 1] != 2 {
break;
}
opts.sack_permitted = true;
i += 2;
}
8 => {
if i + 10 > options.len() || options[i + 1] != 10 {
break;
}
opts.timestamps = true;
i += 10;
}
_ => {
if i + 1 >= options.len() {
break;
}
let len = usize::from(options[i + 1]);
if len < 2 || i + len > options.len() {
break;
}
i += len;
}
}
}
opts
}
#[derive(Debug, Clone, Copy)]
pub struct SynAckParams {
pub src_ip: Ipv4Addr,
pub dst_ip: Ipv4Addr,
pub src_port: u16,
pub dst_port: u16,
pub seq: u32,
pub ack: u32,
pub src_mac: [u8; 6],
pub dst_mac: [u8; 6],
pub mss: u16,
pub wscale: Option<u8>,
pub sack_permitted: bool,
}
#[must_use]
pub fn build_tcp_syn_ack_frame(p: &SynAckParams) -> Vec<u8> {
let mut options: Vec<u8> = Vec::with_capacity(16);
options.push(2);
options.push(4);
options.extend_from_slice(&p.mss.to_be_bytes());
if p.sack_permitted {
options.push(4);
options.push(2);
}
if let Some(shift) = p.wscale {
options.push(3);
options.push(3);
options.push(shift);
}
while !options.len().is_multiple_of(4) {
options.push(1); }
let tcp_hdr_len = 20 + options.len();
let ip_total_len = 20 + tcp_hdr_len;
let frame_len = ETH_HEADER_LEN + ip_total_len;
let mut frame = vec![0u8; frame_len];
frame[0..6].copy_from_slice(&p.dst_mac);
frame[6..12].copy_from_slice(&p.src_mac);
frame[12..14].copy_from_slice(&0x0800u16.to_be_bytes());
let ip = ETH_HEADER_LEN;
frame[ip] = 0x45;
frame[ip + 2..ip + 4].copy_from_slice(&(ip_total_len as u16).to_be_bytes());
frame[ip + 6..ip + 8].copy_from_slice(&0x4000u16.to_be_bytes()); frame[ip + 8] = 64;
frame[ip + 9] = 6; frame[ip + 12..ip + 16].copy_from_slice(&p.src_ip.octets());
frame[ip + 16..ip + 20].copy_from_slice(&p.dst_ip.octets());
let ip_cksum = ipv4_header_checksum(&frame[ip..ip + 20]);
frame[ip + 10..ip + 12].copy_from_slice(&ip_cksum.to_be_bytes());
let tcp = ip + 20;
frame[tcp..tcp + 2].copy_from_slice(&p.src_port.to_be_bytes());
frame[tcp + 2..tcp + 4].copy_from_slice(&p.dst_port.to_be_bytes());
frame[tcp + 4..tcp + 8].copy_from_slice(&p.seq.to_be_bytes());
frame[tcp + 8..tcp + 12].copy_from_slice(&p.ack.to_be_bytes());
frame[tcp + 12] = ((tcp_hdr_len / 4) as u8) << 4;
frame[tcp + 13] = 0x12; frame[tcp + 14..tcp + 16].copy_from_slice(&65535u16.to_be_bytes());
frame[tcp + 20..tcp + 20 + options.len()].copy_from_slice(&options);
let tcp_cksum = tcp_checksum(p.src_ip, p.dst_ip, &frame[tcp..]);
frame[tcp + 16..tcp + 18].copy_from_slice(&tcp_cksum.to_be_bytes());
frame
}
#[derive(Debug, Clone, Copy)]
pub struct SynParams {
pub src_ip: Ipv4Addr,
pub dst_ip: Ipv4Addr,
pub src_port: u16,
pub dst_port: u16,
pub seq: u32,
pub src_mac: [u8; 6],
pub dst_mac: [u8; 6],
pub mss: u16,
pub wscale: Option<u8>,
}
#[must_use]
pub fn build_tcp_syn_frame(p: &SynParams) -> Vec<u8> {
let mut options: Vec<u8> = Vec::with_capacity(12);
options.push(2);
options.push(4);
options.extend_from_slice(&p.mss.to_be_bytes());
options.push(4);
options.push(2);
if let Some(shift) = p.wscale {
options.push(3);
options.push(3);
options.push(shift);
}
while !options.len().is_multiple_of(4) {
options.push(1);
}
let tcp_hdr_len = 20 + options.len();
let ip_total_len = 20 + tcp_hdr_len;
let frame_len = ETH_HEADER_LEN + ip_total_len;
let mut frame = vec![0u8; frame_len];
frame[0..6].copy_from_slice(&p.dst_mac);
frame[6..12].copy_from_slice(&p.src_mac);
frame[12..14].copy_from_slice(&0x0800u16.to_be_bytes());
let ip = ETH_HEADER_LEN;
frame[ip] = 0x45;
frame[ip + 2..ip + 4].copy_from_slice(&(ip_total_len as u16).to_be_bytes());
frame[ip + 6..ip + 8].copy_from_slice(&0x4000u16.to_be_bytes());
frame[ip + 8] = 64;
frame[ip + 9] = 6;
frame[ip + 12..ip + 16].copy_from_slice(&p.src_ip.octets());
frame[ip + 16..ip + 20].copy_from_slice(&p.dst_ip.octets());
let ip_cksum = ipv4_header_checksum(&frame[ip..ip + 20]);
frame[ip + 10..ip + 12].copy_from_slice(&ip_cksum.to_be_bytes());
let tcp = ip + 20;
frame[tcp..tcp + 2].copy_from_slice(&p.src_port.to_be_bytes());
frame[tcp + 2..tcp + 4].copy_from_slice(&p.dst_port.to_be_bytes());
frame[tcp + 4..tcp + 8].copy_from_slice(&p.seq.to_be_bytes());
frame[tcp + 12] = ((tcp_hdr_len / 4) as u8) << 4;
frame[tcp + 13] = 0x02; frame[tcp + 14..tcp + 16].copy_from_slice(&65535u16.to_be_bytes());
frame[tcp + 20..tcp + 20 + options.len()].copy_from_slice(&options);
let tcp_cksum = tcp_checksum(p.src_ip, p.dst_ip, &frame[tcp..]);
frame[tcp + 16..tcp + 18].copy_from_slice(&tcp_cksum.to_be_bytes());
frame
}
#[must_use]
pub fn build_tcp_rst_frame(p: &TcpFrameParams) -> Vec<u8> {
let mut rst_params = *p;
rst_params.window = 0;
let mut frame = build_tcp_ack_frame(&rst_params);
let tcp = ETH_HEADER_LEN + 20;
frame[tcp + 13] = 0x14; frame[tcp + 16..tcp + 18].copy_from_slice(&[0, 0]);
let tcp_cksum = tcp_checksum(p.src_ip, p.dst_ip, &frame[tcp..]);
frame[tcp + 16..tcp + 18].copy_from_slice(&tcp_cksum.to_be_bytes());
frame
}