0%

Linux虚拟网络之虚拟机

本文将通过实验来理解Linux上的虚拟机如何通过tap、bridge、router以及iptables实现相关的网络访问功能。

环境准备

系统: Ubuntu 16.04.6 LTS

实验topo

软件包

1. qemu

由于整套实验环境都是在虚拟机上完成,在没有开启诸如Intel-VTx技术的情况下,使用qemu作为虚拟机模拟器

1
2
3
$ apt install qemu
$ apt install uml-utilities
$ apt install bridge-utils

2. 虚拟机镜像

本文使用cirros(当前版本:0.4.0)作为虚拟机镜像

1
2
$ wget http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img
$ cp cirros-0.4.0-x86_64-disk.img cirros.img

3. vncserver

使用qemu启动cirros需要用到图形界面,在纯命令行模式启动,需要使用vncserver

1
$ apt install tightvncserver

4. 环境检查

先拉起个虚机试试

1
$ qemu-system-x86_64 -vnc 10.180.13.232:99 cirros-0.4.0-x86_64-disk.img

5. 测试

用vnc viewer连接当前虚拟机

tap接口使用

1. 创建虚拟机

1
(host)$ qemu-system-x86_64 -vnc 10.180.13.232:99 cirros-0.4.0-x86_64-disk.img -netdev tap,id=mynet0,ifname=tap1,script=no,downscript=no -device e1000,netdev=mynet0

创建成功后,可以看到,自动创建了一个tap1的接口,但没有up

1
2
3
4
5
6
7
8
9
(host)$ ifconfig -a
...
tap1 Link encap:Ethernet HWaddr 7a:d1:ba:68:06:b2
BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
...

2. 测试连通性

  • 配置host的tap1接口IP
1
(host)$ ifconfig tap1 192.168.1.1/24 up
  • 配置虚拟机的IP
1
(vm)$ sudo ifconfig eth0 192.168.1.2
  • ping
1
2
3
4
(vm)$ ping 192.168.1.2
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=16.6 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=1.51 ms

3. 小结

使用tap类型的netdev创建qemu虚拟机时,会自动创建一个tap接口,而这个tap接口与VM的接口相连。

连接图如下:

虚拟机通信

理解了虚拟机使用tap接口之后,就可以考虑使用网桥方式进行2个甚至多个虚拟机之间通信了。

1. 创建虚拟机

先拉起4个VM,分别指定网络

注意,必须为每个虚机指定MAC地址,否则,拉起的虚机将使用同样的MAC地址

1
2
(host)$ qemu-system-x86_64 -vnc 10.180.13.232:10 cirros.img -netdev tap,id=mynet0,ifname=tap0,script=no,downscript=no -device e1000,netdev=mynet0,mac=52:54:98:76:54:30 &
(host)$ qemu-system-x86_64 -vnc 10.180.13.232:11 cirros.img -netdev tap,id=mynet1,ifname=tap1,script=no,downscript=no -device e1000,netdev=mynet1,mac=52:54:98:76:54:31 &

查看Host网卡

1
2
3
4
5
6
(host)$ ip link show
...
46: tap0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 42:47:1b:de:8e:07 brd ff:ff:ff:ff:ff:ff
47: tap1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether f2:23:b1:c5:55:d3 brd ff:ff:ff:ff:ff:ff

可以看到随虚拟机启动,创建了4个tap接口

2. 配置IP

VM1

1
(vm1)$ sudo ifconfig eth0 192.168.1.10

VM2

1
(vm2)$ sudo ifconfig eth0 192.168.1.11

3. 创建网桥

Host

1
2
3
4
5
6
(host)$ brctl addbr br0
(host)$ brctl addif br0 tap0
(host)$ brctl addif br0 tap1
(host)$ bridge name bridge id STP enabled interfaces
br0 8000.42471bde8e07 no tap0
tap1

尝试ping一下,发现还是ping不通。因为对应的tap接口及br0没有up

1
2
3
(host)$ ifconfig br0 up
(host)$ ifconfig tap0 up
(host)$ ifconfig tap1 up

现在,可以两个VM相互ping一下了

4. 访问外部PC

虚拟机需要访问外部网络,则需要添加物理接口,如下图

Host

1
2
(host)$ ifconfig ens192 up
(host)$ brctl addif br0 ens192

VM1

1
2
3
4
(vm1)$ ping 192.168.1.3
PING 192.168.1.3 (192.168.1.3): 56 data bytes
64 bytes from 192.168.1.3: seq=0 ttl=64 time=29.700 ms
...

抓个包看看 - ens192

1
2
3
4
5
(host)$ tcpdump -i ens192
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
11:15:02.317164 IP 192.168.1.11 > 192.168.1.3: ICMP echo request, id 47873, seq 0, length 64
11:15:02.317527 IP 192.168.1.3 > 192.168.1.11: ICMP echo reply, id 47873, seq 0, length 64

5. 小结

本节主要实验了虚拟机通过网桥进行相互通信及访问外网。对应了qemu网络的基于网桥的虚拟网卡

NAT模式

Host需要开启转发功能

临时修改:echo "1" > /proc/sys/net/ipv4/ip_forward

NAT模式主要通过iptables来实现。通过将虚拟机发出的报文的源地址转换为物理接口的IP,实现与外网的通信

拓扑图如下:

1. 为网桥配置IP地址

用网桥的br0接口作为192.168.1.0/24网段的网关

1
(host)$ ifconfig br0 192.168.1.1/24

测试host与外部网络连通性

需要确保host有通往外部网络的路由

1
2
3
(host)$ ping 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=115 time=11.7 ms

2. 配置iptables

将192.168.1.0/24网段转换为发送出去的接口IP

MASQUERADE: 用发送数据的网卡上的IP来替换源IP

1
(host)$ iptables -t nat -A POSTROUTING -s 192.168.1.0/24 ! -d 192.168.1.0/24  -j MASQUERADE

3. 虚拟机配置默认路由

1
(vm1)$ route add -net 0.0.0.0/0 gw 192.168.1.1

4. 测试连通性

1
2
3
4
(vm1)$ ping 223.5.5.5
PING 223.5.5.5 (223.5.5.5): 56 data bytes
64 bytes from 223.5.5.5: seq=0 ttl=114 time=14.161 ms
...

5. 抓包看看

tap1

1
2
3
4
5
6
(host)$ tcpdump -i tap0 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tap0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:33:41.158181 IP 192.168.1.10 > 223.5.5.5: ICMP echo request, id 53761, seq 0, length 64
14:33:41.332908 IP 223.5.5.5 > 192.168.1.10: ICMP echo reply, id 53761, seq 0, length 64
...

tap1接口上体现了虚拟机的请求: 192.168.1.10 - 223.5.5.5

br0

1
2
3
4
5
$ tcpdump -i br0 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:35:05.964841 IP 192.168.1.10 > 223.5.5.5: ICMP echo request, id 54017, seq 0, length 64
14:35:06.028936 IP 223.5.5.5 > 192.168.1.10: ICMP echo reply, id 54017, seq 0, length 64

网桥接口br0上的报文于tap0相同,既当前并没有修改报文

ens160

1
2
3
4
5
$ tcpdump -i ens160 -n host 223.5.5.5
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens160, link-type EN10MB (Ethernet), capture size 262144 bytes
14:36:22.762002 IP 10.180.13.232 > 223.5.5.5: ICMP echo request, id 54273, seq 0, length 64
14:36:22.775182 IP 223.5.5.5 > 10.180.13.232: ICMP echo reply, id 54273, seq 0, length 64

可以看到在ens160接口上,IP则变成了10.180.13.232 - 223.5.5.5之间的通信

6. 小结

在本节中,当虚拟机访问外部网络时,从虚拟机内部发出的报文,在主机上通过查路由的方式转发到外部网络中。而在转发出去之前,将源IP地址修改为当前主机的接口IP