mod common;
use common::{
local_ipv4, parse_ipv4_arg, print_help_if_requested, remote_ipv4, ExampleResult, EXAMPLE_IFACE,
};
use crafter::prelude::*;
fn main() -> ExampleResult<()> {
if print_help_if_requested(
"usage: cargo run --example tcp_options -- [--src IP] [--dst IP]\n\nBuild labeled TCP packets covering common options, SACK blocks, Fast Open, and generic options.",
) {
return Ok(());
}
let src = parse_ipv4_arg("--src", local_ipv4())?;
let dst = parse_ipv4_arg("--dst", remote_ipv4())?;
println!("example: tcp_options");
println!("mode: offline");
let syn = syn_options(src, dst)?;
inspect_tcp_packet("mss window-scale sack-permitted timestamp", &syn)?;
inspect_tcp_packet("sack blocks", &sack_blocks(src, dst)?)?;
inspect_tcp_packet("fast-open generic padding", &misc_options(src, dst)?)?;
dry_run_plan(&syn)?;
Ok(())
}
fn syn_options(src: std::net::Ipv4Addr, dst: std::net::Ipv4Addr) -> ExampleResult<Packet> {
let tcp = Tcp::new()
.sport(41000)
.dport(443)
.seq(1)
.flags(TCP_FLAG_SYN)
.tcp_option(TcpOption::mss(1460))?
.tcp_option(TcpOption::window_scale(7))?
.tcp_option(TcpOption::sack_permitted())?
.tcp_option(TcpOption::timestamp(0x1020_3040, 0))?;
Ok(Ipv4::new().src(src).dst(dst).id(0x5401) / tcp)
}
fn sack_blocks(src: std::net::Ipv4Addr, dst: std::net::Ipv4Addr) -> ExampleResult<Packet> {
let tcp = Tcp::new()
.sport(443)
.dport(41000)
.seq(10_000)
.ack(20_000)
.flags(TCP_FLAG_ACK)
.tcp_option(TcpOption::sack(vec![
TcpSackBlock::new(20_100, 20_400),
TcpSackBlock::new(20_800, 21_000),
]))?;
Ok(Ipv4::new().src(dst).dst(src).id(0x5402) / tcp)
}
fn misc_options(src: std::net::Ipv4Addr, dst: std::net::Ipv4Addr) -> ExampleResult<Packet> {
let tcp = Tcp::new()
.sport(41000)
.dport(80)
.seq(1)
.flags(TCP_FLAG_SYN)
.tcp_option(TcpOption::no_operation())?
.tcp_option(TcpOption::fast_open([0xaa_u8, 0xbb, 0xcc, 0xdd]))?
.tcp_option(TcpOption::generic(254, [0x01_u8, 0x02]))?
.tcp_option(TcpOption::mss(1460))?;
Ok(Ipv4::new().src(src).dst(dst).id(0x5403) / tcp / Raw::from("misc"))
}
fn inspect_tcp_packet(label: &str, packet: &Packet) -> ExampleResult<()> {
let compiled = packet.compile()?;
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes())?;
let tcp = decoded
.layer::<Tcp>()
.expect("decoded TCP option packet should contain Tcp");
let options = tcp.parsed_options()?;
println!("packet: {label}");
println!("summary: {}", packet.summary());
println!("decoded summary: {}", decoded.summary());
println!(
"data offset: {} words ({} bytes)",
tcp.data_offset_value(),
tcp.header_len()
);
for (index, option) in options.iter().enumerate() {
println!("tcp option {}: {:?}", index + 1, option);
}
println!("show:\n{}", decoded.show());
println!("hexdump:\n{}", compiled.hexdump());
Ok(())
}
fn dry_run_plan(packet: &Packet) -> ExampleResult<()> {
let plan = packet.send_dry_run(SendOptions::new().iface(EXAMPLE_IFACE).network_layer())?;
println!("dry-run plan");
println!("interface: {}", plan.interface());
println!("target: {:?}", plan.target());
println!("compiled bytes: {}", plan.len());
Ok(())
}