Date: Thu, 1 Jun 2006 06:23:34 +1000 (EST) From: Bruce Evans <bde@zeta.org.au> To: Emil Kondayan <emil@ekon-bg.com> Cc: freebsd-net@freebsd.org Subject: Re: Question about netinet/ip.h Message-ID: <20060601054321.H32147@delplex.bde.org> In-Reply-To: <200605312157.04027.emil@ekon-bg.com> References: <200605291103.k4TB38uo097477@freefall.freebsd.org> <200605312157.04027.emil@ekon-bg.com>
next in thread | previous in thread | raw e-mail | index | archive | help
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
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20060601054321.H32147>