]> andersk Git - routeradvert-scan.git/blob - routeradvert-scan.py
Initial commit of routeradvert-scan.
[routeradvert-scan.git] / routeradvert-scan.py
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
28 import errno
29 import math
30 import os
31 import re
32 import signal
33 import socket
34 import subprocess
35 import sys
36 import time
37
38 # configuration
39 interface = 'eth1'
40 timeout = 30
41 zclass = 'andersk-auto'
42 zinstance = '6to4'
43 zsig = '%s on %s' % (sys.argv[0], socket.gethostname())
44 # end configuration
45
46 mac_re = re.compile(r'^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$')
47
48 seen_macs = {}
49
50 def msg(m):
51     os.spawnlp(os.P_WAIT, 'zwrite', 'zwrite', '-q', '-d', '-c', zclass, '-i', zinstance, '-s', zsig, '-m', m)
52
53 def check_gone():
54     now = time.time()
55     next = None
56     for (mac, t) in seen_macs.items():
57         if t < now:
58             del seen_macs[mac]
59             msg('Gone 6to4 router: %s\nCurrent 6to4 routers: %s' % (mac, seen_macs.keys()))
60         elif next is None or next > t:
61             next = t
62     if next is None:
63         signal.alarm(0)
64     else:
65         signal.alarm(int(math.ceil(next - now)))
66
67 signal.signal(signal.SIGALRM, lambda signum, frame: check_gone())
68
69 p = subprocess.Popen(['tcpdump', '-elnp', '-i', interface, 'icmp6 and (ip6[40] = 134)'], stdout=subprocess.PIPE)
70 while True:
71     check_gone()
72     while True:
73         try:
74             line = p.stdout.readline()
75         except IOError, (e, s):
76             if e == errno.EINTR:
77                 continue
78         break
79     signal.alarm(0)
80     if line == '':
81         break
82     words = line.split()
83     if len(words) >= 2 and mac_re.match(words[1]):
84         mac = words[1]
85         t = time.time() + timeout
86         if mac in seen_macs:
87             seen_macs[mac] = t
88         else:
89             seen_macs[mac] = t
90             msg('New 6to4 router: %s\n%s\nCurrent 6to4 routers: %s' % (mac, line.rstrip('\n'), seen_macs.keys()))
91     else:
92         print >>sys.stderr, 'Unrecognized line: ', line.rstrip('\n')
This page took 2.770901 seconds and 5 git commands to generate.