Hub-Spoke 静态路由问题
Hub-Spoke 静态路由问题
网络拓扑

- Hub 设备位于数据中心,配在 port2 配置“拨号用户(Dynamic)”模式的 IPSec 连接。
- Spoke1 与 Spoke2 均在 port2 上配置“静态 IP 地址(Static)”类型的 IPSec 连接,与 Hub 建立 IPSec 隧道。
- Hub 与 Spoke 之间通过静态路由进行通信。
配置信息
仅以 Hub 和 Spoke1 为例。基础网络 IP、路由配置略。
Hub
IPSec 隧道配置,隧道类型为 dynamic,第二阶段保护网段为 0.0.0.0/0↔︎0.0.0.0/0,
add-route功能为关闭状态。config vpn ipsec phase1-interface edit "Hub" set type dynamic set interface "port2" set ike-version 2 set peertype any set net-device disable set proposal aes128-sha256 set add-route disable set dpd on-idle set psksecret ENC YsyLk34z2H6ffoT8QYhgsX5aS5FN4Vq3g9IpjKgh/7uBJdl+W3IMHDuXxXpT1a+ZyxR37cOfpB1Uzf0Vnv8D2Q0E+3259DPYr556hZRDGYo46f= set dpd-retryinterval 60 next end config vpn ipsec phase2-interface edit "Hub" set phase1name "Hub" set proposal aes128-sha1 set keepalive enable next end配置 IPSec Tunnel IP。
config system interface edit "Hub" set ip 10.10.10.1 255.255.255.255 set type tunnel set remote-ip 10.10.10.254 255.255.255.0 set interface "port2" next end配置防火墙策略放通 IPSec 隧道与内网口 port3 之间的流量。
config firewall policy edit 1 set name "VPN_Hub" set srcintf "Hub" "port3" set dstintf "Hub" "port3" set action accept set srcaddr "all" set dstaddr "all" set schedule "always" set service "ALL" next end配置去往 Spoke1 内网网段(192.168.1.0/24)和 Spoke2 内网网段(192.168.2.0/24)的静态路由,出接口选择 IPSec Tunnel 接口。
config router static edit 2 set dst 192.168.1.0 255.255.255.0 set device "Hub" next edit 3 set dst 192.168.2.0 255.255.255.0 set device "Hub" next end
Spoke1
IPSec 隧道配置,隧道类型为 static,第二阶段保护网段为 0.0.0.0/0↔︎0.0.0.0/0。
config vpn ipsec phase1-interface edit "Spoke1" set interface "port2" set ike-version 2 set peertype any set net-device disable set proposal aes128-sha256 set remote-gw 100.1.1.2 set dpd on-idle set psksecret ENC fDnGerNZb+ogGLimNoAdp/nQj8tOZRBwu91bMs1MqK4oB/t1Szc8C1Ma43Pga8cW8Qxdew7TgFECbvwq1nYxPqVU6pnbj/DkpT/nuGzvTietA0mjCBubl/1jPU8wkFWABfIXsivn0YOSyjHmQx5LSdbhjvM9FTI6CKjixMszGuC8T8kpczpL2h1FAX1SkytiKeztJg== next end config vpn ipsec phase2-interface edit "Spoke1" set phase1name "Spoke1" set proposal aes128-sha1 set auto-negotiate enable next end配置 IPSec Tunnel IP。
config system interface edit "Spoke1" set ip 10.10.10.2 255.255.255.255 set type tunnel set remote-ip 10.10.10.254 255.255.255.0 set interface "port2" next end配置防火墙策略放通 IPSec 隧道与内网口 port3 之间的流量。
config firewall policy edit 1 set name "VPN_Spoke1" set srcintf "port3" "Spoke1" set dstintf "port3" "Spoke1" set action accept set srcaddr "all" set dstaddr "all" set schedule "always" set service "ALL" next end配置去往 Hub 内网网段(192.168.0.0/24)的静态路由,出接口选择 IPSec Tunnel 接口。
config router static edit 2 set dst 192.168.0.0 255.255.255.0 set device "Spoke1" next end
Spoke2
略,与 Spoke1 一致。
问题现象
IPSec 隧道建立后,查看 Hub 和 Spoke 的 IPSec 一阶段状态中的
tun_id,可以看到 Hub 建立了两个动态子 IPSec 连接(Hub_0 和 Hub_1),Hub 和 Spoke 显示已建立的隧道 ID(tun_id)均为对端公网 IP 地址。Hub # diagnose vpn ike gateway list | grep "name\|tun_id" name: Hub_0 tun_id: 200.1.1.2/::10.0.0.5 <----隧道ID为Spoke1的公网IP name: Hub_1 tun_id: 201.1.1.2/::10.0.0.7 <----隧道ID为Spoke2的公网IPSpoke1 # diagnose vpn ike gateway list | grep "name\|tun_id" name: Spoke1 tun_id: 100.1.1.2/::100.1.1.2 <----隧道ID为Hub的公网IPSpoke2 # diagnose vpn ike gateway list | grep "name\|tun_id" name: Spoke2 tun_id: 100.1.1.2/::100.1.1.2 <----隧道ID为Hub的公网IP使用 PC2(Spoke1 内网)访问 PC1(Hub 内网),可以正常通信。
C:\Users\Administrator.SUMMERICE2019>ipconfig Windows IP 配置 以太网适配器 Ethernet0: IPv4 地址 . . . . . . . . . . . . : 192.168.1.100 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 192.168.1.1 C:\Users\Administrator.SUMMERICE2019>ping 192.168.0.100 正在 Ping 192.168.0.100 具有 32 字节的数据: 来自 192.168.0.100 的回复: 字节=32 时间=3ms TTL=126 来自 192.168.0.100 的回复: 字节=32 时间=1ms TTL=126 来自 192.168.0.100 的回复: 字节=32 时间=1ms TTL=126 来自 192.168.0.100 的回复: 字节=32 时间=2ms TTL=126 192.168.0.100 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失), 往返行程的估计时间(以毫秒为单位): 最短 = 1ms,最长 = 3ms,平均 = 1ms使用 PC1(Hub 内网)访问 PC2(Spoke1 内网)或 PC3(Spoke2 内网),无法正常通信。
C:\Users\Administrator.SUMMERICE2019>ipconfig Windows IP 配置 以太网适配器 Ethernet0: IPv4 地址 . . . . . . . . . . . . : 192.168.0.100 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 192.168.0.1 C:\Users\Administrator.SUMMERICE2019>ping 192.168.1.100 正在 Ping 192.168.1.100 具有 32 字节的数据: 请求超时。 请求超时。 请求超时。 请求超时。 192.168.1.100 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 0,丢失 = 4 (100% 丢失),在 Hub 上抓包显示 ICMP Request 报文已被送入 IPSec Tunnel 接口。Debug Flow 虽然显示 ICMP Request 报文已被送入 IPSec Tunnel 接口,但路由查找的
gw(网关)为 0.0.0.0,正确的gw应为步骤 1 中看到的 Hub 与 Spoke1 的 IPSec 子连接Hub_0的tun_id200.1.1.1。
在 Hub 上查看 IPSec Tunnel 接口的统计信息,可以看到 txe(发送错误)计数随着 PC1 访问 PC2 的流量在一直增长。
Hub # diagnose netlink interface list name Hub | grep "txe=\|if=" if=Hub family=00 type=768 index=16 mtu=1420 link=0 master=0 stat: rxp=15 txp=15 rxb=900 txb=900 rxe=0 txe=102 rxd=0 txd=0 mc=0 collision=0 @ time=1715074667 Hub # diagnose netlink interface list name Hub | grep "txe=\|^if=" if=Hub family=00 type=768 index=16 mtu=1420 link=0 master=0 stat: rxp=15 txp=15 rxb=900 txb=900 rxe=0 txe=108 <----txe计数增长 rxd=0 txd=0 mc=0 collision=0 @ time=1715074676
问题原因
这是由于 FortiOS 7.0 新的 IPSec 内核设计导致的。FortiOS 7.0 之前版本的 Dynamic 模式 IPSec 使用“route tree”功能查找流量应该匹配的子隧道。FortiOS 7.0 之后将“route tree”功能移除,使用“tunn-id”用于标识 Dynamic 模式 IPSec 连接生成的每个子连接。当 Dynamic 模式的 IPSec 连接生成子连接(如 Hub_0,Hub_1……)时,将自动分配一个
tunn-id给这个子连接,默认配置下,使用对端的公网 IP 作为隧道的tunn-id。在 Spoke1(Static 类型的 IPSec)上查看去往 Hub 内网网段(192.168.0.0/24)的路由,可以看到路由信息中有对应的
tun_id,为 Hub 的公网地址 100.1.1.2,所以 Spoke 内网 PC 去往 Hub 内外 PC 的流量可以正确送入对应tun_id的 IPSec 隧道。Spoke1 # get router info routing-table all | grep 192.168.0.0 S 192.168.0.0/24 [10/0] via Spoke1 tunnel 100.1.1.2, [1/0]在 Hub 上查看路由表,可以看到已配置的 Hub 去往 Spoke1 和 Spoke2 的静态路由在路由表中显示为 Tunnel 接口的直连路由,并未显示对应的
tun_id(这里应为 Spoke1 和 Spoke2 的公网 IP)。这是由于在 Hub 上配置的去往 Spoke 内网的静态路由并未指定出接口和下一跳为哪个 IPSec 子连接(Hub_0 或 Hub_1)。Hub # get router info routing-table all | grep 192.168 C 192.168.0.0/24 is directly connected, port3 S 192.168.1.0/24 [10/0] is directly connected, Hub, [1/0] S 192.168.2.0/24 [10/0] is directly connected, Hub, [1/0]当 PC1(Hub 内网)访问 PC2(Spoke1 内网)或 PC3(Spoke2 内网)时,发起流量进入 Hub 的 IPSec 隧道后,由于路由中没有
tun_id标识,IPSec 不知道应该将其送往哪个子 IPSec 连接(Hub_0 或 Hub_1)。
解决方法 1 - 使用 add-route
在 Spoke 上的 IPSec 二阶段配置中,修改源保护网段为明细保护网段,目标保护网段仍为 0.0.0.0/0。
Spoke1: config vpn ipsec phase2-interface edit "Spoke1" set src-subnet 192.168.1.0 255.255.255.0 next end Spoke2: config vpn ipsec phase2-interface edit "Spoke1" set src-subnet 192.168.2.0 255.255.255.0 next end将 Hub 上配置的去往 Spoke 内外的静态路由禁用或删除。
config router static edit 2 set status disable set dst 192.168.1.0 255.255.255.0 set device "Hub" next edit 3 set status disable set dst 192.168.2.0 255.255.255.0 set device "Hub" next end在 Hub 上的 IPSec 一阶段配置中,将
add-route功能开启,根据 Spoke 协商的源保护网段自动添加去往 Spoke 内网的路由。config vpn ipsec phase1-interface edit "Hub" set add-route enable next end在 Hub 上查看路由表,可以看到通过
add-route功能添加的去往 Spoke 内网的路由自动添加了tunn-id标记,去往 Spoke1 内网的路由指定的是 IPSec 子连接 Hub_0 的tunn-id,去往 Spoke2 内网的路由指定的是 IPSec 子连接 Hub_1 的tunn-id。Hub # get router info routing-table all | grep 192.168 C 192.168.0.0/24 is directly connected, port3 S 192.168.1.0/24 [15/0] via Hub tunnel 200.1.1.2, [1/0] S 192.168.2.0/24 [15/0] via Hub tunnel 201.1.1.2, [1/0]使用 PC1(Hub 内网)访问 PC2(Spoke1 内网)或 PC3(Spoke2 内网),可以正常通信。
C:\Users\Administrator.SUMMERICE2019>ipconfig 以太网适配器 Ethernet0: IPv4 地址 . . . . . . . . . . . . : 192.168.0.100 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 192.168.0.1 C:\Users\Administrator.SUMMERICE2019>ping 192.168.1.100 正在 Ping 192.168.1.100 具有 32 字节的数据: 来自 192.168.1.100 的回复: 字节=32 时间=1ms TTL=126 来自 192.168.1.100 的回复: 字节=32 时间=1ms TTL=126 来自 192.168.1.100 的回复: 字节=32 时间=1ms TTL=126 来自 192.168.1.100 的回复: 字节=32 时间=3ms TTL=126 192.168.1.100 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失), 往返行程的估计时间(以毫秒为单位): 最短 = 1ms,最长 = 3ms,平均 = 1msDebug Flow 显示 ICMP Request 报文到达 FortiGate 后,路由查找网关为 Hub 与 Spoke1 的 IPSec 子隧道 Hub_0 的
tun_id200.1.1.2。该流量最终可以被送入 IPSec 子隧道 Hub_0 送往 Spoke1。
解决方法 2 - 配置 VPN 路由下一跳
重要
此方法要求 Spoke 的公网 IP 不会变化。
Spoke IPSec 二阶段仍然配置 0.0.0.0↔︎0.0.0.0 的保护网段,Hub 关闭
add-route功能。在 Hub 上修改去往 Spoke 的静态路由,配置下一跳为 Spoke 对应的公网 IP(tun_id)。
重要
出接口为 IPSec Tunnel 时,相关路由的下一跳(gateway)只能在 CLI 下配置。
config router static edit 2 set dst 192.168.1.0 255.255.255.0 set gateway 200.1.1.2 set device "Hub" next edit 3 set dst 192.168.2.0 255.255.255.0 set gateway 201.1.1.2 set device "Hub" next end在 Hub 查看路由表,去往 Spoke 内网的路由下一跳变为 Spoke1 和 Spoke2 的公网 IP,对应相关已建立的 IPSec 子隧道 Hub_0、Hub_1 的
tun_id。Hub # get router info routing-table all | grep 192.168 C 192.168.0.0/24 is directly connected, port3 S 192.168.1.0/24 [10/0] via Hub tunnel 200.1.1.2, [1/0] S 192.168.2.0/24 [10/0] via Hub tunnel 201.1.1.2, [1/0]使用 PC1(Hub 内网)访问 PC2(Spoke1 内网)或 PC3(Spoke2 内网),可以正常通信。
C:\Users\Administrator.SUMMERICE2019>ipconfig 以太网适配器 Ethernet0: IPv4 地址 . . . . . . . . . . . . : 192.168.0.100 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 192.168.0.1 C:\Users\Administrator.SUMMERICE2019>ping 192.168.1.100 正在 Ping 192.168.1.100 具有 32 字节的数据: 来自 192.168.1.100 的回复: 字节=32 时间=1ms TTL=126 来自 192.168.1.100 的回复: 字节=32 时间=1ms TTL=126 来自 192.168.1.100 的回复: 字节=32 时间=1ms TTL=126 来自 192.168.1.100 的回复: 字节=32 时间=3ms TTL=126 192.168.1.100 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失), 往返行程的估计时间(以毫秒为单位): 最短 = 1ms,最长 = 3ms,平均 = 1msDebug Flow 显示 ICMP Request 报文到达 FortiGate 后,路由查找网关为 Hub 与 Spoke1 的 IPSec 子隧道 Hub_0 的
tun_id200.1.1.2。该流量最终可以被送入 IPSec 子隧道 Hub_0 送往 Spoke1。
解决方法 3 - 使用 exchange-ip
Spoke IPSec 二阶段仍然配置 0.0.0.0↔︎0.0.0.0 的保护网段,Hub 关闭
add-route功能。在所有 Spoke 上修改 IPSec 一阶段配置,开启
exchange-ip功能,Spoke 与 Hub 进行 IKE 协商时会发送对应隧道已配置的 IPSec Tunnel IP 地址,隧道建立完成后,Hub 将使用 Spoke 发送的 IPSec Tunnel IP 作为对应的隧道 ID(tun_id)。Spoke1: config vpn ipsec phase1-interface edit "Spoke1" set exchange-interface-ip enable next end Spoke2: config vpn ipsec phase1-interface edit "Spoke2" set exchange-interface-ip enable next end重要
在本文“配置信息”章节中已为所有 IPSec Tunnel 接口配置了 IP。
如果 Spoke 的 IPSec Tunnel 接口没有手动配置 IP 或没有通过 mode-cfg 获取 IP,则需要手工指定
exchange-ip要交换的 IP 地址(该地址可自定义,不需要是设备上已存在的地址):config vpn ipsec phase1-interface edit "Spoke2" set exchange-interface-ip enable set exchange-ip-addr4 10.10.10.2 next endIPSec 隧道建立后,查看 Hub 和 Spoke 的 IPSec 一阶段状态中的
tun_id,可以看到 Hub 建立了两个动态子 IPSec 连接(Hub_0 和 Hub_1),Hub 和 Spoke 显示已建立的隧道 ID(tun_id)均为对端公网 IP 地址。Hub # diagnose vpn ike gateway list | grep "name\|tun_id" name: Hub_0 tun_id: 10.10.10.2/::10.0.0.50 <----隧道ID为Spoke1的IPSec Tunnel IP name: Hub_1 tun_id: 10.10.10.3/::10.0.0.53 <----隧道ID为Spoke2的IPSec Tunnel IP在 Hub 上修改去往 Spoke 的静态路由,配置下一跳为 Spoke 对应的 IPSec Tunnel 接口 IP。
重要
出接口为 IPSec Tunnel 时,相关路由的下一跳(gateway)只能在 CLI 下配置。
config router static edit 2 set dst 192.168.1.0 255.255.255.0 set gateway 10.10.10.2 set device "Hub" next edit 3 set dst 192.168.2.0 255.255.255.0 set gateway 10.10.10.3 set device "Hub" next end在 Hub 查看路由表,去往 Spoke 内网的路由下一跳变为 Spoke1 和 Spoke2 的 IPSec Tunnel IP,对应相关已建立的 IPSec 子隧道 Hub_0、Hub_1 的
tun_id。Hub # get router info routing-table all | grep 192.168 C 192.168.0.0/24 is directly connected, port3 S 192.168.1.0/24 [10/0] via Hub tunnel 10.10.10.2, [1/0] S 192.168.2.0/24 [10/0] via Hub tunnel 10.10.10.3, [1/0]使用 PC1(Hub 内网)访问 PC2(Spoke1 内网)或 PC3(Spoke2 内网),可以正常通信。
C:\Users\Administrator.SUMMERICE2019>ipconfig 以太网适配器 Ethernet0: IPv4 地址 . . . . . . . . . . . . : 192.168.0.100 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 192.168.0.1 C:\Users\Administrator.SUMMERICE2019>ping 192.168.1.100 正在 Ping 192.168.1.100 具有 32 字节的数据: 来自 192.168.1.100 的回复: 字节=32 时间=1ms TTL=126 来自 192.168.1.100 的回复: 字节=32 时间=1ms TTL=126 来自 192.168.1.100 的回复: 字节=32 时间=1ms TTL=126 来自 192.168.1.100 的回复: 字节=32 时间=3ms TTL=126 192.168.1.100 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失), 往返行程的估计时间(以毫秒为单位): 最短 = 1ms,最长 = 3ms,平均 = 1msDebug Flow 显示 ICMP Request 报文到达 FortiGate 后,路由查找网关为 Hub 与 Spoke1 的 IPSec 子隧道 Hub_0 的
tun_id200.1.1.2。该流量最终可以被送入 IPSec 子隧道 Hub_0 送往 Spoke1。
Exchange IP 交互报文
IKEv1 主模式:位于第一阶段第 5、6 个报文中
Payload: Notification (11)(Notify Message Type为DOI-specific codes (32249))的Notification DATA。
IKEv1 野蛮模式:位于第一阶段第 2、3 个报文中
Payload: Notification (11)(Notify Message Type为DOI-specific codes (32249))的Notification DATA。
IKEv2:位于 IKE_AUTH 报文中
Payload: Notify (41)(Notify Message Type为Private Use - STATUS TYPES (61689))的Notification DATA。
解决方法 4 - 使用动态路由
使用 BGP 或 OSPF 等动态路由,自动学习去往 Spoke 内网网段的路由会携带对应的tun_id。