使用 tun/tap 界面的错误序列号

wrong sequence number using tun/tap interface

我正在尝试使用 tun/tap 将 TCP 数据包读入结构, 所以 IFF_TUN 标志设置为使用 tun 设备(无以太网 headers)。

我有这样的结构(我不关心字节序问题):

TCP Header:

struct tcphdr {
  uint16_t sport;
  uint16_t dport;
  uint32_t seq;
  uint32_t ack_seq;
  uint8_t rsvd : 4;
  uint8_t dataoff : 4;
  uint8_t fin : 1,
          syn : 1,
          rst : 1,
          psh : 1,
          ack : 1,
          urg : 1,
          ece : 1,
          cwr : 1;
  uint16_t win;
  uint16_t csum;
  uint16_t urp;
} __attribute__((packed));

Ipv4 Header:

struct ipv4hdr {
  uint8_t ihl : 4;
  uint8_t version : 4;
  uint8_t tos;
  uint16_t len;
  uint16_t id;
  uint16_t frag_offset;
  uint8_t ttl;
  uint8_t proto;
  uint16_t csum;
  uint32_t saddr;
  uint32_t daddr;
} __attribute__((packed));

并像这样读取数据包:

size_t nbytes = read(fd, bytes, 1504); // fd is eg. fd = open("/dev/net/tun", O_RDWR)

uint16_t eth_flags = bytes[0] << 8 | bytes[1]; // big-endian
uint16_t eth_proto = bytes[2] << 8 | bytes[3]; // big-endian

if (eth_proto != 0x800)
  // ignore no ipv4 packets
  // https://en.wikipedia.org/wiki/EtherType
  continue;

if (ip_hdr->proto != 0x06)
  // ignore non-TCP packets
  // https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
  continue;

到目前为止,还不错。但是当我尝试读取 TCP 序列号时,我得到了错误的数字:

ipv4hdr *ip_hdr = (ipv4hdr *)(bytes + 4); // 4 first bytes are packet information provided by kernel
tcphdr *tcp_hdr = (tcphdr *)(bytes + 4 + ip_hdr->ihl * 4); // reading TCP at the end of the IP header
std::cout << std::hex << ntohl(tcp_hdr->seq) << std::endl; // the output number is wrong!

tshark 输出显示 seq=0(如下所示):

Capturing on 'tun0'
    1 0.000000000  192.168.0.1 → 192.168.0.2  TCP 60 44248 → 8000 [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=1233752815 TSecr=0 WS=128

但我的代码输出是 7be53b39

经过一番搜索,我找到了解决方案。

参考wireshark wiki:

By default Wireshark and TShark will keep track of all TCP sessions and convert all Sequence Numbers (SEQ numbers) and Acknowledge Numbers (ACK Numbers) into relative numbers. This means that instead of displaying the real/absolute SEQ and ACK numbers in the display, Wireshark will display a SEQ and ACK number relative to the first seen segment for that conversation.

每次对话中看到的第一个数据包的所有序列号始终从 0 开始,所以我的输出没有错:)