【问题标题】:Byte order of fields when parsing network header解析网络头时字段的字节顺序
【发布时间】:2020-02-02 09:15:45
【问题描述】:

在解析 TCP 头时,有一个名为数据偏移量的字段,长度为 4 位。解析header时,需要将字段反转为host oder。问题来了:当反转这些不是 16 位或 32 位长的字段时,这意味着我不能使用 ntohsntohl,我是按字段还是按字节反转它们,或者以其他方式?

假设一个字节包含两个字段,f1f2,每个字段大小为 4 位。数据为1000 0100。对于逐场反转,结果应为0001 0010。对于逐字节反转,结果为0010 0001。哪一个是正确的?

更新:

这是我用来解析标题的struct

#pragma pack(push, 1)
struct tcp_hdr_t {
  uint16_t src_port;
  uint16_t dst_port;
  uint32_t seq;
  uint32_t ack;
  uint8_t data_offset : 4;
  uint8_t f_reserved : 3;
  uint8_t f_ns : 1;
  uint8_t f_cwr : 1;
  uint8_t f_ece : 1;
  uint8_t f_urg : 1;
  uint8_t f_ack : 1;
  uint8_t f_psh : 1;
  uint8_t f_rst : 1;
  uint8_t f_syn : 1;
  uint8_t f_fin : 1;
  uint16_t window_size;
  uint16_t checksum;
  uint16_t urgent_p;
};
#pragma pack(pop)

如果我不反转数据偏移量和标志字段,则结果与 Wireshark 的结果相比是错误的。

如您所见,原始数据是0xa002,但结果似乎是0xa,因为数据偏移量不需要反转,但标志部分似乎反转了。

【问题讨论】:

  • 这有助于回答您的问题吗? stackoverflow.com/questions/3022552/…
  • 有点意思,但我仍然对要反转的部分感到困惑。不管怎么说,还是要谢谢你。 :)
  • 8 位或更少 - 无事可做,这完全是关于字节顺序而不是位顺序。 16、32 - 对 64 位使用 ntohs 和 ntohl(根据需要使用相反的值 - 不保证对称) - 使用 htobe64 和 be64toh,但您可能不会在标题中找到它们。

标签: c++ c networking tcp


【解决方案1】:

当你说host byte oder时,你会得到答案。

正如你的问题,如果你有1000 0100

0001 0010 在单个半字节中按位反转,与所有这些无关。

0010 0001 是轻咬反转,这也与这一切无关。

1 半字节 = 4 位
1 字节 = 8 位

来自热门Beej's Guide

只是为了让你真的不开心,不同的计算机在内部对其多字节整数使用不同的字节顺序(即任何大于 char 的整数。)结果是,如果你 send() 一个两字节的短 int 从将 Intel 盒子连接到 Mac(我的意思是,在它们成为 Intel 盒子之前),一台计算机认为是数字 1,另一台计算机认为是数字 256,反之亦然。

解决这个问题的方法是让每个人都抛开分歧,同意摩托罗拉和 IBM 的做法是正确的,而英特尔的做法很奇怪,所以我们都将字节顺序转换为“大端”在将它们发送出去之前。由于英特尔是“小端”机器,因此将我们首选的字节顺序称为“网络字节顺序”在政治上要正确得多。因此,这些函数会从您的本机字节顺序转换为网络字节顺序,然后再转换回来。

如果您只与1 byte 打交道,您甚至不需要费心去做任何事情。

【讨论】:

  • 感谢您的回答。事情看起来不一样。我已经更新了我的问题。
【解决方案2】:

您看到的问题与位字段的实现方式有关。

来自C standard 的第 6.7.2.1p11 节关于结构联合说明符:

一个实现可以分配任何大的可寻址存储单元 足以容纳一个位域。如果有足够的空间,一个位域 紧跟在结构中另一个位字段之后的应该是 打包到同一单元的相邻位中。如果空间不足 仍然,是否放入不适合的位域 下一个单元或与相邻单元重叠的是 实现定义。 内部位域的分配顺序 一个单位(高阶到低阶或低阶到高阶)是 实现定义。 可寻址存储的对齐方式 单位未指定。

