pingを打つと「にゃーん」を返すサービスについての便乗ネタです

最近、ネットワーク業界の巷で話題になっている”pingアスキーアート"について、少し動作原理を試してみました。ほんのさわりだけなので恐縮ですが、あくまでの個人勉強のためですね。

元ネタは、@kooshinさんによるJANOG BoF & LT Night #2 の発表資料になります。

- [pingアスキーアート](https://www.janog.gr.jp/meeting/bof-lt-night-2/doc/bof-lt-night-2_lt_ping-iwata.pdf) ( JANOG BoF & LT Night #2 の発表資料です)
- [pingを打つと「にゃーん」を返すサービス、ネットワークエンジニアが開発](http://www.itmedia.co.jp/news/articles/1709/07/news106.html)

それにしても、湧き溢れるアイディアを実際に具体化してしまった@kooshinさんは、ほんと凄いです。

# ■ 試してみた、Ubuntu環境
Ubuntu 16.04.3 LTSで環境を作ってみました。

```
root@ubuntu:~# cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.3 LTS"
```

# ■ rootユーザのpython仮想環境を作る
rootユーザでのpython環境を汚したくないので、pyenvで仮想環境を整備しておきます。
本題とは、あまり関係ないので、ここの作業は、スキップしても問題ないです。

### (1) 事前に、各種パッケージをインストールしておく
```
tsubo@ubuntu:~$ sudo apt-get install git gcc make openssl libssl-dev libbz2-dev libreadline-dev libsqlite3-dev
```

### (2) pyenv環境を整備する
```
root@ubuntu:~# git clone https://github.com/yyuu/pyenv.git ~/.pyenv
```

### (3) rootユーザにpyenv環境変数を追加する
####.bashrcに以下の環境変数を追加する
```
export PYENV_ROOT=$HOME/.pyenv
export PATH=$PYENV_ROOT/bin:$PATH
eval "$(pyenv init -)"
```
####追加した環境変数を有効にする
```
root@ubuntu:~# source .bashrc 
```

### (4) pyenvのインストール結果を確認しておく
```
root@ubuntu:~# pyenv --version
pyenv 1.1.3-33-g48aa0c4
```
### (5) python 3.6.2を有効にする
####python3.6.2インストールする

```
root@heat:~# pyenv install 3.6.2
Downloading Python-3.6.2.tar.xz...
-> https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tar.xz
Installing Python-3.6.2...
Installed Python-3.6.2 to /root/.pyenv/versions/3.6.2
```
####python3.6.2を有効にする
```
root@ubuntu:~# pyenv global 3.6.2
root@ubuntu:~# pyenv versions
  system
* 3.6.2 (set by /root/.pyenv/version)
```

# ■ 各種pythonライブラリを事前にインストールする
これらのpythonライブラリ環境が必要になります。

- [NetfilterQueue](https://github.com/kti/python-netfilterqueue)
- [Scapy](http://scapy.readthedocs.io/en/latest/)

### (1) NetfilterQueueをインストールする
```
root@ubuntu:~# pip install NetfilterQueue
Collecting NetfilterQueue
  Downloading NetfilterQueue-0.8.1.tar.gz (58kB)
    100% |████████████████████████████████| 61kB 3.2MB/s 
Installing collected packages: NetfilterQueue
  Running setup.py install for NetfilterQueue ... done
Successfully installed NetfilterQueue-0.8.1
```
### (2) Scapyをインストールする
```
root@ubuntu:~# pip install scapy-python3
Collecting scapy-python3
  Downloading scapy-python3-0.21.tar.gz (2.2MB)
    100% |████████████████████████████████| 2.2MB 694kB/s 
Installing collected packages: scapy-python3
  Running setup.py install for scapy-python3 ... done
Successfully installed scapy-python3-0.21
```

# ■ まずは、NetfilterQueue動作を確認してみる

### (1) Pingサンプルアプリ配備する
まずは、[NetfilterQueue](https://github.com/kti/python-netfilterqueue)に掲載されたサンプルアプリを配備します。

```python:sample_icmp.py 
from netfilterqueue import NetfilterQueue

def print_and_accept(pkt):
    print(pkt)
    pkt.accept()

nfqueue = NetfilterQueue()
nfqueue.bind(1, print_and_accept)
try:
    nfqueue.run()
except KeyboardInterrupt:
    print('')

nfqueue.unbind()
```
### (2) iptablesを設定する
```
root@ubuntu:~# iptables -A INPUT -p icmp -j NFQUEUE --queue-num 1
```

### (3) 実際、サンプルアプリを起動します
```
root@ubuntu:~# python sample_icmp.py 
```
### (4) 別端末等から、pingをうってみると、
```
ttsubo-no-macbook-pro:~ ttsubo$ ping 192.168.195.204
PING 192.168.195.204 (192.168.195.204): 56 data bytes
64 bytes from 192.168.195.204: icmp_seq=0 ttl=64 time=0.409 ms
64 bytes from 192.168.195.204: icmp_seq=1 ttl=64 time=0.491 ms
64 bytes from 192.168.195.204: icmp_seq=2 ttl=64 time=0.732 ms
64 bytes from 192.168.195.204: icmp_seq=3 ttl=64 time=0.753 ms
64 bytes from 192.168.195.204: icmp_seq=4 ttl=64 time=0.400 ms
^C
--- 192.168.195.204 ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.400/0.557/0.753/0.155 ms
```
**問題なく、pingが成功しますね。**

### (5) 先ほど、サンプルアプリが起動画面に、、、

```
root@ubuntu:~# python sample_icmp.py 
ICMP packet, 84 bytes
ICMP packet, 84 bytes
ICMP packet, 84 bytes
ICMP packet, 84 bytes
ICMP packet, 84 bytes
```
という感じて、ICMPパケットを受信したことが確認できるようになりました。


# ■ つづいて、scapy動作を確認してみる

### (1) Pingサンプルアプリを拡張する
ICMP EchoRequestのシーケンス番号が"5の倍数”だったら、ICMP EchoReplyを返信しないように、先ほどのサンプルアプリを改造します。

```python:sample_icmp_fake.py
from scapy.all import *
from netfilterqueue import NetfilterQueue


def print_and_accept(pkt):
    packet = IP(pkt.get_payload())
    icmp = packet[ICMP]
    if (icmp.seq % 5) == 0:
        pkt.drop()
    else:
        pkt.accept()

if __name__ == "__main__":
    nfqueue = NetfilterQueue()
    nfqueue.bind(1, print_and_accept)
    try:
        nfqueue.run()
    except KeyboardInterrupt:
        print('')

    nfqueue.unbind()
```

### (2) 実際、サンプルアプリを起動します
```
root@ubuntu:~# python sample_icmp_fake.py 
WARNING: No route found for IPv6 destination :: (no default route?). This affects only IPv6
```
### (3) 別端末等から、pingをうってみると、
```
ttsubo-no-macbook-pro:~ ttsubo$ ping 192.168.195.204
PING 192.168.195.204 (192.168.195.204): 56 data bytes
Request timeout for icmp_seq 0
64 bytes from 192.168.195.204: icmp_seq=1 ttl=64 time=1.515 ms
64 bytes from 192.168.195.204: icmp_seq=2 ttl=64 time=1.507 ms
64 bytes from 192.168.195.204: icmp_seq=3 ttl=64 time=1.367 ms
64 bytes from 192.168.195.204: icmp_seq=4 ttl=64 time=1.383 ms
Request timeout for icmp_seq 5
64 bytes from 192.168.195.204: icmp_seq=6 ttl=64 time=1.453 ms
64 bytes from 192.168.195.204: icmp_seq=7 ttl=64 time=1.694 ms
64 bytes from 192.168.195.204: icmp_seq=8 ttl=64 time=1.301 ms
64 bytes from 192.168.195.204: icmp_seq=9 ttl=64 time=1.376 ms
Request timeout for icmp_seq 10
64 bytes from 192.168.195.204: icmp_seq=11 ttl=64 time=1.273 ms
64 bytes from 192.168.195.204: icmp_seq=12 ttl=64 time=1.358 ms
64 bytes from 192.168.195.204: icmp_seq=13 ttl=64 time=1.161 ms
64 bytes from 192.168.195.204: icmp_seq=14 ttl=64 time=1.180 ms
Request timeout for icmp_seq 15
64 bytes from 192.168.195.204: icmp_seq=16 ttl=64 time=1.305 ms
64 bytes from 192.168.195.204: icmp_seq=17 ttl=64 time=1.288 ms
^C
--- 192.168.195.204 ping statistics ---
18 packets transmitted, 14 packets received, 22.2% packet loss
round-trip min/avg/max/stddev = 1.161/1.369/1.694/0.135 ms
```
**期待通り、icmp_seqが"5の倍数"の場合には、pingが失敗しますね。**

# ■ 最後に、
以上、ごく一部ですが、動作確認できました。
OpenFlowとか、使わずとも、パケット制御できてしまうあたりが、とても、感銘を受けました。