systemd, bridges, containers, and IPv6

I apologize for the quickie here; it’s late, I’m tired, and I just cobbled together a solution to a problem that’s been bothering me since this weekend. First, some backstory since I know you guys appreciate it when I go off on tangents. (And if you don’t, you can just scroll passed this nonsense.)

I’ve been migrating most of my services on my home file server into their own containers. I already did this on my VPS, and I plan on doing it to others eventually, because the service isolation is somewhat helpful and prevents stupid mistakes from doing equally stupid things to the host. Containers aren’t a panacea, of course, and the man pages specifically warn against using them as a security apparatus. It’s not effective, so they say, but I’m also a believer in defense-in-depth.

Anyway, the intent has been mostly a matter of isolation. I have a Minecraft server running, among other things, and I’ve been wanting to isolate it from the rest of the system. Not only does this make configuration somewhat easier since I can have service-specific user accounts without polluting the host passwd entries, but it means I can provide some package isolation as well (no Java on the host–yay!). This isn’t without its shortcomings, mind you, and it’s been one Hell of an interesting battle, particularly when IPv6 is thrown into the mix. But I digress, and the server-specific configuration is a welcome post for another day.

In the coming weeks, I’ll see about making a post on using systemd-nspawn for container isolation, why I chose it over Docker and LXC, how to get it working with user namespaces, and how to configure it with IPv6 addresses routed to each container (rather than through auto-config). Hint: you need routable prefixes like a /48 or /56–trying to (ab)use the neighbor discovery proxy and the respective sysctl settings to segment parts of your /64 non-routable prefix won’t work. Don’t even bother, because it’s not worth the misery.

Meanwhile, back to business: Since I do quite a bit of development of various sorts on my desktop, I also wanted to muck about with containers (which I’ve done for isolating builds), but I wanted to bridge the container with my network-facing interface in a manner that it would happily pull down IPv4 and IPv6 addresses from DHCP. My network, on both IP protocols, is serviced by DHCPv4 and DHCPv6 with more-or-less static assignments tossed out to the appropriate systems with a few exceptions. Specifically, connected devices either get their automatically configured IPv6 address, a static address plus an automatically configured address, or some mix (or absence) thereof. Ideally, containers should receive these addresses as well and behave just as an ordinary OS instance or virtual machine would.

Unfortunately, if you follow the systemd-networkd guides for Arch, you’ll quickly wind up in a circumstance where your network is only going to be half-configured. You might be able to get the IPv4 addresses you expect, if you configure the bridge for DHCP, but your IPv6 range isn’t necessarily going to work even if it does accumulate a set of addresses. Bummer.

Oh, and pay attention to casing: If you enter any of the values entirely in lowercase even by accident, nothing will work. I made this mistake, and my eyes never saw anything wrong because I was too focused on reading “bridge” to notice that it should have been entered as “Bridge.” The obsession, in this case, with the word “bridge” apparently served as an override for sensible parsing that included letter case, but this is a fairly common problem when you’ve been steeped in a similar class of issues for days on multiple systems; it’s a similar phenomenon to semantic satiation wherein words begin to look “strange” if you stare at them too long.

For the purposes of this discussion, we’ll assume that we’ve created files in a similar spirit to the Arch guide for systemd-networkd bridges: vbr0.netdev, vbr0.network, and whatever the name is of your hardware interface (mine is enp6s0, so I named its configuration rather creatively: enp6s0.network).

As with the guide, our files are as follows:

# vbr0.netdev
[NetDev]
Name=vbr0
Kind=bridge
# vbr0.network
[Match]
Name=vbr0
 
[Network]
DHCP=yes

(Notice DHCP=yes above. If you need static assignments, change this accordingly.)

And finally:

# enp6s0.network
[Match]
Name=enp6s0
 
[Network]
IPv6AcceptRouterAdvertisements=no
Bridge=vbr0

Oh! What’s this IPv6AcceptRouterAdvertisements=no? More importantly why do we need it? We want to accept IPv6 RAs on our network, don’t we?

The answer might surprise you: Yes and no. Yes, we need router advertisements, but no, we don’t need them on the bridge’s slave device. (Bonus: You won’t find this in the current documentation.) If you fail to add this option, your physical ethernet device will collect an autoconfiguration address, probably accumulate the appropriate route information, configure itself for IPv6, all while your bridge device does the same thing. You’ll be left with an interface that works for IPv4, is configured for both IPv4 and IPv6, but refuses to do anything with its IPv6 assignments. Obviously, if your network isn’t running radvd or similar, you won’t need this, and you certainly won’t need this if you’re not using IPv6. However, if you’re not running IPv6, you probably aren’t reading this article, are you?

