There are two types of Internet domain socket addresses: IPv4 and IPv6.
An IPv4 socket address is stored in a sockaddr_in structure, defined in <netinet/in.h>
as follows:
struct in_addr { /* IPv4 4-byte address */ in_addr_t s_addr; /* Unsigned 32-bit integer */ }; struct sockaddr_in { /* IPv4 socket address */ sa_family_t sin_family; /* Address family (AF_INET) */ in_port_t sin_port; /* Port number */ struct in_addr sin_addr; /* IPv4 address */ unsigned char __pad[X]; /* Pad to size of 'sockaddr' structure (16 bytes) */ };
In Generic Socket Address Structures: struct sockaddr, we saw that the generic sockaddr structure commences with a field identifying the socket domain. This corresponds to the sin_family field in the sockaddr_in structure, which is always set to AF_INET
. The sin_port and sin_addr fields are the port number and the IP address, both in network byte order. The in_port_t and in_addr_t data types are unsigned integer types, 16 and 32 bits in length, respectively.
Like an IPv4 address, an IPv6 socket address includes an IP address plus a port number. The difference is that an IPv6 address is 128 bits instead of 32 bits. An IPv6 socket address is stored in a sockaddr_in6 structure, defined in <netinet/in.h>
as follows:
struct in6_addr { /* IPv6 address structure */ uint8_t s6_addr[16]; /* 16 bytes == 128 bits */ }; struct sockaddr_in6 { /* IPv6 socket address */ sa_family_t sin6_family; /* Address family (AF_INET6) */ in_port_t sin6_port; /* Port number */ uint32_t sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ uint32_t sin6_scope_id; /* Scope ID (new in kernel 2.4) */ };
The sin_family field is set to AF_INET6
. The sin6_port and sin6_addr fields are the port number and the IP address. (The uint8_t data type, used to type the bytes of the in6_addr structure, is an 8-bit unsigned integer.) The remaining fields, sin6_flowinfo and sin6_scope_id, are beyond the scope of this book; for our purposes, they are always set to 0. All of the fields in the sockaddr_in6 structure are in network byte order.
IPv6 addresses are described in RFC 4291. Information about IPv6 flow control (sin6_flowinfo) can be found in Appendix A of [Stevens et al., 2004] and in RFCs 2460 and 3697. RFCs 3493 and 4007 provide information about sin6_scope_id.
IPv6 has equivalents of the IPv4 wildcard and loopback addresses. However, their use is complicated by the fact that an IPv6 address is stored in an array (rather than using a scalar type). We use the IPv6 wildcard address (0::0
) to illustrate this point. The constant IN6ADDR_ANY_INIT
is defined for this address as follows:
#define IN6ADDR_ANY_INIT { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }
On Linux, some details in the header files differ from our description in this section. In particular, the in6_addr structure contains a union definition that divides the 128-bit IPv6 address into 16 bytes, eight 2-byte integers, or four 32-byte integers. Because of the presence of this definition, the glibc definition of the IN6ADDR_ANY_INIT
constant actually includes one more set of nested braces than is shown in the main text.
We can use the IN6ADDR_ANY_INIT
constant in the initializer that accompanies a variable declaration, but can’t use it on the right-hand side of an assignment statement, since C syntax doesn’t permit structured constants to be used in assignments. Instead, we must use a predefined variable, in6addr_any, which is initialized as follows by the C library:
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
Thus, we can initialize an IPv6 socket address structure using the wildcard address as follows:
struct sockaddr_in6 addr; memset(&addr, 0, sizeof(struct sockaddr_in6)); addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_port = htons(SOME_PORT_NUM);
The corresponding constant and variable for the IPv6 loopback address (::1
) are IN6ADDR_LOOPBACK_INIT
and in6addr_loopback.
Unlike their IPv4 counterparts, the IPv6 constant and variable initializers are in network byte order. But, as shown in the above code, we still must ensure that the port number is in network byte order.
If IPv4 and IPv6 coexist on a host, they share the same port-number space. This means that if, for example, an application binds an IPv6 socket to TCP port 2000 (using the IPv6 wildcard address), then an IPv4 TCP socket can’t be bound to the same port. (The TCP/IP implementation ensures that sockets on other hosts are able to communicate with this socket, regardless of whether those hosts are running IPv4 or IPv6.)
With the IPv6 sockets API, the new generic sockaddr_storage structure was introduced. This structure is defined to be large enough to hold any type of socket address (i.e., any type of socket address structure can be cast and stored in it). In particular, this structure allows us to transparently store either an IPv4 or an IPv6 socket address, thus removing IP version dependencies from our code. The sockaddr_storage structure is defined on Linux as follows:
#define __ss_aligntype uint32_t /* On 32-bit architectures */ struct sockaddr_storage { sa_family_t ss_family; __ss_aligntype __ss_align; /* Force alignment */ char __ss_padding[SS_PADSIZE]; /* Pad to 128 bytes */ };