概要

スマホの通信をパケットキャプチャする。動機としてはスマホゲームをやってるときにかなりの頻度でロード中に固まってそのままタイムアウトする現象が引っ越してから発生しており、何らかの原因がわかるかなと思ったため。

結果的には現時点では原因は不明だが、パケットキャプチャ環境を作っていたら色々勉強になったのでやり方をメモしておくことにする。

手順の概要

最終的には下記のような構成で"PC"にてパケットをキャプチャした。

図を見ると明らかだと思うが、PCにはNICが2つ搭載しているのでこれを利用して、PCをルーターのように使いスマホ側のNICのパケットを取るという手法。ちなみにPC2というのがあるが、これは疎通確認やRouter2の設定などスマホではやりにくい操作を行うためのノートPCでキャプチャには無くても良い。むしろキャプチャ中はPCからではPC2のパケットなのかスマホからのパケットなのかがわからなくなるので外していた。

大まかな手順は下記の通り。

  1. 図のように各種機器を接続する(Router2とスマホの間はWi-Fiであり、それ以外は有線)
  2. PCのインターネット側のNICはDHCPによるIP割当を受ける、スマホ側(図下側)のNICは手動にて192.168.10.1/24を設定する
  3. PCについてipv4がフォワード設定ONとなっていることを確認する
  4. PCについてスマホ側のNICにてiptablesを用いてNATを設定する
  5. PCについてスマホ側のNICにてdhcpdを起動してIP自動割当を可能にする
  6. PCについてスマホ側のNICにてiptablesでDNSとDHCPのパケットを遮断しないよう設定する
  7. Router2をルーターモードで起動してPCからDHCPでIP割当を受ける(ローカルルーターモード)
  8. スマホを接続して疎通確認する
  9. 問題なければキャプチャする

これを見ると、Router2はブリッジモードのほうが良いように思うが、ブリッジモードだと何故かうまく動かなかったので今回は面倒だったがルーターモードで実施した。

以下に詳細な手順を残しておく。

手順に関するメモ

前提

  • PCのOSはArch Linuxである
    • PCのインターネット側のNICをeno1とする(実際にはKVM向けのブリッジを作っていたのでbr0だが、eno1と置き換えて記載する)
    • PCのスマホ側のNICをeno2とする
  • Router2はAterm WX1500HPである
  • スマホはAndroidスマホである

NICに手動でIPアドレスを設定する

PCにてスマホ側のNICには手動でIPアドレスを設定する。今回は192.168.10.1とした。

$ ip addr add 192.168.10.1/24 dev eno2

PCのipv4フォワードがONになっていることを確認する

net.ipv4.ip_forwardがON(1)になっていなければならない。下記のようにして確認する。

$ sudo sysctl -a | grep 'ip_forward'
net.ipv4.ip_forward = 1
net.ipv4.ip_forward_update_priority = 1
net.ipv4.ip_forward_use_pmtu = 0

なっていなければ、設定を変更する。パケットキャプチャ環境は永続させるようなものではないので今回の起動中だけONであれば良い。そのためコマンドで下記のように一時的に変更する。

$ sudo sysctl net.ipv4.ip_forward=1

また、念の為デフォルトルートを次の通りに変更しておく。

$ sudo ip route del default
$ sudo ip route add default via 192.168.0.1 dev eno1
  • フォワードの必要性

    今回PCはルーターとして動作することになる。フォワードの設定は、PCがスマホ側からパケットを受け取ったとき、それを別のネットワークに転送できるようにする設定である。今回の場合、スマホ側からは次のようなパケットが飛んでくることが予想できる。

    • 宛先: いろいろ
    • 発信元: 192.168.10.2 (router2のNICのアドレス)

    スマホはいろんな場所に通信するので、宛先はいろいろ。当然すべての宛先をルーティングテーブル列挙するのは不可能である。そのため、このような場合はルーターはデフォルトルートにパケットを投げることになっている。

    PCは2つのNICをもつので、デフォルトルートはどうなるのかと一瞬わからなくなるが、NICがいくつあっても基本的にデフォルトルートは1つである。パケットを受け取ったら宛先を見て、どのNICから次のホストのIPに投げるかを考えるのがルーティングなので自分が知らないネットワークのIPが書いていればデフォルトルートに投げるものである。今回のルーティングテーブルは次のようになっている。

    $ ip route
    default via 192.168.0.1 dev eno1
    172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
    192.168.0.0/24 dev eno1 proto kernel scope link src 192.168.0.25 metric 1024
    192.168.0.1 dev eno1 proto dhcp scope link src 192.168.0.25 metric 1024
    192.168.10.0/24 dev eno2 proto kernel scope link src 192.168.10.1 linkdown
    218.219.82.240 via 192.168.0.1 dev eno1 proto dhcp src 192.168.0.25 metric 1024
    220.208.109.240 via 192.168.0.1 dev eno1 proto dhcp src 192.168.0.25 metric 1024
    

    これは(どのNIC、IPから届いたかに関わらず)自分に届いたパケットを見て、宛先IPを確認し、宛先IPが一番左側のIPに合致しなければ defaultに設定されているeno1のNICから192.168.0.1宛に送信するということになる。インターネット行きのIPアドレスはルーティングテーブルには書いてないので、基本的にスマホからの通信は全部eno2で受け取ったら defaultのeno1に転送される。この転送がフォワードで、これをするには先の設定が必要である。

    ちなみに上のテーブルのproto kernelが書いている行はカーネルが自動で設定した行なので何も触る必要はない。パケットは当然帰りもあるはずで、それは多分5行目、192.168.10.1からの192.168.10.0/24宛のパケットはeno2に転送しろというやつで転送される。 (実際にはsrcの指定は不要だと思うが、カーネルが自動で設定したので仕方がない)

    IPの基本ではるのだが、defaultにvia 192.168.0.1とあるがスマホ側からのパケットがこのPCで転送される際に、IPパケットの宛先IPは書き換えられるわけではない。ここが書き換えられると、受け取ったrouterが自分宛てだと勘違いしてしまい、スマホが贈りたかったインターネット上のサーバーまでパケットが届かなくなる。実際は192.168.0.1のMACアドレスが調べられて、Ethernetフレームの宛先MACアドレスがrouter1のアドレスになって転送されるはず。

    ルーティングを真面目に考えたことがあまりないので、少し混乱してしまったが、こんな感じだと思う。

NATの設定をする

PCのスマホ側にNATの設定をする。これはArchWikiにヒントをもらったわけだが、最初はNATを使っていなくて通信できず迷っていた。冷静に考えるとNATが必要であることがわかる。なぜならPCがルーターとなっているので、eno1とeno2は異なるネットワークでなければならず、そのためにはNATで変換が必要になるから。

手順はまさにWikiの通りとなる。

https://wiki.archlinux.jp/index.php/%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%8D%E3%83%83%E3%83%88%E5%85%B1%E6%9C%89#NAT_.E3.81.AE.E6.9C.89.E5.8A.B9.E5.8C.96

つまり下記のようにコマンドを打てばよい。iptablesが起動してなかったら、起動する。

$ sudo iptables -t nat -A POSTROUTING -o eno1 -j MASQUERADE
$ sudo iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
$ sudo iptables -A FORWARD -i eno2 -o internet0 -j ACCEPT
  • NATが必要な理由

    NATが必要な理由は、逆にNATがなければどうなるかを考えるとわかる。

    NATはもともとグローバルIPを割り当てることなくプライベートIPを使って通信ができるようにする技術である。今回スマホ側は192.168.10.0/24を割り当てるわけだが、このIPのままNATを通さずに通信をしたらどうなるかを考える。先の通り、PCによってこのスマホ側からのパケットが受け取られたあと、Router1に向かって投げられる。このときNATがなければSourceIPは192.168.10.Xのままになる。受け取ったRouter1は自分の配下のネットワークは192.168.0.0/24だと思っているのに 192.168.10.XのIPが飛んでくることになる。この時点でおかしいとわかる。Router1は自分が管理するネットワーク内から身に覚えないSourceIPをもつパケットを受け取ることになるので、正しく処理できない。もしRouter1がNATを使っておらず、Router2のインターネット側のIPまでが全部グローバルIPであればPCでもNATの設定は不要になるのかなと思う。

dhcpdを使ってDHCPを設定する

Router2をブリッジモードで動かすことができるのであれば、この手順は不要かもしれない。今回はうまく行かなかったので実施する。ブロードバンドルーターと呼ばれる家庭向けのルーターはけしからんことにWAN側のIPをstaticに設定できない。自動判別しかできないので、DHCPを動かしてあげないと自分のIPを設定できないため、この手順が必要となる。

まずdhcpdをインストールする。