If you’ve made it this far, I assume you’re interested in the final moment of truth and the culmination of our journey together. I therefore present to you a systemd-nspawn container with DHCP assignments for IPv4 and IPv6 auto configuration (addresses redacted for privacy):

[root@buildbot network]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: host0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 1e:21:2b:fc:51:b9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.5.24/23 brd 192.168.5.255 scope global dynamic host0
       valid_lft 938sec preferred_lft 938sec
    inet6 [prefix redacted]:664e:9f3d/128 scope global noprefixroute 
       valid_lft forever preferred_lft forever
    inet6 [prefix redacted]:fefc:51b9/64 scope global mngtmpaddr noprefixroute 
       valid_lft forever preferred_lft forever
    inet6 fe80::1c21:2bff:fefc:51b9/64 scope link 
       valid_lft forever preferred_lft forever

If you haven’t bothered planning your IPv6 migration, you really ought to. The inevitable is coming sooner or later. You do want to be prepared, don’t you?

No comments.
***

Twisted Python and IPv6

» I hate walls of text. Take me to the downloads! «

Updated October 11th, 2010: Added support for Twisted applications and epoll (and possibly kqueue).

Updated December 3rd, 2010: Changed a few things with the patch distribution. See comments for details. Be aware that this information is or will soon be deprecated. Twisted 10.2 is now available along with a number of improvements, and this patch will likely be ported to plugin status. I am leaving this post mostly intact for historical purposes, although corrections have been made to switch the namespace to tx from twistedpatch to clarify further that this patch has absolutely nothing to do with the wonderful folks who write Twisted.

Introduction

IPv6 support doesn’t exist in the base distribution of Twisted Python (the current version as of this writing is v10.1). Over the years, there have been several inquiries related to IPv6 support including a proposed patch, but support for the protocol is still forthcoming. Personally, I don’t like patching a base distribution. After all, it’ll be overwritten the next time I update, and I really don’t want to go through the effort of patching a second time. And who’s to whether or not the patch will apply cleanly in a few months? If you’re not certain this is an important subject, consider that some statistics estimate IPv4 exhaustion will occur in about 230+ days. Food for thought.

My solution is a little different than just simply modifying a handful of files somewhere in $PYTHONPATH and borrows from some of the methods Zope has used for quite some time with their hot fixes. Instead of patching Twisted directly, I have elected to monkey patch Twisted at runtime. This holds several advantages:

  • Whenever IPv6 support is finally added to Twisted, it should be fairly simple to remove this patch. Simply change two lines: Replace the appropriate import statement for the Twisted reactor and replace the listenTCP6 method call with whatever Twisted eventually settles on.
  • The patch consists of logically separate Python code and is therefore easier to maintain, update, and make other interesting changes to.
  • Zope uses monkey patching to issue hot fixes; if it’s good enough for them, it’s good enough for us.
  • There’s no need to modify your base Twisted distribution. That’s what this patch does at runtime. This means less hassle every time your distribution pushes an update to Twisted.
  • Less code, less duplication. All IPv6 implementations in this patch make use of existing Twisted classes. Nearly everything is provided by subclassing IPv4 Twisted classes and overriding the appropriate methods. This means that IPv6 support should be functionally equivalent to IPv4–when it’s finished.

A word of warning: This will not currently work with .tac Twisted apps and you it has not been tested with a reactor other than the select reactor. This patch only works if you are launching the reactor yourself. If someone wants to take this and modify it to work with twistd, feel free. It shouldn’t be too hard. Also, be aware that IPv6 support appears to be going forward in the Twisted base distribution, so this patch is probably superfluous. I have posted a version of the Twisted patch that should work with twistd. This means you can now use the patch in your .tac Twisted application code! The same caveats apply, of course, and there are some additional usage instructions. See below. You should only consider this as an interim solution. This patch is not a perfect solution, it isn’t the best solution, but it is a solution that precludes you from having to patch Twisted directly.

It’s worth mentioning that this patch uses listenTCP6 instead of outright replacing listenTCP; the latter appears to be the preferred solution. I don’t necessarily agree. Instead, I recommend using the listenTCP6 nomenclature, because it provides the opportunity to listen separately on IPv4 and IPv6 interfaces simply by changing the method call. However, if you wish to use a single method (like listenTCP) to listen on both IPv4 and IPv6 interfaces, simply comment out line 86 in tcp6.py:

            s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)

