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