Help us understand the problem. What is going on with this article?

SORACOM Junctionで始める楽しいIoTデバイスパケットキャプチャ

はじめに

もしあなたが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:侵入検知システム)にかけてマルウェアなどで不正使用されていないかを検知する、などといった使い方が紹介されています。これを今回はデバイスの開発に活かします。

mirroring.png

デバイス開発してると、本当のところはどんな通信してるのか?が気になるケースありませんか?コスト計算で通信量を正確に把握したい。だというのに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を作る

スクリーンショット 2020-03-07 21.01.06.png
スクリーンショット 2020-03-07 21.02.53.png
スクリーンショット 2020-03-07 21.03.52.png
スクリーンショット 2020-03-07 21.06.25.png
スクリーンショット 2020-03-07 21.08.33.png
スクリーンショット 2020-03-07 21.12.27.png
スクリーンショット 2020-03-07 21.14.23.png
スクリーンショット 2020-03-07 21.15.28.png
スクリーンショット 2020-03-07 21.16.35.png
スクリーンショット 2020-03-07 21.18.46.png
スクリーンショット 2020-03-07 21.20.29.png
スクリーンショット 2020-03-07 21.21.14.png
スクリーンショット 2020-03-07 21.21.57.png
スクリーンショット 2020-03-07 21.22.46.png
必要なVPCの設定ができました。

AWSのEC2にサーバーを立てる

スクリーンショット 2020-03-07 21.25.27.png
スクリーンショット 2020-03-07 21.28.12.png
スクリーンショット 2020-03-07 21.30.02.png
SORACOMのチュートリアルではAmazon Linuxを使っていますが、新しいAMIに対応するということでAmazon Linux 2を選択しています。どちらでもよいです。
スクリーンショット 2020-03-07 21.33.20.png
無料枠対象のt2.microで十分です。
スクリーンショット 2020-03-07 21.36.16.png
スクリーンショット 2020-03-07 21.39.09.png
スクリーンショット 2020-03-07 21.40.54.png
スクリーンショット 2020-03-07 21.41.41.png
スクリーンショット 2020-03-07 21.50.02.png
スクリーンショット 2020-03-07 21.52.03.png
スクリーンショット 2020-03-07 21.54.33.png
もちろん既存のキーペアを使っても良いです。
スクリーンショット 2020-03-07 21.57.31.png
スクリーンショット 2020-03-07 21.59.36.png
これでEC2の作成は完了です。

VPGを作成する

スクリーンショット 2020-03-07 23.13.54.png
スクリーンショット 2020-03-07 23.15.52.png
スクリーンショット 2020-03-07 23.16.56.png
スクリーンショット 2020-03-07 23.18.07.png
記載されているとおり、作成すると費用が発生するので注意しましょう。具体的には980円かかります。(2020/03/07現在)また、3分ほど時間がかかります。
スクリーンショット 2020-03-07 23.21.15.png
スクリーンショット 2020-03-07 23.23.38.png
スクリーンショット 2020-03-07 23.27.06.png
これでVPGの作成は完了です。

VPGとVPCをVPCピアリングする

AWSコンソールにて
AWSアカウントIDを確認
スクリーンショット 2020-03-07 23.30.09.png
VPC IDを確認
スクリーンショット 2020-03-07 21.01.06.png
スクリーンショット 2020-03-07 23.34.54.png

SORACOM コンソールにて
スクリーンショット 2020-03-07 23.25.35.png
スクリーンショット 2020-03-07 23.38.03.png
追加の費用が発生する、と書かれていますが、VPG1つにつきVPGピア接続1つは無料です。(基本料金に含まれている)

AWSコンソールにて
スクリーンショット 2020-03-07 23.42.33.png
スクリーンショット 2020-03-07 23.44.22.png
スクリーンショット 2020-03-07 23.48.18.png
スクリーンショット 2020-03-07 23.49.54.png
スクリーンショット 2020-03-07 23.51.12.png
スクリーンショット 2020-03-07 23.52.13.png
スクリーンショット 2020-03-07 23.53.32.png
スクリーンショット 2020-03-07 23.56.43.png
これでVPG(というかSORACOMの所有するVPC)とVPCがピアリングされ、回線からAWSのVPCに到達できるようになりました。

VPGのJunction設定でEC2にミラーリングパケットを送る設定をする

AWSコンソールにて
スクリーンショット 2020-03-07 21.25.27.png
スクリーンショット 2020-03-08 0.01.10.png
スクリーンショット 2020-03-08 0.02.01.png
スクリーンショット 2020-03-08 0.03.48.png
スクリーンショット 2020-03-08 0.06.38.png
SORACOM Junctionから届くGREパケットがセキュリティグループを通すようにしました。