But before you do, you should probably consider the implications of using IPv4-mapped addresses which is what the socket may fall back to using when an IPv4 endpoint is connected. You were warned.

I also argue that adding another method like listenTCP6 is in developers’ best interests because this reason is no longer valid. DNS AAAA records extend IPv6 support to the same hostname an IPv4 address can reference; after all, how does one expect KAME.net implemented the dancing turtle?

Usage Instrutions – Using the Reactor Directly

Usage is exceedingly basic. Extract the twistedpatch into your source directory and replace this import:

from twisted.internet import reactor

With this import:

from tx.ipv6.internet import reactor

This does not import a separate reactor. Instead, what you receive is the exact same reactor import as you would from importing twisted.internet (in fact, that’s what the twistedpatch does) with the listenTCP6 method patched into it. You may then change your application code to read as follows:

if __name__ == "__main__":
    app = MyApp()
    reactor.listenTCP(8080, app)
    reactor.listenTCP6(8080, app) # Add IPv6 support.
    reactor.run()

Usage Instrutions – Making a Twisted Application

As with the listenTCP call, I have committed further blasphemy and added a new class to the Twisted application framework. This allows you to isolate and control IPv4 and IPv6 instances independently without having to worry about IPv4-mapped addresses and other interesting side effects. Remember: This solution isn’t optimal; the Twisted developers would much rather have a single TCP instance to keep the API clean. (I disagree, because IPv6 is a different protocol entirely compared to IPv4… but no matter.) Here’s how you would construct a Twisted application with this patch:

# Import the Twisted service. DO NOT import the internet features.
from twisted.application import service
 
# Import the internet features separately from the twisted patch.
from tx.ipv6.application import internet
 
application = service.Application("myapp")
internet.TCPServer(8000, Application()).setServiceParent(application)
internet.TCP6Server(8000, Application()).setServiceParent(application) # Note the use of TCP6 here

This will launch two separate factory application instances, both on port 8000, using IPv4 and IPv6. You can control each instance independently.

Obligatory Warnings

I have neither presented this patch to Twisted Matrix Laboratories nor have I addressed IPv6 with them in any way. Thus, you should infer that this patch is entirely unauthorized and unsupported by Twisted Matrix Labs. This patch is mostly unfinished and I have yet to run any of Twisted’s unit tests on it. I suspect they will probably fail. It does, however, appear to work rather well with Cyclone:

2010-10-06 14:54:05-0600 [HTTPConnection,0,2001:470:d:407:2d62:6624:eac6:1b96] 200 GET / (2001:470:d:407:2d62:6624:eac6:1b96) 3.29ms

Effectively, here’s what you should expect:

  • There is no UDP support. Yet. It should be trivial to add. Simply subclass the appropriate items in twisted.internet.udp and copy the results into tx/ipv6/internet/udp6.py.
  • Address resolution as supplied by Twisted will probably break. Frankly, anything related to addresses internally in Twisted will probably break when faced with IPv6. This is expected.
  • listenTCP6 will listen only on IPv6 addresses. This is by design; if there is a compelling reason to outright replace listenTCP, I may implement that in the future. As it stands right now, I suspect that IPv4-mapped addresses have a potential to cause more issues than allowing them would otherwise solve. I also don’t believe that this encumbers the Twisted API in any way. After all, Twisted does have listenUNIX for domain sockets–why not add something unique for IPv6?
  • Importing the reactor from tx.ipv6.internet instead of twisted.internet should not break existing code. If it does, there’s a problem. This patch does not introduce a new reactor, it simply patches the existing one.
  • This patch is fully unsupported by Twisted Matrix Laboratories. Do not submit tickets to them if you have this patch installed; instead, revert the two changes (mentioned above), and test your code. If the problem persists, then it is probably a bug in your code–or a bug in Twisted (unlikely). If the problem disappears, then it’s a problem in my code, and you may complain to me about it. Do not complain to Twisted Matrix under any circumstance unless you are absolutely convinced that it is a problem with Twisted.
  • I am not a particularly good Python programmer, and this patch was written hastily in about a half hour. My expertise currently lies in the unfortunate realm of PHP and Java with about 3 or 4 other languages (Python included) added somewhere into that mix. I don’t know enough about Twisted internals or Python to know whether or not I am committing a horrible blasphemy with this patch. Thus, if you don’t like this patch, please submit corrections or write your own. I really don’t mind. I’m just one guy who happens to want IPv6 support distributed as widely as possible, and if this patch can help meet or inspire others to meet that goal, I’ll be happy.

