├── __init__.py ├── arp2.cap ├── dpkt.odp ├── sctp.cap ├── ftp_ipv6.cap ├── http_ipv4.cap ├── http_ipv6.cap ├── ipv4_dns.cap ├── ipv6_tcp_wan.cap ├── http_ipv4_complex.cap ├── 4000_byte_ipv6_packet.cap ├── 60000_byte_ipv6_packet.cap ├── ftp_active_ipv4_FAIL.cap ├── ftp_passive_ipv4_FAIL.cap ├── http_ipv6_wireshark_1.cap ├── Python_course_notes-2011-01-11.odt ├── Python_course_notes_spring_2011.odt ├── simple_udp_receiver.py ├── simple_udp_sender.py ├── README ├── test_cases.txt ├── decode_udp.py ├── homepage.css ├── simple_udp_sender2.py ├── decode_http.py ├── dpkt_udp_sender.py ├── decode_http_2.py ├── decode_arp.py ├── decode_tcp.py ├── decode_dns.py ├── decode_mdns.py ├── decode_tcp_iterator.py ├── decode_tcp_iterator_2.py ├── decode_tcp_iterator_2P.py ├── dpkt_notes.html └── dpkt.html /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /arp2.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/arp2.cap -------------------------------------------------------------------------------- /dpkt.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/dpkt.odp -------------------------------------------------------------------------------- /sctp.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/sctp.cap -------------------------------------------------------------------------------- /ftp_ipv6.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/ftp_ipv6.cap -------------------------------------------------------------------------------- /http_ipv4.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/http_ipv4.cap -------------------------------------------------------------------------------- /http_ipv6.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/http_ipv6.cap -------------------------------------------------------------------------------- /ipv4_dns.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/ipv4_dns.cap -------------------------------------------------------------------------------- /ipv6_tcp_wan.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/ipv6_tcp_wan.cap -------------------------------------------------------------------------------- /http_ipv4_complex.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/http_ipv4_complex.cap -------------------------------------------------------------------------------- /4000_byte_ipv6_packet.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/4000_byte_ipv6_packet.cap -------------------------------------------------------------------------------- /60000_byte_ipv6_packet.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/60000_byte_ipv6_packet.cap -------------------------------------------------------------------------------- /ftp_active_ipv4_FAIL.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/ftp_active_ipv4_FAIL.cap -------------------------------------------------------------------------------- /ftp_passive_ipv4_FAIL.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/ftp_passive_ipv4_FAIL.cap -------------------------------------------------------------------------------- /http_ipv6_wireshark_1.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/http_ipv6_wireshark_1.cap -------------------------------------------------------------------------------- /Python_course_notes-2011-01-11.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/Python_course_notes-2011-01-11.odt -------------------------------------------------------------------------------- /Python_course_notes_spring_2011.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffsilverm/dpkt_doc/HEAD/Python_course_notes_spring_2011.odt -------------------------------------------------------------------------------- /simple_udp_receiver.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # 3 | # A simple UDP packet receiver with a twist. This gets a potentially very large 4 | # packet to demonstrate fragmentation of a UDP packet, so we can test packet 5 | # reassembly 6 | 7 | import socket 8 | 9 | UDP_IP="" # listen to anything IPv4 or IPv6 10 | UDP_PORT=50005 11 | 12 | sock = socket.socket( socket.AF_INET6, # Internet IPv4 or IPv6 13 | socket.SOCK_DGRAM ) # UDP 14 | sock.bind( (UDP_IP,UDP_PORT) ) 15 | 16 | while True: 17 | data, addr = sock.recvfrom( 1048576 ) # buffer size huge 18 | print "received message from ", addr, "Lenth of data: ", len(data) 19 | -------------------------------------------------------------------------------- /simple_udp_sender.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # 3 | # A simple UDP packet sender with a twist. This sends a very large packet to demonstrate fragmentation 4 | # of a UDP packet, so we can test packet reassembly 5 | import socket 6 | 7 | UDP_IP="192.168.1.104" 8 | UDP_PORT=50005 9 | 10 | message="" 11 | while len(message) <= 16384 : 12 | message = message + str(len(message)) + " " + 16*"+" + "\n" 13 | 14 | print "UDP target IP:", UDP_IP 15 | print "UDP target port:", UDP_PORT 16 | print "message:", message, "Length: ", len(message) 17 | 18 | sock = socket.socket( socket.AF_INET, # Internet 19 | socket.SOCK_DGRAM ) # UDP 20 | sock.sendto( message, (UDP_IP, UDP_PORT) ) 21 | 22 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | dpkt is a great packet decoding library that is poorly documented and has few 2 | examples. In this project, I propose that we document dpkt. For each 3 | protocol, there is a set of functions. Each function needs to be documented. 4 | We will also create sample packet captures and software that decodes the sample 5 | packet captures. 6 | 7 | Each protocol is a deliverable. Each function is a deliverable. Each sample 8 | packet capture is a deliverable. 9 | 10 | The list of packet captures for testing purposes is given in the file 11 | test_cases.txt, which is a tab-delimited file giving the file name and test 12 | conditions the file was generated under. 13 | 14 | To install dpkt under ubuntu: 15 | sudo apt-get install python-dpkt 16 | sudo apt-get install python-libpcap 17 | 18 | -------------------------------------------------------------------------------- /test_cases.txt: -------------------------------------------------------------------------------- 1 | File Test conditions 2 | 4000_byte_ipv6_packet.cap UDP over IPv6 showing IP fragmentation 3 | 60000_byte_ipv6_packet.cap UDP over IPv6 showing IP fragmentation 4 | arp2.cap ARP showing both an ARP failure and success 5 | ftp_active_ipv4_FAIL.cap Active mode FTP over IPv4, showing a failure 6 | ftp_ipv6.cap Extended passive mode FTP over IPv6 7 | ftp_passive_ipv4_FAIL.cap Passive mode FTP over IPv4, showing a failure 8 | http_ipv4.cap HTTP over IPv4, simple page, 13 packets 9 | http_ipv6.cap HTTP over IPv6 captured with tcpdump (This doesn't play nice with libpcap) 10 | http_ipv4_complex.cap HTTP over IPv4, complex page 171 packets 11 | http_ipv6_wireshark_1.cap HTTP over IPv6 captured with wireshark 12 | ipv4_dns.cap DNS over IPv4 showing a variety of different lookups 13 | ipv6_tcp_wan.cap TCP over IPv6 over a WAN 14 | sctp.cap Stream Control Transmission Protocol 15 | -------------------------------------------------------------------------------- /decode_udp.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # 3 | # This program decodes UDP packets from the wire 4 | 5 | import dpkt 6 | import sys 7 | import socket 8 | import pcap 9 | 10 | 11 | 12 | 13 | def decode_udp ( pc ) : 14 | """decode_udp is a generator function that listens to a pcap.pcap object and returns a UDP object when it hears a packet""" 15 | for ts, pkt in pc: 16 | # parse the packet. Decode the ethertype 17 | eth = dpkt.ethernet.Ethernet(pkt) 18 | if eth.type == dpkt.ethernet.ETH_TYPE_IP : 19 | ip = eth.data 20 | if ip.p == dpkt.ip.IP_PROTO_UDP : 21 | # This doesn't deal with IP fragments 22 | udp = ip.data 23 | # Pass the IP addresses, source port, destination port, and data back to the caller. 24 | yield ( ip.src, udp.sport, ip.dst, udp.dport, udp.data, ip.v) 25 | elif eth.type == dpkt.ethernet.ETH_TYPE_IP6 : 26 | ip = eth.data 27 | if ip.nxt == dpkt.ip.IP_PROTO_UDP : 28 | # This doesn't deal with IP fragments 29 | udp = ip.data 30 | # Pass the IP addresses, source port, destination port, and data back to the caller. 31 | yield ( ip.src, udp.sport, ip.dst, udp.dport, udp.data, ip.v) 32 | else : 33 | # If the packet is something else, then I need to figure out a better way of handling it. 34 | pass 35 | 36 | def main() : 37 | if sys.argv[1] == "-i" : 38 | pc = pcap.pcap( sys.argv[2] ) 39 | elif sys.argv[1] == "-f" : 40 | pc = dpkt.pcap.Reader( open ( sys.argv[2] ) ) 41 | else : 42 | print """Use -i INTERFACE to packet capture from an interface. 43 | Use -f FILENAME to read a packet capture file""" 44 | sys.exit(2) 45 | 46 | for src, sport, dst, dport, data, ip_version in decode_udp( pc ) : 47 | if ip_version == 4 : 48 | print "from ", socket.inet_ntoa(src),":",sport, " to ", socket.inet_ntoa(dst),":",dport 49 | else : 50 | print "from ", socket.inet_ntop(AF_INET6, src),".",sport, " to ", socket.inet_ntop(AF_INET6, dst), ".", dport 51 | 52 | 53 | 54 | if __name__ == "__main__" : 55 | main() 56 | 57 | -------------------------------------------------------------------------------- /homepage.css: -------------------------------------------------------------------------------- 1 | /* -- add 2 | 3 | 4 | 5 | to the header section of the HTML file to reference this */ 6 | BODY { background: white; color: black} 7 | A:link { color: red } 8 | A:visited { color: maroon } 9 | A:active { color: fuchsia } 10 | H1.myclass {border-width: thin; border: solid; text-align: center} 11 | H1, .h1 { font-size: 36pt; font-weight: bold; background: #ffdcdc; margin-left: 18pt } 12 | H2, .h2 { font-size: 30pt; font-weight: bold; background: #ffffcc; margin-left: 36pt } 13 | H3, .h3 { font-size: 24pt; font-weight: bold; background: #ccffcc; margin-left: 54pt } 14 | H4, .h4 { font-size: 20pt; font-weight: bold; background: #ccffff; margin-left: 72pt } 15 | H5, .h5 { font-size: 16pt; font-weight: bold; background: #ccccff; margin-left: 72pt } 16 | H6, .h6 { font-size: 14pt; font-weight: bold; background: #cccccc; margin-left: 90pt } 17 | P { background: #fefefe; text-indent: 5em } 18 | .sat { background: #c0ffc0 } 19 | .sun { background: #c0c0ff } 20 | .wkd { background: #e0e0e0 } 21 | .hol { background: #ffffc0 } 22 | .toc { background: #ffc0c0 } 23 | code, samp, kbd, var { white-space: pre; font-size: 10pt; } 24 | samp { background: #fefeff; } 25 | kbd { text-decoration: underline; } 26 | var { font-style: italic; } 27 | /* kbd { white-space: pre; text-decoration: underline; font-size: 10pt; } */ 28 | .appointments { color: red; background: #f0f0f0 } 29 | .important { background: #00ff00 } 30 | .tbd { color: white; background: red } 31 | .inferred { color: blue; background: yellow } 32 | OL { list-style-type: decimal } /* 1 2 3 4 5 etc. */ 33 | OL OL { list-style-type: upper-alpha } /* A B C D E etc. */ 34 | OL OL OL { list-style-type: decimal } 35 | OL OL OL OL { list-style-type: lower-roman } /* i ii iii iv v etc. */ 36 | OL OL OL OL OL { list-style-type: lower-alpha } /* a b c d e etc. */ 37 | .hotthought {border-width: thin; border: solid; color: red } 38 | .thought { color: red } 39 | .caution { border-width: thin; border: dashed; color: blue } 40 | .warning { border-width: medium; border: double; color: red } 41 | .button { border-width: thin; border: solid; background: #f0f0fc } 42 | 43 | -------------------------------------------------------------------------------- /simple_udp_sender2.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # 3 | # A simple UDP packet sender This can send a very large UDP packet to demonstrate fragmentation 4 | # of a UDP packet, so we can test packet reassembly 5 | import socket 6 | import getopt 7 | import sys 8 | 9 | 10 | def help() : 11 | print sys.argv[0] + """ -6h -s size -d destination_IP_addr 12 | -6 Use IPv6 (default is IP4) (must come before -d if ipV6) 13 | -d IP destination IP address (either IPv4 or IPv6) (required) 14 | -s size size of the UDP packet (0 < size < 65536, upper limit is not enforced, default is 4096 15 | -p port destination port (default is 50005, 0< port < 65536) 16 | -h help 17 | """ 18 | sys.exit(2) 19 | 20 | 21 | def parse_args ( ) : 22 | """Parse the command line arguments. The arguments are: 23 | -6 Use IPv6 (default is IP4) (must come before -d if ipV6) 24 | -d IP destination IP address (either IPv4 or IPv6) (required) 25 | -s size size of the UDP packet (0 < size < 65536, upper limit is not enforced, default is 4096 26 | -p port destination port (default is 50005, 0< port < 65536) 27 | -h help 28 | """ 29 | try : 30 | optlist, args = getopt.getopt ( sys.argv[1:], "6d:s:h") 31 | ipv6 = 0 32 | destination_ip = "" 33 | destination_port = 50005 34 | message_size = 4096 35 | for opt in optlist : 36 | if opt[0] == "-6" : ipv6 = 1 37 | elif opt[0] == "-d" : 38 | destination_ip = opt[1] 39 | elif opt[0] == "-s" : 40 | message_size = int( opt[1] ) 41 | elif opt[0] == "-p" : 42 | destination_port = int( opt[1] ) 43 | if not ( 0 < destination_port < 65536 ) : 44 | raise ValueError 45 | elif opt[0] == "-h" : 46 | help() 47 | if destination_ip == "" : 48 | print "The -d DESTINATION_IP switch is required" 49 | raise ValueError 50 | except ( getopt.GetoptError, ValueError ) : 51 | help() 52 | return (destination_ip, destination_port, message_size, ipv6) 53 | 54 | 55 | 56 | 57 | if __name__ == "__main__" : 58 | (destination_ip, destination_port, message_size, ipv6) = parse_args( ) 59 | 60 | 61 | message = message_size * "*" 62 | 63 | print "UDP target IP:", destination_ip 64 | print "UDP target port:", destination_port 65 | 66 | 67 | sock = socket.socket( ( socket.AF_INET6 if ipv6 else socket.AF_INET ), socket.SOCK_DGRAM ) 68 | sock.sendto( message, (destination_ip, destination_port) ) # destination IP address must be a string, not a packeted IP address 69 | 70 | 71 | -------------------------------------------------------------------------------- /decode_http.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import dpkt 4 | import sys 5 | 6 | f = open(sys.argv[1]) 7 | pcap = dpkt.pcap.Reader(f) 8 | 9 | for ts, buf in pcap: 10 | eth = dpkt.ethernet.Ethernet(buf) 11 | ip = eth.data # This is an oversimplification - IP packets can fragment if an MTU in the path is smaller than the MTU of the LAN 12 | # Also, this changes a little bit with IPv6. To tell the difference between IPv4 and IPv6, you have to look 13 | # at the ethertype field, which is given by http://www.iana.org/assignments/ethernet-numbers. IPv4 is 0x800 or 2048 14 | # and IPv6 is 0x86DD or 34525 15 | tcp = ip.data # This is an oversimplification - this is true if and only if the protocol field of the IP packet is 6. 16 | # See http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml for details of protocol numbers. 17 | # See http://tools.ietf.org/html/rfc791 for details on IPv4 18 | 19 | # If the destination port is 80 and there is data in the packet, then probably this is an HTTP request. TO be certain, there should 20 | # be a TCP finite state machine in here. 21 | if tcp.dport == 80 and len(tcp.data) > 0: 22 | http_req = dpkt.http.Request(tcp.data) 23 | print "URI is ", http_req.uri 24 | for header in http_req.headers.keys() : 25 | print header, http_req.headers[header] 26 | # ['_Request__methods', '_Request__proto', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__hash__', '__hdr_defaults__', '__init__', '__len__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'body', 'data', 'headers', 'method', 'pack', 'pack_hdr', 'unpack', 'uri', 'version'] 27 | print "method is ", http_req.method 28 | # 'body', 'data', 'headers', 'method', 'pack', 'pack_hdr', 'unpack', 'uri', 'version' 29 | print "HTTP headers, packed ", http_req.pack() 30 | print "HTTP version", http_req.version 31 | print "HTTP data ", http_req.data # I think this is valid if the method is POST 32 | if tcp.sport == 80 and len(tcp.data) > 0: 33 | try : 34 | http = dpkt.http.Response(tcp.data) 35 | print "HTTP version is ", http.version 36 | print "Status code is ", http.status 37 | print "Status reason ", http.reason 38 | for header in http.headers.keys() : 39 | print header, http.headers[header] 40 | # print "date", http.headers['date'] 41 | # print "accept-ranges", http.headers['accept-ranges'] 42 | # print "content-type", http.headers['content-type'] 43 | # print "connection", http.headers['connection'] 44 | # print "server", http.headers['server'] 45 | except dpkt.dpkt.UnpackError : 46 | print "Encounted an unpacking error" 47 | f.close() 48 | -------------------------------------------------------------------------------- /dpkt_udp_sender.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # A simple UDP packet sender This can send a very large UDP packet to demonstrate fragmentation 5 | # of a UDP packet, so we can test packet reassembly 6 | import socket 7 | import getopt 8 | import sys 9 | import dpkt 10 | import struct 11 | 12 | 13 | def help() : 14 | print sys.argv[0] + """ -6h -s size -d destination_IP_addr 15 | -6 Use IPv6 (default is IP4) (must come before -d if ipV6) 16 | -d IP destination IP address (either IPv4 or IPv6) (required) 17 | -s size size of the UDP packet (0 < size < 65536, upper limit is not enforced, default is 4096 18 | -p port destination port (default is 50005, 0< port < 65536) 19 | -h help 20 | """ 21 | sys.exit(2) 22 | 23 | 24 | def parse_args ( ) : 25 | """Parse the command line arguments. The arguments are: 26 | -6 Use IPv6 (default is IPv4) (must come before -d if IPv6) 27 | -d IP destination IP address (either IPv4 or IPv6) (required) 28 | -l length size of the UDP packet (0 < size < 65536, upper limit is not enforced, default is 4096 29 | -p port destination port (default is 50005, 0< port < 65536) 30 | -s port source port (default is 50004, 0< port < 65536) 31 | -h help 32 | """ 33 | try : 34 | optlist, args = getopt.getopt ( sys.argv[1:], "6d:s:h") 35 | ipv6 = 0 36 | destination_ip = "" 37 | destination_port = 50005 38 | source_port = 50004 39 | message_length = 4096 40 | for opt in optlist : 41 | if opt[0] == "-6" : ipv6 = 1 42 | elif opt[0] == "-d" : 43 | destination_ip = opt[1] 44 | elif opt[0] == "-l" : 45 | message_length = int( opt[1] ) 46 | elif opt[0] == "-p" : 47 | destination_port = int( opt[1] ) 48 | if not ( 0 < destination_port < 65536 ) : 49 | raise ValueError 50 | elif opt[0] == "-s" : 51 | source_port = int ( opt[1] ) 52 | if not ( 0 < destination_port < 65536 ) : 53 | raise ValueError 54 | elif opt[0] == "-h" : 55 | help() 56 | if destination_ip == "" : 57 | print "The -d DESTINATION_IP switch is required" 58 | raise ValueError 59 | except ( getopt.GetoptError, ValueError ) : 60 | help() 61 | return (destination_ip, destination_port, source_port, message_length, ipv6) 62 | 63 | 64 | def create_udp_packet (destination_ip, destination_port, source_port, message_length, ipv6) : 65 | udp = dpkt.udp.UDP() 66 | udp.sport = source_port 67 | udp.dport = destination_port 68 | udp.ulen = message_length + 8 # add 8 because that's the length of the UDP header 69 | message = message_length * "*" 70 | udp.data = message 71 | # Need to calculate a UDP checksum 72 | return udp.pack() 73 | 74 | def createRawIPSocket( ipv6 ): 75 | sock = socket.socket(socket.AF_INET6 if ipv6 else socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP) 76 | return sock 77 | 78 | if __name__ == "__main__" : 79 | ( destination_ip, destination_port, source_port, message_length, ipv6) = parse_args( ) 80 | sock = createRawIPSocket( ipv6 ) 81 | # create_udp_packet needs the destination IP address so it can calculate the UDP checksum 82 | udp_packet = create_udp_packet ( destination_ip, destination_port, source_port, message_length, ipv6) 83 | sock.sendto(udp_packet, (destination_ip, destination_port ) ) 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /decode_http_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # This program uses decode_tcp_iterator_2.py to get data for HTTP parsing. That way, 5 | # the input strings can be longer than what will fit into a single packet. 6 | 7 | 8 | import dpkt 9 | import sys 10 | import decode_tcp_iterator_2P as d 11 | import socket 12 | 13 | 14 | 15 | def main(pc) : 16 | """This is the outer loop that prints strings that have been captured from the TCP streams, terminated by a packet that 17 | has the PUSH flag set.""" 18 | for cid, received_string, ip_version in d.decode_tcp(pc) : # cid = connection_id 19 | # This next line is for debugging only 20 | # print d.connection_id_to_str (cid, ip_version), received_string 21 | if cid[3] == 80 : # cid[3] is the destination port 22 | # This is a message going to the server, a request 23 | src_addr = socket.inet_ntoa(cid[0]) if ip_version == 4 else socket.inet_ntop(socket.AF_INET6, cid[0]) 24 | print "HTTP request from ", src_addr 25 | http_req = dpkt.http.Request(received_string) 26 | print "URI is ", http_req.uri 27 | for header in http_req.headers.keys() : 28 | print header, http_req.headers[header] 29 | # ['_Request__methods', '_Request__proto', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__hash__', '__hdr_defaults__', '__init__', '__len__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'body', 'data', 'headers', 'method', 'pack', 'pack_hdr', 'unpack', 'uri', 'version'] 30 | print "method is ", http_req.method 31 | # 'body', 'data', 'headers', 'method', 'pack', 'pack_hdr', 'unpack', 'uri', 'version' 32 | print "HTTP headers, packed ", http_req.pack() 33 | print "HTTP version", http_req.version 34 | # print "HTTP data ", http_req.data # I think this is valid if the method is POST 35 | 36 | elif cid[1] == 80 : # cid[1] is the source port 37 | try : 38 | http = dpkt.http.Response(received_string) 39 | print "HTTP version is ", http.version 40 | print "Status code is ", http.status 41 | print "Status reason is ", http.reason 42 | for header in http.headers.keys() : 43 | print header, " is ", http.headers[header] 44 | # print "date", http.headers['date'] 45 | # print "accept-ranges", http.headers['accept-ranges'] 46 | # print "content-type", http.headers['content-type'] 47 | # print "connection", http.headers['connection'] 48 | # print "server", http.headers['server'] 49 | except dpkt.dpkt.UnpackError : 50 | print "Encounted an unpacking error" 51 | else : 52 | print "skipping ", d.connection_id_to_str( cid, ip_version ) 53 | 54 | 55 | if __name__ == "__main__" : 56 | if len(sys.argv) < 2 : 57 | decode_tcp_help() 58 | # create an interator to return the next packet. The source can be either an interface using the libpcap library or it can be a file in pcap 59 | # format such as created by tcpdump. 60 | if sys.argv[1] == "eth0" : # wired ethernet interface 61 | pc = pcap.pcap("eth0", promisc=True ) 62 | elif sys.argv[1] == "wlan0" : # wireless interface 63 | pc = pcap.pcap("wlan0", promisc=True ) 64 | elif sys.argv[1] == "sixxs" : # IPv6 tunnel pseudo device 65 | pc = pcap.pcap("sixxs", promisc=True ) 66 | else : 67 | pc = dpkt.pcap.Reader ( open(sys.argv[1] ) ) # file interface 68 | main(pc) 69 | 70 | -------------------------------------------------------------------------------- /decode_arp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # This program listens to an ethernet, filters on ARP packets, and builds a table of physical (Ethernet) addresses <=> IPv4 addresses 3 | 4 | import dpkt 5 | import sys 6 | import socket 7 | import pcap 8 | import subprocess 9 | import binascii 10 | import time 11 | 12 | def add_colons_to_mac( mac_addr ) : 13 | """This function accepts a 12 hex digit string and converts it to a colon 14 | separated string""" 15 | s = list() 16 | for i in range(12/2) : # mac_addr should always be 12 chars, we work in groups of 2 chars 17 | s.append( mac_addr[i*2:i*2+2] ) 18 | r = ":".join(s) # I know this looks strange, refer to http://docs.python.org/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer-xrange 19 | return r 20 | 21 | 22 | def main() : 23 | # This code allows this program to run equally well on my laptop and my desktop. I did it this 24 | # way to demonstrate different interface names. If I was really clever, I'd figure out how to this 25 | # under MS-Windows 26 | if sys.argv[1] == "-i" : 27 | pc = pcap.pcap( sys.argv[2] ) 28 | elif sys.argv[1] == "-f" : 29 | pc = dpkt.pcap.Reader( open ( sys.argv[2] ) ) 30 | else : 31 | print """Use -i INTERFACE to [packet capture from an interface. 32 | Use -f FILENAME to read a packet capture file""" 33 | sys.exit(2) 34 | 35 | pc.setfilter('arp') # Use a kernel filter and just pass arp traffic 36 | start_time = time.time() # Return the time as a floating point number expressed in seconds since the epoch 37 | arp_cache = dict() 38 | 39 | # a pcap.pcap object listens to the network and returns a packet object when it hears a packet. 40 | for ts, pkt in pc: 41 | # parse the packet. Because the filter allows only ARP packets through, we don't have to decode the ethertype 42 | eth = dpkt.ethernet.Ethernet(pkt) 43 | arp = eth.arp 44 | print "From: ",add_colons_to_mac( binascii.hexlify(eth.src)), 45 | print " to: ", add_colons_to_mac( binascii.hexlify(eth.dst)) # Ethernet destination addresses may be broadcast addresses 46 | # print arp.hrd 47 | # print arp.pro 48 | if arp.op==1 : 49 | print "request" 50 | elif arp.op==2 : 51 | print "reply" 52 | else : 53 | print "op has an unexpected value %d", arp.op 54 | print "source protocol address", socket.inet_ntoa(arp.spa) 55 | print "source hardware address", add_colons_to_mac( binascii.hexlify(arp.sha) ) 56 | print "Target protocol address", socket.inet_ntoa(arp.tpa) #IPv4 address 57 | print "target hardware address", add_colons_to_mac( binascii.hexlify(arp.tha) ) 58 | arp_cache[arp.spa] = arp.sha 59 | if arp.op==2 : # reply 60 | if arp.tpa in arp_cache : 61 | if arp_cache[arp.tpa] != arp.tha : 62 | print "Duplicate IP addresses detected: ", socket.inet_ntoa(arp.tpa), "is assigned to both ", 63 | add_colons_to_mac( binascii.hexlify(arp.tha) ), " and ", add_colons_to_mac( binascii.hexlify(arp_cache[arp.tha] ) ) 64 | arp_cache[arp.tpa] = arp.tha 65 | # The arp cache has to be refreshed every 30 seconds, so this will catch every machine on the ethernet at least twice. However, you might want to 66 | # continuously monitor your network to catch machines surreptitiously. 67 | elapsed_time = time.time() - start_time 68 | if elapsed_time > 60.0 : 69 | break 70 | 71 | for ip in arp_cache.keys() : 72 | print socket.inet_ntoa(ip),": ", add_colons_to_mac( binascii.hexlify(arp_cache[ip])) 73 | 74 | 75 | if __name__ == "__main__" : 76 | main() 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /decode_tcp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import dpkt 4 | import sys 5 | import socket 6 | 7 | def connection_id_to_str (cid, v=4) : 8 | """This converts the connection ID cid which is a tuple of (source_ip_address, source_tcp_port, destination_ip_address, 9 | destination_tcp_port) to a string. v is either 4 for IPv4 or 6 for IPv6""" 10 | if v == 4 : 11 | src_ip_addr_str = socket.inet_ntoa(cid[0]) 12 | dst_ip_addr_str = socket.inet_ntoa(cid[2]) 13 | return src_ip_addr_str + ":" + str(cid[1])+"<=>"+dst_ip_addr_str + ":" + str(cid[3]) 14 | elif v == 6 : 15 | src_ip_addr_str = socket.inet_ntop(AF_INET6, cid[0]) 16 | dst_ip_addr_str = socket.inet_ntop(AF_INET6, cid[2]) 17 | return src_ip_addr_str + "." + str(cid[1])+"<=>"+dst_ip_addr_str + "." + str(cid[3]) 18 | else : 19 | raise ValueError('Argument to connection_id_to_str must be 4 or 6, is %d' % v) 20 | 21 | def main(f): 22 | """This function decodes a packet capture file f and breaks it up into tcp connections""" 23 | pcap = dpkt.pcap.Reader(f) 24 | print "counter\tsrc prt\tdst prt\tflags" 25 | packet_cntr = 0 26 | connection_table = {} 27 | 28 | for ts, buf in pcap: 29 | packet_cntr += 1 30 | eth = dpkt.ethernet.Ethernet(buf) 31 | # Also, this changes a little bit with IPv6. To tell the difference between IPv4 and IPv6, you have to look 32 | # at the ethertype field, which is given by http://www.iana.org/assignments/ethernet-numbers. IPv4 is 0x800 or 2048 33 | # and IPv6 is 0x86DD or 34525 34 | # This is simplistic - IP packets can be fragmented. Also, this only works for IPv4. IPv6 has a different Ethertype 35 | if eth.type != dpkt.ethernet.ETH_TYPE_IP : 36 | continue 37 | elif eth.type == dpkt.ethernet.ETH_TYPE_IP6 : 38 | pass 39 | ip = eth.data 40 | # If not a TCP paaket 41 | if ip.p != dpkt.ip.IP_PROTO_TCP: 42 | continue 43 | # This is also simplistic - TCP packets can be fragmented, and also retransmitted 44 | # 45 | tcp = ip.data 46 | fin_flag = ( tcp.flags & 0x01 ) != 0 47 | syn_flag = ( tcp.flags & 0x02 ) != 0 48 | rst_flag = ( tcp.flags & 0x04 ) != 0 49 | psh_flag = ( tcp.flags & 0x08 ) != 0 50 | ack_flag = ( tcp.flags & 0x10 ) != 0 51 | urg_flag = ( tcp.flags & 0x20 ) != 0 52 | ece_flag = ( tcp.flags & 0x40 ) != 0 53 | cwr_flag = ( tcp.flags & 0x80 ) != 0 54 | flags = ( 55 | ( "C" if cwr_flag else " " ) + 56 | ( "E" if ece_flag else " " ) + 57 | ( "U" if urg_flag else " " ) + 58 | ( "A" if ack_flag else " " ) + 59 | ( "P" if psh_flag else " " ) + 60 | ( "R" if rst_flag else " " ) + 61 | ( "S" if syn_flag else " " ) + 62 | ( "F" if fin_flag else " " ) ) 63 | 64 | print packet_cntr, "\t", tcp.sport, "\t", tcp.dport, "\t", flags 65 | connection_id = (ip.src, tcp.sport, ip.dst, tcp.dport) 66 | if syn_flag and not ack_flag : 67 | # Each TCP connection is forming. The new connection is stored as a dictionary 68 | # whose key is the tuple (source_ip_address, source_tcp_port, destination_ip_address, destination_tcp_port) 69 | # The connection is stored in a dictionary. The key is the connection_id, value of each key is a list of tcp packets 70 | # Note that there are two connections, one from the client to the server and one from the server to the client. This becomes 71 | # important when the connection is closed, because one side might FIN the connection well before the other side does. 72 | print "Forming a new connection " + connection_id_to_str( connection_id, ip.v ) 73 | connection_table[connection_id] = [] 74 | elif syn_flag and ack_flag : 75 | print "Server responding to a new connection " + connection_id_to_str( connection_id, ip.v ) 76 | connection_table[connection_id] = [] 77 | # This is where I am having a little confusion. My instinct tells me that the connection from the client to the server and the 78 | # connection from the server back to the client should be connected somehow. 79 | elif not syn_flag and ack_flag : 80 | if connection_id not in connection_table.keys(): 81 | # Not a complete connection 82 | connection_table[connection_id] = [] 83 | 84 | connection_table[connection_id].append(tcp.data) 85 | elif fin_flag : 86 | pass 87 | # Also need to think about the RESET flag 88 | 89 | f.close() 90 | for connection_id in connection_table.keys() : 91 | print "Connection "+ connection_id_to_str(connection_id) 92 | print connection_table[connection_id] 93 | 94 | 95 | 96 | if __name__ == "__main__" : 97 | f = open(sys.argv[1]) 98 | main(f) 99 | -------------------------------------------------------------------------------- /decode_dns.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # 3 | # This program decodes DNS packets from the wire or from a capture file 4 | # -*- coding: utf-8 -*- 5 | 6 | import dpkt, dpkt.dns 7 | import sys 8 | import socket 9 | import pcap 10 | import subprocess 11 | 12 | type_table={} # This is a lookup table for DNS query types 13 | 14 | def initialize_tables() : 15 | global type_table 16 | 17 | 18 | # From http://www.networksorcery.com/enp/protocol/dns.htm 19 | type_table = {1:"A", # IP v4 address, RFC 1035 20 | 2:"NS", # Authoritative name server, RFC 1035 21 | 5:"CNAME", # Canonical name for an alias, RFC 1035 22 | 6:"SOA", # Marks the start of a zone of authority, RFC 1035 23 | 12:"PTR", # Domain name pointer, RFC 1035 24 | 13:"HINFO", # Host information, RFC 1035 25 | 15:"MX", # Mail exchange, RFC 1035 26 | 28:"AAAA", # IP v6 address, RFC 3596 27 | 16:"TXT", # 28 | 33:"SRV", # RFC 2782 29 | 255:"ANY", # all cached reco 30 | } 31 | 32 | def hexify(x): 33 | "The strings from DNS resolver contain non-ASCII characters - I don't know why. This function investigates that" 34 | toHex = lambda x:"".join([hex(ord(c))[2:].zfill(2) for c in x]) 35 | return toHex(x) 36 | 37 | def udp_iterator(pc): 38 | """pc is a pcap.pcap object that listens to the network and returns a packet object when it hears a packet go by""" 39 | for ts, pkt in pc: 40 | # parse the packet. Decode the ethertype. If it is IP (IPv4) then process it further 41 | eth = dpkt.ethernet.Ethernet(pkt) 42 | if eth.type == dpkt.ethernet.ETH_TYPE_IP : 43 | ip = eth.data 44 | # If the IP protocol is UDP, then process it further 45 | if ip.p == dpkt.ip.IP_PROTO_UDP : 46 | udp = ip.data 47 | # Pass the IP addresses, source port, destination port, and data back to the caller. 48 | yield ( ip.src, udp.sport, ip.dst, udp.dport, udp.data) 49 | 50 | 51 | def decode_dns_response ( rr, response_type ) : 52 | """This subroutine decodes a DNS response packet. The packet may have more than one rr""" 53 | r_type = rr.type 54 | r_data = rr.rdata 55 | if rr.cls != 1 : 56 | print "Response class is %d, not class IN, might be Hesiod, chaos, or qclass (all of which are anachronisms)",%rr.cls 57 | print "Response is component", response_type,r_type,type_table[r_type] if r_type in type_table else 'unknown' 58 | if r_type == dpkt.dns.DNS_CNAME : 59 | #print "Response is a CNAME ", r_data," in hex: ", hexify(r_data) 60 | print "Response is a CNAME ", rr.cname 61 | elif r_type == dpkt.dns.DNS_A : 62 | print "response is an IPv4 address", socket.inet_ntoa( r_data ) 63 | elif r_type == dpkt.dns.DNS_NS : 64 | #print "Response is a NS name", r_data," in hex: ", hexify(r_data) 65 | print "Response is a NS name", rr.nsname 66 | elif r_type == dpkt.dns.DNS_AAAA : 67 | print "response is an IPv6 address", socket.inet_ntop( socket.AF_INET6, r_data ) 68 | elif r_type == dpkt.dns.DNS_PTR : 69 | #print "response is a hostname from an IP address", r_data, "in hex: ", hexify(r_data) 70 | print "response is a hostname from an IP address", rr.ptrname 71 | elif r_type == dpkt.dns.DNS_SOA : 72 | print 'DNS_SOA:',rr.mname,rr.rname,rr.serial,rr.refresh,rr.retry,rr.expire, rr.minimum 73 | elif r_type == dpkt.dns.DNS_MX : 74 | print 'DNS_MX:',rr.mxname,rr.preference 75 | elif r_type == dpkt.dns.DNS_HINFO : 76 | print 'DNS_HINFO:',rr.text 77 | elif r_type == dpkt.dns.DNS_TXT : 78 | print "TEXT:",rr.text 79 | elif r_type == dpkt.dns.DNS_SRV : 80 | print 'DNS_SRV:',rr.srvname,rr.port,rr.priority,rr.weight 81 | else : 82 | print "Response type is something other than a CNAME, PTR, IPv4 address, or IPv6 address ...", r_type, 83 | if r_type in type_table : 84 | print type_table[r_type] 85 | print "r-data is ", r_data," in hex: ", hexify(r_data) 86 | else : 87 | print "Unknown" 88 | 89 | 90 | def main() : 91 | # This code allows this program to run equally well on my laptop and my desktop. I did it this 92 | # way to demonstrate different interface names. If I was really clever, I'd figure out how to do this 93 | # under MS-Windows 94 | if sys.argv[1] == "-i" : 95 | pc = pcap.pcap( sys.argv[2] ) 96 | elif sys.argv[1] == "-f" : 97 | pc = dpkt.pcap.Reader( open ( sys.argv[2] ) ) 98 | else : 99 | print """Use -i INTERFACE to [packet capture from an interface. 100 | Use -f FILENAME to read a packet capture file""" 101 | sys.exit(2) 102 | initialize_tables() 103 | 104 | for (src, sport, dst, dport, data ) in udp_iterator(pc) : 105 | # Uncomment if you want to see all UDP packets 106 | # print "from ", socket.inet_ntoa(src),":",sport, " to ", socket.inet_ntoa(dst),":",dport 107 | if dport == 53 : 108 | # UDP/53 is a DNS query 109 | dns = dpkt.dns.DNS(data) 110 | if dns.opcode != dpkt.dns.DNS_QUERY : 111 | print "A DNS packet was sent to the nameserver, but the opcode was %d instead of DNS_QUERY (this is a software error)" % dns.opcode 112 | if dns.qr != dpkt.dns.DNS_Q : 113 | print "A DNS packet was sent to the name server, but dns.qr is not 0 and should be. It is %d" % dns.qr 114 | print "query for ", dns.qd[0].name, "ID is ", dns.id, "dns.qr is ", dns.qr, "query type is ", dns.qd[0].type, type_table[dns.qd[0].type] 115 | print "dns.qd is ", dns.qd 116 | elif sport == 53 : 117 | # UDP/53 is a DNS response 118 | dns = dpkt.dns.DNS(data) 119 | print "responding to ", dns.id, "dns.qr is ", dns.qr 120 | if dns.qr != dpkt.dns.DNS_R : 121 | print "A DNS packet was received from a name server, but dns.qr is not 1 and should be. It is %d" % dns.qr 122 | if dns.get_rcode() == dpkt.dns.DNS_RCODE_NOERR : 123 | print "Response has no error" 124 | elif dns.get_rcode() == dpkt.dns.DNS_RCODE_NXDOMAIN : 125 | print "There is no name in this domain" 126 | else : 127 | print "Response is something other than NOERR or NXDOMAIN %d - this software is incomplete" % dns.get_rcode() 128 | print "The response packet has %d RRs" % len(dns.an) 129 | # Decode the RR records in the NS section 130 | for rr in dns.ns : 131 | decode_dns_response ( rr, "NS") 132 | # Decode the answers in the DNS answer 133 | for rr in dns.an : 134 | decode_dns_response ( rr, "AN" ) 135 | # Decode the additional responses 136 | for rr in dns.ar : 137 | decode_dns_response ( rr, "AR" ) 138 | print "dns.qd is ", dns.qd 139 | 140 | 141 | if __name__ == "__main__" : 142 | main() 143 | 144 | -------------------------------------------------------------------------------- /decode_mdns.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | #https://github.com/jeffsilverm/dpkt_doc/blob/master/decode_dns.py 3 | # 4 | # This program decodes DNS packets from the wire or from a capture file 5 | # A simple MDNS decoder is in https://gist.github.com/m-mizutani/1188242 6 | # -*- coding: utf-8 -*- 7 | 8 | import dpkt, dpkt.dns 9 | import sys 10 | import socket 11 | import pcap 12 | import subprocess 13 | import datetime 14 | 15 | type_table={} # This is a lookup table for DNS query types 16 | 17 | def initialize_tables() : 18 | global type_table 19 | 20 | 21 | # From http://www.networksorcery.com/enp/protocol/dns.htm 22 | type_table = {1:"A", # IP v4 address, RFC 1035 23 | 2:"NS", # Authoritative name server, RFC 1035 24 | 5:"CNAME", # Canonical name for an alias, RFC 1035 25 | 6:"SOA", # Marks the start of a zone of authority, RFC 1035 26 | 12:"PTR", # Domain name pointer, RFC 1035 27 | 13:"HINFO", # Host information, RFC 1035 28 | 15:"MX", # Mail exchange, RFC 1035 29 | 16:"TXT", # 30 | 28:"AAAA", # IP v6 address, RFC 3596 31 | 33:"SRV", # RFC 2782 32 | 255:"ANY", # all cached records, RFC 1035 33 | } 34 | 35 | def hexify(x): 36 | "The strings from DNS resolver contain non-ASCII characters - I don't know why. This function investigates that" 37 | toHex = lambda x:"".join([hex(ord(c))[2:].zfill(2) for c in x]) 38 | return toHex(x) 39 | 40 | def udp_iterator(pc): 41 | """pc is a pcap.pcap object that listens to the network and returns a packet object when it hears a packet go by""" 42 | for ts, pkt in pc: 43 | # parse the packet. Decode the ethertype. If it is IP (IPv4) then process it further 44 | eth = dpkt.ethernet.Ethernet(pkt) 45 | if eth.type == dpkt.ethernet.ETH_TYPE_IP : 46 | ip = eth.data 47 | # If the IP protocol is UDP, then process it further 48 | if ip.p == dpkt.ip.IP_PROTO_UDP : 49 | udp = ip.data 50 | # Pass the IP addresses, source port, destination port, and data back to the caller. 51 | yield ( ip.src, udp.sport, ip.dst, udp.dport, udp.data) 52 | 53 | 54 | def decode_dns_response ( rr, response_type ) : 55 | """This subroutine decodes a DNS response packet. The packet may have more than one rr""" 56 | r_type = rr.type 57 | r_data = rr.rdata 58 | if rr.cls >32768: 59 | print 'Cache-Flush:1', 60 | rr.cls -= 32768 #MSB of rclass repurposed in mdns 61 | if rr.cls != 1 : 62 | print "Response class is %d, not class IN, might be Hesiod, chaos, or qclass (all of which are anachronisms)"%rr.cls 63 | print "Response Type:", response_type ,r_type,type_table[r_type], 64 | if r_type == dpkt.dns.DNS_CNAME : 65 | print "CNAME ", r_data," in hex: ", hexify(r_data) 66 | elif r_type == dpkt.dns.DNS_A : #for mdns response print host name too 67 | print "IPv4 address", socket.inet_ntoa( r_data ),' for ',rr.name 68 | elif r_type == dpkt.dns.DNS_NS : 69 | #print "Response is a NS name", r_data," in hex: ", hexify(r_data) 70 | print "Response is a NS name", rr.nsname 71 | elif r_type == dpkt.dns.DNS_AAAA : 72 | print "IPv6 address", socket.inet_ntop( socket.AF_INET6, r_data ),' for ',rr.name 73 | elif r_type == dpkt.dns.DNS_PTR : 74 | # 75 | print 'Name ',rr.ptrname,"for IPv4 address", rr.name 76 | elif r_type == dpkt.dns.DNS_SOA : 77 | print rr.mname,rr.rname,rr.serial,rr.refresh,rr.retry,rr.expire, rr.minimum 78 | elif r_type == dpkt.dns.DNS_MX : 79 | print rr.mxname,rr.preference 80 | elif r_type == dpkt.dns.DNS_HINFO : 81 | print rr.text 82 | elif r_type == dpkt.dns.DNS_TXT : 83 | print "TEXT",rr.text 84 | elif r_type == dpkt.dns.DNS_SRV : 85 | print rr.srvname,rr.port,rr.priority,rr.weight 86 | else : 87 | print "Response type is something other than a CNAME, PTR, IPv4 address, or IPv6 address", r_type, 88 | if r_type in type_table : 89 | print type_table[r_type] 90 | print "r-data is ",'%r'% r_data," in hex: ", hexify(r_data) 91 | else : 92 | print "Unknown" 93 | 94 | def print_hdr(dns): 95 | print 'HDR: id=%5d op=%5d Q/R=%1d AA:%1d TC:%1d RD:%1d RA:%1d opcode=%2d rcode=%2d QC=%2d AC=%2d NC=%2d AR%2d'%\ 96 | (dns.id,dns.op,dns.qr,(dns.op & dpkt.dns.DNS_AA)>>10,(dns.op & dpkt.dns.DNS_TC)>>9,(dns.op & dpkt.dns.DNS_RD)>>8,(dns.op & dpkt.dns.DNS_RA)>>7,dns.opcode,dns.rcode,len(dns.qd),len(dns.an),len(dns.ns),len(dns.ar)) 97 | 98 | 99 | def main() : 100 | # This code allows this program to run equally well on my laptop and my desktop. I did it this 101 | # way to demonstrate different interface names. If I was really clever, I'd figure out how to do this 102 | # under MS-Windows 103 | if sys.argv[1] == "-i" : 104 | pc = pcap.pcap( sys.argv[2] ) 105 | elif sys.argv[1] == "-f" : 106 | pc = dpkt.pcap.Reader( open ( sys.argv[2] ) ) 107 | else : 108 | print """Use -i INTERFACE to [packet capture from an interface. 109 | Use -f FILENAME to read a packet capture file""" 110 | sys.exit(2) 111 | initialize_tables() 112 | mdns_ip=socket.inet_aton('224.0.0.251'); 113 | 114 | for (src, sport, dst, dport, data ) in udp_iterator(pc) : 115 | # Uncomment if you want to see all UDP packets 116 | #print "from ", socket.inet_ntoa(src),":",sport, " to ", socket.inet_ntoa(dst),":",dport 117 | if src != mdns_ip and dst != mdns_ip and dport != 5353 and sport != 5353:continue 118 | assert dport == 5353 or sport == 5353 119 | dns = dpkt.dns.DNS(data) 120 | print '\n',datetime.datetime.now(), socket.inet_ntoa(src),":",sport, " to ", socket.inet_ntoa(dst),":",dport 121 | print_hdr(dns) 122 | #print dns.__repr__() 123 | #print '%r' % data 124 | if dns.qr == 0: 125 | #print "from ", socket.inet_ntoa(src),":",sport, " to ", socket.inet_ntoa(dst),":",dport,type(data),len(data),'%r' % data 126 | # UDP/53 is a DNS query 127 | if dns.opcode != dpkt.dns.DNS_QUERY : 128 | print "A DNS packet was sent to the nameserver, but the opcode was %d instead of DNS_QUERY (this is a software error)" % dns.opcode,'dns.id is',dns.id 129 | if dns.qr != dpkt.dns.DNS_Q : 130 | print "A DNS packet was sent to the name server, but dns.qr is not 0 and should be. It is %d" % dns.qr 131 | if (len(dns.qd)>0): 132 | for i in range(len(dns.qd)): 133 | print "query for ", dns.qd[i].name, "query type is ", dns.qd[i].type, type_table[dns.qd[i].type] 134 | else: 135 | print "query for ", '????' 136 | if dns.qr == 1 or len(dns.an)>0: 137 | # UDP/53 is a DNS response 138 | if dns.qr != dpkt.dns.DNS_R : #this is not an error, a query packet may contain answers 139 | print "A DNS packet was received from a name server, but dns.qr is not 1 and should be. It is %d" % dns.qr 140 | if dns.rcode == dpkt.dns.DNS_RCODE_NOERR : 141 | pass#print "no error", 142 | elif dns.rcode == dpkt.dns.DNS_RCODE_NXDOMAIN : 143 | print "There is no name in this domain" 144 | else : 145 | print "Response is something other than NOERR or NXDOMAIN %d - this software is incomplete" % dns.get_rcode() 146 | # Decode the RR records in the NS section 147 | for rr in dns.ns : 148 | decode_dns_response ( rr, "NS") 149 | # Decode the answers in the DNS answer 150 | i=0 151 | for rr in dns.an : 152 | #print 'RR[%d]:%r'%(i,rr);i +=1 153 | decode_dns_response ( rr, "AN" ) 154 | # Decode the additional responses 155 | for rr in dns.ar : 156 | decode_dns_response ( rr, "AR" ) 157 | 158 | 159 | if __name__ == "__main__" : 160 | main() 161 | 162 | -------------------------------------------------------------------------------- /decode_tcp_iterator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This module implements a TCP receiver. Refer to RFC 793 http://www.rfc-editor.org/rfc/rfc793.txt for details. 4 | 5 | import dpkt 6 | import sys 7 | import socket 8 | import pcap 9 | import struct 10 | 11 | def connection_id_to_str (cid, v=4) : 12 | """This converts the connection ID cid which is a tuple of (source_ip_address, source_tcp_port, destination_ip_address, 13 | destination_tcp_port) to a string. v is either 4 for IPv4 or 6 for IPv6""" 14 | if v == 4 : 15 | src_ip_addr_str = socket.inet_ntoa(cid[0]) 16 | dst_ip_addr_str = socket.inet_ntoa(cid[2]) 17 | return src_ip_addr_str + ":" + str(cid[1])+"=>"+dst_ip_addr_str + ":" + str(cid[3]) 18 | elif v == 6 : 19 | src_ip_addr_str = socket.inet_ntop(AF_INET6, cid[0]) 20 | dst_ip_addr_str = socket.inet_ntop(AF_INET6, cid[2]) 21 | return src_ip_addr_str + "." + str(cid[1])+"=>"+dst_ip_addr_str + "." + str(cid[3]) 22 | else : 23 | raise ValueError('Argument to connection_id_to_str must be 4 or 6, is %d' % v) 24 | 25 | class Connection_object : 26 | """A connection object stores the state of the tcp connection""" 27 | def __init__ ( self, isn, seq, string ) : 28 | self.isn = isn # initial sequence number. All sequence numbers are relative to this number. 29 | self.seq = seq # last sequence number seen. I'm not sure I need to keep this. 30 | self.buffer = { seq: string } # the keys are the relative sequence numbers, the values are the strings 31 | 32 | 33 | def assemble_buffer( buffer_dictionary ) : 34 | """The buffer dictionary contains segment numbers, which are byte offsets into the stream, and the bytes that the offsets point to. This 35 | function assembles the buffer into order. It should raise an exception if there is missing data - this is not implemented""" 36 | return_buffer = "" 37 | for segment in sorted( buffer_dictionary.keys() ) : 38 | read_end = len(return_buffer) 39 | if read_end+1 != segment : 40 | print "There is a segment missing between %d (already read) and %d (current segment beginning)" % ( read_end, segment ) 41 | return_buffer = return_buffer + buffer_dictionary[segment] 42 | return return_buffer 43 | 44 | def get_message_segment_size (options ) : 45 | """get the maximum segment size from the options list""" 46 | options_list = dpkt.tcp.parse_opts ( options ) 47 | for option in options_list : 48 | if option[0] == 2 : 49 | # The MSS is a 16 bit number. Look at RFC 793 http://www.rfc-editor.org/rfc/rfc793.txt page 17. dpkt decodes it as a 16 50 | # bit number. An MSS is never going to be bigger than 65496 bytes. 51 | # The most common value is 1460 bytes (IPv4) which 0x05b4 or 1440 bytes (IPv6) which is 0x05a0. 52 | mss = struct.unpack(">H", option[1]) 53 | return mss 54 | 55 | 56 | def decode_tcp(pcap): 57 | """This function decodes a packet capture file f and breaks it up into tcp connections""" 58 | print "counter\tsrc prt\tdst prt\tflags" 59 | packet_cntr = 0 60 | connection_table = {} # the keys of the table are the connection ID strings: source IP, 61 | # source port, destination IP, destination port. The values are a tuple which is the 62 | # sequence number and a string which is the assembled stream 63 | 64 | for ts, buf in pcap: 65 | packet_cntr += 1 66 | eth = dpkt.ethernet.Ethernet(buf) 67 | # Also, this changes a little bit with IPv6. To tell the difference between IPv4 and IPv6, you have to look 68 | # at the ethertype field, which is given by http://www.iana.org/assignments/ethernet-numbers. IPv4 is 0x800 or 2048 69 | # and IPv6 is 0x86DD or 34525 70 | # This is simplistic - IPv4 packets can be fragmented. Also, this only works for IPv4. IPv6 has a different Ethertype 71 | if eth.type == dpkt.ethernet.ETH_TYPE_IP : 72 | ip = eth.data 73 | if ip.v != 4 : 74 | raise ValueError, "In packet %d, the ether type is IPv4 but the IP version number is %d not 4" % ( 75 | packet_cntr, ip.v ) 76 | # Deal with IP fragmentation here 77 | elif eth.type == dpkt.ethernet.ETH_TYPE_IP6 : 78 | ip = eth.data 79 | if ip.v != 6 : 80 | raise ValueError, "In packet %d, the ether type is IPv6 but the IP version number is %d not 6" % ( 81 | packet_cntr, ip.v ) 82 | # IPv6 packets don't fragment 83 | else : 84 | print "packet %d is neither IPv4 nor IPv6" % packet_cntr 85 | continue # Not going to deal with anything other than IP 86 | if ip.p == dpkt.ip.IP_PROTO_TCP : 87 | tcp = ip.data 88 | fin_flag = ( tcp.flags & dpkt.tcp.TH_FIN ) != 0 89 | syn_flag = ( tcp.flags & dpkt.tcp.TH_SYN ) != 0 90 | rst_flag = ( tcp.flags & dpkt.tcp.TH_RST ) != 0 91 | psh_flag = ( tcp.flags & dpkt.tcp.TH_PUSH) != 0 92 | ack_flag = ( tcp.flags & dpkt.tcp.TH_ACK ) != 0 93 | urg_flag = ( tcp.flags & dpkt.tcp.TH_URG ) != 0 94 | ece_flag = ( tcp.flags & dpkt.tcp.TH_ECE ) != 0 95 | cwr_flag = ( tcp.flags & dpkt.tcp.TH_CWR ) != 0 96 | # The flags string is really for debugging 97 | flags = ( 98 | ( "C" if cwr_flag else " " ) + 99 | ( "E" if ece_flag else " " ) + 100 | ( "U" if urg_flag else " " ) + 101 | ( "A" if ack_flag else " " ) + 102 | ( "P" if psh_flag else " " ) + 103 | ( "R" if rst_flag else " " ) + 104 | ( "S" if syn_flag else " " ) + 105 | ( "F" if fin_flag else " " ) ) 106 | if syn_flag and not ack_flag : 107 | # Each TCP connection is forming. The new connection is stored as an object in a dictionary 108 | # whose key is the tuple (source_ip_address, source_tcp_port, destination_ip_address, destination_tcp_port) 109 | # The connection is stored in a dictionary. The key is the connection_id, value of each key is an object with fields for the 110 | # current connection state and the total of all the bytes that have been sent 111 | # Note that there are two connections, one from the client to the server and one from the server to the client. This becomes 112 | # important when the connection is closed, because one side might FIN the connection well before the other side does. 113 | connection_id = (ip.src, tcp.sport, ip.dst, tcp.dport) 114 | print "Forming a new connection " + connection_id_to_str( connection_id, ip.v ) + " Initial Sequence Number (ISN) is %d" % tcp.seq 115 | connection_table[connection_id] = Connection_object ( isn = tcp.seq, seq = tcp.seq, string = "" ) 116 | print "Message segment size client side is ", get_message_segment_size ( tcp.opts ) 117 | elif syn_flag and ack_flag : 118 | connection_id = (ip.src, tcp.sport, ip.dst, tcp.dport) 119 | print "Server responding to a new connection " + connection_id_to_str( connection_id, ip.v ) + " Initial Sequence Number (ISN) is %d" % tcp.seq 120 | connection_table[connection_id] = Connection_object ( isn = tcp.seq, seq = tcp.seq, string = "" ) 121 | print "Message segment size client side is ", get_message_segment_size ( tcp.opts ) 122 | 123 | # This is where I am having a little confusion. My instinct tells me that the connection from the client to the server and the 124 | # connection from the server back to the client should be connected somehow. But they aren't, except for the SYN-ACK 125 | # packet. Otherwise, the source IP, destination IP, source port and destination port are mirror images, but the streams 126 | # are separate. The acknowlegement numbers are related, but we don't need to worry about acknowlegements 127 | elif not syn_flag and ack_flag : 128 | sequence_number = tcp.seq 129 | byte_offset = sequence_number - connection_table[connection_id].isn 130 | print flags+" Absolute sequence number %d ISN %d relative sequence number %d" % (sequence_number, connection_table[connection_id].isn, byte_offset) 131 | connection_table[connection_id].buffer[byte_offset] = tcp.data 132 | connection_table[connection_id].seq = sequence_number 133 | # if the push flag is set, then return the string to the caller, along with identifying information so that the 134 | # caller knows which connection is getting data returned. 135 | if psh_flag or urg_flag : 136 | connection_string = assemble_buffer( connection_table[connection_id].buffer ) 137 | yield ( connection_id, connection_string ) 138 | 139 | def decode_tcp_help() : 140 | print """To run this program, give the command 141 | python """+sys.argv[0]+""" FILENAME.py INPUT 142 | where INPUT is either a PCAP file, or eth0: for a wired ethernet or wlan0: for 143 | a wireless (WiFi) network connection""" 144 | sys.exit(1) 145 | 146 | 147 | def main(pc) : 148 | """This is the outer loop that prints strings that have been captured from the TCP streams, terminated by a packet that 149 | has the PUSH flag set.""" 150 | for connection_id, received_string in decode_tcp(pc) : 151 | print connection_id_to_str (connection_id, 4), received_string # This won't work for IPv6 152 | 153 | 154 | 155 | if __name__ == "__main__" : 156 | if len(sys.argv) < 2 : 157 | decode_tcp_help() 158 | # create an interator to return the next packet. The source can be either an interface using the libpcap library or it can be a file in pcap 159 | # format such as created by tcpdump. 160 | if sys.argv[1] == "eth0" : # wired ethernet interface 161 | pc = pcap.pcap("eth0", promisc=True ) 162 | elif sys.argv[1] == "wlan0" : # wireless interface 163 | pc = pcap.pcap("wlan0", promisc=True ) 164 | else : 165 | pc = dpkt.pcap.Reader ( open(sys.argv[1] ) ) # file interface 166 | main(pc) 167 | 168 | -------------------------------------------------------------------------------- /decode_tcp_iterator_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This module implements a TCP receiver. Refer to RFC 793 http://www.rfc-editor.org/rfc/rfc793.txt for details. 4 | 5 | import dpkt 6 | import sys 7 | import socket 8 | import pcap 9 | import struct 10 | 11 | def connection_id_to_str (cid, v=4) : 12 | """This converts the connection ID cid which is a tuple of (source_ip_address, source_tcp_port, destination_ip_address, 13 | destination_tcp_port) to a string. v is either 4 for IPv4 or 6 for IPv6""" 14 | if v == 4 : 15 | src_ip_addr_str = socket.inet_ntoa(cid[0]) 16 | dst_ip_addr_str = socket.inet_ntoa(cid[2]) 17 | return src_ip_addr_str + ":" + str(cid[1])+"=>"+dst_ip_addr_str + ":" + str(cid[3]) 18 | elif v == 6 : 19 | src_ip_addr_str = socket.inet_ntop(socket.AF_INET6, cid[0]) 20 | dst_ip_addr_str = socket.inet_ntop(socket.AF_INET6, cid[2]) 21 | return src_ip_addr_str + "." + str(cid[1])+"=>"+dst_ip_addr_str + "." + str(cid[3]) 22 | else : 23 | raise ValueError('Argument to connection_id_to_str must be 4 or 6, is %d' % v) 24 | 25 | class Connection_object : 26 | """A connection object stores the state of the tcp connection""" 27 | def __init__ ( self, isn, seq, string ) : 28 | self.isn = isn # initial sequence number. All sequence numbers are relative to this number. 29 | self.seq = seq # last sequence number seen. I'm not sure I need to keep this. 30 | self.buffer = { seq: string } # the keys are the relative sequence numbers, the values are the strings 31 | 32 | class ProtocolException(Exception): 33 | """Raise this exception if a protocol error is detected, although it is more likely that my software is broken""" 34 | def __init__(self, value): 35 | self.parameter = value 36 | def __str__(self): 37 | return repr(self.parameter) 38 | 39 | def assemble_buffer( buffer_dictionary ) : 40 | """The buffer dictionary contains segment numbers, which are byte offsets into the stream, and the bytes that the offsets point to. This 41 | function assembles the buffer into order. It should raise an exception if there is missing data - this is not implemented""" 42 | return_buffer = "" 43 | for segment in sorted( buffer_dictionary.keys() ) : 44 | read_end = len(return_buffer) 45 | if read_end+1 != segment : 46 | print "There is a segment missing between %d (already read) and %d (current segment beginning)" % ( read_end, segment ) 47 | return_buffer = return_buffer + buffer_dictionary[segment] 48 | return return_buffer 49 | 50 | def get_message_segment_size (options ) : 51 | """get the maximum segment size from the options list""" 52 | options_list = dpkt.tcp.parse_opts ( options ) 53 | for option in options_list : 54 | if option[0] == 2 : 55 | # The MSS is a 16 bit number dpkt decodes it as a 16 56 | # bit number. An MSS is never going to be bigger than 65496 bytes. 57 | # The most common value is 1460 bytes (IPv4) which 0x05b4 or 1440 bytes (IPv6) which is 0x05a0. The format string ">H" means 58 | # big-endian unsigned 16 bit number. It should be ">L" which is big-endian 32 bit number. 59 | mss = struct.unpack(">H", option[1]) 60 | return mss 61 | 62 | def unpack_tcp_packet ( tcp ) : 63 | """tcp packets are the same between IPv4 and IPv6. This function handles decoding tcp packets""" 64 | 65 | # We need to get more packets, so return to decode_tcp 66 | return 67 | 68 | def decode_tcp(pcap): 69 | """This function decodes a packet capture file pcap and breaks it up into tcp connections""" 70 | print "counter\tsrc prt\tdst prt\tflags" 71 | packet_cntr = 0 72 | connection_table = {} # the keys of the table are the connection ID strings: source IP, 73 | # source port, destination IP, destination port. The values are a tuple which is the 74 | # sequence number and a string which is the assembled stream 75 | 76 | for ts, buf in pcap: 77 | packet_cntr += 1 78 | eth = dpkt.ethernet.Ethernet(buf) 79 | # Also, this changes a little bit with IPv6. To tell the difference between IPv4 and IPv6, you have to look 80 | # at the ethertype field, which is given by http://www.iana.org/assignments/ethernet-numbers. IPv4 is 0x800 or 2048 81 | # and IPv6 is 0x86DD or 34525 82 | # This is simplistic - IPv4 packets can be fragmented. Also, this only works for IPv4. IPv6 has a different Ethertype 83 | # Part of the genius of dpkt is that if you have an ethernet packet with an IPv4 payload, you get an dpkt.ip object, but if you 84 | # have an ethernet packet with an IPv6 payload, then you get an dpkt.ip6 object, which has different fields names. 85 | # Note how similar IPv4 and IPv6 are. 86 | if eth.type == dpkt.ethernet.ETH_TYPE_IP : 87 | ip = eth.data 88 | if ip.v != 4 : 89 | raise ValueError, "In packet %d, the ether type is IPv4 but the IP version number is %d not 4" % ( 90 | packet_cntr, ip.v ) 91 | # Deal with IP fragmentation here 92 | if ip.p == dpkt.ip.IP_PROTO_TCP : 93 | tcp = ip.data 94 | else : 95 | # Some other protocol than TCP, such as UDP. See http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml. 96 | print "packet %d is IPv6 but not TCP. Orotocol field is %d" % (packet_cntr, ip.p ) 97 | continue 98 | elif eth.type == dpkt.ethernet.ETH_TYPE_IP6 : 99 | ip = eth.data 100 | if ip.v != 6 : 101 | raise ValueError, "In packet %d, the ether type is IPv6 but the IP version number is %d not 6" % ( 102 | packet_cntr, ip6.v ) 103 | # IPv6 packets don't fragment 104 | if ip.nxt == dpkt.ip.IP_PROTO_TCP : # The ip6.nxt field in IPv6 is similar to the IPv4 ip.p field 105 | tcp = ip.data 106 | else : 107 | # Some other protocol than TCP, such as UDP. See http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml. 108 | print "packet %d is IPv6 but not TCP. Next header field is %d" % (packet_cntr, ip.nxt ) 109 | continue 110 | else : 111 | print "packet %d is neither IPv4 nor IPv6" % packet_cntr 112 | continue # Not going to deal with anything other than IP 113 | # At this point, we have a TCP packet, which is independent of IPv4 or IPv6 (except we need the source and destination addresses). Form a 114 | # connection ID so we know where to store this packet. 115 | connection_id = (ip.src, tcp.sport, ip.dst, tcp.dport) 116 | fin_flag = ( tcp.flags & dpkt.tcp.TH_FIN ) != 0 117 | syn_flag = ( tcp.flags & dpkt.tcp.TH_SYN ) != 0 118 | rst_flag = ( tcp.flags & dpkt.tcp.TH_RST ) != 0 119 | psh_flag = ( tcp.flags & dpkt.tcp.TH_PUSH) != 0 120 | ack_flag = ( tcp.flags & dpkt.tcp.TH_ACK ) != 0 121 | urg_flag = ( tcp.flags & dpkt.tcp.TH_URG ) != 0 122 | ece_flag = ( tcp.flags & dpkt.tcp.TH_ECE ) != 0 123 | cwr_flag = ( tcp.flags & dpkt.tcp.TH_CWR ) != 0 124 | # The flags string is really for debugging 125 | flags = ( 126 | ( "C" if cwr_flag else " " ) + 127 | ( "E" if ece_flag else " " ) + 128 | ( "U" if urg_flag else " " ) + 129 | ( "A" if ack_flag else " " ) + 130 | ( "P" if psh_flag else " " ) + 131 | ( "R" if rst_flag else " " ) + 132 | ( "S" if syn_flag else " " ) + 133 | ( "F" if fin_flag else " " ) ) 134 | if syn_flag and not ack_flag : 135 | # Each TCP connection is forming. The new connection is stored as an object in a dictionary 136 | # whose key is the tuple (source_ip_address, source_tcp_port, destination_ip_address, destination_tcp_port) 137 | # The connection is stored in a dictionary. The key is the connection_id, value of each key is an object with fields for the 138 | # current connection state and the total of all the bytes that have been sent 139 | # Note that there are two connections, one from the client to the server and one from the server to the client. This becomes 140 | # important when the connection is closed, because one side might FIN the connection well before the other side does. 141 | print "Forming a new connection " + connection_id_to_str( connection_id, ip.v ) + " Initial Sequence Number (ISN) is %d" % tcp.seq 142 | connection_table[connection_id] = Connection_object ( isn = tcp.seq, seq = tcp.seq, string = "" ) 143 | print "Message segment size client side is ", get_message_segment_size ( tcp.opts ) 144 | elif syn_flag and ack_flag : 145 | print "Server responding to a new connection " + connection_id_to_str( connection_id, ip.v ) + " Initial Sequence Number (ISN) is %d" % tcp.seq 146 | connection_table[connection_id] = Connection_object ( isn = tcp.seq, seq = tcp.seq, string = "" ) 147 | print "Message segment size client side is ", get_message_segment_size ( tcp.opts ) 148 | 149 | # This is where I am having a little confusion. My instinct tells me that the connection from the client to the server and the 150 | # connection from the server back to the client should be connected somehow. But they aren't, except for the SYN-ACK 151 | # packet. Otherwise, the source IP, destination IP, source port and destination port are mirror images, but the streams 152 | # are separate. The acknowlegement numbers are related, but we don't need to worry about acknowlegements 153 | # Technically, I don't need to test for the ACK flag since it always set. 154 | elif not syn_flag and ack_flag : 155 | sequence_number = tcp.seq 156 | byte_offset = sequence_number - connection_table[connection_id].isn 157 | print flags+" Absolute sequence number %d ISN %d relative sequence number %d" % (sequence_number, connection_table[connection_id].isn, byte_offset) 158 | connection_table[connection_id].buffer[byte_offset] = tcp.data 159 | connection_table[connection_id].seq = sequence_number 160 | # if the push flag or urg flag is set, then return the string to the caller, along with identifying information so that the 161 | # caller knows which connection is getting data returned. 162 | if psh_flag or urg_flag : 163 | connection_string = assemble_buffer( connection_table[connection_id].buffer ) 164 | yield ( connection_id, connection_string, ip.v ) 165 | else : 166 | # syn_flag is clear and ack_flag is clear. This is probably a software in my software. 167 | raise ProtocolException ( "In packet %d, SYN is clear and ACK is clear. This is terrible" % packet_cntr ) 168 | 169 | 170 | def main(pc) : 171 | """This is the outer loop that prints strings that have been captured from the TCP streams, terminated by a packet that 172 | has the PUSH flag set.""" 173 | for connection_id, received_string, ip_version in decode_tcp(pc) : 174 | print connection_id_to_str (connection_id, ip_version), received_string 175 | 176 | 177 | 178 | if __name__ == "__main__" : 179 | if len(sys.argv) == 3 : 180 | if sys.argv[1] == "-i" : 181 | # create an interator to return the next packet. The source can be either an interface using the libpcap library or it can be a file in pcap 182 | # format such as created by tcpdump. 183 | pc = pcap.pcap( sys.argv[2] ) 184 | elif sys.argv[1] == "-f" : 185 | pc = dpkt.pcap.Reader( open ( sys.argv[2] ) ) 186 | else : 187 | print """Use -i INTERFACE to packet capture from an interface. 188 | Use -f FILENAME to read a packet capture file""" 189 | sys.exit(2) 190 | else : 191 | print """Use -i INTERFACE to packet capture from an interface. 192 | Use -f FILENAME to read a packet capture file""" 193 | sys.exit(2) 194 | 195 | main(pc) 196 | 197 | -------------------------------------------------------------------------------- /decode_tcp_iterator_2P.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This module implements a TCP receiver. Refer to RFC 793 http://www.rfc-editor.org/rfc/rfc793.txt for details. 4 | # This is a production version, with all the debugging print statements removed 5 | 6 | import dpkt 7 | import sys 8 | import socket 9 | import pcap 10 | import struct 11 | 12 | def connection_id_to_str (cid, v=4) : 13 | """This converts the connection ID cid which is a tuple of (source_ip_address, source_tcp_port, destination_ip_address, 14 | destination_tcp_port) to a string. v is either 4 for IPv4 or 6 for IPv6""" 15 | if v == 4 : 16 | src_ip_addr_str = socket.inet_ntoa(cid[0]) 17 | dst_ip_addr_str = socket.inet_ntoa(cid[2]) 18 | return src_ip_addr_str + ":" + str(cid[1])+"=>"+dst_ip_addr_str + ":" + str(cid[3]) 19 | elif v == 6 : 20 | src_ip_addr_str = socket.inet_ntop(socket.AF_INET6, cid[0]) 21 | dst_ip_addr_str = socket.inet_ntop(socket.AF_INET6, cid[2]) 22 | return src_ip_addr_str + "." + str(cid[1])+"=>"+dst_ip_addr_str + "." + str(cid[3]) 23 | else : 24 | raise ValueError('Argument to connection_id_to_str must be 4 or 6, is %d' % v) 25 | 26 | class Connection_object : 27 | """A connection object stores the state of the tcp connection""" 28 | def __init__ ( self, isn, seq, string ) : 29 | self.isn = isn # initial sequence number. All sequence numbers are relative to this number. 30 | self.seq = seq # last sequence number seen. I'm not sure I need to keep this. 31 | self.buffer = { seq: string } # the keys are the relative sequence numbers, the values are the strings 32 | 33 | class ProtocolException(Exception): 34 | """Raise this exception if a protocol error is detected, although it is more likely that my software is broken""" 35 | def __init__(self, value): 36 | self.parameter = value 37 | def __str__(self): 38 | return repr(self.parameter) 39 | 40 | def assemble_buffer( buffer_dictionary ) : 41 | """The buffer dictionary contains segment numbers, which are byte offsets into the stream, and the bytes that the offsets point to. This 42 | function assembles the buffer into order. It should raise an exception if there is missing data - this is not implemented""" 43 | return_buffer = "" 44 | for segment in sorted( buffer_dictionary.keys() ) : 45 | read_end = len(return_buffer) 46 | if read_end+1 != segment : 47 | print "There is a segment missing between %d (already read) and %d (current segment beginning)" % ( read_end, segment ) 48 | return_buffer = return_buffer + buffer_dictionary[segment] 49 | return return_buffer 50 | 51 | def get_message_segment_size (options ) : 52 | """get the maximum segment size from the options list""" 53 | options_list = dpkt.tcp.parse_opts ( options ) 54 | for option in options_list : 55 | if option[0] == 2 : 56 | # The MSS is a 16 bit number dpkt decodes it as a 16 57 | # bit number. An MSS is never going to be bigger than 65496 bytes. 58 | # The most common value is 1460 bytes (IPv4) which 0x05b4 or 1440 bytes (IPv6) which is 0x05a0. The format string ">H" means 59 | # big-endian unsigned 16 bit number. It should be ">L" which is big-endian 32 bit number. 60 | mss = struct.unpack(">H", option[1]) 61 | return mss 62 | 63 | def unpack_tcp_packet ( tcp ) : 64 | """tcp packets are the same between IPv4 and IPv6. This function handles decoding tcp packets""" 65 | 66 | # We need to get more packets, so return to decode_tcp 67 | return 68 | 69 | def decode_tcp(pcap): 70 | """This function decodes a packet capture file pcap and breaks it up into tcp connections""" 71 | # print "counter\tsrc prt\tdst prt\tflags" 72 | packet_cntr = 0 73 | connection_table = {} # the keys of the table are the connection ID strings: source IP, 74 | # source port, destination IP, destination port. The values are a tuple which is the 75 | # sequence number and a string which is the assembled stream 76 | 77 | for ts, buf in pcap: 78 | packet_cntr += 1 79 | eth = dpkt.ethernet.Ethernet(buf) 80 | # Also, this changes a little bit with IPv6. To tell the difference between IPv4 and IPv6, you have to look 81 | # at the ethertype field, which is given by http://www.iana.org/assignments/ethernet-numbers. IPv4 is 0x800 or 2048 82 | # and IPv6 is 0x86DD or 34525 83 | # This is simplistic - IPv4 packets can be fragmented. Also, this only works for IPv4. IPv6 has a different Ethertype 84 | # Part of the genius of dpkt is that if you have an ethernet packet with an IPv4 payload, you get an dpkt.ip object, but if you 85 | # have an ethernet packet with an IPv6 payload, then you get an dpkt.ip6 object, which has different fields names. 86 | # Note how similar IPv4 and IPv6 are. 87 | if eth.type == dpkt.ethernet.ETH_TYPE_IP : 88 | ip = eth.data 89 | if ip.v != 4 : 90 | raise ValueError, "In packet %d, the ether type is IPv4 but the IP version number is %d not 4" % ( 91 | packet_cntr, ip.v ) 92 | # Deal with IP fragmentation here 93 | if ip.p == dpkt.ip.IP_PROTO_TCP : 94 | tcp = ip.data 95 | else : 96 | # Some other protocol than TCP, such as UDP. See http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml. 97 | print "packet %d is IPv6 but not TCP. Protocol field is %d" % (packet_cntr, ip.p ) 98 | continue 99 | elif eth.type == dpkt.ethernet.ETH_TYPE_IP6 : 100 | ip = eth.data 101 | if ip.v != 6 : 102 | raise ValueError, "In packet %d, the ether type is IPv6 but the IP version number is %d not 6" % ( 103 | packet_cntr, ip6.v ) 104 | # IPv6 packets don't fragment 105 | if ip.nxt == dpkt.ip.IP_PROTO_TCP : # The ip6.nxt field in IPv6 is similar to the IPv4 ip.p field 106 | tcp = ip.data 107 | else : 108 | # Some other protocol than TCP, such as UDP. See http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml. 109 | print "packet %d is IPv6 but not TCP. Next header field is %d" % (packet_cntr, ip.nxt ) 110 | continue 111 | else : 112 | print "packet %d is neither IPv4 nor IPv6" % packet_cntr 113 | continue # Not going to deal with anything other than IP 114 | # At this point, we have a TCP packet, which is independent of IPv4 or IPv6 (except we need the source and destination addresses). Form a 115 | # connection ID so we know where to store this packet. 116 | connection_id = (ip.src, tcp.sport, ip.dst, tcp.dport) 117 | fin_flag = ( tcp.flags & dpkt.tcp.TH_FIN ) != 0 118 | syn_flag = ( tcp.flags & dpkt.tcp.TH_SYN ) != 0 119 | rst_flag = ( tcp.flags & dpkt.tcp.TH_RST ) != 0 120 | psh_flag = ( tcp.flags & dpkt.tcp.TH_PUSH) != 0 121 | ack_flag = ( tcp.flags & dpkt.tcp.TH_ACK ) != 0 122 | urg_flag = ( tcp.flags & dpkt.tcp.TH_URG ) != 0 123 | ece_flag = ( tcp.flags & dpkt.tcp.TH_ECE ) != 0 124 | cwr_flag = ( tcp.flags & dpkt.tcp.TH_CWR ) != 0 125 | # The flags string is really for debugging 126 | flags = ( 127 | ( "C" if cwr_flag else " " ) + 128 | ( "E" if ece_flag else " " ) + 129 | ( "U" if urg_flag else " " ) + 130 | ( "A" if ack_flag else " " ) + 131 | ( "P" if psh_flag else " " ) + 132 | ( "R" if rst_flag else " " ) + 133 | ( "S" if syn_flag else " " ) + 134 | ( "F" if fin_flag else " " ) ) 135 | if syn_flag and not ack_flag : 136 | # Each TCP connection is forming. The new connection is stored as an object in a dictionary 137 | # whose key is the tuple (source_ip_address, source_tcp_port, destination_ip_address, destination_tcp_port) 138 | # The connection is stored in a dictionary. The key is the connection_id, value of each key is an object with fields for the 139 | # current connection state and the total of all the bytes that have been sent 140 | # Note that there are two connections, one from the client to the server and one from the server to the client. This becomes 141 | # important when the connection is closed, because one side might FIN the connection well before the other side does. 142 | print "Forming a new connection " + connection_id_to_str( connection_id, ip.v ) + " Initial Sequence Number (ISN) is %d" % tcp.seq 143 | connection_table[connection_id] = Connection_object ( isn = tcp.seq, seq = tcp.seq, string = "" ) 144 | print "Message segment size client side is ", get_message_segment_size ( tcp.opts ) 145 | elif syn_flag and ack_flag : 146 | print "Server responding to a new connection " + connection_id_to_str( connection_id, ip.v ) + " Initial Sequence Number (ISN) is %d" % tcp.seq 147 | connection_table[connection_id] = Connection_object ( isn = tcp.seq, seq = tcp.seq, string = "" ) 148 | print "Message segment size client side is ", get_message_segment_size ( tcp.opts ) 149 | 150 | # This is where I am having a little confusion. My instinct tells me that the connection from the client to the server and the 151 | # connection from the server back to the client should be connected somehow. But they aren't, except for the SYN-ACK 152 | # packet. Otherwise, the source IP, destination IP, source port and destination port are mirror images, but the streams 153 | # are separate. The acknowlegement numbers are related, but we don't need to worry about acknowlegements 154 | # Technically, I don't need to test for the ACK flag since it always set. 155 | elif not syn_flag and ack_flag : 156 | sequence_number = tcp.seq 157 | byte_offset = sequence_number - connection_table[connection_id].isn 158 | # print flags+" Absolute sequence number %d ISN %d relative sequence number %d" % (sequence_number, connection_table[connection_id].isn, byte_offset) 159 | connection_table[connection_id].buffer[byte_offset] = tcp.data 160 | connection_table[connection_id].seq = sequence_number 161 | # if the push flag or urg flag is set, then return the string to the caller, along with identifying information so that the 162 | # caller knows which connection is getting data returned. 163 | if psh_flag or urg_flag : 164 | connection_string = assemble_buffer( connection_table[connection_id].buffer ) 165 | yield ( connection_id, connection_string, ip.v ) 166 | else : 167 | # syn_flag is clear and ack_flag is clear. This is probably a software in my software. 168 | raise ProtocolException ( "In packet %d, SYN is clear and ACK is clear. This is terrible" % packet_cntr ) 169 | 170 | 171 | def main(pc) : 172 | """This is the outer loop that prints strings that have been captured from the TCP streams, terminated by a packet that 173 | has the PUSH flag set.""" 174 | for connection_id, received_string, ip_version in decode_tcp(pc) : 175 | print connection_id_to_str (connection_id, ip_version), received_string 176 | 177 | 178 | 179 | if __name__ == "__main__" : 180 | if len(sys.argv) == 3 : 181 | if sys.argv[1] == "-i" : 182 | # create an interator to return the next packet. The source can be either an interface using the libpcap library or it can be a file in pcap 183 | # format such as created by tcpdump. 184 | pc = pcap.pcap( sys.argv[2] ) 185 | elif sys.argv[1] == "-f" : 186 | pc = dpkt.pcap.Reader( open ( sys.argv[2] ) ) 187 | else : 188 | print """Use -i INTERFACE to packet capture from an interface. 189 | Use -f FILENAME to read a packet capture file""" 190 | sys.exit(2) 191 | else : 192 | print """Use -i INTERFACE to packet capture from an interface. 193 | Use -f FILENAME to read a packet capture file""" 194 | sys.exit(2) 195 | 196 | main(pc) 197 | 198 | -------------------------------------------------------------------------------- /dpkt_notes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | dpkt notes 7 | 8 | 9 |

