Presented below are three data structures which help us model IP address/port pairs.
struct sockaddr_in { // IPv4 Internet-style socket address record
unsigned short sin_family; // protocol family for socket
unsigned short sin_port; // port number (in network byte order)
struct in_addr sin_addr; // IP address (in network byte order)
unsigned char sin_zero[8]; // pad to sizeof(struct sockaddr)
};
struct sockaddr_in6 { // IPv6 Internet-style socket address record
unsigned short sin6_family; // protocol family for socket
unsigned short sin6_port; // port number (in network byte order)
// more fields, total size is > sizeof(struct sockaddr_in)
};
struct sockaddr { // generic socket address record
unsigned short sa_family; // protocol family for socket
char sa_data[14]; // address data (and defines full size to be 16 bytes)
};
- The sockaddr_in struct is dedicated to IPv4 address/port pairs.
- The sin_family field should always be initialized to be AF_INET, which is a constant used to be clear that IPv4 addresses are being used.
- If it seems redundant that a record dedicated to IPv4 addresses needs to store a constant tagging the information in the other fields as IPv4, then stay tuned.
- The sin_port field stores a port number in network byte (aka big endian) order.
- The sin_addr field stores an IPv4 address as a packed, big endian int, as you saw with gethostbyname and the struct hostent.
- The sin_zero field is generally ignored (though it's typically set to store all zero bytes). It exists primarily to pad the record up to 16 bytes.
- The sockaddr_in6 struct is dedicated to IPv6 address/port pairs.
- The sin6_family field should always be set to AF_INET6. As with the sin_family field, sin6_family occupies the first two bytes of the record in which it resides.
- The sin6_port field holds a two-byte, network-byte-ordered port number just as sin_port.
- I don't list the remaining fields, but you can imagine they store some representation of the 128-bit IPv6 address.
- The struct sockaddr type exists as C's best imitation of an abstract base class.
- You rarely if ever declare variables of type struct sockaddr, but many system calls will accept parameters of type struct sockaddr *.
- Rather than define a set of networking system calls for IPv4 addresses and a second set of system calls for IPv6 addresses, Linux defines one set for both.
- If a system call accepts a parameter of type struct sockaddr *, it really excepts the address of either a struct sockaddr_in or a struct sockaddr_in6. The system call relies on the value within the first two bytes—the sa_family field—to determine what the real record type is.