Docs


Stateless DHCPv6 on Mac OS X

Update: I haven't been able to test, but rumor has it that Lion supports DHCPv6 out of the box!

Apparently, up till now Mac OS X (currently Snow Leopard) does not support DHCPv6, nor configuration of the DNS servers through an option in the Router Advertisement (RFC5006). At home, I have a dual stack network connected to the IPv6-Internet through a SixXS-tunnel. SixXS provides dns resolvers that are whitelisted by Google for their Google over IPv6 program. I wanted to use those dns resolvers so that my Mac OS X machine would connect to Google over IPv6. I could off course manually configure the SixXS-resolvers, but that would be too easy ;-) Therefore I started to look for a (temporary) DHCPv6 solution for Mac OS X. Temporary because I hope Apple will include DHCPv6 support in Mac OS X in the future. I had some success with wide-dhcpv6 and below I explain how to set it up.

Disclaimers

  1. I only tested this on Mac OS X 10.6 Snow Leopard.
  2. The whole setup is a bit complex and requires lots of manual work.
  3. I do not provide any guarantees that this will work for you. It worked for me, but your mileage may vary.
  4. The script only supports setting DNS servers with Stateless DHCPv6. It is assumed that the IPv6-address is obtained through stateless autoconfiguration.

Step 1: compile wide-dhcpv6

You need the development tools for this (XCode, etc). These are available on your Mac OS X install DVD. Download and extract wide-dhcpv6 source from the project website. The steps to compile are:

./configure make

If all goes well, you should now have a binary called dhcp6c. This is the DHCPv6 client we need.

Step 2: the script

dhcp6c calls a script to set various configuration entries. This script must be implemented for each supported operating system. I created a script for Mac OS X. Create a file (name it for example dhcp6c-script.sh) with the following contents:

#!/bin/sh
interface="en0"
scutil=/usr/sbin/scutil
get_hash_for_interface () {
  echo "list State:/Network/Service/[^/]+/IPv6" | $scutil | awk '{print $4}' | while read key;
  do
    ifname=`echo "show $key" | $scutil | grep 'InterfaceName' | awk '{print $3}'`;
    if [ "x$ifname" == "x$1" ]; then
      hash=`echo $key | cut -d/ -f4`;
      echo $hash
      return 0
    fi
  done
}
get_current_dnsv4 () {
  key=$1
  echo "show Setup:/Network/Service/$key/DNS" | $scutil | grep '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' | tr -s ' ' | cut -d' ' -f4
}
set_dns_servers () {
  key=$1
  shift
  echo "get Setup:/Network/Service/$key/DNS
d.add ServerAddresses * $*
set Setup:/Network/Service/$key/DNS" | $scutil
}
hash=`get_hash_for_interface $interface`
current=`get_current_dnsv4 $hash`
set_dns_servers $hash $new_domain_name_servers $current

Some notes:

  • The interface is hardcoded in the script since dhcp6c doesn't pass this to the script
  • The script replaces all current IPv6 dns servers. It preserves the IPv4 ones though.

The script must be executable and owned by root or dhcp6c will refuse to execute it. So execute the following commands:

chmod +x dhcp6c-script.sh
sudo chown root dhcp6c-script.sh

Step 3: create a configuration file

dhcp6c needs a configuration file. Create one (for example dhcp6c.conf) with the following contents:

interface en0 { 
  information-only;
  request domain-name-servers; 
  script "/path/to/dhcp6c-script.sh"; 
};

You should off course fill in the correct path to the script!

Step 4: run the DHCPv6 client

You are now ready to run the DHCPv6 client. First run it in the foreground with extensive debugging enabled:

sudo ./dhcp6c -Df -c dhcp6c.conf -p /var/tmp/dhcp6c.pid en0

If all goes fine, you can run dhcp6c without the -Df to run it in the background while producing less debugging output.

References

Using scutil to set DNS server