CUBE SUGAR CONTAINER

技術系のこと書きます。

Linux で VXLAN を扱ってみる

久しぶりに VXLAN について調べたところ、カーネルの機能で VXLAN インターフェイスが作れるようになってたので試してみた。 ここでいう VXLAN というのは、RFC7348 で定義されている Virtual eXtensible Local Area Network というプロトコルを指す。 このプロトコルを使うと Layer 2 のトンネリングが実現できる。

使った環境は次の通り。

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS"
$ uname -r
4.15.0-76-generic
$ dpkg -l | grep iproute2
ii  iproute2                             4.15.0-2ubuntu1                     amd64        networking and traffic control tools
$ lsmod | grep -i vxlan
vxlan                  57344  0
ip6_udp_tunnel         16384  1 vxlan
udp_tunnel             16384  1 vxlan

もくじ

ネットワークの構成

検証には Network Namespace と veth インターフェイスを使って作ったネットワークを用いる。

ネットワークの物理的な構成は次のとおり。

f:id:momijiame:20200208004155p:plain
物理的なネットワーク構成

ネットワークは、全部で 3 つの Network Namespace からできている。 真ん中の router はルータとして動作し、残りの ns1ns2 はホストとして動作する。 端点をもった線はインターフェイスのつながりを表している。 その中でも、緑の破線はトンネリングによって仮想的なつながりがあることを示している。

ネットワークの論理的な構成は次のとおり。

f:id:momijiame:20200208004242p:plain
論理的なネットワーク構成

ネットワークは、全部で 3 つのセグメントからできている。 192.0.2.0/24198.51.100.0/24 は、通常のルーティングをする。 対して、203.0.113.0/24 は VXLAN によって延伸されたブロードキャストドメインで動作する。

下準備

あらかじめ、必要なパッケージをひととおりインストールしておく。

$ sudo apt-get -y install iproute2 iputils-ping tcpdump

ネットワークを作る

まずは Network Namespace を用意する。

$ sudo ip netns add ns1
$ sudo ip netns add router
$ sudo ip netns add ns2

つづいて veth インターフェイスを用意する。

$ sudo ip link add ns1-veth0 type veth peer name gw-veth0
$ sudo ip link add ns2-veth0 type veth peer name gw-veth1

作成した veth インターフェイスを Network Namespace に所属させていく。

$ sudo ip link set ns1-veth0 netns ns1
$ sudo ip link set gw-veth0 netns router
$ sudo ip link set gw-veth1 netns router
$ sudo ip link set ns2-veth0 netns ns2

インターフェイスの状態を UP に設定する。

$ sudo ip netns exec ns1 ip link set ns1-veth0 up
$ sudo ip netns exec router ip link set gw-veth0 up
$ sudo ip netns exec router ip link set gw-veth1 up
$ sudo ip netns exec ns2 ip link set ns2-veth0 up

インターフェイスに IP アドレスを付与する。

$ sudo ip netns exec ns1 ip address add 192.0.2.1/24 dev ns1-veth0
$ sudo ip netns exec router ip address add 192.0.2.254/24 dev gw-veth0
$ sudo ip netns exec router ip address add 198.51.100.254/24 dev gw-veth1
$ sudo ip netns exec ns2 ip address add 198.51.100.1/24 dev ns2-veth0

ns1ns2 のデフォルトルートを router の IP アドレスに向ける。

$ sudo ip netns exec ns1 ip route add default via 192.0.2.254
$ sudo ip netns exec ns2 ip route add default via 198.51.100.254

rotuer がルータとして動作するようにカーネルパラメータを設定する。

$ sudo ip netns exec router sysctl net.ipv4.ip_forward=1

ひとまず、通常のルーティングが動作することを確認しておく。

$ sudo ip netns exec ns1 ping -c 3 198.51.100.1 -I 192.0.2.1
PING 198.51.100.1 (198.51.100.1) from 192.0.2.1 : 56(84) bytes of data.
64 bytes from 198.51.100.1: icmp_seq=1 ttl=63 time=0.217 ms
64 bytes from 198.51.100.1: icmp_seq=2 ttl=63 time=0.043 ms
64 bytes from 198.51.100.1: icmp_seq=3 ttl=63 time=0.191 ms

--- 198.51.100.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2026ms
rtt min/avg/max/mdev = 0.043/0.150/0.217/0.077 ms

VXLAN を設定する

つづいて、今回の主眼である VXLAN のインターフェイスを用意する。 VNI には 100 を使った。

まずは ns1 に VXLAN インターフェイスを作る。

$ sudo ip netns exec ns1 \
     ip link add ns1-vxlan0 \
     type vxlan \
     id 100 \
     remote 198.51.100.1 \
     dstport 4789 \
     dev ns1-veth0

つづいて ns2 にも VXLAN インターフェイスを作る。

$ sudo ip netns exec ns2 \
     ip link add ns2-vxlan0 \
     type vxlan \
     id 100 \
     remote 192.0.2.1 \
     dstport 4789 \
     dev ns2-veth0

あとは、作った VXLAN インターフェイスに IP アドレスを付与したら状態を UP に設定するだけ。

$ sudo ip netns exec ns1 ip link set ns1-vxlan0 up
$ sudo ip netns exec ns1 ip address add 203.0.113.1/24 dev ns1-vxlan0
$ sudo ip netns exec ns2 ip link set ns2-vxlan0 up
$ sudo ip netns exec ns2 ip address add 203.0.113.2/24 dev ns2-vxlan0

パケットキャプチャするために ns2 のインターフェイスに tcpdump をしかけておく。

$ sudo ip netns exec ns2 tcpdump -tnl -i ns2-veth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ns2-veth0, link-type EN10MB (Ethernet), capture size 262144 bytes

ns1 から ns2 に向けて Ping を打つ。 指定する IP アドレスは VXLAN インターフェイスのものを使う。

$ sudo ip netns exec ns1 ping -c 3 203.0.113.2 -I 203.0.113.1
PING 203.0.113.2 (203.0.113.2) from 203.0.113.1 : 56(84) bytes of data.
64 bytes from 203.0.113.2: icmp_seq=1 ttl=64 time=0.541 ms
64 bytes from 203.0.113.2: icmp_seq=2 ttl=64 time=0.094 ms
64 bytes from 203.0.113.2: icmp_seq=3 ttl=64 time=0.074 ms

--- 203.0.113.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2010ms
rtt min/avg/max/mdev = 0.074/0.236/0.541/0.215 ms

ちゃんとトンネリングが成功して、Ping の疎通がある。

先ほどしかけた tcpdump にも、VXLAN のパケットがキャプチャされている。 VXLAN のペイロードになっているパケット (フレーム) も一度に確認できるようだ。

$ sudo ip netns exec ns2 tcpdump -tnl -i ns2-veth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ns2-veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
IP 192.0.2.1.52760 > 198.51.100.1.4789: VXLAN, flags [I] (0x08), vni 100
IP 203.0.113.1 > 203.0.113.2: ICMP echo request, id 9404, seq 1, length 64
IP 198.51.100.1.52760 > 192.0.2.1.4789: VXLAN, flags [I] (0x08), vni 100
IP 203.0.113.2 > 203.0.113.1: ICMP echo reply, id 9404, seq 1, length 64
IP 192.0.2.1.52760 > 198.51.100.1.4789: VXLAN, flags [I] (0x08), vni 100
IP 203.0.113.1 > 203.0.113.2: ICMP echo request, id 9404, seq 2, length 64
IP 198.51.100.1.52760 > 192.0.2.1.4789: VXLAN, flags [I] (0x08), vni 100
IP 203.0.113.2 > 203.0.113.1: ICMP echo reply, id 9404, seq 2, length 64
IP 192.0.2.1.52760 > 198.51.100.1.4789: VXLAN, flags [I] (0x08), vni 100
IP 203.0.113.1 > 203.0.113.2: ICMP echo request, id 9404, seq 3, length 64
IP 198.51.100.1.52760 > 192.0.2.1.4789: VXLAN, flags [I] (0x08), vni 100
IP 203.0.113.2 > 203.0.113.1: ICMP echo reply, id 9404, seq 3, length 64

VXLAN インターフェイスの方で tcpdump をかけると、もちろん ICMP しか観測できない。

$ sudo ip netns exec ns2 tcpdump -tnl -i ns2-vxlan0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ns2-vxlan0, link-type EN10MB (Ethernet), capture size 262144 bytes
IP 203.0.113.1 > 203.0.113.2: ICMP echo request, id 9408, seq 1, length 64
IP 203.0.113.2 > 203.0.113.1: ICMP echo reply, id 9408, seq 1, length 64
IP 203.0.113.1 > 203.0.113.2: ICMP echo request, id 9408, seq 2, length 64
IP 203.0.113.2 > 203.0.113.1: ICMP echo reply, id 9408, seq 2, length 64
IP 203.0.113.1 > 203.0.113.2: ICMP echo request, id 9408, seq 3, length 64
IP 203.0.113.2 > 203.0.113.1: ICMP echo reply, id 9408, seq 3, length 64

いじょう。

参考文献

tools.ietf.org

https://www.kernel.org/doc/Documentation/networking/vxlan.txt

備考

ネットワークの物理的な構成を図示するのに使った blockdiag の定義は次のとおり。

blockdiag {
  ns1-vxlan0 [shape = endpoint];
  ns1-veth0 [shape = minidiamond];
  gw-veth0 [shape = minidiamond];
  gw-veth1 [shape = minidiamond];
  ns2-veth0 [shape = minidiamond];
  ns2-vxlan0 [shape = endpoint];

  group ns1 {
    orientation = portrait;
    label = 'ns1';
    color = '#CCCCFF';
    shape = line;
    ns1-veth0;
    ns1-vxlan0;
  }

  group router {
    label = 'router';
    color = '#CCCCFF';
    shape = line;
    gw-veth0;
    gw-veth1;
  }

  group ns2 {
    orientation = portrait;
    label = 'ns2';
    color = '#CCCCFF';
    shape = line;
    ns2-veth0;
    ns2-vxlan0;
  }

  ns1-vxlan0 -- ns1-veth0 [style = dotted];
  ns1-veth0 -- gw-veth0;
  gw-veth1 -- ns2-veth0;
  ns2-veth0 -- ns2-vxlan0 [style = dotted];
  ns1-vxlan0 -- ns2-vxlan0 [color = '#77FF77', style = dashed];
}

ネットワークの論理的な構成を図示するのに使った nwdiag の定義は次のとおり。

nwdiag {

  network {
    address = '192.0.2.0/24';
    ns1[address = 'ns1-veth0, 192.0.2.1'];
    router[address = 'gw-veth0, 192.0.2.254'];
  }

  network {
    address = '198.51.100.0/24';
    router[address = 'gw-veth1, 198.51.100.254'];
    ns2[address = 'ns2-veth0, 198.51.100.1'];
  }

  network {
    address = '203.0.113.0/24';
    ns1[address = 'ns1-vxlan0, 203.0.113.1'];
    ns2[address = 'ns2-vxlan0, 203.0.113.2'];
  }
}