SysAdmin's Journey

Per-subnet Routing With Solaris 10 Non-global Zones

I had the chance to finally tinker with Solaris 10 zones the other day. They are impressive - very easy to setup! One of my biggest gripes about Solaris is that they've fallen way behind in the area of advanced IP routing. If you want to do things like policy routing in Solaris, you have to install ipfilter, which is no easy task. There is no alternative to iproute2 in Linux. Read after the jump to find a quick hack to setup proper routing for non-global zones on multiple subnets. Update: If you are using Solaris 10 Update 4 or newer, you may be able to use set ip-type=exclusive in your zone config. This will give you what I've hacked together here. Note, that you must have a dedicated interface for the zone in order to use it, and there are some other limitations, described here. Keep an eye on project Crossbow, which will hopefully make all of this go away. In Solaris 10, you set the default gateway at boot time by using /etc/defaultrouter. You can add multiple default routes by adding multiple lines to that file. However, if you do that, the kernel will simply round-robin the packets between the gateways, which will light up any firewall like a Christmas tree. Imagine the following scenario: HostA is a Solaris 10 box with 4 bge interfaces. HostA's IP address is 192.168.20.2/24 and is assigned to bge0. The router for the 192.168.20.0/24 subnet is at 192.168.20.1. HostB is a non-global zone residing on HostA, with an IP address of 192.168.30.2/24 and you've assigned it to bge1 via zonecfg. The router for the 192.168.30.0/24 subnet is at 192.168.30.1. Now, non-global zones don't have their own kernel, they "share" with the global zone. IP routing is handled in the kernel, so the routing configuration for the non-global zone needs to be done in the global zone. The first trick here is to get bge1 to come up on bootup, but not assign it an IP. Do this as root on HostA to establish that:

# echo "0.0.0.0" > /etc/hostname.bge1

This will plumb and bring up the interface, but it will not be assigned an IP. Now, remember what I said about the kernel round-robin routing packets earlier? It will only do that if both gateways are reachable. So, the trick is to make HostB's gateway unreachable from HostA, and HostA's gateway unreachable from HostB. There's a catch-22 here as well, you can only add a default route if the gateway is currently reachable. So how in the world do we get this to work??? With some nice hackery, of course! First, set HostB's configuration so that it does not boot automatically. Don't worry if you need this functionality, we've got a hack for that too:

root@HostA # zonecfg -z HostB
zonecfg:HostB> set autoboot=false
zonecfg:HostB> verify
zonecfg:HostB> commit
zonecfg:HostB> exit

Now, we are going to create our own init script that will bring up bge1 temporarily with HostB's IP, set the default route, then remove that IP. By using HostB's IP, we are allowed to set the route, but when we remove the interface, HostB's gateway becomes unreachable from HostA. Finally, we boot HostB, which sees only the default route for it's interface. Let's setup that init script:

# cat <<EOD > /etc/init.d/zoneboot
#!/usr/bin/sh

/usr/sbin/ifconfig bge1 addif 192.168.30.2/24 up
/usr/sbin/route add default 192.168.30.1
/usr/sbin/ifconfig bge1 removeif 192.168.30.2
/usr/sbin/zoneadm -z HostB boot
EOD

# ln -s /etc/init.d/zoneboot /etc/rc3.d/S99zoneboot

You can of course modify this script to work with more interfaces and zones, but you get the idea. Now, reboot HostA. HostA's routing table looks like so:

# netstat -rn

Routing Table: IPv4
  Destination           Gateway           Flags  Ref     Use     Interface
default              192.168.30.1          UG       1        119
default              192.168.20.1          UG       1        112
192.168.20.2         192.168.20.2          U        1         35 bge0
224.0.0.0            192.168.20.2          U        1          0 bge0
127.0.0.1            127.0.0.1             UH       4        113 lo0

HostA has two default gateways, but only one of them is reachable. Bingo! HostB's routing table looks like this:

Routing Table: IPv4
  Destination           Gateway           Flags  Ref     Use     Interface
default              192.168.30.1           UG      1        112
192.168.30.0         192.168.30.2           U       1         27 bge1:1
224.0.0.0            192.168.30.2           U       1          0 bge1:1
127.0.0.1            127.0.0.1              UH      4        108 lo0:2

Success! Hope you found this helpful!

Comments