こんにちは。シニアネットワークエンジニアの私だ。
Web APIの設計やモダンなクラウドインフラの構築に日々奔走している君たちなら、アプリケーション層のタイムアウトやロードバランサーのヘルスチェック不全には敏感だろう。しかし、その下層で黙々とパケットを運ぶレイヤ2の世界で、ある日突然、静寂の裏で「ネットワークの死」が進行しているとしたらどうだろうか。
今回は、スイッチングネットワークの暗黒面、そしてインフラエンジニアが夜中に叩き起こされる原因ワーストに入る「L2ループ」と、それを未然に防ぐためのループガード(Loop Guard)、そして物理層の裏切りを暴くUDLD(UniDirectional Link Detection)について、実務の現場の泥臭い知見を交えて徹底解説しよう。
—
1. なぜL2ループは恐ろしいのか?
IPルーティングの世界には、生存期間を示す TTL(Time to Live) という命綱が存在する。ルーターを経由するたびにデクリメントされ、0になればパケットは闇に葬られる。だからルーティングループが発生しても、CPUスパイクや一時的なパケットロス程度で踏みとどまることが多い。
だが、イーサネット(L2)の世界には、そんな優しいセーフティネットはない。
スパニングツリープロトコル(STP / RSTP / MSTP)が正常に機能していれば、冗長パスの片方はブロッキング状態(Blocking または Discarding)に置かれ、ループは回避される。しかし、ハードウェアの故障、光ファイバーの片系断線、あるいはファームウェアのバグによって、「本来届くべきBPDU(Bridge Protocol Data Unit)が届かなくなった」とき、悲劇は幕を開ける。
ブロッキングポートが「あ、対向からBPDUが来ないぞ? もしかしてトポロジが変わって自分が指定ポート(Designated Port)になったのかな?」と誤認し、フォワーディング状態へ遷移した瞬間、ブロードキャストストームが発生する。数秒でMACアドレステーブルはフラッピングを起こし、リンクは飽和し、CPU使用率は100%に張り付く。管理用のSSHすら繋がらなくなる、あの絶望的な静寂の完成だ。
—
2. ループガード(Loop Guard)のメカニズム
この「BPDUの沈黙による誤認」を防ぐために生まれたのが、Cisco等のスイッチに実装されているループガード(Loop Guard)である。IEEE 802.1D/802.1Wの標準仕様の隙間を埋める、実務上必須の防御策だ。
動作原理
ループガードは、非指定ポート(Non-Designated Port / 通常はブロッキング状態にあるポート)において有効化する。
正常時、このポートは対向スイッチからのBPDUを「受信」し続けている。しかし、ケーブルの片系障害や対向スイッチのコントロールプレーンの異常によってBPDUが途絶えると、通常のRSTPならタイマー(Max Age)満了後に指定ポートへと昇格してしまう。
しかし、ループガードが有効なポートでBPDUの受信が途絶えると、スイッチはそのポートを「Loop-Inconsistent(ループ不整合)」状態という特殊なブロッキング状態に叩き落とす。これにより、データトラフィックの転送は厳格にブロックされ、ループの発生が未然に防がれるのだ。さらに素晴らしいことに、障害が復旧して再びBPDUを受信し始めると、ポートは自動的に通常のリスニング/ラーニング状態へと復帰する。
設定例(Cisco IOS-XE / NX-OS)
実務において、ループガードはアクセスポートではなく、スイッチ間のトランクリンク(特に双方向の冗長パス)の非指定側や、ルートポート・代替ポートになり得るポートに適用するのが鉄則だ。グローバルで有効化することもできるが、意図しない挙動を避けるためにインターフェース単位で確実に制御することが多い。
! グローバルでループガードのデフォルト動作を有効化する場合(※環境により推奨値が異なるため注意)
spanning-tree loopguard default
! 個別のインターフェース(例: アップリンクの予備ポート)で確実にループガードを適用する
interface GigabitEthernet1/0/24
description === Uplink to Core-SW02 (Backup) ===
switchport mode trunk
spanning-tree guard loop
> 実務Tips: グローバル有効化(spanning-tree loopguard default)は、ポイントツーポイントの全二重リンクすべてに適用されるため非常に強力だが、ハブが介在するようなレガシーな共有セグメントがある環境では、予期せぬポートがブロッキングされるリスクがある。モダンなDC/キャンパスネットワーク以外では、対象ポートを個別に指定する方が安全だ。
—
3. 物理層の裏切り:UDLD(UniDirectional Link Detection)の必要性
ループガードが「BPDUという制御プロトコルの欠落」を検知するのに対し、レイヤ1/レイヤ2の物理的な「片方向リンク(UniDirectional Link)」を直接検知するのが UDLD である。
光ファイバーケーブルを思い出してほしい。通常、Tx(送信)とRx(受信)の2本のファイバーがペアになっている。ここで、受信側のファイバー(Rx)だけが折れたり、トランシーバー(SFP)の受光素子が片側だけ故障したとする。
- 自スイッチ:送信はできるが、受信はできない(対向からの信号が見えない)
- 対向スイッチ:受信はできるが、送信(自側から見れば受信)が途絶える
この時、対向スイッチ側は「おや、相手からBPDUが来なくなったぞ」とループガードやSTPのタイマーを働かせるが、自スイッチ側は対向からの光が見えないため、リンクアップ(line protocol is up)したままで、かつ対向からの異常信号を捉えられないという最悪の非対称状態に陥る。
UDLDの動作モード
UDLDには2つのモードが存在する。
1. ノーマルモード(Normal): 片方向リンクを検出した場合、ログ(Syslog)を出力するが、ポートのシャットダウンは行わない。警告目的。(実務ではほとんど使われない)
2. アグレッシブモード(Aggressive): 片方向リンクを検出すると、ネイティブなハンドシェイクの再送を試みたのち、リンクが復旧しないと判断した時点でポートを自ら err-disabled(エラー無効)状態に落とす。
インフラエンジニアたるもの、片方向リンクによる静かなるパケットロスやループの芽を摘むため、実務ではアグレッシブモード一択で導入すべきである。
設定例とデバッグ手順
CiscoスイッチにおけるUDLDの設定と、障害時のステータス確認コマンドを見てみよう。
! グローバルでUDLDアグレッシブモードを有効化(※対応プラットフォーム全体で推奨)
udld aggressive
! または、特定の高信頼トランクポートで個別有効化
interface TenGigabitEthernet1/0/1
description === Inter-Switch Link to Core-01 ===
udld port aggressive
障害・ステータスの確認コマンド
もし片方向リンクが発生し、UDLDアグレッシブモードが機能した場合、対象ポートは err-disabled になり、Syslogには以下のような痕跡が残る。
! 実行コマンド: show udld neighbors
Device ID Port ID Hops-1-Time State Msg Time
--------------------------------------------------------
SW-CORE-01 Te1/0/1 1 bidir 7s
! もし片方向通信になると、Stateが "bidir"(双方向)から "conflict" や "unknown" に遷移する
! err-disabledになったポートの確認
SW-CORE-02# show interfaces status err-disabled
Port Name Status-Reason
---------------------------------------------------
Te1/0/1 Inter-Switch Link to Core-01 UDLD
この状態から復旧するには、物理的なファイバーやSFPモジュールの交換を行った後、対象インターフェースで shutdown → no shutdown を手動実行するか、errdisable recovery cause udld を設定して自動復旧を有効にしておく必要がある。
—
4. 自動化と監視:インフラ自動化スクリプトでのアプローチ
モダンなインフラストラクチャでは、こうしたL2のヘルスチェックや設定ミスをCI/CDパイプラインや監視スクリプトで静的検証することが求められる。
例えば、Python(Netmiko ライブラリなどを使用)を使い、すべてのトランクポートに loop guard と udld port aggressive が正しく投入されているかを監査するスクリプトの断片を以下に示す。
from netmiko import ConnectHandler
import re
# ネットワーク機器への接続情報
device = {
'device_type': 'cisco_ios',
'host': '192.168.10.1',
'username': 'admin',
'password': 'SecurePassword123!',
}
def audit_l2_protection():
try:
net_connect = ConnectHandler(**device)
# トランクポートの設定状態を取得
output = net_connect.send_command("show running-config | include interface|guard loop|udld")
print("--- L2 Protection Audit Result ---")
print(output)
# 簡易的なバリデーションロジック(実際にはパースライブラリやTextFSMを使用)
if "spanning-tree guard loop" not in output:
print("[警告] ループガードが設定されていないポートが存在する可能性があります!")
else:
print("[正常] ループガードの適用を確認しました。ズバリ完璧です。")
net_connect.disconnect()
except Exception as e:
print(f"接続エラーが発生しました: {e}")
if __name__ == "__main__":
audit_l2_protection()
このように、設定の不備をコードベースで担保しつつ、実機レイヤではループガードとUDLDの二重の防壁を張ることで、レイヤ2の迷宮で迷子になるパケットを完全にハントすることができる。
—
まとめ
ネットワークの信頼性は、派手なルーティングプロトコルだけでなく、こうした「地味だが致命的な障害を防ぐL2の安全装置」の積み重ねの上に成り立っている。
- ループガードは、BPDUの喪失による非指定ポートの誤ったフォワーディング化を防ぎ、L2ループの発生を物理的に食い止める。
- UDLD(アグレッシブモード)は、光ファイバーの片系断線など、片方向リンクという「目に見えない裏切り」を検知し、即座にポートを遮断する。
インフラエンジニアの仕事は、障害が起きてから素早く直すことだけではない。「そもそも障害が発生する余地を与えないこと」こそが、真のプロフェッショナルの仕事である。ぜひ今日の設計や既存環境の見直しに、これらの機能を取り入れてみてほしい。
コメント