Downloads

If I haven’t scared you off with the notion that this patch might just kick your puppy late one night or bring a swarm of locusts barreling through your neighborhood, perhaps you wish to give it a try. Download it here:

twistedpatch-ipv6.tar.gz
MD5(42e3e8047fbbff165f5a598dbeff7129)
SHA1(a297c882ea2267155e948bcc7d7f2a28a869d953)

twistedpatch-ipv6.zip
MD5(14ecb5ad3dfd9780d1baa8204d907865)
SHA1(ea46ea482aed38e06f1a49dbd672beba45a26dba)
4 comments.
***

IPv6, an Experimental Overview

The Internet as we know it is destined to change. That may seem like a profound statement, but it effectively and correctly captures the essence of the Internet’s entire history. Soon–or hopefully very soon–we’ll be witness to one of the most significant transformations made to the very core of how the Internet operates.

I am of course talking about IPv6, the fundamentally-changed and massively-upgraded addressing scheme intended as a replacement to what we’re currently using, IPv4. You may be wondering why IPv6 is such a big deal, and if you haven’t heard of it yet, you should take some time to learn more. There are quite a few compelling reasons pertaining to why we need IPv6, but the most important is simple: We’re running out of publicly-addressable IPv4 addresses. According to Hurricane Electric’s statistics, we have less than two years before the complete exhaustion of our remaining IPv4 pool.

Knowledge is Power: Arm Yourself!

I’m not an authoritative source on IPv6. I’ve simply been testing it for my own diabolical purposes, but there are plenty of places to get started. I know of one particularly excellent blog dedicated to IPv6 fittingly called “Life with IPv6.” You can also find guides for various operating systems. You can find general guides for most operating systems such as this one for gogo.net (formerly Freenet6). There’s also a really good guide on configuring IPv6 for Gentoo Linux, which is general enough (in some sections) to apply to other OSes. If you happen to have a router supported by the DD-WRT replacement firmware, there’s even a guide to configure it. For the FreeBSD crowd, you have the choice of FreeBSD’s official handbook or KAME’s FreeBSD guide.

So what’s this all about?

This rant of mine has its purpose: During my excursion into the new world, I’ve encountered demons, dragons, angels, angry natives, and the occasional mime. While I haven’t any idea where the mime came from, I do know that it is best to share such discoveries. Thus, this post outlines some of the things I’ve encountered, including a few things that don’t seem to be mentioned anywhere. I may also cover a basic ip6tables setup for use in conjunction with an IPv6 tunnel; if not, don’t worry. I’ll be posting something related to that soon.

But above all else, stay away from the little red berries. Those things give you a nasty full-body rash for a week. That poor mime…

Before we begin: Some Caveats

There’s always a “gotcha,” isn’t there? I should mention this before we dive into “getting started,” otherwise you’re liable to come pounding on my door in the middle of the night complaining that I didn’t provide sufficient warning: If you have a separate consumer-grade router rather than a dedicated *nix box, this probably won’t work. You have two choices: Look around for a way to forward protocol type 41 (IPv6) to the target machine or check dd-wrt.com’s router database for your make and model. The latter option certainly isn’t for the faint of heart and it can render your router unusable.

However, there is a silver lining. I have heard that some home router manufacturers are starting to include native support for IPv6 and a few of them even include wireless access points. SixXS (another tunnel broker) maintains a wiki of several well-known brands that support IPv6 and IPv6 tunneling.

I did lie about there being only two options. There’s actually one more, but given my experiences, I wouldn’t necessarily recommend it. Freenet6, operated by gogo.net, is another free and open IPv6 tunnel broker. gogo.net supplies a special client that can work quite well behind a NAT machine (like your router), but the nearest North American endpoint is located in Montreal, Canada. Freenet6 is a great way to test IPv6 if your options are limited. Unfortunately, it’s also painfully slow. (Side note: The sign-up process is obnoxious and creates a “social networking for network professionals” account.)

I suppose there’s also an unspoken forth option: Don’t use IPv6. I don’t really like that option, and you shouldn’t either! Remember: We have less than two years before all of the IPv4 Internet runs out of addresses. Encouraging adoption should be a high priority among Internet-related hardware and software providers. Keep this in mind the next time you purchase a router for your home network–or dust off an old machine and turn it into a routing box. You’d be surprised how easy that really is.

