Unix/Linux socket collation -- address structure

Posted by lilywong on Fri, 18 Feb 2022 21:23:57 +0100


Students who have read UNIX network programming know that this book introduces all aspects of sockets in detail, and gives each structure
Implementation code file of system API. However, with the continuous development of technology, the kernel of the operating system is constantly updated. The book describes the relevant address structures, and
The specific implementation source files sometimes do not match.
In view of this, based on the description in the book, this paper takes relevant notes in combination with the linux operating system, which is used for reading and easy to find.


  • Book UNIX network programming (Volume I) version 3
  • Operating system platform: Centos8 [Linux Centos 3.10.0-1160.49.1.el7.x86_64]
    If specified, the content of this article is based on this operating system platform.


IPv4 socket address structure:

sokcaddr_in, definition file: / usr / include / netinet / in h

29 /* Internet address.  */
30 typedef uint32_t in_addr_t;
31 struct in_addr
32   {
33     in_addr_t s_addr;
34   };

237 /* Structure describing an Internet socket address.  */
238 struct sockaddr_in
239   {
240     __SOCKADDR_COMMON (sin_);
241     in_port_t sin_port;         /* Port number.  */
242     struct in_addr sin_addr;        /* Internet address.  */
244     /* Pad to size of `struct sockaddr'.  */
245     unsigned char sin_zero[sizeof (struct sockaddr)
246                - __SOCKADDR_COMMON_SIZE
247                - sizeof (in_port_t)
248                - sizeof (struct in_addr)];
249   };

__ SOCKADDR_COMMON and__ SOCKADDR_COMMON_SIZE is a macro definition,
Its definition file: / usr / include / bits / SOCKADDR h.

34 #define __SOCKADDR_COMMON(sa_prefix) \
35   sa_family_t sa_prefix##family
37 #define __SOCKADDR_COMMON_SIZE  (sizeof (unsigned short int))

So actually sokcaddr_in structure is defined as follows:

struct sokcaddr_in
  sa_family_t sin_family;
  int_port_t sin_port;
  struct in_addr sin_addr;

  unsigned char sin_zero[sizeof (struct sockaddr)
      - (sizeof (unsigned short int))
      - sizeof (in_port_t)
      - sizeof (struct in_addr)];

sa_family_t is defined in: / usr / include / bits / SOCKADDR h:

27 /* POSIX.1g specifies this type name for the `sa_family' member.  */
28 typedef unsigned short int sa_family_t;

An unsigned short integer with a length of 16 bits.

in_port_t is defined in: / usr / include / netinet / in h.

118 /* Type to represent a port.  */
119 typedef uint16_t in_port_t;

Unsigned 16 bit integer representing the port number. Under IPv4, the port number is always saved in network byte order.

Universal socket address structure:

The emergence of general socket is to meet that the socket function can handle the socket structure address from any supported protocol family. For C language
For example, a direct void * pointer is enough, but ANSI C did not appear when sockets appeared (sockets first appeared in 1982),
Therefore, the designer defines a general address structure struct sockaddr. Currently, the structure is defined in:

177 /* Structure describing a generic socket address.  */
178 struct sockaddr
179   {
180     __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
181     char sa_data[14];       /* Address data.  */
182   };

Expand macro__ SOCKADDR_COMMON definition, and finally struct sockaddr is defined as follows:

struct sockaddr
  sa_family_t sa_family;
  char sa_data[14];

Therefore, socket is defined as taking a pointer to a general socket address structure as one of its parameters. This requires calls to these functions
The pointer of the address structure specifying a specific protocol needs to be cast into a pointer to a general socket address structure to adapt to
Function parameter requirements.

IPv6 socket address structure

The socket address structure of IPv6 protocol is: struct sockaddr_in6,
Defined in: / usr / include / netinet / in h.

211 /* IPv6 address */
212 struct in6_addr
213   {
214     union
215       {
216     uint8_t __u6_addr8[16];
217     uint16_t __u6_addr16[8];
218     uint32_t __u6_addr32[4];
219       } __in6_u;
220 #define s6_addr         __in6_u.__u6_addr8
221 #ifdef __USE_MISC
222 # define s6_addr16      __in6_u.__u6_addr16
223 # define s6_addr32      __in6_u.__u6_addr32
224 #endif
225   };

253 struct sockaddr_in6
254   {
255     __SOCKADDR_COMMON (sin6_);
256     in_port_t sin6_port;    /* Transport layer port # */
257     uint32_t sin6_flowinfo; /* IPv6 flow information */
258     struct in6_addr sin6_addr;  /* IPv6 address */
259     uint32_t sin6_scope_id; /* IPv6 scope-id */
260   };

The actual definitions after macro expansion are as follows:

 struct sockaddr_in6
     sa_family_t sin6_family;
     in_port_t sin6_port;    /* Transport layer port # */
     uint32_t sin6_flowinfo; /* IPv6 flow information */
     struct in6_addr sin6_addr;  /* IPv6 address */
     uint32_t sin6_scope_id; /* IPv6 scope-id */

New universal socket structure

struct sockaddr_ The storage structure overcomes the shortcomings of the old struct SOCKADDR general socket structure.
The new structure is sufficient to accommodate any socket address structure supported by the system.
It is defined in the header file: / usr / include / bits / socket h.

185 /* Structure large enough to hold any socket address (with the historical
186    exception of AF_UNIX).  */
187 #define __ss_aligntype  unsigned long int
188 #define _SS_PADSIZE \
189   (_SS_SIZE - __SOCKADDR_COMMON_SIZE - sizeof (__ss_aligntype))
191 struct sockaddr_storage
192   {
193     __SOCKADDR_COMMON (ss_);    /* Address family, etc.  */
194     char __ss_padding[_SS_PADSIZE];
195     __ss_aligntype __ss_align;  /* Force desired alignment.  */
196   };

__ SS_SIZE and__ SOCKADDR_COMMON_SIZE. Are defined in: / usr / include / bits / SOCKADDR h

#define __SS_SIZE 128

#define __SOCKADDR_COMMON_SIZE        (sizeof (unsigned short int))

Therefore, after macro expansion, the structure is defined as follows:

struct sockaddr_storage
  sa_family ss_family;
  char __ss_padding[128-sizeof(unsigned short int)-sizeof(unsigned long int)];
  unsigned long int __ss_align;

[Abstract] sockaddr_ There are two differences between the general socket address structure provided by storage type and sockaddr.

  • 1. If any socket address structure supported by the system needs alignment, sockaddr_storage can meet the most demanding alignment requirements.
  • 2,sockaddr_storage is the largest enough to accommodate any socket address structure supported by the system.
    Note: except SS_ Other fields of family are transparent to users. sockage_ The structure must be converted to storage or forced to copy
    Suitable for SS_ Other fields can only be accessed in the socket address structure of the address type given by the family field.

Topics: Linux Unix server