Getting Android to Work in an IPv6-Only Network

As previously covered, I recently set up NAT64 on my network so that I didn't need to assign IPv4 addresses to all of my subnets or hosts and they'd still have a usable connection to the Internet at large.

Unfortunately, functional NAT64 is only most useful when hosts can auto-configure themselves with IPv6 addresses, default routes, and DNS64 name servers to resolve with. Figuring out how to get this to work, and then figuring out the workarounds required to get Android to work, took some time...

What Was Intended

Since my network is running off a Cisco 6506, which is a relatively ancient Ethernet switch, it isn't running what you might call the latest and greatest version of an operating system, so it took some historical research to figure out how IPv6 host configuration was even supposed to work at the time IPv6 was implemented on the 6500:

  • A new host connects to the network, and sends out a neighbor discovery protocol/ICMPv6 router solicitation to discover the local IPv6 subnet address and the address of a default gateway to reach the rest of the Internet.
  • The router(s) respond with the local subnet and default gateway, but the ND protocol did not originally include any way to also configure DNS servers, so the router would set the "other-config" flag, which tells the ND client that there is additional information they need that they should query over stateless DHCPv6.
  • The client, now having a local IPv6 address, would then send out a stateless DHCPv6 query to get the addresses for some recursive DNS servers, which the router would answer.
  • The client would now have a self-configure SLAAC address, default gateway, and RDNSS (recursive DNS server), which together enable it to usefully interact with the rest of the Internet.
Great! How do you implement this in IOS?

First you need to define an IPv6 DHCP pool, which ironically isn't really an address pool at all, but just some DNS servers and a local domain name. Realistically, it could be a pool, since IOS does support prefix delegation, but we're not using that here, so we just define what DNS server addresses to use and a local domain name if we felt like it:

ipv6 dhcp pool ipv6dns
 dns-server 2001:4860:4860::6464
 dns-server 2001:4860:4860::64

Since this pool doesn't even really have any state to it, other than maybe defining a different domain-name per subnet, you can reuse the same pool on every subnet that you want DHCPv6 service on, which is what I'm doing on my router, since the domain-name doesn't really make any difference:

interface Vlan43
 description IPv6 only subnet
 no ip address
 ipv6 address 2001:DB8:0:1::1/64
 ipv6 nd other-config-flag
 ipv6 dhcp server ipv6dns

IOS sends out router advertisements by default, so to enable handing out addresses and default gateways we just need to not disable that, but the "ipv6 nd other-config-flag" option is what sets the bit in the router advertisements to tell clients to come back over DHCPv6 to also ask for the DNS server addresses. 

Now, before I outline the pros and cons for this design, I will disclaim that this is my perspective on the issue, so I'm not speaking from a place of authority, having not yet graduated elementary school when all of this originally was designed... This back and forth of ND - DHCPv6 does have some upsides:
  • Since both the ND and DHCPv6 queries are "stateless", all that the router/DHCP server is doing is handing out information it knows the answers to, and isn't adding any per-device information into any sort of database like a DHCP lease table, so a single router could now conceivably assign address to a metric TON of devices.
  • The separation of the DNS configuration from the IPv6 addressing configuration preserves the elegant separation of concerns that protocol designers like to see because it makes them feel more righteous.
There are also some pretty serious downsides:
  • Instead of just a DHCP server, you now need both a correctly configured router advertisement for the L3 addressing information and a correctly configured DHCPv6 server to hand out DNS information.
  • I still don't understand how hostname DNS resolution is supposed to work for this. In IPv4 land, you use something like dnsmasq which both hands out the DHCP leases and then resolves the hostnames back to those same IP addresses. Since all of this host configuration in IPv6 is stateless, by design DNS can't work... Maybe the presumption was that dynamic DNS wouldn't turn out to be a still-born feature?
  • The Android project, for reasons which defy understanding, refuses to implement DHCPv6. 
That last point is a hugely serious issue for my network, since without DHCPv6, there is no mechanism for my Cisco 6506 to communicate to my phone what DNS servers to use over IPv6. My phone gets an IPv6 address and default gateway from my Cisco's router advertisement ICMPv6 packet, and then ignores the "other-config" flag, and is left without any way to resolve DNS records. 

Making the network... you know... useless.

For the record, how Android is presumed to work is by utilizing a later addition to the ICMPv6 router advertisement format, RFC6106, which added a Recursive Domain Name Service Server (RDNSS) option to the router advertisement to allow DNS information to be included in the original RA broadcast along with the local subnet and default gateway addressing information. Unfortunately, since this addition to ICMPv6 was made about fifteen years late, RDNSS options aren't supported by the version of IOS I'm running on my Cisco 6506, so it would seem I'm pretty shit out of luck when it comes to running Android on my IPv6-only subnets of my network.

My (Really Not) Beautiful Solution

So we've got a router that doesn't support the RDNSS option in its router advertisements since it predates the concept of RA RDNSS, and we have one of the most popular consumer operating systems which refuses to support DHCPv6, leaving us as an impasse for configuring DNS servers. I actually spent a few weeks thinking about this one, including slowly digging deeper and deeper into the relevant protocols, before I eventually was reading the raw specifications for the ICMPv6 router advertisement packets (kill me), and realized that there was the ability to have a router broadcast Router Advertisement packets while indicating in the RA packet that they shouldn't be used for routing.
So here's my solution, which admittedly even I think feels a little dirty, but an ugly solution that works... still works.

  • My Cisco 6506 sends out Router Advertisements specifying the local subnet, and that it should be used as a default gateway.
  • I then spun up a small Linux VM on the same subnet running radvd, which advertises that this VM shouldn't be used as a default gateway, but does advertise an RDNSS option pointing to my DNS64 resolver as the DNS server to use, since radvd supports RDNSS.
  • Any normal functional IPv6 stack will receive the Cisco's RA packet, note the "other-config" flag, and send a DHCPv6 query to receive the address for a DNS server.
  • Android receives the Cisco's RA, configures its address and gateway, but ignores the "other-config" flag. The phone will then receive a second RA packet from the Linux server running radvd, which includes the RDNSS option which I'm not able to configure on my router, and Android maybe will merge the two RA configurations together to collectively generate a functional subnet, gateway, and DNS server configuration for the network.
Now let us be very clear; while I was setting this up, I was very confident that this was not going to work. Expecting Android to receive two different RAs from two different hosts with only parts of the information it needed, and combining those together, seems like an insane solution to this problem.

The bad news is that this actually worked.

So we spin up a VM, install radvd on it, and assign it one network interface on my WiFi vlan. The /etc/radvd.conf file is relatively short:
kenneth@kwfradvd:/etc$ cat radvd.conf 
interface ens160 {
AdvSendAdvert on;
IgnoreIfMissing on;
AdvDefaultLifetime 0;
RDNSS 2001:4860:4860::6464 {};
RDNSS 2001:4860:4860::64 {};

The network interface happens to be ens160, the "AdvDefaultLifetime 0;" parameter indicates that this router shouldn't be used as a default gateway, and the two RDNSS parameters specify which IP addresses to use for DNS resolution. Here I show it with both of Google's DNS64 servers, but in reality I'm running my own local DNS64 server, because I'm directly peered with two root DNS servers and b.gtld, so why not run my own resolver?

I still feel a little dirty that this works, but it does! I then installed an access point in each of my data center cabinets on this vlan advertising the SSID "_Peer@FCIX" so we're both advertising my little Internet Exchange project and I've got nice fast WiFi right next to my cabinet.


Popular Posts