openwrt/scripts/qemustart
Yousong Zhou 50fcfd4b65 scripts/qemustart: initial version
The script can be used to quickly spin up QEMU virtual machines with lan
and wan network.  Please read the initial part of the script for
instructions about how to configure host machine

Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com>
2017-04-23 09:19:34 +08:00

282 lines
7.5 KiB
Bash
Executable File

#!/usr/bin/env bash
SELF="$0"
# Linux bridge for connecting lan and wan network of guest machines
BR_LAN="${BR_LAN:-br-lan}"
BR_WAN="${BR_WAN:-br-wan}"
# Host network interface providing internet access for guest machines
IF_INET="${IF_INET:-eth0}"
# qemu-bridge-helper does two things here
#
# - create tap interface
# - add the tap interface to bridge
#
# as such it requires CAP_NET_ADMIN to do its job. It will be convenient to
# have it as a root setuid program. Be aware of the security risks implied
#
# the helper has an acl list which defaults to deny all bridge. we need to add
# $BR_LAN and $BR_WAN to its allow list
#
# # sudo vim /etc/qemu/bridge.conf
# allow br-lan
# allow br-wan
#
# Other allowed directives can be 'allow all', 'deny all', 'include xxx', See
# qemu-bridge-helper.c of qemu source code for details.
#
# The helper can be provided by package qemu-system-common on debian, or
# qemu-kvm-common on rhel
#
HELPER="${HELPER:-/usr/libexec/qemu-bridge-helper}"
### end of global settings
__errmsg() {
echo "$*" >&2
}
do_setup() {
# setup bridge for LAN network
sudo ip link add dev "$BR_LAN" type bridge
sudo ip link set dev "$BR_LAN" up
sudo ip addr add 192.168.1.3/24 dev "$BR_LAN"
# setup bridge for WAN network
#
# minimal dnsmasq config for configuring guest wan network with dhcp
#
# # sudo apt-get install dnsmasq
# # sudo vi /etc/dnsmasq.conf
# interface=br-wan
# dhcp-range=192.168.7.50,192.168.7.150,255.255.255.0,30m
#
sudo ip link add dev "$BR_WAN" type bridge
sudo ip link set dev "$BR_WAN" up
sudo ip addr add 192.168.7.1/24 dev "$BR_WAN"
# guest internet access
sudo sysctl -w "net.ipv4.ip_forward=1"
sudo sysctl -w "net.ipv4.conf.$BR_WAN.proxy_arp=1"
while sudo iptables -t nat -D POSTROUTING -o "$IF_INET" -j MASQUERADE 2>/dev/null; do true; done
sudo iptables -t nat -A POSTROUTING -o "$IF_INET" -j MASQUERADE
}
check_setup_() {
ip link show "$BR_LAN" >/dev/null || return 1
ip link show "$BR_WAN" >/dev/null || return 1
[ -x "$HELPER" ] || {
__errmsg "helper $HELPER is not an executable"
return 1
}
}
check_setup() {
check_setup_ || {
__errmsg "please check the script content to see the environment requirement"
return 1
}
}
#do_setup; check_setup; exit $?
usage() {
cat >&2 <<EOF
Usage: $SELF [-h|--help]
$SELF <target>
[<subtarget> [<extra-qemu-options>]]
[--kernel <kernel>]
[--rootfs <rootfs>]
<subtarget> will default to "generic" and must be specified if
<extra-qemu-options> are present
e.g. <subtarget> for malta can be le, be, le64, be64, le-glibc, le64-glibc, etc
<kernel>, <rootfs> can be required or optional arguments to qemu depending on
the actual <target> in use. They will default to files under bin/targets/
Examples
$SELF x86 64
$SELF x86 64 -enable-kvm -device virtio-balloon-pci
$SELF x86 64 -incoming tcp:0:4444
$SELF x86 64-glibc
$SELF malta be -m 64
$SELF malta le64
$SELF malta be-glibc
$SELF armvirt 32 \\
--kernel bin/targets/armvirt/32/lede-armvirt-32-zImage \\
--rootfs bin/targets/armvirt/32/lede-armvirt-32-root.ext4
EOF
}
rand_mac() {
hexdump -n 3 -e '"52:54:00:" 2/1 "%02x:" 1/1 "%02x"' /dev/urandom
}
parse_args() {
while [ "$#" -gt 0 ]; do
case "$1" in
--kernel) o_kernel="$2"; shift 2 ;;
--rootfs) o_rootfs="$2"; shift 2 ;;
--help|-h)
usage
exit 0
;;
*)
if [ -z "$o_target" ]; then
o_target="$1"
elif [ -z "$o_subtarget" ]; then
o_subtarget="$1"
else
o_qemu_extra=("${o_qemu_extra[@]}" "$1")
fi
shift
;;
esac
done
MAC_LAN="$(rand_mac)"
MAC_WAN="$(rand_mac)"
[ -n "$o_target" ] || {
usage
return 1
}
[ -n "$o_subtarget" ] || o_subtarget="generic"
o_bindir="bin/targets/$o_target/$o_subtarget"
}
start_qemu_armvirt() {
local kernel="$o_kernel"
local rootfs="$o_rootfs"
local cpu
local qemu_exe
case "${o_subtarget%-*}" in
32)
qemu_exe="qemu-system-arm"
cpu="cortex-a15"
[ -n "$kernel" ] || kernel="$o_bindir/lede-$o_target-${o_subtarget%-*}-zImage-initramfs"
;;
64)
qemu_exe="qemu-system-aarch64"
cpu="cortex-a57"
[ -n "$kernel" ] || kernel="$o_bindir/lede-$o_target-${o_subtarget%-*}-Image-initramfs"
;;
*)
__errmsg "target $o_target: unknown subtarget $o_subtarget"
return 1
;;
esac
[ -z "$rootfs" ] || {
if [ ! -f "$rootfs" -a -s "$rootfs.gz" ]; then
gunzip "$rootfs.gz"
fi
o_qemu_extra=( \
"-drive" "file=$rootfs,format=raw,if=virtio" \
"-append" "root=/dev/vda rootwait" \
"${o_qemu_extra[@]}" \
)
}
"$qemu_exe" -machine virt -cpu "$cpu" -nographic \
-netdev bridge,id=lan,br="$BR_LAN,helper=$HELPER" -device virtio-net-pci,id=devlan,netdev=lan,mac="$MAC_LAN" \
-netdev bridge,id=wan,br="$BR_WAN,helper=$HELPER" -device virtio-net-pci,id=devwan,netdev=wan,mac="$MAC_WAN" \
-kernel "$kernel" \
"${o_qemu_extra[@]}"
}
start_qemu_malta() {
local is64
local isel
local qemu_exe
local kernel="$o_kernel"
# o_subtarget can be le, be, le64, be64, le-glibc, le64-glibc, etc..
is64="$(echo $o_subtarget | grep -o 64)"
[ "$(echo "$o_subtarget" | grep -o '^..')" = "le" ] && isel="el"
qemu_exe="qemu-system-mips$is64$isel"
[ -n "$kernel" ] || kernel="$o_bindir/lede-malta-${o_subtarget%-*}-vmlinux-initramfs.elf"
# NOTE: order of wan, lan -device arguments matters as it will affect which
# one will be actually used as the wan, lan network interface inside the
# guest machine
"$qemu_exe" -machine malta -nographic \
-netdev bridge,id=wan,br="$BR_WAN,helper=$HELPER" -device pcnet,netdev=wan,mac="$MAC_WAN" \
-netdev bridge,id=lan,br="$BR_LAN,helper=$HELPER" -device pcnet,netdev=lan,mac="$MAC_LAN" \
-kernel "$kernel" \
"${o_qemu_extra[@]}"
}
start_qemu_x86() {
local rootfs="$o_rootfs"
local qemu_exe
[ -n "$rootfs" ] || {
rootfs="$o_bindir/lede-$o_target-${o_subtarget%-*}-combined-ext4.img"
if [ ! -f "$rootfs" -a -s "$rootfs.gz" ]; then
gunzip "$rootfs.gz"
fi
}
#
# generic: 32-bit, pentium4 (CONFIG_MPENTIUM4), kvm guest, virtio
# legacy: 32-bit, i486 (CONFIG_M486)
# 64: 64-bit, kvm guest, virtio
#
case "${o_subtarget%-*}" in
legacy) qemu_exe="qemu-system-i386" ;;
generic|64) qemu_exe="qemu-system-x86_64" ;;
*)
__errmsg "target $o_target: unknown subtarget $o_subtarget"
return 1
;;
esac
case "${o_subtarget%-*}" in
legacy)
# use IDE (PATA) disk instead of AHCI (SATA). Refer to link
# [1] for related discussions
#
# To use AHCI interface
#
# -device ich9-ahci,id=ahci \
# -device ide-drive,drive=drv0,bus=ahci.0 \
# -drive "file=$rootfs,format=raw,id=drv0,if=none" \
#
# [1] https://dev.openwrt.org/ticket/17947
"$qemu_exe" -nographic \
-netdev bridge,id=lan,br="$BR_LAN,helper=$HELPER" -device e1000,id=devlan,netdev=lan,mac="$MAC_LAN" \
-netdev bridge,id=wan,br="$BR_WAN,helper=$HELPER" -device e1000,id=devwan,netdev=wan,mac="$MAC_WAN" \
-device ide-drive,drive=drv0 \
-drive "file=$rootfs,format=raw,id=drv0,if=none" \
"${o_qemu_extra[@]}"
;;
generic|64)
"$qemu_exe" -nographic \
-netdev bridge,id=lan,br="$BR_LAN,helper=$HELPER" -device virtio-net-pci,id=devlan,netdev=lan,mac="$MAC_LAN" \
-netdev bridge,id=wan,br="$BR_WAN,helper=$HELPER" -device virtio-net-pci,id=devwan,netdev=wan,mac="$MAC_WAN" \
-drive "file=$rootfs,format=raw,if=virtio" \
"${o_qemu_extra[@]}"
;;
esac
}
start_qemu() {
case "$o_target" in
armvirt) start_qemu_armvirt ;;
malta) start_qemu_malta ;;
x86) start_qemu_x86 ;;
*)
__errmsg "target $o_target is not supported yet"
return 1
;;
esac
}
check_setup \
&& parse_args "$@" \
&& start_qemu