【実務・中級編】 Q-in-Q(IEEE 802.1ad / VLAN Stacking)の仕組みとカプセル化 – インフラ・物理&L2/L3ネットワーク基礎実践ガイド

こんにちは。ネットワークの底流を流れるパケットの息吹に、日々エンジニアとしてのロマンを感じているシニアインフラアーキテクトです。

Web APIの設計やモダンなクラウドアーキテクチャの構築において、私たちは普段、レイヤー4からレイヤー7の洗練された世界に身を置いています。しかし、その高レイヤーのトラフィックがデータセンターの壁を越え、あるいは通信事業者の広域網(WAN)を駆け抜けるとき、下層では泥臭く、しかし極めてエレガントなレイヤー2の職人技が動いています。

今回は、その中でも「複数企業のネットワークを安全に、かつ完全に独立したまま同一の広域イーサネット網に相乗りさせる」という、インフラエンジニアの必須教養であり実務のキモである Q-in-Q(IEEE 802.1ad / VLAN Stacking) の世界へ、あなたを誘いましょう。

教科書の定義をなぞるだけでは見えてこない、パケットの裏側の動きや、現場でハマりがちな落とし穴まで、徹底的に解説します。

—

1. なぜQ-in-Qが必要なのか?(背景と基本思想)

現代の企業ネットワークにおいて、VLAN(IEEE 802.1Q)はもはや空気のような存在です。社内の部署ごと、あるいはセキュリティゾーンごとにVLAN IDを切り、トラフィックを論理的に分離するのは常識ですね。

ここで、ある大企業(Customer A)から、「当社の東京本社と大阪支社を、通信事業者のレイヤー2網(L2VPN)で直結してほしい。ただし、当社内ではすでに VLAN 10 から VLAN 100 まで独自のポリシーで使い回している。プロバイダー側で勝手にタグを書き換えられたら困る」という要件を突きつけられたと想像してください。

さらに別の顧客(Customer B)も契約してきて、彼らも勝手に VLAN 10 や VLAN 20 を使っているとします。

4,096の壁と「タグの多重化」

標準的な802.1Qタグが持つVLAN IDフィールドは12ビットです。つまり、$2^{12} = 4,096$ 個しか識別できません。インターネットに接続する数千、数万の顧客のVLAN IDが重複するのは当たり前です。

この課題を解決するために考案されたのが、Q-in-Q(VLAN Stacking / IEEE 802.1ad) です。
思想は非常にシンプル。「顧客のタグ(内側タグ)の上から、プロバイダー網専用のタグ(外側タグ)をもう一枚重ね着させればいいじゃないか」 というわけです。

  • 顧客側VLANタグ(Customer VLAN Tag / C-Tag): 顧客が自由に使えるタグ(ID: 1〜4095)
  • プロバイダー側VLANタグ(Service VLAN Tag / S-Tag): 通信事業者が顧客ごとに割り当てる網内用タグ

これにより、顧客Aのパケットはすべて S-Tag = 100 という外側カプセルに包まれ、顧客Bのパケットは S-Tag = 200 に包まれてプロバイダー網を流れます。網内では S-Tag だけを見てルーティング/スイッチングし、対向拠点に届いた瞬間に外側の S-Tag を剥ぎ取って元の C-Tag 付きパケットを顧客に返す。これがQ-in-Qの仕組みです。

—

2. パケットの構造とEtherTypeの罠

では、パケットがワイヤー上をどのように流れているのか、そのフレーム構造を覗いてみましょう。

通常の802.1Qフレームは、MACアドレッシングの直後に TPID (Tag Protocol Identifier) = 0x8100 とTCI(Tag Control Information:PCP/DEI/VLAN ID)が挿入されます。

Q-in-Q(802.1ad)では、このタグが2重になります。

+---------------------+---------------------+---------------------+---------------------+------------------+
| Destination MAC     | Source MAC          | S-TPID (0x88A8)     | S-TCI (PCP/DEI/S-ID)| C-TPID (0x8100)  |
| (6 Bytes)           | (6 Bytes)           | (2 Bytes)           | (2 Bytes)           | (2 Bytes)        |
+---------------------+---------------------+---------------------+---------------------+------------------+
| C-TCI (PCP/DEI/C-ID)| Length / EtherType  | Payload (Data)                                              |
| (2 Bytes)           | (2 Bytes)           | (Up to 1500+ Bytes)                                         |
+---------------------+---------------------+---------------------+----------------------------------+

ここでインフラエンジニアとして絶対に知っておくべき重要ポイントが、TPID(Tag Protocol Identifier)の差異です。

1. 外側タグ(S-Tag)のTPID: 標準的なIEEE 802.1adでは、機器間での混同を防ぐために 0x88A8 を使用します。ただし、機器や古いシスコのスイッチなどでは、これを 0x8100(ネイティブな802.1Qと同じ値)に設定して「ダブルタグ(QinQ)」として扱うケースも多々あります。
2. 内側タグ(C-Tag)のTPID: 常に 0x8100 です。

このTPIDの不一致は、現場のトラブルシューティングで最も頻繁に遭遇する原因の一つです。「対向機器がパケットをドロップする、あるいはタグとして認識せずペイロード扱いしてしまう」という現象にぶつかったら、まずパケットキャプチャ(Wireshark等)でS-TPIDが 0x88A8 なのか 0x8100 なのかを疑ってください。

—

3. 実務で直面する「MTU(Maximum Transmission Unit)の悲劇」

Q-in-Qを導入する際、設計者が最も見落としがちで、本番稼働後に阿鼻叫喚を生むのが MTUサイズ問題 です。

