/etc/gai.conf – it ain’t what you think it is

If you read the doco (like “man gai.conf“) you would be forgiven for thinking that the contents of /etc/gai.conf controlled source and destination address selection in IPv6. You would be wrong.

It does control destination address selection; it is completely ignored for source address selection. Disclaimer: I’m talking here about Ubuntu, but I suspect all Debian derivatives and probably all Linuxes have the same issue.

/etc/gai.conf contains three types of information: A label table, a precedence table, and an IPv4 scopes table. The label information is needed by source address selection; all three types of information are needed by destination address selection.

For destination address selection, /etc/gai.conf works as advertised. If you make a change to /etc/gai.conf, the new information will be used immediately by all new processes. Running processes will need to be restarted before they “see” the change. You can put “reload yes” in the file to cause even running processes to react immediately to changes, but this will cause all processes to check the last-modified timestamp on /etc/gai.conf (and, worst-case, re-read it) every time they do a DNS lookup, which is probably a bad idea. The comments in /etc/gai.conf suggest you should not do that.

However, for source address selection, /etc/gai.conf is ignored. So we have to find another way to get the label information used.

The obvious way is simply to use the ip program to insert it as needed:

   ip addrlabel add prefix 2001:db8:0:100::/64 label 25

But you would need to do this at startup, for a bunch of prefixes, and where would the information be stored?

I like the idea of all address selection information being in one place. Since destination address selection is already controlled via /etc/gai.conf, and the label table used should really be the same for destination and source address selection, it makes sense to use the /etc/gai.conf file to control source address selection as its designers apparently intended. Therefore we will, in the immortal words of Jean Luc Picard, “make it so” with an appropriate script.

As long as /etc/gai.conf exists, and as long as there is at least one line beginning with “label“, the script below will replace all existing source address selection information with the information out of /etc/gai.conf. This is also the behaviour described in /etc/gai.conf and its man page.

There is no easy way to implement the automatic “reload” facility. For destination address selection, there is an easy and natural per-process synchronisation via the getaddrinfo() calls each process makes. For source address selection, there is no natural synchronisation point. A reload has to first remove all destination address selection information, then replace it, and the replacement is not atomic. If this were to happen just as some process was trying to make a network connection, the results might be less than optimal.

This script is therefore best run at startup, along with other network related startup scripts. However, it can also be run manually whenever needed. The script must be run as root.

Save the script as /etc/init.d/ipv6_sas. Set up SysV startup links to start it in runlevels 2, 3, 4 and 5, and stop it in runlevels 0, 1 and 6. Alternatively, add a suitable conf file in /etc/init (see below) and let Upstart do the work.

#!/bin/sh

GAICONF=/etc/gai.conf
PREFIX=$1
LABEL=$2

# This case statement allows this script to be used as a SysV
# startup script. Delete it if you will only be running the
# script manually or via Upstart.
case "$1" in
  start)
    $0
        exit $? 
    ;;
  restart|reload|force-reload)
    $0
        exit $?
    ;;
  stop)
    # No-op
        exit 0
    ;;
esac

if [ -z $PREFIX ] ; then
   if [ -r $GAICONF ] ; then
      # Is there at least one "label" line?
      grep "^\s*label" /etc/gai.conf > /dev/null
      RETCODE=$?
      if [ $RETCODE = 0 ] ; then
         echo "flushing existing source address selection info..."
         ip addrlabel flush
         echo "adding source address selection info from $GAICONF..."
         grep "^\s*label" /etc/gai.conf | cut -d\  -f2- | xargs -n2 $0 
         echo "...done."
      else
         echo "$GAICONF contains no \"label\" lines. No changes made."
      fi
   else
      echo "$GAICONF not found or not readable. No changes made."
   fi
   exit 0
else
   echo "\tadding prefix $PREFIX with label $LABEL"
   ip addrlabel add prefix $PREFIX label $LABEL
fi

To run this script via Upstart, put these lines in a file called /etc/init/ipv6_sas.conf:

description     "Read IPv6 source address selection rules"
author          "Karl Auer <kauer@biplane.com.au>"

start on (net-device-up
          and local-filesystems
          and runlevel [2345])
stop on runlevel [016]
respawn
env HOME=/
umask 007
exec /etc/init.d/ipv6_sas

[This article was copied from my personal blog, where it was originally posted on July 25, 2012]

Leave a Reply

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