IPv6 Destination Address Selection – what, why, how

IPv6 destination address selection is the process of deciding which IPv6 address a connection should be made to. This is the flip side of IPv6 source address selection, which has been the subject of several earlier articles (start here). Destination address selection is described in the same RFC as source address selection – RFC 6724 (which obsoletes RFC 3484).

Why do we need destination address selection?

If the host you are trying to connect to has several addresses, you (or the application you are using) will need to select just one. This is not a new situation – hosts have had multiple addresses for years. However, there has never been any consistent way of controlling which destination address would be used. It has generally been left to the vagaries of DNS, with applications simply using the first address returned from a lookup, or left to individual applications to make intelligent choices. IPv6 provides a consistent way of making the choice.

IPv6 brings with it a few new wrinkles, too. A single domain name may resolve to both IPv4 and IPv6 addresses, even multiple IPv4 and multiple IPv6 addresses. The addresses returned may be in different scopes.

Two examples

The use case that really makes destination address selection necessary, is where hosts are dual-stack and one protocol should be preferred over the other. If you want to prefer (say) IPv6, then you need some way of making sure that, where a host resolves to an IPv4 address and an IPv6 address, the IPv6 address will be chosen as the destination address.

Or imagine you are using one range of IPv6 addresses for internal communications, and another range of IPv6 addresses for external communications. All your internal hosts will then have two addresses – one internal, one external. They will also have link local addresses, of course, but these are probably not in the DNS. When an internal host opens a connection to another internal host, you want the connection to be made to the internal address of the other host; that is, you want to prefer internal addresses as destinations.

In both these cases you could solve the problem by having different names for each of the two addresses, but that doesn’t scale well. Worse, it requires applications (or even users) to choose the right name. It is obviously better if hosts can be configured to use the desired addresses automatically. That is where destination address selection comes in.

When and where does destination address selection happen?

Destination address selection happens before source address selection. The appropriate source address to use can only be chosen once the target destination is known. There are some exceptions to this, but we won’t consider them here (RFC 6724 does discuss them).

Obviously if an application already has an actual IP address to connect to, the connection will be made (or at least attempted) to that address, and there will be no “selection” as such. This would happen if, for example, the user specified an IP address literal to the application, or if the application read specific IP addresses from a configuration file of some sort. However, the more usual sequence is that an application makes a library function call to resolve a DNS name into one or more IP addresses.

The way this is handled is via a new library function for doing DNS lookups. The new function, getaddrinfo() does two DNS lookups, one for IPv6 addresses and one for IPv4 addresses. It marshalls all the answers into a list, then orders the list according to various criteria, putting the most preferred addresses first. The whole list is returned to the calling application. It is up to the application to do something sensible with the information – such as to try the available addresses in order of preference.

“Destination address selection” is thus a bit of a misnomer. While source address selection really is a selection process, destination address selection would be better termed “destination address ordering” or “destination address prioritisation”.

The above description of getaddrinfo() is an extremely simplified view of what getaddrinfo() does! For a better and more comprehensive description, see “man getaddrinfo”.

How are destination addresses selected?

The process is very like source address selection, in that a list of candidate addresses is set up, then the list is sorted according to a set of rules. Unlike source address selection, which returns only a single selected source address, destination address selection returns the whole list of candidate destination addresses. All of this happens inside getaddrinfo(). Technically it is the application that “selects” an address, but all the heavy lifting has already been done by getaddrinfo().

All addresses found for the destination will be in the returned list, even if they are unusable or unreachable. For example, getaddrinfo() will return all IPv4 and IPv6 addresses for a host if requested, even if the host making the call does not support IPv6 at all. This is because getaddrinfo() does not know why an application is doing the name lookup. The application may only want to display or record the results.

Several rules inspect the source address that would be chosen for a candidate destination address. This means that the source address selection process is performed for each available destination address. This process does not actually select a source address to be actually used; it just figures out the source address that would be used if the given candidate destination address were to be used.

Here are the rules, in summary. For the details, see RFC 3484:

  1. Avoid unusable destinations
  2. Prefer matching scope
  3. Avoid deprecated addresses
  4. Prefer home addresses
  5. Prefer matching label
  6. Prefer higher precedence
  7. Prefer native transport
  8. Prefer smaller scope
  9. Use longest matching prefix
  10. Otherwise, leave the order unchanged

Let’s work through these rules in more detail. The RFC covers quite a few special conditions, for example those around multicast addresses. Here, we are just looking at “normal” destination address selection, where both ends of the desired connection are ordinary unicast addresses.

