]> andersk Git - routeradvert-scan.git/blame - routeradvert-scan.py
Look up owners of offending MAC addresses.
[routeradvert-scan.git] / routeradvert-scan.py
CommitLineData
cde4019a
AK
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# routeradvert-scan.py: Scan for rogue IPv6 router advertisements.
5#
6# Copyright © 2010 Anders Kaseorg <andersk@mit.edu>
7#
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:
15#
16# The above copyright notice and this permission notice shall be
17# included in all copies or substantial portions of the Software.
18#
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
26# SOFTWARE.
27
cb363a3e 28import BeautifulSoup
cde4019a
AK
29import errno
30import math
cb363a3e 31import mechanize
cde4019a
AK
32import os
33import re
34import signal
35import socket
36import subprocess
37import sys
38import time
cb363a3e 39import urllib
cde4019a
AK
40
41# configuration
42interface = 'eth1'
43timeout = 30
44zclass = 'andersk-auto'
45zinstance = '6to4'
46zsig = '%s on %s' % (sys.argv[0], socket.gethostname())
cb363a3e 47cert_file = '/home/anders/Private/andersk.pem'
cde4019a
AK
48# end configuration
49
50mac_re = re.compile(r'^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$')
51
52seen_macs = {}
53
54def msg(m):
55 os.spawnlp(os.P_WAIT, 'zwrite', 'zwrite', '-q', '-d', '-c', zclass, '-i', zinstance, '-s', zsig, '-m', m)
56
cb363a3e
AK
57br = mechanize.Browser()
58br.add_client_certificate("https://nic.mit.edu", cert_file, cert_file)
59
60def get_owner(mac):
61 for lookup in ('host', 'less'):
62 try:
63 data = urllib.urlencode({
64 'action': 'Lookup Host',
65 'lookup': lookup,
66 'mac': mac
67 })
68 br.open('https://nic.mit.edu/bin/dynareg',
69 data)
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']
75 except:
76 pass
77 return None
78
79def show_mac(mac, (t, owner)):
80 return '%s (%s)' % (mac, 'unknown owner' if owner is None else owner)
81
82def show_macs():
83 return ', '.join(show_mac(mac, v) for mac, v in seen_macs.iteritems())
84
cde4019a
AK
85def check_gone():
86 now = time.time()
87 next = None
cb363a3e 88 for (mac, (t, owner)) in seen_macs.items():
cde4019a
AK
89 if t < now:
90 del seen_macs[mac]
cb363a3e
AK
91 msg('Gone 6to4 router: %s\nCurrent 6to4 routers: %s' %
92 (show_mac(mac, (t, owner)), show_macs()))
cde4019a
AK
93 elif next is None or next > t:
94 next = t
95 if next is None:
96 signal.alarm(0)
97 else:
98 signal.alarm(int(math.ceil(next - now)))
99
2898db6e
AK
100msg('Starting %s' % sys.argv[0])
101
cde4019a
AK
102signal.signal(signal.SIGALRM, lambda signum, frame: check_gone())
103
104p = subprocess.Popen(['tcpdump', '-elnp', '-i', interface, 'icmp6 and (ip6[40] = 134)'], stdout=subprocess.PIPE)
105while True:
106 check_gone()
107 while True:
108 try:
109 line = p.stdout.readline()
110 except IOError, (e, s):
111 if e == errno.EINTR:
112 continue
113 break
114 signal.alarm(0)
115 if line == '':
116 break
117 words = line.split()
118 if len(words) >= 2 and mac_re.match(words[1]):
119 mac = words[1]
120 t = time.time() + timeout
121 if mac in seen_macs:
cb363a3e 122 seen_macs[mac][0] = t
cde4019a 123 else:
cb363a3e
AK
124 seen_macs[mac] = [t, get_owner(mac)]
125 msg('New 6to4 router: %s\n%s\nCurrent 6to4 routers: %s' %
126 (show_mac(mac, seen_macs[mac]),
127 line.rstrip('\n'),
128 show_macs()))
cde4019a
AK
129 else:
130 print >>sys.stderr, 'Unrecognized line: ', line.rstrip('\n')
This page took 1.039394 seconds and 5 git commands to generate.