Showing posts with label freebsd. Show all posts
Showing posts with label freebsd. Show all posts

Saturday, June 13, 2026

Using QEMU Guest Agent with bhyve

I assume readers of the blog post are already familiar with FreeBSD and its bhyve hypervisor. Some might not be familiar with the qemu guest agent. Briefly, it's a process running within the guest which allows communicating between the hypervisor and the guest. It allows both querying information, such as disks, filesystems, network interfaces and a lot more. It also allows to manage guests, for example triggering a graceful reboot or shutdown, setting time, updating SSH keys and so forth.

Here's an overview on how to use the qemu guest agent on a FreeBSD guest running on a FreeBSD host with bhyve.

Host (bhyve) configuration

On the bhyve side, it needs to be started like that:

bhyve -c 4 -m 4096 -S -A -I -u -H -P \
   -s 0:0,hostbridge -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \
   -s 1:0,lpc \
   -s 2:0,virtio-console,org.qemu.guest_agent.0=/var/run/bhyve/bhyve.agent \
   -s 3:0,virtio-net,tap1 \
   -s 4:0,virtio-blk,/var/img/disk-freebsd.img \
   -l com1,stdio \
   freebsd

Here, the most important part for us is -s 2:0,virtio-console,org.qemu.guest_agent.0=/var/run/bhyve/bhyve.agent. This connects the org.qemu.guest_agent.0 to the /var/run/bhyve/bhyve.agent UNIX socket. It is not needed to create this socket manually, but it needs to be removed manually when the VM is destroyed.

Guest configuration

Install the qemu-guest-agent package:

# pkg install qemu-guest-agent

Enable it to start automatically on boot:

# sysrc qemu_guest_agent_enable="YES"
And also start it right away so we don't have to wait for the next reboot:
service qemu-guest-agent start

Testing

To test if the agent is actually working, run the following on the host:

echo '{"execute":"guest-get-host-name"}' | sudo nc -U /var/run/bhyve/bhyve.agent

That queries the guest's hostname. Now you should see a response similar to:

{"return": {"host-name": "freebsd"}}

And that's it, it's just that simple! If you want to use the guest agent manually or build something on top of it, please refer to the QEMU Guest Agent Protocol Reference.

Thanks for reading!

Saturday, February 21, 2026

Running FreeBSD (and bhyve!) on Pine Quartz64 Model A

I needed a cheap solution to run bhyve on FreeBSD/arm64. After some research, I decided to try the Quartz64 Model A board.

Requirements for bhyve on arm64 as per vmm(4) are:


     •   arm64: The boot CPU must start in EL2 and the system must have a
         GICv3 interrupt controller.  VHE support will be used if available.

The Quartz64 board satisfies these requirements, so I decided to give it a shot.

FreeBSD installation with u-boot

The FreeBSD Wiki page for Rockchip recommends using the RockPro64 image and using the loader from the sysutils/u-boot-quartz64-a port. It is also possible to use a UEFI-based installation, which I will cover later. I downloaded the latest CURRENT snapshot image for RockPro64 and flashed it to a USB memstick. Then I installed sysutils/u-boot-quartz64-a and flashed the loader to an SD card. I think it's possible to flash everything to the same media, but it's easier for me to have a working loader on the SD card while I experiment with the main OS on the USB memory stick.

The next step is to prepare a USB serial console. I bought one from Pine. Wiring is fairly simple, but do not forget to switch RX/TX.

To connect to the console I used:

sudo minicom -D /dev/cuaU0 -b 1500000

It should be enough to get started.

Issues

I have encountered some issues and limitations. Specifically:

  • The eqos NIC driver detects the device, but fails to attach. Solved by using an external RTL8153 based USB adapter.
  • bhyve(8) might not work if you run a FreeBSD version which does not include this commit.
  • NVMe/PCI does not work.

FWIW, this list is accurate for FreeBSD 16.0-CURRENT as of early/mid Feb 2026.

Not being able to use NVMe was a major deal breaker for me as it's not possible to get decent I/O rates without it, so I decided to test the UEFI approach.

FreeBSD installation with UEFI

For this setup, I kept my original memstick I used together with the u-boot SD card. Then I replaced the u-boot SD card with another one on which I flashed Quartz64 UEFI.

The console setup for it is somewhat different, so I used: cu -s 115200 -l /dev/ttyU0 to connect to it. Another detail: it allows you to enter the configuration menu before booting and choose one of UEFI, UEFI+devicetree, or devicetree modes. I'm using the UEFI mode.

The rest is fairly similar to the u-boot option.

What's different? The main difference for me is that NVMe finally works. Bhyve also works. However, there are some limitations, specifically:

  • Built-in devices are not detected without a device tree. That means no eMMC, no NIC, and maybe others.

I decided to settle on the UEFI option because I have an external USB NIC anyway, and NVMe is more important than eMMC to me. And bhyve works fine in this mode.

Here is how it looks now:

And the dmesg.

Sunday, January 4, 2026

Running bhyve Virtual Machines in a Jail

A couple of months ago I briefly described a libvirt CI setup that I run to make sure libvirt/bhyve keeps working.

One thing I'd like to cover in more depth is running bhyve in a jail. Why is that needed in the first place? My primary motivation was separating the host's libvirt instance from the one being tested. It also helps ensure that the host configuration and packages do not interfere with the ones being tested. I imagine, however, that there could be more cases where running bhyve in a jail makes sense, for example as an additional security measure.

I use Bastille to manage my jails, but the concept is the same for plain jails and probably other jail management software as well.

The settings I have in my Bastillefile are as follows. Some of these are not strictly required to run bhyve(8), but may be necessary to run libvirt, pf, and related tools. It is up to the reader to trim down anything that seems unnecessary.

CONFIG set allow.chflags=1;
CONFIG set allow.raw_sockets=1;

CONFIG set allow.vmm=1;
CONFIG set allow.mount;
CONFIG set allow.mount.devfs;
CONFIG set allow.mount.fdescfs;
CONFIG set allow.mount.procfs;
CONFIG set mount.devfs;
CONFIG set mount.fdescfs;
CONFIG set mount.procfs;
CONFIG set devfs_ruleset="44";

/etc/devfs.rules looks like this:

[devfs_rules_bhyve_jail=44]
add include $devfsrules_jail
add path vmm unhide
add path vmm/* unhide
add path tap* unhide
add path mem unhide
add path kmem unhide
add path nmdm* unhide
add path pci unhide
add path io unhide
add path pf unhide

As you can see, this also includes some extra stuff, such as pf, which you are of course free to remove or adjust to your liking.

And that's it -- this should be enough to get you started with running bhyve(8) in jail(8).

Saturday, September 20, 2025

Tutorial: managing bhyve virtual machines using virt-manager

The goal of this tutorial is to help you get started with virt-manager on FreeBSD for managing bhyve VMs. We’ll go through installation and basic configuration, and then set up a FreeBSD guest.

Installation

To install virt-manager, run:

# pkg install virt-manager

This will also pull in libvirt as a dependency.

Configuration

Edit /etc/rc.conf and add:

libvirtd_enable="YES"

You can validate your setup by running:

# virt-host-validate
 BHYVE: Checking for vmm module                                              : PASS
 BHYVE: Checking for if_tap module                                           : WARN (if_tap module is not loaded, networking will not work)
 BHYVE: Checking for if_bridge module                                        : PASS
 BHYVE: Checking for nmdm module                                             : PASS
#

The if_tap warning isn’t critical if it’s compiled into your kernel.

Next, adjust libvirt’s control socket permissions. Edit /usr/local/etc/libvirt/libvirtd.conf and add:

unix_sock_group = "wheel"
unix_sock_rw_perms = "0770"

The last prep step is to add a default network:

# cp /usr/local/share/examples/libvirt/networks/default.xml /usr/local/etc/libvirt/qemu/networks
# ln -s ../default.xml /usr/local/etc/libvirt/qemu/networks/autostart/default.xml

Now start libvirtd:

# service libvirtd start
Starting libvirtd.
#

And then start virt-manager:

$ virt-manager

Creating a VM

virt-manager initial screen

Go to "File" → "Add Connection", choose bhyve:

Add Connection screen

Click "bhyve", then open the "Virtual Networks" tab. You should see the default network listed:

Virtual network dialog

Press the "Play" button to start it.

Now create a new VM: "File" → "New Virtual Machine".

VM install dialog

Choose "Local install media" → "Forward". Then click "Browse" to select an ISO.

VM storage dialog

By default it shows /var/lib/libvirt/images, but you can pick an ISO from your home directory with "Browse Local".

VM choosing local media

Uncheck "Automatically detect from the installation media / source", search for "FreeBSD", and select "FreeBSD 14.2". (The auto-detection is buggy right now.)

The next step is CPU and memory setup — straightforward.

RAM and CPU dialog

Then configure storage:

VM storage configuration

If everything’s correct, you’ll see the FreeBSD installer:

Installer

I’ll let you handle the installation itself. The only important bit: at the end, make sure you shut the VM down. Boot order isn’t supported yet, so you don’t want to boot from the ISO again.

Installation complete

Once shut down, open "View" → "Details", find "SATA CDROM 1", right-click, and choose "Remove Hardware".

Remove CDROM

Now start the VM again — you should boot straight into your new FreeBSD system:

Fresh VM boot

That’s it for now. I plan to fix a few rough edges to make things smoother. In the meantime, feel free to send me suggestions or bug reports at novel@FreeBSD.org, or open a PR in FreeBSD Bugzilla.

Saturday, June 2, 2018

Configuring OpenBGPD to announce VM's virtual networks

We use BGP quite heavily at work, and even though I'm not interacting with that directly, it feels like it's something very useful to learn at least on some basic level. The most effective and fun way of learning technology is finding some practical application, so I decided to see if it could help to improve networking management for my Virtual Machines.

My setup is fairly simple: I have a host that runs bhyve VMs and I have a desktop system from where I ssh to VMs, both hosts run FreeBSD. All VMs are connected to each other through a bridge and have a common network 10.0.1/24. The point of this exercise is to be able to ssh to these VMs from desktop without adding static routes and without adding vmhost's external interfaces to the VMs bridge.

I've installed openbgpd on both hosts and configured it like this:

vmhost: /usr/local/etc/bgpd.conf

AS 65002
router-id 192.168.87.48
fib-update no

network 10.0.1.1/24

neighbor 192.168.87.41 {
    descr "desktop"
    remote-as 65001
}

Here, router-id is set vmhost's IP address in my home network (192.168.87/24), fib-update no is set to forbid routing table update, which I initially set for testing, but keeping it as vmhost is not supposed to learn new routes from desktop anyway. network announces my VMs network and neighbor describes my desktop box.

Now the desktop box:

desktop: /usr/local/etc/bgpd.conf

AS 65001
router-id 192.168.87.41
fib-update yes

neighbor 192.168.87.48 {                                                                                                                                                                                           
        descr "vmhost"                                                                                                                                                                                             
        remote-as 65002                                                                                                                                                                                            
}

It's pretty similar to vmhost's bgpd.conf, but no networks are announced here, and fib-update is set to yes because the whole point is to get VM routes added.

Both hosts have to have the openbgpd service enabled:

/etc/rc.conf.local

openbgpd_enable="YES"

Now start the service (or wait until next reboot) using service openbgpd start and check if neighbors are there:

vmhost: bgpctl show summary

$ bgpctl show summary                                                                                                                                                                    
Neighbor                   AS    MsgRcvd    MsgSent  OutQ Up/Down  State/PrfRcvd                                                                                                                                   
desktop                 65001       1089       1090     0 09:03:17      0                                                                                                                                          
$

desktop: bgpctl show summary

$ bgpctl show summary
Neighbor                   AS    MsgRcvd    MsgSent  OutQ Up/Down  State/PrfRcvd
vmhost                  65002       1507       1502     0 09:04:58      1
$

Get some detailed information about the neighbor:

desktop: bgpctl sh nei vmhost

$ bgpctl sh nei vmhost                                                                                                                                                                    
BGP neighbor is 192.168.87.48, remote AS 65002                                                                                                                                                                     
 Description: vmhost                                                                                                                                                                                               
  BGP version 4, remote router-id 192.168.87.48                                                                                                                                                                    
  BGP state = Established, up for 09:06:25                                                                                                                                                                         
  Last read 00:00:21, holdtime 90s, keepalive interval 30s                                                                                                                                                         
  Neighbor capabilities:                                                                                                                                                                                           
    Multiprotocol extensions: IPv4 unicast                                                                                                                                                                         
    Route Refresh                                                                                                                                                                                                  
    Graceful Restart: Timeout: 90, restarted, IPv4 unicast                                                                                                                                                         
    4-byte AS numbers                                                                                                                                                                                              
                                                                                                                                                                                                                   
  Message statistics:                                                                                                                                                                                              
                  Sent       Received                                                                                                                                                                              
  Opens                    3          3                                                                                                                                                                            
  Notifications            0          2                                                                                                                                                                            
  Updates                  3          6                                                                                                                                                                            
  Keepalives            1499       1499                                                                                                                                                                            
  Route Refresh            0          0                                                                                                                                                                            
  Total                 1505       1510                                                                                                                                                                            
                                                                                                                                                                                                                   
  Update statistics:                                                                                                                                                                                               
                  Sent       Received                                                                                                                                                                              
  Updates                  0          1                                                                                                                                                                            
  Withdraws                0          0                                                                                                                                                                            
  End-of-Rib               1          1                                                                                                                                                                            
                                                                                                                                                                                                                   
  Local host:         192.168.87.41, Local port:    179                                                                                                                                                            
  Remote host:        192.168.87.48, Remote port: 13528                                                                                                                                                            
                                                                                                                                                                                                                   
$

By the way, as you can see, bgpctl supports shortened commands, e.g. sh nei instead of show neighbor.

Now look for that VMs route:

desktop: bgpctl show rib

$ sudo bgpctl show rib
flags: * = Valid, > = Selected, I = via IBGP, A = Announced, S = Stale
origin: i = IGP, e = EGP, ? = Incomplete

flags destination          gateway          lpref   med aspath origin
*>    10.0.1.0/24          192.168.87.48      100     0 65002 i
$

So that VMs network, 10.0.1/24, it's there! Now check if the system routing table was updated and has this route:

desktop

$ route -n get 10.0.1.45   
   route to: 10.0.1.45
destination: 10.0.1.0                                                                                                                                                                                              
       mask: 255.255.255.0                                                                                                                                                                                         
    gateway: 192.168.87.48                                                                                                                                                                                         
        fib: 0                                                                                                                                                                                                     
  interface: re0                                                                                                                                                                                                   
      flags:                                                                                                                                                                               
 recvpipe  sendpipe  ssthresh  rtt,msec    mtu        weight    expire                                                                                                                                             
       0         0         0         0      1500         1         0                                                                                                                                               
$ ping -c 1 10.0.1.45                                                                                                                                                                     
PING 10.0.1.45 (10.0.1.45): 56 data bytes                                                                                                                                                                          
64 bytes from 10.0.1.45: icmp_seq=0 ttl=63 time=0.192 ms                                                                                                                                                           
                                                                                                                                                                                                                   
--- 10.0.1.45 ping statistics ---                                                                                                                                                                                  
1 packets transmitted, 1 packets received, 0.0% packet loss                                                                                                                                                        
round-trip min/avg/max/stddev = 0.192/0.192/0.192/0.000 ms                                                                                                                                                         
$

Whoa, things work as expected!

Conclusion

As mentioned already, similar result could be achieved without using BGP by using either static routes or bridging interfaces differently, but the purpose of this exercise is to get some basic hands-on experience with BGP. Right now I'm looking into extending my setup in order to try more complex BGP schema. I'm thinking about adding some software switches in front of my VMs or maybe adding a second VM host (if budget allows). You're welcome to comment if you have some ideas how to extend this setup for educational purposes in the context of BGP and networking.

As a side note, I really like openbgpd so far. Its configuration file format is clean and simple, documentation is good, error and information messages are clear, and CLI has intuitive syntax.

Friday, November 4, 2016

bhyve vs VirtualBox benchmarking, part2

"There are in order of increasing severity: lies, damn lies, statistics, and computer benchmarks",
man 8 diskinfo

I got some feedback to my previous post about benchmarking of bhyve and VirtualBox:

So I decided to do some more tests and include e1000 for networking tests and try different drivers and image types for I/O tests.

Networking test

This is a little more extensive test than the one in my previous blog post, now it includes e1000 and virtio-net for both VirtualBox and bhyve. Setting for the test is still the same: iperf and bridged network mode. Commands remain the same, on VM side I run:

iperf -s

On the host, I run:

iperf -c $vmip

And calculate the average of 8 runs. Results:

And actual values (in Gbits/sec) are:

VirtualBoxbhyve
e10001.428751.57375
virtio-net0.432752.4

The most shocking part here is that in VirtualBox e1000 is more than 3x times faster than virtio-net. This seems a little strange, esp. considering that e1000 performance in bhyve is almost the same, but virtio-net in bhyve is approx. 1.5x times faster than e1000 (that's probably a very huge difference too, but at least it's expected virtio to be faster).

I/O testing

I decided to check things suggested in the tweet above and started with disk configuration. I've converted my image to the "fixed size" type image like this:

VBoxManage clonehd uefi_fbsd_20gigs.vdi uefi_fbsd_20gigs_fixed.vdi --variant Fixed

Then I conducted tests for the following configurations:

  • VirtualBox + fixed size image
  • VirtualBox + dynamic size image
  • bhyve + virtio-blk
  • bhyve + ahci-hd

The same image was used for all tests. Fixed size image was produced using the command specified above, raw image for bhyve was created from the VirtualBox image using the qemu-img(1) tool.

I started with bonnie++ test at results surprised me, to put it softly:

Here, we can see that bhyve with virtio-blk shows the best write speed (that is expected), but then shows the worst rewrite speed (which is a little surprising, but the gap is minimal) and shows the worst read speed (more than 2 times slower than VirtualBox; extremely surprising). After that I decided to take a few days break and then to try some different ways of benchmarking.

So, for read performance I used the diskinfo(8) tool this way:

diskinfo -tv ada0 | grep middle

and use the average of 16 runs. For writing, I used dd(1):

dd bs=1M count=2048 if=/dev/zero of=test conv=sync; sync; rm test

and also use the average of 16 runs. I got the following results:

And the numbers (all values are kbytes/sec):

vbox (fixed size img)vbox (dynamic img)bhyve (ahci-hd)bhyve (virtio-blk)
diskinfo1232397152877912960552647685
dd113737135088113889115924

Frankly, this didn't help to figure out state of things, because these results are kind of opposite to what bonnie++ showed: bhyve with virtio-blk shows very high read speed as demonstrated by diskinfo, 1.7x faster than VirtualBox. On the other hand, the numbers that diskinfo is showing are crazy: 2647 mbytes/sec. This feels more like RAM transfer rates, so it looks like some sort of caching is involved here.

As for the dd(1) test part, bhyve with virtio-blk is 16.5% slower than VirtualBox using dynmanic size image.

Conclusion

  • For VirtualBox, if choosing between e1000 and virtio-net, e1000 definitely provides better performance over virtio-net, at least on FreeBSD hosts with FreeBSD guests
  • virtio-net in bhyve is approx. 1.5x faster than VirtualBox with e1000
  • I'll refrain from comments on I/O tests.

Further Reading

Sunday, October 16, 2016

bhyve vs VirtualBox benchmark

I've always been curious how bhyve performance compares to other hypervisors, so I've decided to compare it with VirtualBox. Target audience of these projects is somewhat different, though. VirtualBox is targeted more to desktop users (as it's easy to use, has a nice GUI and guest additions for running GUI within a guest smoothly). And bhyve appears to be targeting more experienced users and operators as it's easier to create flexible configurations with it, as well as automate things. Anyway, it's still interesting to find out which one is faster.

Setup Overview

I used my development box for this benchmarking, and it has no additional load, so results should be more or less clean in that matter. It's running 12-CURRENT:

FreeBSD 12.0-CURRENT amd64

as of Oct, 4th.

It has Intel i5 CPU, 16 gigs of RAM and some old 7200 IDE HDD:

CPU: Intel(R) Core(TM) i5-4690 CPU @ 3.50GHz (3491.99-MHz K8-class CPU)
  Origin="GenuineIntel"  Id=0x306c3  Family=0x6  Model=0x3c  Stepping=3
  Features=0xbfebfbff<FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CLFLUSH,DTS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE>
  Features2=0x7ffafbff<SSE3,PCLMULQDQ,DTES64,MON,DS_CPL,VMX,SMX,EST,TM2,SSSE3,SDBG,FMA,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,TSCDLT,AESNI,XSAVE,OSXSAVE,AVX,F16C,RDRAND>
  AMD Features=0x2c100800<SYSCALL,NX,Page1GB,RDTSCP,LM>
  AMD Features2=0x21<LAHF,ABM>
  Structured Extended Features=0x2fbb<FSGSBASE,TSCADJ,BMI1,HLE,AVX2,SMEP,BMI2,ERMS,INVPCID,RTM,NFPUSG>
  XSAVE Features=0x1<XSAVEOPT>
  VT-x: PAT,HLT,MTF,PAUSE,EPT,UG,VPID
  TSC: P-state invariant, performance statistics
real memory  = 17179869184 (16384 MB)
avail memory = 16448757760 (15686 MB)
ada0: <ST3200822A 3.01> ATA-6 device

For tests I used two VMs, one in bhyve and one in VirtualBox. A bhyve VM was started like this:

bhyve -c 2 -m 4G -w -H -S \
        -s 0,hostbridge \
        -s 4,ahci-hd,/home/novel/img/uefi_fbsd_20gigs.raw \
        -s 5,virtio-net,tap1 \
        -s 29,fbuf,tcp=0.0.0.0:5900,w=800,h=600,wait \
        -s 30,xhci,tablet \
        -s 31,lpc -l com1,stdio \
        -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \
        vm0

Main pieces of the VirtualBox VM configuration are displayed on these screenshots:

Guest is:

FreeBSD 11.0-RELEASE-p1 amd64

on UFS. VirtualBox version 5.1.6 r110634 installed via pkg(8).

World Compilation

The first test is running:

make buildworld buildkernel -j4

on releng/11.0 source tree.

The result is that bhyve is approx. 15% slower (106 minutes vs 92 minutes for VirtualBox):

iperf test

The next test is network performance check using the iperf(8) tool. In order to avoid interaction with hardware NICs and switches (with unpredictable load), all the traffic for testing stays withing the host system:

vm# iperf -s
host# iperf -c $vmip

As a reminder, both bhyve and VirtualBox VMs are configured to use bridged networking. Also, both are using virtio-net NICs.

Result is a little strange because bhyve appears to be more than 4 times faster here:

That seemed to be strange to me, so I ran the test multiple times for both VirtualBox and bhyve, however, all the time the results were pretty close. I'm yet to find out if that's really correct result or something wrong with my testing or setup.

bonnie++ test

bonnie++ is a benchmarking tool for hard drivers and filesystems. Let's jump to the results right away:

This makes bhyve write and read speeds approx. 15% higher than VirtualBox. There's a note though: for VirtualBox VM I used VDI disk format (as it's native for VirtualBox) and SATA emulation because there's no virtio-blk support in VirtualBox. In case of bhyve I also used SATA emulation (I actually intended to use virtio-blk here, but forgot to change commandline), but on a raw image instead.

xz(1) on memory disk test

The last test is to unpack and pack an XZ archive on memory disk. I used memory disk specifically to exclude disk I/O from the equation. For a sample archive I choose:

ftp://ftp.freebsd.org/pub/FreeBSD/releases/ISO-IMAGES/11.0/FreeBSD-11.0-RELEASE-amd64-memstick.img.xz

And the commands to unpack and pack were:

unxz FreeBSD-11.0-RELEASE-amd64-memstick.img.xz 
xz FreeBSD-11.0-RELEASE-amd64-memstick.img

Results are almost the same here:

Summary

  • CPU and RAM performance seems to be identical (though it's not clear why buildworld takes longer on bhyve)
  • I/O performance is 15% better with bhyve
  • Networking performance is 4x better with bhyve (this looks suspicious and requires additional research; also, I'm not familiar with bridged networking implementation in VirtualBox, maybe it could explain the difference)

Further Reading

PS: Initially I was going to use phoronix-test-suite. However, it appears that a lot of important tests fail to run on FreeBSD. The ones that did run, such as apache or sqlite tests, do not seem very representative to me. I'll probably try to run similar tests but with Linux guest.

Tuesday, October 4, 2016

Bhyve Networking Options

Once in a while people on the #bhyve IRC channel on freenode ask questions about bhyve networking configuration, i.e. how to configure things to let a VM have network access.

There are at least 3 ways to do that (that I'm aware of, maybe there are more):

  • Bridged networking
  • NAT
  • NIC Passthrough

I'll try to go over each of those and describe how things work in each scheme.

Common configuration

Common things for all the setups: I'm running FreeBSD 12-CURRENT amd64, I'm having two NICs (re0 and re1), both connected to a home router.

Bridged Networking

Bridged networking, just like name suggests, means bridging together VM interfaces and the uplink interface, putting those in the same L2 segment.

Configuration is relatively straight-forward. Let's start from a completely fresh configuration, where we don't even have re1 (uplink) configured:

kloomba# ifconfig re1
re1: flags=8802 metric 0 mtu 1500
        options=8209b
        ether 18:a6:f7:01:66:52
        nd6 options=29
        media: Ethernet autoselect (100baseTX )
        status: active
kloomba#

Now let's create a bridge named brextand add re1 to it. Also, we'll run dhclient on it to obtain an IP address (in my setup it comes from a DHCP server running on my home router):

kloomba# ifconfig bridge create name brext                                                                                                                                                
kloomba# ifconfig brext addm re1
kloomba# ifconfig brext up
kloomba# ifconfig re1 up
kloomba# dhclient brext

As a result we have an IP address assigned to the brext bridge:

brext: flags=8843 metric 0 mtu 1500
        ether 02:29:bb:66:56:01
        inet 192.168.87.46 netmask 0xffffff00 broadcast 192.168.87.255 
        nd6 options=1
        groups: bridge 
        id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15
        maxage 20 holdcnt 6 proto rstp maxaddr 2000 timeout 1200
        root id 00:00:00:00:00:00 priority 32768 ifcost 0 port 0
        member: re1 flags=143
                ifmaxaddr 0 port 2 priority 128 path cost 200000

Now we need to create a tap (that will be tap1 in my case) interface for VM and boot it up:

kloomba# ifconfig tap create up
kloomba# ifconfig brext addm tap1

And boot a VM like in a way you like, for example:

bhyve -c 2 -m 4G -w -H \
        -s 0,hostbridge \
        -s 3,ahci-cd,/home/novel/FreeBSD-11.0-CURRENT-amd64-20160217-r295683-disc1.iso \
        -s 5,virtio-net,tap1 \
        -s 29,fbuf,tcp=0.0.0.0:5900,w=800,h=600,wait \
        -s 30,xhci,tablet \
        -s 31,lpc -l com1,stdio \
        -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \
        vm0

Now we can open up a VNC client and connect to this VM (I use vncviewer :0) and do the following:

vm# dhclient vtnet0

If things go well, you'll get an IP address from the same subnet as IP address on host's re1 and, obviously, it's served by the same DHCP server that serves the host's re1.

### host ###
kloomba# ifconfig brext
brext: flags=8843 metric 0 mtu 1500
        ether 02:29:bb:66:56:01
        inet 192.168.87.46 netmask 0xffffff00 broadcast 192.168.87.255
        nd6 options=1
        groups: bridge 
        id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15
        maxage 20 holdcnt 6 proto rstp maxaddr 2000 timeout 1200
        root id 00:00:00:00:00:00 priority 32768 ifcost 0 port 0
        member: tap1 flags=143
                ifmaxaddr 0 port 10 priority 128 path cost 2000000
        member: re1 flags=143
                ifmaxaddr 0 port 2 priority 128 path cost 200000
kloomba# 

### vm ###
root@vm0:~ # ifconfig vtnet0
vtnet0: flags=8943 metric 0 mtu 1500
        options=80028
        ether 00:a0:98:1b:c8:07
        inet 192.168.87.47 netmask 0xffffff00 broadcast 192.168.87.255
        nd6 options=29
        media: Ethernet 10Gbase-T 
        status: active
root@vm0:~ # 

To understand a little better what's going on here, let's run ping on a VM:

vm# ping 8.8.8.8

... and check how it looks like on the host:

kloomba# tcpdump -qni brext -c2 -e host 8.8.8.8 
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on brext, link-type EN10MB (Ethernet), capture size 65535 bytes
16:09:44.755083 00:a0:98:1b:c8:07 > 40:4a:03:76:de:1d, IPv4, length 98: 192.168.87.47 > 8.8.8.8: ICMP echo request, id 25603, seq 4, length 64
16:09:44.783499 40:4a:03:76:de:1d > 00:a0:98:1b:c8:07, IPv4, length 98: 8.8.8.8 > 192.168.87.47: ICMP echo reply, id 25603, seq 4, length 64
2 packets captured
6 packets received by filter
0 packets dropped by kernel
kloomba# 

What we can see here? Packets from our VM are leaving the host with the IP address it has on vtnet0, MAC address is also vtnet0's MAC. BTW, 40:4a:03:76:de:1d is MAC of my router.

That is it, it works, does not need stuff like firewalls or routing configuration to work, so this approach is relatively easy. There are downsides of that, however. It's pretty common that the router your PC is connected to is configured to only pass only single MAC per networking port, or maybe even only a whitelisted MAC (that's quite common in office environments) or if your home router does not support that. In that case you'll have to go with the NAT approach that I'll describe next.

NAT Networking

Let's assume we're starting from scratch and don't have that brext bridge we created in the previous section. And we're again starting with creation of the new bridge:

kloomba# ifconfig bridge create name brnat up
kloomba# ifconfig tap create up
kloomba# ifconfig brnat addm tap1
brnat: flags=8843 metric 0 mtu 1500
        ether 02:29:bb:66:56:01
        nd6 options=1
        groups: bridge 
        id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15
        maxage 20 holdcnt 6 proto rstp maxaddr 2000 timeout 1200
        root id 00:00:00:00:00:00 priority 32768 ifcost 0 port 0
        member: tap1 flags=143
                ifmaxaddr 0 port 10 priority 128 path cost 55
kloomba# 

As we can see, we no longer have our uplink interface re1 in the bridge. Now let's start a VM, command will be exactly the same as in the previous section, so I won't repeat it here.

Now, if we go to the VM and try to do dhclient vtnet0 nothing happens, because there are no DHCP server reachable from this VM. It's a good time to decide what IP range we'll use for our VM(s). Let's go with something like 10.0.0.0/24. Let's configure pf to do NATing for us. Basic /etc/pf.conf for this purpose might look like this:

ext_if="re1"

virt_net="10.0.0.0/24"

scrub all

nat on $ext_if from $virt_net to any -> ($ext_if)

pass log all

What we're doing here? For packets coming from $virt_net (our VMs range) we're translating its source address from 10.0.0.0/24 internal net to address of our external interface (re1). Now we can load the rules, enable pf and check if that works.

kloomba# pfctl -f /etc/pf.conf
kloomba# pfctl -e
pfctl: pf already enabled
kloomba# 

We also need to assign a proper IP address to our bridge:

kloomba# ifconfig brnat inet 10.0.0.1/24

Also, it's a good time to ensure that IP forwarding is enabled on the host: sysctl net.inet.ip.forwarding=1.

Now VM is expected to get connectivity if we manually assign an IP address to it:

vm0# ifconfig vtnet0 inet 10.0.0.2/24 up
vm0# route add default 10.0.0.1

Now things should work and we can actually see what's going on with the packets. Let's start ping again in our VM: ping 8.8.8.8 and tcpdump on interfaces on the host. Let's start with brnat:

kloomba# tcpdump -ni brnat -c 2 -e host 8.8.8.8
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on brnat, link-type EN10MB (Ethernet), capture size 65535 bytes
17:28:36.101514 00:a0:98:1b:c8:07 > 02:29:bb:66:56:01, ethertype IPv4 (0x0800), length 98: 10.0.0.2 > 8.8.8.8: ICMP echo request, id 21507, seq 62, length 64
17:28:36.129840 02:29:bb:66:56:01 > 00:a0:98:1b:c8:07, ethertype IPv4 (0x0800), length 98: 8.8.8.8 > 10.0.0.2: ICMP echo reply, id 21507, seq 62, length 64
2 packets captured
2 packets received by filter
0 packets dropped by kernel
kloomba# 

And on our uplink interface re1:

kloomba# tcpdump -ni re1 -c 2 -e host 8.8.8.8                                                                                                                                                                       
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on re1, link-type EN10MB (Ethernet), capture size 65535 bytes
17:31:37.898499 18:a6:f7:01:66:52 > 40:4a:03:76:de:1d, ethertype IPv4 (0x0800), length 98: 192.168.87.44 > 8.8.8.8: ICMP echo request, id 19102, seq 236, length 64
17:31:37.926781 40:4a:03:76:de:1d > 18:a6:f7:01:66:52, ethertype IPv4 (0x0800), length 98: 8.8.8.8 > 192.168.87.44: ICMP echo reply, id 19102, seq 236, length 64
2 packets captured
3 packets received by filter
0 packets dropped by kernel
kloomba# 

We can see that at this point no information about VM is exposed here (i.e. no VM subnet 10.0.0.0/24, no vtnet0 MACs etc); NAT works as expected.

As you can see, NAT networking is a little more complex configuration-wise. Though it's probably the most general solution, you don't have to rely on external routers configuration, bridging support in the hardware/drivers and so forth.

This configuration can be simplified though, a good step to it would be configuring DHCP server on brnat to serve IP addresses from our VM range. This could be done for example using the dns/dnsmasq tiny DHCP server.

NIC Passthrough

This is a somewhat fun way to setup networking because a) you'll need 1 (one) physical NIC per VM b) you'll need one more physical NIC for host if you want it to keep connected. This might be much better with SR-IOV though I've never tried SR-IOV cards on FreeBSD. Anyway, back to the point.

I'm going to passthrough re1, I'm running pciconf -l -v to find its PCI address:

re1@pci0:3:0:0:        class=0x020000 card=0x85051043 chip=0x816810ec rev=0x09 hdr=0x00
    vendor     = 'Realtek Semiconductor Co., Ltd.'
    device     = 'RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller'
    class      = network
    subclass   = ethernet

So I add pptdevs="3/0/0" to /boot/loader.conf and reboot. After reboot it looks this way:

ppt0@pci0:3:0:0:        class=0x020000 card=0x85051043 chip=0x816810ec rev=0x09 hdr=0x00
    vendor     = 'Realtek Semiconductor Co., Ltd.'
    device     = 'RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller'
    class      = network
    subclass   = ethernet

Now starting a VM like this:

bhyve -c 2 -m 1G -w -H -S \
        -s 0,hostbridge \
        -s 4,ahci-hd,/home/novel/img/uefi_fbsd.raw \
        -s 6,passthru,3/0/0 \
        -s 29,fbuf,tcp=0.0.0.0:5900,w=800,h=600,wait \
        -s 30,xhci,tablet \
        -s 31,lpc -l com1,stdio \
        -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \
        vm0

If things go well (i.e.: host supports IOMMU, device supports passthrough, ...), we'll see this device in a VM exactly like it would appear in host:

re0@pci0:0:6:0: class=0x020000 card=0x85051043 chip=0x816810ec rev=0x09 hdr=0x00
    vendor     = 'Realtek Semiconductor Co., Ltd.'
    device     = 'RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller'
    class      = network
    subclass   = ethernet

At this point it can be used just like it was not a VM but another host connected to a network with its own NIC. Once can run dhclient re0 etc.

Further reading

Update Oct, 17th, 2016: added pics.

Sunday, June 7, 2015

OpenStack on FreeBSD/Xen Proof of Concept

In my previos post I described how to run libvirt/libxl on the FreeBSD Xen dom0 host. Today we're going a little further and run OpenStack on top of that.

Screenshot showing the Ubuntu guest running on OpenStack on the FreeBSD host.

Setup Details

I'm running a slightly modified OpenStack stable/kilo version. Everything is deployed on two hosts: controller and compute.

Controller

Controller host is running FreeBSD -CURRENT. It has the following components:

  • MySQL 5.5
  • RabbitMQ 3.5
  • glance
  • keystone through apache httpd 2.4 w/ mod_wsgi

Everything here is installed through FreeBSD ports (except glance and keystone) and don't require any modifications.

For glance I wrote rc.d to have a convenient ways to start it:

(18:19) novel@kloomba:~ %> sudo service glance-api status
glance_api is running as pid 792.
(18:19) novel@kloomba:~ %> sudo service glance-registry status
glance_registry is running as pid 796.
(18:19) novel@kloomba:~ %>

Compute

Compute node is running the following:

  • libvirt from the git repo
  • nova-compute
  • nova-scheduler
  • nova-conductor
  • nova-network

This hosts is running FreeBSD -CURRENT as well. I also wrote some rc.d scripts for nova services except nova-network and nova-compute because I start it by hand and want to see logs right on the screen.

Nova-network is running in the FlatDHCP mode. For Nova I had to implement a FreeBSD version of the linux_net.LinuxNetInterfaceDriver that's responsible for bridge creation and plugging devices into it. It doesn't support vlans at this point though.

Additionally, I have implemented NoopFirewallManager to be used instead linux_net.IptablesManager and modified nova to allow to specify firewall driver to use.

Few more things I modified is fixing network.l3.NullL3 class mismatching interface and modified virt.libvirt to use the 'phy' driver for disks in libvirt domains XML.

And of course I had to disable a few things in nova.conf that obviously not work on FreeBSD.

I hope to put everything together and upload the code on github and create some wiki page documenting the deployment. It's definitely worth to note that things are very very far from being stable. There are some traces here and there, VMs sometimes fail to start, xenlight for some reason could start failing at VMs startup etc etc etc. So if you're looking at it as a production tool, you should definitely forget about it, at this point it's just a thing to hack on.

Tuesday, June 2, 2015

libvirt/libxl on FreeBSD

Few months ago FreeBSD Xen dom0 support was announced. There's even a guide available how to run it: http://wiki.xen.org/wiki/FreeBSD_Dom0.

I will not duplicate stuff described in that document, just suggest that if you're going to try it, it'd probably be better to use the port emulators/xen instead of compiling stuff manually from the git repo.. I'll just share some bits that probably could save some of your time.

X11 and Xen dom0

I wasn't able to make X11 work under dom0. When I startx with the x11/nvidia-driver enabled in xorg.conf, kernel panics. I tried to use an integrated Intel Haswell video, but it's not supported by x11-drivers/xf86-video-intel. It works with x11-driver/xf86-video-vesa, however, the vesa driver causes system lock up on shutdown that triggers fsck every time on the next boot and it's very annoying. Apparently, this behavior is the same even when not under Xen. I decided to stop wasting my time on trying to fix it and just started using it in a headless mode.

IOMMU

You should really not ignore the IOMMU requirement and check if your CPU supports that. If you boot Xen kernel and you don't have IOMMU support, it will fail to boot and you'll have to perform some boot loader tricks to disable Xen to boot your system (i.e. do unload xen and unset xen_kernel). Just google up your CPU name, e.g. 'i5-4690' and follow the link to ark.intel.com. Make sure that it lists VT-d as supported under the 'Advanced Technologies' section. Also, make sure it's enabled in BIOS as well.

UEFI

At the time of writing (May / June 2015), Xen doesn't work with the UEFI loader.

xl cannot allocate memory

You most likely will have to modify your /etc/login.conf to set memorylocked=unlimited for your login class, otherwise the xl tool will fail with some 'cannot allocate memory' error.

libvirt

It's very good that Xen provides the libxl toolkit. It should have been installed when you installed the emulators/xen port as a dependency. The actual port that installs it is sysutils/xen-tools. As the libvirt Xen driver supports libxl, there's not so much work required to make it work on FreeBSD. I made only a minor change to disable some Linux specific /proc checks inside libvirt to make it work on FreeBSD and pushed that to the 'master' branch of libvirt today.

If you want to test it, you'd need to checkout libvirt source code using git:

git clone git://libvirt.org/libvirt.git

and then run ./bootstrap. It will inform if it needs something that's not installed.

For my libxl test setup I configure libvirt this way:

./configure --without-polkit --with-libxl --without-xen --without-vmware --without-esx --without-bhyve CC=gcc48 CFLAGS=-I/usr/local/include LIBS=-L/usr/local/lib

The only really important part here is the '--with-libxl', other flags are more or less specific to my setup. After configure just run gmake and it should build fine. Now you can install everything and run the libvirtd daemon.

If everything went fine, you should be able to connect to it using:

virsh -c "xen://"

Now we can define some domains. Let's check these two examples:

The first one is for a simple pre-configured FreeBSD guest image. The second one defines CDROM device and hard disk devices. It's set to boot from CDROM to be able to install Linux. Both domains are configured to attach to the default libvirt network on the virbr0 bridge. Additionally, both domains support VNC.

You could get domain VNC display number using the vncdisplay command in virsh and then connect to a VM with your favorite VNC client.

I've been using this setup for a couple of days and it works fine. However, more testers are welcome, if you're using it and have some issues please drop me an email to novel@`uname -s`.org or poke me on twitter.

Tuesday, August 26, 2014

ZFS support in libvirt

An upcoming release of libvirt, 1.2.8 that should be released early September, will include an initial support of managing ZFS volumes.

That means that it's possible to boot VMs and use ZFS volumes as disks. Additionally, it allows to control volumes using the libvirt API. Currently, supported operations are:

  • list volumes in a pool
  • create and delete volumes
  • upload and download volumes

It's not possible to create and delete pools yet, hope to implement that in the next release.

Defining a pool

Assume we have some pools and want to use one of them in libvirt:

# zpool list
NAME       SIZE  ALLOC   FREE   FRAG  EXPANDSZ    CAP  DEDUP  HEALTH  ALTROOT
filepool  1,98G  56,5K  1,98G     0%         -     0%  1.00x  ONLINE  -
test       186G  7,81G   178G     0%         -     4%  1.00x  ONLINE  -

Let's take filepool and define it with libvirt. This could be done using this virsh command:

virsh # pool-define-as --name zfsfilepool --source-name filepool --type zfs
Pool zfsfilepool defined

virsh #  pool-start zfsfilepool
Pool zfsfilepool started

virsh # pool-info zfsfilepool
Name:           zfsfilepool
UUID:           5d1a33a9-d8b5-43d8-bebe-c585e9450176
State:          running
Persistent:     yes
Autostart:      no
Capacity:       1,98 GiB
Allocation:     56,50 KiB
Available:      1,98 GiB

virsh # 

As you can see, we specify a type of the pool, its source name, such as seen in zpool list output and a name for it in libvirt. We also need to start it using the pool-start command.

Managing volumes

Let's create a couple of volumes in our new pool.

virsh # vol-create-as --pool zfsfilepool --name vol1 --capacity 1G
Vol vol1 created

virsh # vol-create-as --pool zfsfilepool --name vol2 --capacity 700M
Vol vol2 created

virsh # vol-list zfsfilepool
 Name                 Path                                    
------------------------------------------------------------------------------
 vol1                 /dev/zvol/filepool/vol1                 
 vol2                 /dev/zvol/filepool/vol2                 

virsh #

Dropping a volume is also easy:

virsh # vol-delete --pool zfsfilepool vol2
Vol vol2 deleted

Uploading and downloading data

Let's upload an image to our new volume:

virsh # vol-upload --pool zfsfilepool --vol vol1 --file /home/novel/FreeBSD-10.0-RELEASE-amd64-memstick.img 

... and download

virsh # vol-download --pool zfsfilepool --vol vol1 --file /home/novel/zfsfilepool_vol1.img

Note: if you would check e.g. md5 sum of the downloaded files, the result would be different as downloaded file will be of the same size as a volume. However, if you trim zeros, it'll be the same.

$ md5 FreeBSD-10.0-RELEASE-amd64-memstick.img zfsfilepool_vol1.img 
MD5 (FreeBSD-10.0-RELEASE-amd64-memstick.img) = e8e7cbd41b80457957bd7981452ecf5c
MD5 (zfsfilepool_vol1.img) = a77c3b434b01a57ec091826f81ebbb97
$ truncate -r FreeBSD-10.0-RELEASE-amd64-memstick.img zfsfilepool_vol1.img
$  md5 FreeBSD-10.0-RELEASE-amd64-memstick.img zfsfilepool_vol1.img             
MD5 (FreeBSD-10.0-RELEASE-amd64-memstick.img) = e8e7cbd41b80457957bd7981452ecf5c
MD5 (zfsfilepool_vol1.img) = e8e7cbd41b80457957bd7981452ecf5c
$

Booting a VM from volume

Finally got to the most important part. In use a volume as disk device for VM 'devices' section of the domain XML should be updated with something like this:

    <disk type='volume' device='disk'>
      <source pool='zfsfilepool' volume='vol1'/>
      <target dev='vdb' bus='virtio'/>
    </disk>

Few notes

Note #1: this code is just a few weeks old, so quite likely there are some rough edges. Feel free to report problems to novel%freebsd.org if you spot any problems.

Note #2: this code is FreeBSD-only for now. However, it should not be hard to make it work on Linux with zfsonlinux.org. Its developers were kind enough to add some useful missing flags in some of the CLI tools. However, these changes are not available in any released version so far. There are some more minor differences between zfs on Linux and FreeBSD, but that should not be hard to address. I was planning to get to it as soon as a new version of zfs on linux with the necessary flags is available. However, if you are interested in that and ready to help with testing -- feel free to poke me so it could be done sooner.


Sunday, March 30, 2014

Using Jenkins libvirt-slave-plugin with bhyve

I've played with libvirt-slave-plugin today to make it work with libvirt/bhyve and decided to document my steps in case it would be useful for somebody.

libvirt-slave-plugin

Assuming that you already have Jenkins up and running, installation of libvirt-slave-plugin is as follows. As we need a slightly modified version, we need to build it ourselves. I've made a fork which contains a required modification which could be cloned like that:

git clone -b bhyve git@github.com:jenkinsci/libvirt-slave-plugin.git

The only change I made is adding a single line with 'BHYVE' hypervisor type, you could find the pull request here. When that would be merged, this step will be not required.

So, getting back to the build. You'll need maven that could be installed from ports:

cd /usr/ports/devel/maven2 && make install clean

When it's installed, go back to the plugin we cloned and do:

mvn package -DskipTests=true

When done, login to the Jenkins web interface, go to Manage Jenkins -> Manage Plugins -> Advanced -> Upload Pluging. It'll ask to provide a path to the plugin. It would be target/libvirt-slave.hpi in our plugin directory.

After plugin is installed, please follow to Manage Jenkins -> Configure System -> Add new cloud. Then you'll need to specify hypervisor type BHYVE and configure credentials so Jenkins could reach your libvirtd using SSH. There's a handy 'Test Connection' you could use your configuration.

Once done with that, we can go to Manage Jenkins -> Manage Nodes -> New Node and choose 'libvirt' node type. Then you'll need to choose a libvirt domain to use for the node. From now on, node configuration is pretty straightforward, expect, probably an IP address of the slave. To find out an IP address, you'd need to find out its MAC address (just run virsh dumpxml and you'll find it there) and then find the corresponding file in dnsmasq/default.leases file.

Guest Preparation

The only thing guest OS needs is to have jdk installed. I preferred to download a package with java/openjdk7, but I had to configure network first. My VMs use bridged networking on virbr0, so NAT config looks like that in /etc/pf.conf:

ext_if="re0"
int_if="virbr0"

virt_net="192.168.122.0/24"

scrub all

nat on $ext_if from $virt_net to any -> ($ext_if)

Now openjdk could be installed from the guest using:

pkg install java/openjdk7

Finally, find the nodes in node management menu and press 'Launch slave agent' button. It should be ready for the builds now.

PS It might be useful to sync clock on both guest and host systems using ntpdate.

PPS libvirt version should be at least 1.2.2.


Saturday, March 15, 2014

Bhyve in libvirt

I continue my activities on improving libvirt FreeBSD support and I have some good news. Recent libvirt release, 1.2.2, is the first version to include the bhyve support!

Currently it's in its early stage and doesn't support some of the features and doesn't provide good flexibility, it's just a basic stuff at this point. I'll not provide a detailed description and instead will point you to the document: Libvirt: Bhyve driver. You'll find a sample domain XML which covers all the features currently supported by the driver.

TODO list

While there are lots and lots of things to be done, there are some specific ones I'm focusing on:

  • Console support through nmdm(4). This is very important feature for debugging and checking what's going on in the guest.
  • Domains autostart support. There's a patch already kindly provided by David Shane Holden that just needs review and testing.
  • A little more flexible slot ids allocation / device configuration.

Qemu/FreeBSD status

As a side note, I'll give an update what's changed since my previous blog post about qemu libvirt driver on FreeBSD. So, here's what's new:

  • Proper TAP interfaces cleanup
  • CPU affinity configuration support, check http://libvirt.org/formatdomain.html#elementsCPUAllocation for details
  • virsh console should now work if you run it from freebsd host and connect to libvirtd on Linux
  • Node status support (such as virsh nodecpustats, virsh nodememstats)

Some of these are available in already released versions, some are only in git version.


Monday, November 11, 2013

Running Qemu VMs on FreeBSD

I'm slowly working on getting libvirt more functional on FreeBSD and, thanks to help of a lot of other people who did very valuable reviews of my patches and contributed other portability fixes, at this point libvirt on FreeBSD is capable of running Qemu VMs with some limitations. I'll provide a quick howto on doing that and what limitations exist at this point.

Building libvirt

As I'm playing with the codebase, it's more convenient for me to use direct git checkout instead of port. I'll provide an instuction how to build it (port should work as well, but I haven't tested).

Configure command looks this way:

CFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" ./configure \
                                           --without-polkit \
                                           --disable-werror

CFLAGS and LDFLAGS are needed to help it find yajl includes and libs. We're disabling polkit because we don't want to waste extra time configuring it and we disable treating warnings as errors because some third party libs' headers could mess things up.

When it configures successfully, it's traditional gmake && gmake install.

Preparing a VM

Nothing special about VM preparation, just create a Qemu VM like you usually do, configure it for you needs and you're almost ready to go.

There are some things recommended to do though:

  • Make virtio stuff available (see vtnet(4), virtio_balloon(4), virtio_blk(4) for details) on your guest
  • Configure network interface to using DHCP by adding this line to /etc/rc.conf: ifconfig_vtnet0="DHCP"

Defining and running a domain

Before running up libvirtd, make sure you have bridge(4) and tap(4) modules loaded or built into kernel.

Then you need to execute libvirtd and then connect to it using virsh:

virsh -c "qemu:///system"
Now, we need to create an XML file with domain definition. Here's the one I use: https://gist.github.com/novel/7399465

You might notice we're using virtio for network device and for the disk drive. You'll have to modify a path to the image location and adjust mem/cpu amount for your needs.

Once you're done with that, execute 'define /path/to/domain.xml' in virsh, and domain with name 'qemu' (unless you changed it) will be created. You can start it using 'start qemu' command in virsh.

To see what's going on, we could use vnc. To figure out what vnc port our VM is available at, we need to execute 'vncdisplay qemu' and it should print something like:

127.0.0.1:0

Now we can connect using vnc client, for example, if you use net/vnc, the proper command will be:

vncviewer 127.0.0.1:0

If everything goes well, you'll get an access to your VM's console.

Run ifconfig to check if there's an IP address on vtnet0 interface:

vtnet0: flags=8843 metric 0 mtu 1500
 options=80028
 ether 52:54:00:ae:4c:37
 inet 192.168.122.117 netmask 0xffffff00 broadcast 192.168.122.255
 media: Ethernet 1000baseT 
 status: active

And on the host you'll have:

  • bridge(4) device virbr0
  • tap(4) device vnet0 which corresponds to our VM and is member of virbr0 bridge

It's possible to connect from guest to host and vice versa. Unfortunately, it's not possible to provide access to the internet for VM without manual configuration. Normally, libvirt should do that, but its bridge driver needs some work to do that on FreeBSD, and that's on my todo list.

Apart from bridge stuff, there are a lot of other limitations on FreeBSD currently, but firewalling for bridge driver and nwfilter are probably the most important now. It's not quite easy to come up with a good way to implement it. For example, one has to choose what firewall package to use, pf and ipfw being the main candidates for that.

I'll cover my thoughts and concerns in a separate post.

Feel free to poke me if you have any questions/thoughts.

Update 25/01/2014: some people have troubles because of the old qemu version. On FreeBSD, please use qemu-devel port, because qemu port is too old.

Sunday, October 23, 2011

Apple keyboard on FreeBSD

Recently I bought an iBook G4 just for fun and of course installed FreeBSD on it. Installation goes pretty smooth starting with 9.0: I've started with 9.0-BETA3 and later updated to 10-CURRENT. I used it as a toy mostly but on Thursday my main laptop (which also runs FreeBSD, but amd64/8-STABLE) died and I had to use iBook as my main system and some problems became noticable.

There are two annoying problems with Apple keyboards:

1. Function keys (F1, F2, ...) work as they supposed to work only when pressing them with FN key. I rely on virtual desktops a lot and I use Ctrl-Fx to switch between them, so you imagine how annoying is that to use Ctrl-Fn-Fx to switch a desktop.

2. Apple keyboard misses 'Insert' key completely, so you cannot use Ctrl-Insert for pasting, so it's extremely annoying also.

Fortunately, both problems could be easily solved.

Several days ago Nathan Whitehorn ⟨nwhitehorn@) committed a support for dev.akbd.%d.fn_keys_function_as_primary which allows you to configure F-keys behavior. In order to make the keys look as F1,F2,... without pressing FN, execute:
sysctl dev.akbd.0.fn_keys_function_as_primary=1

The Insert key problem could be solved on X level: I've used xmodmap (x11/xmodmap) to remap F11 to Insert, which is a good solution for me because I don't use F11 key and it's located quite close to a place where you'd expect to have 'Insert' key on a typical keyboard.

So I've created ~/.Xmodmap file:
keycode 95 = Insert

And added execution of "xmodmap ~/.Xmodmap" on X session start.

I'm not sure if F11 has keycode 95 on every Apple keyboard, it's easy to figure out keycode using xev (x11/xev).

Wednesday, May 18, 2011

Dealing with pkg-config detection of FreeBSD's base OpenSSL

Recently I spotted a software that uses pkg-config to find OpenSSL on the system. This way doesn't work on FreeBSD: it does have OpenSSL in base system, but it doesn't supply '*.pc' file, unfortunately. So, when configure script tries to execute pkg-config openssl it fails, obviously.

There were a number of solutions suggested for this problem on #bsdports like replacing 'pkg-config openssl' with 'pkg-config pkg-config', depending on openssl from ports etc, but the cleanest solution is to define libssl_LIBS and libssl_CFLAGS environment variables, as was suggested by bapt@. In port's Makefile it would look something like this:
USE_OPENSSL=yes
CONFIGURE_ENV+=libssl_CFLAGS="-I${OPENSSLINC}" libssl_LIBS="-L${OPENSSLLIB} -lssl"

Yes, configure will not call pkg-config if we define these 'libssl_*' variables. While it looks quite simple, this issue took surprisingly a lot time to figure everything out. :(

Sunday, January 30, 2011

REST service on top of Marcuscom Tinderbox

Like many people who work on FreeBSD Ports I've been using great Marcuscom Tinderbox to verify my changes. Some time ago I started to realise that it's quite a boring task to schedule a lot of builds using web interface again and again and finally I've managed to make myself start making the process a little bit easier.

It seems that building a REST service on top on the existing is a nice way to go, so that's what I've been doing last few days and finally I have a prototype now.

As I didn't want to patch original tinderbox's webui sources, I just created a sub-directory in it called 'api' and started there. To my surprise, I wasn't able to find any ready to use solution to implement REST service in PHP (maybe I've missed something though), so ended up with a few php scripts and rewrite rules to make URLs feel RESTy. It was a quite hard part of the task since I haven't been coding in PHP for years and don't feel comfortable with it even after few days of practice.

Simultaneously I've started coding a client for which I choose Python.

Workflow

The goal was to support minimum but most important (for me at least) scenario -- testing an update for a port. Let's assume the port being tested is security/gnutls-devel. The process looks like that:

1. Produce a patch and apply it to the tree (can be done via ssh, or nfs, whatever, and could be easily scripted up)
2. Check builds we have:


(21:33) novel@fsol:~ %> tbc build
id name status current port updated
1 8.x-FreeBSD IDLE None 2011-01-30 14:05:25
(21:40) novel@fsol:~ %>


So, we see an idle build here with id 1. Not so much choices, so let's use it, but first let's see what's in the queue:


(21:40) novel@fsol:~ %> tbc queue
id username port build pri status enqueued completed
10 novel security/gnutls 8.x-FreeBSD 10 SUCCESS 2011-01-30 13:54:43 2011-01-30 14:05:27
(21:45) novel@fsol:~ %>


Now adding a port to the queue:


(21:46) novel@fsol:~ %> tbc queue add -b 1 security/gnutls-devel
(21:46) novel@fsol:~ %>


Let's check if new port build added successfully:


(21:46) novel@fsol:~ %> tbc queue
id username port build pri status enqueued completed
10 novel security/gnutls 8.x-FreeBSD 10 SUCCESS 2011-01-30 13:54:43 2011-01-30 14:05:27
11 novel security/gnutls-devel 8.x-FreeBSD 10 ENQUEUED 2011-01-30 21:46:20 None
(21:46) novel@fsol:~ %>


Cool, it's here! Now we can see that our build turned into PREPARE state:


(21:48) novel@fsol:~ %> tbc build
id name status current port updated
1 8.x-FreeBSD PREPARE None 2011-01-30 21:48:15
(21:48) novel@fsol:~ %>


All we have to do now is wait and poll for changes. Now we can see it's building:


(21:59) novel@fsol:~ %> tbc build
id name status current port updated
1 8.x-FreeBSD PORTBUILD gnutls-devel-2.11.5 2011-01-30 22:00:25
(22:00) novel@fsol:~ %>


And finally we see it's done:

(22:07) novel@fsol:~ %> tbc queue
id username port build pri status enqueued completed
11 novel security/gnutls 8.x-FreeBSD 10 SUCCESS 2011-01-30 21:46:20 2011-01-30 21:57:18
12 novel security/gnutls-devel 8.x-FreeBSD 10 SUCCESS 2011-01-30 21:50:54 2011-01-30 22:09:22
(22:15) novel@fsol:~ %> tbc queue 11


For everyone who's interested I've uploaded sources on github:



NOTICE: it's an early work in progress! The code is unstable and lacks a lot of features. Anyway, any feedback about an idea and implementation is welcome.

Tuesday, May 18, 2010

Changing partition type on FreeBSD

I had an ufs partition that I've used as a swap device. I was pretty satisfied with this setup until I decided to use it as a kernel dump device. I've ran dumpon on it and figured out it's not possible to use it as dump device. So I decided to change partition type to swap.

I executed bsdlabel and got such error: bsdlabel: Class not found. I've been googling and reading quite a lot of time, lots of such questions are not answered, but finally was able to find a solution. The solution is to use gpart tool. It appeared to be a handy tool and to change type of my ad0s2b partition I executed these commands:



Works great.