Jeff Silverman's dpkt documentation

10 | This page is a notebook of things I have learned about dpkt.
11 |
12 |

HTTP response bodies

13 | Status: New 14 |
15 | Owner: ---- 16 |
17 | Labels: Type-Defect Priority-Medium 18 |
19 |
20 | New issue 69 by cuiheng....@gmail.com: is 22 | dpkt able to parse the HTTP response ???? 23 |
24 | http://code.google.com/p/dpkt/issues/detail?id=69 26 |
27 |
28 | Hi, 29 |
30 |
31 |    I am currently using dpkt to parse libpcap format file. 32 | What I want to do is to extract the raw content of the HTTP response. 33 | However, I found my script ONLY works for the HTTP responses that have 34 | small content length (<1500Byte).
35 | Updates: 36 |
37 |     Status: WontFix 38 |
39 |
40 | Comment #2 on issue 69 by dugsong: is dpkt able to parse the HTTP 41 | response ???? 42 |
43 | http://code.google.com/p/dpkt/issues/detail?id=69 45 |
46 |
47 | dpkt doesn't do any TCP stream reassembly. You need to do that 48 | yourself. 49 |
50 |
51 | Here's an example: 52 |
53 |
54 |     http://code.google.com/p/dsniff/source/browse/trunk/dsniff/lib/reasm.py 56 |
57 |
58 | If you're doing it live, you need to do your own stream parsing 59 | instead, e.g. dsniff's HTTP stream parser: 60 |
61 |
62 |     http://code.google.com/p/dsniff/source/browse/trunk/dsniff/lib/http.py 64 |
65 |
66 | Good luck! 67 |
68 |

