|
@@ -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
|