Python2 + Scapyで、Raspberry Pi 2 Model B をブリッジにできるか試してみた #router_jisaku

以前、Windows10 + Scapyを簡単にさわってみました。
Windows10にScapyをインストールする - メモ的な思考的な

 
もう少し詳しくScapyをさわってみたいと思い、何か作ろうと考えました。

作る題材を探していたところ、書籍「ルーター自作でわかるパケットの流れ」に出会いました。
ルーター自作でわかるパケットの流れ ~ソースコードで体感するネットワークのしくみ:書籍案内|技術評論社

ルーター自作でわかるパケットの流れ

ルーター自作でわかるパケットの流れ

 
書籍ではC言語を使ってパケットを扱い、ブリッジやルーターを自作していました。パケットを扱うならScapyでも書けるのではと思いました。

そこで、Python2 + Scapyを使って、Raspberry Pi 2 Model B をブリッジにできるか試してみました。

 
目次

 

環境

今回は 192.168.10.0/24 のローカル環境にて

  • Mac = HTTPサーバ
  • 無線LANアクセスポイント = スイッチ
  • ラズパイ = ブリッジ
  • Windows10 = ブリッジにぶら下げたクライアント

の役割を持たせました。

 

ネットワーク構成
-----------------------------------------
Mac
(`en0` : WiFi)
-----------------------------------------
|
|
-----------------------------------------
(無線)
無線LANアクセスポイント
(スイッチ)
-----------------------------------------
|
|
-----------------------------------------
(`eth0` : オンボードLANアダプタ)
Raspberry Pi 2 Model B
(`eth1` : 外付けUSB有線LANアダプタ)
-----------------------------------------
|
|
-----------------------------------------
(オンボードLANアダプタ)
Windows10
-----------------------------------------

 

ネットワークアダプタ設定

明示的に設定したネットワークアダプタ設定です。

なお、以降の表記は

とします。

機器 アダプタ IPアドレス DGW DNS
Mac en0 192.168.10.101/24 192.168.10.1 192.168.10.1
無線LAN AP 192.168.10.1/24
ラズパイ eth0 192.168.10.50/24 192.168.10.1
ラズパイ eth1
Windows 192.168.10.201/24 192.168.10.1 192.168.10.1

 

各機器の構成

 
なお、Scapyは2つあります。

ただ、何かあった時に

  • 自分の書き方が悪い
  • scapy-python3の非互換なところ

の区別がつかないため、今回はPython2版のScapyを使いました。

 

ラズパイのセットアップ

以前ラズパイは使用していましたが、2015/7以降使っていなかったため、改めてラズパイをセットアップします。

 

メモリカードの調達

以前は、bootとrootを分けたファイルシステムを使っていました。
Raspberry Pi 2 + Raspbianで、microSDをboot、USBメモリをrootというファイルシステムにする - メモ的な思考的な

ただ、セットアップするのがめんどうなので、相性の良さそうなメモリカードを調達することにしました。

安価で入手しやすく動作報告の多いものを調べたところ、東芝MSDAR40N08G が良さそうでした。

年末年始だったので家電量販店に行きましたが、普通に売っていました。

 

Raspbianのインストール

公式のインストールガイドに従い、Raspbianをインストールしました。
Installing operating system images - Raspberry Pi Documentation

  • RASPBIAN STRETCH WITH DESKTOP のzipファイルをダウンロード
  • MacUSBメモリカードリーダーを接続し、Etcher でzipファイルをmicroSDに書き込む

 

Raspbianの初期セットアップ

ラズパイに microSDを挿入し、外付けキーボード・HDMI接続のモニタ・LANケーブル・外付けUSB有線LANアダプタをつないで、GUIで初期セットアップします。

USBマウスは手元に無かったものの、キーボード操作で何とかなりました。

 

キーボードレイアウトの変更

OADG 109A を選びました。

 

vimのインストール
pi@raspberrypi:~ $ sudo apt-get update
pi@raspberrypi:~ $ sudo apt-get install vim

 

ネットワークアダプタ固定IPアドレス割り当て

今回はブリッジなため、ラズパイのネットワークアダプタにはIPアドレスがいらない気がしました。

ただ、SSHを使ってMac上のターミナルからPythonコードを書きたいため、ラズパイのネットワークアダプタIPアドレスを割り当てます。

設定前のラズパイのネットワークアダプタを見ると

pi@raspberrypi:~ $ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
...

eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
...

でした。

今回は、ラズパイのオンボードLANアダプタをMacに接続するため、 eth0固定IPアドレスを割り当てます。

 
以前ラズパイに固定IPアドレスを割り当てた時は、 /etc/network/interfaces に追加していました。
Raspberry Pi 2 Model BにRaspbianをセットアップした - メモ的な思考的な

ただ、現在はDebian Stretch (Linux 4.9 カーネル)がベースなため、設定ファイルが /etc/dhcpcd.conf に変更となっています。

pi@raspberrypi:~ $ vi /etc/dhcpcd.conf