Using dpkt to decode HTTP headers

69 | Oversimplifying outrageously, you can do something like this:
70 |
71 |
    eth = dpkt.ethernet.Ethernet(buf)
ip = eth.data
tcp = ip.data

# If the destination port is 80 and there is data in the packet, then probably this is an HTTP request. TO be certain, there should
# be a TCP finite state machine in here.
if tcp.dport == 80 and len(tcp.data) > 0:
http_req = dpkt.http.Request(tcp.data)

72 |

The dpkt.http.Request object has the following attributes:
73 |

74 |
['_Request__methods', '_Request__proto', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__hash__', '__hdr_defaults__', '__init__', '__len__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'body', 'data', 'headers', 'method', 'pack', 'pack_hdr', 'unpack', 'uri', 'version']
(Pdb) print http.method
GET
(Pdb) print http.body

(Pdb) print http.headers
{'accept-language': 'en-us,en;q=0.5', 'accept-encoding': 'gzip,deflate', 'connection': 'keep-alive', 'keep-alive': '115', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'user-agent': 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.16) Gecko/20110323 Ubuntu/10.04 (lucid) Firefox/3.6.16', 'accept-charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'host': 'www.commercialventvac.com'}
(Pdb) print http.pack
<bound method Request.pack of Request(version='1.1')>
(Pdb) print http.pack()
GET / HTTP/1.1
accept-language: en-us,en;q=0.5
accept-encoding: gzip,deflate
connection: keep-alive
keep-alive: 115
accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
user-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.16) Gecko/20110323 Ubuntu/10.04 (lucid) Firefox/3.6.16
accept-charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
host: www.commercialventvac.com


