QEMU 虚拟机网卡探究(模拟实现)

前述

我们知道无论是VMware,Virtual Box还是HyperV 都支持 NAT/Bridge/Host-Only 三种上网方式。其中 NAT 是我最常用,最熟悉的。
需要说明的是,无论是NAT 还是 Bridge, 虚拟机与宿主机、虚拟机与虚拟机、虚拟机与外网、宿主机与外网之间都是通的!!!(不要相信CSDN某些人的博客,实践出真知!)
下面就用qemu 模拟实现下 NAT与Bridge。无论哪种本质上都是在宿主机侧的设置,因为虚拟机的IP网关设置不属于必须操作!
注:建议看完NAT自己实现下host-only模式. host-only 虚拟机是不通外网的(提示,按照NAT的步骤,但不设置防火墙试试)

openwrt 虚拟机安装方式

由于 openwrt 官方下载的镜像并不是我们以为的 iso 的格式。因此特地记录下

  1. 查看镜像文件格式
➜  Wdir qemu-img info openwrt-19.07.4-x86-generic-combined-ext4.img
image: openwrt-19.07.4-x86-generic-combined-ext4.img
file format: raw
virtual size: 272 MiB (285736960 bytes)
disk size: 272 MiB

如上可以知道,镜像是 raw 格式的,需要转换为 qemu 能识别的 qcow2 格式。

  1. 转换镜像
➜  Wdir qemu-img convert -f raw -O qcow2 openwrt-19.07.4-x86-generic-combined-ext4.img openwrt-19.07.4-x86-generic-combined-ext4.qcow2
➜  Wdir ls -l |grep openwrt
-rw-r--r-- 1 nobody kvm    285736960 12月  3 16:00 openwrt-19.07.4-x86-generic-combined-ext4.img
-rw-r--r-- 1 nobody kvm    285736960 12月  3 16:00 openwrt-19.07.4-x86-generic-combined-ext4.qcow2
-rw-r--r-- 1 lester lester   8414308  9月  8 21:24 openwrt-19.07.4-x86-generic-combined-ext4.img.gz
➜  Wdir 

如上我们的到了一个 qcow2 格式的镜像文件,qemu 可以直接使用。如果你曾经安装过 qemu 虚拟机,就会知道 qemu 下的虚拟机文件是qcow2 格式的。创建虚拟机步骤略。

NAT

NAT实现需要借助于防火墙,这里用iptables来做转发与网络地址转换!
涉及到虚拟机网卡 vnet0(虚拟机起来后,host端生成的网卡,不是虚拟机内的网卡),网桥 br,宿主机网卡 enp1s0

原理

NAT 普遍被用在路由器中。一个路由设备通常由一个 WAN 口用于连接外网也就是光猫或其他路由设备,多个LAN 口用于连接其他上网的设备,一个由本地 LAN 口组成的网桥 br 用于 DHCP等。
通常本地设备获取的地址都是 192.168.x.x 的IP地址,这些地址都是由运行在网桥上的 dhcp server来分配的。网桥上是一定要有地址的,通常是 192.168.x.1
WAN 口用于链接外网。所有 LAN 侧报文(本地交互除外)都会经由 WAN 转发到外网。

# 注: 虚线部分代表有网络地址转换
入栈: 外网数据 -> WAN ------> br 网桥 -> LAN
出栈: 外网数据 <- WAN <------ br 网桥 <- LAN

iptable与更详细的设置参考
宿主机设置:

# 注: br 为手动创建的网桥。vnet0 为虚拟机在host 上生成的网卡,不是虚拟机内部的网卡
brctl addbr br # 创建网桥
brctl addif br vnet0  # 添加成员
ifconfig br up

报文转发流程图

# 出栈
虚拟机网卡(eth0)-> 虚拟机网卡(vnet0)->网桥(br)---- iptable forward -----宿主机网卡(enp1s0)->发出
# 入栈
虚拟机网卡(eth0)<- 虚拟机网卡(vnet0)<-网桥(br)---- iptable forward -----宿主机网卡(enp1s0)<-收到

如上,需要配置iptable的转发,让报文能够从 br 转到 enp1s0 上,这步很重要(host-only 可以跳过)。(需要 /proc/sys/net/ipv4/ip_forward 为 1)

# 1. 简单粗暴的操作
iptables -F # 清空防火墙规则
iptables -P FORWARD ACCEPT # 接受所有转发

# 2. 也可以这样,定义精细的规则
iptables -A FORWARD -i br -o enp1s0 -j ACCEPT
iptables -A FORWARD -o br -i enp1s0 -j ACCEPT

另外要知道 NAT 发出的报文原地址是enp1s0 的地址,这就要求iptable也要做地址转换(host-only 可以跳过)

iptable -t nat -A POSTROUTING -s 192.168.122.0/24 -j MASQUERADE

至此,NAT 设置完毕,设置地址ping测试就行了

# 宿主机
ifconfig br 192.168.122.1/24

# 虚拟机内
ifconfig eth0 192.168.122.10/24
ip route add default via 192.168.122.1 dev eth0

这时候在虚拟机 eth0 上测试 ping 8.8.8.8 是ok的

虚拟机:

宿主机:

Bridge

本文仅借助了 Linux 网桥设备实现了桥接功能,与 QEMU 的实现并不一样!

实际上,qemu 是利用 macvtap 来实现的桥接。借助 macvlan 来实现复用主机网卡。所以你会发现qemu在设置桥接模式时候并没有出现新的网桥设备,反而多了一个名字为 macvtapX 的网卡,且这个网卡的 MAC 地址与虚拟机的 MAC 地址一样!

宿主机报文转发流程图

# 出栈
网桥(br)-> 宿主机网卡(enp1s0)->发出
# 入栈
网桥(br)<- 宿主机网卡(enp1s0)<-收到

虚拟机报文转发流程图
思考为什么这时候不需要iptable做 forward 规则(因为vnet0,enp1s0 都是网桥子网卡,这时候相当于二层交换机的两个端口,因此是互通的,不需要三层的iptable支持转发)

# 出栈
虚拟机网卡(eth0)-> 虚拟机网卡(vnet0)->网桥(br)-> 宿主机网卡(enp1s0)->发出
# 入栈
虚拟机网卡(eth0)<- 虚拟机网卡(vnet0)<-网桥(br)<- 宿主机网卡(enp1s0)<-收到

宿主机设置:

brctl addbr br
brctl addif br enp1s0 # 添加网卡
brctl addif br vnet0 # 添加网卡
ifconfig br up

# 注意此时宿主机网卡不能配置地址,或者说配置地址也没用,需要把地址配置到网桥br上
ifconfig br 192.168.100.125/24
ip route add default via 192.168.100.1 dev br

宿主机:

虚拟机:

然后在虚拟机做dhcp,或者手动配置(手动配置需要确保地址与宿主机网卡地址在同一网段,比如地址设为 192.168.100.10/24)
现在在虚拟机ping 网关,主机或者8.8.8.8 都是通的

posted @ 2020-09-11 17:06  sinpo828  阅读(3407)  评论(0编辑  收藏  举报