File: tcp_ip.rb

package info (click to toggle)
ruby-bindata 2.4.14-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 600 kB
  • sloc: ruby: 8,566; makefile: 4
file content (175 lines) | stat: -rw-r--r-- 3,335 bytes parent folder | download | duplicates (2)
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