scapy で仮想 IP を作成し ping の応答を得る。


はじめに

Scapy は、ネットワークパケット操作のツールで、パケットの生成からデコードまで、色々な事ができます。
例えば、通信エラー発生時のパケットを同じように作成して動作確認するなど、問題の再現テストなどにも利用できると思います。
ここでは、単に仮想的なアドレスに対しての ping 応答を得るだけの環境を作成してみましたので、その説明です。

scapy のインストール

・RedHat
 https://access.redhat.com/solutions/3759961
 (How to install scapy for network troubleshooting in Red Hat Enterprise Linux 7? )

arp 応答プログラム

IP 通信を行うためには、まずは arp の問い合わせに対して応答を返さないといけません。
そのためのプログラムとして以下。※どこかの URL を参考にしたのですが、もう思い出せない。

#!/usr/bin/python3

from scapy.all import *

# Your network broadcast address
broadcastNet = "192.168.1.255"

macDict = { "192.168.1.123" : "01:01:01:01:01:01" }

# Use MAC address of this machine as source. If not eth0, change this:            
myMAC = get_if_hwaddr('eth1')

def handle_packet(packet):
    if packet[ARP].op == 1:     # who-has
        print("Someone is asking about " + packet.pdst)
        print(packet.summary())

        if packet.pdst in macDict:
            print("Sending ARP response for " + packet.pdst)
            reply = ARP(op=2, hwsrc=macDict[packet.pdst], psrc=packet.pdst, hwdst="ff:ff:ff:ff:ff:ff", pdst=broadcastNet)
            # reply = ARP(op=ARP.is_at, hwsrc=macDict[packet.pdst], psrc=packet.pdst, hwdst="ff:ff:ff:ff:ff:ff", pdst=broadcastN
et)
            go = Ether(dst="ff:ff:ff:ff:ff:ff", src=myMAC) / reply
            sendp(go)
    return

# Sniff for ARP packets. Run handle_packet() on each one
sniff(filter="arp", prn=handle_packet, store=0)

ping を打って arp テーブルを確認

つまり、こういう事。
ping の応答は、まだ得られませんが arp テーブルは出来ていることがわかります。

ping の応答プログラム

#!/usr/bin/python3

from scapy.all import *

def handle_packet(packet):
    if str(packet[ICMP].type) == "8":
        if packet[IP].dst == '192.168.1.123':
            # packet.show()
            echoReply = IP(dst=packet[IP].src, src='192.168.1.123')/ICMP()/Raw()
            echoReply[ICMP].type = 0    # echo reply
            echoReply[ICMP].id = packet[ICMP].id
            echoReply[ICMP].seq = packet[ICMP].seq
            echoReply[ICMP][Raw].load = packet[ICMP][Raw].load
            send(echoReply)
    return

# Sniff for icmp packets
sniff(filter="icmp", prn=handle_packet, store=0)

実行してみる

こんな感じ、めでたく 192.168.1.123 へ ping が飛びました。