ヤマハルーターのLuaスクリプトとラズパイに構築したPHP+MySQL+Grafanaを組み合わせて、ヤマハルーターのトラフィックを集計してグラフ化してみました。
しくみ
ヤマハルーターでLuaスクリプトを定期実行させ、トラフィック情報を収集し、ラズパイへ送信するようにします。
ラズパイにはあらかじめ送信先となるAPIを用意しておき、送信されたデータを解析して、MySQLなどのデータベースにトラフィック情報を格納します。格納したデータはGrafanaなどの可視化ツールによりグラフ化し、ブラウザからかんたんに確認できるようにします。
Grafanaはデータ可視化ツールと呼ばれるもので、手軽にデータベースの内容をグラフ化して表示することができるアプリケーションです。ラズパイにインストールすることで、ブラウザからデータベースの内容を視覚的に参照することができます。Grafanaの概要・使い方については、こちらで紹介しています。
今回はAPIの作成にPHPを使用しましたが、MySQLにデータを登録できるのであれば、どの言語でも対応できます。ヤマハルーターには専用のLuaスクリプトAPIが用意されていますが、多くの場合はピンポイントで数値を得ることはできません。Luaスクリプトからコマンドを実行し、出力を文字列解析する必要が出てきます。Luaスクリプトでも解析は不可能ではありませんが、できることが限られているため、今回はルーターからはコマンドの実行結果をそのまま送信し、API側で正規表現による文字列解析を行うことにしています。
なお、今回は以下の項目を集計対象とします。
- LAN1の受信・送信(合計・IPv4のみ・IPv6のみ)
- LAN2の受信・送信(合計・IPv4のみ・IPv6のみ)
- PP1の受信・送信(PPPoE)
ちなみに、GitHubにはtunnelの集計プログラムも掲載しています。VPNのトラフィックも集計することができますので、興味があれば参照してみてください。
ヤマハルーターのLuaスクリプトの基本
ヤマハルーターでのLuaスクリプトの扱いについて、基本事項をまとめておきます。
コンソールでLuaスクリプトを動かしてみる
ヤマハルーターでコンソールからLuaスクリプトを実行するには、lua -e
コマンドを使用します。
lua -e 'print(123)'
スクリプトの実行には管理ユーザーとしてルーターにログインしておく必要があります。
また、コンソールから直接実行する場合、スクリプトは改行せず、1行にしておく必要があります。コピペして流し込む前に改行をなくしておく必要があるので、注意してください。
WebサイトやAPIへPOSTするには?
WebサイトやAPIへのPOSTには、ヤマハルーター独自APIであるrt.httprequestを使用します。
例えば、http://test.lc/rtxlogger/api.phpにPOSTする場合は、以下のようなコードとなります。
-- POSTデータ作成
req_t = {
url = "http://test.lc/rtxlogger/api.php",//URL
method = "POST",
content_type = "application/x-www-form-urlencoded;charset=sjis"
}
req_t.post_text =
string.format(
"key1=%s&key2=%s",
"hoge",
"fuga"
)
注意点としては、
- 文字コードがShiftJISになること
- エンコードは自分で行う必要があること
などがあげられます。
まず、送信される際の文字コードはUTF-8ではなく、ShiftJISになります。日本語を含むデータを送信する際に文字化けの原因となりますので、content_typeに文字コードがShiftJISであることを明記しておくことをおすすめします。上記サンプルにも盛り込んであります。
また、POSTするボディは自分でエンコードを行う必要があります。サンプルのようにstring.formatを使用して、content_typeにあったエンコードを行ってください。上記サンプルではcontent_typeをapplication/x-www-form-urlencodedとしていますので、「key=value」形式のデータを&(アンパサンド)でつなげています。エンコードした後の形式がわからない場合は、Postmanなどのツールを使用してみるといいかもしれません。
コマンドの実行
ヤマハルーターのコマンドを実行するには、rt.commandを使用します。
rtn, env = rt.command("show environment")
第1引数には実行結果(成功したらtrue)、第2引数には標準出力(コマンドの出力)が格納されます。管理ユーザーとして実行されますので、十分内容を確認してから実行してください。
rt.commandでコマンドの実行結果を取得し、rt.httprequestでラズパイに送信する感じになりますね。
Luaスクリプトの作成
それではさっそくLuaスクリプトを作成していきましょう。なお、コード全体はGitHubを参照してください。
-- YAMAHAルーターで実行するスクリプト
-- コマンド
rtn, lan1 = rt.command("show status lan1")
rtn, lan2 = rt.command("show status lan2")
rtn, pp1 = rt.command("show status pp 1")
-- POSTデータ作成
req_t = {
url = "http://test.lc/rtxlogger/api.php",
method = "POST",
content_type = "application/x-www-form-urlencoded;charset=sjis"
}
req_t.post_text =
string.format(
"lan1=%s&lan2=%s&pp1=%s",
lan1,
lan2,
pp1,
)
-- サーバーにPOST
rt.httprequest(req_t)
LAN1、LAN2、PP1のステータスを取得し、ラズパイにPOSTしています。
コンソールからテストする際は、lua -e
につづいて上のスクリプトを記述します。改行は除去する必要があります。
lua -e 'rtn, lan1 = rt.command("show status lan1")rtn, lan2 = rt.command("show status lan2")rtn, pp1 = rt.command("show status pp 1")req_t = { url = "http://test.lc/rtxlogger/api.php", method = "POST", content_type = "application/x-www-form-urlencoded;charset=sjis"}req_t.post_text = string.format( "lan1=%s&lan2=%s&pp1=%s", lan1, lan2, pp1,)rt.httprequest(req_t)'
ラズパイにAPIを作成する
次にルーターからPOSTされたデータを受けるAPIをラズパイに作成します。
<?php
require_once __DIR__ . '/vendor/autoload.php';
//
// Eloquentの初期化
//
class_alias(Illuminate\Database\Capsule\Manager::class, 'DB');
$database = new DB();
$config = [
'driver' => 'mysql', // データベースの種類
'host' => 'localhost', // ホスト名
'database' => '', // データベース名
'username' => '', // ユーザー名
'password' => '', // パスワード
// 'charset' => 'utf8mb4', // 文字セット(任意)
// 'collation' => 'utf8mb4_general_ci', // コレーション(任意)
];
$database->addConnection($config);
$database->setAsGlobal();
$database->bootEloquent();
//
// POSTされたときの処理
//
if ($_SERVER["REQUEST_METHOD"] == "POST") {
preg_match("/送信パケット: (\d+)/u", mb_convert_encoding($_POST["lan1"], "UTF-8", "sjis"), $d["lan1_total_send"]);
preg_match("/受信パケット: (\d+)/u", mb_convert_encoding($_POST["lan1"], "UTF-8", "sjis"), $d["lan1_total_receive"]);
preg_match("/IPv4\(全体\/ファストパス\): (\d+) パケット/u", mb_convert_encoding($_POST["lan1"], "UTF-8", "sjis"), $d["lan1_v4_send"]);
preg_match("/IPv6\(全体\/ファストパス\): (\d+) パケット/u", mb_convert_encoding($_POST["lan1"], "UTF-8", "sjis"), $d["lan1_v6_send"]);
preg_match("/IPv4: (\d+) パケット/u", mb_convert_encoding($_POST["lan1"], "UTF-8", "sjis"), $d["lan1_v4_receive"]);
preg_match("/IPv6: (\d+) パケット/u", mb_convert_encoding($_POST["lan1"], "UTF-8", "sjis"), $d["lan1_v6_receive"]);
preg_match("/送信パケット: (\d+)/u", mb_convert_encoding($_POST["lan2"], "UTF-8", "sjis"), $d["lan2_total_send"]);
preg_match("/受信パケット: (\d+)/u", mb_convert_encoding($_POST["lan2"], "UTF-8", "sjis"), $d["lan2_total_receive"]);
preg_match("/IPv4\(全体\/ファストパス\): (\d+) パケット/u", mb_convert_encoding($_POST["lan2"], "UTF-8", "sjis"), $d["lan2_v4_send"]);
preg_match("/IPv6\(全体\/ファストパス\): (\d+) パケット/u", mb_convert_encoding($_POST["lan2"], "UTF-8", "sjis"), $d["lan2_v6_send"]);
preg_match("/IPv4: (\d+) パケット/u", mb_convert_encoding($_POST["lan2"], "UTF-8", "sjis"), $d["lan2_v4_receive"]);
preg_match("/IPv6: (\d+) パケット/u", mb_convert_encoding($_POST["lan2"], "UTF-8", "sjis"), $d["lan2_v6_receive"]);
preg_match("/受信: (\d+) パケット/u", mb_convert_encoding($_POST["pp1"], "UTF-8", "sjis"), $d["pp1_receive"]);
preg_match("/送信: (\d+) パケット/u", mb_convert_encoding($_POST["pp1"], "UTF-8", "sjis"), $d["pp1_send"]);
// 現在時刻を取得
$dt = new DateTime();
$dt = $dt->modify('-9 hours'); //GrafanaはUTCで登録する必要あり
// DBに入れるデータを作成する
$data = array_map(function ($item) {
if (isset($item[1])) {
return $item[1];
} else {
return null;
}
}, $d);
$data["update_at"] = $dt;
$data["host"]=$_SERVER["REMOTE_ADDR"];
// DBにインサートする
DB::table("traffics")->insert($data);
}
POSTされたデータを取得し、取得されたデータからパケットに関する必要な数値を抽出しています。ここは正規表現で抽出するしかありませんので、少し大変です。ダイレクトに値を取得できるAPIがルーターに用意されていればよかったのですが、現状では用意されていませんので、がんばるしかないようです。文字コードがShiftJISであるため、pgrep_matchで抽出する前に、一応UTF-8に変換しています。
抽出した通信量データはひとつの配列にまとめ、illuminate/Databaseを使用して、データベースに格納しています。illuminate/DatabaseはSQLフリーでデータベースを扱えるライブラリで、Laravelのクエリビルダとまったく同じことができます。PDOやSQL文に関する知識がなくても、MySQLやSQLiteにデータを保存することができますので、とても便利です。インストールもcomposerからコマンド一発でできてしまいます。ぜひ活用してみてください。
なお、データベースを用意するのが大変という方のために、データをテキストファイルに書き出すコードも掲載しておきます。上のコードに以下の関数を追加して、logging($d)
で呼び出せば、データベースがなくても動作を確認できますよ。
function logging($val)
{
// ログ保存ファイル名
define("TXTFILE", "rtxlogger.log");
// ファイルを追記モードでオープン
$fh = fopen(TXTFILE, "a+");
// GETされたデータをすべて取得して、配列として取得
$str = print_r($val, true);
// ファイルに追記する
fputs($fh, $str);
// ファイルを閉じる
fclose($fh);
}
もちろんですが、ディレクトリの書き込み許可をお忘れなく。
MySQLにデータベースを作成する
収集したデータを保存するためのデータベースを作成します。データベースの作成方法については、ここでは省略しますが、カラムの作成には次のSQLをご利用ください。
ALTER TABLE `traffics` ADD `host` VARCHAR(15) NULL AFTER `id`;
ALTER TABLE `traffics` ADD `lan1_total_send` BIGINT NULL;
ALTER TABLE `traffics` ADD `lan1_total_receive` BIGINT NULL;
ALTER TABLE `traffics` ADD `lan1_v4_send` BIGINT NULL;
ALTER TABLE `traffics` ADD `lan1_v6_send` BIGINT NULL;
ALTER TABLE `traffics` ADD `lan1_v4_receive` BIGINT NULL;
ALTER TABLE `traffics` ADD `lan1_v6_receive` BIGINT NULL;
ALTER TABLE `traffics` ADD `lan2_total_send` BIGINT NULL;
ALTER TABLE `traffics` ADD `lan2_total_receive` BIGINT NULL;
ALTER TABLE `traffics` ADD `lan2_v4_send` BIGINT NULL;
ALTER TABLE `traffics` ADD `lan2_v6_send` BIGINT NULL;
ALTER TABLE `traffics` ADD `lan2_v4_receive` BIGINT NULL;
ALTER TABLE `traffics` ADD `lan2_v6_receive` BIGINT NULL;
ALTER TABLE `traffics` ADD `pp1_receive` BIGINT NULL;
ALTER TABLE `traffics` ADD `pp1_send` BIGINT NULL;
ALTER TABLE `traffics` ADD `update_at` DATETIME NULL;
テストしてみる
Luaスクリプト、APIの作成からデータベースの容易まですべて終わったら、きちんとデータを収集できるかテストしてみましょう。ルーターのコンソールでスクリプトを実行してみます。
lua -e 'rtn, lan1 = rt.command("show status lan1")rtn, lan2 = rt.command("show status lan2")rtn, pp1 = rt.command("show status pp 1")req_t = { url = "http://test.lc/rtxlogger/api.php", method = "POST", content_type = "application/x-www-form-urlencoded;charset=sjis"}req_t.post_text = string.format( "lan1=%s&lan2=%s&pp1=%s", lan1, lan2, pp1,)rt.httprequest(req_t)'
データベースにデータが蓄積されていれば、成功です!
Luaスクリプトの定期実行
毎日AM1:00に作成したLuaスクリプトを実行させ、データを収集するようにします。
schedule at ID 01:00 * lua -e 'スクリプトをここに貼り付け'
例えばこんな感じになります。
schedule at 2 01:00 * lua -e 'rtn, lan1 = rt.command("show status lan1")rtn, lan2 = rt.command("show status lan2")rtn, pp1 = rt.command("show status pp 1")req_t = { url = "http://test.lc/rtxlogger/api.php", method = "POST", content_type = "application/x-www-form-urlencoded;charset=sjis"}req_t.post_text = string.format( "lan1=%s&lan2=%s&pp1=%s", lan1, lan2, pp1,)rt.httprequest(req_t)'
毎時に行いたいなら、以下のようになります。
schedule at 2 *:00 * lua -e 'rtn, lan1 = rt.command("show status lan1")rtn, l
an2 = rt.command("show status lan2")rtn, pp1 = rt.command("show status pp 1")re
q_t = { url = "http://test.lc/rtxlogger/api.php", method = "POST", con
tent_type = "application/x-www-form-urlencoded;charset=sjis"}req_t.post_text =
string.format( "lan1=%s&lan2=%s&pp1=%s", lan1, lan2, pp1,)rt.htt
prequest(req_t)'
IDは重複していると、既存の設定を上書きしてしまいますので注意してください。多くの場合、スケジュールのID1にはNTPによる自動時刻修正が登録されているため、上記のサンプルではあえてIDを2としておきました。
誤って消してしまった人のためにNTPのスケジュールコマンドも掲載しておきます~
schedule at 1 */Sun 01:00 * ntpdate ntp.nict.jp
自分もうっかりやってしまいましたので(^^;)
Grafanaでグラフ化してみる
データの蓄積も確認できましたので、Grafanaでグラフを表示してみます。Grafanaの基本的な使い方については、以下の記事をご覧ください。
新しいダッシュボードを作成し、クエリビルダで書き出したいデータを設定します。
あわせてサイドメニューでUnit(単位)をBytesにします。
これでグラフが表示されました。
ただし、ヤマハルーターはパケットで通信量を表記しているため、得られた数値をbyteに変換する必要があります。そこで、クエリエディタに切り替えて、データベースから数値を取得するときに128倍(∵1パケット128byte)するようにクエリを編集します。
きちんと表示できました!完成です!
通信種類ごとの円グラフも描いてみました。
青はIPoE、赤はIPSecVPN、黄色はPPPoEの通信量(いずれも下り)を示します。
私の場合、拠点間VPNとリモートVPNのみPPPoEに流し、その他すべてIPoEに流すようにしています。最近はFireTVでYouTubeを垂れ流しにしていることが多かったので、ほとんどがIPoEのトラフィックかなと思っていましたが、意外とVPN通信が多いことがわかりました。VPN接続先にある自宅サーバーに大量の写真をバックアップしているため、気づかぬうちにトラフィックが増えていたようです。
まとめ
RTXのLuaスクリプト機能を使い、ラズパイでトラフィックを監視、Grafanaでグラフ化するという今回の企画。実際に1週間ほど運用してみた結果がこちらです。
ちゃんと通信量が記録されていることがわかります。この1週間は不在にしていたため、あまり大きなトラフィック増加はありませんでしたが、ふだんはもっとトラフィックが増えているものと思われます。
固定回線であれば自己満足にすぎませんが、RTX810をはじめ、一部のヤマハルーターはUSB通信端末にも対応しています。移動拠点の場合は通信量を分析することで、最適なプラン設定や無駄な通信を洗い出すのに役立ちそうです。
ちなみに、Grafanaは通知も充実しています。一定のトラフィックを超えたら、LINENotifyで通知するといったこともGrafanaの機能を使えば、かんたんに実現できます。組み合わせてみると面白いかもしれませんね。
まだ1週間しかも不在時ですので、もう少しデータがほしいところです。データが蓄積されてきて、なにか発見があれば、追記していきたいと思います。
コメント