]> git.ipfire.org Git - oddments/cappie.git/blob - cappie/__init__.py
1cf1e8d1a7ab971f4485efa2e5e3356f352708bf
[oddments/cappie.git] / cappie / __init__.py
1 #!/usr/bin/python
2 ###############################################################################
3 # #
4 # Cappie #
5 # Copyright (C) 2010 Michael Tremer #
6 # #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
11 # #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
16 # #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 # #
20 ###############################################################################
21
22 import logging
23 import logging.handlers
24 import pcapy
25 import time
26
27 from ConfigParser import ConfigParser
28 from threading import Thread
29
30 import protocol
31 import queue
32
33 from errors import *
34
35 def getAllInterfaces():
36 filters = ("lo", "any")
37 ret = []
38 for dev in pcapy.findalldevs():
39 if not dev in filters:
40 ret.append(dev)
41 return ret
42
43 class Cappie(object):
44 def __init__(self):
45 self.__interfaces = []
46
47 self.log = logging.getLogger("cappie")
48 self.log.setLevel(logging.INFO)
49
50 # Log to console
51 handler = logging.StreamHandler()
52 handler.setFormatter(logging.Formatter("%(levelname)7s %(message)s"))
53 self.log.addHandler(handler)
54
55 # Setup syslog
56 handler = logging.handlers.SysLogHandler("/dev/log")
57 handler.setFormatter(logging.Formatter("cappie: %(message)s"))
58 self.log.addHandler(handler)
59
60 self.queue = queue.Queue(self.log)
61
62 self.log.info("Cappie successfully started")
63
64 def __del__(self):
65 self.shutdown()
66 self.log.info("Exiting")
67
68 def setDebug(self, debug):
69 if debug:
70 self.log.setLevel(logging.DEBUG)
71 else:
72 self.log.setLevel(logging.INFO)
73
74 def addInterface(self, dev, **kwargs):
75 if not dev in getAllInterfaces():
76 raise InterfaceError, "No such interface %s" % dev
77
78 kwargs["cappie"] = self
79
80 iface = Interface(dev, **kwargs)
81 self.__interfaces.append(iface)
82
83 def run(self):
84 if not self.__interfaces:
85 raise RuntimeError, "No interfaces were configured"
86
87 # Start queue
88 self.queue.start()
89
90 # Start a thread for each interface
91 for iface in self.__interfaces:
92 iface.start()
93
94 while True:
95 if not self.queue.isAlive():
96 self.log.critical("Queue thread died unexpectedly.")
97 return
98
99 for iface in self.__interfaces:
100 if not iface.isAlive():
101 self.log.critical("Thread died unexpectedly. %s" % iface.dev)
102 return
103 time.sleep(60)
104
105 def readConfig(self, configfile):
106 config = ConfigParser()
107 config.read([configfile])
108
109 global_opts = {}
110 if config.has_section("global"):
111 for option, value in config.items("global"):
112 global_opts[option] = value
113
114 config.remove_section("global")
115
116 for iface in config.sections():
117 options = {}
118 for option, value in config.items(iface):
119 options[option] = value
120 self.addInterface(iface, **options)
121
122 def shutdown(self):
123 for iface in self.__interfaces:
124 iface.shutdown()
125
126 self.queue.shutdown()
127
128
129 class Interface(Thread):
130 heartbeat = 0.1
131
132 def __init__(self, dev, cappie, promisc=False, mtu=1500):
133 Thread.__init__(self)
134
135 self.cappie = cappie
136 self.dev = dev
137 self.log = self.cappie.log
138 self.mtu = mtu
139 self.promisc = promisc
140 self.queue = self.cappie.queue
141
142 self.db = Database(self)
143
144 self.log.debug("Created new interface %s" % self.dev)
145
146 self.__running = True
147
148 def _callback(self, header, data):
149 self.log.debug("Received packet on %s" % self.dev)
150 try:
151 p = protocol.decode_packet(data)
152 except PacketTypeError, e:
153 self.log.error("Got unknown packet: %s" % e)
154 return
155 except DecodeError, e:
156 self.log.warning("Got decoding error: %s" % e)
157 return
158
159 # Dump packet information
160 for key, val in p.items():
161 self.log.debug(" %s: %s" % (key, val))
162
163 if not self.db.has(p["source_address"]):
164 self.db.put(p["source_address"], "SOURCE_IP_ADDRESS", p["source_ip_address"])
165
166 def run(self):
167 self.log.info("Starting interface %s" % self.dev)
168
169 self.db.open()
170
171 p = pcapy.open_live(self.dev, self.mtu, self.promisc, 0)
172 p.setfilter(self.filter)
173 #p.loop(0, self._callback)
174
175 p.setnonblock(1)
176 while True:
177 if not self.__running:
178 self.db.close()
179 return
180
181 if p.dispatch(1, self._callback):
182 continue
183
184 time.sleep(self.heartbeat)
185
186 def shutdown(self):
187 if not self.__running:
188 return
189
190 self.log.debug("Sending shutdown signal to %s" % self.dev)
191 self.__running = False
192
193 @property
194 def filter(self):
195 return "arp or rarp"
196
197
198 class Database(object):
199 def __init__(self, interface):
200 self.interface = interface
201 self.dev = self.interface.dev
202 self.log = self.interface.log
203
204 self.__data = {}
205
206 def open(self):
207 self.log.debug("Opened database for %s" % self.dev)
208
209 def close(self):
210 self.log.debug("Closing database for %s" % self.dev)
211 print self.__data
212
213 def get(self, mac):
214 if self.has(mac):
215 return self.__data[mac]
216
217 def has(self, mac):
218 return self.__data.has_key(mac)
219
220 def put(self, mac, key, val):
221 if not self.has(mac):
222 self.__data[mac] = {}
223
224 # TODO Check key for sanity
225
226 self.__data[mac][key] = val