# macOS 使用 Libvirt + QEMU 建置 VM 指引
https://www.naut.ca/blog/2020/08/26/ubuntu-vm-on-macos-with-libvirt-qemu/
# NAT 網路
https://rambling-ideas.salessandri.name/setup-nat-network-for-qemu-in-mac-osx/
Creating the bridge interface
sudo ifconfig bridge1 create
bridge1: flags=8822<BROADCAST,SMART,SIMPLEX,MULTICAST> mtu 1500
options=63<RXCSUM,TXCSUM,TSO4,TSO6>
ether f2:18:98:45:b4:01
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
nd6 options=201<PERFORMNUD,DAD>
media: <unknown type>
status: inactive
sudo ifconfig bridge1 192.168.100.1/24
bridge1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=63<RXCSUM,TXCSUM,TSO4,TSO6>
ether f2:18:98:45:b4:01
inet 192.168.100.1 netmask 0xffffff00 broadcast 192.168.100.255
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
nd6 options=201<PERFORMNUD,DAD>
media: <unknown type>
status: inactive
Packet forwarding and NAT
vim pfctl-nat-config
pfctl-nat-config
nat on en10 from bridge1:network to any -> (en10)
sudo pfctl -F all
Password:
No ALTQ support in kernel
ALTQ related functions disabled
rules cleared
nat cleared
dummynet cleared
0 tables deleted.
0 states cleared
source tracking entries cleared
pf: statistics cleared
pf: interface flags reset
sudo pfctl -f ./pfctl-nat-config -e
pfctl: Use of -f option, could result in flushing of rules
present in the main ruleset added by the system at startup.
See /etc/pf.conf for further details.
No ALTQ support in kernel
ALTQ related functions disabled
pf enabled
Setting up the DHCP server
sudo vim /etc/bootpd.list
/etc/bootpd.list
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>bootp_enabled</key>
<false/>
<key>dhcp_enabled</key>
<array>
<string>bridge1</string>
</array>
<key>netboot_enabled</key>
<false/>
<key>relay_enabled</key>
<false/>
<key>Subnets</key>
<array>
<dict>
<key>name</key>
<string>VM NAT Network (192.168.100.0/24)</string>
<key>net_mask</key>
<string>255.255.255.0</string>
<key>net_address</key>
<string>192.168.100.0</string>
<key>net_range</key>
<array>
<string>192.168.100.2</string>
<string>192.168.100.254</string>
</array>
<key>allocate</key>
<true/>
<key>dhcp_router</key>
<string>192.168.100.1</string>
<key>dhcp_domain_name_server</key>
<array>
<string>8.8.8.8</string>
</array>
</dict>
</array>
</dict>
</plist>
sudo /usr/libexec/bootpd -D
QEMU and interface setup
./s01-create-hd
./s02-install-vm
# 安裝 tap/tun 工具
brew install --cask tuntap
- Reboot OS
# 建置 tap 網路
釋放網路介面卡網址
$ ifconfig
......
en7: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=6407<RXCSUM,TXCSUM,VLAN_MTU,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM>
ether 00:14:3d:14:45:f8
inet6 fe80::184f:b99:3a1e:bdc1%en7 prefixlen 64 secured scopeid 0x14
inet 192.168.66.236 netmask 0xffffff00 broadcast 192.168.66.255
nd6 options=201<PERFORMNUD,DAD>
media: autoselect (100baseTX <full-duplex>)
status: active
$ sudo ifconfig en7 down
$ ifconfig
......
en7: flags=8822<BROADCAST,SMART,SIMPLEX,MULTICAST> mtu 1500
options=6407<RXCSUM,TXCSUM,VLAN_MTU,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM>
ether 00:14:3d:14:45:f8
nd6 options=201<PERFORMNUD,DAD>
media: autoselect (none)
status: inactive
$ sudo ifconfig bridge0 down
$ ifconfig
......
bridge0: flags=8822<BROADCAST,SMART,SIMPLEX,MULTICAST> mtu 1500
options=3<RXCSUM,TXCSUM>
ether d0:37:45:d2:70:98
inet6 fe80::cf0:4f39:9250:31d5%bridge0 prefixlen 64 secured scopeid 0xf
inet6 fd00:30b7:d45e:4bf2:103e:27cc:b83f:321d prefixlen 64 autoconf secured
inet6 fd00:30b7:d45e:4bf2:a1d4:1d24:9af3:2a70 prefixlen 64 autoconf temporary
inet 192.168.66.21 netmask 0xffffff00 broadcast 192.168.66.255
inet 192.168.66.19 netmask 0xffffff00 broadcast 192.168.66.255
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: en10 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 10 priority 0 path cost 0
member: en7 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 20 priority 0 path cost 0
nd6 options=201<PERFORMNUD,DAD>
media: autoselect
status: active
......
$ sudo ifconfig bridge0 addm en7
$ ifconfig
......
en7: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
options=6407<RXCSUM,TXCSUM,VLAN_MTU,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM>
ether 00:14:3d:14:45:f8
inet6 fe80::184f:b99:3a1e:bdc1%en7 prefixlen 64 duplicated secured scopeid 0x14
inet 192.168.66.236 netmask 0xffffff00 broadcast 192.168.66.255
nd6 options=201<PERFORMNUD,DAD>
media: autoselect (100baseTX <full-duplex>)
status: active
$ sudo sh -c "exec 5<>/dev/tap0; chown $USER: /dev/tap0"
$ ll /dev/tap0
crw-rw---- 1 alanjui wheel 22, 0 12 19 14:07 /dev/tap0
$ sudo ifconfig bridge0 addm tap0
$ ifconfig
......
bridge0: flags=8822<BROADCAST,SMART,SIMPLEX,MULTICAST> mtu 1500
options=3<RXCSUM,TXCSUM>
ether d0:37:45:d2:70:98
inet6 fe80::cf0:4f39:9250:31d5%bridge0 prefixlen 64 secured scopeid 0xf
inet6 fd00:30b7:d45e:4bf2:103e:27cc:b83f:321d prefixlen 64 autoconf secured
inet6 fd00:30b7:d45e:4bf2:a1d4:1d24:9af3:2a70 prefixlen 64 autoconf temporary
inet 192.168.66.21 netmask 0xffffff00 broadcast 192.168.66.255
inet 192.168.66.19 netmask 0xffffff00 broadcast 192.168.66.255
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: en10 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 10 priority 0 path cost 0
member: en7 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 20 priority 0 path cost 0
member: tap0 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 19 priority 0 path cost 0
nd6 options=201<PERFORMNUD,DAD>
media: autoselect
status: active
......
tap0: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
ether 52:53:79:9b:1c:3a
media: autoselect
status: active
open (pid 11861)
sudo ifconfig bridge0 up
(1) Establish the Bridge Interface
sudo ifconfig bridge1 192.168.100.1/24
(2) Enable NAT on macOS
sudo ./pf_nat.sh
pf_nat.sh
#!/bin/sh
NIC="en10"
BRIDGE="bridge1"
sudo cat > /usr/local/etc/pf-nat.conf << EOF
nat on $NIC from $BRIDGE:network to any -> ($NIC)
EOF
sudo pfctl -d
sudo sysctl -w net.inet.ip.forwarding=1
sudo pfctl -f /usr/local/etc/pf-nat.conf -e
(3) Setting up the DHCP server
sudo /usr/libexec/bootpd -D
/etc/bootpd.list :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>bootp_enabled</key>
<false/>
<key>dhcp_enabled</key>
<array>
<string>bridge1</string>
</array>
<key>netboot_enabled</key>
<false/>
<key>relay_enabled</key>
<false/>
<key>Subnets</key>
<array>
<dict>
<key>name</key>
<string>VM NAT Network (192.168.100.0/24)</string>
<key>net_mask</key>
<string>255.255.255.0</string>
<key>net_address</key>
<string>192.168.100.0</string>
<key>net_range</key>
<array>
<string>192.168.100.2</string>
<string>192.168.100.254</string>
</array>
<key>allocate</key>
<true/>
<key>dhcp_router</key>
<string>192.168.100.1</string>
<key>dhcp_domain_name_server</key>
<array>
<string>8.8.8.8</string>
</array>
</dict>
</array>
</dict>
</plist>
(4) Install VM
./s01-create-hd
sudo ./s02-install-vm
s02-install-vm :
#!/bin/sh
VM_NAME="ARCH-01"
MAC_ADDR=$(./qemu-mac-hasher.py "$VM_NAME")
echo "VM_NAME=$VM_NAME"
echo "MAC_ADDR=$MAC_ADDR"
qemu-system-x86_64 \
-name "$VM_NAME" \
-machine q35,accel=hvf \
-cpu host \
-smp cpus=2 \
-m 2048 \
-vga virtio \
-display default,show-cursor=on \
-usb \
-device usb-tablet \
-drive media=disk,format=qcow2,file=./vdisk1.qcow2 \
-cdrom $HOME/ISO/debian-10.7.0-amd64-netinst.iso \
-netdev tap,id=nd0,ifname=tap0,script=./qemu-ifup.sh,downscript=./qemu-ifdown.sh \
-device virtio-net-pci,netdev=nd0,mac="$MAC_ADDR" \
-boot n
# brew services manager
brew services --help
Usage: brew services [subcommand]
Manage background services with macOS' launchctl(1) daemon manager.
If sudo is passed, operate on /Library/LaunchDaemons (started at boot).
Otherwise, operate on ~/Library/LaunchAgents (started at login).
[sudo] brew services [list]:
List all managed services for the current user (or root).
[sudo] brew services run (formula|--all):
Run the service formula without registering to launch at login (or boot).
[sudo] brew services start (formula|--all):
Start the service formula immediately and register it to launch at login
(or boot).
[sudo] brew services stop (formula|--all):
Stop the service formula immediately and unregister it from launching at
login (or boot).
[sudo] brew services restart (formula|--all):
Stop (if necessary) and start the service formula immediately and register
it to launch at login (or boot).
[sudo] brew services cleanup:
Remove all unused services.
--all Run subcommand on all services.
-d, --debug Display any debugging information.
-q, --quiet Make some output more quiet.
-v, --verbose Make some output more verbose.
-h, --help Show this message.
brew services start libvirt
brew services stop libvirt
brew services restart libvirt
$ brew services
Name Status User Plist
libvirt started alanjui /Users/alanjui/Library/LaunchAgents/homebrew.mxcl.libvirt.plist
unbound started root /Library/LaunchDaemons/homebrew.mxcl.unbound.plist
# Network
Ref: Setting up Qemu with a tap interface (opens new window)
# How to install Qemu on MAC OSX 10.6 & 10.7 (opens new window)
# Bridged network on macOS host (10.14.6 Mojave) (opens new window)
s01-chreate-hd
#!/bin/sh
qemu-img create -f qcow2 vdisk1.qcow2 30G
s00-generate-mac
#!/usr/local/bin/bash
# generate a random mac address for the qemu nic
# printf 'DE:AD:BE:EF:%02X:%02X\n' $((RANDOM%256)) $((RANDOM%256))
VAR1=$((RANDOM%256))
VAR2=$((RANDOM%256))
MAC_ADDR="DE:AD:BE:EF:$VAR1:$VAR2"
echo "export MAC_ADDR='$MAC_ADDR'"
export MAC_ADDR="$MAC_ADDR"
qemu-mac-hasher.py
#!/usr/bin/env python
# usage: qemu-mac.hasher.py <VMName>
import sys
import zlib
crc = str(hex(zlib.crc32(sys.argv[1].encode("utf-8"))))[-8:]
print("52:54:%s%s:%s%s:%s%s:%s%s" % tuple(crc))
s02-install
#!/bin/sh
VM_NAME="arch-001"
MAC_ADDR=$(./qemu-mac-hasher.py "$VM_NAME")
echo "VM_NAME = $VM_NAME"
echo "MAC_ADDR = $MAC_ADDR"
qemu-system-x86_64 \
-name "$VM_NAME" \
-machine type=q35,accel=hvf \
-cpu host \
-smp cpus=6 \
-m 4G \
-vga virtio \
-display default,show-cursor=on \
-usb \
-device usb-tablet \
-drive file=./vdisk1.qcow2,if=virtio \
-cdrom $HOME/ISO/debian-10.7.0-amd64-netinst.iso \
-nic user,id=eth0,restrict=off,ipv4=on,ipv6=off,model=virtio,mac="$MAC_ADDR" \
-boot n
s03-start-vm
#!/bin/sh
qemu-system-x86_64 \
-name "$VM_NAME" \
-machine type=q35,accel=hvf \
-cpu host \
-smp cpus=6 \
-m 4G \
-vga virtio \
-display default,show-cursor=on \
-usb \
-device usb-tablet \
-drive file=./vdisk1.qcow2,if=virtio \
-nic user,id=eth0vi,model=virtio,restrict=off \
-boot c
# 參考
ifconfig 指令
ifconfig --help
ifconfig: illegal option -- -
usage: ifconfig \[-C\] [-L] interface address_family [address [dest_address]]
[parameters]
ifconfig interface create
ifconfig -a \[-C\] [-L] \[-d\] [-m] \[-u\] [-v] [address_family]
ifconfig -l \[-d\] [-u] [address_family]
ifconfig \[-C\] [-L] \[-d\] [-m] \[-u\] [-v]
Download tuntaposx
https://github.com/mal/docker-for-mac-host-bridge/issues/2
mal/docker-for-mac-host-bridge#2 (opens new window)
https://github.com/AlmirKadric-Published/docker-tuntap-osx
https://gist.github.com/retspen/4e6ec73271594399c050f6f8027a9880
https://gist.github.com/retspen/4e6ec73271594399c050f6f8027a9880 (opens new window)
# NIC Models:
sudo qemu-system-x86_64 -nic tap,model=help
Password:
Supported NIC models:
e1000
e1000-82544gc
e1000-82545em
e1000e
i82550
i82551
i82557a
i82557b
i82557c
i82558a
i82558b
i82559a
i82559b
i82559c
i82559er
i82562
i82801
ne2k_pci
pcnet
rtl8139
tulip
virtio-net-pci
virtio-net-pci-non-transitional
virtio-net-pci-transitional
vmxnet3
-nic user
#!/bin/sh
VM_NAME="ARCH-01"
MAC_ADDR=$(./qemu-mac-hasher.py "$VM_NAME")
echo "VM_NAME=$VM_NAME"
echo "MAC_ADDR=$MAC_ADDR"
qemu-system-x86_64 \
-name "$VM_NAME" \
-machine q35,accel=hvf \
-cpu host \
-smp cpus=2 \
-m 2048 \
-vga virtio \
-display default,show-cursor=on \
-usb \
-device usb-tablet \
-drive media=disk,format=qcow2,file=./vdisk1.qcow2 \
-cdrom $HOME/ISO/debian-10.7.0-amd64-netinst.iso \
-nic user,ipv6=off,model=virtio-net-pci,mac="$MAC_ADDR" \
-boot n