パケットキャプチャでSTPを学ぶ(1) 環境構築
はじめに
STP(Spanning Tree Protocol)知ってますか?
最後に使ったのはいつですか?
ネットワークを学んでいると、「STPは冗長性を確保しつつL2ループを防ぐための仕組みですよ」くらいは出てくるのですが、現場では近年(少なくとも2022年では)あまり見かけなくなりました。
でも忘れたころに急に出くわすんですよね。
一度手を動かしてみて、自分の目で見て確かめると記憶に残りやすい、ということで試してみましょう。
用意するものはLinux 1台だけです。WSLでもOKです。
仮想スイッチングハブの実装
Linuxでは仮想スイッチングハブの実装として大きく分けて2つあります。
- Linux Bridge
- 昔からある実装
- 特に何しなくても使える
- STPは使えるけどRSTP(Rapid Spanning Tree Protocol)は使えない[1]
- Open vSwitch
- ここ10年くらい?で新しくできた実装
- Open vSwitchのインストール作業が必要(aptで入る)
- RSTPやLACP(Link Aggregation Control Protocol)のような「スイッチングハブらしい」機能が使える
まずは一番簡単な例ということでLinux Bridgeの実装で確認します。
構成図
構成図はこちらです。
1階の端末は2台のL2スイッチに収容、2階も同じ。各階のスイッチをコアL2スイッチで集約、みたいなイメージです。
図はdrawthe.netで描いています。YAMLはこちら。描き方はこちら。
環境構築
環境構築のコマンド全文はこちら
# create bridge
ip link add core1 type bridge
ip link add core2 type bridge
ip link add access1 type bridge
ip link add access2 type bridge
ip link add access3 type bridge
ip link add access4 type bridge
# enable STP
ip link set core1 type bridge stp_state 1
ip link set core2 type bridge stp_state 1
ip link set access1 type bridge stp_state 1
ip link set access2 type bridge stp_state 1
ip link set access3 type bridge stp_state 1
ip link set access4 type bridge stp_state 1
# create network namspaces
ip netns add pc1
ip netns add pc2
ip netns add pc3
ip netns add pc4
# create cable(veth pairs)
ip link add core1_e0 type veth peer name core2_e0
ip link add core1_e1 type veth peer name access1_e1
ip link add core1_e2 type veth peer name access3_e1
ip link add core2_e1 type veth peer name access2_e1
ip link add core2_e2 type veth peer name access4_e1
ip link add access1_e0 type veth peer name access2_e0
ip link add access3_e0 type veth peer name access4_e0
ip link add access1_e2 type veth peer name eth0 netns pc1
ip link add access2_e2 type veth peer name eth0 netns pc2
ip link add access3_e2 type veth peer name eth0 netns pc3
ip link add access4_e2 type veth peer name eth0 netns pc4
# PC
ip netns exec pc1 ip addr add 10.0.0.1/24 dev eth0
ip netns exec pc2 ip addr add 10.0.0.2/24 dev eth0
ip netns exec pc3 ip addr add 10.0.0.3/24 dev eth0
ip netns exec pc4 ip addr add 10.0.0.4/24 dev eth0
ip netns exec pc1 ip link set lo up
ip netns exec pc1 ip link set eth0 up
ip netns exec pc2 ip link set lo up
ip netns exec pc2 ip link set eth0 up
ip netns exec pc3 ip link set lo up
ip netns exec pc3 ip link set eth0 up
ip netns exec pc4 ip link set lo up
ip netns exec pc4 ip link set eth0 up
# attach ports to bridges
ip link set core1_e0 master core1
ip link set core1_e1 master core1
ip link set core1_e2 master core1
ip link set core2_e0 master core2
ip link set core2_e1 master core2
ip link set core2_e2 master core2
ip link set access1_e1 master access1
ip link set access1_e0 master access1
ip link set access1_e2 master access1
ip link set access2_e1 master access2
ip link set access2_e0 master access2
ip link set access2_e2 master access2
ip link set access3_e1 master access3
ip link set access3_e0 master access3
ip link set access3_e2 master access3
ip link set access4_e1 master access4
ip link set access4_e0 master access4
ip link set access4_e2 master access4
# linkup
ip link set core1_e0 up
ip link set core1_e1 up
ip link set core1_e2 up
ip link set core1 up
ip link set core2_e0 up
ip link set core2_e1 up
ip link set core2_e2 up
ip link set core2 up
ip link set access1_e1 up
ip link set access1_e0 up
ip link set access1_e2 up
ip link set access1 up
ip link set access2_e1 up
ip link set access2_e0 up
ip link set access2_e2 up
ip link set access2 up
ip link set access3_e1 up
ip link set access3_e0 up
ip link set access3_e2 up
ip link set access3 up
ip link set access4_e1 up
ip link set access4_e0 up
ip link set access4_e2 up
ip link set access4 up
Bridgeの作成
以下のコマンドでcore1という名前のLinux Bridgeを作成します。access1等、他のBridgeも作成します。作成方法は先のコマンド全文を見てください。
ip link add core1 type bridge
STPの有効化
以下のコマンドでcore1 BridgeのSTPを有効にします。
ip link set core1 type bridge stp_state 1
PCの作成
PC(の代わりになるnetwork namespace)を作成をします。
ip netns add pc1
ケーブルの作成
仮想的なケーブルであるvEthernet Pairを作成します。
この時点ではcore1_e0という名前のポートの対向がcore2_e0という名前のポートであるケーブルを作っているだけです。どのBridgeにも所属していません。
ip link add core1_e0 type veth peer name core2_e0
PCの設定
PCのIP設定、リンクアップします。
ip netns exec pc1 ip addr add 10.0.0.1/24 dev eth0
ip netns exec pc1 ip link set lo up
ip netns exec pc1 ip link set eth0 up
Bridgeにポートを付ける
ip linkコマンドでcore1_e0ポートの親をcore1 Bridgeに設定します。まだリンクアップはしていません。
ip link set core1_e0 master core1
ひと呼吸
ここでひと呼吸して落ち着いてください。
今回作成したネットワークはL2レイヤで見るとループ構造になっています。設定がおかしかったり、実装が不十分だとL2ループが発生します。
この環境はすべてのオブジェクトを仮想的に1台のマシンの中でエミュレーションしています。L2ループが発生するとCPU使用率が跳ね上がります。
別のコンソールウィンドウを開いてtopコマンドを実行するか、定期的にuptimeを発行してCPU使用率を監視しましょう。
この環境ですべてのBridgeを止めるコマンドは以下のコマンドです。CPU使用率が跳ね上がって、ループが疑われたら以下のコマンド投入する心の準備をしておきましょう。
ip link set core1 down
ip link set core2 down
ip link set access1 down
ip link set access2 down
ip link set access3 down
ip link set access4 down
Bridgeリンクアップ
Bridgeの各ポートとBridge自身をリンクアップさせます。
CPU使用率が跳ね上がってきたらBridgeをリンクダウンさせましょう。
ip link set core1_e0 up
ip link set core1_e1 up
ip link set core1_e2 up
ip link set core1 up
状態確認
STP状態確認
bridge linkコマンドでSTPの状態が分かります。
出力が横長で読みづらいのですが、各ポートについて「state listening」とか「state forwarding」とかが出力されるはずです。
(CPU使用率が跳ね上がってきたらBridgeをリンクダウンさせましょう)
# bridge link show
187: core2_e0@core1_e0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master core2 state blocking priority 32 cost 2
188: core1_e0@core2_e0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master core1 state listening priority 32 cost 2
189: access1_e1@core1_e1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master access1 state listening priority 32 cost 2
190: core1_e1@access1_e1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master core1 state listening priority 32 cost 2
191: access3_e1@core1_e2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master access3 state listening priority 32 cost 2
192: core1_e2@access3_e1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master core1 state listening priority 32 cost 2
193: access2_e1@core2_e1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master access2 state listening priority 32 cost 2
194: core2_e1@access2_e1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master core2 state listening priority 32 cost 2
195: access4_e1@core2_e2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master access4 state listening priority 32 cost 2
196: core2_e2@access4_e1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master core2 state listening priority 32 cost 2
197: access2_e0@access1_e0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master access2 state blocking priority 32 cost 2
198: access1_e0@access2_e0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master access1 state listening priority 32 cost 2
199: access4_e0@access3_e0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master access4 state listening priority 32 cost 2
200: access3_e0@access4_e0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master access3 state listening priority 32 cost 2
201: access1_e2@tunl0: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 master access1 state listening priority 32 cost 2
202: access2_e2@tunl0: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 master access2 state listening priority 32 cost 2
203: access3_e2@tunl0: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 master access3 state listening priority 32 cost 2
204: access4_e2@tunl0: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 master access4 state listening priority 32 cost 2
awkで必要な列だけ取り出してsortでアルファベット順に並べるとちょっと見やすくなります。
# bridge link show | awk -F '[@ ]' '{print $2"\t"$10}' | sort
access1_e0 listening
access1_e1 listening
access1_e2 listening
access2_e0 listening
access2_e1 listening
access2_e2 listening
access3_e0 listening
access3_e1 listening
access3_e2 listening
access4_e0 listening
access4_e1 listening
access4_e2 listening
core1_e0 listening
core1_e1 listening
core1_e2 listening
core2_e0 listening
core2_e1 listening
core2_e2 listening
疎通確認
pc1からpc2宛にpingしてみます。STPのトポロジ計算が終わるまではpingは成功しません。60秒間pingしても疎通できなければ何かがおかしいです。
(CPU使用率が跳ね上がってきたらBridgeをリンクダウンさせましょう)
# ip netns exec pc1 ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=2.08 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.045 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.044 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.044 ms
^C
--- 10.0.0.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3117ms
rtt min/avg/max/mdev = 0.044/0.553/2.081/0.881 ms
最終状態確認
pingが通るようになったら、最終的にSTPがどの状態に収束したのかを確認しましょう。このトポロジだと、どこか2か所に「blocking」と出てくるはずです。
# bridge link show | awk -F '[@ ]' '{print $2"\t"$10}' | sort
access1_e0 forwarding
access1_e1 forwarding
access1_e2 forwarding
access2_e0 blocking
access2_e1 forwarding
access2_e2 forwarding
access3_e0 forwarding
access3_e1 forwarding
access3_e2 forwarding
access4_e0 forwarding
access4_e1 forwarding
access4_e2 forwarding
core1_e0 forwarding
core1_e1 forwarding
core1_e2 forwarding
core2_e0 blocking
core2_e1 forwarding
core2_e2 forwarding
access2_e0とcore2_e0がblocking portなので、以下の状態になっています。pc1からpc2にいくのにものすごい遠回りをしています。[2]
今回はSTPのパラメータを設定せずにすべてデフォルト値のまま動かしました。どのポートがblockingになるのかはランダムに決まります。[3]
パケットキャプチャ
まずはパケットキャプチャを取ってみましょう。
tcpdump -U -i access1_e1 -w - | /mnt/c/Program\ Files/Wireshark/Wireshark.exe -k -i - &
start cmd /c ssh LinuxのIPアドレス tcpdump -U -i access1_e1 -w - ^| "c:\Program Files\Wireshark\Wireshark.exe" -k -i -
パケットがうまく取れました。
- フレームが「IEEE 802.3」として認識されていますね。普段よく見るIP、TCPだとここは「Ethernet II」になってるんですよね。
- Logical-Link Control(LLC)という見慣れないレイヤが入ってます。
- Spanning Tree ProtocolのBPDU(Bridge Protocol Data Unit)フレームが見えます。
- Root BridgeのID、各種タイマーの値が含まれていることも分かります。
BPDUのフレームの構造はこちらのサイトが詳しいです。ネットワークを学ぶ者なら必読のサイトです。
後片付け
作成したオブジェクトは再起動すれば消えます。
再起動なしで消したいときは以下のコマンドを実行してください。
ip -br link show type veth | awk -F '[@ ]' '{print $1}' | xargs -n 1 -t ip link delete
ip -br link show type bridge | awk '{print $1}' | xargs -n 1 -t ip link delete
ip -a netns delete
この記事で作っている以外のvethやLinux Bridge、network namespaceがあるときは以下のコマンドです。
ip link delete access1_e0
ip link delete access1_e2
ip link delete access2_e2
ip link delete access3_e0
ip link delete access3_e2
ip link delete access4_e2
ip link delete core1_e0
ip link delete core1_e1
ip link delete core1_e2
ip link delete core2_e1
ip link delete core2_e2
ip link delete core1
ip link delete core2
ip link delete access1
ip link delete access2
ip link delete access3
ip link delete access4
ip netns delete pc1
ip netns delete pc2
ip netns delete pc3
ip netns delete pc4
参考:動作確認を行った環境について
# 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 tools
まとめ
WSLの中に仮想的にスイッチングハブを作ってSTPが動作すること、パケットキャプチャが取れることが分かりました。
BPDU交換でSTPトポロジを形成していく様子や、経路切り替わりするときのパケットの動きも今後見ていきたいと思います。
Discussion