L2ループとSTP
同じ三角形の接続で、フレームの循環とループを防ぐ状態を比較します。
説明
L2スイッチを冗長に接続すると、フレームが循環する経路ができる場合があります。通常のEthernetにはIPのTTLのような中継回数の上限がなく、STP系の制御で転送に使う経路を木構造にします。
三台を三角形につなぎ、ブロードキャストが二方向へ回り続ける様子を計算します。ブリッジIDと経路コストからrootと待機ポートを決め、一本の断線後に別の経路へ切り替わる状態を比較します。
1三角形の物理接続
S1-S2、S1-S3、S2-S3の三本です。各リンクの経路コストを10に固定し、ブロードキャストをS1の端末ポートから入れます。
links = {"S1": {"S2","S3"}, "S2": {"S1","S3"}, "S3": {"S1","S2"}}物理接続; active = [("S1", "S2"), ("S1", "S3")]; print(active)最初の二方向最初のフレームはS2とS3へ一つずつ出ます。各スイッチが受信ポート以外へ転送するだけでは、三角形の回り込みを止められません。
2受信ポート以外への転送
一つの転送段階を進める関数です。戻ってきた同じフレームを識別して捨てる機能は入れていません。
def advance(frames, graph):一段進める
return sorted((receiver, target) for sender, receiver in frames for target in graph[receiver] if target != sender)受信元以外の隣接先へ送信元と受信先の組を、次のスイッチ間転送へ進めます。通常のEthernetブロードキャストには各スイッチが共通に重複を排除できる識別番号がなく、受信ポート除外だけでは循環を防げません。
3同じフレームがS1へ戻る
二段進め、S1へ戻るコピーを数えます。
round1 = advance(active, links); round2 = advance(round1, links)二段の転送; print(round1, round2)循環する向き; print(sum(receiver == "S1" for sender,receiver in round2))S1へ戻った数S2→S3とS3→S2を経て、二つともS1へ戻ります。この単純な三角形では毎段のコピー数が必ず指数的に増えるのではなく、同じ二つが回り続けます。端末への重複配送が続き、経路が増えれば複製も増え得ます。
4root bridgeを選ぶ
同じSTPインスタンスで、優先度部分が同じ三台のブリッジIDを比較します。MACは整数で短縮表示しています。
bridge_ids = {"S1": (32769,1), "S2": (32769,2), "S3": (32769,3)}IDの比較条件; root = min(bridge_ids,key=bridge_ids.get); print(root)最小IDを選ぶ最小のS1がrootです。ここでは上位部分が同じためMACの順で決まります。実際のブリッジIDは優先度とシステムID拡張、MACを合わせて比較します。
5ルートブリッジへの経路とブロッキングポート
S2とS3はS1へ直接行けばコスト10、相手経由なら20です。S2-S3間ではrootへのコストが同じため、送信側ブリッジIDを比較します。
cost_to_root = {"S1":0,"S2":10,"S3":10}rootへのコスト; designated = min(["S2","S3"],key=lambda s:(cost_to_root[s],bridge_ids[s]))コストの次にIDを比較; alternate = "S3" if designated == "S2" else "S2"; print(designated, alternate)二つのポートの役割S2側がdesignated、S3側がalternateになります。安定したRSTPではalternate側はdiscardingで通常データを転送しませんが、BPDUは処理します。物理リンクを抜くことと同じではありません。
6通常データを転送する木構造
S2-S3を通常データの有効経路から外し、同じブロードキャストを流します。
tree = {"S1": {"S2","S3"}, "S2": {"S1"}, "S3": {"S1"}}通常データに使う経路; print(advance(active,tree))同じフレームの次の転送空のリストとなり、S2とS3から別スイッチへ回り込みません。物理的な予備リンクを残しながら、通常データの有効経路から循環を取り除いています。
7S1-S3の断線後に予備経路を使う
S1-S3が切れ、STP系の再計算が完了した状態を作ります。S3はS2経由でrootへ到達します。
recovered = {"S1": {"S2"}, "S2": {"S1","S3"}, "S3": {"S2"}}断線後に有効となった木; first = [("S1","S2")]; second = advance(first,recovered)予備経路へ転送; print(first,second,advance(second,recovered))循環せずに届くことを確認S1→S2→S3で終わり、循環しません。S3のroot経路はコスト20になります。切替中の損失や時間は検出方法・タイマー・プロトコル等に依存し、この安定後の計算だけでは無停止を保証できません。
まとめ
受信ポートを除外するだけでは、複数スイッチを回る循環を防げません。
STP系はIDと経路コストから役割を選び、通常データの有効経路を木構造にします。
障害時は予備経路へ切り替えられますが、安定後の到達性と切替中の損失・時間は別に確認します。
操作履歴と解説を振り返れます。
Python 3
links = {"S1": {"S2","S3"}, "S2": {"S1","S3"}, "S3": {"S1","S2"}}; active = [("S1", "S2"), ("S1", "S3")]; print(active)[('S1', 'S2'), ('S1', 'S3')]def advance(frames, graph):
return sorted((receiver, target) for sender, receiver in frames for target in graph[receiver] if target != sender)round1 = advance(active, links); round2 = advance(round1, links); print(round1, round2); print(sum(receiver == "S1" for sender,receiver in round2))[('S2', 'S3'), ('S3', 'S2')] [('S2', 'S1'), ('S3', 'S1')]
2bridge_ids = {"S1": (32769,1), "S2": (32769,2), "S3": (32769,3)}; root = min(bridge_ids,key=bridge_ids.get); print(root)S1
cost_to_root = {"S1":0,"S2":10,"S3":10}; designated = min(["S2","S3"],key=lambda s:(cost_to_root[s],bridge_ids[s])); alternate = "S3" if designated == "S2" else "S2"; print(designated, alternate)S2 S3
tree = {"S1": {"S2","S3"}, "S2": {"S1"}, "S3": {"S1"}}; print(advance(active,tree))[]
recovered = {"S1": {"S2"}, "S2": {"S1","S3"}, "S3": {"S2"}}; first = [("S1","S2")]; second = advance(first,recovered); print(first,second,advance(second,recovered))[('S1', 'S2')] [('S2', 'S3')] []演習完了