There is a slight issue when it comes to IPv6 and FreeBSD jails: DAD can place a curfew on the services binding to an IPv6 address within a jail. shame
What happens is that IPv6 has something called DAD:
Duplicate Address Detection. This is
a procedure for determining if an IPv6 address is available on the local link.
The procedure has a delay of sorts. In the case of FreeBSD, it is the sysctl
net.inet6.ip6.dad_count which defaults to 1.
From the sysctl description: Number of ICMPv6 NS messages sent during duplicate address detection
This causes a slight delay before the address is available for binding and can
be seen using ifconfig. If the system is still waiting, the word tentative
will be listed after the address.
Unfortunately, services will attempt binding to the IPv6 address before the
delay is complete. This causes them to either fail to start or to not bind to
that address. sshd is a good example of this. It will successfully bind to the
IPv4 address in the jail but not the IPv6 address:
error: Bind to port 22 on :: failed: Can't assign requested address.
Fortunately (yeah!), there are a few options:
Disable DAD for the whole system:
net.inet6.ip6.dad_count=0This is an easy solution and should not cause problems in setups where the administrator controls everything. It is the “Sledgehammer” approach.
Disable DAD for the NIC:
ifconfig <interface> no_dadThis is lighter weight than the whole system approach as it only limits the scope to the NIC. However, it affects the whole NIC. I will call it the “Hammer” approach.
Take a nap
Within the
/etc/jail.conf(or/etc/jail.conf.d/<jail>)exec.start = "/bin/sleep 5"; exec.start += "/bin/sh /etc/rc";A sleep can be made prior to the start of a jail. Adding a sleep prior to the execution of
/etc/rcgives time for DAD to finish a nap and let the address pass its test. This can work, but has two problems:- It is a race condition because DAD may not have finished his nap.
- There is a delay for the jail to start when it does not need to wait that long. Having multiple jails starting in serial can take much longer this way.
I name this “Nap Time”.
Watch for the nap to be over
Watch DAD finish his nap or be impatient and continue. This is the method I am now using. It is similar to the sleep method yet can finish much sooner. It monitors the specified interface for up to 10 seconds waiting for all the
tentativelabels to disappear.exec.start += "for i in $(jot 10); do $(ifconfig ${interface} inet6 | \ grep -q tentative) || break; sleep 1; done"; exec.start += "/bin/sh /etc/rc"; interface = "em0";It too has issues but tends to work well and faster:
- This is a bit loose as there could be multiple addresses on the same interface.
- This does not check against which address is tentative.
What should I name this? Hmm… How about “Out After Curfew”? ;)