共计 6362 个字符,预计需要花费 16 分钟才能阅读完成。
日常工作环境中,我们习惯于使用
ping
去测试网络的连通性。如果ping
不通,我们往往会去怀疑是不是路由配置错了。
路由是什么
我们知道,IP
地址是网络世界里的门牌号。你可以通过 IP
地址访问远在天边的网站, 那么数据是如何到达网站的呢?靠的就是路径上每个节点的路由。
路由,简单的说就是指导 IP 报文该去哪的指示牌。
一般说来,主机会在以下两个时机进行路由查询
- 收到报文时,查询路由决定是上送本机(LOCAL IN),或者从哪个出接口转发(FORWARD)
- 本机发送报文时,查询报文出接口
注意,转发需要开启 net/ipv4/ip_forward
路由表长什么样
以一个典型的主机为例,tristan
有一个外部网卡 eth0
和一个内部还回网卡lo
[root@tristan]# ifconfig
eth0 Link encap:Ethernet HWaddr 00:80:C8:F8:4A:51
inet addr:192.168.99.35 Bcast:192.168.99.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:27849718 errors:1 dropped:0 overruns:0 frame:0
TX packets:29968044 errors:5 dropped:0 overruns:2 carrier:3
collisions:0 txqueuelen:100
RX bytes:943447653 (899.7 Mb) TX bytes:2599122310 (2478.7 Mb)
Interrupt:9 Base address:0x1000
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:7028982 errors:0 dropped:0 overruns:0 frame:0
TX packets:7028982 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1206918001 (1151.0 Mb) TX bytes:1206918001 (1151.0 Mb)
[root@tristan]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.99.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo
0.0.0.0 192.168.99.254 0.0.0.0 UG 0 0 0 eth0
通过 route -n
我们可以看到主机上简要的路由表信息 (当然通过ip route
也可以),那么上面的路由信息中的每一表项代表什么意思呢?
- 如果报文的目的 IP 地址在
192.168.99.0/24
这个网段,那么它应该从eth0
进行转发。 - 如果报文的目的 IP 地址在
127.0.0.1/8
这个网段,那么它应该从lo
进行转发。 - 其他情况下 (0.0.0.0/0),报文从
eth0
转发,下一跳 IP 地址是192.168.99.254
第 1,2 条应该都很好理解,比较难以理解的是第 3 条,为什么它的 Gateway
不是全0.0.0.0
, 这需要从网络拓扑说起
Gateway
上图左边是一幅典型的网络拓扑。主机之间通过交换机组成一个小型局域网,再通过一个路由器连接到更大的网络。
图中的路由器对于局域网内的主机来说,即可称为 Gateway
,一般翻译为 网关
。他的地位如同一个国家的 海关
, 出了 海关
,IP
报文就在另一个网络之中了。
回到前面的路由表的第 3 条,当主机发现报文的目的 IP 地址不满足其他表项时,就表示这个报文需要送到局域网外部,所以它会将报文发送或者转发给网关,也就是192.168.99.254
。这种情况下,
192.168.99.254
被称为 默认网关
,这条表项也称为 默认路由
。与这个名字对应的,第 1,2 条路由表项也被称为 网段路由
局域网内部通信
当我们为网卡配置 IP
地址时或者由 DHCP
服务器分配 IP
地址,Linux
内核会根据 IP
地址和掩码 自动 生成对应的网段路由。这相当于告诉系统,这个网段的主机在同一个局域网内部
举个例子,假设我们在 tristan
主机去 ping
局域网内的 susan
主机。那么这个 ICMP request
报文向下面这样组装就 OK 了。
如果 tristan
还不知道 susan
主机上 eth0
的MAC
地址,那么它首先要发送 ARP
广播报文去获得。
局域网外通信
与局域网外的主机通信就需要通过 (via
) 网关了
相似的例子,假设我们在 tristan
主机去 ping
局域网外的 paul
主机的 eth0
, 在查询路由后,内核选择默认网关,那么组装的ICMP request
的报文头将是下面这样:
设置静态路由
网段路由
有时,系统生成的默认路由并不能满足我们的要求,我们需要手动设置路由
[root@tristan]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.99.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo
0.0.0.0 192.168.99.254 0.0.0.0 UG 0 0 0 eth0
比如刚才的拓扑,tristan
主机要与 jerry
主机所在的网络通信,显然根据现有的路由表,显然是不行的。因此我们需要在 tristan
主机上配置额外的路由表项:
[root@tristan]# route add -net 192.168.98.0 netmask 255.255.255.0 gw 192.168.99.1
[root@tristan]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.99.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.98.0 192.168.99.1 255.255.255.0 UG 0 0 0 eth0
127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo
0.0.0.0 192.168.99.254 0.0.0.0 UG 0 0 0 eth0
新添加的路由表项表示本机与 192.168.98.0/24
范围内的主机主机通信,需要途径网关192.168.99.1
使用 ip route
命令也可以达到相同的效果, 通过 via
设置下一跳网关,本质上没有区别
[root@tristan]# ip route add 192.168.98.0/24 via 192.168.99.1
我们甚至可以添加或者删除默认路由
[root@tristan]# route del default gw 192.168.99.254
[root@tristan]# route add default gw 192.168.99.1
主机路由
除了设置整个网段,我们还可以仅为某个目的 IP 设置路由, 即 主机路由
,主机路由实际上这是掩码为255.255.255.255
的网段路由的特殊形式
[root@tristan]# route add -host 192.168.98.42 gw 192.168.99.1
或者
[root@tristan]# route add -net 192.168.98.42 netmask 255.255.255.255 gw 192.168.99.1
都可以生成一条特定主机的路由,Flags
字段中的 H
表示Host
[root@tristan]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.99.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.98.42 192.168.99.1 255.255.255.255 UGH 0 0 0 eth0
127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo
0.0.0.0 192.168.99.254 0.0.0.0 UG 0 0 0 eth0
等价路由
等价路由 (ECMP
) 是指存在多条链路到达同一目的,链路之间可以起到负载均衡和链路备份的目的,比如下面的拓扑:
在主机 melo
设置的路由如下
[root@melo]# route show scope global
default via 203.0.113.5 dev eth0
192.0.2.0/25
nexthop via 203.0.113.7 dev eth1 weight 1
nexthop via 203.0.113.9 dev eth2 weight 1
表示如果报文目的地址是 192.0.2.0/25
,那么它将等可能地选择out1
或者 out2
作为出接口。
策略路由
多张路由表
上面提到的所有路由选择都是根据目的 IP 找到对应的路由表项进行的。除此之外,Linux
还提供了一种更高级灵活的方式可以完成更加复杂的路由选择策略,这就是 策略路由
。它使得用户可以基于源 IP 等信息进行路由配置。
策略路由的基本原理是系统会根据 IP 报文的特征使用不同的路由表。它需要在内核编译时勾选CONFIG_IP_MULTIPLE_TABLES
Linux
内核最多支持 256 张路由表, 其中有 4 张是系统默认保留的,用户可以新建 252 张表
从 /etc/iproute2/rt_tables
可以看到内核保留的 4 张表,其中有用的是local
表和 main
表
root@ubuntu-1:/home/user1# cat /etc/iproute2/rt_tables
#
# reserved values
#
255 local
254 main
253 default
0 unspec
local
表中存储的是内核自动生成 本机路由
和广播路由
。
当我们使用 route 命令或者不指定路由表时的 ip route 时,操作的表是
main
表,所以我们需要使用额外的参数才能操作和查看local
表
[root@real-server]# ip address show dev eth1
6: eth1: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:80:c8:e8:1e:fc brd ff:ff:ff:ff:ff:ff
inet 10.10.20.89/24 brd 10.10.20.255 scope global eth1
[root@real-server]# ip route show dev eth1
10.10.20.0/24 proto kernel scope link src 10.10.20.89
[root@real-server]# ip route show dev eth1 table local
broadcast 10.10.20.0 proto kernel scope link src 10.10.20.89
broadcast 10.10.20.255 proto kernel scope link src 10.10.20.89
local 10.10.20.89 proto kernel scope host src 10.10.20.89
[root@real-server]# ip address add 192.168.254.254/24 brd+ dev eth1
[root@real-server]# ip address show dev eth1
6: eth1: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:80:c8:e8:1e:fc brd ff:ff:ff:ff:ff:ff
inet 10.10.20.89/24 brd 10.10.20.255 scope global eth1
inet 192.168.254.254/24 brd 192.168.254.255 scope global eth1
[root@real-server]# ip route show dev eth1
10.10.20.0/24 proto kernel scope link src 10.10.20.89
192.168.254.0/24 proto kernel scope link src 192.168.254.254
[root@real-server]# ip route show dev eth1 table local
broadcast 10.10.20.0 proto kernel scope link src 10.10.20.89
broadcast 192.168.254.0 proto kernel scope link src 192.168.254.254
broadcast 10.10.20.255 proto kernel scope link src 10.10.20.89
local 192.168.254.254 proto kernel scope host src 192.168.254.254
local 10.10.20.89 proto kernel scope host src 10.10.20.89
broadcast 192.168.254.255 proto kernel scope link src 192.168.254.254
一般情况下,我们不应该也没必要操作 local
表,虽然内核并没有禁止用户对 local
表的操作
ip route add table local local 10.10.20.64 dev eth0 proto kernel scope host src 10.10.20.67
ip route add table local local 192.168.43.12 dev eth4 proto kernel scope host src 192.168.43.14
路由表的选择
使用 ip rule
命令,我们可以看到系统选择路由表的规则
[root@real-server]# ip rule show
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
第一项表示默认查找的优先级,数字越小的优先级越高,所以系统默认会从 local
表进行查找。规则中的 from all
表示所有源地址。
我们可以 ip rule
命令添加自定义的规则,
ip rule add from 192.168.101.0/24 table 10
ip rule add to 192.168.102.0/24 table 20
ip rule add to 192.168.103.0/24 table 30 prio 123
上面的规则表示来自 192.168.101.0/24
网段的报文都查询 table 10
, 目的地址为192.168.102.0/24
网段的报文查询 table 20
,目的地址为192.168.103.0/24
网段的报文查询 table 30
且优先级为123
[root@real-server]# ip rule show
0: from all lookup local
123: from all to 192.168.103.0/24 lookup 30
32764: from all to 192.168.102.0/24 lookup 20
32765: from 192.168.101.0/24 lookup 10
32766: from all lookup main
32767: from all lookup default
之后,用户就可以通过 ip route
命令向具体的 table
添加路由表项了。
(完)