(Pdb) print http.pack_hdr()
accept-language: en-us,en;q=0.5
accept-encoding: gzip,deflate
connection: keep-alive
keep-alive: 115
accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
user-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.16) Gecko/20110323 Ubuntu/10.04 (lucid) Firefox/3.6.16
accept-charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
host: www.commercialventvac.com

(Pdb)

75 |

dsniff has a tcp decoder, I think.

76 | Except I can't find it.  It is available for downloading from 77 | ubuntu.  Find the source code.
78 |
79 |

SCTP Stream Control Transmission Protocol

80 | Get the source code from https://github.com/philpraxis/pysctp.git.  81 | In 82 | order 83 | to 84 | build the C software, I need the package libsctp-dev, which 85 | I installed using synaptic.  The installation script puts the file 86 | _sctp.h in /usr/local.  It should go 88 | in /usr/local/include.  89 | Fixed 90 | that.  91 | pysctp 92 | comes with two SCTP clients, but no SCTP 93 | servers, so it is difficult to test.  I sent a message to the 94 | author of the software, Philippe 95 | Langlois <Phil __AT__ p1sec.com> .
96 |
97 |

98 |

An analysis of the file http_ipv4.cap

99 |

100 | I think the decode_tcp_iterator.py file is not doing the sequence 101 | numbers correctly. The ack numbers that are n/a are because the ACK 102 | flag is clear. 103 |

