文档的背景和作用
写这个文档是因为工作中遇到一个情况,一台服务器有两个网卡,想实现两个网卡都能够有自己的网关(Linux 默认只有一个网关),解决办法就是添加新的路由表,并把新路由表应用到网卡上,网上搜索了一下别人的文章,虽然也有人写了如何配置双网关,但是持久化配置基本上都用 /etc/rc.d/rc.local
来实现持久化配置,但是这个有一个缺点,就是重启网络服务或网卡时,规则会失效,所以自己就摸索了一下如何写路由表并且在重启网络服务的情况下持久化保存配置,并记录下来。
系统环境
这次用的系统为 RHEL 系列的系统,Ubuntu 系还没测试,有机会再写
系统下有两个 IP,一个是 192.168.1.10,另一个是 192.168.2.10
[root@rhel ~]# ip -br a
lo UNKNOWN 127.0.0.1/8 ::1/128
ens33 UP 192.168.221.135/24 fe80::20c:29ff:fe78:4fe2/64
ens36 UP 192.168.2.10/32 fe80::20c:29ff:fe78:4fec/64
virbr0 DOWN 192.168.122.1/24
virbr0-nic DOWN
ens37 UP 192.168.1.10/24 fe80::20c:29ff:fe78:4ff6/64
RHEL 系列系统配置路由表(双网关)
添加路由表
在 /etc/iproute2/rt_tables
内添加两个路由表,一个是 nic1,另一个是 nic2。(编号任取)
[root@rhel ~]# cat /etc/iproute2/rt_tables
#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
#1 inr.ruhep
101 nic1
102 nic2
临时添加路由
[root@rhel ~]# ip route add default via 192.168.1.254 dev ens37 table nic1
[root@rhel ~]# ip route add default via 192.168.2.254 dev ens36 table nic2
[root@rhel ~]# ip route show table nic1
default via 192.168.1.254 dev ens37
[root@rhel ~]# ip route show table nic2
default via 192.168.2.254 dev ens36
设置路由规则
将指定的 IP 添加到自定义的路由表中。
[root@rhel ~]# ip rule
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
[root@rhel ~]# ip rule add from 192.168.1.10 table nic1
[root@rhel ~]# ip rule add from 192.168.2.10 table nic2
[root@rhel ~]# ip rule
0: from all lookup local
32764: from 192.168.2.10 lookup nic2
32765: from 192.168.1.10 lookup nic1
32766: from all lookup main
32767: from all lookup default
测试双网关
[root@rhel ~]# ip route get 8.8.8.8 from 192.168.1.10
8.8.8.8 from 192.168.1.10 via 192.168.1.254 dev ens37
cache
[root@rhel ~]# ip route get 8.8.8.8 from 192.168.2.10
8.8.8.8 from 192.168.2.10 via 192.168.2.254 dev ens36
cache
可以看到在 192.168.1.10 和 192.168.2.10 两个 IP 上获取访问 8.8.8.8 的路由时,走的网关是不同的。
这里要注意的一点是虽然双网关都生效了,但是这个是其他主机访问这台主机时才会生效,如果是本机向外去访问或 ping 时,还是需要配置默认网关,否则本机主动向外访问时不通。(这个默认网关不需要设置路由表,直接配置在默认的 main 表就可以)
添加本机网卡路由
从这开始往下是用两个主机来测试,A 主机地址为 192.168.221.133,B 主机地址为 192.168.221.135
上一步已经做到了双网卡,但是此时访问同网段 IP 时也会走网关,如果网关不通,那么同网段也会无法访问。
假设 A 机器的地址为 192.168.221.133,且这个 IP 使用路由表 test1。
[root@localhost ~]# ip rule
0: from all lookup local
32765: from 192.168.221.133 lookup test1
32766: from all lookup main
32767: from all lookup default
路由表的路由只有一条,这里的 192.168.221.50 是不存在的地址。
[root@localhost ~]# ip route show table test1
default via 192.168.221.50 dev ens33
此时从其他机器来 ping 192.168.221.133 是不通的。
[root@rhel ~]# ping -c3 192.168.221.133
PING 192.168.221.133 (192.168.221.133) 56(84) bytes of data.
--- 192.168.221.133 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2000ms
修改路由为正确的网关。
[root@localhost ~]# ip route del default via 192.168.221.50 dev ens33 table test1
[root@localhost ~]# ip route add default via 192.168.221.2 dev ens33 table test1
[root@localhost ~]# ip route show table test1
default via 192.168.221.2 dev ens33
[root@localhost ~]# ping -c1 192.168.221.2
PING 192.168.221.2 (192.168.221.2) 56(84) bytes of data.
64 bytes from 192.168.221.2: icmp_seq=1 ttl=128 time=0.446 ms
--- 192.168.221.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.446/0.446/0.446/0.000 ms
从其他机器测试 ping。
[root@rhel ~]# ping -c3 192.168.221.133
PING 192.168.221.133 (192.168.221.133) 56(84) bytes of data.
64 bytes from 192.168.221.133: icmp_seq=1 ttl=128 time=0.331 ms
64 bytes from 192.168.221.133: icmp_seq=2 ttl=128 time=0.482 ms
64 bytes from 192.168.221.133: icmp_seq=3 ttl=128 time=0.431 ms
以上可知,现在同网段的地址也会走网关,如果网关出问题,那么同网段的也不会通,但是实际上同网段的不需要网关,直接走本机网卡就可以,所以要加第二条路由,让同网段的 IP 不走网关,走本机网卡,添加步骤如下:
-
查看默认路由表的本机网卡路由
[root@localhost ~]# ip route default via 192.168.221.134 dev ens33 proto static metric 100 192.168.1.133 dev ens36 proto kernel scope link src 192.168.1.133 metric 101 192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 192.168.221.0/24 dev ens33 proto kernel scope link src 192.168.221.133 metric 100
这里的
192.168.221.0/24 dev ens33 proto kernel scope link src 192.168.221.133 metric 100
就是本机网卡的路由,不同版本的系统可能不会完全一样,但是大同小异。(这里边有一个src 192.168.221.133
,后边解释这个,这里边需要添加的路由信息只有192.168.221.0/24 dev ens33 proto kernel scope link
) -
在路由表中添加路由
[root@localhost ~]# ip route show table test1 default via 192.168.221.50 dev ens33 [root@localhost ~]# ip route add 192.168.221.0/24 dev ens33 proto kernel scope link table test1 [root@localhost ~]# ip route show table test1 default via 192.168.221.50 dev ens33 192.168.221.0/24 dev ens33 proto kernel scope link
-
再次用其他主机测试
[root@rhel ~]# ping -c3 192.168.221.133 PING 192.168.221.133 (192.168.221.133) 56(84) bytes of data. 64 bytes from 192.168.221.133: icmp_seq=1 ttl=64 time=0.155 ms 64 bytes from 192.168.221.133: icmp_seq=2 ttl=64 time=0.391 ms 64 bytes from 192.168.221.133: icmp_seq=3 ttl=64 time=0.382 ms
测试成功
路由 src 的解释
上一步中提到了 src 192.168.221.133
,这个参数的作用是当一个网卡上有多个 IP 时,流量包出去的时候,流量包的地址信息是 192.168.221.133,这个可以抓包看到。
-
在 192.168.221.135 上准备抓包
[root@rhel ~]# ip -br a lo UNKNOWN 127.0.0.1/8 ::1/128 ens33 UP 192.168.221.135/24 fe80::20c:29ff:fe78:4fe2/64 ens36 UP 192.168.2.10/24 fe80::20c:29ff:fe78:4fec/64 virbr0 DOWN 192.168.122.1/24 virbr0-nic DOWN ens37 UP 192.168.1.10/24 fe80::20c:29ff:fe78:4ff6/64 [root@rhel ~]# tcpdump -i ens33 -nn icmp tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
-
从 192.168.221.133 上添加一个地址 192.168.221.134,然后来 ping 192.168.221.135
[root@localhost ~]# ip addr add dev ens33 192.168.221.134/24 [root@localhost ~]# ip -br a lo UNKNOWN 127.0.0.1/8 ::1/128 ens33 UP 192.168.221.133/24 192.168.221.134/24 fe80::7a6d:b2a0:6591:5e43/64 ens36 UP 192.168.1.133/32 fe80::a83:ef9b:6809:7d7d/64 virbr0 DOWN 192.168.122.1/24 virbr0-nic DOWN [root@localhost ~]# ping -c3 192.168.221.135 PING 192.168.221.135 (192.168.221.135) 56(84) bytes of data. 64 bytes from 192.168.221.135: icmp_seq=1 ttl=64 time=0.350 ms 64 bytes from 192.168.221.135: icmp_seq=2 ttl=64 time=0.470 ms 64 bytes from 192.168.221.135: icmp_seq=3 ttl=64 time=0.371 ms --- 192.168.221.135 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2000ms rtt min/avg/max/mdev = 0.350/0.397/0.470/0.052 ms
-
查看 192.168.221.135 的抓包。
[root@rhel ~]# tcpdump -i ens33 -nn icmp tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes 21:18:10.049737 IP 192.168.221.133 > 192.168.221.135: ICMP echo request, id 4630, seq 1, length 64 21:18:10.049776 IP 192.168.221.135 > 192.168.221.133: ICMP echo reply, id 4630, seq 1, length 64 21:18:11.049780 IP 192.168.221.133 > 192.168.221.135: ICMP echo request, id 4630, seq 2, length 64 21:18:11.049813 IP 192.168.221.135 > 192.168.221.133: ICMP echo reply, id 4630, seq 2, length 64 21:18:12.049820 IP 192.168.221.133 > 192.168.221.135: ICMP echo request, id 4630, seq 3, length 64 21:18:12.049865 IP 192.168.221.135 > 192.168.221.133: ICMP echo reply, id 4630, seq 3, length 64
可以看到源地址是 192.168.221.133。
-
修改 192.168.221.133 路由信息
[root@localhost ~]# ip route show table test1 default via 192.168.221.50 dev ens33 192.168.221.0/24 dev ens33 proto kernel scope link [root@localhost ~]# ip route del 192.168.221.0/24 dev ens33 proto kernel scope link table test1 [root@localhost ~]# ip route add 192.168.221.0/24 dev ens33 proto kernel scope link src 192.168.221.134 table test1 [root@localhost ~]# ip route show table test1 default via 192.168.221.50 dev ens33 192.168.221.0/24 dev ens33 proto kernel scope link src 192.168.221.134
-
再次从 192.168.221.133 向 192.168.221.135 ping
[root@localhost ~]# ping -c3 192.168.221.135 PING 192.168.221.135 (192.168.221.135) 56(84) bytes of data. 64 bytes from 192.168.221.135: icmp_seq=1 ttl=64 time=0.351 ms 64 bytes from 192.168.221.135: icmp_seq=2 ttl=64 time=0.164 ms 64 bytes from 192.168.221.135: icmp_seq=3 ttl=64 time=1.42 ms --- 192.168.221.135 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2000ms rtt min/avg/max/mdev = 0.164/0.647/1.426/0.556 ms
-
检查 192.168.221.135 的抓包
[root@rhel ~]# tcpdump -i ens33 -nn icmp tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes 21:18:10.049737 IP 192.168.221.133 > 192.168.221.135: ICMP echo request, id 4630, seq 1, length 64 21:18:10.049776 IP 192.168.221.135 > 192.168.221.133: ICMP echo reply, id 4630, seq 1, length 64 21:18:11.049780 IP 192.168.221.133 > 192.168.221.135: ICMP echo request, id 4630, seq 2, length 64 21:18:11.049813 IP 192.168.221.135 > 192.168.221.133: ICMP echo reply, id 4630, seq 2, length 64 21:18:12.049820 IP 192.168.221.133 > 192.168.221.135: ICMP echo request, id 4630, seq 3, length 64 21:18:12.049865 IP 192.168.221.135 > 192.168.221.133: ICMP echo reply, id 4630, seq 3, length 64 21:21:53.986172 IP 192.168.221.133 > 192.168.221.135: ICMP echo request, id 4718, seq 1, length 64 21:21:53.986211 IP 192.168.221.135 > 192.168.221.133: ICMP echo reply, id 4718, seq 1, length 64 21:21:54.986386 IP 192.168.221.133 > 192.168.221.135: ICMP echo request, id 4718, seq 2, length 64 21:21:54.986413 IP 192.168.221.135 > 192.168.221.133: ICMP echo reply, id 4718, seq 2, length 64 21:21:55.987624 IP 192.168.221.133 > 192.168.221.135: ICMP echo request, id 4718, seq 3, length 64 21:21:55.987694 IP 192.168.221.135 > 192.168.221.133: ICMP echo reply, id 4718, seq 3, length 64
可以发现源地址变成了 192.168.221.135 了
这里我没有继续做更多的测试,我目前发现的就是路由中没有
src 192.168.221.133
字段时,Linux 会自动判断源地址,会将网卡的第一个地址设置为源地址,以ip -br a
举例,会将当前网卡最左边的地址作为源地址。[root@localhost ~]# ip -br a lo UNKNOWN 127.0.0.1/8 ::1/128 ens33 UP 192.168.221.133/24 192.168.221.134/24 fe80::7a6d:b2a0:6591:5e43/64 ens36 UP 192.168.1.133/32 fe80::a83:ef9b:6809:7d7d/64 virbr0 DOWN 192.168.122.1/24 virbr0-nic DOWN
这里就会自动将 192.168.221.133 作为源地址。
我这里是抓包看的,如果想继续测试这个源地址,可以用 iptables 来测试,假设源地址是 192.168.221.134,可以用 iptables 将所有源地址为 192.168.221.134 的包拦截,这样的话就只有源地址是 192.168.221.133 时才能 ping 通。
持久化配置
上边的配置都是当前临时生效,想要持久生效(重启也生效),需要将配置写到文件下,持久化配置分两种,一个是网络管理服务为 network,另一个是网络管理服务为 NetworkManager。
下边只写了怎么持久化路由配置,别忘了先在
/etc/iproute2/rt_tables
下定义路由表。
network 服务
当系统默认网络服务为 network 时,配置如下
-
将
ip rule
写在/etc/rc.d/rc.local
下。(注意给/etc/rc.d/rc.local
执行权限)[root@localhost ~]# cat /etc/rc.d/rc.local #!/bin/bash # THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES # # It is highly advisable to create own systemd services or udev rules # to run scripts during boot instead of using this file. # # In contrast to previous versions due to parallel execution during boot # this script will NOT be run after all other services. # # Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure # that this script will be executed during boot. touch /var/lock/subsys/local ip rule add from 192.168.221.133 table test1
-
将路由配置写在 `` 下。
[root@localhost ~]# cat /etc/sysconfig/network-scripts/route-ens33 default via 192.168.221.2 dev ens33 table test1 192.168.221.0/24 dev ens33 proto kernel scope link table test1
-
重启服务,检查路由配置
[root@localhost ~]# systemctl restart network [root@localhost ~]# ip route show table test1 default via 192.168.221.2 dev ens33 192.168.221.0/24 dev ens33 proto kernel scope link
这里没有写,如果使用
ifup
和ifdown
单独重启某个网卡的话,配置也会生效,可以自行测试。
这里边只写了 IPV4 的配置,如果是 IPV6 的话只需要把 route-ens33 改成 route6-ens33,
ip rule
改成ip -6 rule
关于
ip rule
,其实也可以写到/etc/sysconfig/network-scripts/
下的rule-ens33
文件里,但是我发现这样写每次重启服务都会重新添加 rule 的规则,而重启 network 服务后不会吧 rule 规则删除,所以可以把ip rule
写在/etc/rc.d/rc.local
下。
NetworkManager 服务
-
在
/etc/NetworkManager/dispatcher.d/
下写一个文件,名字为30-route-ens33
[root@localhost dispatcher.d]# cat /etc/NetworkManager/dispatcher.d/30-route-ens33 #!/bin/bash if [ "$2" = "up" ]; then case $1 in ens33) ip rule add from 192.168.221.254 table test1 ip route add 192.168.221.0/24 dev ens33 proto kernel scope link table test1 ip route add default via 192.168.221.2 dev ens33 table test1 esac fi if [ "$2" = "down" ]; then case $1 in ens33) ip rule del from 192.168.221.254 table test1 ip route del 192.168.221.0/24 dev ens33 proto kernel scope link table test1 ip route del default via 192.168.221.2 dev ens33 table test1 esac fi
-
重启网卡
[root@localhost dispatcher.d]# nmcli connection down ens33 && nmcli connection up ens33 Connection 'ens33' successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/12) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/14)
此时路由配置完成。
这里需要注意一点,网卡配置的名字要和脚本里的对应上,上述脚本里的配置名字是 ens33(不是网卡名),所以网卡的配置名字也得是 ens33,下边的例子里 ens33 网卡有两个配置,一个是 System ethx,另一个是 ens33,当前生效的是 System ethx,所以在执行
nmcli connection up ens33
和nmcli connection down ens33
都能够更新路由和规则(正常nmcli connection down ens33
应该删除规则的),虽然最后也能实现相同的功能,但是最好还是把名字对应上。[root@localhost dispatcher.d]# nmcli connection show NAME UUID TYPE DEVICE System ethx d45d97fb-8530-60e2-2d15-d92c0df8b0fc ethernet ens33 ens36 418da202-9a8c-b73c-e8a1-397e00f3c6b2 ethernet ens36 virbr0 d4ce2ccb-7540-4e1d-bddb-14078a5bd9f5 bridge virbr0 ens33 1e1966b9-3652-42b5-b38b-92ef20af3ef4 ethernet --
测试规则
-
将网卡启动,检查规则是否生效.
[root@localhost ~]# nmcli connection show NAME UUID TYPE DEVICE ens36 418da202-9a8c-b73c-e8a1-397e00f3c6b2 ethernet ens36 virbr0 28a641a1-4595-4987-bc91-c2fb4c3f4e27 bridge virbr0 ens33 1e1966b9-3652-42b5-b38b-92ef20af3ef4 ethernet -- [root@localhost ~]# ip rule 0: from all lookup local 32766: from all lookup main 32767: from all lookup default [root@localhost ~]# ip route show table test1 [root@localhost ~]# nmcli connection up ens33 Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/6) [root@localhost ~]# nmcli connection show NAME UUID TYPE DEVICE ens33 1e1966b9-3652-42b5-b38b-92ef20af3ef4 ethernet ens33 ens36 418da202-9a8c-b73c-e8a1-397e00f3c6b2 ethernet ens36 virbr0 28a641a1-4595-4987-bc91-c2fb4c3f4e27 bridge virbr0 [root@localhost ~]# ip rule 0: from all lookup local 32765: from 192.168.221.133 lookup test1 32766: from all lookup main 32767: from all lookup default [root@localhost ~]# ip route show table test1 default via 192.168.221.254 dev ens33 192.168.221.0/24 dev ens33 proto kernel scope link
可以看到网卡未启动是,没有路由和规则,网卡启动后,路由和规则出现。
-
将网卡关闭,检查路由和规则是否删除。
[root@localhost ~]# nmcli connection down ens33 Connection 'ens33' successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/6) [root@localhost ~]# ip rule 0: from all lookup local 32766: from all lookup main 32767: from all lookup default [root@localhost ~]# ip route show table test1
可以看到路由和规则已经删除了,所以 NetworkManager 服务的持久化路由配置成功,因为
nmcli connection up ens33
就能够加载路由和规则,所以重启也会生效。
Debian 系列配置路由表(双网关)
Debian 系列的系统使用 netplan 来配置网络,这个配置路由表很方便,但是它不管规则。
-
编辑
/etc/iproute2/rt_tables
文件,添加路由表。root@admin:/etc/netplan# cat /etc/iproute2/rt_tables # # reserved values # 255 local 254 main 253 default 0 unspec # # local # #1 inr.ruhep 101 test
-
编辑
/etc/netplan/00-installer-config.yaml
文件来配置路由表。root@admin:~# cat /etc/netplan/00-installer-config.yaml # This is the network config written by 'subiquity' network: ethernets: ens33: addresses: - 192.168.144.60/24 gateway4: 192.168.144.254 nameservers: addresses: [] search: [] ens37: addresses: - 192.168.221.140/24 dhcp4: false routes: - to: default via: 192.168.221.254 table: 101 - to: 192.168.221.0/24 scope: link table: 101 version: 2
这里边我在 ens37 网卡上配置了两个路由,一个是网关,另一个是本机网卡地址的路由,都在路由表 test 上(编号 101)。
-
重新加载配置。
root@admin:~# netplan apply root@admin:~# ip route show table test default via 192.168.221.254 dev ens37 proto static 192.168.144.0/24 dev ens37 proto static scope link
-
利用 systemd 文件来设置开机自动加载规则。
root@admin:~# cat /etc/systemd/system/ens37-rule.service [Unit] Description=Add rule from 192.168.221.140 Requires=network.target After=network.target [Service] ExecStart=/usr/sbin/ip rule add from 192.168.221.140 table test Type=oneshot [Install] WantedBy=multi-user.target
-
重启系统并检查配置。
root@admin:~# ip rule 0: from all lookup local 32765: from 192.168.221.140 lookup test 32766: from all lookup main 32767: from all lookup default root@admin:~# ip route show table test default via 192.168.221.254 dev ens37 proto static 192.168.144.0/24 dev ens37 proto static scope link root@admin:~# ip route get 8.8.8.8 from 192.168.221.140 8.8.8.8 from 192.168.221.140 via 192.168.221.254 dev ens37 table test uid 0 cache root@admin:~# ip route get 192.168.221.50 from 192.168.221.140 192.168.221.50 from 192.168.221.140 dev ens37 table test uid 0 cache
可以看到配置已生效。