- The Point of IPv6
- Secure by Default
- Sockets and Protocols
- IPv6: The Protocol of the Present
Sockets and Protocols
Hopefully now you can see that supporting IPv6 is useful. The question then becomes how? Most networking code either uses Berkeley sockets or a high-level API built on top of them. If you're using a high-level API, then you probably already have IPv6 support provided by someone else. If you're using sockets, then you need to make some small changes to your code.
The Berkeley socket API was based on the UNIX idea that everything is a file. You communicate with remote machines using file descriptors with some special features. Once you have a socket, the API is completely protocol-agnostic. Whether you're using IPv4, IPv6, AppleTalk, or some other protocol, the code is the same.
Unfortunately, creating sockets is where things start to go wrong. You create a socket with this function:
int socket(int domain, int type, int protocol);
The three parameters all define some aspect of the protocol. Most of the time, you end up hard-coding the domain as PF_INET, meaning IPv4. Things then go even more wrong when you bind the socket to a local address and then either connect or accept connections. All of the relevant functions take a pointer to a sockaddr structure as an argument.
This structure is quite simple, but you never actually use it directly. Instead, you use something like a sockaddr_in structure, which starts the same way as a sockaddr structure (with a size and address family) but then contains an IPv4 address and port number. You usually use the gethostbyname() function to look these up.
If you want to support another protocol, then you needed to add a different code path with a different lookup mechanism. One of the recent improvements in the 2004 version of POSIX was the definition of the getaddrinfo() function. This looks up a server identified by a string description of an address and a service. This means that you don't have to hardcode port numbers either. Using it is quite simple. This code will connect to the InformIT HTTP server using any available connection:
struct addrinfo hints, *results; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; int error = getaddrinfo("informit.com", "http", &hints, &results); if (error) { /* fail */ return -1; } int s = -1; for (struct addrinfo *res = results; res != NULL && s < 0 ; res = res->ai_next) { s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //If the socket failed, try the next address if (s < 0) { continue ; } //If the connection failed, try the next address if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { close(s); s = -1; continue; } } freeaddrinfo(results); return s;
The first two arguments to getaddrinfo() are the name of the server and the name of the protocol. The resolver will look up the address of the server and service combination. On some platforms, such as OS X, this even includes resolving DNS SRV records, so this will set the port correctly even if the server is running on a non-standard port, as long as the DNS entry advertises that fact. The hints structure is used to specify some constraints on the returned address info. In this case, we only want stream sockets, and we're willing to accept them in absolutely any protocol the underlying network stack supports. The final argument is a pointer that is used to return an array of address info structures.
Note that all of the arguments to socket() and connect() are all from the address info returned by the getaddrinfo() call. None of this code is at all aware of whether you are using IPv4, IPv6, or some other new protocol. As long as your operating system supports a protocol, this code can use it.