]> git.ipfire.org Git - oddments/cappie.git/blame - cappie/__init__.py
Merge branch 'master' of ssh://git.ipfire.org/pub/git/oddments/cappie
[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 *
54e92b60 35from events import *
53478050
MT
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:
74e192ba 97 if not self.queue.isAlive():
53478050
MT
98 self.log.critical("Queue thread died unexpectedly.")
99 return
100
101 for iface in self.__interfaces:
74e192ba 102 if not iface.isAlive():
53478050
MT
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
497e1b32
MT
130 @property
131 def db(self):
132 return self.queue.db
133
53478050
MT
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
53478050
MT
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
54e92b60 167 self._handlePacket(p)
53478050
MT
168
169 def run(self):
170 self.log.info("Starting interface %s" % self.dev)
171
64e40ed0
MT
172 util.setprocname("interface %s" % self.dev)
173
53478050
MT
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:
53478050
MT
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
ded2591d
MT
199 def addEvent(self, event):
200 return self.cappie.queue.add(event)
53478050 201
54e92b60
MT
202 def _handlePacket(self, packet):
203 if packet.operation == OPERATION_RESPONSE:
204 self.addEvent(EventResponseTrigger(self, packet))
205 #self.addEvent(EventCheckDuplicate(self, packet))
53478050 206
54e92b60
MT
207 elif packet.operation == OPERATION_REQUEST:
208 self.addEvent(EventRequestTrigger(self, packet))