packetfu 1.1.5 → 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. data/.document +5 -2
  2. data/.gitignore +1 -0
  3. data/LICENSE.txt +1 -1
  4. data/bench/after-2012-07-28.txt +25 -0
  5. data/bench/before-2012-07-28.txt +25 -0
  6. data/bench/benchit.rb +68 -0
  7. data/bench/calc_delta.rb +17 -0
  8. data/bench/octets.rb +22 -0
  9. data/bench/octets_after.txt +8 -0
  10. data/bench/octets_after_refactor.txt +8 -0
  11. data/bench/octets_before.txt +8 -0
  12. data/lib/packetfu.rb +8 -3
  13. data/lib/packetfu/packet.rb +2 -2
  14. data/lib/packetfu/pcap.rb +20 -4
  15. data/lib/packetfu/protos/arp.rb +7 -160
  16. data/lib/packetfu/protos/arp/header.rb +160 -0
  17. data/lib/packetfu/protos/arp/mixin.rb +38 -0
  18. data/lib/packetfu/protos/eth.rb +5 -247
  19. data/lib/packetfu/protos/eth/header.rb +247 -0
  20. data/lib/packetfu/protos/eth/mixin.rb +20 -0
  21. data/lib/packetfu/protos/hsrp.rb +13 -123
  22. data/lib/packetfu/protos/hsrp/header.rb +120 -0
  23. data/lib/packetfu/protos/hsrp/mixin.rb +31 -0
  24. data/lib/packetfu/protos/icmp.rb +10 -97
  25. data/lib/packetfu/protos/icmp/header.rb +93 -0
  26. data/lib/packetfu/protos/icmp/mixin.rb +17 -0
  27. data/lib/packetfu/protos/ip.rb +7 -295
  28. data/lib/packetfu/protos/ip/header.rb +335 -0
  29. data/lib/packetfu/protos/ip/mixin.rb +43 -0
  30. data/lib/packetfu/protos/ipv6.rb +7 -191
  31. data/lib/packetfu/protos/ipv6/header.rb +190 -0
  32. data/lib/packetfu/protos/ipv6/mixin.rb +31 -0
  33. data/lib/packetfu/protos/tcp.rb +13 -939
  34. data/lib/packetfu/protos/tcp/ecn.rb +42 -0
  35. data/lib/packetfu/protos/tcp/flags.rb +83 -0
  36. data/lib/packetfu/protos/tcp/header.rb +307 -0
  37. data/lib/packetfu/protos/tcp/hlen.rb +40 -0
  38. data/lib/packetfu/protos/tcp/mixin.rb +48 -0
  39. data/lib/packetfu/protos/tcp/option.rb +323 -0
  40. data/lib/packetfu/protos/tcp/options.rb +106 -0
  41. data/lib/packetfu/protos/tcp/reserved.rb +42 -0
  42. data/lib/packetfu/protos/udp.rb +12 -110
  43. data/lib/packetfu/protos/udp/header.rb +107 -0
  44. data/lib/packetfu/protos/udp/mixin.rb +23 -0
  45. data/lib/packetfu/utils.rb +24 -24
  46. data/lib/packetfu/version.rb +1 -1
  47. data/packetfu.gemspec +2 -2
  48. data/test/test_ip.rb +0 -19
  49. data/test/test_octets.rb +18 -21
  50. data/test/test_tcp.rb +10 -0
  51. data/test/test_udp.rb +17 -0
  52. metadata +79 -50
