Linux VRF with L3 Master Device

Linux kernel 4.4から登場した、L3 Master Device (l3mdev) によるVirtual Routing and Forwading (VRF) を軽く触ってみたメモ。

そもそも何をするためのものなのか

Linuxのネットワーク廻りを触っていると、たまに「特定のネットワークインタフェースから入ってきた通信に、特定のルーティングルールを適用したい」といった場面がある。こうしたケースでは、以前より Routing Policy Database (RPDB) を利用して Policy Based Routing (PBR) を行う方法が知られている。

d.hatena.ne.jp

一方、ネットワーク機器の世界では一般的に、L3ドメインを分割する手段として Virtual Routing and Forwarding (VRF) という機能が PBR とは別に存在している。VRFは、あるネットワークインタフェース群に適用されるルーティングテーブルを分離させるためのものである。このような概念をLinux上で実現するために登場したのが、L3 Master Device である。

net: L3 master device [LWN.net]

kernel/git/davem/net-next.git - David Miller's -next networking tree

使用方法

前提として、カーネルが4.4以降かつ NET_L3_MASTER_DEV=yコンパイルされている必要がある。たとえばFedoraの場合、4.11以前のパッケージではこのオプションが無効であるため注意が必要。また、iproute2 のバージョンが古いと、後述するl3mdevが暗黙的に追加するポリシーが ip rule コマンドで正しく表示されないので同様に注意する必要がある。

Bug 1428530 – Set NET_L3_MASTER_DEV=y to enable ipvlan module

設定のおおまかな流れとしては次の3ステップ。

  1. L3 Master Device (l3mdev) のネットワークインタフェースを作成する
  2. 既存のネットワークインタフェースのmasterとしてl3madevを指定する
  3. VRF毎にルーティングを設定する

流れ自体はbridge interfaceを作成する場合と似ている。l3mdev は通常のネットワークインタフェース (net_device) と同様に振る舞うので、l3mdev自体にIPアドレスを振ることも可能。ネットワーク機器で言うところのloopback address的な使い方が可能。

使用例

まずはl3mdevを作成する。ここでは vrf-xvrf-y の2個のVRFを作成する。

# ip link add dev vrf-x type vrf table 10
# ip link set dev vrf-x up
# ip link add dev vrf-y type vrf table 20
# ip link set dev vrf-y up

ip link コマンドを実行すると、2個のインタフェースが作成されていることが分かる。

$ ip link
2: ens4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:15:7f:1c brd ff:ff:ff:ff:ff:ff
3: ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:4e:69:5d brd ff:ff:ff:ff:ff:ff
4: ens6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:cb:ef:bd brd ff:ff:ff:ff:ff:ff
5: ens7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:f1:2e:11 brd ff:ff:ff:ff:ff:ff
6: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:83:4c:ca brd ff:ff:ff:ff:ff:ff
9: vrf-x: <NOARP,MASTER,UP,LOWER_UP> mtu 65536 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 5a:b6:1d:4a:84:21 brd ff:ff:ff:ff:ff:ff
11: vrf-y: <NOARP,MASTER,UP,LOWER_UP> mtu 65536 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 76:c3:e8:67:00:01 brd ff:ff:ff:ff:ff:ff

ここでは ens4, ens5vrf-x に、ens6, ens7vrf-y に所属させることにする。

# ip link set dev ens4 master vrf-x
# ip link set dev ens5 master vrf-x
# ip link set dev ens6 master vrf-y
# ip link set dev ens7 master vrf-y

再度 ip link コマンドを実行すると、ens[4-7] のmasterが設定されていることが分かる。

$ ip link
2: ens4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master vrf-x state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:15:7f:1c brd ff:ff:ff:ff:ff:ff
3: ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master vrf-x state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:4e:69:5d brd ff:ff:ff:ff:ff:ff
4: ens6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master vrf-y state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:cb:ef:bd brd ff:ff:ff:ff:ff:ff
5: ens7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master vrf-y state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:f1:2e:11 brd ff:ff:ff:ff:ff:ff
6: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:83:4c:ca brd ff:ff:ff:ff:ff:ff
9: vrf-x: <NOARP,MASTER,UP,LOWER_UP> mtu 65536 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 5a:b6:1d:4a:84:21 brd ff:ff:ff:ff:ff:ff
11: vrf-y: <NOARP,MASTER,UP,LOWER_UP> mtu 65536 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 76:c3:e8:67:00:01 brd ff:ff:ff:ff:ff:ff

