views:

64

answers:

6

Hey guys,
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?

A: 

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

swegi
+2  A: 

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".

quixoto
Before, I thought it didn't matter what type it was because you're just specifying the number of bits it should take up, but now I think that you specify a type which takes up a certain amount of bytes (and therefore bits), then you can have different bit fields in the struct as long as they all add up to the type you specified before. Is that correct?
Hock
I believe that in C++ that is not an extension but a requirement from the standard. I would have to carefully go through the grammar again, which I might try later tonight if I gather time for it.
David Rodríguez - dribeas
Yes, fundamentally the compiler only cares about the underlying type that you're using as a bitfield; the ability to name individual bits is just a syntactic convenience for you-- it doesn't introduce a new "bit" type or anything like that. It's unlikely (though you should try it or read the docs) that you can have full smaller types in the middle of bitfields in a structure, if that's what you're asking.
quixoto
Ok thanks, and by the way it worked perfectly and fixed the problem. I love SO.
Hock
@Hock: If the number of bits is greater than the type, then extra fields will be added, so `int a:16, b:16, c:16` will take 2 `int` if `sizeof(int)==4`. Note that the order of the fields in a class or struct must be the same, and as such, if there are any intermediate field, even if they could be packed into a single element they won't be: `int a:1, b, c:1` will take 3 `int`. Where the elements of the bitfield are located within the actual field is undefined `int a:1, b:1, c:1;` will take a single `int` but there is no guarantee as to which bit represents each variable.
David Rodríguez - dribeas
A: 

Maybe using the offsetof macro will help you.

celavek
+1  A: 

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 ?

jdu.sg
+1  A: 

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.

dan04
A: 

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

bshields