]> git.ipfire.org Git - oddments/cappie.git/blame - cappie/__init__.py
Set names of threads for better debugging.
[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
64e40ed0 32import util
53478050
MT
33
34from errors import *
35
36def getAllInterfaces():
37 filters = ("lo", "any")
38 ret = []
39 for dev in pcapy.findalldevs():
40 if not dev in filters:
41 ret.append(dev)
42 return ret
43
44class Cappie(object):
45 def __init__(self):
46 self.__interfaces = []
47
48 self.log = logging.getLogger("cappie")
49 self.log.setLevel(logging.INFO)
50
51 # Log to console
52 handler = logging.StreamHandler()
53 handler.setFormatter(logging.Formatter("%(levelname)7s %(message)s"))
54 self.log.addHandler(handler)
55
56 # Setup syslog
57 handler = logging.handlers.SysLogHandler("/dev/log")
58 handler.setFormatter(logging.Formatter("cappie: %(message)s"))
59 self.log.addHandler(handler)
60
61 self.queue = queue.Queue(self.log)
62
63 self.log.info("Cappie successfully started")
64
65 def __del__(self):
66 self.shutdown()
67 self.log.info("Exiting")
68
69 def setDebug(self, debug):
70 if debug:
71 self.log.setLevel(logging.DEBUG)
72 else:
73 self.log.setLevel(logging.INFO)
74
75 def addInterface(self, dev, **kwargs):
76 if not dev in getAllInterfaces():
77 raise InterfaceError, "No such interface %s" % dev
78
79 kwargs["cappie"] = self
80
81 iface = Interface(dev, **kwargs)
82 self.__interfaces.append(iface)
83
84 def run(self):
85 if not self.__interfaces:
86 raise RuntimeError, "No interfaces were configured"
87
88 # Start queue
89 self.queue.start()
90
91 # Start a thread for each interface
92 for iface in self.__interfaces:
93 iface.start()
94
95 while True:
96 if not self.queue.is_alive():
97 self.log.critical("Queue thread died unexpectedly.")
98 return
99
100 for iface in self.__interfaces:
101 if not iface.is_alive():
102 self.log.critical("Thread died unexpectedly. %s" % iface.dev)
103 return
104 time.sleep(60)
105
106 def readConfig(self, configfile):
107 config = ConfigParser()
108 config.read([configfile])
109
110 global_opts = {}
111 if config.has_section("global"):
112 for option, value in config.items("global"):
113 global_opts[option] = value
114
115 config.remove_section("global")
116
117 for iface in config.sections():
118 options = {}
119 for option, value in config.items(iface):
120 options[option] = value
121 self.addInterface(iface, **options)
122
123 def shutdown(self):
124 for iface in self.__interfaces:
125 iface.shutdown()
126
127 self.queue.shutdown()
128
129
130class Interface(Thread):
131 heartbeat = 0.1
132
133 def __init__(self, dev, cappie, promisc=False, mtu=1500):
134 Thread.__init__(self)
135
136 self.cappie = cappie
137 self.dev = dev
138 self.log = self.cappie.log
139 self.mtu = mtu
140 self.promisc = promisc
141 self.queue = self.cappie.queue
142
143 self.db = Database(self)
144
145 self.log.debug("Created new interface %s" % self.dev)
146
147 self.__running = True
148
149 def _callback(self, header, data):
150 self.log.debug("Received packet on %s" % self.dev)
151 try:
152 p = protocol.decode_packet(data)
153 except PacketTypeError, e:
154 self.log.error("Got unknown packet: %s" % e)
155 return
156 except DecodeError, e:
157 self.log.warning("Got decoding error: %s" % e)
158 return
159
160 # Dump packet information
161 for key, val in p.items():
162 self.log.debug(" %s: %s" % (key, val))
163
164 if not self.db.has(p["source_address"]):
165 self.db.put(p["source_address"], "SOURCE_IP_ADDRESS", p["source_ip_address"])
166
167 def run(self):
168 self.log.info("Starting interface %s" % self.dev)
169
64e40ed0
MT
170 util.setprocname("interface %s" % self.dev)
171
53478050
MT
172 self.db.open()
173
174 p = pcapy.open_live(self.dev, self.mtu, self.promisc, 0)
175 p.setfilter(self.filter)
176 #p.loop(0, self._callback)
177
178 p.setnonblock(1)
179 while True:
180 if not self.__running:
181 self.db.close()
182 return
183
184 if p.dispatch(1, self._callback):
185 continue
186
187 time.sleep(self.heartbeat)
188
189 def shutdown(self):
190 if not self.__running:
191 return
192
193 self.log.debug("Sending shutdown signal to %s" % self.dev)
194 self.__running = False
195
196 @property
197 def filter(self):
198 return "arp or rarp"
199
200
201class Database(object):
202 def __init__(self, interface):
203 self.interface = interface
204 self.dev = self.interface.dev
205 self.log = self.interface.log
206
207 self.__data = {}
208
209 def open(self):
210 self.log.debug("Opened database for %s" % self.dev)
211
212 def close(self):
213 self.log.debug("Closing database for %s" % self.dev)
214 print self.__data
215
216 def get(self, mac):
217 if self.has(mac):
218 return self.__data[mac]
219
220 def has(self, mac):
221 return self.__data.has_key(mac)
222
223 def put(self, mac, key, val):
224 if not self.has(mac):
225 self.__data[mac] = {}
226
227 # TODO Check key for sanity
228
229 self.__data[mac][key] = val