0227-TCP 請求報文

波尔發表於2024-08-14

環境

  • 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協議

總結

瞭解了 TCP 協議的欄位,該協議基於 IP 協議。

附錄

相關文章