また、ip rule コマンドを実行すると、priority 1000に l3mdev-table というポリシーが自動的に追加されていることが分かる。なお、この機能はkernel 4.8から実装されたもの*1なので、4.4~4.7のカーネルでl3mdevを使用する場合、手動でルールを追加する必要がある。

$ ip rule
0:      from all lookup local 
1000:   from all lookup [l3mdev-table] 
32766:  from all lookup main 
32767:  from all lookup default

ここで、例として 172.16.0.1/32 の経路を各VRFに追加する。

$ ip addr
2: ens4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master vrf-x state UP group default qlen 1000
    link/ether 52:54:00:15:7f:1c brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.1/24 scope global ens4
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe15:7f1c/64 scope link 
       valid_lft forever preferred_lft forever
3: ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master vrf-x state UP group default qlen 1000
    link/ether 52:54:00:4e:69:5d brd ff:ff:ff:ff:ff:ff
    inet 192.168.2.1/24 scope global ens5
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe4e:695d/64 scope link 
       valid_lft forever preferred_lft forever
4: ens6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master vrf-y state UP group default qlen 1000
    link/ether 52:54:00:cb:ef:bd brd ff:ff:ff:ff:ff:ff
    inet 10.0.1.1/24 scope global ens6
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fecb:efbd/64 scope link 
       valid_lft forever preferred_lft forever
5: ens7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master vrf-y state UP group default qlen 1000
    link/ether 52:54:00:f1:2e:11 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.1/24 scope global ens7
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fef1:2e11/64 scope link 
       valid_lft forever preferred_lft forever
6: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:83:4c:ca brd ff:ff:ff:ff:ff:ff
    inet 10.168.20.201/24 brd 10.168.20.255 scope global ens3
       valid_lft forever preferred_lft forever
    inet6 fe80::24fd:811e:6432:bd36/64 scope link 
       valid_lft forever preferred_lft forever
9: vrf-x: <NOARP,MASTER,UP,LOWER_UP> mtu 65536 qdisc noqueue state UP group default qlen 1000
    link/ether 5a:b6:1d:4a:84:21 brd ff:ff:ff:ff:ff:ff
11: vrf-y: <NOARP,MASTER,UP,LOWER_UP> mtu 65536 qdisc noqueue state UP group default qlen 1000
    link/ether 76:c3:e8:67:00:01 brd ff:ff:ff:ff:ff:ff
# ip route add 172.16.0.1/32 via 192.168.1.2 table 10 
# ip route add 172.16.0.1/32 via 10.0.1.2 table 20

ip route list を見ると、デフォルトおよび各VRF毎に独立したルーティングテーブルが設定されていることが確認できる。

$ ip route list
default via 10.168.20.1 dev ens3 proto static metric 100 
10.168.20.0/24 dev ens3 proto kernel scope link src 10.168.20.201 metric 100
$ ip route list vrf vrf-x
172.16.0.1 via 192.168.1.2 dev ens4 
192.168.1.0/24 dev ens4 proto kernel scope link src 192.168.1.1 
192.168.2.0/24 dev ens5 proto kernel scope link src 192.168.2.1 
$ ip route list vrf vrf-y
10.0.1.0/24 dev ens6 proto kernel scope link src 10.0.1.1 
10.0.2.0/24 dev ens7 proto kernel scope link src 10.0.2.1 
172.16.0.1 via 10.0.1.2 dev ens6 

Network Namespaceとの比較

Linuxのネットワークリソースを分離・独立させる手段としては、最近はNetwork Namespaceがポピュラーである。Network Namespaceはネットワークスタック全体を分離するのに対し、l3mdevはL3のルーティングのみを分離する。l3mdevのモデルは、L3ドメインをまたいでプロセスを実行したい場合、具体的にはルーティングデーモンを実行する場合に都合が良い。このあたりの背景は、L3 Master Deviceの提案者であるDavid Ahernが書いた記事が詳しい。

cumulusnetworks.com

アプリケーション側の挙動に関しては、カーネル付随ドキュメントの Applications セクションに記載がある通り sysctlnet.ipv4.tcp_l3mdev_accept / net.ipv4.udp_l3mdev_accept によって変化するようである。このあたりはまた別途触ってみる予定。

Reference