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