I am attempting to create a tool using Scapy to discover link-local IPv6 hosts by sending a fake router advertisement to the FF02::1 multicast address.
*SNIP*
router_advertisement = scapy.IPv6(src=ra_src_addr, dst='FF02::1')/scapy.ICMPv6ND_RA(routerlifetime=0, reachabletime=0)/scapy.ICMPv6NDOptSrcLLAddr(lladdr=hw_addr)/scapy.ICMPv6NDOptPrefixInfo(prefixlen=64, validlifetime=0x6, preferredlifetime=0x6, prefix='dead::')
answer, unanswer = scapy.sr(router_advertisement, timeout=10, multi=True)
for reply in answer:
print(reply[1][scapy.Ether].src + ' : ' + reply[1]scapy.IPv6].src)
Everything above the snip is mostly setting up the parameters of the router advertisement (ra_prefix, hw_addr, etc). I've put the full script on pastebin to avoid cluttering the question: http://pastebin.com/4Q3JheXh
The problem with the above is that while Scapy is successfully sending the router advertisement packet, and I'm seeing neighbor solicitation responses, Scapy is exiting in error before I can view answers with sr().
Full output:
WARNING: No route found for IPv6 destination :: (no default route?)
Begin emission:
Finished to send 1 packets.
...Traceback (most recent call last):
File "find_ipv6_local.py", line 40, in <module>
answer, unanswer = scapy.sr(router_advertisement, timeout=10, multi=True)
File "/usr/lib/python2.7/site-packages/scapy/sendrecv.py", line 317, in sr
a,b=sndrcv(s,x,*args,**kargs)
File "/usr/lib/python2.7/site-packages/scapy/sendrecv.py", line 141, in sndrcv
h = r.hashret()
File "/usr/lib/python2.7/site-packages/scapy/layers/inet6.py", line 423, in hashret
return struct.pack("B", nh)+self.payload.hashret()
File "/usr/lib/python2.7/site-packages/scapy/packet.py", line 711, in hashret
return self.payload.hashret()
File "/usr/lib/python2.7/site-packages/scapy/layers/inet6.py", line 1317, in hashret
return struct.pack("HH",self.mladdr)+self.payload.hashret()
struct.error: pack expected 2 items for packing (got 1)
Interestingly, when I set validlifetime and preferredlifetime to 0, Scapy does not crash and burn. However, this is less than helpful, as a lifetime of 0 does not get me any responses.
Have I screwed up somewhere in the script, or is Scapy a little off when it comes to IPv6?
I would argue that scapy
is a little dodgy in this respect. Your trackeback here:
return struct.pack("HH",self.mladdr)+self.payload.hashret()
Should contain the following:
return struct.pack("HH", 0, 0)+""
That struct.pack
has no chance of working, it at least should be:
return struct.pack("HH", *self.mladdr)+self.payload.hashnet()
So that struct.pack
has no chance of receiving the two arguments needed for "HH". That is a bug.
Since you do not care about the payload you can change the scapy code for ICMPv6MLQuery
from:
class ICMPv6MLQuery(_ICMPv6ML): # RFC 2710
name = "MLD - Multicast Listener Query"
type = 130
mrd = 10000
mladdr = "::" # 10s for mrd
overload_fields = {IPv6: { "dst": "ff02::1", "hlim": 1, "nh": 58 }}
def hashret(self):
if self.mladdr != "::":
return struct.pack("HH",self.mladdr)+self.payload.hashret()
else:
return self.payload.hashret()
To
class ICMPv6MLQuery(_ICMPv6ML): # RFC 2710
name = "MLD - Multicast Listener Query"
type = 130
mrd = 10000
mladdr = "::" # 10s for mrd
overload_fields = {IPv6: { "dst": "ff02::1", "hlim": 1, "nh": 58 }}
def hashret(self):
return self.payload.hashret()
i.e. kill the struct.pack
altogether.
On another note: this:
WARNING: No route found for IPv6 destination :: (no default route?)
May prevent you from receiving an answer (the NIC may decide that nothing could have arrived and ignore it). It is wise to configure at least some trivial route, e.g.
$ route -6
Kernel IPv6 routing table
Destination Next Hop Flag Met Ref Use If
::/0 [::] U 256 0 0 eth0