Python

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

More than 1 year has passed since last update.

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

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

それにしても、湧き溢れるアイディアを実際に具体化してしまった@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ライブラリ環境が必要になります。

(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に掲載されたサンプルアプリを配備します。

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を返信しないように、先ほどのサンプルアプリを改造します。

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とか、使わずとも、パケット制御できてしまうあたりが、とても、感銘を受けました。