Search code examples
cbit-fields

Using bitfields for parsing network packets


I have the thankless job of writing an IPv6 header parser.

I'm wondering if the version, traffic class and flow control labels could be parsed out using bitfields.

I wrote some test code. Executing on an x86 system I get unexpected results.

#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);
}

I'd expect the first nibble to be available in ip_version, but it doesn't seem to be, instead I get:

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

or with the field order inverted

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

Can anyone explain why this happens?


Solution

  • With bitfields, it's implementation dependent how they are laid out. You're better off declaring a 32 bit field for the start of the packet and using bit shifting to extract the relevant fields.

    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];
    

    Indeed, even the Linux netinet/ip6.h header doesn't use a bit field for the ipv6 header:

    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 */
      };