A brief description of how packet routing works on the Internet
When you make a request over the internet, the underlying network needs to know how to get that request to its intended destination, and where to send the response to so that you see it. This means that everything, every single packet, needs a destination address and a source address. There’s also a port number, which we won’t worry too much about yet because it is largely the same in IPv6 as in IPv4.
For a connection between two computers connected by an ethernet cable, this isn’t too difficult; packets either route to localhost or to the other computer. But it gets more complex when multiple network hops are involved. Now there is also the case where packets are forwarded, so they don’t go directly to a destination but instead go to a relay that then sends them on to the next network node until the packets eventually get where they’re headed.
Starting with classic IPv4, here’s what an example IP address might look like: 147.182.154.234. The dots separate bytes, and addresses are 4 bytes long. Each byte can contain values between 0 and 255. A subnet is specified as a prefix and the number of bits in the prefix; your home LAN subnet might look like 192.168.1.0/24, and to the router this means that it is in charge of routing traffic to and from any IP address starting with this prefix.
The Problem with IPv4
The issue with IPv4, broadly, is address space exhaustion. We should in theory have 2^32 = 4,294,967,296 addresses available. You might notice immediately that this is less than the population of the Earth, so not everyone can have a dedicated IPv4 address, but the problem is worse than that. A few addresses are reserved for good reason: 255.255.255.255 for example is reserved for broadcast purposes. 127.0.0.1 is reserved for localhost. But actually, for some reason, the entire 127.0.0.0/8 subnet, consisting of 16,777,216 addresses, is reserved soley for localhost! This is a lot of addresses to dedicate to loopback connections.
Practically speaking, there are about 3.7 billion general-purpose non-reserved IPv4 addresses. It sounds like that’s still a lot, and certainly the people who made IPv4 back in 1982 thought so. But at the time Apple IIs were selling like hotcakes and it was unthinkable that a single person would have more than one network-connected device.
Nowadays having only one Internet-connected device seems absurd, not just to technologists but to average people as well. Your desktop or laptop computer is connected to the Internet, sure, but so is your smartphone, your tablet, and maybe your smart TV and home security cameras, too. In order to have bidirectional connectivity, all of those devices need an address where they can receive packets, and well, there aren’t that many addresses with IPv4.
NAT: The Hack that Keeps IPv4 Working
Okay, so there are too many devices and not enough addresses. But the infrastructure that everything is running on only speaks IPv4, so what can we do? Well, we have a lot of ports that we can use, specifically there are 65,536 (2^16) such ports for each of TCP and UDP. The first 1024 are reserved for administrators, and several others are reserved for specific protocols, but those aren’t the ports that interest us. To be on the safe side, let’s say we have 60,000 ports that we can play with. HTTP is what most users will be interested in using, and that runs on TCP, so we don’t care about the UDP ports for right now. Using these ports, we can divvy up one IPv4 address over many devices.
Here’s the plan: first, we use some of those reserved addresses from IPv4, which we know will never map to anything on the public internet, and we assign our own devices one of those reserved addresses. Most commonly, this is somewhere in the 192.168.0.0/16 subnet, but there are a few other options we could use as well. This lets our devices communicate with each other, and with the device assigning the addresses (the router). The router then has not just the network card that lets it talk to devices on the LAN, but also a second network card, with a different (and public) IPv4 address. The router then does Network Address Translation, so when an outbound request comes from some device on the LAN, the router rewrites the source and port number so that the router is now seen as the source of the packets, and forwards the request that the user wants to where it’s meant to go. Now when a response comes back, the response doesn’t go directly to the user’s device, but rather to the router. Here’s where the extra ports come in: because of the port that the response came back on, the router can now determine which device to forward the response to. So the router then rewrites the response packet so it’s what the user’s device expects, and forwards it to the user’s device.
This works! It works surprisingly well, in fact, to the point that it’s the dominant paradigm in home networking.
So, What’s the Problem?
Just because we worked around the limitations of IPv4 doesn’t mean they went away. There are a few downsides to NAT routing, one of which is that if you host a service on your local device no one can connect to it until you configure the router specifically for it. Think about hosting a video game server on your desktop computer. With NAT, you need to go into your router settings and forward the correct port before anyone can connect to it. There have been a few attempts at resolving this, like Universal Plug and Play, but they come with security considerations and other headaches. Additionally, think of a large organization like a University: they’re going to need more than one IPv4 address to support the thousands of students and researchers on campus, and they need a means of segregating external and internal network traffic.
The problem has been mitigated, but not resolved.
Enter IPv6
The Internet Engineering Task Force realized this would be a problem back in the 1990s and came up with a solution: IPv6. If we’re running out of addresses and we’ve allocated too many reserved addresses, then the obvious proper solution is to have a longer address and be more careful about what gets reserved. So in 1998 IPv6 was introduced as a draft standard.
However, it wasn’t widely adopted. Why not? A few reasons, starting with the fact that IPv4 with NAT routing was working just fine. This was a time of trying to get as many people connected as cheaply and quickly as possible, when there were still many IPv4 addresses to be had, and there was no driving market force that made a compelling case for IPv6. No one was going to get rich changing their infrastructure to use it, and the problems it addresses were too far in the future to be a concern to most people. Humans love to procrastinate, and this was no exception.
It’s finally time
Over the years a few things have happened: more people have more devices connected to the internet; optical fiber is widespread for networking; programming libraries and infrastructure like DNS have been updated to fully support IPv6. IPv6 also finally got ratified as standard (in 2017). ISPs have slowly and incrementally been adding IPv6 support to their infrastructure. And now if you sign up for the most popular ISP in your area, the router or modem they give you will most likely support and use IPv6 out of the box. It will support IPv4 too, of course, for compatibility with older devices. But chances are if you haven’t made any changes to the settings and you have a relatively modern device, it’ll be connecting to the internet over IPv6, and only using IPv4 as a fallback.
Putting it into practice for a website
I’m in the website development and hosting business, and what this means for me is that I need to make sure users get the best, fastest, and most reliable experience possible. So I need my websites and infrastructure to support IPv6 whenever my users have it, and a fallback to IPv4 just in case. The barrier to entry has gone down substantially, and it usually only takes a few configuration changes to implement IPv6 support.
First, you need a hosting provider which supports IPv6; most of them do nowadays, but it’s good to check. Some have restrictions depending on what operating system you’re running on your server. You’ll need to log in to copy the IPv6 address that got assigned to your server, so you know where to point DNS to.
Next, you need to create an AAAA record in your DNS settings. An AAAA record is just like an A record, but for IPv6 instead of IPv4. Keep your A record in place, as a fallback, but expect most of your traffic to come over IPv6. CNAMEs work with IPv6 by default, so no changes are needed there; your CNAME will automatically apply to IPv6 once your AAAA record is in place for whereever the CNAME points to. Wait for the DNS to propagate.
If you’re like me and you’re running websites, you’ll want to configure your websites to listen over IPv6. In nginx, this means adding a listen [::]:443 ssl http2 line to your server block. HTTP should redirect to HTTPS (please use TLS, folks), so we want that redirect to work for IPv6 and that requires another line like listen [::]:80 in the HTTP section of your config. The settings might be a little different for Apache2 but the basic concept should apply to any web server. Restart your web server program to apply the changes.
If you have firewall rules in place, it’s important to add a setting to allow IPv6 connections over ports 80 and 443 (or any other ports you’re using) so your firewall doesn’t block these incoming IPv6 connections. I use ip(6)tables, so for me this means adding a rule in my ip6tables configuration to allowing incoming connections on those TCP ports, something like the following: sudo ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT ; sudo ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT. iptables is a whole topic in and of itself, so I won’t go into details about it here. If you’re curious, let me know and I might write a separate article about it.
If you have web services behind reverse proxy layers, you’ll want to make the proxy layer also go over IPv6. How you do this varies a lot with the backend and network configuration that you use. For an nginx reverse proxy running on the same computer as a backend service, you can use proxy_pass http://ip6-localhost:<port-number>/; in your server configuration location block. On the backend side, if we’re using fastapi with uvicorn then we can update the uvicorn.run call to be something like uvicorn.run('server:backend_app_name',host=['::1','127.0.0.1'],port=args.port). The point of this is to make the entire connection end-to-end IPv6 wherever possible.
Lastly, it’s a good idea to regenerate SSL certificates and make sure that certbot or any other tools you’re using will recognize your website at the new IPv6 address. And of course, TEST IT; use a device with an IPv6 connection and make sure everything loads and you don’t see any unexpected errors. Make sure IPv4 still works too, because there’s no reason not to support it in addition to IPv6.
Closing Thoughts
IPv6 is a good solution to a problem that wasn’t widely recognized or understood at the time that IPv6 was first proposed. So people put off its adoption because there wasn’t an obvious reason to have it. That time has passed, and behind the scenes your ISPs, operating system developers, hosting providers, and most of the websites you go to have all added support for IPv6. It’s time to stop treating IPv6 as a second-class citizen and time to recognize that it’s the primary way people will access your information online. Make your services support it, and make it the default. There’s really no reason not to.
Share this post: