はじめに
もしあなたがIoTデバイス開発者であれば、実際本番で使うかはともかくSORACOMのSIMを持っておくことをお勧めします。デバイスの開発にとても便利だからです。
例えば「セッション切断」使ったことはありますか?任意のタイミングで通信を切断する機能で、デバイスの回線を再接続する機能の開発で重宝します。正直これだけでSORCOMのSIMを使う価値はあります。
通信を回線側から切られる、って普通の使い方ではテストしづらいんですよね。自デバイスの方から回線を切るとちゃんと切断処理が入るため問題起こりにくいのですが、回線側から切られるとモデムがバグって復帰しなくなる、みたいなケースがあります。またAPIでセッション切断とセッション履歴取得ができるので、10分ごとにセッションを切断して毎回接続復帰できたか、という自動テストも簡単です。
他にもSORACOMにはデバイス開発に役立つ機能が満載でして、例えば、
- SIMの休止: 通信切断された時にデータをバッファしておき、再開したら復旧する機能の確認に使う
- SIMの速度変更: 通信速度が遅くなっても成り立つか、のテストに使う
- メタデータサービス: デバッグ変数を入れるのに使う
- Harvest Data: デバイスからのログを送信、保存、確認するのに使う
などは簡単に使えて便利です。
さて、今まで使っていなかったSORACOM Junctionが、実はデバイス開発にとても役立つのでは?ということに気付きました。なぜ今まで気付かなかったのか?SORACOM Junctionはほぼセキュリティ対策の文脈でしか見ることがなく、そのあたりはクラウドの皆さんがビッグデータとか機械学習とかなんやかんやでうまいことやってくれるから、僕にはあまり関係ないかな、という思いがありました。しかし実際に使ってみると、これは僕たちデバイス開発者の開発や、デバッグや、トラブルシューティングを助けてくれるサービスだ!ということが判明しましたので、記事にしました。
この記事で伝えたいことは、「SORACOM Junctionを使うと、自分自身でパケットキャプチャできないシンプルなIoTデバイスのパケットキャプチャができるようになること」です。デバイス開発がはかどりますよ。あと何か楽しい!
ではいきましょう。
SORACOM Junctionとは
公式の説明はこちら
https://soracom.jp/services/junction/
SORACOM Junction(以下、Junction)は、透過型トラフィック処理サービスです。
なるほどわからん。
大きく分けると「ミラーリング」「リダイレクション」「インスペクション」の3つの機能があります。
ミラーリングは回線の通信経路に流れるパケットをコピーして指定した宛先に送信する機能、リダイレクションは通信経路を指定した宛先に変更する機能、インスペクションはパケットの統計情報のレポートを出力してくれる機能です。透過型、と言われているのは、デバイスやクラウド側の設定などを変更することなく、SORACOMの設定を変更するだけで使えるようになる、ということでしょうか。
ひとつひとつを見ていきますと、
リダイレクションは3G/LTEの通信経路を自分のネットワークに変更することができるため、完全にパケットをコントロール出来るようになります。自分でSORACOMや他のMVNOと同じような制御をすることができるようになる、という感じでしょうか。SORACOMは回線の開始、停止や速度の上げ下げ、閉域に入れたり一時的に経路作ったりなど、様々なネットワーク制御を利用者に提供してくれていますが、その極致とも言えるサービスでしょう。
インスペクションは通信状態をなんとなく掴むことができ、Elasticsearch、Kabanaと組み合わせた時の動画なんかも出ています。
https://www.youtube.com/watch?v=7V8OAtp0GGI
(10:00の時点くらいで使ってます)見てると面白い。正常状態と異常状態を比較して、異常が起きたらどこに通信してるか、などを大まかに把握するのに役立ちそうです。
そしてミラーリング。本番で使っている回線の通信をトラフィック解析したり、IDS(Intrusion Detection System:侵入検知システム)にかけてマルウェアなどで不正使用されていないかを検知する、などといった使い方が紹介されています。これを今回はデバイスの開発に活かします。
デバイス開発してると、本当のところはどんな通信してるのか?が気になるケースありませんか?コスト計算で通信量を正確に把握したい。だというのにSORACOMが出してる通信量が、計算よりなんか多い気がする、とか。例えばLTE-Mボタン1回押すと300Bくらいの通信量が発生するのですが、なんか多いと思いませんか?なんか想定外の通信が発生してる?計算方法間違ってる?謎は深まるばかりです。
ラズベリーパイとか、OSが載ってるデバイスならWiresharkなりtcpdumpなりでパケットキャプチャすればだいたい分かるのですが、シンプルなセンサーデバイスなどの場合、パケットキャプチャするのは難しいです。セルラーモデム内でパケット作られてる場合、無理と言っても良いですね。
そういう時にはSORACOM Junction Mirroringです。実際にSORACOMが処理しているパケットをクラウド上でキャプチャすることができるため、想定外のパケットがあればそれが何であるか、簡単に把握することができます。すごい!
ここからは設定に入りますが、かなり長いので、実際のキャプチャ結果に興味有る方はパケットキャプチャ実践からご覧ください。
SORACOM Junction Mirroringの設定
それでは、Junctionの設定をしていきましょう。SORACOMは公式のチュートリアル(Getting Started)が優秀なので、これに沿って進めていけば設定できます。
https://dev.soracom.io/jp/start/junction_mirroring/
とはいえ、この資料が作られた時とはAWSのコンソールが変わっていたりするので、2020/3/7時点での手順を書いていきます。
Junctionは、VPG(Virtual Private Gateway)の使用が前提になっており、この構成で一番費用がかからないのがSORACOM Canalとの組み合わせです。
- AWSのVPCを作る
- AWSのEC2にサーバーを立てる
- VPGを作成する
- VPGとVPCをVPCピアリングする
- VPGのJunction設定でEC2にミラーリングパケットを送る設定をする
- SIMグループを作ってVPG設定をする
という手順で進めます。一点注意ですが、VPGはまあまあ費用のかかるサービスです。初期設定に980円、維持に50円/時間かかります。ミラーリング設定しているとさらに15円/時間かかります。試す際にはこれくらいの費用かかるということと、試し終わったらVPGを忘れず消す、ということをご確認ください。(この前3日間くらいVPG消し忘れて、3000円ほどの課金が発生してしまいました)
AWSとSORACOMのログイン部分は省略します。
AWSのVPCを作る
AWSのEC2にサーバーを立てる
SORACOMのチュートリアルではAmazon Linuxを使っていますが、新しいAMIに対応するということでAmazon Linux 2を選択しています。どちらでもよいです。
無料枠対象のt2.microで十分です。
もちろん既存のキーペアを使っても良いです。
これでEC2の作成は完了です。
VPGを作成する
記載されているとおり、作成すると費用が発生するので注意しましょう。具体的には980円かかります。(2020/03/07現在)また、3分ほど時間がかかります。
これでVPGの作成は完了です。
VPGとVPCをVPCピアリングする
AWSコンソールにて
AWSアカウントIDを確認
VPC IDを確認
SORACOM コンソールにて
追加の費用が発生する、と書かれていますが、VPG1つにつきVPGピア接続1つは無料です。(基本料金に含まれている)
AWSコンソールにて
これでVPG(というかSORACOMの所有するVPC)とVPCがピアリングされ、回線からAWSのVPCに到達できるようになりました。
VPGのJunction設定でEC2にミラーリングパケットを送る設定をする
AWSコンソールにて
SORACOM Junctionから届くGREパケットがセキュリティグループを通すようにしました。
SORACOMコンソールにて
これでVPGを通るパケットがEC2に転送されるようになりました。
SIMグループを作ってVPG設定をする
追加費用が発生すると言われますが、1000回線までは変わりません。だいたい大丈夫です。
これで設定は終了です。お疲れ様でした。あとは通信を確認したいSIMをこのSIMグループに入れれば、EC2にその通信パケットが送られてきます。
パケットキャプチャ実践
パケットキャプチャには、サーバー側はtcpdump、クライアント側はWiresharkを用います。クライアント側にWiresharkが入ってなければインストールしましょう。今回はmacOSで実施します。申し訳ないですが、WindowsでのWiresharkの使い方はよく分からないです。
ダウンロードした秘密鍵「Junction-Mirroring-Test.pem」がカレントディレクトリにあり、パーミッションが600になっているものとします。
EC2のサーバーのパブリックIPをEC2のコンソールで確認しておきます。
以下のコマンドでキャプチャを始められます。
mkfifo /tmp/junction_mirroring
wireshark -k -i /tmp/junction_mirroring &
ssh -X -i "Junction-Mirroring-Test.pem" ec2-user@<public-ip> "sudo /usr/sbin/tcpdump -s 0 -U -n -w - -i eth0 proto gre" > /tmp/junction_mirroring
では早速いろんなデバイスの通信を見てみましょうか。SIMグループはHarvestがONになっているものとし、Unified Endpointにアクセスが来た場合はHarvestにデータが入るようにしています。
なお、データはGRE(Generic Routing Encapsulation)というプロトコルでカプセル化されて届きますが、Wiresharkは賢いのでカプセル化を外して表示してくれます。また、何かの都合(イーサネットフレームの最小サイズ?)でパケットサイズの最小が98バイトになり、不足分が末尾にゼロパディングされますが、その部分は通信量に加算されません。通信量に加算されるのは、カプセルの中のイーサネットフレーム内のデータです。おおよそイーサネットヘッダー、IPヘッダー、TCP/UDPヘッダー、ペイロードで構成されています。
LTE-Mボタン
なんと皆さん、衝撃の事実が今、発覚しました!ボタンが送信してる先はUnified Endpointだと信じてたのですが、何と、button.soracom.ioという専用のエンドポイントがあります。え?いやこれびっくりした。もちろん解決されたIPアドレスは100.127.69.42なのでUnified Endpointに行ってるのですが、多分将来的にボタンからのデータをさばくのにもっとふさわしいエンドポイントができた時とか、逆にUnified Endpointを拡張する時にボタンが足かせにならないようにとか、そういう考えがあるのでしょう。さすがぬかりない。
ボタンのデータ量が妙に多いな、と思ってたのは、DNSが入ってたからですね。ボタン自体が送っているペイロードはたったの4B。すごすぎ。送信先がHarvestなので戻りのペイロードは3Bになります。
Stats:getAirStatsのAPIにてこの時の通信量を取得すると以下の結果です。
[
{
"date": "2020-03-07T15:43:48.677",
"unixtime": 1583595828,
"dataTrafficStatsMap": {
"t1.standard": {
"uploadByteSizeTotal": 195,
"downloadByteSizeTotal": 138,
"uploadPacketSizeTotal": 2,
"downloadPacketSizeTotal": 2
}
}
}
]
パケットキャプチャから取得したデータは以下のような感じです。
DNSクエリ | DNS応答 | データ送信 | データ応答 | |
---|---|---|---|---|
イーサネットヘッダー | 14 | 14 | 14 | 14 |
IPヘッダー | 20 | 20 | 20 | 20 |
UDPヘッダー | 8 | 8 | 8 | 8 |
ペイロード | 35 | 51 | 4 | 3 |
謎 | 36 | 0 | 36 | 0 |
合計 | 113 | 93 | 82 | 45 |
合計はぴったり?いやぴったりじゃない。前回の記事でもあった、送信側の謎の36Bがあります。いやこれホント何なんだろ。気になって夜も眠れない。
シングルクリック、ダブルクリック、ロングについては、以下のようになりました。
押し方 | データ |
---|---|
シングル | 4d010351 |
ダブル | 4d020352 |
ロング | 4d030353 |
押し方の違いが2バイト目と4バイト目に現れてる?何回押しても同じデータだから、ボタン押した回数とかではなさそう。あとはバッテリー残量がどこかに入っているはず。
ということでボタンについては、
- 毎回DNSとデータ送信してる
- 送信先はbutton.soracom.io
- 送信データはたった4B
- 受信データはエンドポイントが返すデータによって変わるので変動する
ということがわかりました。いやこれ今明かされるボタンの秘密で想定以上の成果出ちゃいましたね。
GPSマルチユニット
次はGPSマルチユニットです。
これは前回の記事でも見ましたが、もう一度電源入れるところから見てみましょう。
前回と同じ感じですね。
- センサーアップロードしてから設定ダウンロード(これは電源入れた時、ボタン押した時、1日1回で、通常のセンサーアップロードは設定ダウンロードを伴いません)
- センサーアップロードは100.127.69.42直指定(エンドポイント名使っていない)
- 設定ダウンロードはmetadata.soracom.io/vq/userdataからHTTPのGETアクセス
- デバイスがFIN/ACK返していないのでクラウド側から何回もFIN/ACKが送られてくる
電源入れるところからやっても100.127.69.42にあたるエンドポイントに対するDNSはしていないので、こちらはIPアドレス直指定なのでしょう。
通信量と実パケットとの関係を再掲すると、
[
{
"date": "2020-02-29T07:46:25.93",
"unixtime": 1582962385,
"dataTrafficStatsMap": {
"s1.standard": {
"uploadByteSizeTotal": 906,
"downloadByteSizeTotal": 1694,
"uploadPacketSizeTotal": 6,
"downloadPacketSizeTotal": 14
}
}
}
]
アップロードパケット | ダウンロードパケット | アップロードバイト数 | ダウンロードバイト数 | |
---|---|---|---|---|
センサーアップロード | 1 | 1 | 176 | 45 |
DNS | 1 | 1 | 115 | 95 |
3Wayハンドシェイク | 2 | 1 | 188 | 62 |
HTTPリクエスト | 1 | 1 | 337 | 54 |
HTTPレスポンス | 1 | 1 | 90 | 952 |
FIN/ACK | 0 | 9 | 0 | 486 |
合計 | 6 | 14 | 906 | 1694 |
ちなみにGPSマルチユニットの設定がされていないSIMの場合はこうなりまして、
(赤線引いているのはたまに来るICMPv6のパケットでVPG通信とは関係なく来るようです)
メタデータを取得しようとすると403 Forbiddenになりますが、その後10分後にGPSだけがONになった状態で送られてくるんですよね。つまりそういうデフォルト設定があって、設定が取得できなかったらその設定で動作するようです。なるほど。
Wio-LTE
最後はWio-LTEを見ます。
こちらの記事で見たように、HTTP、HTTPS、Beam HTTP→HTTPS、Beam TCP→HTTPS、Beam UDP→HTTPSでのアクセスをそれぞれ見てみましょう。
接続先はAmazon S3に「junction-test-bucket」というバケットを用意し、その中にhello.txtを用意しました。コンテンツは「hello」です。
HTTPはS3のStatic website hosting機能で以下のURLにアクセス出来るようにしました。
http://junction-test-bucket.s3-website-ap-northeast-1.amazonaws.com/hello.txt
HTTPSは通常のオブジェクトアクセス用のURLです。
https://junction-test-bucket.s3-ap-northeast-1.amazonaws.com/hello.txt
HTTP
#include <WioLTEforArduino.h>
WioLTE Wio;
void setup() {
delay(200);
Wio.Init();
Wio.PowerSupplyLTE(true);
delay(500);
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("### TURN ON ERROR! ###");
return;
}
if (!Wio.Activate("soracom.io", "sora", "sora")) {
return;
}
SerialUSB.println("Connected.");
delay(1000);
}
void loop() {
char buf[1024];
char data[1024];
int result;
char url[] = "http://junction-test-bucket.s3-website-ap-northeast-1.amazonaws.com/hello.txt";
result = Wio.HttpGet(url, data, sizeof(data), 10000);
if (result){
sprintf(buf, "Send OK %d %s", result, data);
SerialUSB.println(buf);
} else {
SerialUSB.println("Send NG");
}
delay(300000);
}
パケット分析
アップロードパケット | ダウンロードパケット | アップロードバイト数 | ダウンロードバイト数 | |
---|---|---|---|---|
DNS | 1 | 1 | 156 | 550 |
3Wayハンドシェイク | 2 | 1 | 188 | 62 |
HTTPリクエスト | 2 | 2 | 340 | 108 |
HTTPレスポンス | 2 | 2 | 180 | 446 |
FIN/ACK | 2 | 1 | 180 | 54 |
RST | 1 | 0 | 90 | 0 |
合計 | 10 | 7 | 1134 | 1220 |
通信量
[
{
"date": "2020-03-08T11:58:52.526",
"unixtime": 1583668732,
"dataTrafficStatsMap": {
"s1.fast": {
"uploadByteSizeTotal": 1134,
"downloadByteSizeTotal": 1220,
"uploadPacketSizeTotal": 10,
"downloadPacketSizeTotal": 7
}
}
}
]
DNSする、3wayハンドシェイクする、GETリクエストする(パケットは2つに分かれている)、レスポンス返ってくる(パケットは2つに分かれている)、ソケット閉じる、RST送る(Wio→S3)、という流れになっています。
はて?最後のRSTは何なんだろうか?FIN/ACK→FIN/ACK→ACKでソケットはクローズできているはずなんですが。なんかそういう作法ありましたっけ?あとHTTPリクエストとレスポンスが2つに分かれています。こんなに細かくする必要あるのかな?ともに通信量や電力を消費しそうな感じではありますね。
あとDNSが地味にでかい。CNAME入ってるので大きくなるみたいです。
HTTPS
#include <WioLTEforArduino.h>
WioLTE Wio;
void setup() {
delay(200);
Wio.Init();
Wio.PowerSupplyLTE(true);
delay(500);
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("### TURN ON ERROR! ###");
return;
}
if (!Wio.Activate("soracom.io", "sora", "sora")) {
return;
}
SerialUSB.println("Connected.");
delay(1000);
}
void loop() {
char buf[1024];
char data[1024];
int result;
char url[] = "https://junction-test-bucket.s3-ap-northeast-1.amazonaws.com/hello.txt";
result = Wio.HttpGet(url, data, sizeof(data), 10000);
if (result){
sprintf(buf, "Send OK %d %s", result, data);
SerialUSB.println(buf);
} else {
SerialUSB.println("Send NG");
}
delay(300000);
}
パケット分析
アップロードパケット | ダウンロードパケット | アップロードバイト数 | ダウンロードバイト数 | |
---|---|---|---|---|
DNS | 1 | 1 | 148 | 536 |
3Wayハンドシェイク | 2 | 1 | 188 | 62 |
TLSハンドシェイク | 11 | 10 | 1266 | 4321 |
HTTPリクエスト | 2 | 2 | 390 | 108 |
HTTPレスポンス | 2 | 2 | 180 | 526 |
TLS Alert | 1 | 1 | 121 | 85 |
FIN/ACK | 3 | 1 | 270 | 54 |
RST | 1 | 0 | 90 | 0 |
合計 | 23 | 18 | 2653 | 5692 |
通信量
[
{
"date": "2020-03-08T12:48:48.195",
"unixtime": 1583671728,
"dataTrafficStatsMap": {
"s1.fast": {
"uploadByteSizeTotal": 2653,
"downloadByteSizeTotal": 5746,
"uploadPacketSizeTotal": 23,
"downloadPacketSizeTotal": 19
}
}
}
]
やはりでかい、パケット数は42個、通信量は8399Bにもなります。同じデータを取得しているとは思えないですね。。全体的な流れはHTTPと同じですが、やはりTLSハンドシェイクが大きいです。特にCertificate(サーバー証明書の取得)がかなり大きい。その他も手順が多く、パケット数もかさむので全体として大きくなる。ハンドシェイクが終わればTLSのオーバーヘッドは5Bなので、そこは大したことないですね。
そして、パケット数が1個合わない!サイズもキャプチャからの分析より54B多くなってます。まあこれには心当たりあります。TLSハンドシェイクのダウンロード5個に対して、アップロードのACKが6個返ってきている。そしてパケットサイズが2768Bのパケットがあります。これ本来は2つに分割されていたパケットを、Junctionが1つにまとめて転送してるんじゃないかな?普通はMTU(Maximum Transmission Unit)で分割しないといけないけど、VPCに送る場合はもっと大きくして良いみたいな事情があり、まとめられるところはまとめている。仮にこのパケットが2つに分かれた場合、イーサネットヘッダ(14B)、IPヘッダ(20B)、TCPヘッダ(20B)の計54B増えるので、ちょうど計算が合います。つまりJunctionで転送されているパケットと、通信量の計算に使われているパケットは、内容は同じだけど厳密に言えば異なる、ということが分かりますね。サイズ大きいパケットがある時は気をつけましょう。
そして当然ですが暗号化されているので具体的にどんな通信をしているかは分からなくなっています。いいですね。先ほどのHTTPでは通信丸見えでした。こういうことがあるので、インターネットへの通信は暗号化が必要なのですね。(SORACOMサービスの通信は大丈夫なのかって?それはまた記事を改めて検証しようと思います)
あと最後ACKが1個多く、またRSTがある。これ毎回発生してるんですかね。WioのモジュールだとTCP切断後1分後に必ずRSTが送られる?色々想定していないパケットが送られているものですね。
HTTP Beam → HTTPS
#include <WioLTEforArduino.h>
WioLTE Wio;
void setup() {
delay(200);
Wio.Init();
Wio.PowerSupplyLTE(true);
delay(500);
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("### TURN ON ERROR! ###");
return;
}
if (!Wio.Activate("soracom.io", "sora", "sora")) {
return;
}
SerialUSB.println("Connected.");
delay(1000);
}
void loop() {
char buf[1024];
char data[1024];
int result;
char url[] = "http://beam.soracom.io:8888/";
result = Wio.HttpGet(url, data, sizeof(data), 10000);
if (result){
sprintf(buf, "Send OK %d %s", result, data);
SerialUSB.println(buf);
} else {
SerialUSB.println("Send NG");
}
delay(300000);
}
パケット分析
アップロードパケット | ダウンロードパケット | アップロードバイト数 | ダウンロードバイト数 | |
---|---|---|---|---|
DNS | 1 | 1 | 111 | 91 |
3Wayハンドシェイク | 2 | 1 | 188 | 62 |
HTTPリクエスト | 2 | 2 | 291 | 108 |
HTTPレスポンス | 1 | 1 | 90 | 433 |
FIN/ACK | 2 | 2 | 180 | 108 |
合計 | 8 | 7 | 860 | 802 |
通信量
[
{
"date": "2020-03-08T14:03:48.824",
"unixtime": 1583676228,
"dataTrafficStatsMap": {
"s1.fast": {
"uploadByteSizeTotal": 860,
"downloadByteSizeTotal": 802,
"uploadPacketSizeTotal": 8,
"downloadPacketSizeTotal": 7
}
}
}
]
基本的にはHTTPを直接送る時と同じです。BeamへのDNSの応答が小さいため、通信量が小さくなっているのはいいことかもしれない。あとRSTがなく、BeamのFIN/ACK → WioのACK、WioのFIN/ACK → BeamのACKで切れています。 こっちの方が正しい切り方なのかな?つなぎ方(3wayハンドシェイク)は良く聞きますが、切断の仕方はあまりちゃんと知らないんですよね。後でRFCを読んでみますか。
TCP Beam → HTTPS
そういえばTCP Beam、UDP BeamはPOSTメソッドなんでしたね。S3のオブジェクトでは対応できないので、API GatewayのMockエンドポイントを適当に作って対応しました。
#include <WioLTEforArduino.h>
WioLTE Wio;
void setup() {
delay(200);
Wio.Init();
Wio.PowerSupplyLTE(true);
delay(500);
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("### TURN ON ERROR! ###");
return;
}
if (!Wio.Activate("soracom.io", "sora", "sora")) {
return;
}
SerialUSB.println("Connected.");
delay(1000);
}
void loop() {
char buf[1024];
char data[1024];
int connectId;
connectId = Wio.SocketOpen("beam.soracom.io", 23080, WIOLTE_TCP);
if (connectId < 0) {
SerialUSB.println("### Connect ERROR! ###");
goto finish;
}
sprintf(data, "hello");
if (!Wio.SocketSend(connectId, data)) {
SerialUSB.println("### SEND ERROR! ###");
goto close;
}
int length;
length = Wio.SocketReceive(connectId, buf, sizeof (buf), 5000);
if (length < 0) {
SerialUSB.println("### RECEIVE ERROR! ###");
goto close;
}
if (length == 0) {
SerialUSB.println("### RECEIVE TIMEOUT! ###");
goto close;
}
SerialUSB.println(buf);
close:
if (!Wio.SocketClose(connectId)) {
SerialUSB.println("### Close ERROR! ###");
if (!Wio.SocketClose(connectId)) {
SerialUSB.println("### Close ERROR! ###");
}
}
finish:
delay(300000);
}
パケット分析
アップロードパケット | ダウンロードパケット | アップロードバイト数 | ダウンロードバイト数 | |
---|---|---|---|---|
DNS | 1 | 1 | 111 | 91 |
3Wayハンドシェイク | 2 | 1 | 188 | 62 |
HTTPリクエスト | 1 | 1 | 95 | 54 |
HTTPレスポンス | 1 | 1 | 90 | 64 |
FIN/ACK | 2 | 1 | 180 | 54 |
RST | 1 | 0 | 90 | 0 |
合計 | 8 | 5 | 754 | 325 |
通信量
[
{
"date": "2020-03-08T14:53:48.292",
"unixtime": 1583679228,
"dataTrafficStatsMap": {
"s1.fast": {
"uploadByteSizeTotal": 754,
"downloadByteSizeTotal": 325,
"uploadPacketSizeTotal": 8,
"downloadPacketSizeTotal": 5
}
}
}
]
HTTPヘッダなどがなくなった分、かなり小さいデータになります。パケット分割されることもなくなりました。一方、TCPの切り方がまた変わって、WioのFIN/ACK → BeamのFIN/ACK → WioのACK → WioのRSTで切れるようになっています。そのせいか、TCPのCloseが必ず1回失敗する、という現象が発生するんですね。過去の記事でも疑問でしたが、Wioはこのような切り方をされるとRSTで切る、という動作をするようです。HTTPエンドポイントとTCPエンドポイントでこのあたりの実装が微妙に違うのかな?
Closeが失敗した後、RSTが発生するとその時にも送信電力を使ってしまうので、なくせないかなと思います。HTTPエンドポイントと同じような切断のされ方になれば。
UDP Beam → HTTPS
#include <WioLTEforArduino.h>
WioLTE Wio;
void setup() {
delay(200);
Wio.Init();
Wio.PowerSupplyLTE(true);
delay(500);
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("### TURN ON ERROR! ###");
return;
}
if (!Wio.Activate("soracom.io", "sora", "sora")) {
return;
}
SerialUSB.println("Connected.");
delay(1000);
}
void loop() {
char buf[1024];
char data[1024];
int connectId;
connectId = Wio.SocketOpen("beam.soracom.io", 23080, WIOLTE_UDP);
if (connectId < 0) {
SerialUSB.println("### Connect ERROR! ###");
goto finish;
}
sprintf(data, "hello");
if (!Wio.SocketSend(connectId, data)) {
SerialUSB.println("### SEND ERROR! ###");
goto close;
}
int length;
length = Wio.SocketReceive(connectId, buf, sizeof (buf), 5000);
if (length < 0) {
SerialUSB.println("### RECEIVE ERROR! ###");
goto close;
}
if (length == 0) {
SerialUSB.println("### RECEIVE TIMEOUT! ###");
goto close;
}
SerialUSB.println(buf);
close:
if (!Wio.SocketClose(connectId)) {
SerialUSB.println("### Close ERROR! ###");
}
finish:
delay(300000);
}
パケット分析
アップロードパケット | ダウンロードパケット | アップロードバイト数 | ダウンロードバイト数 | |
---|---|---|---|---|
DNS | 1 | 1 | 111 | 91 |
HTTPリクエスト | 1 | 0 | 83 | 0 |
HTTPレスポンス | 0 | 1 | 0 | 51 |
合計 | 2 | 2 | 194 | 142 |
通信量
[
{
"date": "2020-03-08T15:13:52.803",
"unixtime": 1583680432,
"dataTrafficStatsMap": {
"s1.fast": {
"uploadByteSizeTotal": 194,
"downloadByteSizeTotal": 142,
"uploadPacketSizeTotal": 2,
"downloadPacketSizeTotal": 2
}
}
}
]
さすがUDPにするとデータはとても小さくなります。切断に伴う変な動きもない。やはりセンサーデータアップロードのように、データそのものが小さく、順序もあまり気にしなくて良いような場合は、通信量的にも消費電力的にもUDPが良いですね。
なお、書いていませんでしたがDNSはどこか(LTEモジュール内?)でキャッシュされているらしく、2回目以降の通信ではUDPに限らずDNSのリクエストはしませんでした。賢いですね。
総括
SORACOM Junction Mirroringを使うと、ボタンやWioのようなOSのない単純なデバイスであっても、ラズパイのようなLinuxで動作するのと同等のパケットキャプチャをすることができました。切断に伴う変な動作(LTEモジュールとサーバーとの相性問題みたいな感じ)などを確認することができ、さらに書く操作ごとの通信量をほぼ完璧に把握することができましたね。これはいいものです。
後片付け
今回構築したVPC、EC2、VPGは忘れず削除するようにしましょう。特にVPGは50円/時間かかるので、消し忘れると思わぬ出費になります。
これでVPGとJunction関係の課金は止まります。接続中のSIMがあると削除できないのですが、切断後1時間は切断が検知されなかったりするので、その場合はセッション切断を使うとよいです。
AWS コンソールにて
VPCは課金要素ではないのですが、無駄に使っているのももったいないので消しておきます。
これで後片付け完了です。お疲れ様でした!
Junctionは強力だけど、構築と削除がやや面倒なので、このあたりは自動化したいところですね。
おわりに
SORACOM Junctionのミラーリング機能を使えば、自身でパケットキャプチャできない貧弱なデバイスであっても、クラウド上でのパケットキャプチャが可能になります。
想定外のパケットが発生していないか?通信量の計算は合っているか?などの確認が簡単にできるようになりますね。
ぜひIoTデバイスの開発者の皆さんにおかれましては、開発・デバッグ・トラブルシューティングなどに活用してもらえればと思います。