vm-bhyve with NAT on FreeBSD

I’ve been running FreeBSD on my primary server for a while. There’s a number of things I like, and I’m enjoying the challenge of getting to grips with how the system is put together. It’s been a good challenge so far, with many highlights.

The idea of the server was to be VM host - bhyve is a lovely hypervisor to interact with, and I’ve tried a few management tools for it. Initially, i used iohyve which was lovely. It’s a really nice command syntax for interacting with VMs, and understands zfs which makes snapshotting dead easy. My favourite thing I managed was accessing the UEFI frame buffer over VNC - that was super cool. Granted, this is a bhyve thing and not restricted to iohyve, but it was still cool.

Unfortuntely, when the network configuration changed a little, I no longer had access to an external DHCP pool - I have one address only. By default, iohyve uses a bridge adaptor to communicate with the outside world, and addresses were coming from the network. All of a sudden, my VMs had no public IP.

I couldn’t really think of a way to make iohyve do NAT - there probably are ways, but none are really documented online. There are, however, ways of making vm-bhyve do NAT. Time to go back to the start! vm-bhyve is a different management tool, with largely the same featureset. It does, however, implement a virtual switch and allow me control over the switch. The vm-bhyve wiki details more about these virtual switches and how to implement NAT. I mixed this with the quick start guide to come up with the following steps:

1. pkg install vm-bhyve dnsmasq
2. sysrc gateway_enable="YES"
3. sysrc pf_enable="YES"
4. sysrc vm_enable="YES"
5. zfs create pool/vm
6. sysrc vm_dir="zfs:pool/vm"
7. echo "nat on bge0 from {10.13.37.1/24} to any -> (bge0)" >> /etc/pf.conf
8. vm init
9. cp /usr/local/share/examples/vm-bhyve/* /mountpoint/for/pool/vm/.templates/
10. vm switch create -a 10.13.37.1/24 public
11. sysctl net.inet.ip.forwarding=1
12. service pf start
13. cat << 'EOF' >> /usr/local/etc/dnsmasq.conf
port=0
domain-needed
no-resolv
nameserver 1.1.1.1
except-interface=lo0
bind-interfaces
local-service
dhcp-authoritative

interface=vm-public
dhcp-range=10.13.37.10,10.13.37.254
EOF

14. Service dnsmasq start

I think I’ve caught everything I did - I am able to bring up both Debian and FreeBSD VMs easily using the quick start guidelines and a scan of the manpage. Even better, they have networking and are able to ping each other and the outside world! Further to that, I’ve made a couple of templates and store them in /pool/vm/.templates - in these I have a definition of my default virtual disk size and more. I can share these if you ask, but the syntax is really easy to be honest.

Explore and play!


i'm hibby · blog ·