Rule 1, “avoid unusable destinations” moves addresses that cannot be used down the list. There are several reasons why a destination address might be unusable – one interesting one is if there is no candidate source address for the candidate destination address. Another reason would be if the destination address were known to be unreachable.

Rule 2, “prefer matching scope” prefers destination addresses that are in the same scope as the source address. That is, if there are two destination addresses, A and B, and A has a source address that matches its scope while B dees not, then A will be sorted before B.

Rule 3, “avoid deprecated addresses”  tries to avoid the need to make connections from a deprecated address, by avoiding destination addresses where a deprecated source address would be chosen. Given two possible destination addresses A and B, if the source address that would be chosen for A is deprecated, and the source address that would be chosen for B is not deprecated, then prefer B over A.

Rule 4, “prefer home addresses“, tries to help a node use its Mobile IPv6 home address in preference to a care-of address. A destination address is preferred if the source address that would be chosen for that destination address is a home address. This rule has no effect when MobileIPv6 is not in use.

Rule 5, “prefer matching label” does a lookup in a table of prefix labels to find a label for each destination address. It then does the same lookup for the source address that would be chosen for each candidate destination address. Destination addresses that have a label matching the label of their source address will be sorted above destination addresses that do not. The label table is the same label table used for source address selection.

Rule 6, “prefer higher precedence“, uses a table of prefix precedences to find a precedence value for a given destination address. This is a second table rather like the label table, except that the prefixes are mapped to precedence values. High precedence values are preferred over lower precedence values. The default precedence table looks like this (the rightmost column is not part of the table data):

   Prefix             Precedence
      ::1/128           50       (localhost)
      ::/0              40       (any IPv6)
      ::ffff:0:0/96     35       (any IPv4)
      2002::/16         30       (6to4)
      2001::/32          5       (Teredo)
      fc00::/7           3       (ULA)
      ::/96              1       (IPv4 compatible, deprecated)
      fec0::/10          1       (site local, deprecated)
      3ffe::/16          1       (6bone, deprecated)

Rule 7, “prefer native transport“, will prefer native IPv6 addresses over IPv6 addresses being used in an encapsulating transport. 6over4 and ISATAP are examples of encapsulating transports. Any encapsulating transport that uses a specific prefix, such as 6to4’s “2002::/16” or Teredo’s “2001::/16” can be handled in the precedence table used in Rule 6.

Rule 8, “prefer smaller scope“, aims to use the “most local” destination address. For example, if you have as candidate destinations a link local address and a global address, this rule will sort the link local address ahead of the global address. As with source address selection. IPv4 addresses get a scope retrofitted to them – zeroconf (169.254.0.0/16) and localhost 127.0.0.0/8() addresses are “link local”, all others are “global” in scope.

Rule 9, “use longest matching prefix“, will prefer the candidate destination address that shares the greatest number of contiguous leading bits with the source address that would be chosen for it. Such an address is likely to be topologically closer to the source address. This rule is not used when comparing addresses in different families! RFC 6724 says the bitwise comparison stops after the source address prefix length because deeper comparison is pretty meaningless; the now-obsolete RFC 3484 didn’t have this limit.

Rule 10 says “otherwise, leave the order unchanged“. That is, leave the order as it was returned from the DNS. This rule is needed to prevent arbitrary re-ordering of addresses that compare as “equal” – that is, where neither address is preferred over the other.

RFC 6724 says that the last two rules can be replaced by an implementation-dependent alternative if the implementation has some better way to choose between two otherwise “equal” destination addresses. The example given in the RFC is an implementation that knows which of two addresses has the better communications path from its source address, since this is the intent behind Rule 9.

A worked example

We’ll use the same example as we used for source address selection. There are two hosts on different subnets, but in the same network. Each host has a single ethernet interface and of course a loopback interface. The loopback interface has a local host address; the ethernet interface has a link local address, an autoconfigured address, and an address obtained via DHCPv6. We are running dual stack, so we also have an IPv4 localhost address on the loopback interface and an IPv4 address obtained via DHCP on the ethernet interface. The complete list of addresses involved is thus:

Host A:

   fe80::302:2ff:fe04:506/64
   2001:db8:0:100:302:2ff:fe04:506/64
   2001:db8:0:100::27/64
   ::1/128
   127.0.0.1/8
   192.168.1.77/24

Host B:

   fe80::305:6ff:fe07:809/64
   2001:db8:0:200:305:6ff:fe07:809/64
   2001:db8:0:200::38/64
   ::1/128
   127.0.0.1/8
   192.168.2.78/24