@@ -0,0 +1,42 @@
1
+ module PacketFu
2
+ # Implements the Explict Congestion Notification for TCPHeader.
3
+ #
4
+ # ==== Header Definition
5
+ #
6
+ #
7
+ # Fixnum (1 bit) :n
8
+ # Fixnum (1 bit) :c
9
+ # Fixnum (1 bit) :e
10
+ class TcpEcn < Struct.new(:n, :c, :e)
11
+
12
+ include StructFu
13
+
14
+ def initialize(args={})
15
+ super(args[:n], args[:c], args[:e]) if args
16
+ end
17
+
18
+ # Returns the TcpEcn field as an integer... even though it's going
19
+ # to be split across a byte boundary.
20
+ def to_i
21
+ (n.to_i << 2) + (c.to_i << 1) + e.to_i
22
+ end
23
+
24
+ # Reads a string to populate the object.
25
+ def read(str)
26
+ force_binary(str)
27
+ return self if str.nil? || str.size < 2
28
+ if 1.respond_to? :ord
29
+ byte1 = str[0].ord
30
+ byte2 = str[1].ord
31
+ else
32
+ byte1 = str[0]
33
+ byte2 = str[1]
34
+ end
35
+ self[:n] = byte1 & 0b00000001 == 0b00000001 ? 1 : 0
36
+ self[:c] = byte2 & 0b10000000 == 0b10000000 ? 1 : 0
37
+ self[:e] = byte2 & 0b01000000 == 0b01000000 ? 1 : 0
38
+ self
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,83 @@
1
+ module PacketFu
2
+ # Implements flags for TCPHeader.
3
+ #
4
+ # ==== Header Definition
5
+ #
6
+ # Fixnum (1 bit) :urg
7
+ # Fixnum (1 bit) :ack
8
+ # Fixnum (1 bit) :psh
9
+ # Fixnum (1 bit) :rst
10
+ # Fixnum (1 bit) :syn
11
+ # Fixnum (1 bit) :fin
12
+ #
13
+ # Flags can typically be set by setting them either to 1 or 0, or to true or false.
14
+ class TcpFlags < Struct.new(:urg, :ack, :psh, :rst, :syn, :fin)
15
+
16
+ include StructFu
17
+
18
+ def initialize(args={})
19
+ # This technique attemts to ensure that flags are always 0 (off)
20
+ # or 1 (on). Statements like nil and false shouldn't be lurking in here.
21
+ if args.nil? || args.size.zero?
22
+ super( 0, 0, 0, 0, 0, 0)
23
+ else
24
+ super(
25
+ (args[:urg] ? 1 : 0),
26
+ (args[:ack] ? 1 : 0),
27
+ (args[:psh] ? 1 : 0),
28
+ (args[:rst] ? 1 : 0),
29
+ (args[:syn] ? 1 : 0),
30
+ (args[:fin] ? 1 : 0)
31
+ )
32
+ end
33
+ end
34
+
35
+ # Returns the TcpFlags as an integer.
36
+ # Also not a great candidate for to_s due to the short bitspace.
37
+ def to_i
38
+ (urg.to_i << 5) + (ack.to_i << 4) + (psh.to_i << 3) +
39
+ (rst.to_i << 2) + (syn.to_i << 1) + fin.to_i
40
+ end
41
+
42
+ # Helper to determine if this flag is a 1 or a 0.
43
+ def zero_or_one(i=0)
44
+ if i == 0 || i == false || i == nil
45
+ 0
46
+ else
47
+ 1
48
+ end
49
+ end
50
+
51
+ # Setter for the Urgent flag.
52
+ def urg=(i); self[:urg] = zero_or_one(i); end
53
+ # Setter for the Acknowlege flag.
54
+ def ack=(i); self[:ack] = zero_or_one(i); end
55
+ # Setter for the Push flag.
56
+ def psh=(i); self[:psh] = zero_or_one(i); end
57
+ # Setter for the Reset flag.
58
+ def rst=(i); self[:rst] = zero_or_one(i); end
59
+ # Setter for the Synchronize flag.
60
+ def syn=(i); self[:syn] = zero_or_one(i); end
61
+ # Setter for the Finish flag.
62
+ def fin=(i); self[:fin] = zero_or_one(i); end
63
+
64
+ # Reads a string to populate the object.
65
+ def read(str)
66
+ force_binary(str)
67
+ return self if str.nil?
68
+ if 1.respond_to? :ord
69
+ byte = str[0].ord
70
+ else
71
+ byte = str[0]
72
+ end
73
+ self[:urg] = byte & 0b00100000 == 0b00100000 ? 1 : 0
74
+ self[:ack] = byte & 0b00010000 == 0b00010000 ? 1 : 0
75
+ self[:psh] = byte & 0b00001000 == 0b00001000 ? 1 : 0
76
+ self[:rst] = byte & 0b00000100 == 0b00000100 ? 1 : 0
77
+ self[:syn] = byte & 0b00000010 == 0b00000010 ? 1 : 0
78
+ self[:fin] = byte & 0b00000001 == 0b00000001 ? 1 : 0
79
+ self
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,307 @@
1
+ require 'packetfu/protos/tcp/reserved'
2
+ require 'packetfu/protos/tcp/hlen'
3
+ require 'packetfu/protos/tcp/ecn'
4
+ require 'packetfu/protos/tcp/flags'
5
+ require 'packetfu/protos/tcp/option'
6
+ require 'packetfu/protos/tcp/options'
7
+
8
+
9
+ module PacketFu
10
+ # TCPHeader is a complete TCP struct, used in TCPPacket. Most IP traffic is TCP-based, by
11
+ # volume.
12
+ #
13
+ # For more on TCP packets, see http://www.networksorcery.com/enp/protocol/tcp.htm
14
+ #
15
+ # ==== Header Definition
16
+ #
17
+ # Int16 :tcp_src Default: random
18
+ # Int16 :tcp_dst
19
+ # Int32 :tcp_seq Default: random
20
+ # Int32 :tcp_ack
21
+ # TcpHlen :tcp_hlen Default: 5 # Must recalc as options are set.
22
+ # TcpReserved :tcp_reserved Default: 0
23
+ # TcpEcn :tcp_ecn
24
+ # TcpFlags :tcp_flags
25
+ # Int16 :tcp_win, Default: 0 # WinXP's default syn packet
26
+ # Int16 :tcp_sum, Default: calculated # Must set this upon generation.
27
+ # Int16 :tcp_urg
28
+ # TcpOptions :tcp_opts
29
+ # String :body
30
+ #
31
+ # See also TcpHlen, TcpReserved, TcpEcn, TcpFlags, TcpOpts
32
+ class TCPHeader < Struct.new(:tcp_src, :tcp_dst,
33
+ :tcp_seq,
34
+ :tcp_ack,
35
+ :tcp_hlen, :tcp_reserved, :tcp_ecn, :tcp_flags, :tcp_win,
36
+ :tcp_sum, :tcp_urg,
37
+ :tcp_opts, :body)
38
+ include StructFu
39
+
40
+ def initialize(args={})
41
+ @random_seq = rand(0xffffffff)
42
+ @random_src = rand_port
43
+ super(
44
+ Int16.new(args[:tcp_src] || tcp_calc_src),
45
+ Int16.new(args[:tcp_dst]),
46
+ Int32.new(args[:tcp_seq] || tcp_calc_seq),
47
+ Int32.new(args[:tcp_ack]),
48
+ TcpHlen.new(:hlen => (args[:tcp_hlen] || 5)),
49
+ TcpReserved.new(args[:tcp_reserved] || 0),
50
+ TcpEcn.new(args[:tcp_ecn]),
51
+ TcpFlags.new(args[:tcp_flags]),
52
+ Int16.new(args[:tcp_win] || 0x4000),
53
+ Int16.new(args[:tcp_sum] || 0),
54
+ Int16.new(args[:tcp_urg]),
55
+ TcpOptions.new.read(args[:tcp_opts]),
56
+ StructFu::String.new.read(args[:body])
57
+ )
58
+ end
59
+
60
+ attr_accessor :flavor
61
+
62
+ # Helper function to create the string for Hlen, Reserved, ECN, and Flags.
63
+ def bits_to_s
64
+ bytes = []
65
+ bytes[0] = (self[:tcp_hlen].to_i << 4) +
66
+ (self[:tcp_reserved].to_i << 1) +
67
+ self[:tcp_ecn].n.to_i
68
+ bytes[1] = (self[:tcp_ecn].c.to_i << 7) +
69
+ (self[:tcp_ecn].e.to_i << 6) +
70
+ self[:tcp_flags].to_i
71
+ bytes.pack("CC")
72
+ end
73
+
74
+ # Returns the object in string form.
75
+ def to_s
76
+ hdr = self.to_a.map do |x|
77
+ if x.kind_of? TcpHlen
78
+ bits_to_s
79
+ elsif x.kind_of? TcpReserved
80
+ next
81
+ elsif x.kind_of? TcpEcn
82
+ next
83
+ elsif x.kind_of? TcpFlags
84
+ next
85
+ else
86
+ x.to_s
87
+ end
88
+ end
89
+ hdr.flatten.join
90
+ end
91
+
92
+ # Reads a string to populate the object.
93
+ def read(str)
94
+ force_binary(str)
95
+ return self if str.nil?
96
+ self[:tcp_src].read(str[0,2])
97
+ self[:tcp_dst].read(str[2,2])
98
+ self[:tcp_seq].read(str[4,4])
99
+ self[:tcp_ack].read(str[8,4])
100
+ self[:tcp_hlen].read(str[12,1])
101
+ self[:tcp_reserved].read(str[12,1])
102
+ self[:tcp_ecn].read(str[12,2])
103
+ self[:tcp_flags].read(str[13,1])
104
+ self[:tcp_win].read(str[14,2])
105
+ self[:tcp_sum].read(str[16,2])
106
+ self[:tcp_urg].read(str[18,2])
107
+ self[:tcp_opts].read(str[20,((self[:tcp_hlen].to_i * 4) - 20)])
108
+ self[:body].read(str[(self[:tcp_hlen].to_i * 4),str.size])
109
+ self
110
+ end
111
+
112
+ # Setter for the TCP source port.
113
+ def tcp_src=(i); typecast i; end
114
+ # Getter for the TCP source port.
115
+ def tcp_src; self[:tcp_src].to_i; end
116
+ # Setter for the TCP destination port.
117
+ def tcp_dst=(i); typecast i; end
118
+ # Getter for the TCP destination port.
119
+ def tcp_dst; self[:tcp_dst].to_i; end
120
+ # Setter for the TCP sequence number.
121
+ def tcp_seq=(i); typecast i; end
122
+ # Getter for the TCP sequence number.
123
+ def tcp_seq; self[:tcp_seq].to_i; end
124
+ # Setter for the TCP ackowlegement number.
125
+ def tcp_ack=(i); typecast i; end
126
+ # Getter for the TCP ackowlegement number.
127
+ def tcp_ack; self[:tcp_ack].to_i; end
128
+ # Setter for the TCP window size number.
129
+ def tcp_win=(i); typecast i; end
130
+ # Getter for the TCP window size number.
131
+ def tcp_win; self[:tcp_win].to_i; end
132
+ # Setter for the TCP checksum.
133
+ def tcp_sum=(i); typecast i; end
134
+ # Getter for the TCP checksum.
135
+ def tcp_sum; self[:tcp_sum].to_i; end
136
+ # Setter for the TCP urgent field.
137
+ def tcp_urg=(i); typecast i; end
138
+ # Getter for the TCP urgent field.
139
+ def tcp_urg; self[:tcp_urg].to_i; end
140
+
141
+ # Getter for the TCP Header Length value.
142
+ def tcp_hlen; self[:tcp_hlen].to_i; end
143
+ # Setter for the TCP Header Length value. Can take
144
+ # either a string or an integer. Note that if it's
145
+ # a string, the top four bits are used.
146
+ def tcp_hlen=(i)
147
+ case i
148
+ when PacketFu::TcpHlen
149
+ self[:tcp_hlen] = i
150
+ when Numeric
151
+ self[:tcp_hlen] = TcpHlen.new(:hlen => i.to_i)
152
+ else
153
+ self[:tcp_hlen].read(i)
154
+ end
155
+ end
156
+
157
+ # Getter for the TCP Reserved field.
158
+ def tcp_reserved; self[:tcp_reserved].to_i; end
159
+ # Setter for the TCP Reserved field.
160
+ def tcp_reserved=(i)
161
+ case i
162
+ when PacketFu::TcpReserved
163
+ self[:tcp_reserved]=i
164
+ when Numeric
165
+ args = {}
166
+ args[:r1] = (i & 0b100) >> 2
167
+ args[:r2] = (i & 0b010) >> 1
168
+ args[:r3] = (i & 0b001)
169
+ self[:tcp_reserved] = TcpReserved.new(args)
170
+ else
171
+ self[:tcp_reserved].read(i)
172
+ end
173
+ end
174
+
175
+ # Getter for the ECN bits.
176
+ def tcp_ecn; self[:tcp_ecn].to_i; end
177
+ # Setter for the ECN bits.
178
+ def tcp_ecn=(i)
179
+ case i
180
+ when PacketFu::TcpEcn
181
+ self[:tcp_ecn]=i
182
+ when Numeric
183
+ args = {}
184
+ args[:n] = (i & 0b100) >> 2
185
+ args[:c] = (i & 0b010) >> 1
186
+ args[:e] = (i & 0b001)
187
+ self[:tcp_ecn] = TcpEcn.new(args)
188
+ else
189
+ self[:tcp_ecn].read(i)
190
+ end
191
+ end
192
+
193
+ # Getter for TCP Options.
194
+ def tcp_opts; self[:tcp_opts].to_s; end
195
+ # Setter for TCP Options.
196
+ def tcp_opts=(i)
197
+ case i
198
+ when PacketFu::TcpOptions
199
+ self[:tcp_opts]=i
200
+ else
201
+ self[:tcp_opts].read(i)
202
+ end
203
+ end
204
+
205
+ # Resets the sequence number to a new random number.
206
+ def tcp_calc_seq; @random_seq; end
207
+ # Resets the source port to a new random number.
208
+ def tcp_calc_src; @random_src; end
209
+
210
+ # Returns the actual length of the TCP options.
211
+ def tcp_opts_len
212
+ self[:tcp_opts].to_s.size
213
+ end
214
+
215
+ # Sets and returns the true length of the TCP Header.
216
+ # TODO: Think about making all the option stuff safer.
217
+ def tcp_calc_hlen
218
+ self[:tcp_hlen] = TcpHlen.new(:hlen => ((20 + tcp_opts_len) / 4))
219
+ end
220
+
221
+ # Generates a random high port. This is affected by packet flavor.
222
+ def rand_port
223
+ rand(0xffff - 1025) + 1025
224
+ end
225
+
226
+ # Gets a more readable option list.
227
+ def tcp_options
228
+ self[:tcp_opts].decode
229
+ end
230
+
231
+ # Gets a more readable flags list
232
+ def tcp_flags_dotmap
233
+ dotmap = tcp_flags.members.map do |flag|
234
+ status = self.tcp_flags.send flag
235
+ status == 0 ? "." : flag.to_s.upcase[0].chr
236
+ end
237
+ dotmap.join
238
+ end
239
+
240
+ # Sets a more readable option list.
241
+ def tcp_options=(arg)
242
+ self[:tcp_opts].encode arg
243
+ end
244
+
245
+ # Equivalent to tcp_src.
246
+ def tcp_sport
247
+ self.tcp_src.to_i
248
+ end
249
+
250
+ # Equivalent to tcp_src=.
251
+ def tcp_sport=(arg)
252
+ self.tcp_src=(arg)
253
+ end
254
+
255
+ # Equivalent to tcp_dst.
256
+ def tcp_dport
257
+ self.tcp_dst.to_i
258
+ end
259
+
260
+ # Equivalent to tcp_dst=.
261
+ def tcp_dport=(arg)
262
+ self.tcp_dst=(arg)
263
+ end
264
+
265
+ # Recalculates calculated fields for TCP (except checksum which is at the Packet level).
266
+ def tcp_recalc(arg=:all)
267
+ case arg
268
+ when :tcp_hlen
269
+ tcp_calc_hlen
270
+ when :tcp_src
271
+ @random_tcp_src = rand_port
272
+ when :tcp_sport
273
+ @random_tcp_src = rand_port
274
+ when :tcp_seq
275
+ @random_tcp_seq = rand(0xffffffff)
276
+ when :all
277
+ tcp_calc_hlen
278
+ @random_tcp_src = rand_port
279
+ @random_tcp_seq = rand(0xffffffff)
280
+ else
281
+ raise ArgumentError, "No such field `#{arg}'"
282
+ end
283
+ end
284
+
285
+ # Readability aliases
286
+
287
+ alias :tcp_flags_readable :tcp_flags_dotmap
288
+
289
+ def tcp_ack_readable
290
+ "0x%08x" % tcp_ack
291
+ end
292
+
293
+ def tcp_seq_readable
294
+ "0x%08x" % tcp_seq
295
+ end
296
+
297
+ def tcp_sum_readable
298
+ "0x%04x" % tcp_sum
299
+ end
300
+
301
+ def tcp_opts_readable
302
+ tcp_options
303
+ end
304
+
305
+ end
306
+
307
+ end
@@ -0,0 +1,40 @@
1
+ module PacketFu
2
+ # Implements the Header Length for TCPHeader.
3
+ #
4
+ # ==== Header Definition
5
+ #
6
+ # Fixnum (4 bits) :hlen
7
+ class TcpHlen < Struct.new(:hlen)
8
+
9
+ include StructFu
10
+
11
+ def initialize(args={})
12
+ super(args[:hlen])
13
+ end
14
+
15
+ # Returns the TcpHlen field as an integer. Note these will become the high
16
+ # bits at the TCP header's offset, even though the lower 4 bits
17
+ # will be further chopped up.
18
+ def to_i
19
+ hlen.to_i & 0b1111
20
+ end
21
+
22
+ # Reads a string to populate the object.
23
+ def read(str)
24
+ force_binary(str)
25
+ return self if str.nil? || str.size.zero?
26
+ if 1.respond_to? :ord
27
+ self[:hlen] = (str[0].ord & 0b11110000) >> 4
28
+ else
29
+ self[:hlen] = (str[0] & 0b11110000) >> 4
30
+ end
31
+ self
32
+ end
33
+
34
+ # Returns the object in string form.
35
+ def to_s
36
+ [self.to_i].pack("C")
37
+ end
38
+
39
+ end
40
+ end