Package killerbee :: Module pcapdump
[hide private]
[frames] | no frames]

Source Code for Module killerbee.pcapdump

  1  import struct 
  2  import time 
  3   
  4  PCAPH_MAGIC_NUM = 0xa1b2c3d4 
  5  PCAPH_VER_MAJOR = 2 
  6  PCAPH_VER_MINOR = 4 
  7  PCAPH_THISZONE  = 0 
  8  PCAPH_SIGFIGS   = 0 
  9  PCAPH_SNAPLEN   = 65535 
 10   
 11  DOT11COMMON_TAG = 000002 
 12  GPS_TAG         = 30002 
 13   
14 -class PcapReader:
15
16 - def __init__(self, savefile):
17 ''' 18 Opens the specified file, validates a libpcap header is present. 19 @type savefile: String 20 @param savefile: Input libpcap filename to open 21 @rtype: None 22 ''' 23 PCAPH_LEN = 24 24 self.__fh = open(savefile, mode='rb') 25 self._pcaphsnaplen = 0 26 header = self.__fh.read(PCAPH_LEN) 27 28 # Read the first 4 bytes for the magic number, determine endianness 29 magicnum = struct.unpack("I", header[0:4])[0] 30 if magicnum != 0xd4c3b2a1: 31 # Little endian 32 self.__endflag = "<" 33 elif magicnum == 0xa1b2c3d4: 34 # Big endign 35 self.__endflag = ">" 36 else: 37 raise Exception('Specified file is not a libpcap capture') 38 39 pcaph = struct.unpack("%sIHHIIII"%self.__endflag, header) 40 if pcaph[1] != PCAPH_VER_MAJOR and pcaph[2] != PCAPH_VER_MINOR \ 41 and pcaph[3] != PCAPH_THISZONE and pcaph[4] != PCAPH_SIGFIGS \ 42 and pcaph[5] != PCAPH_SNAPLEN: 43 raise Exception('Unsupported pcap header format or version') 44 45 self._pcaphsnaplen = pcaph[5] 46 self._datalink = pcaph[6]
47 54
55 - def close(self):
56 ''' 57 Closes the output packet capture; wrapper for pcap_close(). 58 @rtype: None 59 ''' 60 self.pcap_close()
61
62 - def pcap_close(self):
63 ''' 64 Closes the output packet capture. 65 @rtype: None 66 ''' 67 self.__fh.close()
68
69 - def pnext(self):
70 ''' 71 Wrapper for pcap_next to mimic method for Daintree SNA. See pcap_next() 72 ''' 73 return self.pcap_next()
74
75 - def pcap_next(self):
76 ''' 77 Retrieves the next packet from the capture file. Returns a list of 78 [Hdr, packet] where Hdr is a list of [timestamp, snaplen, plen] and 79 packet is a string of the payload content. Returns None at the end 80 of the packet capture. 81 @rtype: List 82 ''' 83 # Read the next header block 84 PCAPH_RECLEN = 16 85 rechdrdata = self.__fh.read(PCAPH_RECLEN) 86 87 try: 88 rechdrtmp = struct.unpack("%sIIII"%self.__endflag, rechdrdata) 89 except struct.error: 90 return [None,None] 91 92 rechdr = [ 93 float("%s.%s"%(rechdrtmp[0],rechdrtmp[1])), 94 rechdrtmp[2], 95 rechdrtmp[3] 96 ] 97 if rechdr[1] > rechdr[2] or rechdr[1] > self._pcaphsnaplen or rechdr[2] > self._pcaphsnaplen: 98 raise Exception('Corrupted or invalid libpcap record header (included length exceeds actual length)') 99 100 # Read the included packet length 101 frame = self.__fh.read(rechdr[1]) 102 return [rechdr, frame]
103 104
105 -class PcapDumper:
106 - def __init__(self, datalink, savefile, ppi = False):
107 ''' 108 Creates a libpcap file using the specified datalink type. 109 @type datalink: Integer 110 @param datalink: Datalink type, one of DLT_* defined in pcap-bpf.h 111 @type savefile: String 112 @param savefile: Output libpcap filename to open 113 @rtype: None 114 ''' 115 if ppi: from killerbee.pcapdlt import DLT_PPI 116 self.ppi = ppi 117 self.__fh = open(savefile, mode='wb') 118 self.datalink = datalink 119 self.__fh.write(''.join([ 120 struct.pack("I", PCAPH_MAGIC_NUM), 121 struct.pack("H", PCAPH_VER_MAJOR), 122 struct.pack("H", PCAPH_VER_MINOR), 123 struct.pack("I", PCAPH_THISZONE), 124 struct.pack("I", PCAPH_SIGFIGS), 125 struct.pack("I", PCAPH_SNAPLEN), 126 struct.pack("I", DLT_PPI if self.ppi else self.datalink) 127 ]))
128
129 - def pcap_dump(self, packet, ts_sec=None, ts_usec=None, orig_len=None, 130 freq_mhz = None, ant_dbm = None, location = None):
131 ''' 132 Appends a new packet to the libpcap file. Optionally specify ts_sec 133 and tv_usec for timestamp information, otherwise the current time is 134 used. Specify orig_len if your snaplen is smaller than the entire 135 packet contents. 136 @type ts_sec: Integer 137 @param ts_sec: Timestamp, number of seconds since Unix epoch. Default 138 is the current timestamp. 139 @type ts_usec: Integer 140 @param ts_usec: Timestamp microseconds. Defaults to current timestamp. 141 @type orig_len: Integer 142 @param orig_len: Length of the original packet, used if the packet you 143 are writing is smaller than the original packet. Defaults to the 144 specified packet's length. 145 @type location: Tuple 146 @param location: 3-tuple of (longitude, latitude, altitude). 147 @type packet: String 148 @param packet: Packet contents 149 @rtype: None 150 ''' 151 152 # Build CACE PPI headers if requested 153 if self.ppi is True: 154 pph_len = 8 #ppi_header 155 156 #CACE PPI Field 802.11-Common 157 pph_len += 24 #802.11-common header and data 158 rf_freq_mhz = 0x0000 159 if freq_mhz is not None: rf_freq_mhz = freq_mhz 160 rf_ant_dbm = 0 161 if ant_dbm is not None: rf_ant_dbm = ant_dbm 162 caceppi_f80211common = ''.join([ 163 struct.pack("<H", DOT11COMMON_TAG), #2 = Field Type 802.11-Common 164 struct.pack("<H", 20), #20 = 802.11-Common length in bytes 165 struct.pack("<Q", 0), #FSF-Timer 166 struct.pack("<H", 0), #Flags 167 struct.pack("<H", 0), #Rate 168 struct.pack("<H", rf_freq_mhz), #Channel-Freq 169 struct.pack("<H", 0x0080), #Channel-Flags = 2GHz 170 struct.pack("<B", 0), #FHSS-Hopset 171 struct.pack("<B", 0), #FHSS-Pattern 172 struct.pack("<b", rf_ant_dbm), #dBm-Ansignal 173 struct.pack("<b", 0) #dBm-Antnoise 174 ]) 175 176 #PPI-GEOLOCATION if information available 177 if location is not None: 178 pph_len += 20 #geolocation header and data length 179 (lon, lat, alt) = location 180 # Sanity checking on values of location data: 181 if lat > -180.00000005 and lat < 180.00000005: 182 lat_i = int(round((lat + 180.0) * 1e7)) 183 else: 184 raise Exception("Latitude value is out of expected range: %.8f" % lat) 185 if lon > -180.00000005 and lon < 180.00000005: 186 lon_i = int(round((lon + 180.0) * 1e7)) 187 else: 188 raise Exception("Longitude value is out of expected range: %.8f" % lon) 189 if alt > -180000.00005 and alt < 180000.00005: 190 alt_i = int(round((alt + 180000.0) * 1e4)) 191 else: 192 raise Exception("Altitude value is out of expected range: %.8f" % lon) 193 # Build Geolocation PPI Header 194 caceppi_fgeolocation = ''.join([ 195 struct.pack("<H", GPS_TAG), #2 = Field Type 802.11-Common 196 struct.pack("<H", 20), #20 = 802.11-Common length in bytes 197 struct.pack("<B", 1), #Geotag Version 198 struct.pack("<B", 2), #Geotag Pad 199 struct.pack("<H", 24), #Geotag Length 200 struct.pack("<I", 0x0E), #GPS fields (only lat, long, and alt for now) 201 struct.pack("<I", lat_i), #GPS Latitude 202 struct.pack("<I", lon_i), #GPS Longitude 203 struct.pack("<I", alt_i), #GPS Altitude 204 ]) 205 206 #CACE PPI Header 207 caceppi_hdr = ''.join([ 208 struct.pack("<B", 0), #PPH version 209 struct.pack("<B", 0x00), #PPH flags 210 struct.pack("<H", pph_len), #PPH len 211 struct.pack("<I", self.datalink) #Field 212 ]) 213 214 if ts_sec == None or ts_usec == None: 215 # There must be a better way here that I don't know -JW 216 s_sec, s_usec = str(time.time()).split(".") 217 ts_sec = int(s_sec) 218 ts_usec = int(s_usec) 219 220 plen = len(packet) 221 if orig_len == None: 222 orig_len = plen 223 224 #Encapsulated packet header and packet 225 output_list = [ struct.pack("I", ts_sec), 226 struct.pack("I", ts_usec), 227 struct.pack("I", orig_len), 228 struct.pack("I", plen) ] 229 230 if self.ppi is True: 231 output_list[2] = struct.pack("I", orig_len + pph_len) 232 output_list[3] = struct.pack("I", plen + pph_len) 233 output_list.append(caceppi_hdr) 234 if location is not None: 235 output_list.append(caceppi_fgeolocation) 236 output_list.append(caceppi_f80211common) 237 238 output_list.append(packet) 239 output = ''.join(output_list) 240 241 #DEBUG Output: 242 #print "Pcap:", '\\x'+'\\x'.join(["%02x" % ord(x) for x in output]) 243 #print "PPI:", '\\x'+'\\x'.join(["%02x" % ord(x) for x in (caceppi_hdr + caceppi_f80211common)]) 244 #print "802154:", packet.encode("hex") 245 246 self.__fh.write(output) 247 # Specially for handling FIFO needs: 248 try: 249 self.__fh.flush() 250 except IOError, e: 251 raise e 252 253 return
254 255
256 - def close(self):
257 ''' 258 Closes the output packet capture; wrapper for pcap_close(). 259 @rtype: None 260 ''' 261 self.pcap_close()
262
263 - def pcap_close(self):
264 ''' 265 Closed the output packet capture. 266 @rtype: None 267 ''' 268 self.__fh.close()
269