標準的なイーサネットのフレームサイズは1,500バイトです。ここに、Q-in-Qによって4バイトのタグが2枚(計8バイト)追加されます。

  • 通常のフレーム: 1,500バイト
  • 802.1Qタグ付き(1枚): 1,500 + 4 = 1,504バイト
  • Q-in-Qタグ付き(2枚): 1,500 + 4 + 4 = 1,508バイト (S-TPIDのサイズ変動を含めるとさらに増える場合あり)

もし、プロバイダー網内のスイッチや中継回線の物理インターフェースのMTUが標準の 1500 バイトに設定されているとどうなるでしょうか?
スイッチは1,508バイトのQ-in-Qフレームを受信した瞬間、それを 「ジャイアントフレーム(不正な巨大パケット)」 とみなして容赦なく破棄(ドロップ)します。TCPの3wayハンドシェイクはSYNで成功するのに、HTTPのGETリクエスト(大きなペイロード)を投げた瞬間に通信がフリーズする……という、夜も眠れなくなる典型的なトラブルの完成です。

対策

プロバイダー側のバックボーンスイッチ、および顧客と接続するアクセスポートのMTUは、最低でも 1504 バイト以上(できれば安全を見て 1522 バイトやジャンボフレーム対応の 9000 バイト) に設定することが鉄則です。

—

4. ネットワーク機器における設定・構築例

では、実際のCisco IOSライクな機器と、Linux(iproute2)環境での設定例を見てみましょう。インフラ運用において、仕様書通りの美しい設定がいかに重要かがわかります。

パターンA: Cisco IOSスイッチ(Catalyst等)での設定

顧客からのトランク回線を受け入れ、プロバイダー網内でS-Tag(VLAN 100)を付与してカプセル化する設定です。

! 1. 物理インターフェースをIEEE 802.1ad(Dot1q-tunnel)のポートモードに設定
interface GigabitEthernet0/1
 description === Customer A Access Port ===
 switchport access vlan 100
 switchport mode dot1q-tunnel
 no shutdown

! 2. プロバイダー網側のコアへ向かうトランクポートの設定(S-Tagを透過・伝送)
interface GigabitEthernet0/2
 description === Provider Backbone Trunk ===
 switchport mode trunk
 switchport trunk allowed vlan 100,200,300
 no shutdown

*解説:* switchport mode dot1q-tunnel を指定することで、このポートに入ってきた顧客のすべてのVLANタグ(C-Tag)を剥がさずにそのまま保持し、自動的に access vlan で指定した VLAN 100(S-Tag)を外側に付加してトランク側へ送り出します。

パターンB: Linux(iproute2)環境でのQ-in-Qカプセル化

ベアメタルサーバーやLinuxルーター(FRRouting等)を使ってソフトウェア的にQ-in-Qを終端・生成する場合の設定です。インフラ自動化のスクリプトや検証環境で重宝します。

#!/bin/bash
# =====================================================================
# Linux (iproute2) による Q-in-Q (802.1ad) インターフェースの構築スクリプト
# =====================================================================

# 1. 物理インターフェース(eth0)上に外側タグ(S-Tag: VLAN 100)を持つvlanデバイスを作成
# 親インターフェースのMTUをあらかじめ1522に拡張しておくこと
ip link set dev eth0 mtu 1522
ip link add link eth0 name eth0.100 type vlan id 100 proto 802.1ad

# 2. 作成したS-Tagインターフェースを有効化
ip link set dev eth0.100 up

# 3. さらにその内側に、内側タグ(C-Tag: VLAN 10)を持つvlanデバイスをネストして作成
ip link add link eth0.100 name eth0.100.10 type vlan id 10 proto 802.1q

# 4. 内側インターフェースを有効化し、IPアドレスを付与
ip link set dev eth0.100.10 up
ip addr add 192.168.100.1/24 dev eth0.100.10

echo "Q-in-Q仮想インターフェース (eth0.100.10) の構築が完了しました。"

—

5. 運用現場におけるトラブルシューティングの極意

最後に、現場でQ-in-Qの障害に直面したときに、私たちがどのようにパケットを追いかけるべきか、その思考プロセスを共有します。

1. レイヤー1/2の基本確認 (show interfaces / ip -s link)

  • カウンタのエラー(Giant Frames、Runts、Input Errors)が跳ね上がっていないか確認します。跳ね上がってい上述のMTU不足やデュプレックスのミスマッチを疑います。

2. TPIDのミスマッチの特定 (Wireshark / tcpdump)

  • キャプチャを取得し、外側のタグのEtherTypeが 0x88A8 になっているか、それとも 0x8100 なのかを確認します。古い機器や特定のベンダー間接続では、グローバルコンフィグでTPIDを 0x8100 に統一(qinq mac-auth や tpId の変更)する必要がある場合があります。

3. PCP(Priority Code Point)の透過確認

  • Q-in-Qフレームの中には、QoSを担保するためのCoS(Class of Service:3ビットの優先度)が含まれています。プロバイダー網内でこのPCP値が意図せずクリア(デフォルトの0に書き換え)されていないか、QoSポリシー(MAP)を確認します。

—

まとめ

Q-in-Qは、一見すると「タグを二重に貼るだけの泥臭い技術」に見えるかもしれません。しかし、何十社もの顧客が入り乱れる広域イーサネットの基盤において、顧客のプライベートな空間を完全に守りつつ、プロバイダー網の効率的な運用を両立させるための、シビアで洗練されたアーキテクチャです。

Web APIやアプリケーションの向こう側で、これらレイヤー2のパケットたちがどのようにカプセル化され、海を越え、データセンター間を疾走しているのか――。そのイメージを持つことこそが、トラブルに強い真のフルスタック・インフラエンジニアへの第一歩です。

あなたのネットワークライフに、深淵なるパケットの加護があらんことを。

コメント

タイトルとURLをコピーしました