The trunk can use the main server again (for the time being).
[switzerland.git] / switzerland / common / PcapLogger.py
blob16a26516c6ff653b209dc586df723be8ae0aa03c
1 #!/usr/bin/env python
2 import time
3 import os
4 import os.path
5 import threading
6 import types
7 import logging
8 import struct
10 log = logging.getLogger()
12 class LoggerError(Exception):
13 pass
15 class PcapWriter:
16 "Write some packets to a pcap file"
17 def __init__(self, path):
19 magic = 0xa1b2c3d4L
20 majv = 2
21 minv = 4
22 timewarp = 0
23 sigfigs = 0 # Get this from the reconciliator? Tricky because there
24 # are significant figures relative to real time and
25 # significant figures relative to other packes. we really
26 # should ask each client's NTP for the clock jitter...
27 snaplen = 1600
28 linktype = 1 # DLT_ETHER
30 hdr = struct.pack("@IHHIIII", magic, majv, minv, timewarp, sigfigs,
31 snaplen, linktype)
32 self.file = open(path, "w")
33 self.file.write(hdr)
35 def write(self, packet, timestamp):
36 sec = int(timestamp)
37 usec = int((timestamp - sec)*1000000)
38 # Two fake MAC addresses followed by 0x0800 for an IPv4 packet
39 ethhdr = "\xaa" * 6 + "\xbb" * 6 + "\x08\x00"
40 length = len(packet) + len(ethhdr)
41 pkthdr = struct.pack("@IIII", sec, usec, length, length)
42 self.file.write(pkthdr)
43 self.file.write(ethhdr)
44 self.file.write(packet)
47 class PcapLogger:
48 """
49 This device organises the elaborate switzerland logging structure, which
50 is a standard log containing references to a directory of incident-specific
51 pcap log files.
52 """
54 # XXXXXX fix this default before release
55 def __init__(self, log_dir):
56 self.lock = threading.RLock()
57 self.make_or_check_directory(log_dir, "log dir")
58 #ts = self.timestamp()
59 self.log_dir = log_dir
60 # XXX do not expect this to be secure if log_dir is world writeable
61 # on a multi-user system
62 self.make_or_check_directory(self.log_dir, "pcap log dir")
64 def make_or_check_directory(self, path, alias="directory"):
65 alias += " "
66 try:
67 os.mkdir(path, 0755)
68 except OSError, e:
69 assert not os.path.islink(path), alias + path + " should not be a symlink!"
70 assert os.path.isdir(path), "cannot create " + alias + path + ":\n"+`e`
72 def new_flow(self, flow, id):
73 log.info("New bidirectional flow %d : %s\n" % (id,`flow`))
75 def timestamp(self, t=None):
76 if t == None:
77 t = time.time()
78 frac = `t - int(t)`[2:5]
79 return time.strftime("%Y%m%d%H%M%S", time.localtime(t)) + frac
81 def log_forged_in(self, context, id):
82 self.lock.acquire()
83 try:
84 in_path, out_path = self.__gen_filenames(context)
85 pcap = PcapWriter(in_path)
86 self.log_to_pcap(context, pcap)
87 pcap.file.close()
88 finally:
89 self.lock.release()
90 return out_path
92 def log_forged_out(self, context, out_path):
93 self.lock.acquire()
94 try:
95 pcap = PcapWriter(out_path)
96 self.log_to_pcap(context, pcap)
97 pcap.file.close()
98 finally:
99 self.lock.release()
101 def __gen_filenames(self, context):
102 "Determine the filenames for the inbound and outbound logs"
104 ts = self.timestamp(context[0][0])
105 filename1 = ts + "-in.pcap"
106 filename2 = ts + "-out.pcap"
107 path1 = self.log_dir + os.path.sep + filename1
108 path2 = self.log_dir + os.path.sep + filename2
109 n = 0
110 while os.path.exists(path1):
111 # This inbound filename has already been used; find another
112 filename1 = ts + "-" + `n` + "-in.pcap"
113 filename2 = ts + "-" + `n` + "-out.pcap"
114 path1 = self.log_dir + os.path.sep + filename1
115 path2 = self.log_dir + os.path.sep + filename2
116 n +=1
117 log.info("Recording inbound modified packets & context in %s\n" % path1)
119 return (path1,path2)
121 def log_to_pcap(self, packets, pcap_log):
122 ## consider adding linktype= to this:
124 for timestamp, hash, packet in packets:
125 # be careful what we feed to scapy!
126 if type(packet) != types.StringType:
127 raise LoggerError, "Packet data %s is not a string!\n" % packet
129 try:
130 pcap_log.write(packet,timestamp)
131 except:
132 log.error("Tripped on %s", `pcap_log`)
133 raise
134 pcap_log.file.flush()
136 if __name__ == "__main__":
137 l = PcapLogger("/var/log/switzerland/")
138 logging.basicConfig()
139 log = logging.getLogger()
140 log.setLevel(logging.DEBUG)
141 l.log_forged_in([(2,"0x0x", "yjfoisjfoidsjfseehayee"), (3,"0y0y", "yejsidofjsoidhooyee")], 1)