虛擬路由和轉發 (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