qemustart 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. #!/usr/bin/env bash
  2. SELF="$0"
  3. # Linux bridge for connecting lan and wan network of guest machines
  4. BR_LAN="${BR_LAN:-br-lan}"
  5. BR_WAN="${BR_WAN:-br-wan}"
  6. # Host network interface providing internet access for guest machines
  7. IF_INET="${IF_INET:-eth0}"
  8. # qemu-bridge-helper does two things here
  9. #
  10. # - create tap interface
  11. # - add the tap interface to bridge
  12. #
  13. # as such it requires CAP_NET_ADMIN to do its job. It will be convenient to
  14. # have it as a root setuid program. Be aware of the security risks implied
  15. #
  16. # the helper has an acl list which defaults to deny all bridge. we need to add
  17. # $BR_LAN and $BR_WAN to its allow list
  18. #
  19. # # sudo vim /etc/qemu/bridge.conf
  20. # allow br-lan
  21. # allow br-wan
  22. #
  23. # Other allowed directives can be 'allow all', 'deny all', 'include xxx', See
  24. # qemu-bridge-helper.c of qemu source code for details.
  25. #
  26. # The helper can be provided by package qemu-system-common on debian, or
  27. # qemu-kvm-common on rhel
  28. #
  29. HELPER="${HELPER:-/usr/libexec/qemu-bridge-helper}"
  30. ### end of global settings
  31. __errmsg() {
  32. echo "$*" >&2
  33. }
  34. do_setup() {
  35. # setup bridge for LAN network
  36. sudo ip link add dev "$BR_LAN" type bridge
  37. sudo ip link set dev "$BR_LAN" up
  38. sudo ip addr add 192.168.1.3/24 dev "$BR_LAN"
  39. # setup bridge for WAN network
  40. #
  41. # minimal dnsmasq config for configuring guest wan network with dhcp
  42. #
  43. # # sudo apt-get install dnsmasq
  44. # # sudo vi /etc/dnsmasq.conf
  45. # interface=br-wan
  46. # dhcp-range=192.168.7.50,192.168.7.150,255.255.255.0,30m
  47. #
  48. sudo ip link add dev "$BR_WAN" type bridge
  49. sudo ip link set dev "$BR_WAN" up
  50. sudo ip addr add 192.168.7.1/24 dev "$BR_WAN"
  51. # guest internet access
  52. sudo sysctl -w "net.ipv4.ip_forward=1"
  53. sudo sysctl -w "net.ipv4.conf.$BR_WAN.proxy_arp=1"
  54. while sudo iptables -t nat -D POSTROUTING -o "$IF_INET" -j MASQUERADE 2>/dev/null; do true; done
  55. sudo iptables -t nat -A POSTROUTING -o "$IF_INET" -j MASQUERADE
  56. }
  57. check_setup_() {
  58. ip link show "$BR_LAN" >/dev/null || return 1
  59. ip link show "$BR_WAN" >/dev/null || return 1
  60. [ -x "$HELPER" ] || {
  61. __errmsg "helper $HELPER is not an executable"
  62. return 1
  63. }
  64. }
  65. check_setup() {
  66. [ -n "$o_network" ] || return 0
  67. check_setup_ || {
  68. __errmsg "please check the script content to see the environment requirement"
  69. return 1
  70. }
  71. }
  72. #do_setup; check_setup; exit $?
  73. usage() {
  74. cat >&2 <<EOF
  75. Usage: $SELF [-h|--help]
  76. $SELF <target>
  77. [<subtarget> [<extra-qemu-options>]]
  78. [--kernel <kernel>]
  79. [--rootfs <rootfs>]
  80. [--machine <machine>]
  81. [-n|--network]
  82. <subtarget> will default to "generic" and must be specified if
  83. <extra-qemu-options> are present
  84. e.g. <subtarget> for malta can be le, be, le64, be64, le-glibc, le64-glibc, etc
  85. <kernel>, <rootfs> can be required or optional arguments to qemu depending on
  86. the actual <target> in use. They will default to files under bin/targets/
  87. Examples
  88. $SELF x86 64
  89. $SELF x86 64 --machine q35,accel=kvm -device virtio-balloon-pci
  90. $SELF x86 64 -incoming tcp:0:4444
  91. $SELF x86 64-glibc
  92. $SELF malta be -m 64
  93. $SELF malta le64
  94. $SELF malta be-glibc
  95. $SELF armsr armv7 \\
  96. --machine virt,highmem=off \\
  97. --kernel bin/targets/armsr/armv7/openwrt-armsr-armv7-generic-kernel.bin \\
  98. --rootfs bin/targets/armsr/armv7/openwrt-armsr-armv7-generic-ext4-rootfs.img
  99. EOF
  100. }
  101. rand_mac() {
  102. hexdump -n 3 -e '"52:54:00" 3/1 ":%02x"' /dev/urandom
  103. }
  104. parse_args() {
  105. o_network=
  106. o_qemu_extra=()
  107. while [ "$#" -gt 0 ]; do
  108. # Cmdline options for the script itself SHOULD try to be
  109. # prefixed with two dashes to distinguish them from those for
  110. # qemu executables.
  111. #
  112. # Also note that qemu accepts both --opt and -opt
  113. case "$1" in
  114. --kernel) o_kernel="$2"; shift 2 ;;
  115. --rootfs) o_rootfs="$2"; shift 2 ;;
  116. --machine|-machine|-M) o_mach="$2"; shift 2 ;;
  117. --network|-n) o_network=1; shift ;;
  118. --help|-h)
  119. usage
  120. exit 0
  121. ;;
  122. *)
  123. if [ -z "$o_target" ]; then
  124. o_target="$1"
  125. elif [ -z "$o_subtarget" ]; then
  126. o_subtarget="$1"
  127. else
  128. o_qemu_extra+=("$1")
  129. fi
  130. shift
  131. ;;
  132. esac
  133. done
  134. MAC_LAN="$(rand_mac)"
  135. MAC_WAN="$(rand_mac)"
  136. [ -n "$o_target" ] || {
  137. usage
  138. return 1
  139. }
  140. [ -n "$o_subtarget" ] || o_subtarget="generic"
  141. eval "$(grep ^CONFIG_BINARY_FOLDER= .config 2>/dev/null)"
  142. o_bindir="${CONFIG_BINARY_FOLDER:-bin}/targets/$o_target/$o_subtarget"
  143. }
  144. start_qemu_armsr() {
  145. local kernel="$o_kernel"
  146. local rootfs="$o_rootfs"
  147. local mach="${o_mach:-virt}"
  148. local cpu
  149. local qemu_exe
  150. case "${o_subtarget%-*}" in
  151. armv7)
  152. qemu_exe="qemu-system-arm"
  153. cpu="cortex-a15"
  154. [ -n "$kernel" ] || kernel="$o_bindir/openwrt-$o_target-${o_subtarget%-*}-generic-initramfs-kernel.bin"
  155. ;;
  156. armv8)
  157. qemu_exe="qemu-system-aarch64"
  158. cpu="cortex-a57"
  159. [ -n "$kernel" ] || kernel="$o_bindir/openwrt-$o_target-${o_subtarget%-*}-generic-initramfs-kernel.bin"
  160. ;;
  161. *)
  162. __errmsg "target $o_target: unknown subtarget $o_subtarget"
  163. return 1
  164. ;;
  165. esac
  166. [ -z "$rootfs" ] || {
  167. if [ ! -f "$rootfs" -a -s "$rootfs.gz" ]; then
  168. gunzip "$rootfs.gz"
  169. fi
  170. o_qemu_extra+=( \
  171. "-drive" "file=$rootfs,format=raw,if=virtio" \
  172. "-append" "root=/dev/vda rootwait" \
  173. )
  174. }
  175. [ -z "$o_network" ] || {
  176. o_qemu_extra+=( \
  177. "-netdev" "bridge,id=lan,br=$BR_LAN,helper=$HELPER" \
  178. "-device" "virtio-net-pci,id=devlan,netdev=lan,mac=$MAC_LAN" \
  179. "-netdev" "bridge,id=wan,br=$BR_WAN,helper=$HELPER" "-device" \
  180. "virtio-net-pci,id=devwan,netdev=wan,mac=$MAC_WAN" \
  181. )
  182. }
  183. "$qemu_exe" -machine "$mach" -cpu "$cpu" -nographic \
  184. -kernel "$kernel" \
  185. "${o_qemu_extra[@]}"
  186. }
  187. start_qemu_malta() {
  188. local is64
  189. local isel
  190. local qemu_exe
  191. local cpu
  192. local rootfs="$o_rootfs"
  193. local kernel="$o_kernel"
  194. local mach="${o_mach:-malta}"
  195. # o_subtarget can be le, be, le64, be64, le-glibc, le64-glibc, etc..
  196. is64="$(echo $o_subtarget | grep -o 64)"
  197. [ "$(echo "$o_subtarget" | grep -o '^..')" = "le" ] && isel="el"
  198. qemu_exe="qemu-system-mips$is64$isel"
  199. [ -n "$is64" ] && cpu="MIPS64R2-generic" || cpu="24Kc"
  200. [ -n "$kernel" ] || kernel="$o_bindir/openwrt-malta-${o_subtarget%-*}-vmlinux-initramfs.elf"
  201. [ -z "$rootfs" ] || {
  202. if [ ! -f "$rootfs" -a -s "$rootfs.gz" ]; then
  203. gunzip "$rootfs.gz"
  204. fi
  205. o_qemu_extra+=( \
  206. "-drive" "file=$rootfs,format=raw" \
  207. "-append" "root=/dev/sda rootwait" \
  208. )
  209. }
  210. # NOTE: order of wan, lan -device arguments matters as it will affect which
  211. # one will be actually used as the wan, lan network interface inside the
  212. # guest machine
  213. [ -z "$o_network" ] || {
  214. o_qemu_extra+=(
  215. -netdev bridge,id=wan,br="$BR_WAN,helper=$HELPER" -device pcnet,netdev=wan,mac="$MAC_WAN"
  216. -netdev bridge,id=lan,br="$BR_LAN,helper=$HELPER" -device pcnet,netdev=lan,mac="$MAC_LAN"
  217. )
  218. }
  219. "$qemu_exe" -machine "$mach" -cpu "$cpu" -nographic \
  220. -kernel "$kernel" \
  221. "${o_qemu_extra[@]}"
  222. }
  223. start_qemu_x86() {
  224. local qemu_exe
  225. local kernel="$o_kernel"
  226. local rootfs="$o_rootfs"
  227. local mach="${o_mach:-pc}"
  228. [ -n "$rootfs" ] || {
  229. rootfs="$o_bindir/openwrt-$o_target-${o_subtarget%-*}-generic-squashfs-combined.img"
  230. if [ ! -f "$rootfs" -a -s "$rootfs.gz" ]; then
  231. gunzip "$rootfs.gz"
  232. fi
  233. }
  234. #
  235. # generic: 32-bit, pentium4 (CONFIG_MPENTIUM4), kvm guest, virtio
  236. # legacy: 32-bit, i486 (CONFIG_M486)
  237. # 64: 64-bit, kvm guest, virtio
  238. #
  239. case "${o_subtarget%-*}" in
  240. legacy) qemu_exe="qemu-system-i386" ;;
  241. generic|64) qemu_exe="qemu-system-x86_64" ;;
  242. *)
  243. __errmsg "target $o_target: unknown subtarget $o_subtarget"
  244. return 1
  245. ;;
  246. esac
  247. [ -n "$kernel" ] && {
  248. o_qemu_extra+=( \
  249. "-kernel" "$kernel" \
  250. "-append" "root=/dev/vda console=ttyS0 rootwait" \
  251. )
  252. }
  253. [ -z "$o_network" ] || {
  254. case "${o_subtarget%-*}" in
  255. legacy)
  256. o_qemu_extra+=(
  257. -netdev "bridge,id=lan,br=$BR_LAN,helper=$HELPER" -device "e1000,id=devlan,netdev=lan,mac=$MAC_LAN"
  258. -netdev "bridge,id=wan,br=$BR_WAN,helper=$HELPER" -device "e1000,id=devwan,netdev=wan,mac=$MAC_WAN"
  259. )
  260. ;;
  261. generic|64)
  262. o_qemu_extra+=(
  263. -netdev "bridge,id=lan,br=$BR_LAN,helper=$HELPER" -device "virtio-net-pci,id=devlan,netdev=lan,mac=$MAC_LAN"
  264. -netdev "bridge,id=wan,br=$BR_WAN,helper=$HELPER" -device "virtio-net-pci,id=devwan,netdev=wan,mac=$MAC_WAN"
  265. )
  266. ;;
  267. esac
  268. }
  269. case "${o_subtarget%-*}" in
  270. legacy)
  271. # use IDE (PATA) disk instead of AHCI (SATA). Refer to link
  272. # [1] for related discussions
  273. #
  274. # To use AHCI interface
  275. #
  276. # -device ich9-ahci,id=ahci \
  277. # -device ide-hd,drive=drv0,bus=ahci.0 \
  278. # -drive "file=$rootfs,format=raw,id=drv0,if=none" \
  279. #
  280. # [1] https://dev.openwrt.org/ticket/17947
  281. "$qemu_exe" -machine "$mach" -nographic \
  282. -device ide-hd,drive=drv0 \
  283. -drive "file=$rootfs,format=raw,id=drv0,if=none" \
  284. "${o_qemu_extra[@]}"
  285. ;;
  286. generic|64)
  287. "$qemu_exe" -machine "$mach" -nographic \
  288. -drive "file=$rootfs,format=raw,if=virtio" \
  289. "${o_qemu_extra[@]}"
  290. ;;
  291. esac
  292. }
  293. start_qemu() {
  294. case "$o_target" in
  295. armsr) start_qemu_armsr ;;
  296. malta) start_qemu_malta ;;
  297. x86) start_qemu_x86 ;;
  298. *)
  299. __errmsg "target $o_target is not supported yet"
  300. return 1
  301. ;;
  302. esac
  303. }
  304. parse_args "$@" \
  305. && check_setup \
  306. && start_qemu