这意味着您不能可移植地依赖位字段的顺序。例如,Linux 上的文件 /usr/include/netinet/tcp.h 包含以下内容:

struct tcphdr
  {
    __extension__ union
    {
      struct
      {
    u_int16_t th_sport;     /* source port */
    u_int16_t th_dport;     /* destination port */
    tcp_seq th_seq;     /* sequence number */
    tcp_seq th_ack;     /* acknowledgement number */
# if __BYTE_ORDER == __LITTLE_ENDIAN
    u_int8_t th_x2:4;       /* (unused) */
    u_int8_t th_off:4;      /* data offset */
# endif
# if __BYTE_ORDER == __BIG_ENDIAN
    u_int8_t th_off:4;      /* data offset */
    u_int8_t th_x2:4;       /* (unused) */
# endif
    u_int8_t th_flags;
# define TH_FIN 0x01
# define TH_SYN 0x02
# define TH_RST 0x04
# define TH_PUSH    0x08
# define TH_ACK 0x10
# define TH_URG 0x20
    u_int16_t th_win;       /* window */
    u_int16_t th_sum;       /* checksum */
    u_int16_t th_urp;       /* urgent pointer */
      };
      struct
      {
    u_int16_t source;
    u_int16_t dest;
    u_int32_t seq;
    u_int32_t ack_seq;
# if __BYTE_ORDER == __LITTLE_ENDIAN
    u_int16_t res1:4;
    u_int16_t doff:4;
    u_int16_t fin:1;
    u_int16_t syn:1;
    u_int16_t rst:1;
    u_int16_t psh:1;
    u_int16_t ack:1;
    u_int16_t urg:1;
    u_int16_t res2:2;
# elif __BYTE_ORDER == __BIG_ENDIAN
    u_int16_t doff:4;
    u_int16_t res1:4;
    u_int16_t res2:2;
    u_int16_t urg:1;
    u_int16_t ack:1;
    u_int16_t psh:1;
    u_int16_t rst:1;
    u_int16_t syn:1;
    u_int16_t fin:1;
# else
#  error "Adjust your <bits/endian.h> defines"
# endif
    u_int16_t window;
    u_int16_t check;
    u_int16_t urg_ptr;
      };
    };
};

您可以在这里看到需要跳过的箍,才能将事情放在正确的位置。其他实现可能会有所不同。

在您的代码中处理此问题的最佳方法是去掉位域并用一对uint8_t 成员替换它们,并使用位掩码来提取必要的子域。

例如:

struct tcp_hdr_t {
  uint16_t src_port;
  uint16_t dst_port;
  uint32_t seq;
  uint32_t ack;
  uint8_t offset_flags1;
  uint8_t flags2;
  uint16_t window_size;
  uint16_t checksum;
  uint16_t urgent_p;
};

#define DATA_OFFSET(hdr) (((hdr).offset_flags1 & 0x0f) >> 4)
#define FLAG_NONCE(hdr)  (((hdr).offset_flags1 & 0x01) >> 0)
#define FLAG_CWR(hdr)    (((hdr).flags2 & 0x80) >> 7)
#define FLAG_ECE(hdr)    (((hdr).flags2 & 0x40) >> 6)
#define FLAG_URG(hdr)    (((hdr).flags2 & 0x20) >> 5)
#define FLAG_ACK(hdr)    (((hdr).flags2 & 0x10) >> 4)
#define FLAG_PSH(hdr)    (((hdr).flags2 & 0x08) >> 3)
#define FLAG_RST(hdr)    (((hdr).flags2 & 0x04) >> 2)
#define FLAG_SYN(hdr)    (((hdr).flags2 & 0x02) >> 1)
#define FLAG_FIN(hdr)    (((hdr).flags2 & 0x01) >> 0)

【讨论】:

  • 这真的很有帮助!谢谢!
猜你喜欢
  • 2022-01-22
  • 2014-05-18
  • 1970-01-01
  • 2015-03-30
  • 2021-04-26
  • 1970-01-01
  • 1970-01-01
  • 2013-06-28
  • 1970-01-01
相关资源
最近更新 更多