Chinaunix首页 | 论坛 | 博客
  • 博客访问: 34609
  • 博文数量: 6
  • 博客积分: 31
  • 博客等级: 民兵
  • 技术积分: 79
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-21 19:46
个人简介

还好!

文章分类
文章存档

2016年(1)

2015年(4)

2013年(1)

我的朋友

分类: LINUX

2015-05-18 14:35:42

通过几天的努力,终于可以通过宿机做路由,让主机上网。本来想买一台两千块的多核多网卡的cpu,安装上VMware vSphere Hyperviso虚拟机平台开启多linux系统,搭建上环境,用于构建iptables的netfilter架构,用于开发和测试,但通过几天对路由 转发原理、路由表设置、路由器原理、对宿机通过主机上网方法和内核添加nat的关注,终于搞通了环境。从我一开始的设想来看,我的思路是正确,也就是说, 从理论上是能做到的,实际也确实是做到了。

搭建的环境:主机ubuntu12.04、宿机ubuntu9.04、虚拟机vmware 9。

1 虚拟机设置:
1.1 虚拟机需要重新编译内核
将nf_nat这个选项打开,当然了,这需要内核裁剪方面的基本功,顺便多说两句,最快捷的方法是在make menuconfig 界面之下,输入"/"(斜杠)开启搜索环境变量,并输入"\",注意:引号中的部分"\<"不是乱码,是英文输入法下的反斜杠和左尖号,是搜索关键词两边的"\<"是匹配边界,如果不这么做会搜到所有包含这个内容环境变量名,会是一堆,很不清晰,如果大家熟悉vim的话,不难看出,这个vim的匹配边界风格。



搜索结果如下:



  │ Symbol: NF_NAT [=m]
大家可以看到我nat对应的环境变量已经被定义成了模块,make执行是,会察看这个环境变量的状态,并执行相应的命令,这在编译内核的makefile当中可以看到影子:
ifneq ($(KERNELRELEASE),)
    obj-m    :=xx.o
else
    PWD    :=$(shell pwd)
    KVER    :=$(shell uname -r)
    KDIR    :=/lib/modules/$(KVER)/build/
default:
    $(MAKE) -C $(KDIR) M=$(PWD)
clean:
    -rm -rf *.ko *.o .*.cmd *.mod.c modules.order \
        Module.symvers .tmp_versions

其中,本环境变量依赖结果如下:
Depends on: NET && INET && NETFILTER && IP_NF_IPTABLES && NF_CONNTRACK_IPV4
不能看出开启nat要依赖,网络,netfilter架构,iptables和链接跟踪状态

而下边则是此环境变量在meke menuconfig表单中的位置,层层找下去即可,大家可以看见,我的不过如果你没开启上边的对应环境变量的依赖,那你可能进去到对应的目录,也没有对应的选项,表现最为典型的就是链接跟踪状态,也就是NF_CONNTRACK_IPV4。重复上边搜索步骤将依赖一个个的开启。

  │   Location:                                                                                                                          │  
  │     -> Networking support (NET [=y])                                                                                                 │  
  │       -> Networking options                                                                                                          │  
  │         -> Network packet filtering framework (Netfilter) (NETFILTER [=y])                                                           │  
  │           -> IP: Netfilter Configuration                                                                                             │  
  │             -> IP tables support (required for filtering/masq/NAT) (IP_NF_IPTABLES [=m])


这一行则是该环境变量对一个提示,也就是说,这个环境变量
  │ Prompt: Full NAT



大家可以看到,我所有nat选项,均以开启,在这其中,我们也不难看出个别选项的内容,比如MASQUERADE用与隐藏内网地址的伪装。

所有准备都做了好,编译便是。顺便再多说一句,我们可以设置一下,自己内核的名字,不如我的:
$ uname -r
2.6.30-kdb-kgdb-nat
这是因为我编译的内核开启了kdb、kgdb和nat的三个内容进去。
设置自己内核名字的方法:

  │ Symbol: LOCALVERSION [=-kdb-kgdb-nat]                                                                                                │  
  │ Prompt: Local version - append to kernel release                                                                                     │  
  │   Defined at init/Kconfig:74                                                                                                         │  
  │   Location:                                                                                                                          │  
  │     -> General setup 


可以看见我的新内核名字在括号里了,改变只需要回车,进入一个对话框,填入的是全名。

如果ubuntu9.04或是其他版本的ubunt未能找到匹配的包,网络下载和iso镜像安装都行不通的情况下,请尝试下边这个网站,我也是无意中发现了这个新大陆,网站的介绍也不难看出,这个ubuntu的官方合作平台,能找到所有历史ubuntu有效版本的对应包,到目前为止,我ubuntu9.04上缺的,已经失效过期的包,都在这个网站上搜到了,只有一个例外,那就是语言包。方法很简单,在搜索框里输入要搜索的ubuntu版本和对应的包名,再在一堆搜到的列表里找到是安装包的链接,下载便是。这会解决,在编译时,找不到make menuconfig这个界面所依赖的库的问题。请不要谢我,请叫我雷锋。