Getting Started

First, if you don’t have access to a native IPv6 network (and that’s what this post assumes; if you do, stop reading now, you won’t find much more useful information!), you’ll need a tunnel broker. Tunnel brokers provide access to a native IPv6 network and, typically, a large chunk of routeable addresses over an IPv4 tunnel. The Gentoo IPv6 guide is a great place to start and includes links to several tunnel brokers around the world. Hurricane Electric is an excellent broker and provides access via tunnelbroker.net. HE also has several locations worldwide, including 10 or so tunnel endpoints across North America. I believe they’ve recently added several new endpoints in Europe.

Hurricane Electric’s tunnelbroker.net service allocates a /64 chunk of routeable addresses and allows you to create up to 5 separate tunnels from different locations. They also provide a full /48 prefix for those in need of a larger address pool, but I can’t imagine anyone who’d be in need of more than 1.8×1019 addresses–which is the address space allocated to a full /64. Better still: Hurricane electric provides configuration samples for several operating systems, including Linux, FreeBSD, and several flavors of Windows. All you have to do is set up your tunnel, pick your external address, and choose from tunnelbroker.net’s configurations what best matches your setup and go from there.

If you’re using Gentoo, the IPv6 guide is mostly safe, but don’t bother following it for configuring your tunnel–you’ll need to follow Hurricane Electric’s suggested configurations for “Linux-route2.” If you don’t, you might wind up being unable to route IPv6 packets between your internal network and the outside world. Don’t say I didn’t warn you.

If all goes well, you should wind up with a new device that looks something like this:

he0       Link encap:IPv6-in-IPv4
          inet6 addr: 2001:470:c:407::2/64 Scope:Global
          inet6 addr: fe80::4800:2c85/128 Scope:Link
          UP POINTOPOINT RUNNING NOARP  MTU:1280  Metric:1
          RX packets:4136 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3342 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:3564671 (3.3 MiB)  TX bytes:492987 (481.4 KiB)

I won’t go over the details–there’s plenty of guides dedicated to setting up tunnels, configuring your kernel, and installing necessary software. If you can’t get the tunnel working, make sure you did something analogous to:

ip tunnel add he6 mode sit remote <SERVER IPv4 IP ADDRESS> local <YOUR IPv4 ADDRESS> ttl 255
ip link set he6 up
ip addr add <CLIENT IPv6 ADDRESS> dev he6
ip route add ::/0 dev he6

You can verify the link state by pinging a couple of IPv6-only sites (use ping6!). First, try the server IPv6 address from tunnelbroker.net; if that works, try these:

$ ping6 ipv6.google.com
$ ping6 www.kame.net

There are many other IPv6-enabled locations. I’ll leave their discovery as an exercise to the reader.

Once you’re able to successfully ping locations beyond your own delegated subnet and Hurricane Electric’s routes, it’s time to move on to more advanced topics.

Router Discovery

IPv6 includes provisions that limit the overall necessity of DHCP. No longer must addresses be delegated by a central host, and IPv6’s auto-configuration provides routing (via discovery) to connected clients. In most cases, DHCP on a pure IPv6 network is only required for supplying name server addresses and other related chores. Even then, I’ve read speculative mumblings on at least one mailing list that some such tasks could be handled via nearest-neighbor discovery. I haven’t yet tested that, however. Windows Vista (and up) already happens to have shaky IPv6 support for anything out of the ordinary.

Depending on your platform, router discovery announcements may be provided by either radvd (most Linux distros) or rtadvd (*BSD). Providing IPv6 access to your network is simply a matter of installing the appropriate package and tweaking radvd/rtadvd’s configuration file. Bear in mind that rtadvd’s configuration is somewhat terser than radvd, though it’s also much shorter. FreeBSD’s handbook has an example for that, too. However, if you’re using some flavor of Linux, you’re in for a real treat. Here’s what my configuration looks like:

interface eth1
{
    AdvSendAdvert on;
    AdvLinkMTU 1280;
    MaxRtrAdvInterval 300;
    prefix 2001:470:d:407::/64
    {
        AdvOnLink on;
        AdvAutonomous on;
    };
};

