环境
- Time 2022-11-24
- WSL-Ubuntu 22.04
- Rust 1.65.0
- pnet 0.31.0
- tun-tap 0.1.3
前言
说明
参考:https://docs.rs/pnet/latest/pnet/index.html
RFC 793
目标
了解 TCP 协议头中的字段,其也是基于 IP 协议的。
配置 TUN
IP 地址不要和主机的网卡地址在一个段,以便选择这个网卡进行路由。
root@jiangbo12490:~# ip tuntap add tun0 mode tun
root@jiangbo12490:~# ip addr add 192.168.44.144/24 dev tun0
root@jiangbo12490:~# ip link set up dev tun0
root@jiangbo12490:~# ip add show dev tun0
7: tun0: <NO-CARRIER,POINTOPOINT,MULTICAST,NOARP,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 500
link/none
inet 192.168.44.144/24 scope global tun0
valid_lft forever preferred_lft forever
main.rs
use pnet::packet::ip::IpNextHeaderProtocols;
use pnet::packet::{ipv4::Ipv4Packet, tcp::TcpPacket};
use tun_tap::{Iface, Mode};
fn main() -> std::io::Result<()> {
let iface = Iface::without_packet_info("tun0", Mode::Tun)?;
let mut buffer = vec![0; 1500];
loop {
let size = iface.recv(&mut buffer)?;
let packet = Ipv4Packet::new(&buffer).unwrap();
if packet.get_version() == 6 {
println!("IPv6 packet, continue");
continue;
}
if packet.get_next_level_protocol() != IpNextHeaderProtocols::Tcp {
println!("not tcp packet, continue");
continue;
}
// 因为头部长度的单位是 4 字节
let length = packet.get_header_length() as usize * 4;
let packet = TcpPacket::new(&buffer[length..]).unwrap();
// 2 字节的源端口
println!("source {}", packet.get_source());
// 2 字节的目的端口
println!("destination {}", packet.get_destination());
// 4 字节的序列号号
println!("sequence {}", packet.get_sequence());
// 4 字节的确认号
println!("acknowledgement {}", packet.get_acknowledgement());
// 4 位 TCP 首部长度,单位是 4 字节,最长 60 字节
println!("data_offset {}", packet.get_data_offset());
// 3 位的保留位
println!("reserved {}", packet.get_reserved());
// 9 位的控制位,标志位
println!("flags {}", packet.get_flags());
// 2 字节的窗口大小
println!("window {}", packet.get_window());
// 2 字节的校验和
println!("checksum {}", packet.get_checksum());
// 2 字节的紧急指针
println!("urgent_ptr {}", packet.get_urgent_ptr());
// TCP 20 字节的首部,可以加上最长 40 个字节的选项
println!("options {:?}", packet.get_options());
println!("length: {}, {:?}", size, &buffer[..size]);
}
}
发送 TCP 请求
当想建立连接时,会发起握手请求,发送第一个 TCP 数据包。
root@jiangbo12490:~# nc 192.168.44.244 4444
程序输出
source 35788
destination 4444
sequence 3640119208
acknowledgement 0
data_offset 10
reserved 0
flags 2
window 64240
checksum 41834
urgent_ptr 0
options [TcpOption { number: TcpOptionNumber(2), length: [4], data: [5, 180] }, TcpOption { number: TcpOptionNumber(4), length: [2], data: [] }, TcpOption { number: TcpOptionNumber(8), length: [10], data: [188, 204, 204, 53, 0, 0, 0, 0] }, TcpOption { number: TcpOptionNumber(1), length: [], data: [] }, TcpOption { number: TcpOptionNumber(3), length: [3], data: [7] }]
length: 60, [69, 0, 0, 60, 99, 220, 64, 0, 64, 6, 252, 10, 192, 168, 44, 144, 192, 168, 44, 244, 139, 208, 17, 92, 216, 247, 207, 168, 0, 0, 0, 0, 160, 2, 250, 240, 163, 106, 0, 0, 2, 4, 5, 180, 4, 2, 8, 10, 188, 204, 204, 53, 0, 0, 0, 0, 1, 3, 3, 7]
tcpdump 抓包
root@jiangbo12490:~# tcpdump -A -n -i tun0
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
20:22:54.371126 IP 192.168.44.144.35788 > 192.168.44.244.4444: Flags [S], seq 3383309069, win 64240, options [mss 1460,sackOK,TS val 3167330987 ecr 0,nop,wscale 7], length 0
E..<q.@.@..B..,...,....\..3.........x..........
............
其中的 flags 和 options 字段,之后再看。
Wireshark
总结
了解了 TCP 协议的字段,该协议基于 IP 协议。