虚拟路由和转发 (VRF)

VRF 设备

VRF 设备与 ip 规则相结合,提供了在 Linux 网络堆栈中创建虚拟路由和转发域(也称为 VRF,具体来说是 VRF-lite)的能力。一个用例是多租户问题,其中每个租户都有自己唯一的路由表,并且至少需要不同的默认网关。

进程可以通过将套接字绑定到 VRF 设备来实现“VRF 感知”。然后,通过套接字的数据包使用与 VRF 设备关联的路由表。VRF 设备实现的一个重要特性是它只影响第 3 层及以上,因此 L2 工具(例如 LLDP)不受影响(即,它们不需要在每个 VRF 中运行)。该设计还允许使用更高优先级的 ip 规则(基于策略的路由,PBR)优先于 VRF 设备规则,从而按需定向特定流量。

此外,VRF 设备允许 VRF 嵌套在命名空间中。例如,网络命名空间在设备层提供网络接口的分离,命名空间内接口上的 VLAN 提供 L2 分离,然后 VRF 设备提供 L3 分离。

设计

使用关联的路由表创建 VRF 设备。然后将网络接口绑定到 VRF 设备

+-----------------------------+
|           vrf-blue          |  ===> route table 10
+-----------------------------+
   |        |            |
+------+ +------+     +-------------+
| eth1 | | eth2 | ... |    bond1    |
+------+ +------+     +-------------+
                         |       |
                     +------+ +------+
                     | eth8 | | eth9 |
                     +------+ +------+

在 IPv4 和 IPv6 处理堆栈中,在绑定的设备上接收的数据包被切换到 VRF 设备,给人一种数据包流经 VRF 设备的印象。类似地,在出口处,使用路由规则将数据包发送到 VRF 设备驱动程序,然后再发送到实际接口。这允许在 VRF 设备上使用 tcpdump 捕获进出 VRF 的所有数据包[1]。类似地,可以使用 VRF 设备应用 netfilter[2] 和 tc 规则,以指定适用于整个 VRF 域的规则。

设置

  1. 创建 VRF 设备时,会将其与 FIB 表关联。例如,

    ip link add vrf-blue type vrf table 10
    ip link set dev vrf-blue up
    
  2. l3mdev FIB 规则将查找定向到与设备关联的表。单个 l3mdev 规则足以满足所有 VRF。当使用默认优先级 1000 创建第一个设备时,VRF 设备会为 IPv4 和 IPv6 添加 l3mdev 规则。如果需要,用户可以删除该规则并使用不同的优先级添加,或者安装每个 VRF 规则。

    在 v4.8 内核之前,每个 VRF 设备都需要 iif 和 oif 规则

    ip ru add oif vrf-blue table 10
    ip ru add iif vrf-blue table 10
    
  3. 设置表的默认路由(因此也是 VRF 的默认路由)

    ip route add table 10 unreachable default metric 4278198272
    

    此高度量值确保默认的不可达路由可以被路由协议套件覆盖。FRRouting 将内核度量解释为组合的管理距离(高字节)和优先级(低 3 个字节)。因此,上述度量转换为 [255/8192]。

  4. 将 L3 接口绑定到 VRF 设备

    ip link set dev eth1 master vrf-blue
    

    绑定设备的本地和已连接路由会自动移动到与 VRF 设备关联的表。任何依赖于绑定设备的额外路由都将被删除,并且需要在绑定后重新插入到 VRF FIB 表中。

    可以启用 IPv6 sysctl 选项 keep_addr_on_down,以便在 VRF 绑定更改时保留 IPv6 全局地址

    sysctl -w net.ipv6.conf.all.keep_addr_on_down=1
    
  5. 将额外的 VRF 路由添加到关联的表

    ip route add table 10 ...
    

应用程序

要在 VRF 中工作的应用程序需要将其套接字绑定到 VRF 设备

setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, dev, strlen(dev)+1);

或使用 cmsg 和 IP_PKTINFO 指定输出设备。

