VRF

RouterOS 能够为基于 BGP 的 MPLS VPN 创建多个虚拟路由和转发(VRF)实例,允许使用独立的路由表和 IP 前缀隔离。VRF 配置通过 /ip/vrf 菜单管理,路由表映射会自动创建。每个 VRF 最多支持 1024 个实例,并且需要对 VRF-aware 进行显式服务配置

VRF

描述

RouterOS 允许在单台路由器上创建多个虚拟路由和转发(VRF)实例。这对于基于 BGP 的 MPLS VPN 很有价值。与 BGP VPLS(一种 OSI 第 2 层技术)不同,BGP VRF VPN 工作在第 3 层,并在路由器之间交换 IP 前缀。VRF 解决了 IP 前缀重叠的问题,并通过为不同 VPN 使用独立路由来提供所需的隐私性。

你可以设置 VRF-Lite 配置,或使用带 VPNv4 地址族的多协议 BGP 将 VRF 路由表中的路由分发到其他路由器或同一路由器上的不同路由表。

配置

VRF 表在 /ip/vrf 菜单中创建。创建 VRF 配置后,会添加一个路由表映射(会创建一个同名的动态表)。每个活跃的 VRF 始终会有一个映射的路由表。

[admin@arm-bgp] /ip/vrf> print
Flags: X - disabled; * - builtin
 0  * name="main" interfaces=all

[admin@arm-bgp] /routing/table> print
Flags: D - dynamic; X - disabled, I - invalid; U - used
 0 D   name="main" fib

所添加 VRF 的顺序很重要。要正确匹配哪个 interface 属于哪个 VRF,请将 VRF 放在正确的顺序中(匹配从最顶部的条目开始,类似于防火墙规则)。

信息

由于每个 VRF 都有一个映射的路由表,唯一 VRF 的最大数量限制为 1024。 考虑以下示例:

[admin@arm-bgp] /ip/vrf> print
Flags: X - disabled; * - builtin
 0  * name="main" interfaces=all
 1    name="myVrf" interfaces=lo_vrf

由于第一个条目匹配所有 interface,第二个 VRF 没有添加任何 interface。要解决此问题,必须更改条目的顺序。

[admin@arm-bgp] /ip/vrf> move 1 0
[admin@arm-bgp] /ip/vrf> print
Flags: X - disabled; * - builtin
 0    name="myVrf" interfaces=lo_vrf
 1  * name="main" interfaces=all

分配给 VRF 的 interface 上的直连路由会自动安装到相应的路由表中。

信息

当 interface 分配给 VRF 时,直连路由会被添加到 VRF 路由表中。然而,RouterOS 服务不会仅仅因为配置中指定了 IP 地址就自动知道该使用哪个 VRF。每个服务都需要 VRF 支持并进行显式配置。有关某个服务是否具有 VRF 支持以及 VRF 配置选项的信息,请参阅相应的服务文档。 例如,配置 SSH 服务监听属于该 VRF 的 interface 上的连接:

[admin@arm-bgp] /ip/service> set ssh vrf=myVrf
[admin@arm-bgp] /ip/service> print
Flags: X, I - INVALID
Columns: NAME, PORT, CERTIFICATE, VRF
#   NAME     PORT  CERTIFICATE  VRF
0   telnet     23               main
1   ftp        21
2   www        80               main
3   ssh        22               myVrf
4 X www-ssl   443  none         main
5   api      8728               main
6   winbox   8291               main
7   api-ssl  8729  none         main

在 /ip/route 菜单中添加路由时,通过指定 routing-table 参数将路由添加到 VRF,并通过在网关 IP 后附加 @name 来指明在哪个路由表中解析网关:

/ip/route/add dst-address=192.168.1.0/24 gateway=172.16.1.1@myVrf routing-table=myVrf

当网关被显式设置为在另一个 VRF 中解析时,VRF 之间就可能发生流量泄露,例如:

# add route in the myVrf, but resolve the gateway in the main table
/ip/route/add dst-address=192.168.1.0/24 gateway=172.16.1.1@main routing-table=myVrf

# add route in the main table, but resolve the gateway in the myVrf
/ip/route/add dst-address=192.168.1.0/24 gateway=172.16.1.1@myVrf

信息

如果网关配置没有显式配置要在其中解析的表,则网关在 "main" 表中解析。

支持的服务

不同服务可以放置在特定的 VRF 中,服务在该 VRF 中监听传入连接或创建传出连接。默认情况下,所有服务都使用 main 表,但你可以通过单独的 vrf 参数,或在 IP 地址末尾用 "@" 分隔指定 VRF 名称来更改这一点。

以下是支持的服务列表。

