qemu联网与控制
qemu联网与控制
我家跨上了"信息高速路"🤗
因为要开发qemu外围的设备,需要qemu中的guest系统识别到这个设备并能够开发与这个设备相匹配的驱动。但从源码编译的Linux系统过于简陋,甚至lspci命令都无法显示详细的硬件信息,找不到设备对应的mmio和pmio地址。想要更新安装pciutils,就需要从源码开始进行编译安装;
一想到后续的dhcp、hostapd 、wpa_supplicant 甚至 ifconfig的net-tools都要从源码开始安装,就不得不考虑一个更简单易用的环境来进行开发。
本来是想使用build-root来编译源码,但修改配置文件仍然十分麻烦,把时间消耗在这些外围的工具上属于十分不明智的行为。
本文的重点为:
-
一切的开始——qemu的源码编译
-
简单完美的开发环境——qemu中 guest ubuntu desktop 系统的安装
-
我家跨上了信息高速路——qemu网络配置
-
优雅的退出——启用 qemu monitor模式
我的系统环境:
从源码编译qemu
虽然从源码编译一个软件十分令人反感,但由于要开发qemu相关的模拟设备,从源码编译qemu是不得不品尝的一环。
首先需要安装编译所需的依赖,这里使用ninja编译
sudo apt -y install clang ninja-build build-essential zlib1g-dev pkg-config libglib2.0-dev binutils-dev libpixman-1-dev libfdt-dev
从官网或其他某些镜像站(比如Tsinghua的镜像站)获取qemu正式版的源码,我这里是qemu-9.0.0的源码
wget https://download.qemu.org/qemu-9.0.0.tar.xz
解压
xz -d qemu-9.0.0.tar.xz
tar xvf qemu-9.0.0.tar
进入qemu源码目录,新建build目录并进入
cd qemu-9.0.0
mkdir build
cd build
生成源码编译配置文件,可以先使用--help命令查看配置文件的选项:
../configure --help
这里仅编译系统模式下的x86_64模拟器和aarch模拟器,一个用来跑Ubuntu,另一个在未来用来跑openwrt;启用kvm用于在模拟时加速,启用debug模式用于调试qemu:
../configure --enable-kvm --enable-debug --target-list=x86_64-softmmu,aarch64-softmmu
一般生成配置文件这一步不会出错,如果报python错误,需要安装python-is-python3:
sudo apt install python-is-python3
开始编译:
ninja
之后会在build目录下生成所需的三个可执行文件:
-
qemu-system-x86_64 x86_64的模拟器
-
qemu-img 用于生成虚拟磁盘文件,对guest来说就是磁盘
-
qemu-system-aarch64 aarch64的模拟器
qemu中Ubuntu的安装
-
下载系统镜像
安装系统肯定要下载一个系统iso镜像。这里安装Ubuntu的desktop版本的系统,虽然server版本的也能用,但配置网络环境的过程过于繁琐,而且server版本启动时需要连接网络读取配置文件,在没有配置好qemu网络时启动非常的慢;这里选择去Tsinghua镜像站下载,尽管我想使用北邮的镜像站,但是在校内访问北邮镜像站比访问Tsinghua的镜像站还要慢是怎么回事?!
wget https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/22.04/ubuntu-22.04.4-desktop-amd64.iso # 当然也可以试试北邮的 wget https://mirrors.bupt.edu.cn/ubuntu-releases/22.04/ubuntu-22.04.4-desktop-amd64.iso
-
制作虚拟磁盘
只启动一个Linux Kernel不需要虚拟磁盘,但仍然需要使用busybox等工具制作一个文件系统(file system)才能正常的使用Linux,否则会在启动后提示缺少rootfs。这里不需要手动制作文件系统,Ubuntu的安装程序会自动的格式化磁盘并创建文件系统的目录,因此仅需要创建一个虚拟磁盘文件即可;
使用qemu自带的工具创建磁盘文件,这里创建一个名为ubuntu.qcow2的文件
../qemu_simudevice/build/qemu-img create -f qcow2 ubuntu.qcow2 40G
-
安装系统
ubuntu好就好在和物理机一样的安装使用方式,自带完整的qemu驱动,无需手动寻找安装。启动qemu并加载iso文件:
../qemu_simudevice/build/qemu-system-x86_64 \ -m 2G \ -drive format=qcow2,file=ubuntu.qcow2 \ -enable-kvm \ -vnc 10.129.19.237:0 \ -cdrom ./ubuntu-22.04.4-desktop-amd64.iso
我的qemu是跑在远程服务器端,所以需要在-vnc命令后跟上服务器的ip地址,才能在远程通过vnc的方式访问到虚拟机的界面;qemu启动的虚拟机默认会在5900端口上启动一个vnc server,这里的:0并不是指的端口号,而是指vnc server的编号,0代表的端口号为5900,1代表5901,以此类推。
启动后就能在远程的vnc客户端中通过ip:5900来访问到guest ubuntu,下方的图片为已经安装好系统的情况,首次访问需要先安装系统,由于在启动时没有设置任何与网络相关的参数,在安装系统时是无法连接到Internet的;
安装完毕,重启时Ubuntu会提示弹出安装cd,此时只需在host的控制台使用Ctrl + c关掉qemu,之后注释掉iso相关的参数再启动即可。
Ubuntu已经被安装进上面我们制作的虚拟磁盘ubuntu.qcow2中:
../qemu_simudevice/build/qemu-system-x86_64 \ -m 2G \ -drive format=qcow2,file=ubuntu.qcow2 \ -enable-kvm \ -vnc 10.129.19.237:0
qemu网络配置
安装Ubuntu目的就是利用其丰富的线上安装包仓库来安装各种工具,方便我们对qemu进行开发;qemu提供了多种方式让guest连接Internet,其原生支持启动时自动执行/etc/qemu-ifup脚本来创建tap虚拟网卡并连接Internet,关闭时自动执行/etc/qemu-ifdown脚本删除tap虚拟网卡;
由于从源码编译的qemu-system-x86_64并没有安装进host系统中,因此执行的脚本路径变为/build/qemu-bundle/usr/local/etc/
编译完成后/build/qemu-bundle/usr/local/路径下并没有/etc目录,需要手动新建目录和文件,并写入qemu wiki上指定的脚本 https://wiki.qemu.org/Documentation/Networking/NAT
执行该脚本需要先在host系统中安装如下工具:
sudo apt install bridge-utils iptables dnsmasq
qemu-ifup
#!/bin/sh
#
# Copyright IBM, Corp. 2010
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.com>
#
# This work is licensed under the terms of the GNU GPL, version 2. See
# the COPYING file in the top-level directory.
# Set to the name of your bridge
BRIDGE=br0
# Network information
NETWORK=192.168.53.0
NETMASK=255.255.255.0
GATEWAY=192.168.53.1
DHCPRANGE=192.168.53.2,192.168.53.254
# Optionally parameters to enable PXE support
TFTPROOT=
BOOTP=
do_brctl() {
brctl "$@"
}
do_ifconfig() {
ifconfig "$@"
}
do_dd() {
dd "$@"
}
do_iptables_restore() {
iptables-restore "$@"
}
do_dnsmasq() {
dnsmasq "$@"
}
check_bridge() {
if do_brctl show | grep "^$1" > /dev/null 2> /dev/null; then
return 1
else
return 0
fi
}
create_bridge() {
do_brctl addbr "$1"
do_brctl stp "$1" off
do_brctl setfd "$1" 0
do_ifconfig "$1" "$GATEWAY" netmask "$NETMASK" up
}
enable_ip_forward() {
echo 1 | do_dd of=/proc/sys/net/ipv4/ip_forward > /dev/null
}
add_filter_rules() {
do_iptables_restore <<EOF
# Generated by iptables-save v1.3.6 on Fri Aug 24 15:20:25 2007
*nat
:PREROUTING ACCEPT [61:9671]
:POSTROUTING ACCEPT [121:7499]
:OUTPUT ACCEPT [132:8691]
-A POSTROUTING -s $NETWORK/$NETMASK -j MASQUERADE
COMMIT
# Completed on Fri Aug 24 15:20:25 2007
# Generated by iptables-save v1.3.6 on Fri Aug 24 15:20:25 2007
*filter
:INPUT ACCEPT [1453:976046]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [1605:194911]
-A INPUT -i $BRIDGE -p tcp -m tcp --dport 67 -j ACCEPT
-A INPUT -i $BRIDGE -p udp -m udp --dport 67 -j ACCEPT
-A INPUT -i $BRIDGE -p tcp -m tcp --dport 53 -j ACCEPT
-A INPUT -i $BRIDGE -p udp -m udp --dport 53 -j ACCEPT
-A FORWARD -i $1 -o $1 -j ACCEPT
-A FORWARD -s $NETWORK/$NETMASK -i $BRIDGE -j ACCEPT
-A FORWARD -d $NETWORK/$NETMASK -o $BRIDGE -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o $BRIDGE -j REJECT --reject-with icmp-port-unreachable
-A FORWARD -i $BRIDGE -j REJECT --reject-with icmp-port-unreachable
COMMIT
# Completed on Fri Aug 24 15:20:25 2007
EOF
}
start_dnsmasq() {
do_dnsmasq \
--strict-order \
--except-interface=lo \
--interface=$BRIDGE \
--listen-address=$GATEWAY \
--bind-interfaces \
--dhcp-range=$DHCPRANGE \
--conf-file="" \
--pid-file=/var/run/qemu-dnsmasq-$BRIDGE.pid \
--dhcp-leasefile=/var/run/qemu-dnsmasq-$BRIDGE.leases \
--dhcp-no-override \
${TFTPROOT:+"--enable-tftp"} \
${TFTPROOT:+"--tftp-root=$TFTPROOT"} \
${BOOTP:+"--dhcp-boot=$BOOTP"}
}
setup_bridge_nat() {
if check_bridge "$1" ; then
create_bridge "$1"
enable_ip_forward
add_filter_rules "$1"
start_dnsmasq "$1"
fi
}
setup_bridge_vlan() {
if check_bridge "$1" ; then
create_bridge "$1"
start_dnsmasq "$1"
fi
}
setup_bridge_nat "$BRIDGE"
if test "$1" ; then
do_ifconfig "$1" 0.0.0.0 up
do_brctl addif "$BRIDGE" "$1"
fi
qemu-ifdown
#! /bin/sh
# Script to shut down a network (tap) device for qemu.
# Initially this script is empty, but you can configure,
# for example, accounting info here.
#! /bin/sh
switch=br0
brctl delif ${switch} $1
ifconfig $1 down
#ip link set $1 down
#tunctl -d $1
之后在启动qemu的命令行中添加net相关的参数,使用sudo启动qemu,其会自动执行上述的脚本来创建虚拟网卡,修改转发路由表;guest系统会自动的读取到虚拟的网卡并联网;
../qemu_simudevice/build/qemu-system-x86_64 \
-m 2G \
-drive format=qcow2,file=ubuntu.qcow2 \
-enable-kvm \
-net tap -net nic \
-vnc 10.129.19.237:0
使用lspci命令查看设备,可以看到虚拟的以太网卡设备:
打开浏览器,可以正常的访问Internet
启动qemu monitor模式
每次退出qemu都使用Ctrl + c退出有点过于强硬,但是使用vnc启动虚拟机后,使用Ctrl + a , c的方式又无法切换到monitor控制台,可以通过添加-nographic参数来改变qemu启动后控制台的属性。添加该参数后并不会影响vnc的工作:
../qemu_simudevice/build/qemu-system-x86_64 \
-m 2G \
-drive format=qcow2,file=ubuntu.qcow2 \
-enable-kvm \
-net tap -net nic \
-vnc 10.129.19.237:0 \
-nographic
启动qemu后,host控制台会在输出几行命令后停止输出,这并非卡住,而是Linux的启动输出至vnc中,此时在host端使用Ctrl + a , c便可切换到monitor模式,在monitor模式下使用quit便可退出qemu