默认情况下,未绑定套接字的端口绑定的范围仅限于默认 VRF。也就是说,它不会与在绑定到 l3mdev 的接口上到达的数据包匹配,并且如果进程绑定到 l3mdev,则可以绑定到同一端口。

通过启用 tcp_l3mdev_accept 和 udp_l3mdev_accept sysctl 选项,在默认 VRF 上下文(即,未绑定到任何 VRF 设备)中运行的 TCP 和 UDP 服务可以在所有 VRF 域中工作

sysctl -w net.ipv4.tcp_l3mdev_accept=1
sysctl -w net.ipv4.udp_l3mdev_accept=1

这些选项默认情况下处于禁用状态,以便仅为该 VRF 中的数据包选择 VRF 中的套接字。RAW 套接字有一个类似的选项,出于向后兼容性的原因,该选项默认情况下处于启用状态。这是为了使用 cmsg 和 IP_PKTINFO 指定输出设备,但使用未绑定到相应 VRF 的套接字。这允许例如较旧的 ping 实现通过指定设备来运行,而无需在 VRF 中执行它。可以禁用此选项,以便在 VRF 上下文中接收的数据包仅由绑定到 VRF 的原始套接字处理,而默认 VRF 中的数据包仅由未绑定到任何 VRF 的套接字处理

sysctl -w net.ipv4.raw_l3mdev_accept=0

VRF 设备上的 netfilter 规则也可用于限制对默认 VRF 上下文中运行的服务的访问。

将 VRF 感知应用程序(同时在 VRF 外部和内部创建套接字的应用程序)与 net.ipv4.tcp_l3mdev_accept=1 结合使用是可能的,但在某些情况下可能会导致问题。使用该 sysctl 值,未指定将选择哪个侦听套接字来处理 VRF 流量的连接;也就是说,绑定到 VRF 的套接字或未绑定的套接字都可以用于接受来自 VRF 的新连接。如果套接字配置了额外的选项(例如,TCP MD5 密钥),并且期望 VRF 流量将专门由绑定到 VRF 的套接字处理,则这种有些意外的行为可能会导致问题,就像 net.ipv4.tcp_l3mdev_accept=0 的情况一样。最后,作为提醒,无论选择哪个侦听套接字,都将根据入口接口在 VRF 中创建已建立的套接字,如前所述。


将 iproute2 用于 VRF

