はじめに:VLANの枯渇という「現場の絶望」と、Q-in-Qがもたらす救済
ネットワークエンジニアとしてキャリアを積んでいると、一度は必ず「VLAN IDが足りない」という壁にぶち当たります。標準的なIEEE 802.1Qが提供するVLAN IDは12ビット。つまり、 $2^{12} = 4096$ 個しか識別子を作れません。
「たった4000個もあるのだから十分だろう」と思われるかもしれませんが、これが大規模なデータセンターや、レイヤ2のトランスペアレントな接続を好む閉域網サービス(広域イーサネットなど)の現場では、全く足りないのです。例えば、数十社、数百社の企業顧客を収容する通信事業者(キャリア・ISP)の立場になってみてください。各顧客が自由に自身の社内ネットワークでVLAN(例: VLAN 10 や VLAN 100)を設計して持ち込んできます。もしプロバイダ側のスイッチがそれらのタグをそのまま処理しようとしたらどうなるでしょうか? A社の VLAN 10 とB社の VLAN 10 がプロバイダ網内で完全に混ざり合い、大惨事を引き起こします。
「顧客ごとにVLANを分離したい。しかし、顧客が使うVLAN IDの範囲には干渉したくない」
この現場の切実な要求を鮮やかに解決するのが、今回解説する IEEE 802.1ad(通称:Q-in-Q / 802.1Qトンネリング) です。
今回は、パケットのワイヤーフォーマットの深部を覗き込み、プロバイダ網を駆け抜ける二重タギングの挙動、そして実務で必ずハマるTPID(Tag Protocol Identifier)の闇と設定の勘所を、シニアエンジニアの視点から徹底的に解説します。
—
1. 802.1ad(Q-in-Q)のフレーム構造:なぜ「タギングの重ね着」が必要なのか
通常のIEEE 802.1Qフレームは、従来のイーサネットヘッダー(宛先MAC、送信元MAC)の直後に、EtherType 0x8100 とTCI(Tag Control Information:PCP, DEI, VLAN ID)の合計4バイトを挿入します。
これに対してQ-in-Q(802.1ad)は、「顧客がすでに付与しているVLANタグ(インナータグ)」の上から、さらにプロバイダ網用の「VLANタグ(アウタータグ)」を強制的に被せる(カプセル化する)技術です。
ワイヤー上のバイト列とEtherTypeの正体
通常の802.1Qと、802.1adのフレーム構造を比較してみましょう。
【通常の802.1Qフレーム】
+---------------------+---------------------+-------------------+-----------------+-----------------+
| 宛先MAC (6Bytes) | 送信元MAC (6Bytes) | TPID: 0x8100 (2B) | TCI: VLAN ID等(2B)| 上位プロトコル...|
+---------------------+---------------------+-------------------+-----------------+-----------------+
【802.1ad (Q-in-Q) フレーム:二重タギング】
+---------------------+---------------------+-------------------+-----------------+-------------------+-----------------+-----------------+
| 宛先MAC (6Bytes) | 送信元MAC (6Bytes) | アウターTPID | アウターTCI | インナーTPID | インナーTCI | 上位プロトコル...|
| | | (0x8a88 / 2Bytes) | (プロバイダ用) | (0x8100 / 2Bytes) | (顧客用) | |
+---------------------+---------------------+-------------------+-----------------+-------------------+-----------------+-----------------+
ここで非常に重要なのが TPID (Tag Protocol Identifier) の値です。
- インナータグ(顧客側): 従来通りの
0x8100 - アウタータグ(プロバイダ側): IEEE 802.1ad標準である
0x8a88(※機器やベンダー、古い機器との接続によっては0x8100をアウターに使う「Q-in-Qドロップイン」のような変則構成をとることもありますが、標準規格は0x8a88です)
なぜアウターのTPIDを 0x8a88 に変える必要があるのでしょうか?
もしアウターもインナーも同じ 0x8100 にしてしまうと、受信側のスイッチやNIC(ネットワークインターフェースカード)が「どこまでが1つ目のタグで、どこからがペイロード(あるいは2つ目のタグ)なのか」を正しくパースできなくなるからです。EtherTypeのフィールドを 0x8a88 にすることで、機器は「おっ、これは802.1adのサービスプロバイダ用タグだな」と即座に認識し、その内側にさらなるタグ(あるいはIPパケット等)が存在することを期待して処理を進められるのです。
—
2. 通信フロー:プロバイダ網をトンネリングするパケットの旅
では、顧客拠点(拠点A)から別の拠点(拠点B)へ、プロバイダのレイヤ2網を介してデータが流れるときの挙動を追ってみましょう。
[顧客ルータA] ---> (VLAN 100のタグ付与) ---> [PEスイッチA (Edge)]
|
(アウター VLAN 2000 をプッシュ / TPID: 0x8a88)
|
[プロバイダ網 (Core)]
|
(アウター VLAN 2000 をポップ)
v
[顧客ルータB] <--- (インナー VLAN 100のまま配送) <--- [PEスイッチB (Edge)]
ステップごとの挙動
1. 顧客側からの送出:
顧客のルータやスイッチが、データにインナーVLANタグ(例: VLAN ID = 100、TPID=0x8100)を付与してプロバイダ側のエッジスイッチ(PEスイッチ)へ送出します。
2. PEスイッチAでの「プッシュ(Push)」処理:
PEスイッチのポートが「Q-in-Qトンネリングポート(Dot1q-tunnelポートなどと呼ばれる)」として設定されている場合、受信したフレームに対して、プロバイダ網内でルーティング・識別するためのアウターVLANタグ(例: VLAN ID = 2000、TPID=0x8a88)を強制的につけ加えます。これでフレームサイズが4バイト増加します(MTUに注意!後述します)。
3. プロバイダコア網での転送:
コア網のスイッチやルータは、アウタータグ(VLAN 2000)だけを見てパケットを対向のPEスイッチBまで高速にスイッチングします。内部の顧客タグ(VLAN 100)は、プロバイダ網にとっては単なる「見えないペイロードの一部」として扱われるため、何社分のどのようなVLAN IDが流れていようとも一切干渉しません。
4. PEスイッチBでの「ポップ(Pop)」処理:
対向のPEスイッチBに到達すると、アウタータグ(VLAN 2000)が剥がされ(Pop)、最初に顧客が付けたインナータグ(VLAN 100)がむき出しの状態で対向の顧客拠点へと送り出されます。
—
3. 実務の現場で必ず直面する「3大落とし穴」と対策
Q-in-Qは理論的には非常に美しい仕組みですが、現場のインフラエンジニアを何度も泣かせてきた歴史があります。設計やトラブルシューティングの際に必ず押おくべきポイントを授けましょう。
落とし穴1:MTU(Maximum Transmission Unit)のオーバー問題
これが最も多いトラブルです。通常のイーサネットフレームの最大長は1518バイト(タグなしの場合は1500バイトのMTU)です。
Q-in-Qでは、通常のタグ(4バイト)に加えてもう1枚アウタータグ(4バイト)が追加されるため、フレーム全体が 最低でも8バイト肥大化 します。
もしプロバイダ網や経路上のスイッチ・ルータのインターフェースMTUが標準の 1500 バイトのままであると、ジャンボフレーム扱いされてパケットが容赦なくドロップされます。
> 現場のTips:
> Q-in-Qを導入する区間、およびその経路上のすべての機器(トランジットスイッチ含む)では、物理ポートのMTUを少なくとも 1504 バイト以上(できれば安全のために 1522 バイトや 9216 バイトなどのジャンボフレーム対応)に設定しておくことが鉄則です。
落とし穴2:TPIDの不一致(ベンダー間の互換性悪魔)
先ほど 0x8a88 がIEEE 802.1adの標準TPIDだと説明しましたが、Cisco、Juniper、Huawei、あるいは古いCatalystシリーズや白箱スイッチ(Cumulus Linuxなど)の間で、デフォルトのインバウンド/アウトバウンドTPIDの解釈が異なっている場合があります。
片方が 0x8a88 を期待しているのに、もう片方が 0x8100 でアウタータグを送出してきた場合、受信側はそれを「二重タグ」ではなく「シングルタグの異物(あるいは不正なプロトコル)」とみなしてドロップします。
落とし穴3:MACアドレステーブルの枯渇とスパニングツリー(STP)の透過
Q-in-Qを使うことで、顧客はプロバイダ網を「1本の巨大なレイヤ2ケーブル」のように扱えます。しかし、これは顧客が自由にスパニングツリー(STP / BPDU)やDTP、CDP、LLDPなどのレイヤ2制御プロトコルを流せることを意味します。
プロバイダ側でこれらの制御フレームを透過(Tunnel)させるのか、あるいはプロバイダ側で終端・破棄(BPDU Guardなど)するのかを明確にポリシー設計しないと、顧客のループがプロバイダ網全体のレイヤ2ドメインを巻き込む大障害に発展します。
—
4. 実設定サンプル:Cisco IOS / IOS-XE でのQ-in-Q実装例
それでは、実務で最も遭遇する機会が多いCisco機器を例に、802.1ad(IEEE 802.1ad準拠のSelective QinQ / IEEE 802.1Q Tunneling)の設定例を見ていきましょう。
ここでは、顧客側から来るトランクポート、およびプロバイダ網側へ向かうコアポートの設定を記述します。
! --- 1. 顧客接続用インターフェース (Access / Dot1q-tunnel設定) ---
interface GigabitEthernet0/1
description === Customer A Access Port ===
switchport access vlan 2000
switchport mode dot1q-tunnel
! ↑ 顧客からの未タグあるいは単一タグフレームを、網側のVLAN 2000に強制的に収容する
no shutdown
! --- 2. プロバイダ網コア側へ向かうトランクインターフェース ---
interface GigabitEthernet0/2
description === Uplink to Provider Core (802.1ad) ===
switchport trunk encapsulation dot1q
switchport mode trunk
switchport trunk allowed vlan 2000,2001,2002
! 網側ではIEEE 802.1adのTPID (0x8a88) を使用してアウタータグを処理するようグローバルで定義が必要な場合がある
no shutdown
! --- 3. グローバル設定(必要に応じてTPIDを 0x8a88 に明示指定) ---
! 一部のCiscoプラットフォームでは、IEEE 802.1ad標準の0x8a88を有効にするコマンドが必要
etherType 802.1ad dot1q-tunnel
> 解説メモ:
> 上記の switchport mode dot1q-tunnel を設定すると、Ciscoスイッチは受信したフレームに自動的にアウターVLAN(ここでは VLAN 2000)のタグを付与してコア側へ転送します。また、顧客が万が一BPDUなどのL2コントロールフレームを送ってきた場合でも、それらを透過的に対向拠点へスルーさせる動作(BPDU Filtering / Tunneling)が有効になります。
—
5. Pythonによるパケット構造の検証(Scapyを活用したシミュレーション)
「理屈は分かったけれど、本当にワイヤー上でフレームがどうなっているのか自分の目で確かめたい」という好奇心旺盛なエンジニアのために、Pythonのパケット操作ライブラリである Scapy を使って、802.1ad(Q-in-Q)のイーサネットフレームを構築・検証するコードを紹介します。
手元にLinux環境やテスト用のPython環境があれば、以下のコードを実行して二重タギングされたフレームの中身を確認してみてください。
from scapy.all import Ether, Dot1Q, IP, TCP, raw
def create_qinq_packet():
"""
IEEE 802.1ad (Q-in-Q) の二重タギングパケットを構築する関数
アウターTPID: 0x8a88 (802.1ad)
インナーTPID: 0x8100 (802.1Q)
"""
# 宛先・送信元MACアドレス
dst_mac = "00:11:22:33:44:55"
src_mac = "66:55:44:33:22:11"
# 1. アウタータグ (802.1adのTPID 0x8a88 を明示、VLAN ID: 2000)
outer_vlan = Dot1Q(vlan=2000, type=0x8a88)
# 2. インナータグ (通常の802.1QのTPID 0x8100、VLAN ID: 100)
inner_vlan = Dot1Q(vlan=100, type=0x0800) # 次はIPv4 (0x0800)
# 3. ペイロード (ダミーのIP/TCPパケット)
ip_layer = IP(src="192.168.100.10", dst="10.0.0.1")
tcp_layer = TCP(sport=12345, dport=80)
# フレームの結合 (Ethernet -> Outer Tag -> Inner Tag -> IP -> TCP)
# 注意: Scapyで多重タギングを表現する場合、レイヤーを `/` で重ねていきます
frame = Ether(dst=dst_mac, src=src_mac) / outer_vlan / inner_vlan / ip_layer / tcp_layer
print("[*] 生成されたQ-in-Qフレームの構造 (Summary):")
frame.show()
# 生のバイト列(Hex)を出力してワイヤー上の姿を確認
raw_bytes = raw(frame)
print(f"\n[*] ワイヤー上のバイト列 (Hex, 全長: {len(raw_bytes)} bytes):")
print(raw_bytes.hex())
return frame
if __name__ == "__main__":
create_qinq_packet()
実行時のポイント
このスクリプトを実行すると、Ethernetヘッダーの直後に 0x8a88 が現れ、その後にアウターTCI、さらにインナーの 0x8100 とTCIが綺麗に並んでいるバイナリ表現を確認できます。実務でパケットキャプチャ(Wireshark等)を取得した際、「Malformed Packet」や予期せぬパースエラーに悩んだときは、このTPIDのバイト列が期待通りの値になっているかをHexビューで確認するのがエンジニアの定石です。
—
おわりに:次世代へのステップとして
IEEE 802.1ad(Q-in-Q)は、VXLANやSRv6、EVPNといった現代のモダンなデータセンター仮想化技術やクラウドファブリックが台頭するはるか前から、キャリア網の拡張を支え続けてきた「いぶし銀」のプロトコルです。
現代においても、シンプルな拠点間接続や、複雑なカプセル化オーバーヘッド(VXLANの50バイト超のオーバヘッドなど)を避けたい小規模〜中規模の閉域網において、そのコストパフォーマンスと処理の軽さは圧倒的な強みを誇ります。
パケットがどのようなタグを纏い、どのスイッチで剥がされ、どこへ向かうのか。その頭の中のルーティングとタギングのイメージを完璧に同期させることができれば、どんな難解なレイヤ2障害も恐るるに足りません。ネットワークの深淵へ挑むあなたのインフラ人生の武器となれば幸いです。
コメント