用Iproute2进行隧道配置
Simone Piunno 著 wqch 翻译
1、Iproute2简介
Iproute2是一个在Linux下的高级网络管理工具软件。实际上,它是通过rtnetlink sockets方式动态配置内核的一些小工具组成的,从Linux2.2内核开始,Alexey Kuznetsov 实现了通过rtnetlink sockets用来配置网络协议栈,它是一个现代的强大的接口。
Iproute2最吸引人的特色就是它用完整而有机制的简单命令替代了之前以下命令的功能,如ifconfig,arp,route,iptunnel,而且还添加了其它不少的功能。
如今,Iproute2已经在很多主要的发行版里被默认安装,即使他们的初始化脚本命令被嵌入在一些老的网络工具包里,如ifconfig,以及少用的iptunnel。如果你的发行版没有包含这个重要的工具包,你也可以从网上下载并自己编译。下载地址ftp://ftp.inr.ac.ru/ip-routing/。
在写这篇文章的时候,Iproute2最大的缺点就是缺乏相关的说明文档,然而,它的IP命令语法的简单而且还很象英语,这在很大程度上对说明文档做了相应的补偿。相信习惯于ifconfig和route命令的人不愿意在用ip的时候遇到任何困难,而且他们会觉得自己象在家里用一样。本文假设读者已经有一定的网络基础,并且已经在Linux中用过ifconfig和route命令。
2、隧道简介
我们想像一下在二个网络节点间要进行数据通信的问题,如果这二个节点在不同于IPv4的协议上传输,或者用非全球可用IP地址直接传送到一个私有局域网LAN中。为了解决这类问题,典型的是在两个节点间用虚拟的点对点连接,我们称之为隧道结构。
你可以把在网络上传输的每个数据包想像为一个信封,它里面有几个字节,外面还写上了发送者和接收者的地址。隧道把这个信封简单的隐藏在另一个不同的发送者和接收者的信封里面,有效的进行数据包的传送。当数据包到达外部接收者(写在最外面的信封上的接收者)的时候,就把这个外部信封去掉并扔了,所以这个时候的信封(数据包)可以继续被传送到它的真正目的地。
封装和拆分附加信封的节点被叫做端点,它们需要一个全球可用的IPv4地址。这就是为什么当经过网络地址转换(NAT)时隧道就不能用了。而且,如果构建一个通过防火墙的隧道,那么防火墙就必须特殊配置来允许隧道传输。
一个典型的隧道应用就是通过一个纯IPv4网络来连接二个IPv6节点。这二个节点可以通过构建一个伪装的点到点IPv6连接的IPv6-in-IPv4隧道,这种方式可以把二个IPv6岛连接在一起(6bone就是通过这样一种网络隧道的方式工作的)。IPv6-over-IPv4传输隧道有二种方式:自动配置(详见RFC2373)和人工配置,本文将讨论的是后者——人工配置。
3、创建隧道
用Iproute2构建隧道是很容易的事情。首先,你需要为隧道命一个名,如果你把隧道名字选择用foo,此时,你可以用如下命令来建立SIT模式的隧道:
ip tunnel add foo mode sit remote 192.168.1.42
以这种方式,将IPv4地址为192.168.1.42的远程端点也建一个SIT模式的IPv6-in-IPv4隧道。注意,我们还没有指定那个IP地址来用这个本地隧道,以及那个接口等等,这些结果可以通过命令ip tunnel show 来看到:
[root@abulafia root]# ip tunnel show
sit0: ipv6/ip remote any local any ttl 64 nopmtudisc
foo: ipv6/ip remote 192.168.1.42 local any ttl inherit
我们创建的隧道在第二行。现在需要看看所有可用接口的列表,不管他们是真正的网络适配器还是模拟的:
[root@abulafia root]# ip link show
1: lo:
mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:
mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:48:54:1b:25:30 brd ff:ff:ff:ff:ff:ff
4: sit0@none:
mtu 1480 qdisc noop
link/sit 0.0.0.0 brd 0.0.0.0
6: foo@none:
mtu 1480 qdisc noop
link/sit 0.0.0.0 peer 192.168.1.42
事实上需要注意的是lo和eth0是被标志为up,说明是已经激活,而隧道没有。为了再次检查,用ifconfig查看:
[root@abulafia root]# ifconfig
eth0 link encap:ethernet hwaddr 00:48:54:1b:25:30
inet addr:192.168.0.1 bcast:192.168.0.255 mask:255.255.255.0
inet6 addr: fe80::248:54ff:fe1b:2530/10 scope:link
up broadcast running multicast mtu:1500 metric:1
rx packets:0 errors:0 dropped:0 overruns:0 frame:0
tx packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
rx bytes:0 (0.0 b) tx bytes:528 (528.0 b)
interrupt:9 base address:0x5000
lo link encap:local loopback
inet addr:127.0.0.1 mask:255.0.0.0
inet6 addr: ::1/128 scope:host
up loopback running mtu:16436 metric:1
rx packets:35402 errors:0 dropped:0 overruns:0 frame:0
tx packets:35402 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
rx bytes:3433996 (3.2 mb) tx bytes:3433996 (3.2 mb)
显然,不显示没有被激活的接口,所以必须记住,ip连接命令显示所有可用接口,不管他们是否被激活。为了激活foo,用命令:
ip link set foo up
解除它用:
ip link set foo down
彻底的删掉隧道用:
ip tunnel del foo
4、特殊隧道
在之前的段落中,介绍了怎样构建一个IPv6-in-IPv4隧道,现在来看看几个不同的情况。
4.1、GRE隧道
如果你不需要IPv6,但是假如你想通过一个不同协议的传输网络来传递普通IPv4数据包,那么最好用GRE模式代替SIT模式。例如:
[root@abulafia root]# ip tunnel add foo4 mode gre remote 192.168.1.42
[root@abulafia root]# ip tunnel show
gre0: gre/ip remote any local any ttl inherit nopmtudisc
foo4: gre/ip remote 192.168.1.42 local any ttl inherit
[root@abulafia root]# ip link show
1: lo:
mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:
mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:48:54:1b:25:30 brd ff:ff:ff:ff:ff:ff
7: gre0@none:
mtu 1476 qdisc noop
link/gre 0.0.0.0 brd 0.0.0.0
9: foo4@none:
mtu 1476 qdisc noop
link/gre 0.0.0.0 peer 192.168.1.42
GRE模式是一个特殊的隧道传输,它被Cisco路由器支持,Cisco路由器可以在IPv4上实现不同协议之间数据包的传送。还有另外一种被Linux实现的隧道:ipip,它同样可以实现IPv4-in-IPv4的封装,但是它只被Linux实现,而且只有单播的IP over IP(所以你不能传送IPX或者广播数据包)。总的来说,GRE模式更好。
4.2 直接本地端点
即使内核很聪明的可以为你做隧道选择,最好还是很清楚的说明将要用做隧道的IPv4地址和接口标志。用命令带本地和设备参数来实现:
[root@abulafia root]# ip tunnel add foo mode sit local 192.168.0.1 remote 192.168.1.42 dev eth0
[root@abulafia root]# ip tunnel show
sit0: ipv6/ip remote any local any ttl 64 nopmtudisc
foo: ipv6/ip remote 192.168.1.42 local 192.168.0.1 dev eth0 ttl inherit
[root@abulafia root]# ip link show
1: lo:
mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:
mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:48:54:1b:25:30 brd ff:ff:ff:ff:ff:ff
4: sit0@none:
mtu 1480 qdisc noop
link/sit 0.0.0.0 brd 0.0.0.0
11: foo@eth0:
mtu 1480 qdisc noop
link/sit 192.168.0.1 peer 192.168.1.42
Please notice that now the interface is labeled as foo@eth0, to remind us where the tunnel has been explicitly connected.
请注意接口被标志为foo@eth0,这样可更清楚的提示我们隧道在哪儿被连接。
4.3 生存时间
使用隧道时,建一个附加的环回网络接口是很容易的。为了限制这个问题,基本原则是生成一个较底TTL值的数据包。初始的TTL可以用命令ip tunnel add来指定TTL值,默认值是从关联的隧道网络接口继承下来的。IANA建议TTL的值是64。
5、为接口指派IP地址
像其它网络接口一样,隧道也可以被指派一个或者多个地址。
5.1、主地址
直接指派主地址:
ip addr add 3ffe:9001:210:3::42/64 dev foo
ip addr add 192.168.0.2/24 dev foo4
ip addr add 10.20.30.40/8 dev eth0
The number immediately following the slash is to suggest to the kernel the network prefix we prefer, useful to automatically compute broadcast address and netmask on IPv4 LANs (this is called CIDR notation). However, tunnels are point-to-point interfaces and this number is then ignored.
紧跟在地址后面的数字是网络前缀,用来在IPv4局域网中自动计算广播地址和网络掩码(叫做CIDR)。然而,隧道是点到点接口,这些数字是被忽略的。
注意:为了给接口指派一个IP地址,首先你需要用ip link 来把该接口激活。
要从一个接口去掉一个地址,可以直接用del代替之前添加地址所用的add:
ip addr del 3ffe:9001:210:3::42/64 dev foo
ip addr del 192.168.0.2/24 dev foo4
列出在自己服务器上可用的IP地址:
[root@abulafia root]# ip addr show
1: lo:
mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 brd 127.255.255.255 scope host lo
inet6 ::1/128 scope host
2: eth0:
mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:48:54:1b:25:30 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/24 brd 192.168.0.255 scope global eth0
inet6 fe80::248:54ff:fe1b:2530/10 scope link
4: sit0@NONE:
mtu 1480 qdisc noop
link/sit 0.0.0.0 brd 0.0.0.0
5: foo@NONE:
mtu 1480 qdisc noop
link/sit 0.0.0.0 peer 192.168.1.42
inet6 3ffe:9001:210:3::42/64 scope global
inet6 fe80::c0a8:1/10 scope link
5.2、别名
当在一个接口上用多个地址时,习惯于用ifconfig的人会对ip addr add命令在添加多个IP后,却不产生像eth0:1,eth0:2等这样的虚拟接口而吃惊。这个是从2.0内核继承下来的一个命名机制,直到今天也没有改变。例如:
[root@abulafia root]# ip addr add 192.168.0.11/24 dev eth0
[root@abulafia root]# ip addr show eth0
2: eth0:
mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:48:54:1b:25:30 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/24 brd 192.168.0.255 scope global eth0
inet 192.168.0.11/24 scope global secondary eth0
inet6 fe80::248:54ff:fe1b:2530/10 scope link
[root@abulafia root]# ifconfig
eth0 Link encap:Ethernet HWaddr 00:48:54:1B:25:30
inet addr:192.168.0.1 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: fe80::248:54ff:fe1b:2530/10 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
RX bytes:0 (0.0 b) TX bytes:528 (528.0 b)
Interrupt:9 Base address:0x5000
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:34732 errors:0 dropped:0 overruns:0 frame:0
TX packets:34732 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:3386912 (3.2 Mb) TX bytes:3386912 (3.2 Mb)
foo Link encap:IPv6-in-IPv4
inet6 addr: 3ffe:9001:210:3::42/64 Scope:Global
inet6 addr: fe80::c0a8:1/10 Scope:Link
UP POINTOPOINT RUNNING NOARP MTU:1480 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:0
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)
添加的IP地址已经工作,并用ip addr show可以显示,但是ifconfig甚至看不见它的存在。为了解决这个问题,加上一个标签参数:
[root@abulafia root]# ip addr add 192.168.0.11/24 label eth0:1 dev eth0
[root@abulafia root]# ip addr show eth0
2: eth0:
mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:48:54:1b:25:30 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/24 brd 192.168.0.255 scope global eth0
inet 192.168.0.11/24 scope global secondary eth0:1
inet6 fe80::248:54ff:fe1b:2530/10 scope link
[root@abulafia root]# ifconfig
eth0 Link encap:Ethernet HWaddr 00:48:54:1B:25:30
inet addr:192.168.0.1 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: fe80::248:54ff:fe1b:2530/10 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
RX bytes:0 (0.0 b) TX bytes:528 (528.0 b)
Interrupt:9 Base address:0x5000
eth0:1 Link encap:Ethernet HWaddr 00:48:54:1B:25:30
inet addr:192.168.0.11 Bcast:0.0.0.0 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
Interrupt:9 Base address:0x5000
注意:我们可以选择任意的字符串作接口标签,没有强迫使用2.0内核的命名机制;如果要用ifconfig实现向后兼容,就必须依照此命名机制。
5.3、隧道IP地址
当我们的计算机是一个独立主机或者不是一个提供到整个局域网IPv6连接的路由器时,为本地端节点隧道选择一个全球/公用IP地址是最好的事,分别是一个SIT/IPv6-in-IPv4隧道的IPv6地址和一个GRE/IPv4-in-IPv4隧道的IPv4地址。
取而代之,若配置一个路由器,最好让SIT/IPv6-in-IPv4隧道用一个链路本地地址(IPv6的链路本地地址可以通过无状态地址自动配置或者人工配置)和GRE/IPv4-in-IPv4隧道用一个私有地址(IPv4没有链路本地地址)。有效的地址只能用在eth0上(或者在局域网那边的接口上)。注意在配置的时候需要激活转发接口,用命令:
sysctl -w net.ipv4.conf.all.forwarding=1 # for GRE (IPv4-in-IPv4)
sysctl -w net.ipv6.conf.all.forwarding=1 # for SIT (IPv6-in-IPv4)
甚至可以决定打开在一对间转发,这种情况下,用如下命令:
sysctl -w net.ipv6.conf.eth0.forwarding=1
sysctl -w net.ipv6.conf.pippo.forwarding=1
6、路由
既然隧道已经被配置好了,现在需要指定哪些数据运输将被定向通过它。IPv6的一般命令格式如下:
ip route add 2000::/3 dev foo
这样,在目的地址前3位是001的地址(即全球IPv6单播地址空间)将被定向到foo接口,这只是IPv6地址空间的八分之一,但要保证所有可能的远程主机都是在这个范围内。
在IPv4路由表中:
[root@abulafia root]# ip route
192.168.0.0/24 dev eth0 scope link
127.0.0.0/8 dev lo scope link
而IPv6路由表:
[root@abulafia root]# ip -6 route
2000::/3 dev foo proto kernel metric 256 mtu 1480 advmss 1420
fe80::/10 dev eth0 proto kernel metric 256 mtu 1500 advmss 1440
fe80::/10 dev foo proto kernel metric 256 mtu 1480 advmss 1420
ff00::/8 dev eth0 proto kernel metric 256 mtu 1500 advmss 1440
ff00::/8 dev foo proto kernel metric 256 mtu 1480 advmss 1420
default dev eth0 proto kernel metric 256 mtu 1500 advmss 1440
unreachable default dev lo metric -1 error -101
如果想指定一个网关(不是给隧道的),可以用via参数,例如:
ip route add 192.168.1.0/24 via 192.168.0.254 dev eth0
去掉路由可以用ip route del命令,但是要小心的是如果这样写ip route del default将移除默认的IPv4路由,并不是IPv6的路由。要移除IPv6默认的目的地址需要用命令ip -6 route del default。
7、一个完整的例子
一个典型的6bone的IPv6隧道:
ip tunnel add $TUNNEL mode sit local any remote $V4_REMOTEADDR ttl 64
ip link set $TUNNEL up
ip addr add $V6_LOCALADDR dev $TUNNEL
ip route add 2000::/3 dev $TUNNEL
$TUNNEL是一个任意的指定隧道的名称,$V4_REMOTEADDR是隧道另一端的IPv4地址,$V6_LOCALADDR是分配给本地主机的IPv6本地地址。为本地端节点地址用一个任意值,因为这样可以处理一个动态IPv4地址(如通过拨号连接到ISP)。显然,当我们的地址改变时,需要通知隧道代理,但是这个已经不是本文所设计的范围,而且也没有一个标准的处理程序。
关闭隧道:
ip tunnel del $TUNNEL
也可以自动的移除路由接口和地址。
原文:
http://members.ferrara.linux.it/pioppo/howto/iproute2tunnel-en.html