104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 175 | 176 | 178 | 179 | 180 | 181 | 183 | 185 | 187 | 189 | 191 | 193 | 194 | 195 | 197 | 199 | 201 | 203 | 205 | 207 | 208 | 209 | 211 | 213 | 215 | 217 | 219 | 221 | 222 | 223 | 225 | 227 | 229 | 231 | 233 | 235 | 236 | 237 | 239 | 241 | 243 | 245 | 247 | 249 | 250 | 251 |
Packetdirectionabsolute seq numrelative seq numabsolute ack numrelative ack num
1C->S9881848210n/an/a
2S->C217193197219881848221
3C->S988184822121719319721
4C->S9881848221112171931972111
5S->C217193197312171931973111
6C->S9881848321112171932883911
7S->C21719328839119881849322339
8C->S 988184932
174 |
9112171934311
177 |
2339
9
182 |
S->C
184 |
2171934311
186 |
2339
188 |
988184932
190 |
3391
192 |
10
196 |
C->S
198 |
988184932
200 |
3391
202 |
2171935363
204 |
111
206 |
11
210 |
S->C (fin)
212 |
2171935363
214 |
111
216 |
988184932
218 |
3392
220 |
12
224 |
C->S (fin)
226 |
988184932
228 |
3392
230 |
2171935364
232 |
112
234 |
13
238 |
S->C
240 |
2171935364
242 |
3392
244 |
988184933
246 |
112
248 |
252 |

