My goal is to make my home network as simple as possible, but not to use IPv6 exclusively. That said, wherever possible I have enabled and preferred IPv6 to shake out any issues and to see where things can be improved. I try to mimic a “realistic” dual stack environment because that is the most useful balance to me so that I can continue to get things done while automatically preferring IPv6 wherever possible.
Here is a simple ASCII diagram of my physical network:
[ LAN ] -- [ WAP/switch ] -- [ OpenBSD 4.3 ] -- { Internet }
The end result for those that don’t want to read the whole thing is an extremely stable and functional LAN that supports IPv6-enabled devices easily and automatically without denying anything to IPv4-only hosts. Windows Vista clients, for example, can simply plug in or associate with my WAP and have IPv6 connectivity with zero configuration. I realize this is relatively trivial (and hopefully my explanation is as trivial) but I feel like this is important: I commonly hear that IPv6 is very difficult to use or difficult to set up. While there are some things you need to know to set up an IPv6 network (as with IPv4), there is (or rather, should be) absolutely nothing you need to know as a client in a properly configured dual-stack environment. When a user decides to go to freebsd.org they should need to do a little sleuthing to figure out that most, if not all, of their network communication just took place over IPv6 ;)
I will go through the steps I took (neatly sidestepping the mistakes I made…) to set this up as well as posting any relevant configuration files I have. I’ll try to keep this segmented into easily visible sections, because I don’t like splitting this kind of thing up into multiple blog posts. Additionally, before I begin, I am going to use example IPv6 addresses within the RFC 3849 documentation-use-only IPv6 prefix, and example IPv4 addresses from TEST-NET described in RFC 3330 instead of my own. The IPv6 documentation prefix is 2001:db8::/32 and IPv4 TEST-NET is 192.0.2.0/24. I will use my private addresses (in 10.0.0.0/8 address space) as they actually are configured on my home network. What does this mean for you? Quoting from RFC 3849, “[a]ddresses within this block should not appear on the public Internet,” so don’t expect any of these addresses to work for you without altering them!
Step 1: Check configuration
Since this post is about OpenBSD, I’m using OpenBSD as an example. First of all, there are some sysctl options to be sure you have set to allow you to forward packets and be a well-behaved router.
/etc/sysctl.conf
# 1=Permit forwarding (routing) of IPv4 packets
net.inet.ip.forwarding=1
# 1=Permit forwarding (routing) of IPv6 packets
net.inet6.ip6.forwarding=1
# 1=Permit IPv6 autoconf (forwarding must be 0)
net.inet6.ip6.accept_rtadv=0
Next, make sure you have a pf.conf that you are content with, because in a minute you will (hopefully) become connected via IPv6. Here is a barebones pf.conf which is a literal copy and paste from the pf.conf currently on my OpenBSD box. If you’re thinking about copying and pasting this, please make sure it matches your security policies. I like my pf.conf to be more liberal than many people, so if you don’t understand what this does I would recommend man 5 pf.conf.
/etc/pf.conf
# Macros
ext_if="rl0"
int_if="xl0"
# Tables
# Options
set block-policy return
set skip on lo
# Normalization
scrub in
scrub out
# Queuing
# Translation
nat on $ext_if inet from ! ($ext_if) -> ($ext_if)
# Filtering
pass in
pass out
Make sure pf is enabled:
pfctl -e
and that your ruleset is loaded:
pfctl -f /etc/pf.conf
where /etc/pf.conf is the location of your pf.conf (this is default).
Step 2: Get connected
If you’re in the USA, statistically you probably do not have native IPv6 connectivity. This is a little unfortunate, but thankfully there are organizations who are willing to allow us to use their services to tunnel IPv6 over the existing IPv4 network to get to their point of presence, and from there the traffic can travel over the IPv6 internet. While this is not ideal, this will have to do for most of us. If you have native IPv6 connectivity, you’re probably laughing at me :)
I used Hurricane Electric as my tunnel broker. Once I was signed up, I asked for a /64. One nice thing about Hurricane Electric (and this might be true for other tunnel brokers as well, I have no idea) is that they provide customized configurations for nearly any operating system.
I’ll explain more about this later, but I’d like to show the information Hurricane Electric gave me here (using the example prefixes instead of mine) so you can tell how to apply this to your own tunnel (and I’m sure other tunnel brokers do it similarly). Hopefully the explanation afterward will give these values some meaning if they don’t already make sense to you:
Server IPv4 address: 192.0.2.74
Server IPv6 address: 2001:db8:1f04:4c9::1/64
Client IPv4 address: 192.0.2.44
Client IPv6 address: 2001:db8:1f04:4c9::2/64
Routed /64: 2001:db8:1f05:4c9::/64
Server marks Hurricane Electric’s IPv4 and IPv6 tunnel endpoints, and Client marks my IPv4 and IPv6 tunnel endpoints.
Routed /64 is the subnet I am assigned. Note that it differs slightly (1f05 instead of 1f04) from the tunnel endpoints.
Since I’m setting up my OpenBSD 4.3 box as one of the endpoints of the tunnel, I selected OpenBSD and had them generate a configuration based on the assigned addresses. Here was the configuration generated for me (note the line continuation):
ifconfig gif0 tunnel 192.0.2.44 192.0.2.74
ifconfig gif0 inet6 alias 2001:db8:1f04:4c9::2 \
2001:db8:1f04:4c9::1 prefixlen 128
route -n add -inet6 default 2001:db8:1f04:4c9::1
You could happily copy and paste this, but chances are if you’ve read this far you’re going to want to know what exactly this is doing. I know I did, as I don’t like to simply copy and paste configuration from other people without knowing what it does first. The following is kind of verbose, but it might help some people to understand better what the above commands mean.
The first line says “ifconfig, I want to operate on a generic tunneling interface (gif0, man 4 gif for more information) to create a tunnel from my IPv4 address assigned to me by my ISP (192.0.2.44) to another IPv4 address somewhere else on the internet (192.0.2.74).” The tunnel concept is no more complex than thinking of a virtual tube that connects two points. While the internet may route the physical packets between the two endpoints 30 hops around the world, as far as the logic is concerned, the tunnels are directly connected. You can think of this tunnel as the underlying “road” or transport on top of which our IPv6 packets will travel to get to a place where they can be routed natively, as we (I) do not yet have native IPv6 connectivity to the internet.
The next line says “ifconfig, I want to operate on gif0 again, this time specifying things about IPv6 (inet6). I would like to create a new address for this interface as opposed to altering any existing addresses (alias), and I would like this address to be 2001:db8:1f04:4c9::2. Since we’re talking about a tunnel, I want the other end (Hurricane Electric’s end) of my tunnel to be 2001:db8:1f04:4c9::1. Finally, since I’m dealing with just a single host address on a point-to-point link, I will use prefixlen 128.”
Dead simple so far, right? The last line says “I’d like to adjust my routing tables (route -n, don’t worry about the -n for now but you can read the man page for route(8) if you’re curious) to add an IPv6 (-inet6) route which will be my default route (if my machine doesn’t know exactly where to send packets, they go here), and I want them to head toward the “far end” of my tunnel which is 2001:db8:1f04:4c9::1, where hopefully they’ll eventually be routed and arrive at their destination.” Note here that we specify the “far end” of the tunnel. We want our packets to go through the tunnel to the other end where they’ll be picked up by Hurricane Electric, not simply go to our end of the tunnel and stop short of their destination.
gif(4) is a neat little pseudo-interface that can encapsulate any combination of IPv6 or IPv4 packets based on how it is configured. Now that we’ve set up a gif(4) interface (gif0 above), it will see that since the tunnel is set up via IPv4 (the first line above) that IPv6 packets traveling through it need to get encapsulated inside IPv4 packets so they can be routed through the IPv4 internet. Once they reach Hurricane Electric at the other end, Hurricane Electric’s endpoint is set up to unpack (decapsulate) the packets and route them over the native IPv6 internet to their destination. The reverse happens exactly as you’d expect; IPv6 packets encapsulated in IPv4 packets coming in from the Hurricane Electric tunnel to our gif(4) interface get decapsulated and shuffled across our LAN to their destination as IPv6 packets.
At this point, you’ll want to assign your own IPv6 addresses to your interfaces so that you can access them via IPv6. Hurricane Electric assigned me the 2001:db8:1f05:4c9::/64 subnet as shown above, and you must assign these addresses out of the available allocated space. Working from our example, then, these are some sample valid addresses:
2001:db8:1f05:4c9::10
2001:db8:1f05:4c9::dead:beef
2001:db8:1f05:4c9::420
You can use ifconfig inet6 alias to do such configuration and an example, for completeness, of assigning an address to one of your interfaces might be
ifconfig xl0 inet6 alias 2001:db8:1f05:4c9::10 prefixlen 64
Step 3: Advertise
Now that we’re ready to go and you’ve verified that you can do something like:
ping6 ipv6.google.com
and you get replies, you can move on to telling other IPv6 capable hosts on your network about your connectivity, and how they can get some. OpenBSD ships with rtadvd (router advertisement daemon) which we will use for exactly this purpose.
Again, the configuration file first:
/etc/rtadvd.conf
xl0:\
:addr="2001:db8:1f05:4c9::":prefixlen#64:raflags#64:
It might look like noise at first, so I’ll break it down. man 5 rtadvd.conf will be useful for more details.
Each field in this configuration file is separated by a : character. The first line starts off with an interface that rtadvd is going to advertise on. You may notice that xl0 is my internal interface from my pf.conf. This is because I want rtadvd to advertise the information that follows on my LAN. The backslash and whitespace that follows is simply to make it easy to track things in a large file; they are completely optional. The next section is addr="2001:db8:1f05:4c9::". This gives the address prefix to advertise to hosts. With IPv6, you advertise a prefix of some length and the hosts then fill in the rest themselves. Therefore I am advertising the prefix for the network you saw above. The next section is prefixlen#64. You may notice that string values are distinguished from their corresponding identifiers with = and numeric values are distinguished with #. This prefixlen section tells hosts how long the prefix that I’m advertising is. As the address 2001:db8:1f05:4c9:: expands to 2001:db8:1f05:4c9:0000:0000:0000:0000, I have to say which part of that I’m advertising, and which part is left up to the host to choose for itself. This says I’m advertising the first 64 bits of the address (The first 4 colon-delimited sections), leaving the host receiving this advertisement to deduce that it can pick the other 64 bits for itself. The last section is perhaps the least well-understood. This field raflags#64 stands for router advertisement flags, and they carry, you guessed it, flags about the nature of the router advertisement. There are two flags we are interested in. They are documented in rtadvd.conf with the following:
raflags
(num) Flags field in router advertisement message header. Bit 7
(0x80) means Managed address configuration flag bit, and Bit 6
(0x40) means Other stateful configuration flag bit. The default
value is 0.
I will simplify this slightly to make it as easy as possible to understand at first (hopefully) so if you want details or the authoritative source, refer to RFC 4861, page 19 for more information.
The M flag says that the host will need to get addresses via DHCPv6. In other words, it tells the host that it shouldn’t pick its own identifier (remember those last 64 bits above?), because the network policy is to ask a central location (Managed, see?) for an address first. This will likely trigger DHCPv6 in hosts that support it.
The O flag says that the host may obtain Other information from a central location as appropriate, also using DHCPv6. In other words, if you’d like to make available DNS servers, time servers, etc via DHCP, you’ll want this flag turned on so that hosts ask you about them. Note that this is separate from the address configuration. You may have (and I do indeed do it this way) the O flag set while the M flag is not set, indicating that hosts can pick their own addresses but if they want other neat information they should ask. Note that this is a little more flexible than DHCP available for IPv4, and allows for better separation of network management if you don’t want the “all-or-nothing” approach that DHCP for IPv4 offers.
The value is 64 for raflags, which is the decimal value (and I personally think the man page is confusing in this regard) of the hexadecimal value 0x40, meaning that I have the O flag set, but the M flag remains unset. This is because, in order for users to feel like they have connectivity out of the box, they will need DNS services, and I will provide them with a DNS server address to use (via DHCPv6) as I will show in a moment, so the host needs to know that it can ask for it.
Once you’ve got everything set up like you want it, start the server with
/usr/sbin/rtadvd xl0
where xl0 is the interface you want rtadvd to operate on. xl0 is my internal interface.
Step 4: DNS
I’d like to be able to resolve DNS over IPv6 for machines that support it, and it required a little tweaking on my part to get it working like I wanted it to.
First, I ran rndc-confgen to generate a key to use to communicate with the running DNS server, and did the appropriate things with it. Take a look at the man page for rndc-confgen; I won’t go into the details, but you’ll need to substitute yours below (for YOUR_OWN_SECRET_HERE if you choose to use my configuration file.
/var/named/etc/named.conf (partial)
key "rndc-key" {
algorithm hmac-md5;
secret "YOUR_OWN_SECRET_HERE";
};
acl clients {
10.1.1.0/24;
2001:db8:1f05:4c9::/64;
127.0.0.0/8;
::1/128;
};
controls {
inet 127.0.0.1 port 953
allow { 127.0.0.1; } keys { "rndc-key"; };
};
options {
listen-on { any; };
listen-on-v6 { any; };
empty-zones-enable yes;
allow-recursion { clients; };
};
logging {
category lame-servers { null; };
};
This tells BIND to listen on all of my interfaces but only recursively resolve queries from my local IPv4 and IPv6 networks, which I’ve gone over above. I’ve also done some other things to the default shipped configuration like allowed version queries. If you’re unhappy with my security policies, you’ll need to make sure you modify this file to match yours before putting it into production. With this in place, I simply started the server by executing
/usr/sbin/named
Check /var/log/daemon to make sure everything started properly.
Step 5: DHCPv6 extras
This part isn’t quite as standard on OpenBSD, yet. I decided to go with WIDE-DHCPv6 for no particular technical reason, but it is simple to build and configure.
Once I unpacked the software, I changed into its directory and did
./configure && make && sudo make install
which builds it and installs the software to /usr/local. If you need/want it somewhere else, you can use the standard configure options to alter the prefixes and some other things. My OpenBSD box is a Pentium III running at 1GHz, and it takes a very small amount of time (2 minutes, if that) to configure, build, and install.
Even easier than installing this software is configuring it (in my case at least). I created a file called /usr/local/etc/dhcp6s.conf to configure the server, and the file looks like this:
/usr/local/etc/dhcp6s.conf
option domain-name-servers 2001:db8:1f05:4c9::10;
which simply tells the DHCPv6 server to hand out the IPv6 address 2001:db8:1f04:4c9::10 as the primary IPv6-accessible DNS server. Windows Vista clients, for example, if given one or more IPv6 DNS servers, prefer the IPv6 DNS servers over the IPv4 DNS servers.
You can now start the daemon with
/usr/local/sbin/dhcp6s xl0
substituting xl0 for the interface you would like it to listen on (xl0 is my internal interface) and the path to the server for the path you used if you installed it to a different location.
Step 6: Finalize
Now that we’ve set it all up, let’s make our configuration persistent across reboots.
I used /etc/rc.local to start WIDE-DHCPv6’s dhcp6s on boot.
/etc/rc.local
echo -n 'starting local daemons:'
# Add your local startup actions here.
echo -n ' dhcp6s'
/usr/local/sbin/dhcp6s xl0
echo '.'
My /etc/rc.conf.local looks like this (you may not need all of these):
/etc/rc.conf.local
dhcpd_flags=""
named_flags=""
ntpd_flags="-s"
rtadvd_flags="xl0"
pf=YES
and I have three hostname.if files:
/etc/hostname.gif0
tunnel 192.0.2.44 192.0.2.74
inet6 alias 2001:db8:1f04:4c9::2 128
dest 2001:db8:1f04:4c9::1
!route -n add -inet6 default 2001:db8:1f04:4c9::1
/etc/hostname.xl0
inet 10.1.1.1 255.255.255.0 10.1.1.255
inet6 alias 2001:db8:1f05:4c9::10 64
/etc/hostname.rl0
dhcp NONE NONE NONE
You may need to change some things, for example I obtain my rl0 IPv4 address via DHCP, so my first line of hostname.rl0 contains the right incantation to obtain the address that way.
It was also pointed out to me that assigning an IPv6 address to this external interface if you are using a setup like mine may cause routing confusion (and in fact did in my case!)
Conclusion
I hope this at least gives you a headstart when it comes to setting up a home network on OpenBSD. This isn’t necessarily intended as a guide, more as a way for me to document my thought process as I set up my network. That said, I have written it with people reading as a way to get ideas for themselves in mind, so I would appreciate comments about places where you think this can be improved. Chances are I’ve made a mistake in my thinking or have given out bad information, and I’d appreciate corrections to that effect even more.