$ sudo pacman -S dhcpd

それで、以下のように設定を作成する。/etc/dhpcd.confを書き換える。

# dhcpd.conf
#
# Sample configuration file for ISC dhcpd
#

option domain-name-servers 8.8.8.8, 8.8.4.4;
option subnet-mask 255.255.255.0;
option routers 192.168.10.1;
subnet 192.168.10.0 netmask 255.255.255.0 {
  range 192.168.10.2 192.168.10.250;
}

今回はDNSはオープンリゾルバを利用するが、別のでも良い。

それでdhpcdを起動するが、ArchLinux標準のserviceファイルではNICを指定して起動できない。そのため、他のNICにも悪影響を与える可能性があるので、今回は手動でdhcpdを起動する。まあ、永続的な環境ではなく検証のための一時間強であるためこれで問題ない。

$ sudo dhcpd -4 -cf /etc/dhcpd.conf -pf /run/dhcpd4/dhcpd.pid eno2

これで設定などに問題なければ起動する。

Router2を起動する

Router2を起動すると、上のDHCPサーバーに自動的に接続してローカルルーターモードで動作し始めるはずである。もししなかった場合はルーターの設定画面を開き、手動で再検索すると良いかもしれない。

パケットキャプチャする

Router2が無事に起動したら、あとはRouter2のWi-Fiにスマホをつないで通信をしてみるだけである。そのパケットはPCからWiresharkなどでキャプチャできる。NICはeno2を指定する。

Router2から届くパケットはRouter2でNATされるのでIPアドレスは1種類だけになる。そのためRouter2配下に複数のホストがいるとどのホストからの通信かわからなくなるのでキャプチャするときはRouter2配下には1台だけホストを接続したほうがいい。

結果

キャプチャしてみたはいいものの、通信はすこぶる快調で全くロードのエラーは起きなかった。もしかしたら新しいプロバイダに借りたルーターのWi-Fi機能が変なのかもしれない。ただ、その後そのWi-Fiにつないでゲームしてみても結構快適に動いてたのでたまたま時間帯が悪かっただけの可能性もある。もうちょっと試してみる必要がありそう。手順はわかったので比較的簡単にできるはず。

Router2をブリッジモードにできない理由がわからない

Router2は当初ブリッジモードでやる予定だった。そうすればPCでDHCPの設定は不要になる(NATは必要)。 DHCPの代わりに適当に各端末のNICに同じネットワークアドレスをもつIPを割り当てていけば良い。ブリッジモードとは要はスイッチなのでルーティングは行わず、ハブにつないでるような感じで使えるはず。そのはずなのだが、なぜかうちのWX1500HPでは通信できなかった。

PCでパケットをキャプチャしてみると、スマホに割り当てたIPなどからARPが大量に届いていたのでどうやらEthernetレベルでは疎通できている様子。しかしpingも通らないし、当然インターネットにもつながらなかった。理由は不明。 WX1500HPに有線ケーブルを挿せば本当に普通のスイッチのようになるので、PC2を有線で接続して同じように試してみたがだめだった。 WXを再起動すると起動中の僅かな時間だけpingが通る謎現象が起きたが、起動しきってしまうとやはり疎通できなくなった。

ちなみにWXを介さずに、PCとPC2を直接LANケーブルで接続して、手動でIPを割り当ててpingを飛ばすと普通に疎通するし、 NATを設定すればインターネットに接続もできた。

WXのブリッジモードでなぜ疎通しないかは完全に謎。NECに鬼電しないといけないかもしれないが、まあ上の通りルーターモードにしてDHCPを使えばできるにはできたので今回はヨシ。

終わり

ということで結構苦戦したもののなかなかおもしろかった。PCにNICが2枚ついてるのでこういうことができる。有線NICが2枚なわけだけど、Wi-FIもついてるので頑張れば3NICで遊べるのかもしれない。デスクトップPCでWi-Fiは使わないのでマザボでOFFにしてるけど。ちなみにそのPCのマザボは"ProArt X670E-CREATOR WIFI"ね。

最後に、格闘後のデスクの様子をどうぞ。なんかgeekっぽいねという自己満足。こんな感じでネットワーク強者を目指して色々やってみようね。

Linuxのネットワーク機能って色々できそうなのでKVMとかも使いつつ色々仮想ネットワークで遊んでも面白いかもしれない。あとは、tunとかtapとか、そういうのも理解を深めていきたいね。