# 末尾に以下を追加
interface eth0
static ip_address=192.168.10.50/24
static routers=192.168.10.1
static domain_name_servers=192.168.10.1 

 
設定を反映・確認します。

# 設定を反映
pi@raspberrypi:~ $ sudo service dhcpcd reload
sending signal HUP to pid 323

# 設定内容を確認
pi@raspberrypi:~ $ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.10.50  netmask 255.255.255.0  broadcast 192.168.10.255

 

SSHを有効化

raspi-config にて有効化します。

pi@raspberrypi:~ $ sudo raspi-config

5 Interfacing Options > P2 SSH にて enabled にします。

 
MacからラズパイにSSHしてみましたが、問題なく動作しました。  

$ ssh pi@192.168.10.50
pi@192.168.10.50's password: (raspberryと入力)
Linux raspberrypi 4.9.59-v7+ #1047 SMP Sun Oct 29 12:19:23 GMT 2017 armv7l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Jan  1 23:37:50 2018 from 192.168.10.101

SSH is enabled and the default password for the 'pi' user has not been changed.
This is a security risk - please login as the 'pi' user and type 'passwd' to set a new password.

pi@raspberrypi:~ $ 

 

Scapyのインストール

ラズパイでScapyを実行する場合、 sudo python <scapyのコード> とします。

そのため、Scapyはsudoでインストールします。

# pythonのバージョンを確認
pi@raspberrypi:~ $ sudo python --version
Python 2.7.13

# Scapyのインストール
(env) pi@raspberrypi:~ $ sudo pip install scapy
Collecting scapy
  Downloading scapy-2.3.3.tgz (1.4MB)
    100% |████████████████████████████████| 1.4MB 84kB/s 
Building wheels for collected packages: scapy
  Running setup.py bdist_wheel for scapy ... done
  Stored in directory: /root/.cache/pip/wheels/bd/cf/05/d5abc9b4434f39ffe231517dfb8dab96241fef6a99459051f9
Successfully built scapy
Installing collected packages: scapy
Successfully installed scapy-2.3.3

以上でラズパイのセットアップは完了です。

 

疎通確認の準備

MacでローカルHTTPサーバを起動

ブリッジを介しても通信できるか確認するため、MacPythonのHTTPサーバを起動しておきます。

# Python2の場合
$ python -m SimpleHTTPServer

# Python3の場合
$ python -m http.server

 

Windowsから通信できないことを確認

WindowsからMacPython HTTPサーバへcurlを実行し、通信できないことを確認します。

> curl -m5 http://192.168.10.101:8000
curl: (28) Connection timed out after 5015 milliseconds

 

Scapyの「bridge_and_sniff」関数でラズパイをブリッジ化

ここまででラズパイの準備ができました。次はラズパイをScapyでブリッジにしてみます。

Scapyで便利な関数がないかを探したところ、 scapy/scapy/sendrecv.pybridge_and_sniff() 関数がありました。
https://github.com/secdev/scapy/blob/v2.3.3/scapy/sendrecv.py#L638

 
そこで、ラズパイ上に以下のファイルを作成し、ブリッジにできるかを試してみます。

scapy_bridge_and_sniff.py

# -*- coding: utf-8 -*-
from scapy.sendrecv import bridge_and_sniff


if __name__ == '__main__':
    print '>----- enable bridge -----'
    bridge_and_sniff('eth0', 'eth1')
    print '<----- disable bridge -----'

 
続いて、上記のファイルを実行します。rootでないと動作しないため、 sudo を忘れずに行います。

pi@raspberrypi:~/router_jisaku $ sudo python scapy_bridge_and_sniff.py
>----- enable bridge -----

 
動作確認のため、Windowsからcurlを実行します。

なお、今回はタイムアウトを5秒( -m 5 ) としました。ただ、通信状況によっては5秒以内には完了しないため、必要に応じてタイムアウトを延長します。

>curl -m5 http://192.168.10.101:8000
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
...
</html>

HTMLが返ってきました。通信できたようです。

 
Mac上のHTTPサーバのログにも通信ログがありました。

192.168.10.201 - - [02/Jan/2018 10:30:59] "GET / HTTP/1.1" 200 -

 
スクリプト scapy_bridge_and_sniff.py を停止した時の挙動を見てみます。

ラズパイにて Ctrl + Cスクリプトを停止します。

pi@raspberrypi:~/router_jisaku $ sudo python scapy_bridge_and_sniff.py
>----- enable bridge -----
^C<----- disable bridge -----

 
Windowscurlで確認します。

>curl -m5 http://192.168.10.101:8000
curl: (28) Connection timed out after 5000 milliseconds

接続できなくなっていました。

 
以上より、Python2 + Scapyで、Raspberry Pi 2 Model B をブリッジにできました。

ただ、今回はScapyの bridge_and_sniff() 関数を使うだけだったので、もう少し他の実装を考えてみます。

 

ソースコード

GitHubに上げました。scapy_python2/bridge/scapy_bridge_and_sniff.py が今回のファイルです。
https://github.com/thinkAmi-sandbox/syakyo-router_jisaku