2 # -*- coding: utf-8 -*-
4 # routeradvert-scan.py: Scan for rogue IPv6 router advertisements.
6 # Copyright © 2010 Anders Kaseorg <andersk@mit.edu>
8 # Permission is hereby granted, free of charge, to any person
9 # obtaining a copy of this software and associated documentation files
10 # (the “Software”), to deal in the Software without restriction,
11 # including without limitation the rights to use, copy, modify, merge,
12 # publish, distribute, sublicense, and/or sell copies of the Software,
13 # and to permit persons to whom the Software is furnished to do so,
14 # subject to the following conditions:
16 # The above copyright notice and this permission notice shall be
17 # included in all copies or substantial portions of the Software.
19 # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
20 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
46 zsig = '%s on %s' % (sys.argv[0], socket.gethostname())
47 cert_file = os.path.join(os.path.dirname(__file__), 'sipbcert.pem')
50 mac_re = re.compile(r'^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$')
55 os.spawnlp(os.P_WAIT, 'zwrite', 'zwrite', '-q', '-d', '-c', zclass, '-i', zinstance, '-s', zsig, '-O', 'auto', '-m', m)
57 br = mechanize.Browser()
58 br.add_client_certificate("https://nic.mit.edu", cert_file, cert_file)
61 for lookup in ('host', 'less'):
63 data = urllib.urlencode({
64 'action': 'Lookup Host',
68 br.open('https://nic.mit.edu/bin/dynareg',
70 soup = BeautifulSoup.BeautifulSoup(br.response().read())
71 for tag in soup.findAll('td'):
72 if tag.string == 'owner:':
73 return tag.nextSibling.string or \
74 tag.nextSibling.contents[0]['value']
79 def show_mac(mac, (t, owner)):
80 return '%s (%s)' % (mac, 'unknown owner' if owner is None else owner)
83 return ', '.join(show_mac(mac, v) for mac, v in seen_macs.iteritems())
88 for (mac, (t, owner)) in seen_macs.items():
91 msg('Gone 6to4 router: %s\nCurrent 6to4 routers: %s' %
92 (show_mac(mac, (t, owner)), show_macs()))
93 elif next is None or next > t:
98 signal.alarm(int(math.ceil(next - now)))
100 signal.signal(signal.SIGALRM, lambda signum, frame: check_gone())
102 p = subprocess.Popen(['tcpdump', '-elnp', '-i', interface, 'icmp6 and (ip6[40] = 134)'], stdout=subprocess.PIPE)
107 line = p.stdout.readline()
108 except IOError, (e, s):
116 if len(words) >= 2 and mac_re.match(words[1]):
118 t = time.time() + timeout
120 seen_macs[mac][0] = t
122 seen_macs[mac] = [t, get_owner(mac)]
123 msg('New 6to4 router: %s\n%s\nCurrent 6to4 routers: %s' %
124 (show_mac(mac, seen_macs[mac]),
128 print >>sys.stderr, 'Unrecognized line: ', line.rstrip('\n')