]>
Commit | Line | Data |
---|---|---|
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 | ||
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') |