使用位域解析网络数据包

时间:2017-07-02 01:09:57

标签: c bit-fields

我编写了一个IPv6头解析器。

我想知道是否可以使用位域解析版本,流量类和流量控制标签。

我写了一些测试代码。在x86系统上执行我得到了意想不到的结果。

#include <stdint.h>
#include <stdio.h>

typedef struct __attribute__ ((__packed__)) {
        uint32_t                flow_label:20;
        uint32_t                traffic_class:8;
        uint32_t                ip_version:4;
} test_t;

int main(int argc, char **argv)
{
        uint8_t data[] = { 0x60, 0x00, 0x00, 0x00 };
        test_t  *ipv6 = (void *)data;

        printf("Size is %zu, version %u, traffic class %u, flow label %u\n", sizeof(test_t), ipv6->ip_version, ipv6->traffic_class, ipv6->flow_label);
}

我希望第一个半字节在ip_version中可用,但它似乎不是,而是我得到:

Size is 4, version 0, traffic class 0, flow label 96

或字段顺序倒置

Size is 4, version 0, traffic class 6, flow label 0

有人可以解释为什么会这样吗?

1 个答案:

答案 0 :(得分:4)

对于位域,它的实现取决于它们的布局方式。您最好为数据包的开始声明一个32位字段,并使用位移来提取相关字段。

uint8_t ipver = data[0] >> 4;
uint8_t tclass = ((data[0] & 0xf) << 4) | (data[1] >> 4);
uint32_t flowlbl = (((uint32_t)data[1] & 0xf) << 16) | ((uint32_t)data[2] << 8) | data[3];

实际上,即使Linux netinet / ip6.h头文件也没有为ipv6标头使用位字段:

struct ip6_hdr
  {
    union
      {
    struct ip6_hdrctl
      {
        uint32_t ip6_un1_flow;   /* 4 bits version, 8 bits TC,
                    20 bits flow-ID */
        uint16_t ip6_un1_plen;   /* payload length */
        uint8_t  ip6_un1_nxt;    /* next header */
        uint8_t  ip6_un1_hlim;   /* hop limit */
      } ip6_un1; 
    uint8_t ip6_un2_vfc;       /* 4 bits version, top 4 bits tclass */
      } ip6_ctlun;
    struct in6_addr ip6_src;      /* source address */
    struct in6_addr ip6_dst;      /* destination address */
  };
相关问题