Question about netinet/ip.h

Bruce Evans bde at zeta.org.au
Wed May 31 13:23:49 PDT 2006


On Wed, 31 May 2006, Emil Kondayan wrote:

> Can someone tell me why "ip_hl" and "ip_v" are of type "u_int" when the
> structure is packed and they only fill a byte?

Because ip.h is mostly written in C (!= Gnu C) and bit-fields cannot have
type u_char in C.  From an old draft of C99 (n689.txt):

%      [#8]  A  bit-field  shall have a type that is a qualified or
%      unqualified version of _Bool, signed int, or  unsigned  int.

> And my second question:do these "#define ..." directives allocate space in the
> structure?

No.

> struct ip {
> #if BYTE_ORDER == LITTLE_ENDIAN
> u_int ip_hl:4, /* header length */
> ip_v:4; /* version */
> #endif
> #if BYTE_ORDER == BIG_ENDIAN
> u_int ip_v:4, /* version */
> ip_hl:4; /* header length */
> #endif
> u_char ip_tos; /* type of service */
> u_short ip_len; /* total length */
> u_short ip_id; /* identification */
> u_short ip_off; /* fragment offset field */
> #define IP_RF 0x8000 /* reserved fragment flag */
> #define IP_DF 0x4000 /* dont fragment flag */
> #define IP_MF 0x2000 /* more fragments flag */
> #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
> u_char ip_ttl; /* time to live */
> u_char ip_p; /* protocol */
> u_short ip_sum; /* checksum */
> struct in_addr ip_src,ip_dst; /* source and dest address */
> } __packed;

__packed is a syntax error in C, and shouldn't be needed here since
struct ip is carefully hand-packed.  However, it may help due to Gnu
C's very surprising semantics for u_int bit-fields.  u_char and u_int
bit-fields affected alignment (and thus packing) as follows:

- u_char bit-fields don't affect alignment
- u_int bit-fields result in the struct containing them having the
   same alignment requirements as u_int.

E.g., the follow struct has alignment requirements 1 and thus has size 1
too:

     struct foo { unsigned char x:8; };

but the following struct has alignment requirements 4 (on i386's) and thus
is is padded at the end to size 4 (on i386's):

     struct bar { unsigned int x:8; };

I've never seen this behaviour or more details of it documented.  C only
requires packing bit-fields reasonably contiguously AFAIK.

Struct ip must have size 20, and this happens naturally on machines with
32-bit ints (which is all machines supported by FreeBSD).  On machines
with 64-bit ints, gcc would bogusly pad it to size 24 without the __packed
directive on struct ip.

Even on machines with 32-bit ints, gcc's alignment requirements give
surprising results.  Consider:

     struct baz {
 	    unsigned short x;
 	    struct ip y;
     };

This should have size 22, but without the __packed directive it would
have size 24. There would be unnamed padding before y.

Consider:

     struct boo {
 	    unsigned char x;
 	    struct ip y;
     };

This should have size 22, with unnamed padding before y to align it to
its correct alignment (2, not 4), but _with_ the __packed it has size
21 and the accesses to the shorts in y in it are misaligned.  gcc knows
how to do misaligned accesses and generates extra (slow) code to access
these shorts a byte at a time on machines with strict alignment
requirements (e.g., ia64).  However, if you pass the address of y in
struct boo to a function expecting a "struct ip *", then the requirement
for the extra code is silently lost and misaligned accesses are generated.
Misaligned accesses may also cause bugs by being non-atomic.

The -Wpacked and -Wpadded warnings should be used to inhibit bogus packing.

Bruce


More information about the freebsd-net mailing list