虚拟路由和转发 (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。

在默认 VRF 上下文中运行的 TCP 和 UDP 服务(即,未绑定到任何 VRF 设备)可以通过启用 tcp_l3mdev_accept 和 udp_l3mdev_accept sysctl 选项来跨所有 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