CUBE SUGAR CONTAINER

技術系のこと書きます。

Linux Bridge でタグ VLAN を使う

今回は Linux Bridge (ネットワークブリッジ) でタグ VLAN (IEEE 802.1Q) を使う方法について。 設定が足らなくてだいぶ悩んだのでメモしておく。

使った環境は次の通り。

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.1 LTS"
$ uname -r
4.15.0-29-generic
$ ip -V
ip utility, iproute2-ss180129
$ sudo apt-cache show iproute2 | grep -i version
Version: 4.15.0-2ubuntu1

下準備

最初に、今回使うパッケージをインストールしておく。

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

Linux Bridge でタグ VLAN を有効にする

準備ができたところで、早速本題に入る。 Linux Bridge でタグ VLAN を扱うには vlan_filtering を有効にする必要がある。 vlan_filteringip(8) で有効にできる。

例えば ip(8) を使ってブリッジを作る場合には、次のように有効にできる。 これは br0 という名前でブリッジを作るときの例。

$ sudo ip link add dev br0 type bridge vlan_filtering 1

また、作ったあとからでも次のように有効にできる。 brctl(8) とか使って作った場合にも、これでいけるはず。

$ sudo ip link add dev br0 type bridge
$ sudo ip link set dev br0 type bridge vlan_filtering 1

ブリッジを作ったら状態を UP に設定する。

$ sudo ip link set br0 up

ここからは、上記の設定が正しく動いていることを確認していく。

アクセスポートを追加する

続いては、タグ VLAN を有効にした Linux Bridge に実際にアクセスポートを追加する。 追加するネットワークインターフェースには veth インターフェースを使う。 また、あとから ping(8) で疎通を確認できるように Network Namespace をつなげておく。

まずは Network Namespace を用意する。

$ sudo ip netns add ns1

続いて veth インターフェースのペアを作成する。

$ sudo ip link add ns1-veth0 type veth peer name br0-veth0

片方の veth インターフェースを、先ほど作った Network Namespace に所属させる。

$ sudo ip link set ns1-veth0 netns ns1

所属させたネットワークインターフェースを使える状態にして IP アドレスとして 192.0.2.1/24 を付与しておく。

$ sudo ip netns exec ns1 ip link set ns1-veth0 up
$ sudo ip netns exec ns1 ip addr add dev ns1-veth0 192.0.2.1/24

veth インターフェースのもう片方は、先ほど作った Linux Bridge に追加しておく。

$ sudo ip link set br0-veth0 master br0
$ sudo ip link set br0-veth0 up

準備ができたので、やっと VLAN の設定をしていく。 その前に、まずは bridge(8) で現状を確認しておく。 デフォルトでは、次のように VLAN ID として 1 が使われるように見えている。 これは、ようするに 802.1Q VLAN を使っていない状態。

$ bridge vlan show dev br0-veth0
port    vlan ids
br0-veth0    1 PVID Egress Untagged

そこで、bridge(8) を使ってポートに VLAN を設定する。 今回は Linux Bridge に追加したポートを使った通信で VLAN ID 100 が使われるように設定してみよう。 代わりに、既存のルールは削除しておく。

$ sudo bridge vlan add vid 100 dev br0-veth0 pvid untagged
$ sudo bridge vlan del dev br0-veth0 vid 1

設定は以下のようになった。

$ bridge vlan show dev br0-veth0
port    vlan ids
br0-veth0    100 PVID Egress Untagged

通信先 (対向) を用意する

ns1 に続いて、もう一つ ns2 という名前で Network Namespace を用意して、先ほどと同じようにセットアップしておく。 これは ns1 から ping(8) を打つときの相手になる。

$ sudo ip netns add ns2
$ sudo ip link add ns2-veth0 type veth peer name br0-veth1
$ sudo ip link set ns2-veth0 netns ns2
$ sudo ip netns exec ns2 ip link set ns2-veth0 up
$ sudo ip netns exec ns2 ip addr add dev ns2-veth0 192.0.2.2/24
$ sudo ip link set br0-veth1 master br0
$ sudo ip link set br0-veth1 up
$ sudo bridge vlan add vid 100 dev br0-veth1 pvid untagged
$ sudo bridge vlan del dev br0-veth1 vid 1

通信を観察する

準備ができたので、Linux Bridge の通信を tcpdump(1) でパケットキャプチャする。

$ sudo tcpdump -nel -i br0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br0, link-type EN10MB (Ethernet), capture size 262144 bytes

パケットキャプチャの準備ができたら、別のターミナルから ns1 から ns2 に向かって ping を打ってみよう。

$ sudo ip netns exec ns1 ping -c 3 192.0.2.2
PING 192.0.2.2 (192.0.2.2) 56(84) bytes of data.
64 bytes from 192.0.2.2: icmp_seq=1 ttl=64 time=0.114 ms
64 bytes from 192.0.2.2: icmp_seq=2 ttl=64 time=0.060 ms
64 bytes from 192.0.2.2: icmp_seq=3 ttl=64 time=0.073 ms

--- 192.0.2.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2036ms
rtt min/avg/max/mdev = 0.060/0.082/0.114/0.024 ms

打ち終わったら tcpdump(1) を実行していたターミナルに戻る。 すると、次のように 802.1Q のデータフレームが観測できているはず。 VLAN ID にも、ちゃんと 100 が使われていることが分かる。

$ sudo tcpdump -nel -i br0
...(略)...
18:20:18.031208 b6:05:b6:b6:c6:e3 > a2:bd:24:29:d8:76, ethertype 802.1Q (0x8100), length 102: vlan 100, p 0, ethertype IPv4, 192.0.2.1 > 192.0.2.2: ICMP echo request, id 1453, seq 1, length 64
18:20:18.031240 a2:bd:24:29:d8:76 > b6:05:b6:b6:c6:e3, ethertype 802.1Q (0x8100), length 102: vlan 100, p 0, ethertype IPv4, 192.0.2.2 > 192.0.2.1: ICMP echo reply, id 1453, seq 1, length 64
...(略)

めでたしめでたし。