dot11 0.1.0
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.
- data/README.markdown +4 -0
- data/lib/dot11.rb +17 -0
- data/lib/dot11/dot11.rb +974 -0
- data/lib/dot11/macaddress.rb +51 -0
- data/lib/dot11/packet.rb +45 -0
- data/lib/dot11/packetset.rb +253 -0
- data/lib/dot11/radiotap.rb +37 -0
- data/lib/dot11/raw.rb +19 -0
- data/tests/dot11_test.rb +133 -0
- metadata +64 -0
data/README.markdown
ADDED
data/lib/dot11.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class String
|
2
|
+
def indent(depth)
|
3
|
+
indented = ""
|
4
|
+
self.each_line do |line|
|
5
|
+
indented += " " * depth + line
|
6
|
+
end
|
7
|
+
|
8
|
+
indented
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'dot11', 'packet'))
|
13
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'dot11', 'packetset'))
|
14
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'dot11', 'macaddress'))
|
15
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'dot11', 'dot11'))
|
16
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'dot11', 'raw'))
|
17
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'dot11', 'radiotap'))
|
data/lib/dot11/dot11.rb
ADDED
@@ -0,0 +1,974 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'macaddress'))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'packet'))
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'raw'))
|
4
|
+
|
5
|
+
module Dot11
|
6
|
+
class Dot11 < Packet
|
7
|
+
@@TYPENAMES = [["association request", "association response", "reassociation request", "reassociation response", "probe request", "probe response", "reserved0", "reserved1", "beacon", "ATIM", "disassociation", "authorization", "deauthorization", "reserved2", "reserved3", "reserved4"],
|
8
|
+
["reserved0", "reserved1", "reserved2", "reserved3", "reserved4", "reserved5", "reserved6", "reserved7", "reserved8", "PS-poll", "RTS", "CTS", "ACK", "CF-end", "CF-end + CF-ack"],
|
9
|
+
["data", "data + CF-ack", "data + CF-poll", "data + CF-ack + CF-poll", "null function (no data)", "CF-ack (no data)", "CF-poll (no data)", "CF-ack + CF-poll (no data)", "reserved0", "reserved1", "reserved2", "reserved3", "reserved4", "reserved5", "reserved6", "reserved7"],
|
10
|
+
["reserved0", "reserved1", "reserved2", "reserved3", "reserved4", "reserved5", "reserved6", "reserved7", "reserved8", "reserved9", "reserved10", "reserved11", "reserved12", "reserved13", "reserved14", "reserved15"]]
|
11
|
+
|
12
|
+
def subtype
|
13
|
+
@subtype ||= 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def subtype=(other)
|
17
|
+
@subtype = other
|
18
|
+
end
|
19
|
+
|
20
|
+
def type
|
21
|
+
@type ||= 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def type=(other)
|
25
|
+
@type = other
|
26
|
+
end
|
27
|
+
|
28
|
+
def version
|
29
|
+
@version ||= 0
|
30
|
+
end
|
31
|
+
|
32
|
+
def version=(other)
|
33
|
+
@version = other
|
34
|
+
end
|
35
|
+
|
36
|
+
def flags
|
37
|
+
@flags ||= 0
|
38
|
+
end
|
39
|
+
|
40
|
+
def flags=(other)
|
41
|
+
@flags = other
|
42
|
+
end
|
43
|
+
|
44
|
+
def duration
|
45
|
+
@duration ||= 0
|
46
|
+
end
|
47
|
+
|
48
|
+
def duration=(other)
|
49
|
+
@duration = other
|
50
|
+
end
|
51
|
+
|
52
|
+
def addr1
|
53
|
+
@addr1 ||= 0
|
54
|
+
end
|
55
|
+
|
56
|
+
def addr1=(other)
|
57
|
+
if other.kind_of?(Integer) || other.kind_of?(String)
|
58
|
+
@addr1 = MACAddress.new(other)
|
59
|
+
elsif other.kind_of?(MACAddress)
|
60
|
+
@addr1 = other
|
61
|
+
else
|
62
|
+
raise "Unrecognized addr #{other.inspect}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def addr2
|
67
|
+
@addr2 ||= 0
|
68
|
+
end
|
69
|
+
|
70
|
+
def addr2=(other)
|
71
|
+
if other.kind_of?(Integer) || other.kind_of?(String)
|
72
|
+
@addr2 = MACAddress.new(other)
|
73
|
+
elsif other.kind_of?(MACAddress)
|
74
|
+
@addr2 = other
|
75
|
+
else
|
76
|
+
raise "Unrecognized addr #{other.inspect}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def addr3
|
81
|
+
@addr3 ||= 0
|
82
|
+
end
|
83
|
+
|
84
|
+
def addr3=(other)
|
85
|
+
if other.kind_of?(Integer) || other.kind_of?(String)
|
86
|
+
@addr3 = MACAddress.new(other)
|
87
|
+
elsif other.kind_of?(MACAddress)
|
88
|
+
@addr3 = other
|
89
|
+
else
|
90
|
+
raise "Unrecognized addr #{other.inspect}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def sc
|
95
|
+
@sc ||= 0
|
96
|
+
end
|
97
|
+
|
98
|
+
def sc=(other)
|
99
|
+
@sc = other
|
100
|
+
end
|
101
|
+
|
102
|
+
def addr4
|
103
|
+
@addr4 ||= 0
|
104
|
+
end
|
105
|
+
|
106
|
+
def addr4=
|
107
|
+
if other.kind_of?(Integer) || other.kind_of?(String)
|
108
|
+
@addr4 = MACAddress.new(other)
|
109
|
+
elsif other.kind_of?(MACAddress)
|
110
|
+
@addr4 = other
|
111
|
+
else
|
112
|
+
raise "Unrecognized addr #{other.inspect}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def payload=(other)
|
117
|
+
@payload = other
|
118
|
+
end
|
119
|
+
|
120
|
+
def data
|
121
|
+
buffer = [(subtype << 4) | (type << 2) | version, flags, duration].concat(addr1.to_arr).pack("CCSC6")
|
122
|
+
|
123
|
+
if (type == 1 && [0x0a, 0x0b, 0x0e, 0x0f].include?(subtype)) || (type != 1)
|
124
|
+
buffer += addr2.to_arr.pack("C6")
|
125
|
+
end
|
126
|
+
|
127
|
+
if [0, 2].include?(type)
|
128
|
+
buffer += addr3.to_arr.pack("C6")
|
129
|
+
end
|
130
|
+
|
131
|
+
if type != 1
|
132
|
+
buffer += [sc].pack("v")
|
133
|
+
end
|
134
|
+
|
135
|
+
if type == 2 && flags & 0x03 == 0x03
|
136
|
+
buffer += addr4.to_arr.pack("C6")
|
137
|
+
end
|
138
|
+
|
139
|
+
if payload
|
140
|
+
buffer += payload.data
|
141
|
+
end
|
142
|
+
|
143
|
+
buffer
|
144
|
+
end
|
145
|
+
|
146
|
+
def ==(other)
|
147
|
+
eql?(other)
|
148
|
+
end
|
149
|
+
|
150
|
+
def eql?(other)
|
151
|
+
return false unless other.kind_of?(Dot11)
|
152
|
+
|
153
|
+
basics = subtype.eql?(other.subtype) && type.eql?(other.type) && version.eql?(other.version) &&
|
154
|
+
flags.eql?(other.flags) && duration.eql?(other.duration) && addr1.eql?(other.addr1)
|
155
|
+
|
156
|
+
return false unless basics
|
157
|
+
|
158
|
+
if (type == 1 && [0x0a, 0x0b, 0x0e, 0x0f].include?(subtype)) || (type != 1)
|
159
|
+
return false unless addr2.eql?(other.addr2)
|
160
|
+
end
|
161
|
+
|
162
|
+
if [0, 2].include?(type)
|
163
|
+
return false unless addr3.eql?(other.addr3)
|
164
|
+
end
|
165
|
+
|
166
|
+
if type != 1
|
167
|
+
return false unless sc.eql?(other.sc)
|
168
|
+
end
|
169
|
+
|
170
|
+
if type == 2 && flags & 0x03 == 0x03
|
171
|
+
return false unless addr4.eql?(other.addr4)
|
172
|
+
end
|
173
|
+
|
174
|
+
return true
|
175
|
+
end
|
176
|
+
|
177
|
+
def /(other)
|
178
|
+
if @payload.nil?
|
179
|
+
@payload = other
|
180
|
+
return self
|
181
|
+
end
|
182
|
+
|
183
|
+
if @payload.respond_to?(:elements)
|
184
|
+
@payload.elements << other
|
185
|
+
end
|
186
|
+
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
alias to_s data
|
191
|
+
|
192
|
+
def inspect
|
193
|
+
binary_flags = flags.to_s(2)
|
194
|
+
flag_names = ['to-DS', 'from-DS', 'MF', 'retry', 'pw-mgt', 'MD', 'wep', 'order']
|
195
|
+
set_flags = []
|
196
|
+
|
197
|
+
8.times do |i|
|
198
|
+
set_flags << flag_names[i] if binary_flags[7 - i] == ?1
|
199
|
+
end
|
200
|
+
|
201
|
+
"Dot11\n" + if @corrupt then " (corrupt)" else "" end +
|
202
|
+
"----------------\n" +
|
203
|
+
"type: ...... #{type} (#{%w(management control data reserved)[type]})\n" +
|
204
|
+
"subtype: ... #{subtype} (#{@@TYPENAMES[type][subtype]})\n" +
|
205
|
+
"version: ... #{version}\n" +
|
206
|
+
"flags: ..... #{"%#02x" % flags} (#{"0" * (8 - flags.to_s(2).length) + flags.to_s(2)}#{if set_flags.size > 0 then ' : ' + set_flags.join(', ') else '' end})\n" +
|
207
|
+
"duration: .. #{duration}\n" +
|
208
|
+
"addr1: ..... #{addr1}\n" +
|
209
|
+
(if addr2 then "addr2: ..... #{addr2}\n" else "" end) +
|
210
|
+
(if addr3 then "addr3: ..... #{addr3}\n" else "" end) +
|
211
|
+
(if sc then "sc: ........ #{"%#02x" % sc} (fragment: #{sc & 0x0F}; sequence: #{(sc & 0xFFF0) >> 4})\n" else "" end) +
|
212
|
+
(if addr4 then "addr4: ..... #{addr4}\n" else "" end) +
|
213
|
+
(if payload then "payload:\n#{payload.to_s.indent(6)}" else "" end)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Lazily dissect the payload
|
217
|
+
def payload
|
218
|
+
return @payload if @payload
|
219
|
+
|
220
|
+
payload_class = if (@flags & 0x40) == 0x40
|
221
|
+
Dot11WEP
|
222
|
+
elsif @type == 0
|
223
|
+
[ Dot11AssoReq, Dot11AssoResp, Dot11ReassoReq, Dot11ReassoResp, Dot11ProbeReq, Dot11ProbeResp, nil, nil,
|
224
|
+
Dot11Beacon, Dot11ATIM, Dot11Disas, Dot11Auth, Dot11Deauth, nil, nil, nil,
|
225
|
+
nil, nil, nil, nil, nil, nil, nil, nil,
|
226
|
+
nil, nil, nil, nil, nil, nil, nil ][@subtype]
|
227
|
+
elsif @type == 2
|
228
|
+
if @subtype == 0
|
229
|
+
Dot11Data
|
230
|
+
elsif @subtype == 4
|
231
|
+
Dot11NullData
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
return nil if payload_class.nil?
|
236
|
+
|
237
|
+
@payload = payload_class.new(@rest) unless (payload_class == Dot11NullData || @rest.nil? || @rest.empty?)
|
238
|
+
end
|
239
|
+
|
240
|
+
def self.fields()
|
241
|
+
# Why can't ruby have ordered maps??
|
242
|
+
# The offsets are unnecessary but removing them would have been even more complicated
|
243
|
+
# TODO: remove offsets and compute possible offsets for each field automatically
|
244
|
+
[[:subtype, {:type => :int, :offset => 0, :size => 1, :bitrange => 4..7}],
|
245
|
+
[:type, {:type => :int, :offset => 0, :size => 1, :bitrange => 2..3}],
|
246
|
+
[:version, {:type => :int, :offset => 0, :size => 1, :bitrange => 0..1}],
|
247
|
+
[:flags, {:type => :int, :offset => 1, :size => 1}],
|
248
|
+
[:duration, {:type => :int, :offset => 2, :size => 2}],
|
249
|
+
[:addr1, {:type => :mac, :offset => 4, :size => 6}],
|
250
|
+
# Ugly syntax! TODO: come up with a better way to represent negation than a one-element array
|
251
|
+
[:addr2, {:type => :mac, :offset => 10, :size => 6, :condition => [{:subtype => [0x0a, 0x0b, 0x0e, 0x0f]}, {:type => [1]}]}],
|
252
|
+
[:addr3, {:type => :mac, :offset => {[{:subtype => [0x0a, 0x0b, 0x0e, 0x0f]}, {:type => [1]}] => 16, :else => 10}, :size => 6, :condition => {:type => [0, 2]}}],
|
253
|
+
#[:sc, {:type => :int, :size => 2, :offset => {}, => :condition => {:type => [1]}}]
|
254
|
+
]
|
255
|
+
end
|
256
|
+
|
257
|
+
private
|
258
|
+
|
259
|
+
def dissect(data)
|
260
|
+
fields = data.unpack("CCSC6")
|
261
|
+
|
262
|
+
@subtype = (fields[0] & 0xF0) >> 4
|
263
|
+
@type = (fields[0] & 0x0C) >> 2
|
264
|
+
@version = fields[0] & 0x03
|
265
|
+
|
266
|
+
@flags = fields[1]
|
267
|
+
@duration = fields[2]
|
268
|
+
|
269
|
+
# The array2mac calculations could be lazy if we really needed speed
|
270
|
+
@addr1 = MACAddress.new(fields[3..-1])
|
271
|
+
|
272
|
+
@rest = data[10..-1]
|
273
|
+
|
274
|
+
if (@type == 1 && [0x0a, 0x0b, 0x0e, 0x0f].include?(@subtype)) || (@type != 1)
|
275
|
+
if !@rest || @rest.empty?
|
276
|
+
@corrupt = true
|
277
|
+
return
|
278
|
+
end
|
279
|
+
|
280
|
+
@addr2 = MACAddress.new(@rest.unpack("C6"))
|
281
|
+
@rest = @rest[6..-1]
|
282
|
+
end
|
283
|
+
|
284
|
+
if [0, 2].include?(@type)
|
285
|
+
if !@rest || @rest.empty?
|
286
|
+
@corrupt = true
|
287
|
+
return
|
288
|
+
end
|
289
|
+
|
290
|
+
@addr3 = MACAddress.new(@rest.unpack("C6"))
|
291
|
+
@rest = @rest[6..-1]
|
292
|
+
end
|
293
|
+
|
294
|
+
if @type != 1
|
295
|
+
if !@rest || @rest.empty?
|
296
|
+
@corrupt = true
|
297
|
+
return
|
298
|
+
end
|
299
|
+
|
300
|
+
@sc = @rest.unpack("v")[0]
|
301
|
+
@rest = @rest[2..-1]
|
302
|
+
end
|
303
|
+
|
304
|
+
if @type == 2 && @flags & 0x03 == 0x03
|
305
|
+
if !@rest || @rest.empty?
|
306
|
+
@corrupt = true
|
307
|
+
return
|
308
|
+
end
|
309
|
+
|
310
|
+
@addr4 = MACAddress.new(@rest.unpack("C6"))
|
311
|
+
@rest = @rest[6..-1]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
class Dot11Elt < Packet
|
316
|
+
attr_accessor :id, :info_length, :info
|
317
|
+
|
318
|
+
def Dot11Elt.register_element(id, klass)
|
319
|
+
@@registered_elements ||= {}
|
320
|
+
|
321
|
+
@@registered_elements[id] = klass
|
322
|
+
end
|
323
|
+
|
324
|
+
def data
|
325
|
+
buffer = [id, info_length].pack("CC")
|
326
|
+
|
327
|
+
buffer += info
|
328
|
+
end
|
329
|
+
|
330
|
+
def to_s
|
331
|
+
"Dot11Elt\n" +
|
332
|
+
"------------\n" +
|
333
|
+
"id: ............ #{id}\n" +
|
334
|
+
"info_length: ... #{info_length}\n" +
|
335
|
+
"info: .......... #{info.inspect}\n"
|
336
|
+
end
|
337
|
+
|
338
|
+
private
|
339
|
+
|
340
|
+
def dissect(data)
|
341
|
+
fields = data.unpack("CC")
|
342
|
+
|
343
|
+
@id = fields[0]
|
344
|
+
@info_length = fields[1]
|
345
|
+
|
346
|
+
@info = data[2, @info_length]
|
347
|
+
|
348
|
+
@rest = data[2 + @info_length..-1]
|
349
|
+
end
|
350
|
+
|
351
|
+
class << self
|
352
|
+
# Hook into new to "subclass on the fly"
|
353
|
+
alias old_new new
|
354
|
+
|
355
|
+
def new(parameters)
|
356
|
+
return old_new(parameters) if self != Dot11Elt
|
357
|
+
|
358
|
+
if parameters.kind_of?(String)
|
359
|
+
elt_id = parameters.unpack("C")[0]
|
360
|
+
|
361
|
+
if @@registered_elements && @@registered_elements[elt_id]
|
362
|
+
return @@registered_elements[elt_id].new(parameters)
|
363
|
+
else
|
364
|
+
elt = Dot11Elt.allocate
|
365
|
+
elt.send(:initialize, parameters)
|
366
|
+
|
367
|
+
return elt
|
368
|
+
end
|
369
|
+
|
370
|
+
elsif parameters.kind_of?(Hash)
|
371
|
+
|
372
|
+
if @@registered_elements && @@registered_elements[parameters[:id]]
|
373
|
+
return @@registered_elements[parameters[:id]].new(parameters)
|
374
|
+
else
|
375
|
+
elt = Dot11Elt.allocate
|
376
|
+
elt.send(:initialize, parameters)
|
377
|
+
|
378
|
+
return elt
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
end
|
383
|
+
|
384
|
+
def element_id(id)
|
385
|
+
@id = id
|
386
|
+
Dot11Elt.register_element(id, self)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
class Dot11EltSSID < Dot11Elt
|
392
|
+
element_id 0
|
393
|
+
|
394
|
+
def essid
|
395
|
+
return @info
|
396
|
+
end
|
397
|
+
|
398
|
+
def to_s
|
399
|
+
"Dot11EltSSID\n" +
|
400
|
+
"-------------\n" +
|
401
|
+
"id: ............ 0\n" +
|
402
|
+
"info_length: ... #{info_length}\n" +
|
403
|
+
"essid: ......... #{info.inspect} (#{essid})\n"
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
class Dot11EltRates < Dot11Elt
|
408
|
+
element_id 1
|
409
|
+
|
410
|
+
def rates
|
411
|
+
return @rates if @rates
|
412
|
+
|
413
|
+
@rates = []
|
414
|
+
|
415
|
+
@info.each_byte do |b|
|
416
|
+
@rates << (b & 0x7f) / 2
|
417
|
+
end
|
418
|
+
|
419
|
+
@rates
|
420
|
+
end
|
421
|
+
|
422
|
+
def to_s
|
423
|
+
"Dot11EltRates\n" +
|
424
|
+
"------------------\n" +
|
425
|
+
"id: ............ 1\n" +
|
426
|
+
"info_length: ... #{info_length}\n" +
|
427
|
+
"rates: ......... #{info.inspect} (#{rates.join(', ')})\n"
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
class Dot11EltESR < Dot11Elt
|
432
|
+
element_id 50
|
433
|
+
|
434
|
+
def rates
|
435
|
+
return @rates if @rates
|
436
|
+
|
437
|
+
@rates = []
|
438
|
+
|
439
|
+
@info.each_byte do |b|
|
440
|
+
@rates << (b & 0x7f) / 2
|
441
|
+
end
|
442
|
+
|
443
|
+
@rates
|
444
|
+
end
|
445
|
+
|
446
|
+
def to_s
|
447
|
+
"Dot11EltESR\n" +
|
448
|
+
"------------------\n" +
|
449
|
+
"id: ............ 50\n" +
|
450
|
+
"info_length: ... #{info_length}\n" +
|
451
|
+
"rates: ......... #{info.inspect} (#{rates.join(', ')})\n"
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
module Dot11EltContainer
|
456
|
+
def elements_by_id
|
457
|
+
hash = {}
|
458
|
+
|
459
|
+
elements.each do |element|
|
460
|
+
hash[element.id] = element
|
461
|
+
end
|
462
|
+
|
463
|
+
hash
|
464
|
+
end
|
465
|
+
|
466
|
+
def elements
|
467
|
+
if @elements.nil?
|
468
|
+
@elements = []
|
469
|
+
|
470
|
+
if @rest
|
471
|
+
dissect_elements(@rest)
|
472
|
+
end
|
473
|
+
|
474
|
+
return @elements
|
475
|
+
end
|
476
|
+
|
477
|
+
@elements
|
478
|
+
end
|
479
|
+
|
480
|
+
def element_data
|
481
|
+
buffer = ""
|
482
|
+
|
483
|
+
elements.each do |element|
|
484
|
+
buffer += element.data
|
485
|
+
end
|
486
|
+
|
487
|
+
buffer
|
488
|
+
end
|
489
|
+
|
490
|
+
def element_to_s
|
491
|
+
buffer = ""
|
492
|
+
|
493
|
+
elements.each do |element|
|
494
|
+
buffer += element.to_s + "\n"
|
495
|
+
end
|
496
|
+
|
497
|
+
buffer
|
498
|
+
end
|
499
|
+
|
500
|
+
def /(other)
|
501
|
+
elements << other
|
502
|
+
|
503
|
+
self
|
504
|
+
end
|
505
|
+
|
506
|
+
private
|
507
|
+
|
508
|
+
def dissect_elements(data)
|
509
|
+
@elements = []
|
510
|
+
|
511
|
+
current_pos = 0
|
512
|
+
|
513
|
+
while current_pos < data.length
|
514
|
+
info_length = data[current_pos, 2].unpack("xC")[0]
|
515
|
+
total_elt_length = 2 + info_length
|
516
|
+
|
517
|
+
@elements << Dot11Elt.new(data[current_pos, total_elt_length])
|
518
|
+
|
519
|
+
current_pos += total_elt_length
|
520
|
+
end
|
521
|
+
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
class Dot11Beacon < Packet
|
526
|
+
attr_accessor :timestamp, :beacon_interval, :capabilities
|
527
|
+
|
528
|
+
include Dot11EltContainer
|
529
|
+
|
530
|
+
def data
|
531
|
+
buffer = [timestamp & 0xFFFFFFFF, (timestamp & 0xFFFFFFFF00000000) >> 32, beacon_interval, capabilities].pack("V2vn")
|
532
|
+
|
533
|
+
buffer += element_data
|
534
|
+
end
|
535
|
+
|
536
|
+
def to_s
|
537
|
+
binary_caps = capabilities.to_s(2)
|
538
|
+
cap_names = ['ESS', 'IBSS', 'CF Pollable', 'CF Poll Request', 'Privacy', 'Reserved5', 'Reserved6', 'Reserved7', 'Reserved8', 'Reserved9', 'Reserved10', 'Reserved11', 'Reserved12', 'Reserved13', 'Reserved14', 'Reserved15', ]
|
539
|
+
set_caps = []
|
540
|
+
|
541
|
+
16.times do |i|
|
542
|
+
set_caps << cap_names[i] if binary_caps[15 -i] == ?1
|
543
|
+
end
|
544
|
+
|
545
|
+
"Dot11Beacon\n" +
|
546
|
+
"-------------------------\n" +
|
547
|
+
"timestamp: ......... #{timestamp}\n" +
|
548
|
+
"beacon_interval: ... #{beacon_interval} (#{beacon_interval * 0.001024} seconds)\n" +
|
549
|
+
"capabilities: ...... #{capabilities} (#{"0" * (16 - binary_caps.length) + binary_caps}#{if set_caps.size > 0 then ' : ' + set_caps.join(', ') else '' end})\n" +
|
550
|
+
"elements:\n" +
|
551
|
+
element_to_s.indent(7)
|
552
|
+
end
|
553
|
+
|
554
|
+
private
|
555
|
+
|
556
|
+
def dissect(data)
|
557
|
+
fields = data.unpack("V2vn")
|
558
|
+
|
559
|
+
p fields
|
560
|
+
|
561
|
+
@timestamp = (fields[1] << 32) | fields[0]
|
562
|
+
@beacon_interval = fields[2]
|
563
|
+
@capabilities = fields[3]
|
564
|
+
|
565
|
+
@rest = data[12..-1]
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
class Dot11ATIM < Packet
|
570
|
+
def dissect(data)
|
571
|
+
raise "Not implemented"
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
class Dot11Disas < Packet
|
576
|
+
attr_accessor :reason
|
577
|
+
|
578
|
+
def data
|
579
|
+
[reason].pack("v")
|
580
|
+
end
|
581
|
+
|
582
|
+
def to_s
|
583
|
+
"Dot11Disas\n" +
|
584
|
+
"-------------\n" +
|
585
|
+
"reason: #{reason}\n"
|
586
|
+
end
|
587
|
+
|
588
|
+
private
|
589
|
+
|
590
|
+
def dissect(data)
|
591
|
+
@reason = data.unpack("v")[0]
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
class Dot11AssoReq < Packet
|
596
|
+
attr_accessor :capabilities, :listen_interval
|
597
|
+
|
598
|
+
include Dot11EltContainer
|
599
|
+
|
600
|
+
def data
|
601
|
+
buffer = [capabilities, listen_interval].pack("nv")
|
602
|
+
|
603
|
+
buffer += element_data
|
604
|
+
end
|
605
|
+
|
606
|
+
def to_s
|
607
|
+
"Dot11AssoReq\n" +
|
608
|
+
"---------------\n" +
|
609
|
+
"capabilities: ...... #{capabilities}\n" +
|
610
|
+
"listen_interval: ... #{listen_interval}\n" +
|
611
|
+
"elements:\n" +
|
612
|
+
element_to_s.indent(7)
|
613
|
+
end
|
614
|
+
|
615
|
+
private
|
616
|
+
|
617
|
+
def dissect(data)
|
618
|
+
fields = data.unpack("nv")
|
619
|
+
|
620
|
+
@capabilities = fields[0]
|
621
|
+
@listen_interval = fields[1]
|
622
|
+
|
623
|
+
@rest = data[4..-1]
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
class Dot11AssoResp < Packet
|
628
|
+
attr_accessor :capabilities, :status, :aid
|
629
|
+
|
630
|
+
include Dot11EltContainer
|
631
|
+
|
632
|
+
def data
|
633
|
+
buffer = [capabilities, status, aid].pack("nvv")
|
634
|
+
|
635
|
+
buffer += element_data
|
636
|
+
end
|
637
|
+
|
638
|
+
def to_s
|
639
|
+
"Dot11AssoResp\n" +
|
640
|
+
"---------------\n" +
|
641
|
+
"capabilities: ... #{capabilities}\n" +
|
642
|
+
"status: ......... #{status}\n" +
|
643
|
+
"aid: ............ #{aid}\n" +
|
644
|
+
"elements:\n" +
|
645
|
+
element_to_s.indent(7)
|
646
|
+
end
|
647
|
+
|
648
|
+
private
|
649
|
+
|
650
|
+
def dissect(data)
|
651
|
+
fields = data.unpack("nvv")
|
652
|
+
|
653
|
+
@capabilities = fields[0]
|
654
|
+
@status = fields[1]
|
655
|
+
@aid = fields[2]
|
656
|
+
|
657
|
+
@rest = data[6..-1]
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
class Dot11ReassoReq < Packet
|
662
|
+
attr_accessor :capabilities, :current_ap, :listen_interval
|
663
|
+
|
664
|
+
include Dot11EltContainer
|
665
|
+
|
666
|
+
def data
|
667
|
+
buffer = [capabilities].concat(mac2array(current_ap)).concat([listen_interval]).pack("nC6v")
|
668
|
+
|
669
|
+
buffer += element_data
|
670
|
+
end
|
671
|
+
|
672
|
+
def to_s
|
673
|
+
"Dot11ReassoReq\n" +
|
674
|
+
"---------------\n" +
|
675
|
+
"capabilities: ...... #{capabilities}\n" +
|
676
|
+
"current_ap: ........ #{current_ap}\n" +
|
677
|
+
"listen_interval: ... #{listen_interval}\n" +
|
678
|
+
"elements:\n" +
|
679
|
+
element_to_s.indent(7)
|
680
|
+
end
|
681
|
+
|
682
|
+
private
|
683
|
+
|
684
|
+
def dissect(data)
|
685
|
+
fields = data.unpack("nC6v")
|
686
|
+
|
687
|
+
@capabilities = fields[0]
|
688
|
+
@current_ap = Packet.array2mac(fields[1, 6])
|
689
|
+
@listen_interval = fields[7]
|
690
|
+
|
691
|
+
@rest = data[10..-1]
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
class Dot11ReassoResp < Packet
|
696
|
+
include Dot11EltContainer
|
697
|
+
|
698
|
+
def data
|
699
|
+
element_data
|
700
|
+
end
|
701
|
+
|
702
|
+
def to_s
|
703
|
+
"Dot11ReassoResp\n" +
|
704
|
+
"---------------\n" +
|
705
|
+
"elements:\n" +
|
706
|
+
element_to_s.indent(7)
|
707
|
+
end
|
708
|
+
|
709
|
+
private
|
710
|
+
|
711
|
+
def dissect(data)
|
712
|
+
@rest = data
|
713
|
+
end
|
714
|
+
end
|
715
|
+
|
716
|
+
class Dot11ProbeReq < Packet
|
717
|
+
include Dot11EltContainer
|
718
|
+
|
719
|
+
def data
|
720
|
+
element_data
|
721
|
+
end
|
722
|
+
|
723
|
+
def to_s
|
724
|
+
"Dot11ProbeReq\n" +
|
725
|
+
"---------------\n" +
|
726
|
+
"elements:\n" +
|
727
|
+
element_to_s.indent(7)
|
728
|
+
end
|
729
|
+
|
730
|
+
private
|
731
|
+
|
732
|
+
def dissect(data)
|
733
|
+
@rest = data
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
class Dot11ProbeResp < Packet
|
738
|
+
attr_accessor :timestamp, :beacon_interval, :capabilities
|
739
|
+
|
740
|
+
include Dot11EltContainer
|
741
|
+
|
742
|
+
def data
|
743
|
+
buffer = [timestamp & 0xFFFFFFFF, (timestamp & 0xFFFFFFFF00000000) >> 32, beacon_interval, capabilities].pack("V2vn")
|
744
|
+
|
745
|
+
buffer += element_data
|
746
|
+
end
|
747
|
+
|
748
|
+
def to_s
|
749
|
+
binary_caps = capabilities.to_s(2)
|
750
|
+
cap_names = ['ESS', 'IBSS', 'CF Pollable', 'CF Poll Request', 'Privacy', 'Reserved5', 'Reserved6', 'Reserved7', 'Reserved8', 'Reserved9', 'Reserved10', 'Reserved11', 'Reserved12', 'Reserved13', 'Reserved14', 'Reserved15', ]
|
751
|
+
set_caps = []
|
752
|
+
|
753
|
+
16.times do |i|
|
754
|
+
set_caps << cap_names[i] if binary_caps[15 -i] == ?1
|
755
|
+
end
|
756
|
+
|
757
|
+
"Dot11ProbeResp\n" +
|
758
|
+
"-------------------------\n" +
|
759
|
+
"timestamp: ......... #{timestamp}\n" +
|
760
|
+
"beacon_interval: ... #{beacon_interval} (#{beacon_interval * 0.001024} seconds)\n" +
|
761
|
+
"capabilities: ...... #{capabilities} (#{"0" * (16 - binary_caps.length) + binary_caps}#{if set_caps.size > 0 then ' : ' + set_caps.join(', ') else '' end})\n" +
|
762
|
+
"elements:\n" +
|
763
|
+
element_to_s.indent(7)
|
764
|
+
end
|
765
|
+
|
766
|
+
private
|
767
|
+
|
768
|
+
def dissect(data)
|
769
|
+
fields = data.unpack("V2vn")
|
770
|
+
|
771
|
+
@timestamp = (fields[1] << 32) | fields[0]
|
772
|
+
@beacon_interval = fields[2]
|
773
|
+
@capabilities = fields[3]
|
774
|
+
|
775
|
+
@rest = data[12..-1]
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
class Dot11Auth < Packet
|
780
|
+
attr_accessor :algo, :seqnum, :status
|
781
|
+
|
782
|
+
include Dot11EltContainer
|
783
|
+
|
784
|
+
def data
|
785
|
+
buffer = [algo, seqnum, status].pack("vvv")
|
786
|
+
|
787
|
+
buffer += element_data
|
788
|
+
end
|
789
|
+
|
790
|
+
def to_s
|
791
|
+
"Dot11Auth\n" +
|
792
|
+
"-------------\n" +
|
793
|
+
"algo: #{algo}\n" +
|
794
|
+
"seqnum: #{seqnum}\n" +
|
795
|
+
"status: #{status}\n"
|
796
|
+
end
|
797
|
+
|
798
|
+
private
|
799
|
+
|
800
|
+
def dissect(data)
|
801
|
+
fields = data.unpack("vvv")
|
802
|
+
|
803
|
+
@algo = fields[0] || 0
|
804
|
+
@seqnum = fields[1] || 0
|
805
|
+
@status = fields[2] || 0
|
806
|
+
|
807
|
+
@rest = data[6..-1]
|
808
|
+
end
|
809
|
+
end
|
810
|
+
|
811
|
+
class Dot11Deauth < Packet
|
812
|
+
attr_accessor :reason
|
813
|
+
|
814
|
+
def data
|
815
|
+
[reason].pack("v")
|
816
|
+
end
|
817
|
+
|
818
|
+
def to_s
|
819
|
+
"Dot11Deauth\n" +
|
820
|
+
"-------------\n" +
|
821
|
+
"reason: #{reason}\n"
|
822
|
+
end
|
823
|
+
|
824
|
+
private
|
825
|
+
|
826
|
+
def dissect(data)
|
827
|
+
@reason = data.unpack("v")[0]
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
class Dot11Data < Packet
|
832
|
+
attr_accessor :payload
|
833
|
+
|
834
|
+
def data
|
835
|
+
payload.data
|
836
|
+
end
|
837
|
+
|
838
|
+
def to_s
|
839
|
+
"Dot11Data\n" +
|
840
|
+
"-------------\n" +
|
841
|
+
"payload: \n#{payload.to_s.indent(6)}\n"
|
842
|
+
end
|
843
|
+
|
844
|
+
def payload
|
845
|
+
return @payload if @payload
|
846
|
+
|
847
|
+
@payload = LLC.new(@rest)
|
848
|
+
end
|
849
|
+
|
850
|
+
def /(other)
|
851
|
+
@payload = other
|
852
|
+
self
|
853
|
+
end
|
854
|
+
|
855
|
+
private
|
856
|
+
|
857
|
+
def dissect(data)
|
858
|
+
@rest = data
|
859
|
+
end
|
860
|
+
end
|
861
|
+
|
862
|
+
class Dot11NullData < Packet
|
863
|
+
def data
|
864
|
+
""
|
865
|
+
end
|
866
|
+
|
867
|
+
def to_s
|
868
|
+
"Dot11NullData\n"
|
869
|
+
end
|
870
|
+
|
871
|
+
private
|
872
|
+
|
873
|
+
def dissect(data)
|
874
|
+
@rest = data
|
875
|
+
end
|
876
|
+
end
|
877
|
+
|
878
|
+
class Dot11WEP < Packet
|
879
|
+
def data
|
880
|
+
|
881
|
+
end
|
882
|
+
|
883
|
+
def to_s
|
884
|
+
"Dot11WEP\n" +
|
885
|
+
"-------------\n" +
|
886
|
+
"unknown\n"
|
887
|
+
end
|
888
|
+
|
889
|
+
private
|
890
|
+
|
891
|
+
def dissect(data)
|
892
|
+
@rest = data
|
893
|
+
end
|
894
|
+
end
|
895
|
+
|
896
|
+
class LLC < Packet
|
897
|
+
attr_accessor :dsap, :ssap, :control, :payload
|
898
|
+
|
899
|
+
def data
|
900
|
+
[dsap, ssap, control].pack("CCC") + payload.data
|
901
|
+
end
|
902
|
+
|
903
|
+
def to_s
|
904
|
+
"LLC\n" +
|
905
|
+
"-------------\n" +
|
906
|
+
"dsap: #{dsap}\n" +
|
907
|
+
"ssap: #{ssap}\n" +
|
908
|
+
"control: #{control}\n" +
|
909
|
+
(if payload then "payload:\n#{payload.to_s.indent(6)}" else "" end)
|
910
|
+
end
|
911
|
+
|
912
|
+
def payload
|
913
|
+
return @payload if @payload
|
914
|
+
|
915
|
+
@payload = SNAP.new(@rest)
|
916
|
+
end
|
917
|
+
|
918
|
+
def /(other)
|
919
|
+
@payload = other
|
920
|
+
self
|
921
|
+
end
|
922
|
+
|
923
|
+
private
|
924
|
+
|
925
|
+
def dissect(data)
|
926
|
+
fields = data.unpack("CCC")
|
927
|
+
|
928
|
+
@dsap = fields[0]
|
929
|
+
@ssap = fields[1]
|
930
|
+
@control = fields[2]
|
931
|
+
|
932
|
+
@rest = data[3..-1]
|
933
|
+
end
|
934
|
+
end
|
935
|
+
|
936
|
+
class SNAP < Packet
|
937
|
+
attr_accessor :oui, :code, :payload
|
938
|
+
|
939
|
+
def data
|
940
|
+
[oui, code].pack("QXv") + payload.data
|
941
|
+
end
|
942
|
+
|
943
|
+
def to_s
|
944
|
+
"SNAP\n" +
|
945
|
+
"-------\n" +
|
946
|
+
"oui: #{oui}\n" +
|
947
|
+
"code: #{code}\n" +
|
948
|
+
(if payload then "payload:\n#{payload.to_s.indent(6)}" else "" end)
|
949
|
+
end
|
950
|
+
|
951
|
+
def payload
|
952
|
+
return @payload if @payload
|
953
|
+
|
954
|
+
return @payload = Raw.new(@rest)
|
955
|
+
end
|
956
|
+
|
957
|
+
def /(other)
|
958
|
+
@payload = other
|
959
|
+
self
|
960
|
+
end
|
961
|
+
|
962
|
+
private
|
963
|
+
|
964
|
+
def dissect(data)
|
965
|
+
fields = data.unpack("CCCv")
|
966
|
+
|
967
|
+
@oui = fields[0] << 16 | fields[1] << 8 | fields[2]
|
968
|
+
@code = fields[3]
|
969
|
+
|
970
|
+
@rest = data[4..-1]
|
971
|
+
end
|
972
|
+
end
|
973
|
+
end
|
974
|
+
end
|