SORACOMコンソールにて
スクリーンショット 2020-03-08 0.10.25.png
スクリーンショット 2020-03-08 0.12.09.png
これでVPGを通るパケットがEC2に転送されるようになりました。

SIMグループを作ってVPG設定をする

スクリーンショット 2020-03-08 0.16.12.png
スクリーンショット 2020-03-08 0.17.09.png
スクリーンショット 2020-03-08 0.18.11.png
スクリーンショット 2020-03-08 0.19.59.png
スクリーンショット 2020-03-08 0.22.01.png
追加費用が発生すると言われますが、1000回線までは変わりません。だいたい大丈夫です。

これで設定は終了です。お疲れ様でした。あとは通信を確認したいSIMをこのSIMグループに入れれば、EC2にその通信パケットが送られてきます。

パケットキャプチャ実践

パケットキャプチャには、サーバー側はtcpdump、クライアント側はWiresharkを用います。クライアント側にWiresharkが入ってなければインストールしましょう。今回はmacOSで実施します。申し訳ないですが、WindowsでのWiresharkの使い方はよく分からないです。

ダウンロードした秘密鍵「Junction-Mirroring-Test.pem」がカレントディレクトリにあり、パーミッションが600になっているものとします。

EC2のサーバーのパブリックIPをEC2のコンソールで確認しておきます。
スクリーンショット 2020-03-08 0.35.00.png

以下のコマンドでキャプチャを始められます。

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ボタン

まずは王道のLTE-Mボタンですね。
シングルクリック!
スクリーンショット 2020-03-08 0.41.47.png

なんと皆さん、衝撃の事実が今、発覚しました!ボタンが送信してる先は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マルチユニットです。
これは前回の記事でも見ましたが、もう一度電源入れるところから見てみましょう。

スクリーンショット 2020-03-08 6.35.59.png

前回と同じ感じですね。

  • センサーアップロードしてから設定ダウンロード(これは電源入れた時、ボタン押した時、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の場合はこうなりまして、
スクリーンショット 2020-03-08 19.37.58.png
(赤線引いているのはたまに来る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);
}

パケット
スクリーンショット 2020-03-08 21.00.59.png

パケット分析

アップロードパケット ダウンロードパケット アップロードバイト数 ダウンロードバイト数
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);
}

スクリーンショット 2020-03-08 21.53.32.png

パケット分析

アップロードパケット ダウンロードパケット アップロードバイト数 ダウンロードバイト数
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);
}

パケット
スクリーンショット 2020-03-08 23.05.42.png

パケット分析

アップロードパケット ダウンロードパケット アップロードバイト数 ダウンロードバイト数
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);
}

パケット
スクリーンショット 2020-03-08 23.54.13.png

パケット分析

アップロードパケット ダウンロードパケット アップロードバイト数 ダウンロードバイト数
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);
}

パケット
スクリーンショット 2020-03-09 0.15.04.png

パケット分析

アップロードパケット ダウンロードパケット アップロードバイト数 ダウンロードバイト数
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円/時間かかるので、消し忘れると思わぬ出費になります。

SORACOM コンソールにて
スクリーンショット 2020-03-09 0.35.35.png
スクリーンショット 2020-03-09 0.37.22.png

スクリーンショット 2020-03-09 0.27.57.png
スクリーンショット 2020-03-09 0.29.28.png
スクリーンショット 2020-03-09 0.32.08.png
スクリーンショット 2020-03-09 0.33.49.png
これでVPGとJunction関係の課金は止まります。接続中のSIMがあると削除できないのですが、切断後1時間は切断が検知されなかったりするので、その場合はセッション切断を使うとよいです。

AWS コンソールにて

スクリーンショット 2020-03-07 21.25.27.png
スクリーンショット 2020-03-08 0.01.10.png
スクリーンショット 2020-03-09 0.42.33.png
スクリーンショット 2020-03-09 0.44.26.png
これでEC2の課金は止まります。

VPCは課金要素ではないのですが、無駄に使っているのももったいないので消しておきます。
スクリーンショット 2020-03-07 21.01.06.png
スクリーンショット 2020-03-09 0.48.36.png
スクリーンショット 2020-03-09 0.49.51.png
スクリーンショット 2020-03-09 0.51.41.png

これで後片付け完了です。お疲れ様でした!
Junctionは強力だけど、構築と削除がやや面倒なので、このあたりは自動化したいところですね。

おわりに

SORACOM Junctionのミラーリング機能を使えば、自身でパケットキャプチャできない貧弱なデバイスであっても、クラウド上でのパケットキャプチャが可能になります。
想定外のパケットが発生していないか?通信量の計算は合っているか?などの確認が簡単にできるようになりますね。
ぜひIoTデバイスの開発者の皆さんにおかれましては、開発・デバッグ・トラブルシューティングなどに活用してもらえればと思います。

1stship
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした