パケットキャプチャでSTPを学ぶ(8) Port Priority変更でRoot Port選定
前回のおさらい
Linux(WSL)に作ったBridge(L2スイッチ)でSTPを有効にしてパケットキャプチャして現物のパケットを見ながら遊んでました。Port Cost、Bridge IDを変更することで目的のSTPトポロジに収束させることも確認できました。
今回は以下のRoot Port選定基準のうち、ポートIDを変更することで目的のSTPトポロジに収束させてみることにします。と言いたいのですが、それができないのでPort IDを変更して遊んでみます。
- ルートパスコストが最小
- Bridge IDが最小
- ポートIDが最小
(各ポートが受信したBPDUを見て、条件1が同位の場合は条件2で、条件2が同位の場合は条件3で決定します)
ケーブルの追加
このネットワーク構成で、条件3のポートIDを変更してSTPトポロジ(Root Port)を変更してみようと思ったのですが、Root Portの選定にPort IDが関与するのは、Bridge IDが同じ場合のみです。
今回の構成では1台のBridgeが、同一Bridge IDのBPDUを受け取る箇所、もっと端的にいうとBridge間にケーブルが2本引かれている箇所はありません。
幸いにも仮想環境で実験しているので、Bridgeのポートが空いているかとかLANケーブルが手元にあるかを考える必要はないです。core1とcore2の間にケーブル増やしちゃいましょう。
ip link add core1_e3 type veth peer name core2_e3
ip link set core1_e3 master core1
ip link set core2_e3 master core2
ip link set core1_e3 up
ip link set core2_e3 up
blocking portの確認もしておきます。新規に追加したcore2_e3がBlockingになっていますね。
# bridge link | sed -ne 's/[^ ]* \([^@]*\)@.*\(state [^ ]*\).*/\1\t\2/p' | sort
access1_e0 state forwarding
access1_e1 state forwarding
access1_e2 state forwarding
access2_e0 state blocking
access2_e1 state forwarding
access2_e2 state forwarding
access3_e0 state forwarding
access3_e1 state forwarding
access3_e2 state forwarding
access4_e0 state blocking
access4_e1 state forwarding
access4_e2 state forwarding
core1_e0 state forwarding
core1_e1 state forwarding
core1_e2 state forwarding
core1_e3 state forwarding
core2_e0 state forwarding
core2_e1 state forwarding
core2_e2 state forwarding
core2_e3 state blocking
図はこちら。core1とcore2の間にケーブルが2本あり、片方(e3)がblockingです。
パケットキャプチャ確認
BPDUは上位から下位に対して送るので、core2でBPDUを受信しているはずです。(Blocking Portはパケットの転送を無効にしているだけでリンクダウンではないので、BPDUを受信しています)
coreのe0, e3でキャプチャを取得します。
# tcpdump -i core2_e0 -U -w - | /mnt/c/Program\ Files/Wireshark/wireshark.exe -k
-i - &
# tcpdump -i core2_e3 -U -w - | /mnt/c/Program\ Files/Wireshark/wireshark.exe -k
-i - &
core2_e0
core2_e3
core2_e0はポートID 0x8001、core2_e3は0x8004を取得しています。
ポートIDは2バイト(16ビット)です。
標準STPでは上位8ビットがPort Priority、下位8ビットがPort Numberです。
Port Priorityは8ビットなので0~255の256通りの値を利用可能ですが、0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240を使うことが多いです。
core2_e0, core2_e3ともにPort Priorityは0x80(128)になっています。
Port Numberは1から数え始めてるので、e0が0x01、e3は0x04なのでしょう。[1]
bridge linkコマンド
パケットキャプチャを見る限り、Port Priorityは0x80です。0x00と0xFFのちょうど真ん中なので、デフォルト値としては適切だと思います。
問題はip linkコマンドの表示です。これらのBPDUを送出しているcore1の設定を見ると、「priority 32」って表示されるんですよね。
# bridge link show dev core1_e0
14: core1_e0@core2_e0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master core1 s
tate forwarding priority 32 cost 2
# bridge link show dev core1_e3
32: core1_e3@core2_e3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master core1 s
tate forwarding priority 32 cost 2
man bridgeでマニュアルを読んでみるとbridge link setでSTPのPort Priorityを設定できると書いてある[2]ので、さっきの32はPort Priorityのことだと思われるんですが、なぜ設定値が32で、実際に送られる値は128なのか?
priority PRIO
the STP port priority. The priority value is an unsigned 8-bit quantity (number between 0 and 255).
This metric is used in the designated port an droot port selection algorithms.
値を変更して試してみましょう。
# bridge link show dev core1_e3
32: core1_e3@core2_e3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master core1 s
tate forwarding priority 32 cost 2
# bridge link set dev core1_e3 priority 16
# bridge link show dev core1_e3
32: core1_e3@core2_e3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master core1 s
tate forwarding priority 16 cost 2
32の半分の16にセットしました。bridge link showコマンドの出力も16になりました。
パケットキャプチャをみても0x80の半分の0x40になっています。ついでにBPDUのフラグにTopology Changeフラグが立ったので、トポロジにも変更が起きているみたいです。
# bridge link | sed -ne 's/[^ ]* \([^@]*\)@.*\(state [^ ]*\).*/\1\t\2/p' | sort
access1_e0 state forwarding
access1_e1 state forwarding
access1_e2 state forwarding
access2_e0 state blocking
access2_e1 state forwarding
access2_e2 state forwarding
access3_e0 state forwarding
access3_e1 state forwarding
access3_e2 state forwarding
access4_e0 state blocking
access4_e1 state forwarding
access4_e2 state forwarding
core1_e0 state forwarding
core1_e1 state forwarding
core1_e2 state forwarding
core1_e3 state forwarding
core2_e0 state blocking
core2_e1 state forwarding
core2_e2 state forwarding
core2_e3 state forwarding
Blocking Portがcore2_e3からcore2_e0に移動しました。
core2のRoot Portが移動したのでやりたかったことは確認できました。
忘れないうちに書いておくと、ポイントは次の通りです。
項目 | Port Cost | Port Priority |
---|---|---|
Root Port選定時の優先度 | 最初にチェックされる | Root Path Cost(=Port Costの積算値)とBridge IDが同位の場合チェックされる |
設定箇所 | BPDUを受信するBridge | BPDUを送信するBridge |
設定変更の効果(影響) | STP木構造の下流すべてに及ぶ | BPDUを直接受信する1台だけ |
bridge linkコマンドの謎
16を設定すると0x40(10進数64)になってしまう件をもうちょっと調査してみました。
bridge link setコマンドでセットした値とBPDUの関係は以下の通りです。
ちょっとバグっぽい感じですね。4ビット左シフトなら、(標準STPではなく)RSTPのPriority設定を意識しているともとれるのですが、実際には2ビット左シフトされてBPDUに格納されていますね。
bridge link setで設定するときはBPDUにセットしたい値を4で割って(2ビット左シフト右シフトして)指定するとよさそうです。(128にしたいときは4で割った答えである32をセットする)
bridge link setコマンドの設定値 | 送信されたBPDUのPort ID上8ビット | 同 10進数表現 | 同 2進数表現 |
---|---|---|---|
0 | 0x00 | 0 | 0000 0000 |
1 | 0x04 | 4 | 0000 0100 |
16 | 0x40 | 64 | 0100 0000 |
32 | 0x80 | 128 | 1000 0000 |
63 | 0xfc | 252 | 1111 1100 |
これを超える数は設定エラーでした。
# bridge link set dev core1_e3 priority 64
RTNETLINK answers: Numerical result out of range
# bridge link set dev core1_e3 priority 255
RTNETLINK answers: Numerical result out of range
遊び終わったので最後にPriority値戻しとケーブル削除しておきました。
bridge link set dev core1_e3 priority 32
ip link del core1_e3
Kernel source
カーネルのソースコードをざっと追ってみたのですが、おそらくここのコードが原因かなという気がします。
/* Port id is composed of priority and port number.
* NB: some bits of priority are dropped to
* make room for more ports.
*/
static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
{
return ((u16)priority << BR_PORT_BITS)
| (port_no & ((1<<BR_PORT_BITS)-1));
}
priorityをBR_PORT_BITSビット分だけ左シフトしていて、BR_PORT_BITSはここで10であると定義されています。
#define BR_PORT_BITS 10
一方で、IEEEの8021Q-2018を見ると以下のように16ビットのポートIDのうち、上位4ビットがPriorityと書いてあるので、16-4=12ビット左シフトすべきではないかと。(もしくは下位4ビットをゼロクリアしてから8ビット左シフト)
14.2.7 Encoding of Port Identifiers
A Port Identifier is a 16-bit unsigned integer. If two Port Identifiers are numerically compared, the lesser number denotes the port of better priority. The most significant 4 bits of a Port Identifier is a settable priority component that permits the relative priority of ports on the same Bridge to be managed (13.27.46), and can be modified independently for the CIST and each MSTI. The less significant 12 bits is the Port Number expressed as an unsigned binary number, and is same for the CIST and all MSTIs. The value 0 is not used as a Port Number.
The Port Identifier for the CIST is encoded in two octets, comprising both the CIST’s priority component and the Port Number used for all spanning trees. The 4-bit priority component for each MSTI is encoded as a binary number, in the MSTI Configuration Message for that MSTI. Each SPT uses the same Port Identifier
as the CIST, so SPT Port Identifiers are not separately encoded.
NOTE—IEEE Std 802.1D, 1998 Edition [B16], and prior revisions specified a priority component of 8 bits and a Port Number of 8 bits. To maintain management compatibility with prior implementations the priority component is still considered to be an 8-bit value, but its values are restricted to those where the least significant 4 bits are zero (and hence ignored in encoding).
参考:動作確認を行った環境について
# cat /etc/issue
Ubuntu 22.04 LTS \n \l
# uname -r
5.10.16.3-microsoft-standard-WSL2
# dpkg -l iproute2
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-==============-===============-============-================================
====
ii iproute2 5.15.0-1ubuntu2 amd64 networking and traffic control t
ools
Discussion