diff options
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/qemustart | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/scripts/qemustart b/scripts/qemustart new file mode 100755 index 0000000..5cadd7d --- /dev/null +++ b/scripts/qemustart @@ -0,0 +1,281 @@ +#!/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 |