自 v4.7 起,iproute2 支持 vrf 关键字。为了向后兼容,本节列出了适当的命令——带有 vrf 关键字的和没有 vrf 关键字的旧形式。

  1. 创建 VRF

    要实例化 VRF 设备并将其与表关联

    $ ip link add dev NAME type vrf table ID
    

    自 v4.8 起,内核支持 l3mdev FIB 规则,其中单个规则覆盖所有 VRF。在首次创建设备时,会为 IPv4 和 IPv6 创建 l3mdev 规则。

  2. 列出 VRF

    要列出已创建的 VRF

    $ ip [-d] link show type vrf
      NOTE: The -d option is needed to show the table id
    

    例如

    $ ip -d link show type vrf
    11: mgmt: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
        link/ether 72:b3:ba:91:e2:24 brd ff:ff:ff:ff:ff:ff promiscuity 0
        vrf table 1 addrgenmode eui64
    12: red: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
        link/ether b6:6f:6e:f6:da:73 brd ff:ff:ff:ff:ff:ff promiscuity 0
        vrf table 10 addrgenmode eui64
    13: blue: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
        link/ether 36:62:e8:7d:bb:8c brd ff:ff:ff:ff:ff:ff promiscuity 0
        vrf table 66 addrgenmode eui64
    14: green: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
        link/ether e6:28:b8:63:70:bb brd ff:ff:ff:ff:ff:ff promiscuity 0
        vrf table 81 addrgenmode eui64
    

    或者在简要输出中

    $ ip -br link show type vrf
    mgmt         UP             72:b3:ba:91:e2:24 <NOARP,MASTER,UP,LOWER_UP>
    red          UP             b6:6f:6e:f6:da:73 <NOARP,MASTER,UP,LOWER_UP>
    blue         UP             36:62:e8:7d:bb:8c <NOARP,MASTER,UP,LOWER_UP>
    green        UP             e6:28:b8:63:70:bb <NOARP,MASTER,UP,LOWER_UP>
    
  3. 将网络接口分配给 VRF

    通过将网络设备绑定到 VRF 设备,将网络接口分配给 VRF

    $ ip link set dev NAME master NAME
    

    在绑定时,连接的和本地的路由会自动移动到与 VRF 设备关联的表。

    例如

    $ ip link set dev eth0 master mgmt
    
  4. 显示分配给 VRF 的设备

    要显示已分配给特定 VRF 的设备,请将 master 选项添加到 ip 命令

    $ ip link show vrf NAME
    $ ip link show master NAME
    

    例如

    $ ip link show vrf red
    3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP mode DEFAULT group default qlen 1000
        link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff
    4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP mode DEFAULT group default qlen 1000
        link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff
    7: eth5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master red state DOWN mode DEFAULT group default qlen 1000
        link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff
    

    或者使用简要输出

    $ ip -br link show vrf red
    eth1             UP             02:00:00:00:02:02 <BROADCAST,MULTICAST,UP,LOWER_UP>
    eth2             UP             02:00:00:00:02:03 <BROADCAST,MULTICAST,UP,LOWER_UP>
    eth5             DOWN           02:00:00:00:02:06 <BROADCAST,MULTICAST>
    
  5. 显示 VRF 的邻居条目

    要列出与绑定到 VRF 设备的设备关联的邻居条目,请将 master 选项添加到 ip 命令

    $ ip [-6] neigh show vrf NAME
    $ ip [-6] neigh show master NAME
    

    例如

    $  ip neigh show vrf red
    10.2.1.254 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE
    10.2.2.254 dev eth2 lladdr 5e:54:01:6a:ee:80 REACHABLE
    
    $ ip -6 neigh show vrf red
    2002:1::64 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE
    
  6. 显示 VRF 的地址

    要显示与 VRF 关联的接口的地址,请将 master 选项添加到 ip 命令

    $ ip addr show vrf NAME
    $ ip addr show master NAME
    

    例如

    $ ip addr show vrf red
    3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP group default qlen 1000
        link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff
        inet 10.2.1.2/24 brd 10.2.1.255 scope global eth1
           valid_lft forever preferred_lft forever
        inet6 2002:1::2/120 scope global
           valid_lft forever preferred_lft forever
        inet6 fe80::ff:fe00:202/64 scope link
           valid_lft forever preferred_lft forever
    4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP group default qlen 1000
        link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff
        inet 10.2.2.2/24 brd 10.2.2.255 scope global eth2
           valid_lft forever preferred_lft forever
        inet6 2002:2::2/120 scope global
           valid_lft forever preferred_lft forever
        inet6 fe80::ff:fe00:203/64 scope link
           valid_lft forever preferred_lft forever
    7: eth5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master red state DOWN group default qlen 1000
        link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff
    

    或者使用简要格式

    $ ip -br addr show vrf red
    eth1             UP             10.2.1.2/24 2002:1::2/120 fe80::ff:fe00:202/64
    eth2             UP             10.2.2.2/24 2002:2::2/120 fe80::ff:fe00:203/64
    eth5             DOWN
    
  7. 显示 VRF 的路由

    要显示 VRF 的路由,请使用 ip 命令显示与 VRF 设备关联的表

    $ ip [-6] route show vrf NAME
    $ ip [-6] route show table ID
    

    例如

    $ ip route show vrf red
    unreachable default  metric 4278198272
    broadcast 10.2.1.0 dev eth1  proto kernel  scope link  src 10.2.1.2
    10.2.1.0/24 dev eth1  proto kernel  scope link  src 10.2.1.2
    local 10.2.1.2 dev eth1  proto kernel  scope host  src 10.2.1.2
    broadcast 10.2.1.255 dev eth1  proto kernel  scope link  src 10.2.1.2
    broadcast 10.2.2.0 dev eth2  proto kernel  scope link  src 10.2.2.2
    10.2.2.0/24 dev eth2  proto kernel  scope link  src 10.2.2.2
    local 10.2.2.2 dev eth2  proto kernel  scope host  src 10.2.2.2
    broadcast 10.2.2.255 dev eth2  proto kernel  scope link  src 10.2.2.2
    
    $ ip -6 route show vrf red
    local 2002:1:: dev lo  proto none  metric 0  pref medium
    local 2002:1::2 dev lo  proto none  metric 0  pref medium
    2002:1::/120 dev eth1  proto kernel  metric 256  pref medium
    local 2002:2:: dev lo  proto none  metric 0  pref medium
    local 2002:2::2 dev lo  proto none  metric 0  pref medium
    2002:2::/120 dev eth2  proto kernel  metric 256  pref medium
    local fe80:: dev lo  proto none  metric 0  pref medium
    local fe80:: dev lo  proto none  metric 0  pref medium
    local fe80::ff:fe00:202 dev lo  proto none  metric 0  pref medium
    local fe80::ff:fe00:203 dev lo  proto none  metric 0  pref medium
    fe80::/64 dev eth1  proto kernel  metric 256  pref medium
    fe80::/64 dev eth2  proto kernel  metric 256  pref medium
    ff00::/8 dev red  metric 256  pref medium
    ff00::/8 dev eth1  metric 256  pref medium
    ff00::/8 dev eth2  metric 256  pref medium
    unreachable default dev lo  metric 4278198272  error -101 pref medium
    
  8. VRF 的路由查找

    可以为 VRF 执行测试路由查找

    $ ip [-6] route get vrf NAME ADDRESS
    $ ip [-6] route get oif NAME ADDRESS
    

    例如

    $ ip route get 10.2.1.40 vrf red
    10.2.1.40 dev eth1  table red  src 10.2.1.2
        cache
    
    $ ip -6 route get 2002:1::32 vrf red
    2002:1::32 from :: dev eth1  table red  proto kernel  src 2002:1::2  metric 256  pref medium
    
  9. 从 VRF 中删除网络接口

    通过断开与 VRF 设备的绑定,将网络接口从 VRF 中删除

    $ ip link set dev NAME nomaster
    

    连接的路由将移回默认表,本地条目将移到本地表。

    例如

    $ ip link set dev eth0 nomaster
    

