Fields in a struct skipping bytes

2019-04-11 21:13发布

问题:

I have a struct I have written which is supposed to represent an entire UDP packet, with the ethernet header and all. Here it is:

#pragma pack(1)
struct UDPPacket {
    // an array to hold the destination mac address of the packet
    unsigned char dstmac[6];

    // an array to hold the source mac address of the packet
    unsigned char srcmac[6];

    // two bytes to hold the packet type, this is almost always IP (08 00)
    WORD ethtype;

    // each of the subfields of this take up 4 bits. ver, the first half,
    // is the ip version, which should usually be 4 for ipv4, and the second
    // is the length of the header divided by 4, which is almost always 5
    struct {
        unsigned ver : 4;
        unsigned len : 4;
    } verlen;

    // this isn't used in ipv4 and is 0
    BYTE tos;

    // the total length of the header + data
    WORD iplen;

    // the id of this datagram for reassembling fragmented packets
    WORD id;

    // the first subfield occupies 3 bits and is the flags of this packet, which is usually 0
    // the second subfield is the fragmentation offset for large datagrams that have been split up for sending, usually 0
    struct {
        unsigned flags : 3;
        unsigned fragmentation : 13;
    } flagfrag;

    // time to live; usually 35 or 128
    BYTE ttl;

    // the protocol with which this packet is being transported
    // 1 = ICMP, 2 = IGMP, 6 = TCP, 17 = UDP
    BYTE protocol;

    // the ip checksum of this packet
    WORD ipchecksum;

    // the source ip of this packet
    DWORD src;

    // the destination ip of this packet
    DWORD dest;
    // the port from which this packet is coming
    WORD srcport;

    // the port this packet is headed to
    WORD destport;

    // the length of the udp header + data, not including the ip header
    // so it's usually basically iplen - 20
    WORD udplen;

    // the udp checksum of this packet
    WORD udpchecksum;

    // a char pointer to the data of the packet
    unsigned char data[10000];
};
#pragma pack()

Of course, this being a representation of a real UDP packet, the bytes have to be at the same offset as they would be in a packet, and pointers to this type of struct will be cast to unsigned char*s for sending.
My problem is that when I try to assign anything after UDPPacket.verlen, it skips ahead about 5 bytes and starts there. For instance, when I assign the iplen feild, rather than setting the bytes at offsets 16 and 17, it assigns them at something like 23 and 24 (I can't say exactly because I don't have my program available here on my phone).
Is there an apparent reason for this that I'm missing, or did I just do something wrong?

回答1:

Your #pragmas look right. Bitfields don't "auto-pack" to the smallest type that fits the number of bits explicitly specified. I suspect that verlen is taking your given type "unsigned" and assuming that it's a bitfiend of size unsigned int, which sounds like 32 bits in your compiler. Try making the fields of verlen "unsigned char" instead.

More here. The ability to specify "unsigned char" here is an MSFT extension (to ANSI C), but should work: http://msdn.microsoft.com/en-us/library/yszfawxh(VS.80).aspx

N.B. Same here goes for flagfrag, which should be "unsigned short".



回答2:

The standard does not require the compiler to pack the bit fields into a single storage unit ( int, char, whatever ). So even with the pragma, I would expect those 2 bitfields to take up 2 chars or ints. I don't have the docs for that compiler - does it say how the pragma affects bitfields ?



回答3:

Your #pragma pack directive is right, but I think the underlying type of your bifields (verlen and flagfrag) are int instead of the char and short you were expecting.



回答4:

You have to check your padding and alignment settings of your compiler.



回答5:

Maybe using the offsetof macro will help you.



回答6:

unsigned is equivalent here to unsigned int, which is 4 bytes. So iplen should be at offset 23, which it sounds like it is.