IPv6 the SmartOS Way
Update: As of 20150917T235937Z full support for IPv6 has been added to vmadm
with the added ips
and gateways
parameters. If you’re using SmartDataCenter,
these parameters won’t (yet) be added automatically, so the following may be
useful to you. But if you’re using SmartOS, see the updated
SmartOS IPv6 configuration wiki page.
There have been a lot of requests for IPv6 support in SmartOS. I’m happy to say that there is now partial support for IPv6 in SmartOS, though it’s not enabled by default and there may be some things you don’t expect. This essay is specific to running stand-alone SmartOS systems on bare metal. This doesn’t apply to running instances in the Joyent Cloud or for private cloud SDC.
Update: I now have a project up on Github that fully automates enabling SLAAC IPv6 on SmartOS. It works for global and non-global zones and automatically identifies all interfaces available, regardless of the driver name.
First, some definitions so we’re all speaking the same language.
- Compute Node (CN): A non-virtualized physical host.
- Global Zone (GZ): The Operating System instance in control of all real hardware resources.
- OS Zone: A SmartMachine zone using OS virtualization. This is the same thing as a Solaris zone.
- KVM Zone A zone running a KVM virtual machine using hardware emulation.
- Compute Instance (CI): A SmartMachine zone or KVM virtual machine.
- Smart Data Center (SDC): Joyent’s Smart Data Center private cloud product. SDC backends the Joyent Cloud.
There are two modes of networking with SmartOS. The default is for the global
zone to control the address and routes. A static IP is assigned in the zone
definition when it’s created, along with a netmask and default gateway and
network access is restricted to the assigned IP to prevent tennants from causing
shenanigans on your network. The other is to set the IP to DHCP, enable
allow_ip_spoofing
and be done with it. The former mode is preferred for public
cloud providers (such as Joyent) and the latter may be preferred for private
cloud providers (i.e., enterprises) or small deployments where all tennants are
trusted. For example, at home where I have only a single CN and I’m the only
operator, I just use DHCP and allow_ip_spoofing
.
By far the easiest way to permit IPv6 in a SmartOS zone is to have
router-advertisements on your network and enable allow_ip_spoofing
. As long as
the CI has IPv6 enabled (see below for enabling IPv6 within the zone) you’re
done. But some don’t want to abandon the protection that anti-spoofing provides.
Whether you use static assignment or DHCP in SmartOS, the CI (and probably you too) doesn’t care what the IP is. In fact, KVM zones with static IP configuration are configured for DHCP with the Global Zone acting as the DHCP server. If you have another DHCP server on your network it will never see the requests and they will not conflict. In SDC, entire networks are allocated to SDC. By default SDC itself will assign IPs to CIs. In the vast majority of cases it doesn’t matter which IP a host has, just as long as it has one.
Which brings us to IPv6. It’s true that in SmartOS when a NIC is defined for a
CI you can’t define an IPv6 address in the ip
field (in my testing this is
because netmask
is a required parameter for static address assignment, but
there’s no valid way to express an IPv6 netmask that is acceptable to vmadm
).
But like it or not, IPv4 is still a required part of our world. A host without
some type of IPv4 network access will be extremely limited. There’s also no
ip6
field.
But there doesn’t need to be. Remembering that in almost all cases we don’t care
which IP so long as there is one, IPv6 can be enabled without allowing IP
spoofing by adding IPv6 addresses to the allowed_ips
property of the
NIC. The most common method of IPv6 assignment is SLAAC. If you’re
using SLAAC then you neither want, nor need SmartOS handing out IPv6 addresses.
The global and link-local addresses can be derived from the mac
property of
NIC of the CI. Add these to allowed_ips
property of the NIC definition and the
zone definition is fully configured for IPv6 (you don’t need an IPv6 gateway
definition because it will be picked up automatically by router-advertisements).
Permitting IPv6 in a Zone
Here’s an example nic from a zone I have with IPv6 addresses allowed. Note that both the derived link-local and global addresses are permitted.
[root@wasp ~]# vmadm get 94ff50ad-ac74-46ac-8b9d-c05ddf55f434 | json -a nics
[
{
"interface": "net0",
"mac": "72:9c:d5:34:47:59",
"nic_tag": "external",
"gateway": "198.51.100.1",
"allowed_ips": [
"fe80::709c:d5ff:fe34:4759",
"2001:db8::709c:d5ff:fe34:4759"
],
"ip": "198.51.100.37",
"netmask": "255.255.0.0",
"model": "virtio",
"primary": true
}
]
In my workflow, I create zones with autoboot
set to false, then add IPv6
addresses based on the mac
assigned by vmadm
then I enable autoboot and boot
the zone. This is scripted of course, so it’s a single atomic action.
Enabling IPv6 in a SmartMachine Instance
Once the zone definition has the IPv6 address(es) allowed it needs to be enabled in the zone. For KVM images, most vended by Joyent will already have IPv6 enabled (even Ubuntu Certified images in Joyent Cloud will boot with link-local IPv6 addresses, though they will be mostly useless). For SmartOS instances you will need to enable it.
In order to enable IPv6 in a SmartOS zone you need to enable ndp
and use
ipadm create-addr
.
svcadm enable ndp ipadm create-addr -t -T addrconf net0/v6
Instead of doing this manually I’ve taken the extra step and created an SMF manifest for IPv6.
I have a user-script
that downloads this from github, saves it to
/opt/custom/smf/ipv6.xml
and restarts manifest-import
. After the import is
finished, IPv6 can be enabled with svcadm
. Using the -r
flag enables all
dependencies (i.e., ndp
) as well.
svcadm enable -r site/ipv6
Enabling the service is also done as part of the user-script
.
If you do actually want specific static IPv6 assignment, do everthing I’ve
described above. Then, in addition to that use mdata-get sdc:nics
to pull the
NIC definition and extract the IPv6 addresses from allowed_ips
and explicitly
assign them. I admit that for those who want explicit static addresses this is
less than ideal, but with a little effort it can be scripted and made completely
automatic.