The above configuration was borrowed almost entirely from the Gentoo IPv6 guide and it works well. There are some caveats, though. If you plan on supplying DHCP via IPv6, you must add AdvManagedFlag on; somewhere after the AdvSendAdvert declaration; if you don’t, your clients won’t ever solicit DHCP requests. As far as I can tell, this isn’t mentioned in any how-to guide that I’ve yet read.

The fascinating thing about IPv6 is that the immediate availability of a router discovery service on the local network will propagate IPv6 addresses to all connected clients that support it. It’s almost like magic (in fact, it is). Try pinging some IPv6 hosts from your clients to prove it!

Linux-only Issues

radvd has a nasty habit of unsetting the sys.net.ipv6.conf.all.forwarding sysctl variable. When it does, connected IPv6 clients won’t be able to do anything via IPv6 and, if available, will degrade to IPv4 access. If you’re having trouble accessing the IPv6 Internet from your clients (or vice-versa), check this sysctl value. You can do so either via sysctl or by examining the contents of /proc/sys/net/ipv6/conf/all/forwarding. If it’s set to 1, then forwarding is enabled and your tunnel may not be working (or ip6tables might be incorrectly configured). If so, keep reading.

You may also encounter an issue where clients are allocated IPv6 addresses via auto-configuration but refuse to access the IPv6 Internet. Under my Gentoo install, this was resolved when at least one IPv6 address from the pool delegated via my tunnel broker was added to the appropriate interface on the routing box. It’s possible that this issue is specific to my setup.

DHCP

DHCP is still somewhat necessary on IPv6 networks where neighbor discovery doesn’t work for domain name resolution services. Unfortunately, there’s a big catch: It works. Partially. This section highlights some of my experiences with two main stream DHCP client/server packages: DHCPv6 and ISC DHCP v4.1.

DHCPv6

DHCPv6 behaves similarly to the WIDE-DHCP implementation. DHCPv6 has also been superseded by ISC DHCP v4.1. I’ve also had somewhat more luck with static IP assignments using DHCPv6. Here are some thoughts:

Pros
  • Most distros provide this as the defacto DHCPv6 DHCP client/server software.
  • Configuration is slightly less verbose than ISC’s DHCP.
  • Lightweight, spartan implementation.
Cons
  • Highly finicky configuration system.
  • New development halted; development is in maintenance mode.
  • Requires slightly more work than ISC’s implementation to work correctly.
Example Configuration
interface eth1 {
    allow rapid-commit;
    option dns_servers 2001:470:d:407::1 2001:470:d:407:0:baad:beef:cafe destrealm.org;
    send information-only;
 
    link AAA {
        allow unicast;
        send unicast;
        send rapid-commit;
        allow rapid-commit;
        pool {
            range 2001:aaa:12:1234::bead:500 to 2001:aaa:12:1234::bead:ffff/64;
            prefix 2001:aaa:12:1234::/64;
        };
    };
    host statichost {
        duid 00:01:00:01:12:aa:23:cb:10:ac:19:83:82:a8;
        iaidinfo {
            iaid 221149200;
            renew-time 1000;
            rebind-time 2000;
        };
        address {
            2001:aaa:12:1234::100/64;
            prefer-life-time 2000;
            valid-life-time 3000;
        };
    };
};
Notes

To a certain degree, I had the most luck with DHCPv6. Static addresses would assign (correctly) to FreeBSD clients and name server addresses would (usually) propagate. Neither Windows Vista nor Windows 7 would accept static IPv6 address assignment; I’m not sure why. Even with the correct DUID-LLT (local link + time), Windows would refuse static assignment. Dynamic addresses worked fine for all OSes I tested.

DHCPv6 version 1.2.x will build under Gentoo but it won’t run. The ebuild is incorrect and the resulting binary will attempt to load data from /var/lib/lib. Fortunately, there is a workaround.

ISC DHCP v4.1

ISC DHCP 4.1 is still very much a work in progress but works fantastic for IPv4 networks and acceptably for IPv6. ISC DHCP is the current defacto standard for DHCP.

Pros
  • Configuration is easy to understand and follow
  • DHCP configuration for IPv6 networks is almost identical to its IPv4 counterparts.
  • As an ISC project, ISC DHCP has a long running track record and earlier versions likely power most corporate and ISP networks.
Cons
  • IPv6 is only starting to stabilize in the still somewhat experimental 4.x branch.
  • Most basic IPv6 features have been implemented but some esoteric ones have yet to be developed.
  • Slightly larger package than DHCPv6; might not be suitable for highly restricted embedded applications.
