From owner-freebsd-net@FreeBSD.ORG Wed May 31 20:23:42 2006 Return-Path: X-Original-To: freebsd-net@freebsd.org Delivered-To: freebsd-net@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id DE01D16B14C for ; Wed, 31 May 2006 20:23:41 +0000 (UTC) (envelope-from bde@zeta.org.au) Received: from mailout1.pacific.net.au (mailout1.pacific.net.au [61.8.0.84]) by mx1.FreeBSD.org (Postfix) with ESMTP id 63D6C43D6D for ; Wed, 31 May 2006 20:23:40 +0000 (GMT) (envelope-from bde@zeta.org.au) Received: from mailproxy1.pacific.net.au (mailproxy1.pacific.net.au [61.8.0.86]) by mailout1.pacific.net.au (Postfix) with ESMTP id 06A37727FC3; Thu, 1 Jun 2006 06:23:39 +1000 (EST) Received: from katana.zip.com.au (katana.zip.com.au [61.8.7.246]) by mailproxy1.pacific.net.au (8.13.4/8.13.4/Debian-3sarge1) with ESMTP id k4VKNYbi028175; Thu, 1 Jun 2006 06:23:37 +1000 Date: Thu, 1 Jun 2006 06:23:34 +1000 (EST) From: Bruce Evans X-X-Sender: bde@delplex.bde.org To: Emil Kondayan In-Reply-To: <200605312157.04027.emil@ekon-bg.com> Message-ID: <20060601054321.H32147@delplex.bde.org> References: <200605291103.k4TB38uo097477@freefall.freebsd.org> <200605312157.04027.emil@ekon-bg.com> MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII; format=flowed Cc: freebsd-net@freebsd.org Subject: Re: Question about netinet/ip.h X-BeenThere: freebsd-net@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Networking and TCP/IP with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 31 May 2006 20:23:49 -0000 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