ネットワークの「格差」を理解せよ:IEEE 802.1p CoSとPCPが語るQoSの深淵
ネットワークエンジニアとして現場に立っていると、「なぜか特定のトラフィックだけ遅延する」「VoIPの音質がガタガタだ」という相談を死ぬほど受けます。その時、真っ先に疑うべき場所の一つが、L2レイヤにおける「優先度の取り扱い」です。
今日は、教科書的な仕様の羅列ではなく、スイッチのASIC(専用チップ)がパケットをどう選別し、優先しているのか――その「イーサネットの階級社会」とも言える IEEE 802.1p(正確には IEEE 802.1Q の一部)と PCP(Priority Code Point)の仕組みを深掘りします。
—
1. なぜ「タグ」の中に優先度が書かれているのか?
イーサネットフレームは本来、ベストエフォート(届いたらラッキー)な世界です。しかし、音声や動画といったリアルタイムトラフィックは、数ミリ秒の遅延すら許されません。
そこで登場するのが IEEE 802.1Q タグです。標準的なイーサネットフレーム(1518バイト)に4バイトのタグを挿入する際、その中に含まれる TPID(Tag Protocol Identifier)の直後に、3ビットの PCP フィールドが存在します。
この3ビットで表現できるのは 0 から 7 までの 8段階の優先度 です。これが CoS(Class of Service)の正体です。
- PCP 7: ネットワーク制御(ルーティングプロトコル等)
- PCP 6: インターネットワーク制御
- PCP 5: ボイス(低遅延)
- PCP 4: ビデオ(帯域保証)
- PCP 0: デフォルト(ベストエフォート)
現場での鉄則は、「タグが付いていない(Untagged)パケットは、強制的に PCP 0 として扱われる」という点です。スイッチのポートに「ここはVoIP専用」と設定しても、端末からタグが付与されていなければ、スイッチ側で CoS 値を書き換える「信頼(Trust)設定」が必要になります。
—
2. スイッチの内部で何が起きているのか?(キューイングの仕組み)
スイッチのASICにパケットが届くと、PCP の値を見て、そのパケットを「どの出力キュー(Buffer)」に送るかを決定します。
1. 分類(Classification): PCP 値(またはDSCP値)を見て、どのクラスに属するか判断。
2. キューイング(Queuing): 優先度の高いクラスは「優先キュー」へ、低いクラスは「ベストエフォートキュー」へ。
3. スケジューリング(Scheduling): 優先キューが空になるまで、他のキューは出力させない(Strict Priority)、あるいは重み付けをして出力する(WRR/WFQ)。
ここで重要なのは、「PCP値はL2スイッチを越えることができない」という点です。ルータを越えてL3の世界に行くとき、タグは剥がされます。その際、PCP の情報は IPヘッダー 内の DSCP(DiffServ Code Point)値にマッピングして引き継ぐ必要があります。この「L2/L3の橋渡し」を忘れると、ルータを越えた瞬間に優先度がリセットされるという悲劇が起こります。
—
3. 実践:スイッチでのCoSマッピング設定例
Cisco Catalyst等のスイッチで、特定のポートに入ってくるタグなしパケットにCoS値を割り当てる例を見てみましょう。
# 特定のポートを信頼し、入ってきたパケットの優先度をそのまま使う
interface GigabitEthernet1/0/1
description VoIP_Phone_Port
switchport mode access
# UntaggedのパケットをCoS 5(VoIP)として内部で扱う設定
spanning-tree portfast
mls qos cos 5
mls qos cos override
# これにより、端末側がタグを付けなくてもスイッチが「これはVoIPだ」と認識する
—
4. 開発者視点:アプリケーションからのアプローチ
Web API設計やインフラ運用に関わるエンジニアなら、curl や Python で通信を検証する際、DSCP値(L3の優先度)を付与して「ネットワーク側での優先度」をテストすることがあります。PCP 自体はカーネルレベルで制御されますが、socket オプションを通じてヒントを出すことは可能です。
PythonでソケットにDSCP値を付与する例
(PCP はL2スイッチが自動付与することが多いですが、L3層からの優先度付けとして)
import socket
# ソケットを作成
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# IP_TOSを指定して、DSCP値(例: 0xB8=46/EF)をセットする
# これにより、ネットワーク上のルータがこのパケットを優先処理する期待値が高まる
dscp_value = 0xB8
s.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, dscp_value)
s.connect(('192.168.1.10', 80))
s.sendall(b'GET /api/data HTTP/1.1\r\nHost: example.com\r\n\r\n')
s.close()
—
5. トラブルシューティングの勘所
もしあなたが「優先度設定をしたはずなのに効果がない」と悩んでいるなら、以下の手順でパケットを追いかけてください。
1. タグの剥離を確認: wireshark でキャプチャし、フレームヘッダーに 802.1Q Virtual LAN タグがあり、その中の Priority code point が意図した値か確認する。
2. Trust設定の確認: スイッチ側で mls qos trust cos が有効になっているか、あるいは cos override で強制書き換えされていないかを確認する。
3. バッファ詰まりの確認: 優先キューにトラフィックが集中しすぎていないか、スイッチのコマンドでドロップ数を確認する。
# Ciscoスイッチでキューのドロップを確認するコマンド例
show interfaces gigabitEthernet 1/0/1 statistics
ネットワークは物理層からアプリケーション層まで、すべてが地続きです。「フレームの3ビット」という小さな値が、システム全体のパフォーマンスを左右する。この「階級社会」のルールをマスターすれば、あなたのインフラはもっと強くなれるはずです。
次回は、L3層の DSCP との相互変換(マッピングテーブル)について、さらに深掘りしていきましょう。それでは、健闘を祈ります。
コメント