1.2 编译内核、安装内核,略,这里分个小节说明,是为了强调是重要的一个步骤。
这一部分推荐给大家一本书,linux内核api完全参考手册,一共10章内容,其中有一张是内核的预备知识,里边介绍了编译和安装内核的方法,介绍的工具和方法都是比较全的,只不过在升级引导菜单时,我用的不是手动的方法,用的sudo update-grub,当提示是否安装是,记得别选默认的保持当前,选默认的上一个选项,安装新内核。

1.3 虚拟机网卡设置:




虚拟机增加两块网卡:
第一个网卡是桥接到主机物理网卡,而且是自动桥模式,意思就是说,会自动链接到主机能上网的网卡上,以保障虚拟机是畅通的;
因为我用的是笔记本,拥有两块真实物理网卡,先前我觉得,只单单通过硬件的无线上网,是完成不了主机通过虚拟机做路由上网的,不过经过尝试之后,我觉得我过于担心了,或者说,哪怕是不行,我也要尝试一下,到底行不行的,想验证他的思路还是值得尝试的,试过了才知道,是有意外惊喜的。是可以完成的,也就是说,从理论上讲,是可以通过的。实践出真知。
如果大家对vmware是如何设置自动链接到系统的两块网卡当中的一个的感兴趣,那么可以打开虚拟机编辑下有个网络设置一看究竟。

第二个网卡设置成主机私有地址上网。
设置后的网卡情况:

宿机虚拟机ip地址情况:

$ ip addr
1: lo: mtu 16436 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
    link/ether 00:0c:29:df:76:d3 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.104/24 brd 192.168.0.255 scope global eth0
    inet6 fe80::20c:29ff:fedf:76d3/64 scope link
       valid_lft forever preferred_lft forever
3: eth1: mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
    link/ether 00:0c:29:df:76:dd brd ff:ff:ff:ff:ff:ff
    inet 172.16.149.129/24 brd 172.16.149.255 scope global eth1
    inet6 fe80::20c:29ff:fedf:76dd/64 scope link
       valid_lft forever preferred_lft forever
4: pan0: mtu 1500 qdisc noop state DOWN
    link/ether 06:1a:7d:f9:4d:37 brd ff:ff:ff:ff:ff:ff

其中eth0 192.168.0.104/24 是无线上网,和无线路由一个网段的地址,直接上网用。
eth1 172.16.149.129/24 是私有地址,作为私有地址,与主机互联。

虚拟机宿机系统路由表:
$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.0.0     0.0.0.0         255.255.255.0   U     1      0        0 eth0
172.16.149.0    0.0.0.0         255.255.255.0   U     1      0        0 eth1
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 eth1
0.0.0.0         192.168.0.1     0.0.0.0         UG    0      0        0 eth0


1.4 虚拟机nat命令设置
编译好内核,设置虚拟机的nat
执行命令:
$ sudo iptables -t nat -A POSTROUTING -s 172.16.149.0/24 -o eth0 -j SNAT --to 192.168.0.104
这条命令的意思是,在POSTROUTING挂载点,在家一个nat,将我虚拟机内私有的ip网段172.16.149.0/24地址 做源地址nat, nat成 我本机的对外的上网的地址192.168.0.104,并从192.168.0.104所在网段的网口发送出去。

这样,我主机地址inet 172.16.149.1/24就可以通过虚拟机做snat上网,当然,在没做这个设置之前,我的虚拟机是通过主机上网的。所以网段是inet 172.16.149.1/24。

执行一下之后,察看一下,察看一下,是否设置成功且正确:
$ sudo iptables -t nat -vL -n
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain POSTROUTING (policy ACCEPT 1 packets, 71 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 SNAT       all  --  *      eth0    172.16.149.0/24      0.0.0.0/0           to:192.168.0.104

Chain OUTPUT (policy ACCEPT 1 packets, 71 bytes)
 pkts bytes target     prot opt in     out     source               destination         

可以看见在挂载点postrouting增加了一个snat,source源地址是172.16.149.0/24,out出口设备是eth0。




1.5 开启主机转发功能
这个功能一定要开启,如果不开启是无法完成上网的。
其中echo 1到配置文件,是使当前环境转发有效,回头一会我们还会用这个知识点来验证我们的路由确实是完全通过虚拟机上网的。
sudo echo 1 > /proc/sys/net/ipv4/ip_forward
这个配置,是让系统运行起来,就默认执行开启路由转发功能
sudo vi /etc/sysctl.conf
  取消 # net.ipv4.ip_forward = 1 的注释,保存退出

到此虚拟机环境设置完毕。

2 主机环境配置
主机只需要改变下路由,由原来的192.168.0.1及对应的出口网卡设备,改为从虚拟机的私有ip地址及网卡设备上网

重点和难点都在虚拟机当中,当并未设置虚拟机上网时,主机的路由表如下:
$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1  0.0.0.0         UG    0      0        0 wlan0
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 wlan0
172.16.149.0    0.0.0.0         255.255.255.0   U     0      0        0 vmnet1
172.16.159.0    0.0.0.0         255.255.255.0   U     0      0        0 vmnet8
192.168.0.0     0.0.0.0         255.255.255.0   U     2      0        0 wlan0

第一条就是默认路由,意思是说,当下边这四行Destination 目的地址不是我169.254.0.0、172.16.149.0 、172.16.159.0、192.168.0.0 这个四个网段是,走一个默认路由,那就是由192.168.0.1 这个ip做默认路由,由网口wlan0发送出去,ip地址和网卡都指定是有意义的,例如我的笔记本在无线和有线的网线都用上时,就会可能会出现网关ip相同,但面临选择一个不同的网卡接口做出口的情形,而在很多时候,可能有线和无线都启用时,系统会更倾向将有线作为上网的默认网关,先前我搭建了一个台式机通过有线接入笔记本,再由笔记本无线上网时,便出现了一个,笔记本默认反过来将我的台式机当了网关上网,没文化真可怕啊,真是让人哭笑不得。

而下边这四条,在一个包到达系统时,会在默认网关之前先被检查,如果是这个包当中的一个目的地址,就将包从对应的网卡设备发送出去。
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 wlan0
172.16.149.0    0.0.0.0         255.255.255.0   U     0      0        0 vmnet1
172.16.159.0    0.0.0.0         255.255.255.0   U     0      0        0 vmnet8
192.168.0.0     0.0.0.0         255.255.255.0   U     2      0        0 wlan0

我们在我们的主机ubuntu12.04上做如下配置
删除默认路由:
sudo route del default

看下路由表,默认网关已经被删掉了
$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 wlan0
172.16.149.0    0.0.0.0         255.255.255.0   U     0      0        0 vmnet1
172.16.159.0    0.0.0.0         255.255.255.0   U     0      0        0 vmnet8
192.168.0.0     0.0.0.0         255.255.255.0   U     2      0        0 wlan0

检查下网络是不是真的不通了:
$ ping hao123.com
connect: Network is unreachable
ping不通

增加指向虚拟机的默认网关:
$ sudo route add default gw 172.16.149.129 dev vmnet1

这条命令需要大家,察看自己虚拟机内宿机对应的私有地址为何,并且找到主机与之对应的网卡接口是哪个
不难看出,主机路由是这条:
172.16.149.0    0.0.0.0         255.255.255.0   U     0      0        0 vmnet1

宿机内系统对应的ip地址是这个:
eth1: mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
    link/ether 00:0c:29:df:76:dd brd ff:ff:ff:ff:ff:ff
    inet 172.16.149.129/24 brd 172.16.149.255 scope global eth1
    inet6 fe80::20c:29ff:fedf:76dd/64 scope link
       valid_lft forever preferred_lft forever
大家如果想尝试,自己根据自己的情况搞。

不过大家也许也发现了,这样一来,有个怪异的现象就产生了,172.16.149.1将172.16.149.129作为了默认路由,颠覆了我们平时的网关的概念,.1反过来通过其他的.x的ip上网,这不是倒反天罡么,不过其实这就是地址指定的问题,就如张三李四的名号一样,本质没什么特别的,不带来任何不良的影响,继续用便是。

察看路由:

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.16.149.129  0.0.0.0         UG    0      0        0 vmnet1
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 wlan0
172.16.149.0    0.0.0.0         255.255.255.0   U     0      0        0 vmnet1
172.16.159.0    0.0.0.0         255.255.255.0   U     0      0        0 vmnet8
192.168.0.0     0.0.0.0         255.255.255.0   U     2      0        0 wlan0

增加第一条,默认网关,指到了虚拟机。

3.验证路由:
3.1 失败案例:
这样我的环境就搭成了,现在我们来验证,我们的路由,真的是从我们虚拟机当中宿机上的网,并且从我们虚拟机宿机回来的包。
我这么描述是有典故的,我先前将虚拟机和主机设置成了同一网段,都让他们和无线路由器的ip网段192.168.0.1在同一个网段,而让主机的默认路由地址指向虚拟机地址,这么做确实能让数据包上行从虚拟机出去,但是下行却未经过虚拟机,而是从物理接口,直接回到了主机上,绕过了宿机,因为无线网关和主机乃至宿机,都在同一网段,无线设备直接将回包回给了主机,绕过了宿机。

通过抓包就能看出来,当我们在宿机上对eth0和eth1进行tcpdump抓包时,我们只能看到上行流量,看不见下行流量。

3.2 验证过程:
现在我们验证我们我上下行流量均通过宿机上网:

3.2.1 主机ubuntu12.04执行ping命令:
$ ping hao123.com
PING hao123.com (123.125.114.224) 56(84) bytes of data.
64 bytes from 123.125.114.224: icmp_req=1 ttl=55 time=4.21 ms

然后主机不要动,让他继续ping

3.2.2 我们在宿机上做操作:

3.2.2.1 宿机对私有ip地址网卡接口抓包:

 sudo tcpdump -i eth1 icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes
14:11:21.627820 IP 172.16.149.1 > 123.125.114.224: ICMP echo request, id 5803, seq 41, length 64
14:11:21.633852 IP 123.125.114.224 > 172.16.149.1: ICMP echo reply, id 5803, seq 41, length 64
14:11:21.640808 IP 172.16.149.1 > 192.168.1.1: ICMP 172.16.149.1 udp port 9570 unreachable, length 136
14:11:26.640770 IP 172.16.149.1 > 123.125.114.224: ICMP echo request, id 5803, seq 42, length 64
14:11:26.644774 IP 123.125.114.224 > 172.16.149.1: ICMP echo reply, id 5803, seq 42, length 64

其中tcpdump 命令中-i 是interface接口,icmp是ping命令使用的icmp协议,-nn是让tcpdump抓包迅速出效果,不进行dns地址转换等,就是执行命令就能看见有包,如果有包的话,而不是等待一会才有包。

3.2.2.2 再抓宿机上网接口:

$ sudo tcpdump -i eth0 icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
14:15:38.584486 IP 192.168.0.104 > 123.125.114.224: ICMP echo request, id 5803, seq 93, length 64
14:15:38.587805 IP 123.125.114.224 > 192.168.0.104: ICMP echo reply, id 5803, seq 93, length 64
14:15:43.602075 IP 192.168.0.104 > 123.125.114.224: ICMP echo request, id 5803, seq 94, length 64
14:15:43.610451 IP 123.125.114.224 > 192.168.0.104: ICMP echo reply, id 5803, seq 94, length 64

也有上行和下行。

3.3 利用宿机转发开启和关闭做个阻断,然主机不能通过宿机上网
切换到超级用户权限下,如果不切换,执行命令会出现没有权限的提示,加sudo也不行

$ sudo -i
关闭转发:
# echo 0 > /proc/sys/net/ipv4/ip_forward
这时候我们观察我们的主机一直在执行的ping命令,很显然,已经不没效果了,看不见有新效果了。

我们在宿机上对私有地址网卡抓包:

$ sudo tcpdump -i eth1 icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes
14:20:42.727803 IP 172.16.149.1 > 123.125.114.224: ICMP echo request, id 5803, seq 280, length 64
14:20:43.727962 IP 172.16.149.1 > 123.125.114.224: ICMP echo request, id 5803, seq 281, length 64
14:20:44.727805 IP 172.16.149.1 > 123.125.114.224: ICMP echo request, id 5803, seq 282, length 64
14:20:45.727789 IP 172.16.149.1 > 123.125.114.224: ICMP echo request, id 5803, seq 283, length 64
14:20:46.727803 IP 172.16.149.1 > 123.125.114.224: ICMP echo request, id 5803, seq 284, length 64

光有上行,么的下行了。

对宿机上网接口抓包,干脆没有包:

$ sudo tcpdump -i eth0 icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

3.4 宿机重新开启转发规则:
依然是超级用户模式下
# echo 1 > /proc/sys/net/ipv4/ip_forward

最直观的反应就是主机上看ping命令效果:



看下边第122个请求和420个之间,缺少的数量,就是刚才我们关闭转发规则期间,少的包数。
唯一瑕疵的地方,就是我习惯性的用QQ解图快捷键ctrl+alt+a截图,在第403个请求上多几个小乱码,有点对不起观众了,不过大家都懂得,除了是一个码农之外,俺还是个不折不扣的都比吊死+社交控。

到此为止,利用vmware内宿机做路由nat转发,让主机上网的内容,叙述完毕,希望此文能给做此方面工作的,一点借鉴和一些可行性的指导。
社交控里有一句话,我觉得挺适合程序员的分享的原则和思想,“如果聊天不是为了装逼,那将毫无意义。”

还有一点就是,说给看的人,也说给自己,不管别人说的东西好不好,正确与否,都应该花几十秒钟考虑一下,也许他的理论并不能指导你的行动,但他的思路可能会给你一个前进的方向。

主机通过虚拟机内宿机上网,到此结束,谢谢收看,我们下次再见。

参考文章:
iptables的nat设置:

ubuntu做路由表配置:
http://www.cnblogs.com/arrongao/p/4280522.html
阅读(1870) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~