We want to access the web server on Host B from Host A, so we put this URL into our browser on Host A:

http://hostb.mydomain.com.au

The browser will start by calling getaddrinfo() to find out the addresses associated with that name. getaddrinfo() will find only three addresses, because the localhost and link local addresses are not in the DNS. Let’s say the list is returned as follows:

   192.168.2.78
   2001:db8:0:200:305:6ff:fe07:809
   2001:db8:0:200::38

getaddrinfo() works out the source addresses for each of the possible destination addresses. Remember,this is not actually selecting the source address that will be used for this connection! It is just working out the source addresses that would be chosen for each destination if that destination were selected. To see how the source addresses were determined, refer to this article.

   192.168.2.78 (192.168.1.77/24)
   2001:db8:0:200:305:6ff:fe07:809 (2001:db8:0:100:302:2ff:fe04:506/64)
   2001:db8:0:200::38 (2001:db8:0:100:302:2ff:fe04:506/64)

Whoa! How come 2001:db8:0:100:302:2ff:fe04:506/64 is in there twice? With the now obsoleted RFC 3834, it wouldn’t be – because source address selection Rule 8 (use longest matching prefix) was not limited to the prefix length of the source address. The DHCP source address would have sorted ahead of the autoconf source address, because 72 bits match vs only 64 with the DHCP addresses. With RFC 6724, the comparison stops at the prefix length of the source; because both source addresses share the same first 64 bits, they compare “equal”. As a result, whichever one happened to be ahead of the other in the initial, unsorted list will be selected. I’ve rather arbitrarily decided, for the purposes of this description, that that was the autoconf address, so it will be selected as the source address for both candidate IPv6 GUA destination addresses.

Now getaddrinfo() sorts the list. During the sort, pairs of candidate destination addresses are compared according to the ten rules above. In each comparison, the rules are checked in order; as soon as a rule shows a pair being “unequal”, i.e., prefers one of the pair over the other, the comparison stops; the remaining rules are not checked. The following description skips the sort algorithm and just shows the end result.

Rule 1, “avoid unusable destinations”, won’t be used; all of the addresses in the list are usable. Each is reachable, and each possible destination address has a usable source address.

Rule 2, “prefer matching scope” will also make no difference, because all three destination addresses are in the same scope.

Rule 3, “avoid deprecated addresses”, doesn’t do anything; none of the candidates’ corresponding source addresses are deprecated.

Rule 4, “prefer home addresses”, doesn’t do anything; we are not doing MobileIv6.

Rule 5, “prefer matching label”,  will have no effect. All the candidate destination addresses match the labels of their corresponding source addresses.

Rule 6, “prefer higher precedence”, will push the IPv4 address towards the end of the list. The default precedence table, as defined in the RFC, looks like this (the rightmost column is not part of the table data):

   Prefix             Precedence
      ::1/128           50       (localhost)
      ::/0              40       (any IPv6)
      ::ffff:0:0/96     35       (any IPv4)
      2002::/16         30       (6to4)
      2001::/32          5       (Teredo)
      fc00::/7           3       (ULA)
      ::/96              1       (IPv4 compatible, deprecated)
      fec0::/10          1       (site local, deprecated)
      3ffe::/16          1       (6bone, deprecated)

IPv4 addresses have a lower precedence (10) than IPv6 addresses (50), so whenever the are compared with an IPv6 address, they will sort down the list.

Rule 7, “prefer native transport”, doesn’t do anything because none of the addresses involved are reached via encapsulating transports.

Rule 8, “prefer smaller scope”, doesn’t do anything because both the candidate IPv6 addresses have the same scope, and any IPv4 addresses will have been caught by Rule 6 already (but when comparing IPv4 addresses with each other, this rule will rearrange them according to their scope relative to one another).

Rule 9, “use longest matching prefix”, will compare only the first 64 bits of the candidate destination addresses with their source addresses, because that is the prefix length of those source addresses. That means that the two candidates compare “equal” according to this rule, so no swap is made, and we end up at Rule 10.

Rule 10 stipulates that the original ordering of the candidates being compared not be changed, if they survive as far as this rule. Since the autoconf address was returned first by the DNS, and the DHCP address second, we end up with the following final list to return to the browser:

   2001:db8:0:200:305:6ff:fe07:809
   2001:db8:0:200::38
   192.168.2.78

… meaning that we will send our initial page request to 2001:db8:0:200::38.

 

Leave a Reply

Your email address will not be published. Required fields are marked *