パケットキャプチャでSTPを学ぶ(3) Root Bridge選出
前回のおさらい
STP(Spanning Tree Protocol)のパケットキャプチャができる環境作りとパケットキャプチャ取得をしました。
今回は取得したパケットキャプチャを実際に見ながらSTPのRoot Bridge選出の動きをみてみます。
環境
構成図は以下の通りです。
図中のポート、BridgeのMACアドレスは以下の通りです。
デバイス | MACアドレス |
---|---|
access1_e0 | 52:65:55:a4:d6:45 |
access1_e1 | d2:62:ef:5b:0d:85 |
access1_e2, access1 | 32:c8:70:0d:8a:30 |
access2_e0 | ce:3f:e7:b8:99:02 |
access2_e1 | ee:7a:38:59:e8:35 |
access2_e2, access2 | 3e:cc:33:fc:df:10 |
access3_e0 | fe:53:e0:d3:ad:0a |
access3_e1, access3 | 06:6e:04:36:2e:c5 |
access3_e2 | 0a:87:a3:48:e2:4a |
access4_e0 | 5e:31:05:90:b1:0c |
access4_e1 | 96:e6:22:86:7c:86 |
access4_e2, access4 | 42:bf:cd:48:42:30 |
core1_e0 | c2:67:12:ab:9a:a1 |
core1_e1, core1 | 6a:78:dc:b8:0a:3d |
core1_e2 | ba:53:6c:3a:f2:a6 |
core2_e0, core2 | 26:4b:c9:95:0e:e0 |
core2_e1 | 8e:ec:ba:e3:d8:e2 |
core2_e2 | 32:8e:62:9d:2f:1f |
STPの各種パラメータはデフォルト値です。
Root Bridgeとは
STPでは、Bridge同士がBPDU(Bridge Protocol Data Unit)というメッセージを交換しあって、その系の中で「誰が一番偉いか」を決めます。
この後、この最も偉いBridgeを頂点とした木構造(Tree構造)を作っていきます。
「偉さ」というのはBridge IDという数値で決めます。小さい方が「偉い」です。
Bridge IDはBridge PriorityとBridgeのMACアドレスをつなげたものです。
Bridge Priorityのデフォルト値は16進数 0x8000 (10進数 32768)です。
今回でいうとこうなります。
デバイス | Bridge Priority | MACアドレス | Bridge ID | 役割 |
---|---|---|---|---|
access1 | 0x8000 | 32:c8:70:0d:8a:30 | 0x800032c8700d8a30 | |
access2 | 0x8000 | 3e:cc:33:fc:df:10 | 0x80003ecc33fcdf10 | |
access3 | 0x8000 | 06:6e:04:36:2e:c5 | 0x8000066e04362ec5 | Root Bridge |
access4 | 0x8000 | 42:bf:cd:48:42:30 | 0x800042bfcd484230 | |
core1 | 0x8000 | 6a:78:dc:b8:0a:3d | 0x80006a78dcb80a3d | |
core2 | 0x8000 | 26:4b:c9:95:0e:e0 | 0x8000264bc9950ee0 |
BPDUとは
こちらがBPDUパケットです。
フィールド | 説明 |
---|---|
Protocol Identifier | 0です。標準STP、RSTP、MSTPどれでも0です。 |
Protocol Version Identifier | 標準STPは0です。 |
BPDU Type | Bridge情報をやり取りするのはConfiguration BPDUで、この他にTCN(Topology Change Notification)もあります。 |
BPDU flags | STPトポロジに変化があったことをConfiguration BPDUで通知するときに使います |
Root Identification | (その時点で)Root Bridgeだと認識されているBridgeのIDが入ります。 |
- Priority | 基本的には4096, 8192, 12288, 16384, 20480, 24576, 28672, 32768, 36864, 40960, 45056, 49152, 53248, 57344, 61440のどれか。 |
- System ID extention | 標準STPでは0。 |
- System ID | MACアドレス。なんでWiresharkで「Root Bridge System ID」と表記されているのかが謎なんですよね。まず事実としてこのフィールドに入るのはMACアドレスです。いくつかのドキュメントを漁ってみたのですが、このフィールドを「System ID」と呼称しているドキュメントが見つからない… |
Root Path Cost | これも新旧2つの形式があって分かりづらい… とりあえずRoot Bridgeから遠くなるほど数値が積算されて大きくなるものと認識してもらえればOKです。 |
Bridge Identifier | BPDUを送信したBridgeのIDです。 |
Port Identifier | 正直、このフィールドの値を使ってるケースを見たことない |
Message Age | Root Bridgeから出るときは0で、Bridgeを経由する度に積算されていきます。そうするとRoot Path Costとどう違うの?という疑問が出てきます。まぁとりあえずRoot Path Costの方が大事です。Message Ageに着目することはあんまりないかな。あと、何秒加算されるのか、というのがはっきりしないです。検証して確かめたいところ。 |
Max Age | 下のForward Delayと合わせてSTPの収束時間を決める重要なパラメータ。デフォルト20秒。 |
Hello Time | BPDUを出す間隔。デフォルト2秒。 |
Forward Delay | デフォルト15秒。 |
Bridge PriorityがSTPの理解を難しく感じさせる要因の一つだと思うんですよね。Bridge PriorityとSystem ID Extentionの2つを合わせて2バイト(16ビット)のフィールドです。
16ビットということは0~65535の範囲の数値が入ると考えるのが自然で、当初はそういう風に設計されていたのですが、PVST+(Per-VLAN Spanning Tree Plus)、RSTP(Rapid Spanning Tree Protocol)ではPriorityは上位4ビットのみをPriorityとして使う、下位12ビットはVLAN IDを入れる、という形になりました。
Priorityが「0~15の整数」であれば分かりやすいのですが、上記の経緯があって「0~61440の範囲の4096の倍数」となります。[1]
4096の倍数を使うので、10進数よりも16進数の方が見やすいです。以下の表を見れは一目瞭然ですね。
ただ、ここからは標準STPじゃなくてPVST+、RSTPの話なのですが、「(広義の)Pritory=32868」みたいになることもあります。これは32768+100で、(狭義のPriority)32768+VLAN番号100から成り立っています。16進数で表現すると0x8064、これだと上位4ビットが1000(2進数)なのは分かりやすいのですが、下位12ビットの0x64を頭の中で瞬時に100(10進数)に変換してVLAN100と認識できる人は少ないので、やっぱり10進表記も16進表記も見づらいSTPのPriortyって分かりづらい…って思っちゃうんですよね。
2進数 | 10進数 | 16進数 |
---|---|---|
0001 0000 0000 0000 | 4096 | 1000 |
0010 0000 0000 0000 | 8192 | 2000 |
0011 0000 0000 0000 | 12288 | 3000 |
0100 0000 0000 0000 | 16384 | 4000 |
0101 0000 0000 0000 | 20480 | 5000 |
0110 0000 0000 0000 | 24576 | 6000 |
0111 0000 0000 0000 | 28672 | 7000 |
1000 0000 0000 0000 | 32768 | 8000 |
1001 0000 0000 0000 | 36864 | 9000 |
1010 0000 0000 0000 | 40960 | A000 |
1011 0000 0000 0000 | 45056 | B000 |
1100 0000 0000 0000 | 49152 | C000 |
1101 0000 0000 0000 | 53248 | D000 |
1110 0000 0000 0000 | 57344 | E000 |
1111 0000 0000 0000 | 61440 | F000 |
STPで障害があった場合の途絶時間は基本的には(Forward Delay x 2)か(Forward Delay x 2 + Max Age)と思っておけば大丈夫です。デフォルトでは(15 x 2 = 30秒)か(15 x 2 + 20 = 50秒)です。
ところで、STPの各種タイマーの秒数はSTPについて書かれたドキュメントを見ていると必ず書かれているのですが、パケットキャプチャを見て初めて気づくこともあります。
以下の図の赤丸のところ、違和感ないですか?
真ん中のペインではHello Timerは2(秒)なのに、下のペインでは16進数表現で0x0200?
これが0x0002なら0x0002(16進数)=2(10進数)なので違和感ないです。
0x0200(16進数)=512(10進数)なのになぜ2秒になる?
答えを言うと、このフィールドは以下のように定義されています。
Timer Values shall be encoded in two octets, taken to represent an unsigned binary number multiplied by a unit of time of 1/256 of a second. This permits times in the range 0 to, but not including, 256 s to be represented.
(意訳)タイマー値は2バイトです。
先の例でいうと
まぁ正直、設定値(Max Age, Hello Time, Forward Delay)に小数点以下の数を使うことはないので、「2バイト目は基本的に0が入っている、1バイト目が秒数を表す」って考えた方が楽です。
ただ、Message Ageについては2バイト目が0ではない場合があります。
では、パケットを見ながらどのようにaccess3がRoot Bridgeに選出されるかを見てみましょう。
ここで紹介したキャプチャファイルはここに置いてあるので興味があれば見てください。
起動直後(t=0)
Bridge起動直後のaccess1のパケットキャプチャです。
- access1の3つのポートからBPDUが出ている(赤色)
- Bridgeは最初は全ポートからBPDUを送出
- BPDUのRoot IDには「その時点でBridgeが一番偉い(Root Bridgeである)と思っているIDが入る」
- 他のBridgeの情報がまだないので「一番偉いBridge」=「自分」
- access2からBPDUを受信してる(青色)
- access2は自分自身(access2)が一番偉いと思っているので、BPDUのRoot IDにはaccess2のBridge IDを入れている
- core1からBPDUを受信してる(緑色)
- core1もBPDUにcore1のBridge IDを入れている
他のBridgeも同様に自分自身のBridgeIDをBPDUのRoot IDフィールドに入れたBPDUを出します。
t=0~2
次にaccess1とcore1のBPDUが時間とともにどう変化するのかを確認します。
- t=0の時は先ほど説明した通り、access1とcore1がお互いにBPDUを送りあっています(赤色)
- このとき、access1とcore1のBPDUのRoot IDを比較するとaccess1(0x8000 32:c8:~)の方がcore1(0x8000 6a:78:~)より低いです。これはaccess1の方が偉いということを示しています。
- t=1(Bridge起動から1秒後)の時、access1が送出するBPDUには変化がないのですが、core1が送出するBPDUのRoot IDがcore1(0x8000 6a:78:~)からaccess3(0x8000 06:6e:~)に変わりました。
- STPでは、Bridge間でBPDUをやり取りし、相手が自分より格上だと判断すると下っ端は黙り込みます。以降、偉い人だけが一方的にしゃべり続けます(人間社会みたい…)
- t=0ではaccess1の方が偉かったので、access1はt=1でもBPDUを出しています。
- t=0ではcore1はaccess1に負けたので、BPDUを出さない… つもりだったのですが、強力な後ろ盾(access3)ができたので、「やっぱ自分(の後ろ盾)の方が偉いよ」とaccess1に言っているのです(人間社会みたい…)
- t=2以降はcore1だけがBPDUを送出し、access1は(core1との間では)沈黙しました。
- access1はaccess1が知りうる最も偉いBridge IDよりも、core1の出してきたRoot IDの方が高いので、BPDUを出さなくなったのです。
core1
core1の動きに着目してみます。
- 最初はcore1自身のBridge ID(6a:78:~)をRoot IDと認識して周りにBPDUを送っています(赤色)
- 3つの隣接BridgeからBPDUが送られてきます。それぞれのBPDUは06:6e:~、32:c8:~、26:4b:~です。全部、自分が認識していたRoot IDより偉いIDですが、最も偉いのは06:6e:~(access3)です(青色)
- それ以降は06:6e:~(access3)をRoot IDフィールドにセットしたBPDUを送り続けます(緑色)
access3
最終的にはaccess3がこのネットワーク全体のRoot Bridgeとなるので、ある意味で分かりやすい動きをしています。
- 最初に06:6e:~をRoot IDとしており、最終的にこのRoot IDを最後まで送り続けます(赤色)
- 途中で隣接Bridgeから自分と異なるRoot IDがセットされたBPDUが届きますが、自分の方が偉いので無視です(青色)
access4
これはcore1と似ています。
- 最初はaccess4自身のBridge ID(42:bf:~)をRoot IDと認識して周りにBPDUを送っています(赤色)
- 2つの隣接BridgeからBPDUが送られてきます。それぞれのBPDUは06:6e:~、26:4b:~です。全部、自分が認識していたRoot IDより偉いIDですが、最も偉いのは06:6e:~(access3)です(青色)
- それ以降は06:6e:~(access3)をRoot IDフィールドにセットしたBPDUを送り続けます(緑色)
core2
- 最初はcore2自身のBridge ID(26:4b:~)をRoot IDと認識して周りにBPDUを送っています(赤色)
- 3つの隣接BridgeからBPDUが送られてきます。42:bf:~(access4)、6a:78:~(core1)、3e:cc:~(access2)全て自分より下っ端なのでこの時点では無視しています(青色)
- 隣接BridgeからくるBPDUが弱いので、自分のBridge IDをRoot IDとして継続してBPDUを出しています。隣接Bridgeは自分に対してBPDUを送ってこなくなると思っています(緑色)
- ところがcore1とaccess4はさっきとは別のRoot ID 06:6e:~をセットしてBPDUを送ってきました。これらのBridgeはaccess3からBPDUを受け取っていて、なおかつ26:4b:~(core2)よりも偉いので逆襲してきたわけです。(オレンジ色)
- core2も06:6e:~(access3)をRoot IDフィールドにセットしたBPDUを送り始めました。Bridge起動から3秒経っています(ピンク色)
access2
翻弄される可哀そうなやつです。
- 最初はaccess2自身のBridge ID(3e:cc:~)をRoot IDと認識して周りにBPDUを送っています(赤色)
- 自分が一番偉いと思っています。
- 2つの隣接BridgeからBPDUが送られてきます。それぞれのBPDUは32:c8:~(access1)、26:4b:~(core2)です。どちらも自分が認識していたRoot IDより偉いIDですが、最も偉いのは26:4b:~(core2)です(青色)
- 26:4b:~(core2)が一番偉いと思っているので、core2から受け取ったRoot ID=26:4b:~を自分のe0の先につながっているaccess1をはじめとする隣接装置にBPDUを送っています。(緑色)
- 自分のボスはcore2、子分はaccess1だと思っています。
- 下っ端だと思っていたaccess1が自分宛てにRoot ID=06:6e:~(access3)というBPDUを送って来ました。(オレンジ色)
- 自分のボスはaccess1、子分はcore2だと思っています。
- 今度は逆にaccess1様から頂戴したRoot ID=06:6e:~(access3)をcore2宛に送り始めます。(ピンク色)
- さっきまではRoot ID=26:4b:~(core2)を送ってきていたcore2が、今度はRoot ID=06:6e:~(access3)を送ってきました。(水色)
- access1もcore2もどちらも自分より偉いと悟りました。
- これ以降はaccess2がBPDUを送信するのはe2だけです。e2はPCがつながっているので、向こうからBPDUが来ることはありません(茶色)
時系列での変化
時系列でどのBridgeがだれをRootと認識しているのかを図示しました。
最初は全Bridgeが自分をRootだと思っています。
access3とcore2がそれぞれ勢力圏を伸ばしました。access1は独立王国です。
access1とcore2もaccess3に吸収されました。access2はcore2(26:4b:~)をRoot Bridgeだと思っているのですが、実はcore2は既に陥落しています。
最終的にはaccess3が世界征服しました。
これで全Bridgeの共通認識として、もっとも優先度の高い(Bridge IDパラメータの数値が低い)Bridgeがaccess3であると分かり、access3がRoot Bridgeに選出されました。
まとめ
STPのパケットキャプチャを見ながらRoot Bridgeが選出されるところまでを確認しました。
説明上スルーしたのですが、STPのHelloは2秒おきじゃなかったかな?とか思いながら最初の方は1秒ちょっとでBPDUを出しているように見えるんですよね。
ただ、大まかな流れとしては教科書通りに、BridgeがBPDUを交換し合うところが見えました。
この次は各ポートの役割の決定、STPトポロジの決定ですね。
-
標準STPではPriorityに10、100、10000を使うように設計してもいいのですが、(ネットワーク屋からみると)分かりづくなるので、だいたい4096の倍数を使っていると思います。 ↩︎
Discussion