aboutsummaryrefslogtreecommitdiff
path: root/ethsteal/arp.py
diff options
context:
space:
mode:
Diffstat (limited to 'ethsteal/arp.py')
-rwxr-xr-xethsteal/arp.py192
1 files changed, 192 insertions, 0 deletions
diff --git a/ethsteal/arp.py b/ethsteal/arp.py
new file mode 100755
index 0000000..4bccb7c
--- /dev/null
+++ b/ethsteal/arp.py
@@ -0,0 +1,192 @@
+#!/usr/bin/env python
+#coding=utf8
+#
+# A python implementation of send_arp.c.
+#
+# ARP code and license inspired by arprequest by Antoine Millet
+# See http://pypi.python.org/pypi/arprequest
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# Version 2, December 2004
+#
+# Copyright (C) 2011 Kristoffer Gronlund
+# Everyone is permitted to copy and distribute verbatim or modified
+# copies of this license document, and changing it is allowed as long
+# as the name is changed.
+#
+# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+# 0. You just DO WHAT THE FUCK YOU WANT TO.
+#
+
+from optparse import OptionParser
+import time
+import socket
+import itertools
+import os
+import signal
+import atexit
+import sys
+from struct import pack
+
+
+def commandline():
+ parser = OptionParser(
+ usage="%prog [OPTIONS] device src_ip_addr src_hw_addr broadcast_ip_addr netmask")
+ parser.add_option("-i", "--interval", dest="interval", default="1000",
+ help="Repeat interval in ms", metavar="INTERVAL")
+ parser.add_option("-r", "--repeat", dest="repeat", default="1",
+ help="Repeat count, 0 or less leads to infinite repeat",
+ metavar="REPEAT")
+ parser.add_option("-p", "--pidfile", dest="pidfile",
+ default="/tmp/arp.pid",
+ help="PID file", metavar="PID")
+
+ (options, args) = parser.parse_args()
+
+ if len(args) != 5:
+ parser.error("Expects: [-i repeatinterval-ms] [-r repeatcount] [-p pidfile] \\\n" +
+ " device src_ip_addr src_hw_addr broadcast_ip_addr netmask")
+
+ class Args(object):
+ pass
+ ret = Args()
+ ret.interval = int(options.interval)
+ ret.repeat = int(options.repeat)
+ ret.pidfile = options.pidfile
+ ret.device = args[0]
+ ret.src_ip_addr = args[1]
+ ret.src_hw_addr = args[2]
+ ret.broadcast_ip_addr = args[3]
+ ret.netmask = args[4]
+ return ret
+
+
+def mssleep(ms):
+ time.sleep(ms/1000.0)
+
+bcast_mac = pack('!6B', *(0xFF,)*6)
+zero_mac = pack('!6B', *(0x00,)*6)
+ARPOP_REQUEST = pack('!H', 0x0001)
+ARPOP_REPLY = pack('!H', 0x0002)
+# Ethernet protocol type (=ARP)
+ETHERNET_PROTOCOL_TYPE_ARP = pack('!H', 0x0806)
+# ARP logical protocol type (Ethernet/IP)
+ARP_PROTOCOL_TYPE_ETHERNET_IP = pack('!HHBB', 0x0001, 0x0800, 0x0006, 0x0004)
+
+
+def send_arp(ip, device, sender_mac, broadcast, netmask, arptype,
+ request_target_mac=zero_mac):
+ #if_ipaddr = socket.gethostbyname(socket.gethostname())
+ sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.SOCK_RAW)
+ sock.bind((device, socket.SOCK_RAW))
+
+ socket_mac = sock.getsockname()[4]
+ if sender_mac == 'auto':
+ sender_mac = socket_mac
+# else:
+# raise Exception("Can't ARP this: " + sender_mac)
+
+ arpop = None
+ target_mac = None
+ if arptype == 'REQUEST':
+ target_mac = request_target_mac
+ arpop = ARPOP_REQUEST
+ else:
+ target_mac = sender_mac
+ arpop = ARPOP_REPLY
+
+ sender_ip = pack('!4B', *[int(x) for x in ip.split('.')])
+ target_ip = pack('!4B', *[int(x) for x in ip.split('.')])
+
+ arpframe = [
+ # ## ETHERNET
+ # destination MAC addr
+ bcast_mac,
+ # source MAC addr
+ socket_mac,
+ ETHERNET_PROTOCOL_TYPE_ARP,
+
+ # ## ARP
+ ARP_PROTOCOL_TYPE_ETHERNET_IP,
+ # operation type
+ arpop,
+ # sender MAC addr
+ sender_mac,
+ # sender IP addr
+ sender_ip,
+ # target hardware addr
+ target_mac,
+ # target IP addr
+ target_ip
+ ]
+
+ # send the ARP
+ sock.send(''.join(arpframe))
+
+
+def write_pid_file(file_path):
+ # http://stackoverflow.com/a/10979569/83741
+ handle = os.open(file_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY)
+ with os.fdopen(handle, 'w') as f:
+ f.write(str(os.getpid()))
+
+
+def remove_pid_file(file_path):
+ os.unlink(file_path)
+
+
+def setup_pid_file(file_path):
+ write_pid_file(file_path)
+
+ def signal_handler(signum=None, frame=None):
+ remove_pid_file(file_path)
+ sys.exit(0)
+
+ # http://stackoverflow.com/a/11858588/83741
+ # Only a limited set of signals are available on Windows (see #2)
+ # Also see https://docs.python.org/2/library/signal.html#signal.signal
+ import platform
+ if platform.system() == 'Windows':
+ signal_list = [signal.SIGTERM]
+ else:
+ signal_list = [signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGQUIT]
+ for sig in signal_list:
+ signal.signal(sig, signal_handler)
+
+ atexit.register(lambda: remove_pid_file(file_path))
+
+
+def main():
+ args = commandline()
+ setup_pid_file(args.pidfile)
+ span = range(args.repeat) if args.repeat > 0 else itertools.count()
+ for j in span:
+
+ # Send the same packets as outlined here:
+ # http://support.citrix.com/article/ctx109980
+
+ send_arp(args.src_ip_addr, args.device,
+ args.src_hw_addr,
+ args.broadcast_ip_addr,
+ args.netmask, 'REQUEST',
+ request_target_mac=bcast_mac)
+
+ send_arp(args.src_ip_addr, args.device,
+ args.src_hw_addr,
+ args.broadcast_ip_addr,
+ args.netmask, 'REQUEST',
+ request_target_mac=zero_mac)
+
+ send_arp(args.src_ip_addr, args.device,
+ args.src_hw_addr,
+ args.broadcast_ip_addr,
+ args.netmask, 'REPLY')
+
+ if j != args.repeat - 1:
+ mssleep(args.interval)
+
+
+if __name__ == "__main__":
+ main()