Example Configuration
default-lease-time 1800;
max-lease-time 7200;
authoritative;
 
option dhcp.domain-search "destrealm.org";
option dhcp6.domain-search "destrealm.org";
option dhcp6.name-servers 2001:470:d:407::1, 2001:470:d:407:0:baad:beef:cafe;
 
subnet6 2001:aaa:12:1234::/64 {
    allow unknown-clients;
    default-lease-time 1800;
    min-lease-time 4000;
    max-lease-time 7200;
 
    # Ranges can be provided in many different ways.
    #range6 2001:aaa:12:1234::bead:10 2001:aaa:12:1234::bead:ffff;
    #range6 2001:aaa:12:1234::10 2001:aaa:12:1234::ffff;
    range6 2001:aaa:12:1234:0:ccee:0:0/96;
}
 
host statichost {
    host-identifier option dhcp6.client-id "00:01:00:01:11:f9:4b:5a:08:00:27:c5:40:c7";
    fixed-address6 2001:aaa:12:1234::500;
}

Note: The “host” declaration can appear inside a subnet declaration. I tend to separate them for my home configurations for aesthetic purposes; don’t do this for larger networks.

Other Thoughts

ISC DHCP supports both IPv4 networks and IPv6. However, it is currently impossible to supply DHCP access to both IPv4 and IPv6 networks at the same time without running multiple instances of dhcpd. It’s possible, and Gentoo certainly makes it easy; however, some tweaks are needed.

The instructions for running multiple instances of dhcpd on the same Gentoo server are somewhat misleading. If you read them, your initial impression would be to simply do something like the following:

# cd /etc/init.d
# ln -s dhcpd dhcpd6
# cp ../conf.d/dhcpd ../dhcpd6
# nano -w ../dhcpd6
# cp ../dhcp/dhcpd.conf ../dhcp/dhcpd6.conf
# # Point /etc/conf.d/dhcpd6's config file to /etc/dhcp/dhcpd6.conf
# # Done!

Unfortunately, this won’t work. Under the checkconfig() function in /etc/init.d/dhcpd (which is now also linked to /etc/init.d/dhcpd6) refuses to honor the DHCPD_OPTS defined in /etc/conf.d/dhcpd. Thus, forcing the server to run in IPv6 mode (with the -6 switch, though IPv6 mode is the server’s default) doesn’t work. Instead, it is necessary as of this writing to create a copy of the /etc/init.d/dhcpd script and modify the function accordingly to include ${DHCPD_OPTS}:

checkconfig() {
        /usr/sbin/dhcpd ${DHCPD_OPTS} -cf ${DHCPD_CHROOT}/${DHCPD_CONF} -t 1>/dev/null 2>&1
        ret=$?
        if [ $ret -ne 0 ]; then
                eerror "${SVCNAME} has detected a syntax error in your configuration files:"
                /usr/sbin/dhcpd ${DHCPD_OPTS} -cf ${DHCPD_CHROOT}/${DHCPD_CONF} -t
        fi
 
        return $ret
}

(Admittedly, the above code may cause problems if the DHCPD_OPTS variable contains -cf or similar switches.)

Furthermore, it is also necessary to point the server to a different lease file and include the following in a separate dhcpd.conf:

pid-file-name "/var/run/dhcp/dhcpd6.pid";

Here’s what my /etc/conf.d/dhcpd6 looks like:

DHCPD_CONF="/etc/dhcp/dhcpd6.conf"
DHCPD_IFACE="eth1"
DHCPD_OPTS="-6 -lf /var/lib/dhcp/dhcpd6.leases"

Also, much like DHCPv6, ISC DHCP won’t supply static addresses to Windows clients. I can only assume this is the consequence of an incorrect understanding of how DUIDs work on my behalf. If I make an insightful discovery or someone posts corrections in the comments, I’ll update these notes accordingly.

Link Local Routing

IPv6 doesn’t really support the notion of private networks. At least, it doesn’t support private networks in the same manner as IPv4. Instead, there are several ranges reserved for router discovery and, more importantly, “link local” addresses. Link local addresses are automatically created whenever IPv6 support is enabled for an interface and exist in the fe80::/16 prefix. Any address in this range is not routeable but can be pinged on the local network.

There is one surprising discovery I made with regards to IPv6’s auto-configuration. On the local network, the link local address of your router actually becomes the gateway for hosts that it has automatically configured. Gone are the *.*.*.1 router conventions of IPv4. This may seem unimportant, but when you consider that it will impact what choices you make with regards to your firewall, it suddenly becomes much more important.

