def test_full_packet_parsing_l3_parse_error2(self): full_eii_packet_data = \ [0x00, 0x04, 0x00, 0x01, 0x00, 0x06, 0x08, 0x00, 0x27, 0xc6, 0x25, 0x01, 0x00, 0x00, 0x08, 0x00, 0x41, 0x10, 0x00, 0x5c, 0x93, 0x06, 0x40, 0x00, 0x40, 0x06, 0x8f, 0x75, 0x0a, 0x00, 0x02, 0x0f, 0x0a, 0x00, 0x02, 0x02, 0x00, 0x16, 0xd1, 0xf4, 0x52, 0x1a, 0x58, 0x7c, 0x58, 0x25, 0x2e, 0x9b, 0x50, 0x18, 0x9f, 0xb0, 0x18, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00] packet = pcap_packet.PCAPPacket(full_eii_packet_data, '13:00') self.assertRaises(exceptions.PacketParsingException, packet.parse, [pcap_packet.PCAPSLL]) pmap = packet.layer_data emap = packet.extra_data self.assertEqual(1, len(emap['parse_errors.ip'])) self.assertRegexpMatches(emap['parse_errors.ip'][0], 'field must be at least') self.assertEqual('13:00', packet.timestamp) self.assertTrue('ethernet' in pmap) self.assertEqual(pcap_packet.PCAPSLL, type(pmap['ethernet'])) ether_pmap = pmap['ethernet'] """ :type: pcap_packet.PCAPEthernet """ self.assertEqual('08:00:27:c6:25:01', ether_pmap.source_mac) self.assertEqual('00:00:00:00:00:00', ether_pmap.dest_mac) self.assertEqual(pcap_packet.PCAPIP4, pmap['ethernet'].next_parse_recommendation)
def test_full_packet_parsing_l3_bad_length(self): full_eii_packet_data = \ [0x00, 0x04, 0x00, 0x01, 0x00, 0x06, 0x08, 0x00, 0x27, 0xc6, 0x25, 0x01, 0x00, 0x00, 0x08, 0x00, 0x45, 0x10, 0x00, 0x5c, 0x93, 0x06] packet = pcap_packet.PCAPPacket(full_eii_packet_data, '13:00') self.assertRaises(exceptions.PacketParsingException, packet.parse, [pcap_packet.PCAPSLL]) pmap = packet.layer_data """ :type: dict[str, PCAPPacket.PCAPEncapsulatedLayer] """ emap = packet.extra_data self.assertEqual(1, len(emap['parse_errors.ip'])) self.assertRegexpMatches(emap['parse_errors.ip'][0], 'must at least be') self.assertEqual('13:00', packet.timestamp) self.assertTrue('ethernet' in pmap) self.assertEqual(pcap_packet.PCAPSLL, type(pmap['ethernet'])) ether_pmap = pmap['ethernet'] """ :type: PCAPPacket.PCAPEthernet """ self.assertEqual('08:00:27:c6:25:01', ether_pmap.source_mac) self.assertEqual('00:00:00:00:00:00', ether_pmap.dest_mac) self.assertEqual(pcap_packet.PCAPIP4, pmap['ethernet'].next_parse_recommendation)
def test_full_packet_parsing_l2_bad_length(self): full_eii_packet_data = \ [0x52, 0x54, 0x00, 0x12, 0x35, 0x02, 0x08, 0x00, 0x27, 0xc6] packet = pcap_packet.PCAPPacket(full_eii_packet_data, '13:00') self.assertRaises(exceptions.PacketParsingException, packet.parse) emap = packet.extra_data self.assertEqual(1, len(emap['parse_errors.ethernet'])) self.assertRegexpMatches(emap['parse_errors.ethernet'][0], 'must at least be') self.assertEqual('13:00', packet.timestamp)
def test_full_packet_parsing_set_partial_stack(self): full_eii_packet_data = \ [0x00, 0x04, 0x00, 0x01, 0x00, 0x06, 0x08, 0x00, 0x27, 0xc6, 0x25, 0x01, 0x00, 0x00, 0x08, 0x00, 0x45, 0x10, 0x00, 0x5c, 0x93, 0x06, 0x40, 0x00, 0x40, 0x06, 0x8f, 0x75, 0x0a, 0x00, 0x02, 0x0f, 0x0a, 0x00, 0x02, 0x02, 0x00, 0x16, 0xd1, 0xf4, 0x52, 0x1a, 0x58, 0x7c, 0x58, 0x25, 0x2e, 0x9b, 0x50, 0x18, 0x9f, 0xb0, 0x18, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xb0, 0x2c, 0x08, 0xab, 0x7f, 0x98, 0x3c, 0x58, 0x54, 0xeb, 0xde, 0x01, 0xd1, 0xc7, 0xbe, 0xf8, 0x85, 0xba, 0xe4, 0xb6, 0xea, 0x06, 0x98, 0xf9, 0xc0, 0xd2, 0xac, 0x17, 0x19, 0x85, 0x54, 0xdb, 0xe1, 0x3b, 0x3e, 0x85, 0x61, 0xee, 0x31, 0xaf, 0x36, 0xa6, 0x04, 0xd3, 0x51, 0x21, 0x09, 0x20] packet = pcap_packet.PCAPPacket(full_eii_packet_data, '13:00') pmap = packet.parse([pcap_packet.PCAPSLL]) self.assertEqual('13:00', packet.timestamp) self.assertTrue('ethernet' in pmap) self.assertTrue('ip' in pmap) self.assertTrue('tcp' in pmap) self.assertEqual(pcap_packet.PCAPSLL, type(pmap['ethernet'])) self.assertEqual(pcap_packet.PCAPIP4, type(pmap['ip'])) self.assertEqual(pcap_packet.PCAPTCP, type(pmap['tcp'])) self.assertEqual('08:00:27:c6:25:01', pmap['ethernet'].source_mac) self.assertEqual('00:00:00:00:00:00', pmap['ethernet'].dest_mac) self.assertEqual(pcap_packet.PCAPIP4, pmap['ethernet'].next_parse_recommendation) self.assertEqual('10.0.2.15', pmap['ip'].source_ip) self.assertEqual('10.0.2.2', pmap['ip'].dest_ip) self.assertEqual(5, pmap['ip'].header_length) self.assertEqual(4, pmap['ip'].version) self.assertEqual(pcap_packet.IP4_PROTOCOL_TCP, pmap['ip'].protocol) self.assertEqual(pcap_packet.PCAPTCP, pmap['ip'].next_parse_recommendation) self.assertEqual(22, pmap['tcp'].source_port) self.assertEqual(53748, pmap['tcp'].dest_port) self.assertEqual(1377458300, pmap['tcp'].seq) self.assertEqual(1478831771, pmap['tcp'].ack) self.assertEqual(5, pmap['tcp'].data_offset) self.assertEqual( True, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_ACK)) self.assertEqual( True, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_PUSH)) self.assertEqual( False, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_CWS)) self.assertEqual( False, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_ECE)) self.assertEqual( False, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_FINAL)) self.assertEqual( False, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_RESET)) self.assertEqual( False, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_NS)) self.assertEqual( False, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_SYN)) self.assertEqual( False, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_URGENT)) self.assertEqual(40880, pmap['tcp'].window_size) self.assertEqual(None, pmap['tcp'].next_parse_recommendation)
def read_packet(cli=LinuxCLI(), flag_set=None, interface='any', count=1, packet_type='', pcap_filter=None, max_size=0, packet_queues=None, callback=None, callback_args=None, save_dump_file=False, save_dump_filename=None): tmp_dump_filename = './.tcpdump.out.' + str(time.time()) tcp_processes = [] try: # If flag set provided, use them instead, for synch with # external functions tcp_ready = threading.Event() \ if flag_set is None else flag_set[0] tcp_error = threading.Event() \ if flag_set is None else flag_set[1] tcp_stop = threading.Event() \ if flag_set is None else flag_set[2] tcp_finished = threading.Event() \ if flag_set is None else flag_set[3] # If queue set provided, use them instead for synch # with external functions packet_queue = Queue.Queue() \ if packet_queues is None else packet_queues[0] status_queue = Queue.Queue() \ if packet_queues is None else packet_queues[1] cmd1 = ['tcpdump', '-n', '-xx', '-l'] cmd1 += ['-c', str(count)] \ if count > 0 else [] cmd1 += ['-i', interface] cmd1 += ['-s', str(max_size)] \ if max_size != 0 else [] cmd1 += ['-T', packet_type] \ if packet_type != '' else [] cmd1 += [pcap_filter.to_str()] \ if pcap_filter is not None else [] cmd2 = ['tee', '-a', tmp_dump_filename] # FLAG STATE: ready[clear], stop[clear], finished[clear] with open(name=tmp_dump_filename, mode='w') as f: f.write("--START--\n") tcp_processes = cli.cmd(cmd_list=[cmd1, cmd2], blocking=False) tcp_piped_process = tcp_processes.process tcp_actual_process = tcp_processes.process_array[0] # set current p.stderr flags to NONBLOCK # Note that as stderr is NOT redirected through pipes, we # must listen on the actual tcpdump process's stderr # (not the tee process!) flags_se = fcntl(tcp_actual_process.stderr, F_GETFL) fcntl(tcp_actual_process.stderr, F_SETFL, flags_se | os.O_NONBLOCK) err_out = '' while not tcp_ready.is_set(): try: line = os.read(tcp_actual_process.stderr.fileno(), 256) if line.find('listening on') != -1: # TODO(micucci): Replace sleep after TCPDump s # starts with a real check # This is dangerous, # and might not actually be enough to signal the # tcpdump is actually running. Instead, let's # create a Cython module that passes calls through # to libpcap (there are 0 good libpcap implementations # for Python that are maintained, documented, # and simple). time.sleep(1) tcp_ready.set() else: err_out += line if tcp_piped_process.poll() is not None: out, err = tcp_piped_process.communicate() status_queue.put( {'error': 'tcpdump exited abnormally', 'returncode': tcp_piped_process.returncode, 'stdout': out, 'stderr': err_out}) tcp_error.set() raise exceptions.SubprocessFailedException( 'tcpdump exited abnormally with status: ' + str(tcp_piped_process.returncode) + ', out: ' + out + ', err: ' + err + ', err_out: ' + err_out) time.sleep(0) except OSError: pass # FLAG STATE: ready[set], stop[clear], finished[clear] # tcpdump return output format: # hh:mm:ss.tick L3Proto <Proto-specific fields>\n # \t0x<addr>: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF\n # \t0x<addr>: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF\n # (eight quads of hexadecimal numbers representing 16 # bytes or 4 32-bit words) # # hh:mm:ss.tick L3Proto <Proto-specific fields>\n # \t0x<addr>: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF\n # \t0x<addr>: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF\n # (Next packet) packet_data = [] timestamp = '' with open(tmp_dump_filename, 'r+') as f: # Prepare for the first packet by reading the file # until the first packet's lines arrive (or until # stopped by a stop_capture call) if f.readline().rstrip() != '--START--': status_queue.put( {'error': 'Expected --START-- tag at ' 'beginning of dumpfile', 'returncode': tcp_piped_process.returncode, 'stdout': '', 'stderr': ''}) tcp_error.set() raise exceptions.ArgMismatchException( 'Expected --START-- tag at beginning of dumpfile') while True: # Read the lines and either append data, # start a new packet, or finish line = f.readline() if line == '': # EOF # Is the tcpdump process finished or # signaled to finish? if tcp_piped_process.poll() is not None \ or tcp_stop.is_set(): # If we finished with packet data buffered up, # append that packet to the queue if len(packet_data) > 0: # Create and parse the packet and push it # onto the return list, calling # the callback function if one is set. packet = pcap_packet.PCAPPacket( packet_data, timestamp) packet_queue.put(packet) if callback is not None: callback(packet, *(callback_args if callback_args is not None else ())) # Stop packet collection and exit break # Otherwise, we need to wait for data time.sleep(0) elif line.startswith('\t'): # Normal packet data: buffer into current packet packet_data += parse_line_to_byte_array(line) else: # We hit the end of the packet and will start # a new packet Only run if we had packet data # buffered if len(packet_data) > 0: # Create and parse the packet and push it onto # the return list, calling the callback function # if one is set. packet = pcap_packet.PCAPPacket( packet_data, timestamp) packet_queue.put(packet) if callback is not None: callback(packet, *(callback_args if callback_args is not None else [])) packet_data = [] # Start the new packet by reading the timestamp timestamp = line.split(' ', 2)[0] finally: # Save the tcpdump output (if requested), and delete the # temporary file if save_dump_file is True: LinuxCLI().copy_file( tmp_dump_filename, save_dump_filename if save_dump_filename is not None else 'tcp.out.' + str(time.time())) LinuxCLI().rm(tmp_dump_filename) tcp_processes.terminate() status_queue.put({'success': '', 'returncode': tcp_piped_process.returncode, 'stdout': tcp_piped_process.stdout, 'stderr': tcp_piped_process.stderr}) # FLAG STATE: ready[set], stop[set], finished[clear] tcp_finished.set() # FLAG STATE: ready[set], stop[set], finished[set] return packet_queue