たまたま買った Wi-Fiアクセスポイント (以下 Wi-Fi AP と略記) が、 SNMP 非対応なのはもちろん、 Web管理画面でさえ通信量を見ることができなかったので、 この Wi-Fi AP を接続しているスイッチ (ネットワークハブ) NETGEAR GS108E (と GS116E) のポートの通信量を取得して Cacti (最近の流行りは Grafana ?) で可視化してみた。 Wi-Fi そのものの通信量ではないが、 Wi-Fi AP とスイッチとの間の通信量を測ることができる。
買ったのは NETGEAR WiFi中継機/802.11ac wave2 Nighthawk X4 EX7300-100JPS (以下 EX7300 と略記)。 たまたま amazon で税込5,980円 (送料無料) で売っていて、 中継機 (ワイヤレスエクステンダー) としてだけでなくアクセスポイントとしても使えるとのことなので (NETGEAR製ということもあって) 衝動買い。 もともとルータの機能は必要ないし、 コンセント直挿しで場所を取らないので、 リビング用としてちょうどいいかなと思った次第。
さいきん Wi-Fi中継機が流行りらしく、 各社からお手頃価格で多数の製品が発売されている。 コンセント直挿しで場所を取らないので家庭向きと言えるが、 残念なことにアクセスポイントとして使える製品は (NETGEAR や TP-Link などのごく一部の例外を除くと) ほとんど無い。
中継機も (広義の) アクセスポイントと言えるが、 親機 (Wi-Fiルータ) と無線でつながるのが中継機で、 有線でつながるのが (狭義の) アクセスポイント。 あたりまえだが、 有線でつなぐことができる (家庭内でケーブルを配線できる) なら、 有線のほうがいいに決まってる。
ちなみに私が初めて Wi-Fi中継機を使ったのは 10年前なので、 いまさら流行って少々戸惑いを覚える。
EX7300 は、 たった 6000円なのに性能的には悪くない。 ルータ機能が不要ならお買得と思う。 ノートPC Lavie HZ を Wi-Fi で EX7300 につないで iperf3 を走らせて測定してみたら 530Mbps くらい出ている。
C:\Users\sengoku>c:\bin\iperf3.exe -c esaka -P 5 Connecting to host esaka, port 5201 [ 4] local 192.168.18.147 port 50119 connected to 192.168.18.20 port 5201 [ 6] local 192.168.18.147 port 50120 connected to 192.168.18.20 port 5201 [ 8] local 192.168.18.147 port 50121 connected to 192.168.18.20 port 5201 [ 10] local 192.168.18.147 port 50122 connected to 192.168.18.20 port 5201 [ 12] local 192.168.18.147 port 50123 connected to 192.168.18.20 port 5201 [ ID] Interval Transfer Bandwidth [ 4] 0.00-1.00 sec 13.4 MBytes 112 Mbits/sec [ 6] 0.00-1.00 sec 13.2 MBytes 111 Mbits/sec ... [SUM] 9.00-10.01 sec 63.9 MBytes 533 Mbits/sec - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth [ 4] 0.00-10.01 sec 130 MBytes 109 Mbits/sec sender [ 4] 0.00-10.01 sec 130 MBytes 109 Mbits/sec receiver [ 6] 0.00-10.01 sec 128 MBytes 108 Mbits/sec sender [ 6] 0.00-10.01 sec 128 MBytes 108 Mbits/sec receiver [ 8] 0.00-10.01 sec 128 MBytes 107 Mbits/sec sender [ 8] 0.00-10.01 sec 128 MBytes 107 Mbits/sec receiver [ 10] 0.00-10.01 sec 126 MBytes 106 Mbits/sec sender [ 10] 0.00-10.01 sec 126 MBytes 106 Mbits/sec receiver [ 12] 0.00-10.01 sec 124 MBytes 104 Mbits/sec sender [ 12] 0.00-10.01 sec 124 MBytes 104 Mbits/sec receiver [SUM] 0.00-10.01 sec 636 MBytes 534 Mbits/sec sender [SUM] 0.00-10.01 sec 636 MBytes 534 Mbits/sec receiver iperf Done.
たまたま手元にあった TP-Link WiFi 無線LAN ルーター Archer C3150 11ac にも Wi-Fi でつないで同様に iperf3 を走らせて比較してみたら、 ほとんど差がない。 同等と言ってもいいレベル。 値段は 3倍くらい違うのだから、 EX7300 をブッチぎって欲しかった。
しかもこの Archer C3150 は OpenVPN を使ってると腐るバグがあるように感じる (いちど電源を切らないと復旧しない)。 ヘンテコな付加機能よりも安定性こそ一番重要なのではないか? ついでに言うと勝手に外部と通信しているのが気持ち悪い。 値段が高い Wi-Fiルータにはもうちょっと頑張ってもらいたいところ。
もちろん、 このノートPC を無線LAN ではなく有線LAN につなげば 940Mbps くらいは出るので、 ノートPC の性能がボトルネックになっているわけではない。 というか Wi-Fi のパフォーマンスは周囲の電波状況に強く影響され (もちろん空いてるチャンネルを使っている)、 有線側の状況はほとんど関係がない。 電波暗室とかで測定すれば、 EX7300 と Archer C3150 の差が開くのかもしれないが、 実用上は無意味。
性能でも安定性でも申し分のない EX7300 ではあるが、 残念なことに Web管理画面を隅から隅まで調べてみても通信量を見る方法がない。 どんな安物 Wi-Fiルータ (or Wi-Fi AP) でも、 通信量 (パケット数とか) を表示させる方法はあるものだと思っていたが、 中継機は Wi-Fiルータとは製品コンセプトが違うのかも?
通信量を見ることができない通信機器があるとは思っていなかったので、 意表を突かれた感じ。 今後 Wi-Fi AP を買う際は注意したい。 やっぱり (ルータ機能が必要なくても) 素直に Wi-Fiルータを買うべきなのかも。 ほんとうは (ルータ機能がない) アクセスポイント専用機が欲しいのだけど...
幸い、 この EX7300 を接続しているスイッチ NETGEAR GS108E が「スマート」で、 スイッチの各ポートごとの通信量 (≒ Wi-Fi AP の通信量) を GS108E/GS116E 付属の Windows アプリ ProSafe Plus で見ることができる。
Web アプリではなく Windows アプリなので、 表示された通信量の数値を Web スクレイピングなどのお手軽な方法で取り込むことはできないが、 google で検索したら ProSafe Plus と同等のことができる Python プログラムが見つかった。 6年前に開発が終了しているが、 このスイッチを買ったのも 6年前なので無問題。
さっそくダウンロードして (ざっと内容を確認したのち) 走らせてみる:
senri $ git clone https://github.com/Z3po/Netgearizer.git ↵ Cloning into 'Netgearizer'... remote: Counting objects: 88, done. remote: Total 88 (delta 0), reused 0 (delta 0), pack-reused 88 Unpacking objects: 100% (88/88), done. Checking connectivity... done. senri $ Netgearizer/netgearizer.py ↵ please select one of the following switches with "selectSwitch $NR" --> 0: 192.168.18.239 Information: switch-name: living switch-dhcp: disabled switch-type: GS108Ev2 switch-ip: 192.168.18.239 switch-mac: 4c:60:de:69:01:23 switch-firmware: 1.00.06 switch-netmask: 255.255.255.0 switch-gateway: 192.168.18.1 (Cmd)
スイッチは 3つ (GS116E と GS108E 2台) あるのに、 なぜか 1つ (リビングに設置した GS108E) しか表示されない。 selectSwitch コマンドでスイッチを選択し (選択肢は 1つしか無いが)、 getPortStatistics コマンドでポートごとの通信量を表示できるようだ。
実行してみる:
(Cmd) selectSwitch 0 ↵ (Cmd) getPortStatistics ↵ switch-port-statistics: -> Port 01: >> send: 2664610542955 >> receive: 5993649462240 >> crcerrors: 0 -> Port 02: >> send: 6062498001 >> receive: 328715447216 >> crcerrors: 0 -> Port 03: >> send: 183424114643 >> receive: 8723285000144 >> crcerrors: 0 -> Port 04: >> send: 39935317815 >> receive: 26550837425456 >> crcerrors: 0 -> Port 05: >> send: 137960667631 >> receive: 790398362288 >> crcerrors: 0 -> Port 06: >> send: 3948410207 >> receive: 1156872131840 >> crcerrors: 0 -> Port 07: >> send: 2789623 >> receive: 20875141728 >> crcerrors: 0 -> Port 08: >> send: 1989939088 >> receive: 2852348890112 >> crcerrors: 0 (Cmd)
一見、 正しく動いているように見えるが、 「send:」に表示されている数値は (ProSafe Plus で見たときの) 受信バイト数で、 「receive:」に表示されている数値は送信バイト数を 16倍した値になっている。
受信と送信が入れ替わっているのはご愛敬だが、 16倍になっているのは... netgearizer.py (Python で書かれたスクリプト) を読んでみると、 単純なバグだった。 次のパッチをあてると正常に動作した:
--- netgearizer.py.org 2018-04-07 22:04:42.292912972 +0900 +++ netgearizer.py 2018-04-09 11:34:50.124297214 +0900 @@ -284,5 +284,5 @@ for port in hexvalue: - sendstats = self.__convertFromHex(port[2:18],'cipher') - receivestats = self.__convertFromHex(port[19:35],'cipher') - crcerrors = self.__convertFromHex(port[36:53],'cipher') + receivestats = self.__convertFromHex(port[2:18],'cipher') + sendstats = self.__convertFromHex(port[18:34],'cipher') + crcerrors = self.__convertFromHex(port[82:98],'cipher') result.append(( 'Port ' + str(port[:2]), (('send', sendstats), ('receive', receivestats), ('crcerrors', crcerrors)))) @@ -345,15 +345,16 @@ for key in self.switches.keys(): self.switchList.append(key) print '--> ' + str(counter) + ': ' + key print 'Information: ' self.selectedSwitch = key self.__printResult(True) self.selectedSwitch = None - return True + counter += 1 + return True else: print 'please select one of the switches you get with getSwitches first.' return False if 'ERROR' in resultdict: found = None for key in self.switchattributes.keys():
送信バイト数が 16倍になるのは、 部分文字列の切り出しで「port[18:34]」 (変数「port」の文字列の 18文字目から 34文字目まで切り出す) とすべきところを、 「port[19:35]」 としてしまっていたため。 変数「port」には、 スイッチから受信したデータを 16進数に変換した文字列が格納されている。 1文字ずれて切り出したため、 16倍 (16進数で 1桁ぶん) になっていた。
16進数で 16文字つまり 64bit だが、 ProSafe Plus で表示される GS116E の送受信バイト数が小さすぎると思っていたら、 GS116E はバイト数が 32bit (4GiB) でラップアラウンドしているようだ。 つまり上位 32bit は常にゼロ。ポートによっては毎日ラップアラウンドしている。
GS108Ev2 は 40bit (1TiB) を超えるバイト数になっているポートもあるので、 どこでラップアラウンドするか確認できるまで、 まだだいぶかかりそう。 仮に 1ヶ月で 1TiB くらいの通信量だとすると、 もし 48bit (256TiB) が上限なら 20年もかかってしまう。
もし 64bit 全部使ってカウントしているなら、 上限は 16EiB (エクスビバイト) ということになり、 140万年くらいかかってしまう。 ちょっとのコスト増を惜しんでラップアラウンドするより、 64bit きっちり使って半永久にラップアラウンドしないほうがいい。
実は、 私は今まで Python でプログラミングしたことが無い。 文法もろくに知らなかったのだけど、 部分文字列を切り出す際の Python の位置の指定方法は独特だと思う。 このプログラムの作者も Python は慣れていなくて、 うっかりこのようなバグを作り込んでしまった (前の行で 18文字目まで切り出したので、 その次は 19文字目からとしたくなる気持ちは分かる) のではないか?
さらに興味深いのが、 3つあるはずのスイッチが 1つしか見つからなかったバグ。 なんと、 「return True」文のインデント (字下げ) が間違っていただけ。 インデントでブロックを表わす Python ならではのバグ?
インデントが一段階深かったので、 「return True」文が forループの中に入ってしまい、 必ずループが 1回だけで終了してしまっていた。 だからスイッチを 1つ見つけてすぐループを終了していた。 「return True」文の前の空白を削除してループの外へ出したら、 正しく 3つのスイッチを見つけるようになった。 空白の有無で挙動が変わってしまうのは、 やっぱり気持ち悪い。
この netgearizer.py は対話的にコマンドを入力するソフトウェアだが、 通信量を自動計測する際には使いにくいので、 対話せず通信量を取得するだけのプログラムに書き換えた。 不要な機能をばっさり削除したので、 プログラムの行数が半分以下の 317行になった。 この改変版は、
https://www.gcd.org/sengoku/docs/netgearizer.py
からダウンロードできる。 わずかな改変とはいえ、 なにぶん私が初めて書く Python プログラムなので、 変なところがあったらご指摘頂けると幸い。
この改変版を実行する (引数としてインタフェースを指定する) と、 以下のように LAN 内 (同一セグメント内) の全ての NETGEAR スマートスイッチ (GS116E, GS108E, GS105E など) の、 各ポートの受信バイト数、送信バイト数、エラー数を表示する。
senri:~ $ netgearizer.py eth0 ↵ IP: 192.168.18.239 MAC: 4c:60:de:69:01:23 Type: GS108Ev2 switch-port-statistics: Port01 rx:2673873720246 tx:375671471563 err:0 Port02 rx:6410023533 tx:21992185424 err:0 Port03 rx:183424114643 tx:545205312509 err:0 Port04 rx:40052812544 tx:1664344574414 err:1 Port05 rx:138616484625 tx:52251007903 err:0 Port06 rx:3954090302 tx:72431873899 err:0 Port07 rx:2802623 tx:1308888305 err:0 Port08 rx:1989939088 tx:178271805632 err:0 IP: 192.168.18.237 MAC: 4c:60:de:69:04:56 Type: GS108Ev2 switch-port-statistics: Port01 rx:786331851 tx:1283347 err:0 Port02 rx:0 tx:0 err:0 Port03 rx:0 tx:0 err:0 Port04 rx:0 tx:0 err:0 Port05 rx:0 tx:0 err:0 Port06 rx:474556 tx:875905 err:0 Port07 rx:0 tx:0 err:0 Port08 rx:0 tx:0 err:0 IP: 192.168.18.236 MAC: 84:1b:5e:90:81:72 Type: GS116E switch-port-statistics: Port01 rx:2245190893 tx:3192491053 err:0 Port02 rx:2079058908 tx:4030809209 err:0 Port03 rx:0 tx:0 err:0 Port04 rx:2815311290 tx:1592352771 err:0 Port05 rx:3838533539 tx:2573311206 err:0 Port06 rx:1441584273 tx:1249733039 err:0 Port07 rx:747795050 tx:97733030 err:0 Port08 rx:889171012 tx:559797414 err:0 Port09 rx:3757714660 tx:749158734 err:0 Port0a rx:3210386057 tx:2724670950 err:0 Port0b rx:441710237 tx:2183781562 err:0 Port0c rx:0 tx:0 err:0 Port0d rx:4837745 tx:252496639 err:0 Port0e rx:2058650283 tx:3610878767 err:0 Port0f rx:1435416601 tx:403439849 err:0 Port10 rx:0 tx:0 err:0 senri:~ $
私の自宅の LAN ではタグVLAN を使っているため、 以上のコマンドを実行している Linux マシン「senri」の eth0 インタフェースには IP アドレスを割当てていないが、 問題なく動作している。 しかも、 この Linux マシンは GS116E の Port04 に接続していて、 2台の GS108E には GS116E 経由で間接的に繋がっているのだけど、 GS108E の通信量も取得できている。 Windows アプリの ProSafe Plus だと、 スイッチに直接繋げないと管理できないことがある。
改変版 netgearizer.py で取得した送受信バイト数を Cacti (最近の流行りは Prometheus とか Fluentd ?) に読み込ませて EX7300 の通信量をグラフ化すると、 こんな感じ:
In がスイッチのポートの送信バイト数、 つまりスイッチから EX7300 が受信したバイト数、 すなわち Wi-Fi 子機 (スマホなど) がダウンロードした合計。
Out がスイッチのポートの受信バイト数、 つまりスイッチへ EX7300 が送信したバイト数、 すなわち Wi-Fi 子機がアップロードした合計。
実は ProSafe Plus のもう一つの Python 実装である ProSafeLinux も試してみたのだが、 IP アドレスを割当てていないインタフェースを受付けないので、 先に netgearizer.py の書き換えを試みた次第。
ProSafeLinux のほうが (active ではないにせよ) 今でも開発が続いているし、 前述したようなバグも無いようなので、 試してみるべきだとは思うが、 プログラムの行数が netgearizer.py の倍以上 (私が作った改変版の 5倍以上) もある。 通信量の取得といった単純な目的であれば、 短い方がいい。