--- /dev/null
+#!/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