cstructwiresharkbit-fieldsstruct-member-alignment

Extra bits added in bitfield struct in C


I am trying to create a client C code for CAPWAP protocol. I tried implementing CAPWAP header using a bit field structure. But after sending this structure over socket using sendto(), when I sniff the packet using wireshark, I find that some extra bits are added in between. I have no clue from where this came from. Requesting for help. Thanks in advance.

wireshark

I tried commenting last few members of the struct to make it 4 byte aligned. Still the isue persists.

This is the original header

struct cw_header
{
unsigned preamble : 8;
unsigned hlen : 5;
unsigned rid : 5;
unsigned wbid : 5;
unsigned t : 1;
unsigned f : 1;
unsigned l : 1;
unsigned w : 1;
unsigned m : 1;
unsigned k : 1;
unsigned flags : 3;
unsigned fragment_id : 16;
unsigned fragment_offset : 13;
unsigned reserved : 3;
uint32_t mac_length : 8;
uint32_t mac_addr[6];
uint32_t padding : 8;
};

I tried commenting these

//uint32_t mac_length : 8;
//uint32_t mac_addr[6];
//uint32_t padding : 8;

This is where the struct is populated

struct cw_header create_cw_header()
{
struct cw_header cw_header;
cw_header.preamble = 0;
cw_header.hlen = 1;
cw_header.rid = 1;
cw_header.wbid = 1; 
cw_header.t = 0;
cw_header.f = 0;
cw_header.l = 1;
cw_header.w = 0;
cw_header.m = 1;
cw_header.k = 0;
cw_header.flags = 0;    
cw_header.fragment_id = 0;  ;
cw_header.fragment_offset = 0;  
cw_header.reserved = 0; 
cw_header.mac_length = 6;
get_mac_address(cw_header.mac_addr);
cw_header.padding = 0;
return cw_header;
};

Here is the output of wireshark for first 32 bits

0000 0000 0010 0001 0000 0100 0000 1010 

Expected result : Bits should be in the order mentioned in the struct Error : Extra bits are added in between struct members


Solution

  • "Bits should be in the order mentioned in the struct"

    What order? The C language specifies no order of bits. You cannot portably know if preamble is MSB or LSB.

    "Extra bits are added in between struct members"

    Yep, the compiler is free to place padding bits or padding bytes between members of a bitfield. Bitfields are divided in abstract, "invisible" units called "storage unit" by the standard, which have a given size - often the same size as the CPU alignment but not necessarily so. Between different such storage units there may or may not be padding. The compiler is also free to let bits that won't fit in one storage unit get placed in the next one.

    There's no telling what a particular compiler will do without reading up on the compiler's implementation of bitfields. There is almost no support for them by the C standard.

    Add endianess on top of that and you have a proper mess - in your case both CPU endianess and network endianess will matter.

    The best solution to all of these problems is to drop bitfields entirely. Instead you should use bit-wise operators, that are far more well-defined, deterministic and portable:

    uint32_t cw_preable = something << preamble_offset;
    ...
    uint32_t cw_header = 0;
    cw_header |= cw_preamble;
    ...