Troubles decoding IPv6

253 | I am having troubles getting a capture file that contains IPv6 254 | packets.  I think that the files from tcpdump have a disagreement 255 | with libpcap about something:
256 |
257 |
jeffs@heavy:~/python/PythonClass/dpkt_doc$ python -m pdb decode_tcp_iterator_2.py http_ipv6.cap 
> /home/jeffs/python/PythonClass/dpkt_doc/decode_tcp_iterator_2.py(5)<module>()
-> import dpkt
(Pdb) c
Traceback (most recent call last):
File "/usr/lib/python2.6/pdb.py", line 1285, in main
pdb._runscript(mainpyfile)
File "/usr/lib/python2.6/pdb.py", line 1204, in _runscript
self.run(statement)
File "/usr/lib/python2.6/bdb.py", line 368, in run
exec cmd in globals, locals
File "<string>", line 1, in <module>
File "decode_tcp_iterator_2.py", line 159, in <module>
pc = dpkt.pcap.Reader ( open(sys.argv[1] ) ) # file interface
File "dpkt/pcap.py", line 105, in __init__
self.dloff = dltoff[self.__fh.linktype]
KeyError: 101
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> /home/jeffs/python/PythonClass/dpkt_doc/dpkt/pcap.py(105)__init__()
-> self.dloff = dltoff[self.__fh.linktype]
(Pdb) print self.__fh.linktype
*** AttributeError: 'Reader' object has no attribute '__fh'
(Pdb) print self.__fh.linktype
*** AttributeError: 'Reader' object has no attribute '__fh'
(Pdb)

258 | If I use a capture file from wireshark, it gets past this point, but 259 | then it has a bad value for the ethertype:
260 |
261 |
jeffs@heavy:~/python/PythonClass/dpkt_doc$ python -m pdb decode_tcp_iterator_2.py http_ipv6_wireshark_2.cap 
> /home/jeffs/python/PythonClass/dpkt_doc/decode_tcp_iterator_2.py(5)<module>()
-> import dpkt
(Pdb) list 72
67 eth = dpkt.ethernet.Ethernet(buf)
68 # Also, this changes a little bit with IPv6. To tell the difference between IPv4 and IPv6, you have to look
69 # at the ethertype field, which is given by http://www.iana.org/assignments/ethernet-numbers. IPv4 is 0x800 or 2048
70 # and IPv6 is 0x86DD or 34525
71 # This is simplistic - IPv4 packets can be fragmented. Also, this only works for IPv4. IPv6 has a different Ethertype
72 if eth.type == dpkt.ethernet.ETH_TYPE_IP :
73 ip = eth.data
74 if ip.v != 4 :
75 raise ValueError, "In packet %d, the ether type is IPv4 but the IP version number is %d not 4" % (
76 packet_cntr, ip.v )
77 # Deal with IP fragmentation here
(Pdb) break 72
Breakpoint 1 at /home/jeffs/python/PythonClass/dpkt_doc/decode_tcp_iterator_2.py:72
(Pdb) c
counter src prt dst prt flags
> /home/jeffs/python/PythonClass/dpkt_doc/decode_tcp_iterator_2.py(72)decode_tcp()
-> if eth.type == dpkt.ethernet.ETH_TYPE_IP :
(Pdb) print eth.type
129
(Pdb) print dpkt.ethernet.ETH_TYPE_IP
2048
(Pdb) print dpkt.ethernet.ETH_TYPE_IP6
34525
(Pdb)

262 |

I think this is a bug in wireshark, because in the packet decoding 263 | process, it says that there is no information available for the link 264 | layer.  Wireshark recognizes that the packet is IPv6.  129 is 265 | in the range of the IEEE802.3 length field, so the problem may be that 266 | this is an IEEE802.3 packet and not an Ethernet packet.  I am also 267 | doing the packet capture from the sixxs device.
268 |
269 |

270 |

271 |

272 | 273 | 274 | -------------------------------------------------------------------------------- /dpkt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | dpkt documentation by Jeff Silverman 8 | 9 | 10 | 12 | 13 |
14 | 16 | My documentation on dpkt
17 |
19 |

By Jeff Silverman, jeffsilverm at gmail dot com
20 |

21 | dpkt 23 | is 24 | a 25 | wonderful 26 | piece 27 | of 28 | software 29 | but 30 | it 31 | has 32 | very 33 | little 34 | documentation, 35 | just 36 | some 37 | examples 38 | about 39 | how 40 | to 41 | use 42 | it.  There is a lot more under 43 | the hood then the examples would have you believe.  What the 44 | author, Jon Oberheide, 45 | would 46 | have 47 | you 48 | believe 49 | is 50 | obvious 51 | is 52 | not 53 | obvious 54 | at 55 | all. 56 | There 57 | are 58 | more 59 | examples 60 | showing how 62 | to 63 | parse 64 | a 65 | pcap 66 | file 67 | that 68 | has 69 | HTTP and How 71 | to 72 | generate 73 | and 74 | transmit 75 | an 76 | ICMPv4 77 | packet. 78 |

I am doing this work as a classroom project for The 80 | University of Washington Python Programming class. Most of the 81 | material is in github.com. 82 | To 83 | get 84 | it, 85 | give 86 | the 87 | following 88 | commands: 89 |

90 |
[ps37854]$ mkdir dpkt_doc
 91 | [ps37854]$ cd dpkt_doc
 92 | [ps37854]$ git init .
 93 | Initialized empty Git repository in /home/jeffsilverm/commercialventvac/dpkt_doc/.git/
 94 | [ps37854]$ git pull git://github.com/jeffsilverm/dpkt_doc.git
 95 | remote: Counting objects: 3, done.
 96 | remote: Compressing objects: 100% (2/2), done.
 97 | remote: Total 3 (delta 0), reused 0 (delta 0)
 98 | Unpacking objects: 100% (3/3), done.
 99 | From git://github.com/jeffsilverm/dpkt_doc
100 |  * branch            HEAD       -> FETCH_HEAD
101 | [ps37854]$ 
102 | 
103 |
    104 | 105 |
  1. dpkt python module 106 | 107 |
      108 |
    1. 109 |
        110 |
      1. Files 111 |
      2. 112 |
      3. Attributes of dpkt 113 |
          114 |
        1. dpkt.ah
        2. 115 |
        3. dpkt.aim
        4. 116 |
        5. dpkt.arp 117 |
            118 |
          1. dpkt.arp.data
          2. 119 |
          3. dpkt.arp.hln
          4. 120 |
          5. dpkt.arp.op
          6. 121 |
          7. dpkt.arp.pln
          8. 122 |
          9. dpkt.arp.pro
          10. 123 |
          11. dpkt.arp.sha
          12. 124 |
          13. dpkt.arp.spa
          14. 125 |
          15. dpkt.arp.tha
          16. 126 |
          17. dpt.arp.tpa
          18. 127 |
          128 |
        6. 129 |
        7. dpkt.array
        8. 130 |
        9. dpkt.dns 131 |
            132 |
          1. dpkt.dns.an 133 |
              134 |
            1. cls
            2. 135 |
            3. cname
            4. 136 |
            5. name
            6. 137 |
            138 |
          2. 139 |
          3. dpkt.dns.ar
          4. 140 |
          5. dpkt.dns.data
          6. 141 |
          7. dpkt.dns.get_opcode
          8. 142 |
          9. dpkt.dns.get_qr
          10. 143 |
          11. dpkt.dns.get_rcode
          12. 144 |
          13. dpkt.dns.id
          14. 145 |
          15. dpkt.dns.ns
          16. 146 |
          17. dpkt.dns.op
          18. 147 |
          19. dpkt.dns.opcode
          20. 148 |
          21. dpkt.dns.pack
          22. 149 |
          23. dpkt.dns.pack_hdr
          24. 150 |
          25. dpkt.dns.pack_q 151 |
          26. 152 |
          27. dpkt.dns.pack_rr
          28. 153 |
          29. dpkt.dns.qd
          30. 154 |
          31. dpkt.dns.qr
          32. 155 |
          33. dpkt.dns.rcode
          34. 156 |
          35. dpkt.dns.set_opcode
          36. 157 |
          37. dpkt.dns.set_qr
          38. 158 |
          39. dpkt.dns.set_rcode
          40. 159 |
          41. dpkt.dns.unpack
          42. 160 |
          43. dpkt.dns.unpack_rr
          44. 161 |
          162 |
        10. 163 |
        11. dpkt.ethernet 164 |
            165 |
          1. dpkt.ethernet.Ethernet 166 | 167 |
              168 |
            1. data
            2. 169 |
            3. dst
            4. 170 |
            5. get_type
            6. 171 |
            7. src
            8. 172 |
            9. type
            10. 173 |
            174 |
          2. 175 |
          3. dpkt.ethernet.dpkt
          4. 176 |
          5. dpkt.ethernet.stp
          6. 177 |
          7. dpkt.ethernet.struct
          8. 178 |
          179 |
        12. 180 |
        13. dpkt.http 181 |
            182 |
          1. dpkt.http.
          2. 183 |
          3. dpkt.http.Request(received_string) 184 |
              185 |
            1. dpkt.http.Request.body
            2. 186 |
            3. dpkt.http.Request.headers
            4. 187 |
            5. dpkt.http.Request.method
            6. 188 |
            7. dpkt.http.Request.uri
            8. 189 |
            9. dpkt.http.Request.version
            10. 190 |
            191 |
          4. 192 |
          193 |
        14. 194 |
        15. dpkt.ip 195 |
            196 |
          1. dpkt.ip.IP
          2. 197 |
          3. dpkt.ip.data 198 |
          4. 199 |
          5. dpkt.ip.dst
          6. 200 |
          7. dpkt.ip.hl
          8. 201 |
          9. dpkt.ip.get_proto 202 |
          10. 203 |
          11. dpkt.ip.IP 204 |
          12. 205 |
          13. dpkt.ip.dpkt 206 |
          14. 207 |
          208 |
        16. 209 |
        17. dpkt.ip6 210 | 211 |
            212 |
          1. dpkt.ip6.IP6 213 | 214 |
              215 |
            1. dpkt.ip6.data
            2. 216 |
            3. dpkt.ip6.dst 217 |
            4. 218 |
            5. dpkt.ip6.fc
            6. 219 |
            7. dpkt.ip6.flow
            8. 220 |
            9. dpkt.ip6.get_proto
            10. 221 |
            11. dpkt.ip6.hlim
            12. 222 |
            13. dpkt.ip6.icmp6
            14. 223 |
            15. dpkt.ip6.nxt
            16. 224 |
            17. dpkt.ip6.pack
            18. 225 |
            19. dpkt.ip6.pack_hdr
            20. 226 |
            21. dpkt.ip6.plen
            22. 227 |
            23. dpkt.ip6.set_proto
            24. 228 |
            25. dpkt.ip6.src
            26. 229 |
            27. dpkt.ip6.unpack
            28. 230 |
            29. dpkt.ip6.v
            30. 231 |
            31. dpkt.ip6.v_fc_flow 232 |
            32. 233 |
            234 |
          2. 235 |
          236 |
        18. 237 |
        238 |
      4. 239 |
      5. dpkt.pcap 240 |
          241 |
        1. dpkt.pcap.Reader(f) 242 |
        2. 243 |
        244 |
      6. 245 |
      7. dpkt.tcp 246 |
          247 |
        1. dpkt.tcp.ack
        2. 248 |
        3. dpkt.tcp.data
        4. 249 |
        5. dpkt.tcp.dport
        6. 250 |
        7. dpkt.tcp.flags
        8. 251 |
        9. dpkt.tcp.off
        10. 252 |
        11. dpkt.tcp.off_x2
        12. 253 |
        13. dpkt.tcp.opts
        14. 254 |
        15. dpkt.tcp.pack
        16. 255 |
        17. dpkt.tcp.pack_hdr
        18. 256 |
        19. dpkt.tcp.parse_opts ( 257 | tcp.opts ) 258 |
        20. 259 |
        21. dpkt.tcp.seq
        22. 260 |
        23. dpkt.tcp.sport 261 |
        24. 262 |
        25. dpkt.tcp.sum
        26. 263 |
        27. dpkt.tcp.unpack
        28. 264 |
        29. dpkt.tcp.urp
        30. 265 |
        31. dpkt.tcp.win
        32. 266 |
        267 |
      8. 268 |
      9. dpkt.udp
      10. 269 |
      270 |
    2. 271 |
    3. Classes that inherit 272 | from dpkt.Packet
    4. 273 |
    274 |
  2. 275 |
276 |
281 |
282 |

283 |

dpkt python module
284 |

285 |

dpkt is an ethernet packet decoding module.  It was written by 286 | Dugsong.
287 |

288 |

Fundemental to understanding how dpkt works is the fact that it 289 | decodes single network packets.  This has two consequences:
290 |

291 | 295 |

For example, if you are doing an HTTP GET operation, then probably 296 | the 297 | entire header will fit into 1500 bytes, which is the default message 298 | transfer unit (MTU) size of an ethernet and most modern wide area 299 | networks as well.  However, if you are doing an HTTP POST 300 | operation with a lot of data moving from the client browser to the web 301 | server, dpkt will be able to parse many of the headers but not all of 302 | them.

303 |

304 | These problems I have tried to solve with software that sits on top of 305 | dpkt's low level interfaces.  For example, 306 | decode_tcp_iterator_2.py implements a crude TCP stack on top of dpkt.
307 |

308 |
309 |
310 |

311 |

Files
312 |

313 |
jeffs@heavy:/usr$ find . -name "*dpkt*" -print
./share/doc/python-dpkt
./share/pyshared/dpkt
./share/pyshared/dpkt/dpkt.py
./share/pyshared/dpkt-1.6.egg-info
./share/python-support/python-dpkt.public
./lib/pymodules/python2.6/dpkt
./lib/pymodules/python2.6/dpkt/dpkt.py
./lib/pymodules/python2.6/dpkt/dpkt.pyc
./lib/pymodules/python2.6/dpkt-1.6.egg-info

ls /usr/share/pyshared/dpkt
ah.py dpkt.py icmp.py ntp.py rip.py stp.py
ah.pyc dpkt.pyc icmp.pyc ntp.pyc rip.pyc stp.pyc
aim.py dtp.py igmp.py ospf.py rpc.py stun.py
aim.pyc dtp.pyc igmp.pyc ospf.pyc rpc.pyc stun.pyc
arp.py esp.py __init__.py pcap.py rtp.py tcp.py
arp.pyc esp.pyc __init__.pyc pcap.pyc rtp.pyc tcp.pyc
asn1.py ethernet.py ip6.py pim.py rx.py telnet.py
asn1.pyc ethernet.pyc ip6.pyc pim.pyc rx.pyc telnet.pyc
bgp.py gre.py ip.py pmap.py sccp.py tftp.py
bgp.pyc gre.pyc ip.pyc pmap.pyc sccp.pyc tftp.pyc
cdp.py gzip.py ipx.py pppoe.py sctp.py tns.py
cdp.pyc gzip.pyc ipx.pyc pppoe.pyc sctp.pyc tns.pyc
crc32c.py h225.py loopback.py ppp.py sip.py tpkt.py
crc32c.pyc h225.pyc loopback.pyc ppp.pyc sip.pyc tpkt.pyc
dhcp.py hsrp.py mrt.py qq.py sll.py udp.py
dhcp.pyc hsrp.pyc mrt.pyc qq.pyc sll.pyc udp.pyc
diameter.py http.py netbios.py radius.py smb.py vrrp.py
diameter.pyc http.pyc netbios.pyc radius.pyc smb.pyc vrrp.pyc
dns.py icmp6.py netflow.py rfb.py ssl.py yahoo.py
dns.pyc icmp6.pyc netflow.pyc rfb.pyc ssl.pyc yahoo.pyc

314 |

Attributes of dpkt

315 | >>> dir(dpkt)
316 | ['Error', 'NeedData', 'PackError', 'Packet', 'UnpackError', 317 | '__author__', '__builtins__', '__copyright__', '__doc__', '__file__', 318 | '__license__', '__name__', '__package__', '__path__', '__url__', 319 | '__version__', 'ah', 'aim', 'arp', 'array', 'asn1', 'bgp', 'cdp', 320 | 'copy', 'crc32c', 'dhcp', 'diameter', 'dns', 'dpkt', 'dtp', 'esp', 321 | 'ethernet', 'gre', 'gzip', 'h225', 'hexdump', 'hsrp', 'http', 'icmp', 322 | 'icmp6', 'igmp', 'in_cksum', 'in_cksum_add', 'in_cksum_done', 'ip', 323 | 'ip6', 'ipx', 'itertools', 'loopback', 'mrt', 'netbios', 'netflow', 324 | 'ntp', 'ospf', 'pcap', 'pim', 'pmap', 'ppp', 'pppoe', 'qq', 'radius', 325 | 'rfb', 'rip', 'rpc', 'rtp', 'rx', 'sccp', 'sctp', 'sip', 'sll', 'smb', 326 | 'socket', 'ssl', 'stp', 'struct', 'stun', 'tcp', 'telnet', 'tftp', 327 | 'tns', 'tpkt', 'udp', 'vrrp', 'yahoo']
328 | >>>
329 |

dpkt.ah

330 |

This decodes an IPSEC authentication header.  Insofar as I can 331 | tell, dpkt.ah will only work for IPSEC over IPv4.
332 |

333 |


334 |

335 |

dpkt.aim

336 |

AOL instant messenger

337 |

dpkt.arp

338 |

Address resolution protocol.  If the ethernet packet has type 339 | ETH_TYPE_ARP, then an dpkt.ethernet.Ethernet object will have an arp 340 | attribute.  Refer to decode_arp.py.

341 |
dpkt.arp.data
342 |
dpkt.arp.hln
343 |

The length of the hardware address.  For Ethernet, this is 6 344 | bytes.
345 |

346 |
dpkt.arp.op
347 |

The operation.  1=request, 2=reply
348 |

349 |
dpkt.arp.pln
350 |

The protocol address length.  For IPv4, this is 4.
351 |

352 |
dpkt.arp.pro
353 |

The upper layer protocol for which the ARP request is intended. For 354 | IPv4, this has the value 0x0800. The permitted  values share a 355 | numbering space with those for ethertype.
360 |

361 |
dpkt.arp.sha
362 |

The source hardware address (SHA).  This should be the same as 363 | the 364 | source ethernet address but doesn't have to be.  If the SHA and 365 | the ethernet source address are different, then you might suspect 366 | somebody trying to poison your arp cache.  However, there are 367 | legitimate reasons why they might be different, most of which are 368 | anacronisms these days.  Just because the source ethernet address 369 | and the SHA are the same, doesn't mean that nobody is trying to poison 370 | your arp cache.
371 |

372 |
dpkt.arp.spa
373 |

The source protocol address.  This will usually be an IPv4 374 | address.  You will never see an IPv6 address here, because IPv6 375 | uses neighbor discovery protocol.
376 |

377 |
dpkt.arp.tha
378 |

The target hardware address.  In an ARP request, this will be 379 | 0.  In an ARP reply, it should be the same as the Ethernet source 380 | address, but it doesn't have to be.  You don't see this very often 381 | any more, but there used to be ARP servers which would supply ARP 382 | replies for hosts too dumb to provide their own ARP replies.  You 383 | can also use this field to poison an ARP cache.
384 |

385 |
dpt.arp.tpa
386 |

The target protocol address.  This is the address that ARP is 387 | going to translate into a MAC address.  The IPv4 hosts in the 388 | network listen for ARP requests, which are sent to the broadcast 389 | Ethernet address.  When a host sees its own IP address in the tpa 390 | field, it knows it has to reply with an ARP reply.

391 |
392 |

dpkt.array

393 |
394 |

dpkt.dns

395 | ['Q', 'RR', '__class__', '__delattr__', '__dict__', '__doc__', 396 | '__format__', '__getattribute__', '__getitem__', '__hash__', '__hdr__', 397 | '__hdr_defaults__', '__hdr_fields__', '__hdr_fmt__', '__hdr_len__', 398 | '__init__', '__len__', '__metaclass__', '__module__', '__new__', 399 | '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', 400 | '__slots__', '__str__', '__subclasshook__', '__weakref__', 'an', 'ar', 401 | 'data', 'get_opcode', 'get_qr', 'get_rcode', 'id', 'ns', 'op', 402 | 'opcode', 'pack', 'pack_hdr', 'pack_q', 'pack_rr', 'qd', 'qr', 'rcode', 403 | 'set_opcode', 'set_qr', 'set_rcode', 'unpack', 'unpack_q', 'unpack_rr']
404 |

In general, DNS runs on top of UDP.  DNS can run on top of TCP 405 | and will do so if the query is large and also if there is a zone 406 | transfer.  Many of the values in these fields come from IANA.
408 |

409 |

Insofar as I can tell, dpkt doesn't provide the AD bit.  The AD 410 | bit is set if this response is authoritative according to the policies 411 | of the server.  A caching name server is generally not 412 | authoritative.
413 |

414 |

Name servers return answers that have character values outside of 415 | the range 33 to 126 decimal inclusive.  I don't know why.  416 | Both dig and wireshark are able to decipher the strings properly.
417 |

418 |
dpkt.dns.an
419 |

A list of answers.  Each answer is an RR record.  For a 420 | DNS query, this is an empty list.  To get the results from the DNS 421 | query, you have to iterate over the list and decode each RR record.
422 |

423 |
cls
424 |

Class.  For modern resolvers, this should always be 1.  425 | There were other classes defined in RFC 1035, but nobody uses them 426 | anymore.
427 |

428 |
cname
429 |

This is the canonical name of the thing being looked up.  The 430 | response will be applicable to this cname.
431 |

432 |
name
433 |

The name that was looked up, before translation to a canonical name, 434 | if any.
435 |

436 |
437 |
dpkt.dns.ar
438 |

This is either an Authority Record or an Additional Record.
439 |

440 |
dpkt.dns.data
441 |
dpkt.dns.get_opcode
442 |
dpkt.dns.get_qr
443 |

qr is 0 if this message is a query and 1 if this message is a 444 | response.

445 |
dpkt.dns.get_rcode
446 |
dpkt.dns.id
447 |

The ID field is set by the DNS resolver when it makes a query.  448 | The ID field is also set by the nameserver when it responds to the 449 | query.  That way, the resolver knows which query the response is 450 | in response to.
451 |

452 |
dpkt.dns.ns
453 |

A list of name servers for this domain.  To decode this list, 454 | iterate over each entry in the list and decode as in dpt.dns.an
455 |

456 |
457 |
dpkt.dns.op
458 |
dpkt.dns.opcode
459 |

The opcode is 0  a standard query (QUERY), 1 an inverse query 460 | (IQUERY) (obsoleted by RFC 3425),  2   a server status 461 | request (STATUS), 3 is unassigned, 4 is Notify (RFC  1996), 5 is 462 | update, defined by RFC 2136
463 |

464 |
465 |
dpkt.dns.pack
466 |
dpkt.dns.pack_hdr
467 |
dpkt.dns.pack_q
468 |
469 |
dpkt.dns.pack_rr
470 |
dpkt.dns.qd
471 |
dpkt.dns.qr
472 |

qr is 0 if this message is a query and 1 if this message is a 473 | response.
474 |

475 |
476 |
dpkt.dns.rcode
477 |
dpkt.dns.set_opcode
478 |
dpkt.dns.set_qr
479 |
dpkt.dns.set_rcode
480 |
dpkt.dns.unpack
481 |
dpkt.dns.unpack_rr
482 |

dpkt.ethernet

483 | 'ETH_CRC_LEN', 'ETH_HDR_LEN', 'ETH_LEN_MAX', 'ETH_LEN_MIN', 'ETH_MIN', 484 | 'ETH_MTU', 'ETH_TYPE_8021Q', 'ETH_TYPE_ARP', 'ETH_TYPE_CDP', 485 | 'ETH_TYPE_DTP', 'ETH_TYPE_IP', 'ETH_TYPE_IP6', 'ETH_TYPE_IPX', 486 | 'ETH_TYPE_MPLS', 'ETH_TYPE_MPLS_MCAST', 'ETH_TYPE_PPP', 487 | 'ETH_TYPE_PPPoE', 'ETH_TYPE_PPPoE_DISC', 'ETH_TYPE_PUP', 488 | 'ETH_TYPE_REVARP', 'Ethernet', '__builtins__', '__doc__', '__file__', 489 | '__load_types', '__name__', '__package__', 'dpkt', 'stp', 'struct'
490 |
491 |
dpkt.ethernet.Ethernet
492 |
493 | dpkt.ethernet.Ethernet has attributes 'data', 'dst', 'get_type', 'ip', 494 | 'pack', 'pack_hdr', 'set_type', 'src', 'type', 'unpack']
495 |
496 |
data
497 |

Contains the data payload of the ethernet packet.

498 |
499 |
dst
500 |

Contains the destination address of the ethernet packet as a 6 byte 501 | strings.

502 |
503 |

6 Byte Ethernet addresses can be converted to strings in format 504 | nn:nn:nn:nn:nn:nn with the code:

505 |
def add_colons_to_mac( mac_addr ) :
"""This function accepts a 12 hex digit string and converts it to a colon separated string"""
s = list()
for i in range(12/2) : # mac_addr should always be 12 chars, we work in groups of 2 chars
s.append( mac_addr[i*2:i*2+2] )
r = ":".join(s) # I know this looks strange, refer to http://docs.python.org/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer-xrange
return r


add_colons_to_mac( binascii.hexlify(arp.sha) )

506 |
507 |
get_type
508 |

Returns a class which is something from the Ethernet Type field

509 |
(Pdb) print eth._typesw.keys()
[2048, 8192, 34916, 2054, 34827, 33079, 8196, 34525]
(Pdb) print eth._typesw.values()
[<class 'dpkt.ip.IP'>, <class 'dpkt.cdp.CDP'>, <class
'dpkt.pppoe.PPPoE'>, <class 'dpkt.arp.ARP'>, <class
'dpkt.ppp.PPP'>, <class 'dpkt.ipx.IPX'>, <class
'dpkt.dtp.DTP'>, <class 'dpkt.ip6.IP6'>]
(Pdb) print eth.get_type(2048)
<class 'dpkt.ip.IP'>
(Pdb) print eth.get_type(34525)
<class 'dpkt.ip6.IP6'>
(Pdb)
510 |

Another way to do the same thing is:
511 |

512 |
import dpkt
eth = dpkt.ethernet.Ethernet()
print eth._typesw[2048]
<class 'dpkt.ip.IP'>
print eth._typesw[34525]
<class 'dpkt.ip6.IP6'>
print eth._typesw[33079]
<class 'dpkt.ipx.IPX'>
513 |
src
514 |

Contains the source address of the ethernet packet as a 6 byte 515 | string.  To decode to ASCII, see dst, 516 | above.
517 |

518 |
519 |
type
520 |

Returns the Ethernet type.  For example, type 2048 (0x0800) is 521 | IPv4 and 34525 (0x86DD) is IPv6.  For a complete list of Ethernet 522 | types, refer to http://www.iana.org/assignments/ethernet-numbers 524 | To get a list of ethernet types that are supported by dpkt, refer to 525 | the code at get_type.
526 |

527 |
528 |
dpkt.ethernet.dpkt
529 | ['Error', 'NeedData', 'PackError', 'Packet', 'UnpackError', 530 | '_MetaPacket', '__builtins__', '__doc__', '__file__', '__name__', 531 | '__package__', '__vis_filter', 'array', 'copy', 'hexdump', 'in_cksum', 532 | 'in_cksum_add', 'in_cksum_done', 'itertools', 'socket', 'struct']
533 |
534 |
dpkt.ethernet.stp
535 |

This is spanning tree protocol.
536 |

537 |
dpkt.ethernet.struct
538 |

dpkt.http

539 |

Objects for decoding HTTP.  The message going from the client 540 | browser to the server is the request, the message going from the server 541 | to the client browser is the response.
542 |

543 |
dpkt.http.Reply(received_string)
544 |

Create a dpkt.http.Reply object from the received string.  The 545 | received string probably will include the body of the response and may 546 | be quite large.  Use a module such as decode_tcp_iterator_2 to 547 | combine the payloads of several packets into a single received string.
548 |

549 |
dpkt.http.Reply.status
550 |

This is the numeric code that the server returns to the browser 551 | which describes the outcome of the request.  Values in the range 552 | of 1xx are continuation, values in the range of 2xx are success, values 553 | in the range of 3xx are relocations, values in the range of 4xx are 554 | errors from the client, values in the range of 5xx are errors in the 555 | server.  The status is just 3 digits, there is an explanation of 556 | the error that is returned in dpkt.htt.Reply.reason.  The full 557 | list of status codes is given in RFC 559 | 2616 section 10.
560 |

561 |
dpkt.http.Reply.reason
562 |

The reason is a brief explanation of the status code.  563 | According to RFC 565 | 2616 section 6.1.1 the reason is for human use only and has no 566 | significance to the protocol.
567 |

568 |
dpkt.http.Reply.body
569 |

The body is the payload of the HTTP response.  Its type is 570 | given by value of content-type header, which is a MIME type as 571 | described in RFC 2045, 572 | RFC 2046, and RFC 2047.  The 574 | list of MIME types is maintained by the 576 | Internet Assigned Numbers Authority (IANA).
577 |

578 |
dpkt.http.Reply.headers
579 |

headers is a dictionary of the headers of the reply.  The keys 580 | are the headers that are present, the values are the values of those 581 | headers.  The list of allowed headers is given by RFC 2616 583 | section 14.
584 |

585 |
586 |
dpkt.http.Request(received_string)
587 |

Create a dpkt.http.Request object from the received string.
588 |

589 |
dpkt.http.Request.body
590 |

If the HTTP request method is POST, then this attribute will contain 591 | the input that is passed to the server.  If the method is GET, 592 | then this attribute will be an empty string.
593 |

594 |
dpkt.http.Request.headers
595 |

This is a dictionary.  The keys of the dictionary are the 596 | header fields that are present and the values of the dictionary are the 597 | values of the field.  For example:
598 |

599 |
(Pdb) print http_req.headers
{'host': 'www.kame.net', 'connection': 'Keep-Alive', 'accept': '*/*', 'user-agent': 'Wget/1.12 (linux-gnu)'}
(Pdb)

600 |
dpkt.http.Request.method
601 |
602 |

When doing an HTTP request, the client browser refers to the object 603 | with a method.  The most common methods are GET and PUT.  The 604 | difference between GET and PUT has to do with how arguments are passed 605 | from the client to the server.  With a GET, the arguments are 606 | passed in the URI separated by & characters.  With a PUT, the 607 | values are passed as key value pairs in the body of the request.  608 | Refer to RFC 610 | 2616 section 9.1.2 for a discussion of methods and idempotence.
611 |

612 |

For a list of valid methods, refer to RFC 614 | 2616 section 5.1.
615 |

616 |
dpkt.http.Request.uri
617 |

The URI (Uniform Resource Identifier) the the part of the URL 618 | (Uniform Resource Locator) which comes after the hostname.  So, 619 | for example, the URI of the URL http://www.commercialventvac.com/dpkt.html 621 | is /dpkt.html.  The URI of the URL http://www.commercialventvac.com 623 | is /
624 |

625 |
dpkt.http.Request.version
626 |

The version of HTTP.  Valid values (as of this writing) are 627 | 0.9, 1.0, and 1.1
628 |

629 |
630 |

dpkt.ip

631 |
632 | True
633 | >>>
634 |
'data', 'dst', 'get_proto', 'hl', 'id', 'len', 'off', 'opts', 'p', 'pack', 'pack_hdr', 'set_proto', 'src', 'sum', 'tcp', 'tos', 'ttl', 'unpack', 'v', 'v_hl'
635 |
636 |
dpkt.ip.IP
637 |

This constructor can be used to create a packet.  
638 |

639 |
from dpkt.udp import UDP
from dpkt.ip import IP
import socket


udp = UDP(data="testing")
src=socket.inet_aton("127.0.0.1")
dst=socket.inet_aton("67.205.52.141")
ip = IP(src=src, dst=dst, data=udp )

ip
IP(src='\x7f\x00\x00\x01', dst='C\xcd4\x8d', data=UDP(data='testing'))



640 |
dpkt.ip.data
641 |
642 |

This is the payload of the IP packet
643 |

644 |

645 |
dpkt.ip.dst
646 |

The destination IPv4 address of the packet.  You can convert 647 | the destination IP address to an ASCII string in dotted quad format 648 | using the socket package:
649 |

650 |
import socket
dst_ip_addr_str = socket.inet_ntoa(ip.dst)
651 |
dpkt.ip.hl
652 |

Internet Header Length is the length of the internet header in 32 653 | bit 654 | words, and thus points to the beginning of the data. Note that the 655 | minimum value for a correct header is 5.

656 |
657 |
dpkt.ip.get_proto
658 |
659 |
dpkt.ip.IP
660 |
661 |
dpkt.ip.dpkt
662 |
663 |
664 |

dpkt.ip6
665 |

666 |
dpkt.ip6.IP6
667 |
668 | >>> dir(dpkt.ip6.IP6)
669 | ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 670 | '__getattribute__', '__getitem__', '__hash__', '__hdr__', 671 | '__hdr_defaults__', '__hdr_fields__', '__hdr_fmt__', '__hdr_len__', 672 | '__init__', '__len__', '__metaclass__', '__module__', '__new__', 673 | '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', 674 | '__slots__', '__str__', '__subclasshook__', '__weakref__', '_get_fc', 675 | '_get_flow', '_get_v', '_protosw', '_set_fc', '_set_flow', '_set_v', 676 | 'data', 'dst', 'fc', 'flow', 'get_proto', 'hlim', 'nxt', 'pack', 677 | 'pack_hdr', 'plen', 'set_proto', 'src', 'unpack', 'v', 'v_fc_flow']
678 | >>>
679 | >>> callable(dpkt.ip6.IP6)
680 | True
681 | 'data', 'dst', 'fc', 'flow', 'get_proto', 'hlim', 'icmp6', 'nxt', 682 | 'pack', 'pack_hdr', 'plen', 'set_proto', 'src', 'unpack', 'v', 683 | 'v_fc_flow'
684 |
dpkt.ip6.data
685 |

This is the payload of the packet.  Depending on ip6.nxt, this 686 | will be UDP, TCP, or similar.  dpkt magically casts this into the 687 | proper datatype.
688 |

689 |
dpkt.ip6.dst
690 |
691 |

This is the destination IPv6 address, 128 bits.  To create a 692 | packed IPv6 address from an ASCII string:
693 |

694 |
import socket
dst_addr = socket.inet_pton(socket.AF_INET6, "2001:1938:26f:1:204:4bff:0:1")

695 |
dpkt.ip6.fc
696 |
dpkt.ip6.flow
697 |

IPv6 defines a optimization called a "flow".  If a router sees 698 | a packet with a non-zero flow for the first time, it makes its routing 699 | decision and stores that decision in a fast hash table.  Then when 700 | subsequent packets come by with the same flow, the router can makes its 701 | routing decision faster.  The flow is initialized by a random 702 | number generator on the host that is originating the connection.  703 | It can be left 0.
704 |

705 |
706 |
dpkt.ip6.get_proto
707 |
dpkt.ip6.hlim
708 |

The hop limit.  Each time the packet hops from router to 709 | router, this field is decremented by 1.  When the hlim reaches 0, 710 | the packet is discarded, and an ICMP6 type 3 packet is sent to the 711 | sender.  tracepath6 uses this field to probe the path to a 712 | destination.  It sends a packet with a small hlim.  When the 713 | router decrements the hlim to zero and sends back the ICMPv6 packet to 714 | the sender, tracepath6 records the source address of the ICMPv6 packet 715 | and knows the IP address of the router.
716 |

717 |
718 |
dpkt.ip6.icmp6
719 |
dpkt.ip6.nxt
720 |

The next header type.  If there is no next header, then the 721 | protocol of the next level in the stack.  Typical values are 6 for 722 | TCP, 17 for UDP, 58 for ICMPv6, 132 for SCTP.  For a list of 723 | protocols, see http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml
725 |

726 |
dpkt.ip6.pack
727 |
dpkt.ip6.pack_hdr
728 |
dpkt.ip6.plen
729 |

The payload length, not counting the IPv6 header.  This is a 16 730 | bit unsigned number.
731 |

732 |
dpkt.ip6.set_proto
733 |
dpkt.ip6.src
734 |

The source IPv6 address, which is 128 bits long.  To decode the 735 | IPv6 address into an ASCII string comprehensible by humans, use the 736 | socket.inet_ntop method:
737 |

738 |
import socket
import dpkt
eth = dpkt.ethernet.Ethernet(buf)
ip = eth.data
dst_ip_addr_str = socket.inet_ntop(AF_INET6, ip.dst)
print dst_ip_addr_str
739 |

This will give an output that looks like: 740 | 2001:1938:26f:1:204:4bff:0:1

741 |
dpkt.ip6.unpack
742 |
dpkt.ip6.v
743 |

This is the version of IP, which must be 6.  If the ethernet 744 | type is 34525 and this is not 6, then throw an exception because 745 | something is wrong.
746 |

747 |
dpkt.ip6.v_fc_flow 748 |
749 |

dpkt.pcap

750 | ['DLT_ARCNET', 'DLT_AX25', 'DLT_CHAOS', 'DLT_EN10MB', 'DLT_EN3MB', 751 | 'DLT_FDDI', 'DLT_IEEE802', 'DLT_LINUX_SLL', 'DLT_LOOP', 'DLT_NULL', 752 | 'DLT_PFLOG', 'DLT_PFSYNC', 'DLT_PPP', 'DLT_PRONET', 'DLT_RAW', 753 | 'DLT_SLIP', 'FileHdr', 'LEFileHdr', 'LEPktHdr', 'PCAP_VERSION_MAJOR', 754 | 'PCAP_VERSION_MINOR', 'PMUDPCT_MAGIC', 'PktHdr', 'Reader', 755 | 'TCPDUMP_MAGIC', 'Writer', '__builtins__', '__doc__', '__file__', 756 | '__name__', '__package__', 'dltoff', 'dpkt', 'sys', 'time']
757 |

dpkt.pcap.Reader(f)
758 |

759 |

dpkt.pcap.Reader(f) implements an iterator.  Each iteration 760 | returns a tuple which is a timestamp and a buffer.  The timestamp 761 | contains a time as a floating point number.  The buffer is a 762 | complete packet.  For example:
763 |

764 |
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import dpkt
import sys


f = open(sys.argv[1])
pcap = dpkt.pcap.Reader(f)
frame_counter = 0
for ts, buf in pcap:
frame_counter += 1
if frame_counter > 1 :
print "%d: %f %f" % ( frame_counter, ts, ts - last_time )
last_time = ts

f.close()


765 |

dpkt.tcp

766 |

TCP is a reliable, stream oriented protocol.  Refer to RFC 793 for more 768 | information.

769 |
770 |

dpkt.tcp.ack

771 |

This is the last byte that the receiver has received.  The 772 | sender then knows that it need not resend any bytes prior to this point.
773 |

774 |

dpkt.tcp.data

775 |

The payload of this TCP segment.  Note that the seqments may be 776 | delivered out of order, so it is not sufficient to simply join the 777 | payloads together.  The maximum length of a TCP segment payload is 778 | the MTU size of this network less the length of the IP header less the 779 | length of a TCP header with no options.  For example, Ethernet has 780 | an MTU of 1500 bytes (Most modern networks use this size to avoid 781 | fragmentation on all links - IPv6 the smallest MTU must be at least 782 | 1200 bytes but may be longer).  The IPv4 header is 20 bytes, 783 | minimum.  An IPv6 header is 40 bytes.  The TCP header is 20 784 | bytes, so the maximum length of a TCP segment is 1460 bytes for IPv4 785 | and 1440 bytes for IPv6.
786 |

787 |

dpkt.tcp.dport

788 |

The destination port of the packet, a 16 bit unsigned number.  789 | For a list of well known destination ports, refer to http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers.  791 | If 792 | the SYN flag is set and the ACK flag is cleared, then this packet is 793 | the beginning of a connection and you may use the dport to determine 794 | what service is being used and how to decode the conversation.
795 |

796 |

dpkt.tcp.flags

797 |

The TCP flags.  To decode them, use the following code:

798 |
            fin_flag = ( tcp.flags & dpkt.tcp.TH_FIN ) != 0
syn_flag = ( tcp.flags & dpkt.tcp.TH_SYN ) != 0
rst_flag = ( tcp.flags & dpkt.tcp.TH_RST ) != 0
psh_flag = ( tcp.flags & dpkt.tcp.TH_PUSH) != 0
ack_flag = ( tcp.flags & dpkt.tcp.TH_ACK ) != 0
urg_flag = ( tcp.flags & dpkt.tcp.TH_URG ) != 0
ece_flag = ( tcp.flags & dpkt.tcp.TH_ECE ) != 0
cwr_flag = ( tcp.flags & dpkt.tcp.TH_CWR ) != 0

799 |
800 |

dpkt.tcp.off

801 |

4 bits.  Specifies the size of the TCP header in 32 bit 802 | longwords.  The minimum size of a TCP header is 20 bytes (5 803 | longwords) and the maximum is 60 bytes (15 longwords).  This field 804 | gets its name because it is the offset from the beginning of the header 805 | to the data.

806 |
807 |

dpkt.tcp.off_x2

808 |

dpkt.tcp.opts

809 | This is a list of options.  Needs 810 | more 811 | detail.
812 |
813 |

dpkt.tcp.pack

814 |

dpkt.tcp.pack_hdr

815 |

dpkt.tcp.parse_opts ( 816 | tcp.opts )
817 |

818 |

This method parses the TCP options field into a list of (option 819 | number, option value) tuples.  Use the code sequence
820 |

821 |
option_list = dpkt.tcp.parse_opts ( tcp.opts )

822 |

to decode the options.  For a fully worked, example, see the 823 | get_message_segment_size method in decode_tcp_iterator_2.py.
824 |

825 |

dpkt.tcp.seq

826 |

A 32 bit unsigned number.  If the SYN flag is set, then this is 827 | the initial sequence number, and all subsequent sequence numbers are 828 | relative to this number.  If the SYN flag is clear, then this 829 | number is the location of the payload of this sequence in the data 830 | stream.  Note that segment can be delivered out of order, so the 831 | sequence number is used to get the bytes back in order.
832 |

833 |

dpkt.tcp.sport 834 |

835 |

The source port, a 16 bit unsigned number.  On the initial 836 | connection, this will be an ephemeral port in the range 49152 through 837 | 6553 for most modern operating systems.
838 |

839 |
840 |

dpkt.tcp.sum

841 |
842 |
843 |

dpkt.tcp.unpack

844 |

dpkt.tcp.urp

845 |

dpkt.tcp.win

846 |

A 16 bit unsigned number.  Specifies how far past the current 847 | sequence number in this acknowlegement the receiver is willing to 848 | receive.

849 |

dpkt.udp

850 |
851 |
852 |

Classes that inherit 853 | from dpkt.Packet

854 |

There are a lot of classes that inherit from dpkt.Packet.  I 855 | found these with the command egrep "class.*dpkt.Packet" *.py
856 |

857 |
ah.py:class AH(dpkt.Packet):
aim.py:class FLAP(dpkt.Packet):
aim.py:class SNAC(dpkt.Packet):
arp.py:class ARP(dpkt.Packet):
bgp.py:class BGP(dpkt.Packet):
bgp.py: class Open(dpkt.Packet):
bgp.py: class Parameter(dpkt.Packet):
bgp.py: class Authentication(dpkt.Packet):
bgp.py: class Capability(dpkt.Packet):
bgp.py: class Update(dpkt.Packet):
bgp.py: class Attribute(dpkt.Packet):
bgp.py: class Origin(dpkt.Packet):
bgp.py: class ASPath(dpkt.Packet):
bgp.py: class ASPathSegment(dpkt.Packet):
bgp.py: class NextHop(dpkt.Packet):
bgp.py: class MultiExitDisc(dpkt.Packet):
bgp.py: class LocalPref(dpkt.Packet):
bgp.py: class AtomicAggregate(dpkt.Packet):
bgp.py: class Aggregator(dpkt.Packet):
bgp.py: class Communities(dpkt.Packet):
bgp.py: class Community(dpkt.Packet):
bgp.py: class ReservedCommunity(dpkt.Packet):
bgp.py: class OriginatorID(dpkt.Packet):
bgp.py: class ClusterList(dpkt.Packet):
bgp.py: class MPReachNLRI(dpkt.Packet):
bgp.py: class MPUnreachNLRI(dpkt.Packet):
bgp.py: class Notification(dpkt.Packet):
bgp.py: class Keepalive(dpkt.Packet):
bgp.py: class RouteRefresh(dpkt.Packet):
bgp.py:class RouteGeneric(dpkt.Packet):
bgp.py:class RouteIPV4(dpkt.Packet):
bgp.py:class RouteIPV6(dpkt.Packet):
cdp.py:class CDP(dpkt.Packet):
cdp.py: class Address(dpkt.Packet):
cdp.py: class TLV(dpkt.Packet):
dhcp.py:class DHCP(dpkt.Packet):
diameter.py:class Diameter(dpkt.Packet):
diameter.py:class AVP(dpkt.Packet):
dns.py:class DNS(dpkt.Packet):
dns.py: class Q(dpkt.Packet):
dtp.py:class DTP(dpkt.Packet):
esp.py:class ESP(dpkt.Packet):
ethernet.py:class Ethernet(dpkt.Packet):
gre.py:class GRE(dpkt.Packet):
gre.py: class SRE(dpkt.Packet):
gzip.py:class GzipExtra(dpkt.Packet):
gzip.py:class Gzip(dpkt.Packet):
h225.py:class H225(dpkt.Packet):
h225.py: class IE(dpkt.Packet):
hsrp.py:class HSRP(dpkt.Packet):
http.py:class Message(dpkt.Packet):
icmp6.py:class ICMP6(dpkt.Packet):
icmp6.py: class Error(dpkt.Packet):
icmp6.py: class Echo(dpkt.Packet):
icmp.py:class ICMP(dpkt.Packet):
icmp.py: class Echo(dpkt.Packet):
icmp.py: class Quote(dpkt.Packet):
igmp.py:class IGMP(dpkt.Packet):
ip6.py:class IP6(dpkt.Packet):
ip.py:class IP(dpkt.Packet):
ipx.py:class IPX(dpkt.Packet):
loopback.py:class Loopback(dpkt.Packet):
mrt.py:class MRTHeader(dpkt.Packet):
mrt.py:class TableDump(dpkt.Packet):
mrt.py:class BGP4MPMessage(dpkt.Packet):
mrt.py:class BGP4MPMessage_32(dpkt.Packet):
netbios.py:class Session(dpkt.Packet):
netbios.py:class Datagram(dpkt.Packet):
netflow.py:class NetflowBase(dpkt.Packet):
netflow.py: class NetflowRecordBase(dpkt.Packet):
ntp.py:class NTP(dpkt.Packet):
ospf.py:class OSPF(dpkt.Packet):
pcap.py:class PktHdr(dpkt.Packet):
pcap.py:class FileHdr(dpkt.Packet):
pim.py:class PIM(dpkt.Packet):
pmap.py:class Pmap(dpkt.Packet):
pppoe.py:class PPPoE(dpkt.Packet):
ppp.py:class PPP(dpkt.Packet):
radius.py:class RADIUS(dpkt.Packet):
rfb.py:class RFB(dpkt.Packet):
rfb.py:class SetPixelFormat(dpkt.Packet):
rfb.py:class SetEncodings(dpkt.Packet):
rfb.py:class FramebufferUpdateRequest(dpkt.Packet):
rfb.py:class KeyEvent(dpkt.Packet):
rfb.py:class PointerEvent(dpkt.Packet):
rfb.py:class FramebufferUpdate(dpkt.Packet):
rfb.py:class SetColourMapEntries(dpkt.Packet):
rfb.py:class CutText(dpkt.Packet):
rip.py:class RIP(dpkt.Packet):
rip.py:class RTE(dpkt.Packet):
rip.py:class Auth(dpkt.Packet):
rpc.py:class RPC(dpkt.Packet):
rpc.py: class Auth(dpkt.Packet):
rpc.py: class Call(dpkt.Packet):
rpc.py: class Reply(dpkt.Packet):
rpc.py: class Accept(dpkt.Packet):
rpc.py: class Reject(dpkt.Packet):
rx.py:class Rx(dpkt.Packet):
sccp.py:class ActivateCallPlane(dpkt.Packet):
sccp.py:class CallInfo(dpkt.Packet):
sccp.py:class CallState(dpkt.Packet):
sccp.py:class ClearPromptStatus(dpkt.Packet):
sccp.py:class CloseReceiveChannel(dpkt.Packet):
sccp.py:class DisplayPromptStatus(dpkt.Packet):
sccp.py:class DisplayText(dpkt.Packet):
sccp.py:class KeypadButton(dpkt.Packet):
sccp.py:class OpenReceiveChannel(dpkt.Packet):
sccp.py:class OpenReceiveChannelAck(dpkt.Packet):
sccp.py:class SelectStartKeys(dpkt.Packet):
sccp.py:class SetLamp(dpkt.Packet):
sccp.py:class SetSpeakerMode(dpkt.Packet):
sccp.py:class StartMediaTransmission(dpkt.Packet):
sccp.py:class StartTone(dpkt.Packet):
sccp.py:class StopMediaTransmission(dpkt.Packet):
sccp.py:class SCCP(dpkt.Packet):
sctp.py:class SCTP(dpkt.Packet):
sctp.py:class Chunk(dpkt.Packet):
sll.py:class SLL(dpkt.Packet):
smb.py:class SMB(dpkt.Packet):
ssl.py:class SSL2(dpkt.Packet):
ssl.py:class SSL3(dpkt.Packet):
stp.py:class STP(dpkt.Packet):
stun.py:class STUN(dpkt.Packet):
tcp.py:class TCP(dpkt.Packet):
tftp.py:class TFTP(dpkt.Packet):
tns.py:class TNS(dpkt.Packet):
tpkt.py:class TPKT(dpkt.Packet):
udp.py:class UDP(dpkt.Packet):
vrrp.py:class VRRP(dpkt.Packet):
yahoo.py:class YHOO(dpkt.Packet):
yahoo.py:class YMSG(dpkt.Packet):

858 |
859 |
860 |
861 | Valid HTML 4.01!
865 |
866 | 867 | 868 | 869 | --------------------------------------------------------------------------------