策略路由介绍
传统的 IP 路由基于目标 IP 地址做出转发决策。而策略路由(Policy-Based Routing,PBR)允许网络管理员根据源 IP 地址、协议类型、数据包大小等多种条件来控制数据包的转发路径,提供了更灵活的路由控制机制。
策略路由的主要应用场景包括:
- 基于源地址的路由选择
- 负载均衡
- 服务质量(QoS)保障
- 流量工程
- 多出口网络管理
在 Linux 中,策略路由通过 IP 规则(IP Rule)和多路由表(Multiple Routing Tables)来实现。本文将介绍如何在 Linux 网络命名空间和 veth 对环境中配置和使用策略路由。
策略路由基本概念
在 Linux 中,策略路由由两个主要组件组成:
路由策略数据库(RPDB)
路由策略数据库包含一系列规则,每条规则包含一个选择器和一个动作。选择器用于匹配数据包,动作指定匹配的数据包应该使用哪个路由表。
多路由表
Linux 支持多个路由表,每个表都有自己的路由条目。默认情况下,Linux 使用主路由表(表 ID 为 254)进行路由决策。
Linux 预定义了以下路由表:
- 本地表(Local Table,ID 255):包含本地和广播地址的路由
- 主表(Main Table,ID 254):默认的路由表
- 默认表(Default Table,ID 253):后处理规则
- 未指定表(Unspec Table,ID 0):保留
用户可以创建自定义路由表,表 ID 范围为 1-252。
路由决策过程
当 Linux 内核需要为数据包做出路由决策时,它会按照以下步骤处理:
- 按优先级顺序检查 RPDB 中的规则
- 对于匹配的规则,使用指定的路由表查找路由
- 如果找到匹配的路由,使用该路由转发数据包
- 如果没有找到匹配的路由,继续检查下一条规则
- 如果所有规则都检查完毕仍未找到匹配的路由,丢弃数据包
策略路由命令
在 Linux 中,策略路由主要通过 ip rule 和 ip route 命令来配置。
IP 规则命令
# 查看当前规则
ip rule list
# 添加规则
ip rule add from 192.168.1.0/24 table 10
ip rule add from 192.168.2.0/24 table 20
# 删除规则
ip rule del from 192.168.1.0/24 table 10
# 添加带优先级的规则
ip rule add from 192.168.1.0/24 table 10 priority 100
路由表命令
# 查看指定路由表
ip route list table 10
# 添加路由到指定表
ip route add default via 192.168.1.1 table 10
ip route add 10.0.0.0/8 via 192.168.2.1 table 20
# 删除指定表中的路由
ip route del default table 10
自定义路由表名称
为了方便管理,可以为路由表指定名称。这些名称定义在 /etc/iproute2/rt_tables 文件中:
# 编辑 rt_tables 文件
echo "10 custom1" >> /etc/iproute2/rt_tables
echo "20 custom2" >> /etc/iproute2/rt_tables
# 使用名称引用路由表
ip rule add from 192.168.1.0/24 table custom1
ip route add default via 192.168.1.1 table custom1
基本策略路由配置
我们将创建一个包含三个路由器的网络拓扑,并配置基于源地址的策略路由。
网络拓扑
+------------+
| |
| Router2 |
| |
+------------+
|
| 10.0.12.0/24
|
+------------+ +------------+ +------------+
| | | | | |
| Client1 +---------+ Router1 +---------+ Server1 |
| | | | | |
+------------+ +------------+ +------------+
|
| 10.0.13.0/24
|
+------------+
| |
| Router3 |
| |
+------------+
在这个拓扑中,Router1 有两个出口(通过 Router2 和 Router3)。我们将配置策略路由,使来自 Client1 的流量通过 Router2 转发,而来自 Server1 的流量通过 Router3 转发。
环境准备
# 创建网络命名空间
sudo ip netns add client1
sudo ip netns add router1
sudo ip netns add router2
sudo ip netns add router3
sudo ip netns add server1
# 创建 veth 对
sudo ip link add c1-r1 type veth peer name r1-c1
sudo ip link add r1-r2 type veth peer name r2-r1
sudo ip link add r1-r3 type veth peer name r3-r1
sudo ip link add r1-s1 type veth peer name s1-r1
# 将 veth 对分配给相应的命名空间
sudo ip link set c1-r1 netns client1
sudo ip link set r1-c1 netns router1
sudo ip link set r1-r2 netns router1
sudo ip link set r2-r1 netns router2
sudo ip link set r1-r3 netns router1
sudo ip link set r3-r1 netns router3
sudo ip link set r1-s1 netns router1
sudo ip link set s1-r1 netns server1
# 配置 IP 地址
sudo ip netns exec client1 ip addr add 10.0.1.2/24 dev c1-r1
sudo ip netns exec router1 ip addr add 10.0.1.1/24 dev r1-c1
sudo ip netns exec router1 ip addr add 10.0.12.1/24 dev r1-r2
sudo ip netns exec router2 ip addr add 10.0.12.2/24 dev r2-r1
sudo ip netns exec router1 ip addr add 10.0.13.1/24 dev r1-r3
sudo ip netns exec router3 ip addr add 10.0.13.3/24 dev r3-r1
sudo ip netns exec router1 ip addr add 10.0.2.1/24 dev r1-s1
sudo ip netns exec server1 ip addr add 10.0.2.2/24 dev s1-r1
# 启用接口
sudo ip netns exec client1 ip link set c1-r1 up
sudo ip netns exec client1 ip link set lo up
sudo ip netns exec router1 ip link set r1-c1 up
sudo ip netns exec router1 ip link set r1-r2 up
sudo ip netns exec router1 ip link set r1-r3 up
sudo ip netns exec router1 ip link set r1-s1 up
sudo ip netns exec router1 ip link set lo up
sudo ip netns exec router2 ip link set r2-r1 up
sudo ip netns exec router2 ip link set lo up
sudo ip netns exec router3 ip link set r3-r1 up
sudo ip netns exec router3 ip link set lo up
sudo ip netns exec server1 ip link set s1-r1 up
sudo ip netns exec server1 ip link set lo up
# 启用 IP 转发
sudo ip netns exec router1 sysctl -w net.ipv4.ip_forward=1
sudo ip netns exec router2 sysctl -w net.ipv4.ip_forward=1
sudo ip netns exec router3 sysctl -w net.ipv4.ip_forward=1
# 添加默认路由
sudo ip netns exec client1 ip route add default via 10.0.1.1
sudo ip netns exec server1 ip route add default via 10.0.2.1
# 添加互联网模拟
sudo ip netns exec router2 ip link add lo2 type dummy
sudo ip netns exec router2 ip link set lo2 up
sudo ip netns exec router2 ip addr add 8.8.8.8/32 dev lo2
sudo ip netns exec router3 ip link add lo3 type dummy
sudo ip netns exec router3 ip link set lo3 up
sudo ip netns exec router3 ip addr add 1.1.1.1/32 dev lo3
# 添加到互联网的路由
sudo ip netns exec router2 ip route add 10.0.1.0/24 via 10.0.12.1
sudo ip netns exec router2 ip route add 10.0.2.0/24 via 10.0.12.1
sudo ip netns exec router3 ip route add 10.0.1.0/24 via 10.0.13.1
sudo ip netns exec router3 ip route add 10.0.2.0/24 via 10.0.13.1
配置策略路由
现在,我们将在 Router1 上配置策略路由,使来自 Client1 的流量通过 Router2 转发,而来自 Server1 的流量通过 Router3 转发:
# 创建自定义路由表
sudo ip netns exec router1 bash -c 'echo "1 client" >> /etc/iproute2/rt_tables'
sudo ip netns exec router1 bash -c 'echo "2 server" >> /etc/iproute2/rt_tables'
# 添加路由到自定义表
sudo ip netns exec router1 ip route add default via 10.0.12.2 table client
sudo ip netns exec router1 ip route add 10.0.1.0/24 dev r1-c1 table client
sudo ip netns exec router1 ip route add 10.0.2.0/24 dev r1-s1 table client
sudo ip netns exec router1 ip route add default via 10.0.13.3 table server
sudo ip netns exec router1 ip route add 10.0.1.0/24 dev r1-c1 table server
sudo ip netns exec router1 ip route add 10.0.2.0/24 dev r1-s1 table server
# 添加策略规则
sudo ip netns exec router1 ip rule add from 10.0.1.0/24 table client
sudo ip netns exec router1 ip rule add from 10.0.2.0/24 table server
# 清除路由缓存
sudo ip netns exec router1 ip route flush cache
测试策略路由
现在,我们可以测试策略路由是否正常工作:
# 从 Client1 访问互联网(应该通过 Router2)
sudo ip netns exec client1 traceroute 8.8.8.8
# 从 Server1 访问互联网(应该通过 Router3)
sudo ip netns exec server1 traceroute 1.1.1.1
我们还可以使用 ip route get 命令来验证路由决策:
# 验证从 Client1 到互联网的路由
sudo ip netns exec router1 ip route get 8.8.8.8 from 10.0.1.2
# 验证从 Server1 到互联网的路由
sudo ip netns exec router1 ip route get 1.1.1.1 from 10.0.2.2
高级策略路由配置
除了基于源地址的策略路由外,Linux 还支持基于多种条件的策略路由。
基于目标地址的策略路由
# 添加基于目标地址的规则
sudo ip netns exec router1 ip rule add to 8.8.8.8/32 table client
sudo ip netns exec router1 ip rule add to 1.1.1.1/32 table server
基于入接口的策略路由
# 添加基于入接口的规则
sudo ip netns exec router1 ip rule add iif r1-c1 table client
sudo ip netns exec router1 ip rule add iif r1-s1 table server
基于 TOS 的策略路由
# 添加基于 TOS 的规则
sudo ip netns exec router1 ip rule add tos 0x10 table client
sudo ip netns exec router1 ip rule add tos 0x08 table server
基于 fwmark 的策略路由
结合 iptables 和 fwmark,可以实现更复杂的策略路由:
# 使用 iptables 标记数据包
sudo ip netns exec router1 iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 1
sudo ip netns exec router1 iptables -t mangle -A PREROUTING -p tcp --dport 443 -j MARK --set-mark 2
# 添加基于 fwmark 的规则
sudo ip netns exec router1 ip rule add fwmark 1 table client
sudo ip netns exec router1 ip rule add fwmark 2 table server
负载均衡
策略路由结合多路径路由可以实现负载均衡:
# 创建负载均衡路由表
sudo ip netns exec router1 bash -c 'echo "3 balance" >> /etc/iproute2/rt_tables'
# 添加多路径路由
sudo ip netns exec router1 ip route add default scope global table balance nexthop via 10.0.12.2 weight 1 nexthop via 10.0.13.3 weight 1
# 添加规则
sudo ip netns exec router1 ip rule add from 10.0.1.0/24 table balance
源地址转换
结合 NAT,可以实现更复杂的网络配置:
# 为从 Client1 发出的流量配置 SNAT
sudo ip netns exec router1 iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -o r1-r2 -j SNAT --to-source 10.0.12.1
# 为从 Server1 发出的流量配置 SNAT
sudo ip netns exec router1 iptables -t nat -A POSTROUTING -s 10.0.2.0/24 -o r1-r3 -j SNAT --to-source 10.0.13.1
策略路由故障排除
配置策略路由时可能会遇到各种问题。以下是一些常见问题及其解决方法:
规则顺序问题
规则按优先级顺序处理。如果规则的优先级不正确,可能导致意外的路由行为:
# 查看规则优先级
sudo ip netns exec router1 ip rule list
# 删除规则并重新添加,指定正确的优先级
sudo ip netns exec router1 ip rule del from 10.0.1.0/24 table client
sudo ip netns exec router1 ip rule add from 10.0.1.0/24 table client priority 100
路由表内容问题
确保自定义路由表包含所有必要的路由:
# 查看路由表内容
sudo ip netns exec router1 ip route list table client
sudo ip netns exec router1 ip route list table server
# 添加缺失的路由
sudo ip netns exec router1 ip route add 10.0.1.0/24 dev r1-c1 table client
路由缓存问题
修改路由规则后,需要刷新路由缓存:
sudo ip netns exec router1 ip route flush cache
调试工具
使用以下工具来调试策略路由问题:
# 使用 ip route get 查看路由决策
sudo ip netns exec router1 ip route get 8.8.8.8 from 10.0.1.2
# 使用 tcpdump 监控流量
sudo ip netns exec router1 tcpdump -i r1-r2 -n
sudo ip netns exec router1 tcpdump -i r1-r3 -n
# 使用 traceroute 跟踪路径
sudo ip netns exec client1 traceroute -n 8.8.8.8
总结
策略路由是一种强大的网络功能,它允许网络管理员基于多种条件控制数据包的转发路径。在本文中,我们介绍了 Linux 中策略路由的基本概念、配置方法和高级选项,并通过实际示例演示了如何在 veth 对和网络命名空间环境中配置策略路由。
策略路由的主要优点包括:
- 灵活的路由控制,不仅限于目标地址
- 支持多种条件的路由决策
- 与 Linux 网络堆栈的其他部分(如 iptables)良好集成
- 可以实现复杂的网络功能,如负载均衡、流量工程等
通过掌握策略路由的配置和使用,我们可以更好地控制网络流量,实现更复杂的网络功能。