123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- #!/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() {
- [ -n "$o_network" ] || return 0
- 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>]
- [--machine <machine>]
- [-n|--network]
- <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 --machine q35,accel=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 \\
- --machine virt,highmem=off \\
- --kernel bin/targets/armvirt/32/librecmc-armvirt-32-zImage \\
- --rootfs bin/targets/armvirt/32/librecmc-armvirt-32-root.ext4
- EOF
- }
- rand_mac() {
- hexdump -n 3 -e '"52:54:00" 3/1 ":%02x"' /dev/urandom
- }
- parse_args() {
- o_network=
- o_qemu_extra=()
- while [ "$#" -gt 0 ]; do
- # Cmdline options for the script itself SHOULD try to be
- # prefixed with two dashes to distinguish them from those for
- # qemu executables.
- #
- # Also note that qemu accepts both --opt and -opt
- case "$1" in
- --kernel) o_kernel="$2"; shift 2 ;;
- --rootfs) o_rootfs="$2"; shift 2 ;;
- --machine|-machine|-M) o_mach="$2"; shift 2 ;;
- --network|-n) o_network=1; shift ;;
- --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+=("$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 mach="${o_mach:-virt}"
- local cpu
- local qemu_exe
- case "${o_subtarget%-*}" in
- 32)
- qemu_exe="qemu-system-arm"
- cpu="cortex-a15"
- [ -n "$kernel" ] || kernel="$o_bindir/librecmc-$o_target-${o_subtarget%-*}-zImage-initramfs"
- ;;
- 64)
- qemu_exe="qemu-system-aarch64"
- cpu="cortex-a57"
- [ -n "$kernel" ] || kernel="$o_bindir/librecmc-$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" \
- )
- }
- [ -z "$o_network" ] || {
- o_qemu_extra+=( \
- "-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" \
- )
- }
- "$qemu_exe" -machine "$mach" -cpu "$cpu" -nographic \
- -kernel "$kernel" \
- "${o_qemu_extra[@]}"
- }
- start_qemu_malta() {
- local is64
- local isel
- local qemu_exe
- local rootfs="$o_rootfs"
- local kernel="$o_kernel"
- local mach="${o_mach:-malta}"
- # 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/librecmc-malta-${o_subtarget%-*}-vmlinux-initramfs.elf"
- [ -z "$rootfs" ] || {
- if [ ! -f "$rootfs" -a -s "$rootfs.gz" ]; then
- gunzip "$rootfs.gz"
- fi
- o_qemu_extra+=( \
- "-drive" "file=$rootfs,format=raw" \
- "-append" "root=/dev/sda rootwait" \
- )
- }
- # 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
- [ -z "$o_network" ] || {
- o_qemu_extra+=(
- -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"
- )
- }
- "$qemu_exe" -machine "$mach" -nographic \
- -kernel "$kernel" \
- "${o_qemu_extra[@]}"
- }
- start_qemu_x86() {
- local qemu_exe
- local kernel="$o_kernel"
- local rootfs="$o_rootfs"
- local mach="${o_mach:-pc}"
- [ -n "$rootfs" ] || {
- rootfs="$o_bindir/librecmc-$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
- [ -n "$kernel" ] && {
- o_qemu_extra+=( \
- "-kernel" "$kernel" \
- "-append" "root=/dev/vda console=ttyS0 rootwait" \
- )
- }
- [ -z "$o_network" ] || {
- case "${o_subtarget%-*}" in
- legacy)
- o_qemu_extra+=(
- -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"
- )
- ;;
- generic|64)
- o_qemu_extra+=(
- -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"
- )
- ;;
- 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" -machine "$mach" -nographic \
- -device ide-drive,drive=drv0 \
- -drive "file=$rootfs,format=raw,id=drv0,if=none" \
- "${o_qemu_extra[@]}"
- ;;
- generic|64)
- "$qemu_exe" -machine "$mach" -nographic \
- -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
- }
- parse_args "$@" \
- && check_setup \
- && start_qemu
|