Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions tests/scripts/arp_responder.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
scapy2.conf.use_pcap=True
import scapy.arch.pcapdnet

NEIGH_SOLICIT_ICMP_MSG_TYPE = 135

def hexdump(data):
print " ".join("%02x" % ord(d) for d in data)

Expand Down Expand Up @@ -46,7 +48,7 @@ def __del__(self):
self.socket.close()

def bind(self):
self.socket = scapy2.conf.L2listen(iface=self.iface, filter='arp')
self.socket = scapy2.conf.L2listen(iface=self.iface, filter='arp || ip6[40] = {}'.format(NEIGH_SOLICIT_ICMP_MSG_TYPE))

def handler(self):
return self.socket
Expand Down Expand Up @@ -85,6 +87,7 @@ def poll(self):

class ARPResponder(object):
ARP_PKT_LEN = 64
NDP_PKT_LEN = 90
ARP_OP_REQUEST = 1
def __init__(self, ip_sets):
self.arp_chunk = binascii.unhexlify('08060001080006040002') # defines a part of the packet for ARP Reply
Expand All @@ -97,9 +100,12 @@ def __init__(self, ip_sets):
def action(self, interface):
data = interface.recv()

if len(data) > self.ARP_PKT_LEN:
return
if len(data) <= self.ARP_PKT_LEN:
return self.reply_to_arp(data, interface)
elif len(data) <= self.NDP_PKT_LEN:
return self.reply_to_ndp(data, interface)

def reply_to_arp(self, data, interface):
remote_mac, remote_ip, request_ip, op_type, vlan_id = self.extract_arp_info(data)

# Don't send ARP response if the ARP op code is not request
Expand All @@ -121,6 +127,29 @@ def action(self, interface):

return

def reply_to_ndp(self, data, interface):
remote_mac, remote_ip, target_ip = self.extract_ndp_info(data)

target_ip_str = socket.inet_ntop(socket.AF_INET6, target_ip)
if target_ip_str in self.ip_sets[interface.name()]:
remote_ip_str = socket.inet_ntop(socket.AF_INET6, remote_ip)
neigh_adv_pkt = self.generate_neigh_adv(self.ip_sets[interface.name()][target_ip_str], remote_mac, target_ip_str, remote_ip_str)
interface.send(neigh_adv_pkt)

return

def extract_ndp_info(self, data):
vlan_offset = 0

if len(data) == 90:
vlan_offset = 4

remote_mac = data[6:12]
remote_ip = data[22 + vlan_offset:38 + vlan_offset]
target_ip = data[62 + vlan_offset:78 + vlan_offset]

return remote_mac, remote_ip, target_ip

def extract_arp_info(self, data):
# remote_mac, remote_ip, request_ip, op_type
rem_ip_start = 28
Expand Down Expand Up @@ -151,6 +180,13 @@ def generate_arp_reply(self, local_mac, remote_mac, local_ip, remote_ip, vlan_id

return eth_hdr + self.arp_chunk + local_mac + local_ip + remote_mac + remote_ip + self.arp_pad

def generate_neigh_adv(self, local_mac, remote_mac, target_ip, remote_ip):
neigh_adv_pkt = Ether(src=local_mac, dst=remote_mac)/IPv6(src=target_ip, dst=remote_ip)
neigh_adv_pkt /= ICMPv6ND_NA(tgt=target_ip, R=0, S=1, O=1)
neigh_adv_pkt /= ICMPv6NDOptDstLLAddr(lladdr=local_mac)

return neigh_adv_pkt

def parse_args():
parser = argparse.ArgumentParser(description='ARP autoresponder')
parser.add_argument('--conf', '-c', type=str, dest='conf', default='/tmp/from_t1.json', help='path to json file with configuration')
Expand Down