本示例中使用的命令

cat >> /etc/iproute2/rt_tables.d/vrf.conf <<EOF
1  mgmt
10 red
66 blue
81 green
EOF

function vrf_create
{
    VRF=$1
    TBID=$2

    # create VRF device
    ip link add ${VRF} type vrf table ${TBID}

    if [ "${VRF}" != "mgmt" ]; then
        ip route add table ${TBID} unreachable default metric 4278198272
    fi
    ip link set dev ${VRF} up
}

vrf_create mgmt 1
ip link set dev eth0 master mgmt

vrf_create red 10
ip link set dev eth1 master red
ip link set dev eth2 master red
ip link set dev eth5 master red

vrf_create blue 66
ip link set dev eth3 master blue

vrf_create green 81
ip link set dev eth4 master green


Interface addresses from /etc/network/interfaces:
auto eth0
iface eth0 inet static
      address 10.0.0.2
      netmask 255.255.255.0
      gateway 10.0.0.254

iface eth0 inet6 static
      address 2000:1::2
      netmask 120

auto eth1
iface eth1 inet static
      address 10.2.1.2
      netmask 255.255.255.0

iface eth1 inet6 static
      address 2002:1::2
      netmask 120

auto eth2
iface eth2 inet static
      address 10.2.2.2
      netmask 255.255.255.0

iface eth2 inet6 static
      address 2002:2::2
      netmask 120

auto eth3
iface eth3 inet static
      address 10.2.3.2
      netmask 255.255.255.0

iface eth3 inet6 static
      address 2002:3::2
      netmask 120

auto eth4
iface eth4 inet static
      address 10.2.4.2
      netmask 255.255.255.0

iface eth4 inet6 static
      address 2002:4::2
      netmask 120