]> git.ipfire.org Git - oddments/cappie.git/blame - cappie/__init__.py
Use old API of threading code to be compatible with python 2.5.
[oddments/cappie.git] / cappie / __init__.py
CommitLineData
53478050
MT
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
22import logging
23import logging.handlers
24import pcapy
25import time
26
27from ConfigParser import ConfigParser
28from threading import Thread
29
30import protocol
31import queue
32
33from errors import *
34
35def 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
43class 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:
74e192ba 95 if not self.queue.isAlive():
53478050
MT
96 self.log.critical("Queue thread died unexpectedly.")
97 return
98
99 for iface in self.__interfaces:
74e192ba 100 if not iface.isAlive():
53478050
MT
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
129class 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
198class 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