Samba, CIFS, IPv6, and You

I’ve been running Samba on my local network for literally years to supply access to my /home and various file repositories. However, something unusual started happening the day I booted my server with a new kernel including IPv6 support: Windows 7 would announce that it was “unable to connect all network drives” immediately after login. I assumed (incorrectly) that Samba had crashed, and thus opened Windows explorer, double-clicked the drives, and then they mysteriously opened as if nothing were amiss.

After much poking, prodding, and a liberal application of tcpdump I discovered a couple of important things. First, it is important to have Samba upgrade to at least version 3.2 when adding IPv6 to a network (3.0 appears to be resolvable, even though it doesn’t have fully capable IPv6 support). Second, I also learned that IPv6 packets must be routeable to the Samba server–and must be processed by it as well–because Windows Vista and Windows 7 will both attempt to use CIFS over IPv6. If IPv6 queries fail, these versions of Windows will report that attached network drives cannot be mounted even though they appear to degrade successfully to IPv4 access.

Also, one of the more significant contributors to my Samba problems was related to how I had my firewall configured. Being a good netizen and blocking everything (except what I want) by default, I figured allowing access to the local network via eth1 (my internal interface) was enough. That is to say until I realized that I hadn’t correctly allowed my internal network unadulterated access to the server. Moral to the story: Once you get around to setting up ip6tables, double check your settings. I can’t stress this enough.

Chances are, too, that if you’re using Samba, you’ve probably set it up to provide printing access via the local network to Windows machines that don’t yet support IPP. (Of course, if you’re using Windows Vista or Windows 7 x64, you have no other choice than to use IPP but that’s another rant!) If you’ve enabled ip6tables, you might notice a lengthy delay in printing, and searching for a printer will seem nearly impossible. Here’s why:

Feb 19 16:54:39 sagittarius [277797.502535] IN= OUT=lo SRC=0000:0000:0000:0000:0000:0000:0000:0001 DST=0000:0000:0000:0000:0000:0000:0000:0001 LEN=80 TC=0 HOPLIMIT=64 FLOWLBL=0 PROTO=TCP SPT=42045 DPT=631 WINDOW=32752 RES=0x00 SYN URGP=0
Feb 19 16:54:51 sagittarius [277809.502544] IN= OUT=lo SRC=0000:0000:0000:0000:0000:0000:0000:0001 DST=0000:0000:0000:0000:0000:0000:0000:0001 LEN=80 TC=0 HOPLIMIT=64 FLOWLBL=0 PROTO=TCP SPT=42045 DPT=631 WINDOW=32752 RES=0x00 SYN URGP=0
Feb 19 16:55:15 sagittarius [277833.500054] IN= OUT=lo SRC=0000:0000:0000:0000:0000:0000:0000:0001 DST=0000:0000:0000:0000:0000:0000:0000:0001 LEN=80 TC=0 HOPLIMIT=64 FLOWLBL=0 PROTO=TCP SPT=42045 DPT=631 WINDOW=32752 RES=0x00 SYN URGP=0
Feb 19 16:56:03 sagittarius [277881.502542] IN= OUT=lo SRC=0000:0000:0000:0000:0000:0000:0000:0001 DST=0000:0000:0000:0000:0000:0000:0000:0001 LEN=80 TC=0 HOPLIMIT=64 FLOWLBL=0 PROTO=TCP SPT=42045 DPT=631 WINDOW=32752 RES=0x00 SYN URGP=0

Notice anything amiss? The local machine (that’s ::1/128, or all those zeros followed by a 1) keeps sending requests to port 631 but why. What’s wrong?

This is what happens when Samba sends printer list requests to CUPS. Without a reply, it’ll continue trying until it gives up. The fix? Allow localhost. I know it goes without saying, but it’s easy to overlook, particularly in something like iptables. Moreover, when you’re not used to examining IPv6 networks, it’s easy to overlook the rather unusual looking localhost declaration. Besides, there’s no reason not to grant localhost IPv6 access!

Alas, I get ahead of myself. My ip6tables section will be up and coming in a day or two, and I’ll even have a script prepared. Be aware that there are some unusual complications related to some of IPv6’s reserved ranges (more on that later), but by and large, configuring iptables for IPv6 is analogous to IPv4. The only significant difference is that you’ll have more basic rules to contend with to support things like automatic discovery.

See you next time!

No comments.
***