源贴地址:http://blog.chinaunix.net/u/24470/showart_1386495.html
VirtualBox是一款高效的开源虚拟机,但是其网络设置却不及VMWare方便,因此许多人只
能望而却步,本文基于Fedora9上的VirtualBox2.0讨论其多种虚拟机网络配置。
看过VirtualBox文档,觉得它讲得不是很清楚,现在我们先抛开它的文档,看看网络配置
到底有多神秘!
Linux从它诞生就有着优良的网络性能,而VirtualBox其实并不需要额外的工具就可以完
美地实现和宿主网络共享。
Virtual Box虚拟网络的多中配置方式
一、桥接:
扫盲部分:
什么是桥?简单的说桥是一个接口,是一个在功能层面和eth0一样的东西。不同之处在于
桥是由多个接口共同构成的。从以太层看,把每一个接口收到的数据复制到其他接口。同
样,向桥写入数据,桥内的所有接口都会收到。
现在要介绍一个设备,tap或着tun,简单的说这个东西是一块虚拟网卡。不同的是,tap
是一个以太层的设备,而tun是Ip层的设备。(下面我们要使用的是tap。)具体来说
tap/tun的工作原理是这样的:用户空间的程序向tap/tun写入数据,这些数据会传到内核
的网络模块;内核网络模块也可以写入数据,而这些写入的数据又可以被用户空间的程序
读到,正是因为这样,连上这块虚拟网卡的虚拟机就可以和宿主通信了!
现在我们开始:
首先,创建一个Tap设备:
[root@sunshine ~]# tunctl -u root -t tap0
Set 'tap0' persistent and owned by uid 0
-u:指明这个设备归谁所有(为了避免权限问题引出的不必要的麻烦,本文中创建的所有
设备都属于root);
-t:指明设备名,可以任意起,但是最好遵循linux上的命名规范;
(如果要删除创建的Tap设备,请使用tunctl -d [tapdevicename])
如果你没有tunctl命令请安装tunctl包,下面的brctl也是一样。
接下来,我们需要创建一个桥:
[root@sunshine ~]# brctl addbr br0
然后向桥里添加接口:
[root@sunshine ~]# brctl addif br0 eth0
[root@sunshine ~]# brctl addif br0 tap0(这个就是我们刚才创建的)
我们要保证eth0和tap0没有配置信息(有的话,在某些系统中会失败),所以需要:
[root@sunshine ~]#ifconfig eth0 0.0.0.0
[root@sunshine ~]#ifconfig tap0 0.0.0.0
最后,我们配置br0:
[root@sunshine ~]#ifconfig br0 192.168.40.1 netmask 255.255.255.0 broadcast
192.168.40.255
注意:这个配置信息应该和实际网络中的一致,换句话说,就是和你原来的eth0配置一致。
有人纳闷了,为什么不配置eth0和tap0呢?当两个或着多个接口组成桥的时候,它们可以
被认为是低层设备,因为没有必要再强调它们的独立性了。而桥则成为了一个新的接口。
确保这两个接口被打开,最好再键入下列命令:
[root@sunshine ~]#ifconfig eth0 up
[root@sunshine ~]#ifconfig tap0 up
现在我们配置虚拟机(本文假定你已经装好了一台虚拟机):
把“Interface Name:”一栏改成tap0。
现在就可以启动虚拟机了……
在本例中虚拟机网络配置如下:
值得强调的是,Guest机器的ip必须要和宿主在同一个子网中才能和宿主通信!
如果你不关心具体原理,请略过本段。
现在我们可以这样认为:宿主的主板上新添了一块叫做tap0的网卡,网卡上连着网线,网
线的另一端连着Guest,而同时这块网卡是br0桥的一部分。从以太底层来看,eth0收到的
数据tap0也可以收到,所以连在tap0上的Guest就可以收到进入eth0的数据;而进入tap0
的数据eth0也可以收到,所以连在eth0上的所有主机都可以收到来自Guest的数据。然后
宿主和Guest的网络驱动就会从这一大堆杂乱的数据中挑选出发往自己IP的数据。正是基
于这样的原理宿主和Guest实现了共享接口。(如果你还是不明白,请仔细揣摩前文中关
于桥的定义。)而且也恰恰因为这个原理,桥的效率不高!本文后面会介绍更高效的解决
方案!
又开始扫盲,老手可以跳过了……
br0又是怎么回事?正如前文所述,br0是一个接口。把宿主主板上的eth0以及tap0用“魔
法数据线”连起来,形成了一块新网卡,这就是br0。
现在,客户机器里面ping 192.168.40.1就可以ping通了!
还有一点不爽,现在Guest只能和宿主通信,不能上网!但是经过了上面设置,这一步就
很简单了:
Guest中把默认网关设为宿主的默认网关,本例中为192.168.40.3;DNS设得和宿主的一样
就OK,现在Guest也可以上网了。
新手要是晕了没有关系,道理再简单不过了:因为Guest的和宿主的默认网关在同一子
网,把Guest可以直接访问默认网关,就意味着所Guest把不知道往哪里发的数据包都给了
默认网关,默认网关会妥善处理这些数据包,可以认为这些数据被成功发送了,所以就自
然可以上网了。
呵呵,说了这么多,大概意思就是玩个桥的把戏而已,扫盲成分偏多。下面我们看看比桥
更高效的共享方式:NAT
二、NAT:
必须说明的是,这个NAT是用linux的iptables实现的,而不是VirtualBox上虚拟机网络设
定中的那个NAT,后者功能有限、非常简单,毋需多言,将放在最后讨论。
为什么要用NAT?
桥基本可以实现大多数功能了,但是桥的问题在于:
1、Guest的地位相当于网络上的一台真实主机,这样会带来安全问题。
2、当宿主是静态IP的时候,把Guest设为同一子网中的主机非常容易造成IP冲突!
3、桥比较慢。
说了这些,我们先看看什么是NAT:
NAT就是“网络地址翻译”的简写。NAT有两种:DNAT(目的NAT)、SNAT(原NAT)。前者是
把数据包的目的地址改为指定地址,后者是把数据包的源地址改为指定地址。在这里我们
要用的是SNAT。简而言之,SNAT 的作用就是在宿主机器内部构造一个独立的子网,Guest
就是子网中的一台机器,当子网中的数据要发送出来的时候,这些数据的源地址会被改为
宿主的实际网络地址,这样就实现了IP伪装,也就是说,Guest可以把宿主当作网关,宿
主转发Guest要发送的数据,这样Guest就可以上网了。
好了,说的很抽象,具体看步骤吧:
首先,删除前面创建的br0:
brctl -d br0
然后,重新配置tap0:
[root@sunshine ~]#ifconfig tap0 192.168.40.1 netmask 255.255.255.0 broadcast
192.168.40.255
并且保证图一虚拟机设定中的“Interface Name”为tap0。
现在,打开虚拟机,把虚拟机的ip地址设为和tap0同一个子网(可以采用图二的设定)。
设好之后,回到linux,在终端中敲入如下指令:
iptables -t nat -F POSTROUTING
iptables -t nat -A POSTROUTING -j SNAT -s 192.168.40.0/24 --to 10.10.212.170
解释:
要是不懂iptables,请先去看看它的文档,我只作概略的解释:
linux中的数据包过滤是通过Tables和Chains实现的,Linux 中包含多个Tables,它们用
来处理不同类型的数据包,每个Tables包含多个Chains,而每个Chains指定了不同情况下
对数据包的操作(如收到的数据包怎么处理,要发出的数据包怎么处理,以及要转发的数
据包怎么处理等等)。Nat是其中的一个Table,通常多用于建立ip伪装,POSTROUTING是
Nat中的一个Chain,用于指定当数据包要发出的时候的规则。
请注意:iptables的使用方法随着版本更新变化是比较大的,我的版本是:iptables
v1.4.1.1
如果你的版本和我的差别很大,你最好先自己看看它的文档。
第一条命令中:
-t nat 就是指定操作nat table
-F POSTROUTING就是命令系统把POSTROUTING Chain中的所有规则清空,这么做是为了避
免以前的规则干扰我们。
第二条命令中:
-A POSTROUTING 就是把后面指定的规则加入POSTROUTING中,而它后面跟着的就是要加入
的规则。
-j SNAT 就是指明使用的处理方式为SNAT即源NAT,而后面所跟的部分就是SNAT的一些具
体参数,用于知道系统具体怎么操作这个SNAT。
-s 192.168.40.0/24 指明了数据包来源的范围,即所有来自
192.168.40.0/255.255.255.0这个网络的数据包。192.168.40.0/24 是linux中常用的指
定网络的方式,/24代表子网掩码中从高位起有24个连续的1,换算成点分十进制就
是:255.255.255.0
--to 10.10.212.170指定了把源地址改为10.10.212.170。请注意:10.10.212.170应该是
宿主的实际网络地址,如果你用的是有线网络的话,一般来说是eth0的地址,请根据实际
情况更改。
那么:
iptables -t nat -A POSTROUTING -j SNAT -s 192.168.40.0/24 --to 10.10.212.170
的意思就是:
在发送之前,把所有来自192.168.40.0/24这个子网中的所有数据包的源地址改为
10.10.212.170
现在在虚拟机里面把默认网关设为192.168.40.1,即宿主的地址,这样一来所有Guest中
不知道往哪里发送的数据就全部给了宿主。但是要想成功的把这些数据发送出去,宿主就
必须同意这些数据被转发:
[root@sunshine ~]# echo 1 > /proc/sys/net/ipv4/ip_forward
这条命令就是告诉系统应该同意数据包的转发。
扫盲开始:简单的说转发就是,我收到了一个数据包,但是我看看它又不是我的,所以我
就按照它的目的地址,把它发出去。从系统的角度看,所有需要转发的数据包的目的MAC
地址都和本机的相同,但是目的IP地址却不是本机的,因此操作系统就可以断定,这个数
据包之所以发到我这里就是希望我把它发到它真正该去的地方--目的IP地址。如果操作
系统的策略同意转发,那么操作系统就会根据自己的路由信息把这个数据包发送了,如果
操作系统的策略不同意转发,就会丢弃这个数据包。
最后一项工作就是把Guest中的DNS设为和宿主的一样。
现在Guest就可以上网了!但是与桥不同的是,现在的Guest位于一个宿主内部的虚拟子网
中,本例中是192.168.40.0/24,这样避免了和实际网络中其他主机的IP冲突,并且实际
网络上的其他主机是不能直接访问Guest的,宿主成为了Guest的网关,这样又提供了对
Guest的保护。
也许你还是不清楚这整个过程到底是怎么回事,那么请你看看下面的图:
这个流程是数据包被从Guest发送到外网的过程,而它反过来就是数据包从外网传递到
Guest的过程。
OK,已经把最难的部分说了,“桥接”或着“NAT”已经可以实现大多数功能了,如果你觉得
你只是想要一个简单的通讯方案,那么我建议你直接使用虚拟机设定里的NAT。
三、伪NAT
为什么要说这种NAT是伪NAT呢?因为我觉得它只能提供真实NAT 中的一部分功能。
废话不说了,直接看步骤:
打开虚拟机设置,把“网络”中的“Attached to:”选为NAT,如图三:
启动虚拟机!
然后把虚拟机的设为自动获取IP地址,这样做了之后,就可以直接使用虚拟机上网了。虚
拟机位于10.0.2.0/24的子网上,而宿主作为网关,其地址是10.0.2.2。访问10.0.2.2就
意味着访问了宿主。
但是,这样的问题在于想要实现多个虚拟机互相通讯是不可能的,而且实际网络上的机器
没有任何办法访问到虚拟机。
多虚拟机相互通讯
经过了上面的一番折腾,各位看官应该对VB的网络配置有所了解了,但是仅仅知道怎样配
置虚拟机的网络以实现和宿主通讯以及上网,那么还不如直接使用上文提及的最简单方
法:伪NAT!这里要介绍的就是怎样给予NAT实现多机相互通讯。
为什么只介绍NAT方式呢?因为桥要是用来做这个比较麻烦,也比较低效,但是原理还是
一样的。在彻底弄懂NAT之后,要是有兴趣可以自己把桥的方式折腾出来。
这里的大概思想就是:
创建多块虚拟网卡;
分别连接多台虚拟机;
设好多块网卡之间的路由;
用IP伪装把所有Guest放在宿主后面;
一切在实践中讲吧:
我假定只有两台虚拟机需要相互通讯,多台的怎么弄看完就明白了。
我把两台Guest分别称为XP1和XP2。
首先,创建两个tap设备:tap0、tap1
[root@sunshine ~]# tunctl -u root -t tap0
Set 'tap0' persistent and owned by uid 0
[root@sunshine ~]# tunctl -u root -t tap1
Set 'tap1' persistent and owned by uid 0
分别配置它们:
[root@sunshine ~]#ifconfig tap0 192.168.40.1 netmask 255.255.255.0 broadcast
192.168.40.255
[root@sunshine ~]#ifconfig tap1 192.168.40.3 netmask 255.255.255.0 broadcast
192.168.40.255
把两台虚拟机分别连接到这两块网卡上,XP1对应tap0 ,XP2 对应tap1。
启动两台虚拟机,把XP1的网络设定为:
IP:192.168.40.2
NETMASK:255.255.255.0
DEFAULT GATEWAY:192.168.40.1
DNS:宿主的DNS
把XP2的网络设定为:
IP:192.168.40.4
NETMASK:255.255.255.0
DEFAULT GATEWAY:192.168.40.3
DNS:宿主的DNS
现在XP1 ping 192.168.40.1以及XP2 ping 192.168.40.3都是可以ping通的,但是XP1
ping XP2是ping不通的。因为它们两个虽然从IP上看是属于同一个子网的,但是,它们连
接着不同网卡。这两块网卡从IP 上是属于同一个子网的,这就造成了宿主路由的混乱,
到192.168.40.0/24这个子网有两条路径可以走:一条是通过tap0,另一条是通过tap1。
而实际上,通过tap0只能到达192.168.40.2这台主机;通过tap1只能到达192.168.40.4这
台主机。
现在我们需要手工纠正路由:
[root@sunshine ~]# route add -host 192.168.40.2 dev tap0
[root@sunshine ~]# route add -host 192.168.40.4 dev tap1
这两条命令告诉了系统,要想访问192.168.40.2就必须通过tap0接口;要想访问
192.168.40.4就必须通过tap1接口;
可是现在XP1还是ping不通XP2,这又是为什么呢?
因为以太网上是通过MAC地址定位机器的。
XP1向192.168.40.0/24的子网发出arp查询广播:谁的IP地址是192.168.40.4,请告诉我
你的MAC地址。
但是呢,XP1只和tap0直接相连,换言之,整个子网内收到此广播的IP只有一个那就
是:192.168.40.1,也就是网卡tap0。那么它会相应此查询么?显然不会,因为它的IP又
不是192.168.40.4。同理,XP2也的查询也不会得到相应。这样以来没有对方的MAC地址就
不可能建立连接。
现在,问题已经明了了,就是怎么让tap0以及tap1响应不是自己IP的arp查询,也就是说
无论谁问:“谁的IP是xx.xx.xx.xx,请告诉我你的MAC地址”的时候,它们都应该直接告诉
此查询的发送者,我就是你要找的人,我的MAC是多少多少。这样做就会把数据包引向自
己,然后再做进一步处理。
这个想法听上去很荒唐,但是已经有一个现实的解决方案可以用于实现这样的目的了——
arp代理。
现在,我们开启arp代理:(注意,每个接口是否开启代理都是独立的)
[root@sunshine ~]# echo 1 > /proc/sys/net/ipv4/conf/tap0/proxy_arp
[root@sunshine ~]# echo 1 > /proc/sys/net/ipv4/conf/tap1/proxy_arp
现在tap0和tap1都会以自己的MAC地址响应自己接受到的arp查询了。
那么当XP1 ping XP2的时候,XP1会先发送arp查询广播:谁有192.168.40.4的IP,请告诉
我你的MAC地址!
接到广播的tap0就会响应:我就是你要找的人,我的MAC地址是XX:XX:XX:XX:XX:XX
随后,XP1就会把目的地址为192.168.40.4的数据包发给tap0,但因为tap0的地址是
192.168.40.1这就形成了转发的条件:该数据包的目的MAC是本机,而目的IP不是本机。
而此时宿主的策略允许转发,数据包就会按照路由,即通过tap1转发到192.168.40.4。而
XP2响应此包的方式与此完全相同。这样以来就实现了双机通讯!
所以最后一步就是:
[root@sunshine ~]# echo 1 > /proc/sys/net/ipv4/ip_forward
这样,XP1就可以ping到XP2了!
怎么上外网?我最恨你们这些不专心听讲的人了!NAT啊!
[root@sunshine ~]#iptables -t nat -F POSTROUTING
[root@sunshine ~]#iptables -t nat -A POSTROUTING -j SNAT -s 192.168.40.0/24
--to 10.10.212.170
道理都讲过了,不明白的好好揣摩吧!
总结一下,用命令表达就是:
tunctl -t tap0 -u root
ifconfig tap0 192.168.40.1 netmask 255.255.255.0 broadcast 192.168.40.255
tunctl -t tap1 -u root
ifconfig tap1 192.168.40.3 netmask 255.255.255.0 broadcast 192.168.40.255
route add -host 192.168.40.2 dev tap0
route add -host 192.168.40.4 dev tap1
echo 1 > /proc/sys/net/ipv4/conf/tap0/proxy_arp
echo 1 > /proc/sys/net/ipv4/conf/tap1/proxy_arp
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -j SNAT -s 192.168.40.0/24 --to 192.168.39.100
讲完了,怎么使用桥的方式,应该也不难,道理就是这个道理了。而且多机怎么折腾也应
该明了了。网络这个东西本来就比较抽象,很多细节问题不看代码真的是很难一下子想通
的,但是只要有一些网络基本概念的了解再加上自己的实践,一切就都清晰了。末了,这
篇文章写的非常概略,要是详讲这些主题,恐怕还要千把字。tcpdump是个好东西,抓抓
包,就知道里面的道道了。
在下不才,文中多有不足,还望各位看官不吝赐教!