LinuxでQinQ(Double VLAN 802.1ad)を構築する:マルチテナントトラフィックの分離とVLAN空間の拡張

Network tutorial - IT technology blog
Network tutorial - IT technology blog

テナント数が数十を超えると、802.1Q標準の4094個のVLAN空間が手狭になってきます。QinQ(Double VLAN、802.1ad、802.1Q-in-802.1Qとも呼ばれる)は、外側にVLANタグをもう一層追加することでこの問題を解決し、利用可能なVLAN数を最大約1600万まで拡張します。ISPインフラ、クラウドプロバイダー、そして1本の物理回線上で複数テナントのトラフィックをクリーンに分離する必要がある環境にとって、コアとなる技術です。

クイックスタート — 5分でQinQを構築する

早速、実際の例を見ていきましょう。インターフェースeth0があり、outer VLAN(S-VLAN)100とinner VLAN(C-VLAN)10でQinQインターフェースを作成する場合を想定します。

ステップ1:カーネルの8021qサポートを確認する

# カーネルモジュールの確認とロード
modinfo 8021q
sudo modprobe 8021q
lsmod | grep 8021q

ステップ2:S-VLAN(outer tag — プロバイダー側)を作成する

# 802.1adプロトコルでouter VLANインターフェースを作成する
sudo ip link add link eth0 name eth0.100 type vlan proto 802.1ad id 100
sudo ip link set eth0.100 up

ステップ3:C-VLAN(inner tag — テナント側)をS-VLANの上に重ねる

# outer VLANの上にinner VLANを重ねて作成する
sudo ip link add link eth0.100 name eth0.100.10 type vlan proto 802.1Q id 10
sudo ip link set eth0.100.10 up

# IPを割り当てて確認する
sudo ip addr add 192.168.10.1/24 dev eth0.100.10
ip addr show eth0.100.10

ステップ4:QinQの動作を確認する

# ネストされたインターフェース構造を確認する
ip link show | grep -E 'eth0|vlan'

# フレームをキャプチャしてdouble tagがネットワーク上に現れることを確認する
sudo tcpdump -i eth0 -n -e 'vlan' 2>/dev/null | head -20

tcpdump802.1Q vlan #100, p 0, 802.1Q vlan #10のような出力が確認できれば、QinQは正常に動作しています。

QinQの仕組みを理解する

通常の802.1Qとのフレーム構造の違い

Standard 802.1Q Frame:
| Dst MAC | Src MAC | 0x8100 | PCP+DEI+VID (4B) | EtherType | Payload |

QinQ (802.1ad) Frame:
| Dst MAC | Src MAC | 0x88A8 | S-VID outer (4B) | 0x8100 | C-VID inner (4B) | EtherType | Payload |

EtherType 0x88A8は802.1adのouter tagを識別するシグネチャです。スイッチやルーターはこの値を基に、QinQフレームと通常の802.1Qフレームを区別します。

S-VLANとC-VLAN — それぞれ誰が管理するのか?

  • S-VLAN(Service VLAN / outer tag):サービスプロバイダーが割り当て・管理します。各テナントはそれぞれ専用のS-VLANを受け取り、この値を自分で変更することはできません。
  • C-VLAN(Customer VLAN / inner tag):テナントが管理する範囲です。テナントAとテナントBは、S-VLANが異なるため、同じC-VLAN 10を使用しても競合しません。

重要なポイント:各テナントは完全に独立した4094個のC-VLANを持ちます。合計:4094 S-VLAN × 4094 C-VLAN ≈ 1600万の組み合わせとなります。

実践的なマルチテナント設定

シナリオ:3テナントがeth0を共有する

#!/bin/bash
# setup-qinq-tenants.sh

PHYS_IF="eth0"

# --- Tenant A: S-VLAN 100 ---
ip link add link $PHYS_IF name ${PHYS_IF}.100 type vlan proto 802.1ad id 100
ip link set ${PHYS_IF}.100 up

# C-VLAN 10 (management), C-VLAN 20 (production)
ip link add link ${PHYS_IF}.100 name ${PHYS_IF}.100.10 type vlan proto 802.1Q id 10
ip link add link ${PHYS_IF}.100 name ${PHYS_IF}.100.20 type vlan proto 802.1Q id 20
ip link set ${PHYS_IF}.100.10 up
ip link set ${PHYS_IF}.100.20 up
ip addr add 10.100.10.1/24 dev ${PHYS_IF}.100.10
ip addr add 10.100.20.1/24 dev ${PHYS_IF}.100.20

# --- Tenant B: S-VLAN 200 ---
ip link add link $PHYS_IF name ${PHYS_IF}.200 type vlan proto 802.1ad id 200
ip link set ${PHYS_IF}.200 up