服务支持说明
BGP+/routing/bgp/template add name=bgp-template1 vrf=vrf1``/routing/bgp/vpls add name=bgp-vpls1 site-id=10 vrf=vrf1``/routing/bgp/vpn add label-allocation-policy=per-vrf vrf=vrf1
E-mail+/tool/e-mail set address=192.168.88.1 vrf=vrf1
IP Services+telnet、www、ssh、www-ssl、api、winbox、api-ssl 服务支持 VRF。ftp 服务不支持更改 VRF。/ip/service/set telnet vrf=vrf1
L2TP Client+/interface/l2tp-client add connect-to=192.168.88.1@vrf1 name=l2tp-out1 user=l2tp-client
MPLS+/mpls/ldp/add vrf=vrf1
Netwatch+/tool/netwatch/add host=192.168.88.1@vrf1
NTP+/system/ntp/client/set vrf=vrf1``/system/ntp/server/set vrf=vrf1
OSPF+/routing/ospf/instance add disabled=no name=ospf-instance-1 vrf=vrf1
ping+/ping 192.168.88.1 vrf=vrf1
RADIUS+/radius/add address=192.168.88.1@vrf1``/radius/incoming/set vrf=vrf1
RIP+/routing/rip/instance/add name=rip-instance-1 vrf=vrf1
RPKI+/routing/rpki/add vrf=vrf1
SNMP+/snmp/set vrf=vrf1
EoIP+/interface/eoip add remote-address=192.168.1.1@vrf1
IPIP+/interface/ipip/add remote-address=192.168.1.1@vrf1
GRE+/interface/gre/add remote-address=192.168.1.1@vrf1
SSTP-client+/interface/sstp-client/add connect-to=192.168.1.1@vrf1
OVPN server and client+/interface/ovpn-client/add connect-to=192.168.1.1@vrf1
L2TP-ether+/interface/l2tp-ether/add connect-to=192.168.2.2@vrf
VXLAN+/interface/vxlan/add vni=10 vtep-vrf=vrf1
Fetch+/tool/fetch address=10.155.28.236@vrf1 mode=ftp src-path=my_file.pcap user=admin password=""
DNS+ 从 RouterOS v7.21 开始指定路由器将在哪个 VRF 中监听 DNS 查询。/ip/dns/set vrf=vrf1指定使用哪个 VRF 联系上游服务器。/ip/dns/set servers=8.8.8.8@vrf1
DHCP-Relay+ 从 RouterOS v7.15 开始/ip/dhcp-relay/set dhcp-server-vrf=vrf1如果 DHCP 客户端位于同一 VRF 中,则 "ip dhcp-relay" 配置中不需要特殊参数。
Remote logging+ 从 RouterOS v7.19 开始/system/logging/action add name=remote1 remote=192.168.1.1 target=remote vrf=vrf1

防火墙中的 VRF 接口

警告

在 RouterOS v7.14 之前,带有 in/out-interface 属性的防火墙过滤规则将应用于 VRF 实例内的 interface。从 RouterOS v7.14 开始,这些规则不再针对 VRF 内的单个 interface,而是针对作为整体的 VRF interface。 从 RouterOS v7.14 开始,当 interface 被添加到 VRF 时,会自动创建一个虚拟 VRF interface。要匹配属于某个 VRF interface 的流量,请在防火墙过滤器中使用 VRF 虚拟 interface,例如:

/ip/vrf/add interfaces=ether5 name=vrf5
/ip/firewall/filter/add chain=input in-interface=vrf5 action=accept

如果多个 interface 属于同一个 VRF,但你只需要匹配其中的一个 interface,请使用连接标记。例如:

/ip/vrf/add interfaces=ether15,ether16 name=vrf1516
/ip/firewall/mangle
add action=mark-connection chain=prerouting connection-state=new in-interface=ether15 new-connection-mark=input_allow passthrough=yes
/ip/firewall/filter
add action=accept chain=input connection-mark=input_allow

示例

简单的 VRF-Lite 设置

考虑一个场景,其中两个客户 VRF 需要访问互联网:

/ip/address
add address=172.16.1.2/24 interface=public
add address=192.168.1.1/24 interface=ether1
add address=192.168.2.1/24 interface=ether2

/ip/route
add gateway=172.16.1.1

# add VRF configuration
/ip/vrf
add name=cust_a interface=ether1 place-before 0
add name=cust_b interface=ether2 place-before 0

# add vrf routes
/ip/route
add gateway=172.16.1.1@main routing-table=cust_a
add gateway=172.16.1.1@main routing-table=cust_b

# masquerade local source
/ip/firewall/nat/add chain=srcnat out-interface=public action=masquerade

可能有必要确保到达 "public" interface 的数据包进入正确的 VRF。 这可以通过标记源自 VRF 客户的新连接,并根据 "public" interface 上入站数据包的路由标记来引导流量来解决。

# mark new customer connections
/ip/firewall/mangle
add action=mark-connection chain=prerouting connection-state=new new-connection-mark=\
    cust_a_conn src-address=192.168.1.0/24 passthrough=no
add action=mark-connection chain=prerouting connection-state=new new-connection-mark=\
    cust_b_conn src-address=192.168.2.0/24 passthrough=no

# mark routing
/ip/firewall/mangle
add action=mark-routing chain=prerouting connection-mark=cust_a_conn \
    in-interface=public new-routing-mark=cust_a
add action=mark-routing chain=prerouting connection-mark=cust_b_conn \
    in-interface=public new-routing-mark=cust_b

静态 Inter-VRF 路由

通常,你应通过 BGP 本地导入和导出功能在 VRF 之间交换所有路由。如果这还不够,请使用静态路由来实现这种所谓的路由泄露。

你可以通过两种方式安装一条网关位于与路由本身不同的路由表中的路由。

第一种方式是在添加路由时在网关字段中显式指定路由表。这仅在将路由和网关从 "main" 路由表泄露到不同路由表(VRF)时可行。示例:

# add route to 5.5.5.0/24 in 'vrf1' routing table with gateway in the main routing table
add dst-address=5.5.5.0/24 gateway=10.3.0.1@main routing-table=vrf1

第二种方式是在网关字段中显式指定 interface。指定的 interface 可以属于某个 VRF 实例。示例:

# add route to 5.5.5.0/24 in the main routing table with gateway at 'ether2' VRF interface
add dst-address=5.5.5.0/24 gateway=10.3.0.1%ether2 routing-table=main
# add route to 5.5.5.0/24 in the main routing table with 'ptp-link-1' VRF interface as gateway
add dst-address=5.5.5.0/24 gateway=ptp-link-1 routing-table=main

如你所见,有两种可能的变化——将网关指定为 ip_address%interface,或者简单地指定一个 interface。在大多数情况下,对广播 interface 使用第一种。对点到点 interface 使用第二种;如果路由是某个 VRF 中的直连路由,对广播 interface 也可使用第二种。例如,如果你在 interface ether2 上有一个 1.2.3.4/24 地址,而该 interface 被放入某个 VRF,那么该 VRF 的路由表中就存在一条到 1.2.3.0/24 的直连路由。即使 ether2 是广播 interface,也可以在不同路由表中添加一条仅以 interface 为网关的静态路由 1.2.3.0/24:

add dst-address=1.2.3.0/24 gateway=ether2 routing-table=main

静态 VRF-Lite 直连路由泄露

有时需要从另一个 VRF 访问直连资源。在此示例中,两个直连网络各自位于自己的 VRF 中,你希望允许 client1 访问 client2。

graph LR
    client1["client1
ip (*.2)"] -->|10.11.0.0/24| vrf1["vrf1
ip (*.1)"]
    vrf2["vrf2
ip (*.1)"] -->|10.12.0.0/24| client2["client2
ip (*.2)"]
    subgraph Router["Router"]
        direction LR
        vrf1 ~~~ vrf2
    end
/ip/address
add address=10.11.0.1/24 interface=sfp-sfpplus1
add address=10.12.0.1/24 interface=sfp-sfpplus2

# add VRF configuration
/ip/vrf
add name=vrfTest1 interface=sfp-sfpplus1 place-before 0
add name=vrfTest2 interface=sfp-sfpplus2 place-before 0

通过将网关设置为 "interface@vrf",可以在特定 VRF 上到达直连网络。

# add vrf routes
/ip/route
add dst-address=10.11.0.0/24 gateway="sfp-sfpplus1@vrfTest1" routing-table=vrfTest2
add dst-address=10.12.0.0/24 gateway="sfp-sfpplus2@vrfTest2" routing-table=vrfTest1

验证路由和可达性

[admin@CCR2004_2XS] /ip/route> print detail
Flags: D - dynamic; X - disabled, I - inactive, A - active;
c - connect, s - static, r - rip, b - bgp, o - ospf, i - is-is, d - dhcp, v - vpn, m - modem, y - bgp-mpls-vpn; H - hw-offloaded; + - ecmp

    DAc   dst-address=10.11.0.0/24 routing-table=vrfTest1 gateway=sfp-sfpplus1@vrfTest1 immediate-gw=sfp-sfpplus1 distance=0 scope=10 suppress-hw-offload=no
          local-address=10.11.0.1%sfp-sfpplus1@vrfTest1

 1  As   dst-address=10.12.0.0/24 routing-table=vrfTest1 pref-src="" gateway=vrfTest2 immediate-gw=vrfTest2 distance=1 scope=30 target-scope=10
          suppress-hw-offload=no

 2  As   dst-address=10.11.0.0/24 routing-table=vrfTest2 pref-src="" gateway=vrfTest1 immediate-gw=vrfTest1 distance=1 scope=30 target-scope=10
          suppress-hw-offload=no

    DAc   dst-address=10.12.0.0/24 routing-table=vrfTest2 gateway=sfp-sfpplus2@vrfTest2 immediate-gw=sfp-sfpplus2 distance=0 scope=10 suppress-hw-offload=no
          local-address=10.12.0.1%sfp-sfpplus2@vrfTest2

[admin@cl2] > /ping 10.11.0.2 src-address=10.12.0.2
  SEQ HOST                                     SIZE TTL TIME       STATUS
    0 10.11.0.2                                 56  64 67us
    1 10.11.0.2                                 56  64 61us
    sent=2 received=2 packet-loss=0% min-rtt=61us avg-rtt=64u

警告

尝试泄露重叠网络不会生效。 在另一个 VRF 中访问路由器的本地地址

[admin@cl2] > /ping 10.11.0.1 src-address=10.12.0.2
  SEQ HOST                                     SIZE TTL TIME       STATUS
    0 10.11.0.1                                                   timeout
    1 10.11.0.1                                                   timeout
    sent=2 received=0 packet-loss=100%

使用 "interface@vrf" 网关的方法仅在路由器转发数据包时有效。要访问本地 VRF 地址,你需要路由到 VRF interface。

# add vrf routes
/ip/route
add dst-address=10.11.0.0/24 gateway=vrfTest1@vrfTest1 routing-table=vrfTest2
add dst-address=10.12.0.0/24 gateway=vrfTest2@vrfTest2 routing-table=vrfTest1

[admin@cl2] > /ping 10.11.0.1 src-address=10.12.0.2
  SEQ HOST                                     SIZE TTL TIME       STATUS
    0 10.11.0.1                                 56  64 67us
    1 10.11.0.1                                 56  64 61us
    sent=2 received=2 packet-loss=0% min-rtt=61us avg-rtt=64u

动态 VRF-Lite 路由泄露

在足够大型的设置中,静态路由泄露不够用。为便于演示,考虑与静态路由泄露示例相同的设置,外加 IPv6 地址。

/ip/address
add address=10.11.0.1/24 interface=sfp-sfpplus1
add address=10.12.0.1/24 interface=sfp-sfpplus2

# add VRF configuration
/ip/vrf
add name=vrfTest1 interface=sfp-sfpplus1 place-before 0
add name=vrfTest2 interface=sfp-sfpplus2 place-before 0

/ipv6/address
add address=2001:1::1 advertise=no interface=sfp-sfpplus1
add address=2001:2::1 advertise=no interface=sfp-sfpplus2

你可以使用 BGP VPN 泄露本地路由,而无需实际建立 BGP 会话。

/routing/bgp/vpn
add export.redistribute=connected .route-targets=1:1 import.route-targets=1:2 label-allocation-policy=per-vrf name=bgp-mpls-vpn-1 \
    route-distinguisher=1.2.3.4:1 vrf=vrfTest1
add export.redistribute=connected .route-targets=1:2 import.route-targets=1:1 label-allocation-policy=per-vrf name=bgp-mpls-vpn-2 \
    route-distinguisher=1.2.3.4:1 vrf=vrfTest2

警告

请小心处理导入/导出 route target。如果设置不当,来自同一 VRF 的本地 VRF 路由将被导入。 VRF 之间的直连路由现在已交换。

[admin@CCR2004_2XS] > /routing/route/print where dst-address in 111.0.0.0/8 && afi=ip4
...
 Ac   afi=ip4 contribution=active dst-address=111.11.0.0/24 routing-table=vrfTest1 gateway=sfp-sfpplus1@vrfTest1 immediate-gw=sfp-sfpplus1 distance=0 scope=10
       belongs-to="connected" local-address=111.11.0.1%sfp-sfpplus1@vrfTest1
       debug.fwp-ptr=0x202421E0
 Ay   afi=ip4 contribution=best-candidate dst-address=111.12.0.0/24 routing-table=vrfTest1 label=17 gateway=vrfTest2@vrfTest2 immediate-gw=sfp-sfpplus2
       distance=200 scope=40 target-scope=10 belongs-to="bgp-mpls-vpn-1-bgp-mpls-vpn-2-connected-export-import"
       bgp.ext-communities=rt:1:2 .atomic-aggregate=no .origin=incomplete
       debug.fwp-ptr=0x202425A0
 Ay   afi=ip4 contribution=best-candidate dst-address=111.11.0.0/24 routing-table=vrfTest2 label=16 gateway=vrfTest1@vrfTest1 immediate-gw=sfp-sfpplus1
       distance=200 scope=40 target-scope=10 belongs-to="bgp-mpls-vpn-2-bgp-mpls-vpn-1-connected-export-import"
       bgp.ext-communities=rt:1:1 .atomic-aggregate=no .origin=incomplete
       debug.fwp-ptr=0x202424E0
 Ac   afi=ip4 contribution=active dst-address=111.12.0.0/24 routing-table=vrfTest2 gateway=sfp-sfpplus2@vrfTest2 immediate-gw=sfp-sfpplus2 distance=0 scope=10
       belongs-to="connected" local-address=111.12.0.1%sfp-sfpplus2@vrfTest2
       debug.fwp-ptr=0x20242240

IPv6 也是如此:

[admin@CCR2004_2XS] /routing/route> print detail where dst-address in 2001::/8 && afi=ip6
...
 Ac   afi=ip6 contribution=active dst-address=2001:1::/64 routing-table=vrfTest1 gateway=sfp-sfpplus1@vrfTest1 immediate-gw=sfp-sfpplus1 distance=0 scope=10
       belongs-to="connected" local-address=2001:1::1%sfp-sfpplus1@vrfTest1
       debug.fwp-ptr=0x20242300
 Ay   afi=ip6 contribution=active dst-address=2001:2::/64 routing-table=vrfTest1 label=17 gateway=vrfTest2@vrfTest2 immediate-gw=sfp-sfpplus2 distance=200
       scope=40 target-scope=10 belongs-to="bgp-mpls-vpn-1-bgp-mpls-vpn-2-connected-export-import"
       bgp.ext-communities=rt:1:2 .atomic-aggregate=no .origin=incomplete
       debug.fwp-ptr=0x202425A0
 Ay   afi=ip6 contribution=active dst-address=2001:1::/64 routing-table=vrfTest2 label=16 gateway=vrfTest1@vrfTest1 immediate-gw=sfp-sfpplus1 distance=200
       scope=40 target-scope=10 belongs-to="bgp-mpls-vpn-2-bgp-mpls-vpn-1-connected-export-import"
       bgp.ext-communities=rt:1:1 .atomic-aggregate=no .origin=incomplete
       debug.fwp-ptr=0x202424E0
 Ac   afi=ip6 contribution=active dst-address=2001:2::/64 routing-table=vrfTest2 gateway=sfp-sfpplus2@vrfTest2 immediate-gw=sfp-sfpplus2 distance=0 scope=10
       belongs-to="connected" local-address=2001:2::1%sfp-sfpplus2@vrfTest2
       debug.fwp-ptr=0x20242360

动态 VRF-Lite 路由泄露(旧变通方法)

在 RouterOS v7.14 之前,不存在将路由从一个 VRF 实例泄露到同一路由器上另一个 VRF 实例的机制。

作为一种变通方法,可以在两个本地配置的 loopback 地址之间创建一条隧道,并将每个隧道端点分配到自己的 VRF。动态路由协议或静态路由随后就可以在两个 VRF 之间泄露路由。

这种方法的缺点是,必须在每一对需要泄露路由的 VRF 之间创建隧道(形成全互联),即使只有少数几个 VRF,也会显著增加配置的复杂性,更不用说更复杂的设置了。

例如,在 5 个 VRF 之间泄露路由需要 n × (n − 1) / 2 条连接,这会导致在一台路由器上出现 20 个隧道端点和 20 个 OSPF 实例。

包含两个 VRF 的配置示例:

/interface/bridge
add name=dummy_custC
add name=dummy_custB
add name=lo1
add name=lo2

/ip/address
add address=111.255.255.1 interface=lo1 network=111.255.255.1
add address=111.255.255.2 interface=lo2 network=111.255.255.2
add address=172.16.1.0/24 interface=dummy_custC network=172.16.1.0
add address=172.16.2.0/24 interface=dummy_custB network=172.16.2.0

/interface/ipip
add local-address=111.255.255.1 name=ipip-tunnel1 remote-address=111.255.255.2
add local-address=111.255.255.2 name=ipip-tunnel2 remote-address=111.255.255.1

/ip/address
add address=192.168.1.1/24 interface=ipip-tunnel1 network=192.168.1.0
add address=192.168.1.2/24 interface=ipip-tunnel2 network=192.168.1.0

/ip/vrf
add interfaces=ipip-tunnel1,dummy_custC name=custC
add interfaces=ipip-tunnel2,dummy_custB name=custB

/routing/ospf/instance
add disabled=no name=i2_custB redistribute=connected,static,copy router-id=192.168.1.1 routing-table=custB vrf=custB
add disabled=no name=i2_custC redistribute=connected router-id=192.168.1.2 routing-table=custC vrf=custC
/routing/ospf/area
add disabled=no instance=i2_custB name=custB_bb
add disabled=no instance=i2_custC name=custC_bb
/routing/ospf/interface-template
add area=custB_bb disabled=no networks=192.168.1.0/24
add area=custC_bb disabled=no networks=192.168.1.0/24

结果

[admin@rack1_b36_CCR1009] /routing/ospf/neighbor> print
Flags: V - virtual; D - dynamic
 0  D instance=i2_custB area=custB_bb address=192.168.1.1 priority=128 router-id=192.168.1.2 dr=192.168.1.1 bdr=192.168.1.2
      state="Full" state-changes=6 adjacency=41m28s timeout=33s

 1  D instance=i2_custC area=custC_bb address=192.168.1.2 priority=128 router-id=192.168.1.1 dr=192.168.1.1 bdr=192.168.1.2
      state="Full" state-changes=6 adjacency=41m28s timeout=33s

[admin@rack1_b36_CCR1009] /ip/route> print where routing-table=custB
Flags: D - DYNAMIC; A - ACTIVE; c, s, o, y - COPY
Columns: DST-ADDRESS, GATEWAY, DISTANCE
     DST-ADDRESS       GATEWAY                         DISTANCE
  DAo 172.16.1.0/24     192.168.1.1%ipip-tunnel2@custB       110
  DAc 172.16.2.0/24     dummy_custB@custB                      0
  DAc 192.168.1.0/24    ipip-tunnel2@custB                     0

[admin@rack1_b36_CCR1009] > /ip/route/print where routing-table=custC
Flags: D - DYNAMIC; A - ACTIVE; c, o, y - COPY
Columns: DST-ADDRESS, GATEWAY, DISTANCE
    DST-ADDRESS       GATEWAY                         DISTANCE
  DAc 172.16.1.0/24     dummy_custC@custC                      0
  DAo 172.16.2.0/24     192.168.1.2%ipip-tunnel1@custC       110
  DAc 192.168.1.0/24    ipip-tunnel1@custC                     0

最简单的 MPLS VPN 设置

在此示例中,创建了一个基本的 MPLS backbone(由两台 Provider Edge(PE)路由器 PE1 和 PE2 组成),并配置为在属于 cust-one VPN 的 Customer Edge(CE)路由器 CE1 和 CE2 之间转发流量。

CE1 Router

/ip/address/add address=10.1.1.1/24 interface=ether1
# use static routing
/ip/route/add dst-address=10.3.3.0/24 gateway=10.1.1.2

CE2 Router

/ip/address/add address=10.3.3.4/24 interface=ether1
/ip/route/add dst-address=10.1.1.0/24 gateway=10.3.3.3

PE1 Router

/interface/bridge/add name=lobridge
/ip/address/add address=10.1.1.2/24 interface=ether1
/ip/address/add address=10.2.2.2/24 interface=ether2
/ip/address/add address=10.5.5.2/32 interface=lobridge
/ip/vrf/add name=cust-one interfaces=ether1
/mpls/ldp/add enabled=yes transport-address=10.5.5.2 lsr-id=10.5.5.2
/mpls/ldp/interface/add interface=ether2
/routing/bgp/template/set default as=65000

/routing/bgp/vpn
add vrf=cust-one \
  route-distinguisher=1.1.1.1:111 \
  import.route-targets=1.1.1.1:111 \
  import.router-id=cust-one \
  export.redistribute=connected \
  export.route-targets=1.1.1.1:111 \
  label-allocation-policy=per-vrf
/routing/bgp/connection
add template=default remote.address=10.5.5.3 address-families=vpnv4 local.address=10.5.5.2

# add route to the remote BGP peer's loopback address
/ip/route/add dst-address=10.5.5.3/32 gateway=10.2.2.3

PE2 Router (Cisco)

ip vrf cust-one
rd 1.1.1.1:111
route-target export 1.1.1.1:111
route-target import 1.1.1.1:111
exit

interface Loopback0
ip address 10.5.5.3 255.255.255.255

mpls ldp router-id Loopback0 force
mpls label protocol ldp

interface FastEthernet0/0
ip address 10.2.2.3 255.255.255.0
mpls ip

interface FastEthernet1/0
ip vrf forwarding cust-one
ip address 10.3.3.3 255.255.255.0

router bgp 65000
neighbor 10.5.5.2 remote-as 65000
neighbor 10.5.5.2 update-source Loopback0
address-family vpnv4
neighbor 10.5.5.2 activate
neighbor 10.5.5.2 send-community both
exit-address-family
address-family ipv4 vrf cust-one
redistribute connected
exit-address-family

ip route 10.5.5.2 255.255.255.255 10.2.2.2

结果

检查 VPNv4 路由重分发是否正常工作:

[admin@PE1] /routing/route> print detail where afi="vpn4"
Flags: X - disabled, F - filtered, U - unreachable, A - active;
c - connect, s - static, r - rip, b - bgp, o - ospf, d - dhcp, v - vpn, m - modem, a - ldp-address, l - l
dp-mapping, g - slaac, y - bgp-mpls-vpn;
H - hw-offloaded; + - ecmp, B - blackhole
 Ab   afi=vpn4 contribution=active dst-address=111.16.0.0/24&1.1.1.1:111 routing-table=main label=16
       gateway=111.111.111.4 immediate-gw=111.13.0.2%ether9 distance=200 scope=40 target-scope=30
       belongs-to="bgp-VPN4-111.111.111.4"
       bgp.peer-cache-id=*2C00011 .as-path="65511" .ext-communities=rt:1.1.1.1:111 .local-pref=100
       .atomic-aggregate=yes .origin=igp
       debug.fwp-ptr=0x202427E0

[admin@PE1] /routing/bgp/advertisements> print
 0 peer=to-pe2-1 dst=10.1.1.0/24 local-pref=100 origin=2 ext-communities=rt:1.1.1.1:111 atomic-aggregate=yes

检查 10.3.3.0 是否安装在 cust-one 路由表的 IP 路由中:

[admin@PE1] > /ip/route/print where routing-table="cust-one"
Flags: D - DYNAMIC; A - ACTIVE; c, b, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
# DST-ADDRESS     GATEWAY         DISTANCE
0 ADC 10.1.1.0/24 ether1@cust-one        0
1 ADb 10.3.3.0/24 10.5.5.3              20

近距离查看 cust-one VRF 中的 IP 路由可以看出,10.1.1.0/24 IP 前缀是配置为属于 cust-one VRF 的 interface 上的一条直连路由。10.3.3.0/24 IP 前缀由 PE2 以 VPNv4 路由的形式通过 BGP 通告,并被导入此 VRF 路由表,因为配置的 import.route-targets 与其通告时携带的 BGP 扩展团体属性相匹配。

[admin@PE1] /routing/route> print detail where routing-table="cust-one"
Flags: X - disabled, F - filtered, U - unreachable, A - active;
c - connect, s - static, r - rip, b - bgp, o - ospf, d - dhcp, v - vpn, m - modem, a - ldp-address, l - l
dp-mapping, g - slaac, y - bgp-mpls-vpn;
H - hw-offloaded; + - ecmp, B - blackhole
  Ay   afi=ip4 contribution=active dst-address=10.1.1.0/24 routing-table=cust-one
        gateway=ether1@cust-one immediate-gw=ether1 distance=0 scope=10 belongs-to="connected"
        local-address=10.1.1.2%ether1@cust-one
        debug.fwp-ptr=0x202420C0

 Ay   afi=ip4 contribution=active dst-address=10.3.3.0/24 routing-table=cust-one label=16
        gateway=10.5.5.3 immediate-gw=10.2.2.3%ether2 distance=20 scope=40 target-scope=30
        belongs-to="bgp-mpls-vpn-1-bgp-VPN4-10.5.5.3-import"
        bgp.peer-cache-id=*2C00011 .ext-communities=rt:1.1.1.1:111 .local-pref=100
        .atomic-aggregate=yes .origin=igp
        debug.fwp-ptr=0x20242840

[admin@PE1] /routing/route> print detail where afi="vpn4"
Flags: X - disabled, F - filtered, U - unreachable, A - active;
c - connect, s - static, r - rip, b - bgp, o - ospf, d - dhcp, v - vpn, m - modem, a - ldp-address, l - l
dp-mapping, g - slaac, y - bgp-mpls-vpn;
H - hw-offloaded; + - ecmp, B - blackhole
 Ay   afi=vpn4 contribution=active dst-address=10.1.1.0/24&1.1.1.1:111 routing-table=main label=19
        gateway=ether1@cust-one immediate-gw=ether1 distance=200 scope=40 target-scope=10
        belongs-to="bgp-mpls-vpn-1-connected-export"
        bgp.ext-communities=rt:1.1.1.1:111 .atomic-aggregate=no .origin=incomplete
        debug.fwp-ptr=0x202426C0

 Ab   afi=vpn4 contribution=active dst-address=10.3.3.0/24&1.1.1.1:111 routing-table=main label=16
        gateway=10.5.5.3 immediate-gw=10.2.2.3%ether2 distance=200 scope=40 target-scope=30
        belongs-to="bgp-VPN4-10.5.5.3"
        bgp.peer-cache-id=*2C00011 .ext-communities=rt:1.1.1.1:111 .local-pref=100
        .atomic-aggregate=yes .origin=igp
        debug.fwp-ptr=0x202427E0

Cisco 上也是如此:

PE2#show ip bgp vpnv4 all
BGP table version is 5, local router ID is 10.5.5.3
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
r RIB-failure, S Stale
Origin codes: i - IGP, e - EGP, ? - incomplete
Network Next Hop Metric LocPrf Weight Path
Route Distinguisher: 1.1.1.1:111 (default for vrf cust-one)
*>i10.1.1.0/24 10.5.5.2 100 0 ?
*> 10.3.3.0/24 0.0.0.0 0 32768 ?

PE2#show ip route vrf cust-one
Routing Table: cust-one
Codes: C - connected, S - static, R - RIP, M - mobile, B - BGP
D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2
i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
ia - IS-IS inter area, * - candidate default, U - per-user static route
o - ODR, P - periodic downloaded static route

Gateway of last resort is not set
10.0.0.0/24 is subnetted, 1 subnets
B 10.1.1.0 [200/0] via 10.5.5.2, 00:05:33
10.0.0.0/24 is subnetted, 1 subnets
C 10.3.3.0 is directly connected, FastEthernet1/0

你现在应该能够从 CE1 ping 通 CE2,反之亦然。

[admin@CE1] > /ping 10.3.3.4
10.3.3.4 64 byte ping: ttl=62 time=18 ms
10.3.3.4 64 byte ping: ttl=62 time=13 ms
10.3.3.4 64 byte ping: ttl=62 time=13 ms
10.3.3.4 64 byte ping: ttl=62 time=14 ms
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 13/14.5/18 ms

更复杂的设置(仅变更部分)

与最简单的设置不同,此示例有两个客户:cust-one 和 cust-two。

为它们配置了两个 VPN——cust-one 和 cust-two——并且所有路由都在它们之间交换(这也称为 "route leaking")。

这不是最典型的设置,因为路由通常不会在不同客户之间交换。默认情况下,会阻止从一个 VRF 站点访问另一个 VPN 中的不同 VRF 站点(这就是 VPN 的 "Private" 特性)。独立路由提供了隐私性,也是解决 IP 网络前缀重叠问题所必需的。路由交换与这两项要求直接冲突,但有时可能是需要的(例如,当两个客户迁移到单一网络基础设施时的临时解决方案)。

CE1 Router, cust-one

/ip/route/add dst-address=10.4.4.0/24 gateway=10.1.1.2

CE2 Router, cust-one

/ip/route/add dst-address=10.4.4.0/24 gateway=10.3.3.3

CE1 Router, cust-two

/ip/address/add address=10.4.4.5 interface=ether1
/ip/route/add dst-address=10.1.1.0/24 gateway=10.3.3.3
/ip/route/add dst-address=10.3.3.0/24 gateway=10.3.3.3

PE1 Router

# replace the old BGP VPN with this:
/routing/bgp/vpn
add vrf=cust-one \
  export.redistribute=connected \
  route-distinguisher=1.1.1.1:111 \
  import.route-targets=1.1.1.1:111,2.2.2.2:222  \
  export.route-targets=1.1.1.1:111

PE2 Router (Cisco)

ip vrf cust-one
rd 1.1.1.1:111
route-target export 1.1.1.1:111
route-target import 1.1.1.1:111
route-target import 2.2.2.2:222
exit

ip vrf cust-two
rd 2.2.2.2:222
route-target export 2.2.2.2:222
route-target import 1.1.1.1:111
route-target import 2.2.2.2:222
exit

interface FastEthernet2/0
ip vrf forwarding cust-two
ip address 10.4.4.3 255.255.255.0

router bgp 65000
address-family ipv4 vrf cust-two
redistribute connected
exit-address-family

变体:用另一台 MikroTik 替换 Cisco

PE2 MikroTik 配置

/interface/bridge/add name=lobridge
/ip/address
add address=10.2.2.3/24 interface=ether1
add address=10.3.3.3/24 interface=ether2
add address=10.4.4.3/24 interface=ether3
add address=10.5.5.3/32 interface=lobridge
/ip/vrf
add name=cust-one interfaces=ether2
add name=cust-two interfaces=ether3
/mpls/ldp/add enabled=yes transport-address=10.5.5.3
/mpls/ldp/interface/add interface=ether1

/routing/bgp/template/set default as=65000
/routing/bgp/vpn
add vrf=cust-one \
  export.redistribute=connected \
  route-distinguisher=1.1.1.1:111 \
  import.route-targets=1.1.1.1:111,2.2.2.2:222 \
  export.route-targets=1.1.1.1:111 \
add vrf=cust-two \
  export.redistribute=connected \
  route-distinguisher=2.2.2.2:222 \
  import.route-targets=1.1.1.1:111,2.2.2.2:222 \
  export.route-targets=2.2.2.2:222 \

/routing/bgp/connection
add template=default remote.address=10.5.5.2 address-families=vpnv4 local.address=10.5.5.3

# add route to the remote BGP peer's loopback address
/ip/route/add dst-address=10.5.5.2/32 gateway=10.2.2.2

结果

/ip/route/print 的输出足够有趣,值得详细观察。

[admin@PE2] /ip/route> print
Flags: X - disabled, A - active, D - dynamic,
C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme,
B - blackhole, U - unreachable, P - prohibit
# DST-ADDRESS PREF-SRC GATEWAY DISTANCE
0 ADb 10.1.1.0/24 10.5.5.2 recurs... 20
1 ADC 10.3.3.0/24 10.3.3.3 ether2 0
2 ADb 10.4.4.0/24 20
3 ADb 10.1.1.0/24 10.5.5.2 recurs... 20
4 ADb 10.3.3.0/24 20
5 ADC 10.4.4.0/24 10.4.4.3 ether3 0
6 ADC 10.2.2.0/24 10.2.2.3 ether1 0
7 A S 10.5.5.2/32 10.2.2.2 reacha... 1
8 ADC 10.5.5.3/32 10.5.5.3 lobridge 0

路由 10.1.1.0/24 是从远程 BGP 对等体收到的,并安装在两个 VRF 路由表中。

路由 10.3.3.0/24 和 10.4.4.0/24 也安装在两个 VRF 路由表中。每条在一个表中是直连路由,在另一个表中是 BGP 路由。这与它们是否通过 BGP 通告无关。它们只是被 "通告" 到本地 VPNv4 路由表,然后又被本地重新导入。导入和导出 route-target 决定它们最终出现在哪些表中。

从它们的属性可以明显看出这一点——它们没有通常的 BGP 属性(路由 10.4.4.0/24)。

[admin@PE2] /routing/route> print detail where routing-table=cust-one
...

每站点唯一 RD 与每客户唯一 RD

考虑一个 BGP VPN 设置,其中两个 CUST_A 站点通告相同的网络(111.12.0.0/24)。

graph LR
    CUST_A1["client1"] ---|10.12.0.0/24| cust_a1_vrf["cust_avrf"] ---|BGP VPN| cust_a2_vrf["cust_avrf"] ---|10.12.0.0/24| CUST_A2["client2"]
    subgraph PE1["PE1"]
        direction LR
        cust_a1_vrf
    end
    subgraph PE2["PE2"]
        direction LR
        cust_a2_vrf
    end
    PE1 --- PE2

处理此类设置有两种方式:

  • 每客户(Router 1 上的 CUST_A VPN 与 Router 2 上的 CUST_A VPN 具有相同的 route distinguisher(假设 RD=1:1))。
  • 每站点(每个 CUST_A 站点都有唯一的 Route Distinguisher)。

在第一种情况下,Router1 上的 CUST_A 站点导出了 VPNv4 路由并将其通告给远程 PE。

 Ay   afi=vpnv4 contribution=active dst-address=111.12.0.0/24&1:1 routing-table=main label=16 gateway=vrf-dummy@vrfTest
       immediate-gw=vrf-dummy distance=200 scope=40 target-scope=10 belongs-to="bgp-mpls-vpn-1-connected-export"
       bgp.ext-communities=rt:1:1 .origin=incomplete
       debug.fwp-ptr=0x20302600

Router2 上的 CUST_A 站点也导出 VPNv4 路由,并且也收到了来自另一个站点的 VPNv4 路由:

 Ab + afi=vpnv4 contribution=active dst-address=111.12.0.0/24&1:1 routing-table=main label=16 gateway=111.11.0.2
       immediate-gw=111.11.0.2%sfp-sfpplus1 distance=200 scope=40 target-scope=30 belongs-to="bgp-VPNv4-111.11.0.2"
       bgp.session=to-tested-1 .ext-communities=rt:1:1 .local-pref=100 .origin=igp
       debug.fwp-ptr=0x203421E0

 Ay + afi=vpnv4 contribution=active dst-address=111.12.0.0/24&1:1 routing-table=main label=32 gateway=vrf-dummy@vrfTest
       immediate-gw=vrf-dummy distance=200 scope=40 target-scope=10 belongs-to="bgp-mpls-vpn-1-connected-export"
       bgp.ext-communities=rt:1:1 .origin=incomplete
       debug.fwp-ptr=0x20342540

RouterOS 只通告一条最佳路由。在 ECMP 的情况下,默认会从列表中选择第一条,而它恰好是从远程站点收到的 BGP VPNv4 路由,因此远程站点将不会收到第二条路由。这会导致一种情况:一个站点在 VRF 中有两条冗余路由,而另一个站点没有。在 VRF 没有安装 VPN 路由且本地路由变为非活跃的站点上,BGP 需要发送 withdraw、重新计算 main 表、从远程站点接收更新,并将新的最佳路由导入 CUST_A VRF,从而导致收敛变慢。

此行为可以通过路由选择过滤器来更改,但这超出了本示例的范围。

如果导出到 VPNv4 的路由同时也是从客户 CE-PE 会话收到的 BGP 路由,也会出现类似情况,只不过此时对 VPNv4 路由应用的是 BGP 最佳路径选择过程而不是 ECMP,并且只选择一条最佳路由。

如果为每个站点分配唯一的 route distinguisher(例如,第一个站点为 1.1.1.1:1,第二个站点为 1.1.1.2:1),则 VPNv4 路由不再受选择过程影响,因为它们现在被视为唯一目标,并且两条都会被导入 VRF。

Router1 上的 CUST_A

[admin@CCR2004_2XS_111] /routing/route> print detail
...
 Ay   afi=vpnv4 contribution=active dst-address=111.12.0.0/24&1.1.1.1:1 routing-table=main label=17 gateway=vrf-dummy@vrfTest
       immediate-gw=vrf-dummy distance=200 scope=40 target-scope=10 belongs-to="bgp-mpls-vpn-1-connected-export"
       bgp.ext-communities=rt:1:1 .origin=incomplete
       debug.fwp-ptr=0x20302240

 Ab   afi=vpnv4 contribution=active dst-address=111.12.0.0/24&1.1.1.2:1 routing-table=main label=33 gateway=111.11.0.1
       immediate-gw=111.11.0.1%sfp-sfpplus1 distance=200 scope=40 target-scope=30 belongs-to="bgp-VPNv4-111.11.0.1"
       bgp.session=to-tester-1 .ext-communities=rt:1:1 .local-pref=100 .origin=igp
       debug.fwp-ptr=0x20302480

[admin@CCR2004_2XS_111] /ip/route> print
...
  DAc  111.12.0.0/24       vrf-dummy@vrfTest  vrfTest               0
  D y  111.12.0.0/24       111.11.0.1         vrfTest             200

Router2 上的 CUST_A

[admin@CCR2004_2XS_111] /routing/route> print detail
...
 Ab   afi=vpnv4 contribution=active dst-address=111.12.0.0/24&1.1.1.1:1 routing-table=main label=17 gateway=111.11.0.2
       immediate-gw=111.11.0.2%sfp-sfpplus1 distance=200 scope=40 target-scope=30 belongs-to="bgp-VPNv4-111.11.0.2"
       bgp.session=to-tested-1 .ext-communities=rt:1:1 .local-pref=100 .origin=igp
       debug.fwp-ptr=0x203421E0

 Ay   afi=vpnv4 contribution=active dst-address=111.12.0.0/24&1.1.1.2:1 routing-table=main label=33 gateway=vrf-dummy@vrfTest
       immediate-gw=vrf-dummy distance=200 scope=40 target-scope=10 belongs-to="bgp-mpls-vpn-1-connected-export"
       bgp.ext-communities=rt:1:1 .origin=incomplete
       debug.fwp-ptr=0x20342240

[admin@CCR2004_2XS_111] /ip/route> print
...
 DAc 111.12.0.0/24       vrf-dummy@vrfTest  vrfTest               0
 D y 111.12.0.0/24       111.11.0.2         vrfTest             200

在此情况下,与之前的设置相比,当来自任一站点的本地路由变为非活跃时,会立即切换到备用路由。

参考资料

RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs)

MPLS Fundamentals, chapter 7, Luc De Ghein, Cisco Press 2006