[PYTHON] It is a piggybacking story about the service that returns "Nyan" when you ping

Recently, I tried a little operation principle about "ping ASCII art" which has become a hot topic in the network industry. I'm sorry because it's just a touch, but it's just for personal study.

The original story will be the presentation material of JANOG BoF & LT Night # 2 by @kooshin.

-ping ASCII Art (JANOG BoF & LT Night # 2) This is the presentation material of -A service that returns "Nyan" when you ping, developed by a network engineer

Even so, @kooshin, who has actually embodied the overflowing ideas, is really amazing.

■ I tried the Ubuntu environment

I created an environment with 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"

■ Create a python virtual environment for the root user

I don't want to pollute the python environment as the root user, so I will maintain a virtual environment with pyenv. It has nothing to do with the main subject, so you can skip the work here.

(1) Install various packages in advance

tsubo@ubuntu:~$ sudo apt-get install git gcc make openssl libssl-dev libbz2-dev libreadline-dev libsqlite3-dev

(2) Prepare the pyenv environment

root@ubuntu:~# git clone https://github.com/yyuu/pyenv.git ~/.pyenv

(3) Add the pyenv environment variable to the root user

Add the following environment variables to .bashrc

export PYENV_ROOT=$HOME/.pyenv
export PATH=$PYENV_ROOT/bin:$PATH
eval "$(pyenv init -)"

Enable the added environment variable

root@ubuntu:~# source .bashrc 

(4) Check the installation result of pyenv

root@ubuntu:~# pyenv --version
pyenv 1.1.3-33-g48aa0c4

(5) Enable python 3.6.2.

python3.6.2 install

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

Enable python3.6.2

root@ubuntu:~# pyenv global 3.6.2
root@ubuntu:~# pyenv versions
  system
* 3.6.2 (set by /root/.pyenv/version)

■ Install various python libraries in advance

You will need these python library environments.

(1) Install 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) Install 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

■ First, let's check the operation of NetfilterQueue

(1) Deploy the Ping sample application

First, deploy the sample app posted on 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) Set iptables

root@ubuntu:~# iptables -A INPUT -p icmp -j NFQUEUE --queue-num 1

(3) Actually start the sample application

root@ubuntu:~# python sample_icmp.py 

(4) When I try to ping from another terminal etc.

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

** No problem, ping succeeds. ** **

(5) Earlier, the sample application was displayed on the startup screen ...

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

With that feeling, I can now confirm that the ICMP packet has been received.

■ Next, let's check the scapy operation

(1) Extend the Ping sample app

If the sequence number of ICMP Echo Request is "multiple of 5", modify the previous sample application so that it does not reply ICMP Echo Reply.

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) Actually start the sample application

root@ubuntu:~# python sample_icmp_fake.py 
WARNING: No route found for IPv6 destination :: (no default route?). This affects only IPv6

(3) When I try to ping from another terminal etc.

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

** As expected, if icmp_seq is a "multiple of 5", the ping will fail. ** **

■ Finally

As mentioned above, I was able to confirm the operation, although it is a small part. I was very impressed with the fact that packets can be controlled without using OpenFlow.

Recommended Posts

It is a piggybacking story about the service that returns "Nyan" when you ping
The story that a hash error came out when using Pipenv
The story of making a slackbot that outputs as gif or png when you send the processing code
[AtCoder for beginners] A story about the amount of calculation that you want to know very roughly
[Stada] A script that will call you when Pokemon GO is released
The story that it turns blue when the data read by Pillow is converted so that it can be handled by OpenCV
It seems that the version of pyflakes is not the latest when flake8 is installed
Deep Learning! The story of the data itself that is read when it does not follow after handwritten number recognition
A story that was convenient when I tried using the python ip address module
A story that failed when trying to remove the suffix from the string with rstrip
A story that got stuck when trying to upgrade the Python version on GCE
There is a pattern that the program did not stop when using Python threading
Two solutions to the problem that it is hard to see the vector field when writing a vector field with quiver () of matplotlib pyplot
A story that reduces the effort of operation / maintenance
# Function that returns the character code of a string
A story that struggled with the common set HTTP_PROXY = ~
A story about changing the master name of BlueZ
Zip 4 Gbyte problem is a story of the past
A story that analyzed the delivery of Nico Nama.
When you think the update of ManjaroLinux is strange
Creating a list when the nomenclature is a fixed time
Smartly announce that it is a deprecated implementation --debtcollerctor
Did you forget the password for a PDF with a password? pdfcrack may do something about it
A story about PHP that was okay in the development environment but buggy in the production environment LEVEL 1-3 + 1
About the case that it became a Chinese font after updating with Linux (correction method)
About the matter that nosetests does not pass when __init__.py is created in the project directory
A story that makes it easy to estimate the living area using Elasticsearch and Python
About the contents of wscript when building a D language environment like that with Waf
A story that makes it easier to see Model debugging in the Django + SQLAlchemy environment
A story that pyenv is stuck because the python execution command PATH does not pass