はじめに
こちらは SORACOM Advent Calendar 2022 13 日目の記事です。お気に入りのサービスである「SORACOM Peek」について書こうと思います。
SORACOM Peek とは
SORACOM Peek (以降、Peek) は、SORACOM の SIM を利用した通信時に IP パケットをキャプチャするサービスです。SIM 1 枚から、通信内容に影響を与える設定変更なく利用できる、とても使い勝手の良いサービスだと思います。パケットキャプチャした内容は Wireshark などを使って確認できます。(Wireshark はパケットキャプチャしたり、パケットキャプチャした内容を表示・解析できるソフトウェアです。)
1 行毎に各パケットの「パケットをキャプチャした時間 (Time)」「送信元 IP アドレス (Source)」「送信先 IP アドレス (Destination)」「プロトコル (Protocol)」「キャプチャしたパケットの Byte 数 (Length)」「パケットの情報 (Info)」などが確認できます。
パケットをキャプチャして通信内容の詳細を確認できることは IoT システムの構築・運用において大きな武器になりますが、「パケットキャプチャ」と聞くとなんとなく敷居が高いように感じてしまう方も多いのではないでしょうか。また、パケットキャプチャしてみたは良いものの、「この後どうすれば?」という場面もあるかと思います。そこでこのブログでは私の考える Peek (つまり、SORACOM でのパケットキャプチャ) の具体的な使いどころをいくつかご紹介します。
SORACOM Peek の使いどころ 3 選
今回は Peek で取得したパケットキャプチャファイル (pcap ファイル) の活用方法を 3 つご紹介します。pcap ファイルを取得するまでの手順については IoT SIM のパケットをキャプチャする (Peek for SIM) を参照ください。使い方はとても簡単です!ドキュメント中のスクリーンショットを眺めるだけでもなんとなくの手順は把握できるかと思います。
さて、では本題です!
その 1 「どの通信のデータ量が多いのかを確認する」
IoT システムを運用していると、「あれ、この SIM なんでこんなにデータ通信量多いんだろう? ( MQTT しか使ってないのに。)」みたいなことがあると思います。特に SORACOM では従量課金で使った分だけ料金が増えていくため、データ通信量が気になることが多いのではないでしょうか。そのような場面では SIM のパケットをキャプチャすることで、どの通信のデータ量が多いのかが確認できます。
まず、ダウンロードした pcap ファイルを Wireshark で開きます。以下のように通信内容の詳細が確認できます。(デバイス側の IP アドレスはマスクしています。)
次にメニューの [Statistics] → [Endpoint] を開きます。
[IPv4] タブをクリックすると、以下のように各通信先 (IP アドレス) とのデータ通信量が確認できます。
また、たとえば TCP の通信だけ見たい場合は [TCP] タブをクリックすれば IP アドレスとポート番号別の通信量が確認できます。
特にデータ通信量の多い SIM については定期的に Peek を利用してデータ通信先とデータ通信量が想定どおりかどうかを確認するのも良いかと思います。
その 2 「データが SORACOM プラットフォームまで届いているのかどうかを確認する」
次はトラブルシューティングの際の利用例です。たとえば以下のような構成で「サーバーまでデータが届いていないけど、そもそもこれ SORACOM まではデータ届いているの?」という時に使います。
特に開発中のフェーズではプログラムが処理の途中でエラーになってしまって、そもそもデバイスからデータが送信されていなかったなんてこともあります。なので、まずは Peek で SORACOM まで通信が到達しているかどうかを確認できればデバッグするポイントを絞り込めます。
因みに、Peek で確認するのではなくて SORACOM のユーザーコンソールから SIM の通信量を確認するという方法もあります。テストした時間帯の通信量がゼロであればわざわざ Peek を使わなくてもデバイスからデータが送信されていないことが確認できます。
ただし、「データ通信量がゼロではない = SORACOM まで通信が到達している」とは言えないことに注意が必要です。というのは、例えば DNS など考慮していなかった通信が発生している場合も考えられるためです。そのため、想定していた通信が SORACOM プラットフォームまで到達していることの確認は Peek を使うことが確実だと思います。
その 3 「処理がどこで停止しているのかを確認する」
最後は実際に僕が Peek を使って調査した事例をご紹介します。Scapy というツールを使って、SORACOM Harvest Data に TCP でデータを送ろうとしてハマった時の話です。
先に進む前に、SORACOM Harvest Data と Scapy について簡単に補足します。
SORACOM Harvest Data とは
SORACOM Harvest Data は SORACOM のオンラインデータストレージサービスで、HTTP/TCP/UDP などでデータを送ることができます。例えば、以下のように Raspberry Pi 上で nc コマンドを使えば TCP でデータ送信ができます。
pi@raspberrypi:~ $ nc harvest.soracom.io 8514
hoge
201
SORACOM Harvest Data にデータ (hoge
) が保存されます。
Scapy とは
Scapy は Python で書かれたツールで、パケットを自由に生成・送信できます。このツールを使うことで、後述するように通常 OS 側でよしなにやってくれる TCP の 3 ウェイ・ハンドシェイクを明示的に行うことができます。詳細な使い方は省きますが、インストールして sudo scapy
を実行するとコマンド実行も可能です。たとえば、sr1(IP(dst="pong.soracom.io")/ICMP())
を実行すると pong.soracom.io
に対して ping できます。(icmp echo request の送信と icmp echo reply の受信ができます。)
pi@raspberrypi:~ $ sudo scapy
INFO: PyX dependencies are not installed ! Please install TexLive or MikTeX.
aSPY//YASa
apyyyyCY//////////YCa |
sY//////YSpcs scpCY//Pp | Welcome to Scapy
ayp ayyyyyyySCP//Pp syY//C | Version 2.5.0rc1
AYAsAYYYYYYYY///Ps cY//S |
pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy
SPPPP///a pP///AC//Y |
A//A cyP////C | Have fun!
p///Ac sC///a |
P////YCpc A//A | Craft me if you can.
scccccp///pSP///p p//Y | -- IPv6 layer
sY/////////y caa S//P |
cayCyayP//Ya pY/Ya
sY/PsY////YCc aC//Yp
sc sccaCY//PCypaapyCP//YSs
spCPY//////YPSps
ccaacs
using IPython 7.34.0
>>> sr1(IP(dst="pong.soracom.io")/ICMP())
Begin emission:
Finished sending 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
<IP version=4 ihl=5 tos=0x0 len=28 id=39033 flags= frag=0 ttl=64 proto=icmp chksum=0xe350 src=100.127.100.127 dst=xx.xx.xx.xxx |<ICMP type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |>>
やりたかったこと
僕はネットワークの勉強も兼ねてデバイス (※) 上で Scapy を動かして SORACOM Harvest Data にデータを送信しようとしました。具体的には以下のように TCP コネクションの確立→データ送信→ TCP コネクションの終了までの処理を Scapy を使って実装しようとしました。
※ 今回は Raspberry Pi + SORACOM Onyx LTE USB ドングル + plan-D の組み合わせで動かしました。
因みに、TCP コネクションについての説明は 【図解】TCPコネクションのシーケンスと状態確認〜3way handshake, FIN/RST, netstat〜 が分かりやすいです。
from scapy.all import *
import random
conf.verb = 0
# Establish tcp connection
sport = random.randint(1024,65535)
seq = random.randint(0,1000)
ip = IP(dst="harvest.soracom.io")
tcp = TCP(sport=sport,dport=8514,seq=seq)
tcp.flags = "S"
SYN=ip/tcp # Creation of syn packets
SYNACK=sr1(SYN) # Send syn and receive ack
tcp.seq += 1 # Increase sequence number by 1
tcp.ack = SYNACK.seq + 1 # Set ACK number
tcp.flags = "A" # Set ACK flags
send(ip/tcp) # Send ACK
# Data transmission
load = "send data by scapy."
raw = Raw(load=load) # Set payload
DATA=ip/tcp/raw # Creation of packets
ACK=sr1(DATA) # Send data
# End of connection
tcp.seq += len(load)
tcp.flags = "FA" # Set FIN/ACK flags
FIN=sr1(ip/tcp) # Send FIN
tcp.seq += 1 # Increase sequence number by 1
tcp.ack = FIN.seq + 1 # Increase ACK number by 1
tcp.flags = "A" # Set ACK flags
send(ip/tcp) # Send ACK
しかし、上記のスクリプトを実行しても SORACOM Harvest Data にデータが保存されませんでした。
SORACOM のユーザーコンソールからテストした時間帯の SIM の通信量があることは確認しました。なので、どの処理がうまくいっていないのかを確認するために Peek を仕込んで再度テストしました。その結果を抜粋したものが以下です。(時系列は上から下へ流れています)
マスクしている部分がデバイス側の IP アドレスです。
①、② は SORACOM Harvest Data のエントリポイント (harvest.soracom.io
) の名前解決です。
③ はデバイスから SORACOM Harvest Data への SYN の送信です。
④ は SORACOM Harvest Data からデバイスへの SYN/ACK の返却です。
ここまでは OK。問題は ⑤ です。本来はデバイス側から ACK を返すべきところを、RST と記載されたパケットが SORACOM Harvest Data に送信されて、TCP コネクションの確立に失敗しています。
RST は TCP コネクションを強制的に終了するために送信されるパケットです。つまり、今回のケースではデバイスが TCP コネクションを強制終了していることが分かりました。この時点では原因は特定できていないものの、デバイス側の処理に依存した問題である可能性が高そうです。そのため、まずは Scapy でのデータ通信方法に絞って原因を探っていこうということができます。
なお、原因については Scapy のトラブルシューティングガイドに記載されていました。
本来 TCP コネクションの確立はカーネル側で行われます。Scapy 側で送信した SYN パケットはカーネルに認識されないために、対応からの SYN/ACK パケットが返却された際にカーネルが「何これ、知らないよ。」と RST パケットを送信して TCP コネクションを強制終了させてしまうということでした。この辺りの話は ScapyによるTCP通信 に詳細且つ分かりやすく記載されておりましたので、興味のある方はこちらも参照ください。
上記のカーネル側の介入を防ぐためには iptables で以下のコマンドを実行します。これで一時的に SORACOM Harvest Data (100.127.111.111) へ RST パケットを送らないように設定できます。
iptables -A OUTPUT -p tcp --tcp-flags RST RST -d 100.127.111.111 -j DROP
この設定を入れて再トライしたところ、以下のように無事 SORACOM Harvest Data にデータが保存されました。
成功時と失敗時の処理内容を比較する
なお、今回のように通信プロトコルごとのシーケンスを理解できている場合は問題箇所の特定が比較的容易ですが、そうではない場合はどのように調査すれば良いのでしょうか。僕はシーケンスが分からない場合は正常な通信とそうでない通信の処理内容を比較する、ということをよくやります。例えば、今回のケースでは上述した nc コマンドを利用した TCP 通信時のパケットキャプチャとの結果を比較することで、差分 (データ送信が成功する場合は ACK が送信されるが、失敗した場合には RST パケットが送られる) を確認する、というようなことをすると解決へのヒントになると思います。
まとめ
いかがでしたでしょうか。実は今回 Scapy と SORACOM を絡めたブログを書きたいなぁというところからスタートしたので、「その 3」がやや特殊、且つ分量が多くなってしまった感が否めませんが、「その 1」と「その 2」はそれなりに汎用性も高いのではないかと思います。また、今回ご紹介した内容はあくまで私が考える使い方なので、他にも「こういう使い方があるよ!」「こんな課題を解決したよ!」という方がいらっしゃったら教えてください!
このブログを読んで「いっちょパケットキャプチャしてみっか」と少しでも思っていただけたら嬉しいです。ではでは!