1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 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 174 175
|
require 'bindata'
# This is a simple protocol analyser for examining IPv4 packets
# captured with libpcap on an ethernet network.
#
# The dump file can be obtained like this:
#
# sudo tcpdump -i eth0 -s 0 -n -w dump.pcap
#
# Present MAC addresses in a human readable way
class MacAddr < BinData::Primitive
array :octets, type: :uint8, initial_length: 6
def set(val)
self.octets = val.split(/:/).collect(&:to_i)
end
def get
self.octets.collect { |octet| "%02x" % octet }.join(":")
end
end
# Present IP addresses in a human readable way
class IP_Addr < BinData::Primitive
array :octets, type: :uint8, initial_length: 4
def set(val)
self.octets = val.split(/\./).collect(&:to_i)
end
def get
self.octets.collect { |octet| "%d" % octet }.join(".")
end
end
# TCP Protocol Data Unit
class TCP_PDU < BinData::Record
endian :big
uint16 :src_port
uint16 :dst_port
uint32 :seq
uint32 :ack_seq
bit4 :doff
bit4 :res1
bit2 :res2
bit1 :urg
bit1 :ack
bit1 :psh
bit1 :rst
bit1 :syn
bit1 :fin
uint16 :window
uint16 :checksum
uint16 :urg_ptr
string :options, read_length: :options_length_in_bytes
rest :payload
def options_length_in_bytes
(doff - 5 ) * 4
end
end
# UDP Protocol Data Unit
class UDP_PDU < BinData::Record
endian :big
uint16 :src_port
uint16 :dst_port
uint16 :len
uint16 :checksum
rest :payload
end
# IP Protocol Data Unit
class IP_PDU < BinData::Record
endian :big
bit4 :version, asserted_value: 4
bit4 :header_length
uint8 :tos
uint16 :total_length
uint16 :ident
bit3 :flags
bit13 :frag_offset
uint8 :ttl
uint8 :protocol
uint16 :checksum
ip_addr :src_addr
ip_addr :dest_addr
string :options, read_length: :options_length_in_bytes
buffer :payload, length: :payload_length_in_bytes do
choice :payload, selection: :protocol do
tcp_pdu 6
udp_pdu 17
rest :default
end
end
def header_length_in_bytes
header_length * 4
end
def options_length_in_bytes
header_length_in_bytes - options.rel_offset
end
def payload_length_in_bytes
total_length - header_length_in_bytes
end
end
# Ethernet Frame - NOTE only ipv4 is supported
class Ether < BinData::Record
IPV4 = 0x0800
endian :big
mac_addr :dst
mac_addr :src
uint16 :ether_type
choice :payload, selection: :ether_type do
ip_pdu IPV4
rest :default
end
end
class Pcap
def initialize(filename)
@filename = filename
end
def each_record
File.open(@filename) do |io|
file = PcapFile.read(io)
file.records.each do |rec|
yield rec.data
end
end
end
class PcapFile < BinData::Record
endian :little
struct :head do
uint32 :magic
uint16 :major
uint16 :minor
int32 :this_zone
uint32 :sig_figs
uint32 :snaplen
uint32 :linktype
end
array :records, read_until: :eof do
uint32 :ts_sec
uint32 :ts_usec
uint32 :incl_len
uint32 :orig_len
string :data, length: :incl_len
end
end
end
require 'pp'
unless File.exist?('dump.pcap')
puts "No dump file found. Create one with: sudo tcpdump -i eth0 -s 0 -n -w dump.pcap"
exit 1
end
cap = Pcap.new('dump.pcap')
cap.each_record do |str|
pp Ether.read(str)
end
|