Eric Radman : a Journal

QEMU on Mac and Linux

Disk Image

qemu-img create -f raw pg1.img 20G
qemu-img create -f raw pg1-data.img 50G

Generating MAC addresses

#!/usr/local/bin/python3
import random

def random_mac():
    mac = [ 0x00, 0x16, 0x3e,
        random.randint(0x00, 0x7f),
        random.randint(0x00, 0xff),
        random.randint(0x00, 0xff) ]
    return ':'.join(map(lambda x: "%02x" % x, mac))

print(random_mac())

The prefix 00:16:3e is one of several ranges sometimes used for virtual machines.

QEMU on Linux

qemu-system-x86_64 \
    -accel kvm \
    -m 1024 \
    -cpu host \
    -bios /usr/share/edk2/ovmf/OVMF_CODE.fd \
    -drive file=/vm/pg1.img,format=raw,if=virtio \
    -nic tap,ifname=tap0,model=virtio-net-pci,mac=00:16:3e:02:63:dc \
    -nographic \
    -serial tcp::4444,server,telnet

The edk2-ovmf package provides a UEFI firmware image that will try a network boot

nmcli con add type bridge ifname br0 con-name br0
nmcli con add type bridge-slave ifname eno1 master br0
nmcli con mod br0 ipv4.address "10.200.1.1/24"
nmcli con mod br0 ipv6.address "fd00:c8:1::1/64"
nmcli device reapply br0

The Linux bridge interface takes over member interfaces, so the external IP addess must be assigned to bridge.

Two helper scripts are used to set up networking

# /etc/qemu-ifup
ip link set $1 master br0
ip link set dev $1 up
# /etc/qemu-ifdown
ip link delete $1 master br0

Create /etc/systemd/system/qemu@.service

[Unit]
Description=QEMU virtual machine
After=network-online.target

[Service]
ExecStart=/root/start-vm.sh %i

[Install]
WantedBy=multi-user.target

Enable each VM using it's name

systemctl enable qemu@pg1

OpenBSD on Mac

qemu-system-aarch64 \
    -M virt \
    -accel hvf \
    -m 2048 \
    -cpu cortex-a57 \
    -bios /opt/homebrew/Cellar/qemu/10.0.2_2/share/qemu/edk2-aarch64-code.fd \
    -drive file=miniroot78.img,format=raw,if=virtio \
    -drive file=openbsd.qcow2,format=qcow2,if=virtio \
    -nic vmnet-bridged,ifname=en0 \
    -nographic \
    -serial tcp::4444,server,telnet

Supported options are shown by qemu-system-aarch64 -help.

vmnet-shared will provide an address on a private subnet. vmnet-bridge allows a virtual machine to get an address on the local area network, but must be run as root.

ifconfig will show the interface added to a new bridge

bridge100: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    options=3<RXCSUM,TXCSUM>
    ether 4e:20:b8:1e:da:64
    Configuration:
        id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
        maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
        root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0
        ipfilter disabled flags 0x0
    member: en0 flags=3<LEARNING,DISCOVER>
            ifmaxaddr 0 port 6 priority 0 path cost 0
    member: vmenet0 flags=3<LEARNING,DISCOVER>
            ifmaxaddr 0 port 21 priority 0 path cost 0
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect
    status: active

Docker Images for Alternate Architectures

Docker images can be launched using QEMU to emulate other architectures

apt install -y docker.io
apt install -y qemu-user-static
usermod -a -G docker $USER

The Dockerfile consists of the base image, and the emulator to be used as the starting point

FROM arm32v6/alpine:latest
COPY tmp/qemu-arm-static /usr/bin/qemu-arm-static
RUN apk add musl-dev make gcc git tmux bash

To build the image run

mkdir tmp
cp /usr/bin/qemu-arm-static tmp/
docker run --rm --privileged multiarch/qemu-user-static:register --reset
docker build -t radman/arm-vm .