]>
git.ipfire.org Git - oddments/cappie.git/blob - cappie.py
4 import logging
.handlers
10 from ConfigParser
import ConfigParser
11 from threading
import Thread
13 def getAllInterfaces():
14 filters
= ("lo", "any")
16 for dev
in pcapy
.findalldevs():
17 if not dev
in filters
:
22 return int("".join(["%02d" % ord(c
) for c
in val
]), 16)
25 return ".".join(["%d" % ord(i
) for i
in val
])
28 return ":".join(["%02x" % ord(i
) for i
in val
])
30 def decode_packet(data
):
31 for func
in (decode_arp_packet
,):
34 except PacketTypeError
:
39 raise PacketTypeError
, "Could not determine type of packet"
41 def decode_arp_packet(data
):
47 if not len(data
) == 42:
48 raise DecodeError
, "Data has wrong length"
52 "protocol" : val2int(struct
.unpack("!2s", data
[12:14])[0]),
53 "hw_addr_size" : val2int(struct
.unpack("!1s", data
[18:19])[0]),
54 "hw_prot_size" : val2int(struct
.unpack("!1s", data
[19:20])[0]),
55 "operation" : val2int(struct
.unpack("!2s", data
[20:22])[0]),
59 if not ret
["protocol"] == 0x0806:
60 raise PacketTypeError
, "Not an ARP packet"
62 # TODO Must check hwtype here...
65 ret
["operation"] = operationmap
[ret
["operation"]]
67 raise DecodeError
, "Unknown operation type"
69 address_length
= ret
["hw_addr_size"] + ret
["hw_prot_size"]
70 unpack_str
= "!%ss%ss" % (ret
["hw_addr_size"], ret
["hw_prot_size"])
72 ret
["source_address"], ret
["source_ip_address"] = \
73 struct
.unpack(unpack_str
, data
[22:22 + address_length
])
75 ret
["destination_address"], ret
["destination_ip_address"] = \
76 struct
.unpack(unpack_str
, data
[22 + address_length
:22 + address_length
* 2])
78 for i
in ("source_address", "destination_address"):
79 ret
[i
] = val2mac(ret
[i
])
81 for i
in ("source_ip_address", "destination_ip_address"):
82 ret
[i
] = val2ip4(ret
[i
])
86 def decode_ndp_packet(data
):
89 class PacketTypeError(Exception):
92 class DecodeError(Exception):
96 class InterfaceError(Exception):
100 class Database(object):
101 def __init__(self
, interface
):
102 self
.interface
= interface
103 self
.dev
= self
.interface
.dev
104 self
.log
= self
.interface
.log
109 self
.log
.debug("Opened database for %s" % self
.dev
)
112 self
.log
.debug("Closing database for %s" % self
.dev
)
117 return self
.__data
[mac
]
120 return self
.__data
.has_key(mac
)
122 def put(self
, mac
, key
, val
):
123 if not self
.has(mac
):
124 self
.__data
[mac
] = {}
126 # TODO Check key for sanity
128 self
.__data
[mac
][key
] = val
131 class Interface(Thread
):
134 def __init__(self
, dev
, log
, promisc
=False, mtu
=1500):
135 Thread
.__init
__(self
)
139 self
.promisc
= promisc
142 self
.db
= Database(self
)
144 self
.log
.debug("Created new interface %s" % self
.dev
)
146 self
.__running
= True
148 def _callback(self
, header
, data
):
149 self
.log
.debug("Received packet on %s" % self
.dev
)
151 p
= decode_packet(data
)
152 except PacketTypeError
, e
:
153 self
.log
.error("Got unknown packet: %s" % e
)
155 except DecodeError
, e
:
156 self
.log
.warning("Got decoding error: %s" % e
)
159 # Dump packet information
160 for key
, val
in p
.items():
161 self
.log
.debug(" %s: %s" % (key
, val
))
163 if not self
.db
.has(p
["source_address"]):
164 self
.db
.put(p
["source_address"], "SOURCE_IP_ADDRESS", p
["source_ip_address"])
167 self
.log
.info("Starting interface %s" % self
.dev
)
171 p
= pcapy
.open_live(self
.dev
, self
.mtu
, self
.promisc
, 0)
172 p
.setfilter(self
.filter)
173 #p.loop(0, self._callback)
177 if not self
.__running
:
181 if p
.dispatch(1, self
._callback
):
184 time
.sleep(self
.heartbeat
)
187 if not self
.__running
:
190 self
.log
.debug("Sending shutdown signal to %s" % self
.dev
)
191 self
.__running
= False
198 class Cappie(object):
200 self
.__interfaces
= []
202 self
.log
= logging
.getLogger("cappie")
203 self
.log
.setLevel(logging
.INFO
)
206 handler
= logging
.StreamHandler()
207 handler
.setFormatter(logging
.Formatter("%(levelname)7s %(message)s"))
208 self
.log
.addHandler(handler
)
211 handler
= logging
.handlers
.SysLogHandler("/dev/log")
212 handler
.setFormatter(logging
.Formatter("cappie: %(message)s"))
213 self
.log
.addHandler(handler
)
215 self
.log
.info("Cappie successfully started")
219 self
.log
.info("Exiting")
221 def setDebug(self
, debug
):
223 self
.log
.setLevel(logging
.DEBUG
)
225 self
.log
.setLevel(logging
.INFO
)
227 def addInterface(self
, dev
, **kwargs
):
228 if not dev
in getAllInterfaces():
229 raise InterfaceError
, "No such interface %s" % dev
231 iface
= Interface(dev
, log
=self
.log
, **kwargs
)
232 self
.__interfaces
.append(iface
)
235 if not self
.__interfaces
:
236 raise RuntimeError, "No interfaces were configured"
238 # Start a thread for each interface
239 for iface
in self
.__interfaces
:
243 for iface
in self
.__interfaces
:
244 if not iface
.is_alive():
245 self
.log
.critical("Thread died unexpectedly. %s" % iface
.dev
)
249 def readConfig(self
, configfile
):
252 config
= ConfigParser()
253 config
.read([configfile
])
256 if config
.has_section("global"):
257 for option
, value
in config
.items("global"):
258 global_opts
[option
] = value
260 config
.remove_section("global")
262 for iface
in config
.sections():
264 for option
, value
in config
.items(iface
):
265 options
[option
] = value
266 self
.addInterface(iface
, **options
)
270 self
.__interfaces
= []
273 for iface
in self
.__interfaces
:
277 if __name__
== "__main__":
278 from optparse
import OptionParser
280 op
.add_option("-c", "--config", dest
="config",
281 help="read configuration from file", metavar
="FILE",
282 default
="/etc/cappie/cappie.conf")
283 op
.add_option("-d", action
="store_true", dest
="debug", default
=False)
285 (options
, args
) = op
.parse_args()
289 cappie
.readConfig(options
.config
)
290 cappie
.setDebug(options
.debug
)
294 except KeyboardInterrupt:
296 except RuntimeError, e
:
297 print >>sys
.stderr
, e