# C-VLAN 10 — Tenant Aと同じIDだが完全に独立している
ip link add link ${PHYS_IF}.200 name ${PHYS_IF}.200.10 type vlan proto 802.1Q id 10
ip link set ${PHYS_IF}.200.10 up
ip addr add 10.200.10.1/24 dev ${PHYS_IF}.200.10

# --- Tenant C: S-VLAN 300 ---
ip link add link $PHYS_IF name ${PHYS_IF}.300 type vlan proto 802.1ad id 300
ip link set ${PHYS_IF}.300 up
ip link add link ${PHYS_IF}.300 name ${PHYS_IF}.300.10 type vlan proto 802.1Q id 10
ip link set ${PHYS_IF}.300.10 up
ip addr add 10.300.10.1/24 dev ${PHYS_IF}.300.10

echo "QinQ interfaces ready for 3 tenants"
ip link show type vlan

systemd-networkdを使ってreboot後も設定を永続化する

ip linkで直接設定した内容はreboot後に失われます。設定を永続化するには、.netdev.networkファイルを作成します:

# /etc/systemd/network/10-svlan100.netdev
[NetDev]
Name=eth0.100
Kind=vlan

[VLAN]
Protocol=802.1ad
Id=100
# /etc/systemd/network/20-cvlan100-10.netdev
[NetDev]
Name=eth0.100.10
Kind=vlan

[VLAN]
Protocol=802.1q
Id=10
# /etc/systemd/network/20-cvlan100-10.network
[Match]
Name=eth0.100.10

[Network]
Address=10.100.10.1/24
sudo systemctl restart systemd-networkd
networkctl status eth0.100.10

運用経験から得た実践的なヒント

MTU — QinQで最もよく踏まれる落とし穴

VLANタグを1つ追加するごとに4バイトのヘッダーが増えます。QinQはタグが2層あるため、合計8バイトのオーバーヘッドになります。物理インターフェースのMTUを1500のままにしておくと、大きなフレームがサイレントにドロップされたり、明確な警告なしにフラグメント化されたりする可能性があり、非常に発見しにくい問題となります。

# double tagを収容するために物理インターフェースのMTUを増やす
sudo ip link set eth0 mtu 1508   # 1500 + 4 (outer tag) + 4 (inner tag)

# 各インターフェースの現在のMTUを確認する
ip link show eth0 | grep mtu
ip link show eth0.100.10 | grep mtu

断続的なパケットロスをデバッグした話

私が経験した中で最も難しいネットワーク問題のデバッグは、断続的なパケットロスがピーク時間帯にしか発生しないケースでした。早朝と夕方遅くは全く問題なかったのです。最初はバンド幅の飽和が原因だと思いましたが、メトリクスを確認するとバンド幅にはまだ十分な余裕がありました。

tcpdumpで数日間ネットワーク上のパケットを直接キャプチャして、ようやく原因を突き止めました:QinQインターフェースでのMTUミスマッチです。SSHセッションやファイル転送からの大きなフレームがドロップされていたのです。ピーク時間帯は多くの接続が最大サイズに近いフレームを同時に送信するため問題が露見しましたが、オフピーク時はそのしきい値に触れるほどのトラフィックがなかったのです。

正確なMTUのしきい値を見つける方法:

# -M do フラグでDon't Fragmentを有効にする — フレームがドロップされると即座にエラーが返り、サイレントにならない
ping -M do -s 1472 192.168.10.2   # 1472 payload + 28 (IP+ICMP header) = 1500 byte
ping -M do -s 1464 192.168.10.2   # しきい値を見つけるために徐々に減らしていく
ping -M do -s 1492 192.168.10.2

# 「Frag needed」またはタイムアウトが発生する場合 → MTUが小さすぎる
# ドロップされない最大値を見つける — それがパスの実際のMTU

NICが802.1adハードウェアオフロードをサポートしているか確認する

すべてのNICが802.1adのハードウェアオフロードをサポートしているわけではありません。サポートしていない場合でもカーネルがソフトウェアで処理できますが、高トラフィック時にはスループットに影響が出ます。

# オフロード機能を確認する
ethtool -k eth0 | grep -E 'vlan|tx-vlan|rx-vlan'

# NICがrx-vlan-filterをサポートしているか確認する
ethtool -k eth0 | grep vlan-filter

不要になったインターフェースを削除する

# 内側から外側の順番で削除する(innerが先、outerは後)
sudo ip link del eth0.100.10
sudo ip link del eth0.100.20
sudo ip link del eth0.100

# すべてのVLANインターフェースを一括削除する
for iface in $(ip link show type vlan | grep -oP '^\d+: \K[\w.]+'); do
  ip link del "$iface" 2>/dev/null
done

QinQは強力なツールですが、MTUの管理とネストされたインターフェース構造の追跡において規律が求められます。S-VLAN → C-VLANの流れを一度理解すれば、ハードウェアを追加したりネットワーク全体を再設定したりすることなく、マルチテナントインフラを拡張できます。

Share: