packetgen 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +18 -41
- data/lib/packetgen.rb +1 -1
- data/lib/packetgen/header.rb +14 -9
- data/lib/packetgen/header/arp.rb +1 -0
- data/lib/packetgen/header/base.rb +50 -1
- data/lib/packetgen/header/dot11.rb +349 -0
- data/lib/packetgen/header/dot11/control.rb +59 -0
- data/lib/packetgen/header/dot11/data.rb +43 -0
- data/lib/packetgen/header/dot11/element.rb +35 -0
- data/lib/packetgen/header/dot11/management.rb +25 -0
- data/lib/packetgen/header/dot11/sub_mngt.rb +178 -0
- data/lib/packetgen/header/dot1q.rb +42 -0
- data/lib/packetgen/header/dot1x.rb +78 -0
- data/lib/packetgen/header/esp.rb +2 -2
- data/lib/packetgen/header/eth.rb +1 -8
- data/lib/packetgen/header/ip.rb +13 -2
- data/lib/packetgen/header/ipv6.rb +12 -1
- data/lib/packetgen/header/llc.rb +56 -0
- data/lib/packetgen/inspect.rb +1 -1
- data/lib/packetgen/packet.rb +12 -8
- data/lib/packetgen/pcapng.rb +11 -0
- data/lib/packetgen/pcapng/file.rb +5 -2
- data/lib/packetgen/types.rb +1 -0
- data/lib/packetgen/types/fields.rb +80 -10
- data/lib/packetgen/types/int_string.rb +1 -1
- data/lib/packetgen/types/oui.rb +48 -0
- data/lib/packetgen/types/string.rb +9 -4
- data/lib/packetgen/types/tlv.rb +83 -6
- data/lib/packetgen/version.rb +1 -1
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67bab87e209f3ad1ca3adecf77be2b19c1ba17a5
|
4
|
+
data.tar.gz: c5bd835c57160261677a22693e297c8b27ac3cbe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 466fadd588084aedad8c7e9fbc7ac6313f8f66d47594171fc7728c2056369ffc2e24fedb4794f4a37cd8fcc66ed4dff7db5339e1f19fefc3522d4906a8667953
|
7
|
+
data.tar.gz: c8bd3cb05a61ae939f152355d45fed6ff0e26b6760dd0bea98fddff4aa86263ec1eea27b24024325ae089246cb78baad3958b9df7a949771150d65c45cba7073
|
data/README.md
CHANGED
@@ -4,17 +4,7 @@
|
|
4
4
|
|
5
5
|
# PacketGen
|
6
6
|
|
7
|
-
PacketGen provides simple ways to generate, send and capture network packets
|
8
|
-
|
9
|
-
## Why PacketGen
|
10
|
-
Why create PacketGen ? There is already PacketFu!
|
11
|
-
|
12
|
-
Yes. But PacketFu is limited:
|
13
|
-
* upper protocols use fixed layers: TCP always uses IPv4, IP and IPv6 always uses Ethernet as MAC,...
|
14
|
-
* cannot handle tunneled packets (IP-in-IP, or deciphered ESP packets,...)
|
15
|
-
* cannot easily encapsulate or decapsulate packets
|
16
|
-
* parse packets top-down, and sometimes bad parse down layers
|
17
|
-
* cannot send packet on wire at IP/IPv6 level (Ethernet header is mandatory)
|
7
|
+
PacketGen provides simple ways to generate, send and capture network packets.
|
18
8
|
|
19
9
|
## Installation
|
20
10
|
Via RubyGems:
|
@@ -25,10 +15,11 @@ Or add it to a Gemfile:
|
|
25
15
|
```ruby
|
26
16
|
gem 'packetgen'
|
27
17
|
```
|
18
|
+
|
28
19
|
## Usage
|
29
20
|
|
30
21
|
### Easily create packets
|
31
|
-
```
|
22
|
+
```ruby
|
32
23
|
PacketGen.gen('IP') # generate a IP packet object
|
33
24
|
PacketGen.gen('TCP') # generate a TCP over IP packet object
|
34
25
|
PacketGen.gen('IP').add('TCP') # the same
|
@@ -43,9 +34,7 @@ PacketGen.gen('IP').to_s
|
|
43
34
|
```
|
44
35
|
|
45
36
|
### Send packets on wire
|
46
|
-
|
47
|
-
|
48
|
-
```
|
37
|
+
```ruby
|
49
38
|
# send Ethernet packet
|
50
39
|
PacketGen.gen('Eth', src: '00:00:00:00:01', dst: '00:00:00:00:02').to_w
|
51
40
|
# send IP packet
|
@@ -55,14 +44,12 @@ PacketGen.gen('Eth', src: '00:00:00:00:01', dst: '00:00:00:00:02').add('IP').to_
|
|
55
44
|
```
|
56
45
|
|
57
46
|
### Parse packets from binary data
|
58
|
-
```
|
47
|
+
```ruby
|
59
48
|
packet = PacketGen.parse(binary_data)
|
60
49
|
```
|
61
50
|
|
62
51
|
### Capture packets from wire
|
63
|
-
|
64
|
-
|
65
|
-
```
|
52
|
+
```ruby
|
66
53
|
# Capture packets, action from a block
|
67
54
|
PacketGen.capture('eth0') do |packet|
|
68
55
|
do_stuffs_with_packet
|
@@ -76,7 +63,7 @@ packets = PacketGen.capture('eth0', filter: 'ip src 1.1.1.2', max: 1)
|
|
76
63
|
```
|
77
64
|
|
78
65
|
### Easily manipulate packets
|
79
|
-
```
|
66
|
+
```ruby
|
80
67
|
# access header fields
|
81
68
|
pkt = PacketGen.gen('IP').add('TCP')
|
82
69
|
pkt.ip.src = '192.168.1.1'
|
@@ -101,7 +88,7 @@ pkt2.decapsulate(pkt2.ip) # pkt2 is now inner IP/TCP packet
|
|
101
88
|
```
|
102
89
|
|
103
90
|
### Read/write PcapNG files
|
104
|
-
```
|
91
|
+
```ruby
|
105
92
|
# read a PcapNG file, containing multiple packets
|
106
93
|
packets = PacketGen.read('file.pcapng')
|
107
94
|
packets.first.udp.sport = 65535
|
@@ -112,44 +99,34 @@ PacketGen.write('more_packets.pcapng', packets)
|
|
112
99
|
```
|
113
100
|
|
114
101
|
### Add custom header/protocol
|
115
|
-
Since v1.1.0, PacketGen permits adding
|
116
|
-
First, define the new header class.
|
102
|
+
Since v1.1.0, PacketGen permits adding your own header classes.
|
103
|
+
First, define the new header class. For example:
|
117
104
|
|
118
105
|
```ruby
|
119
106
|
module MyModule
|
120
|
-
class MyHeader <
|
121
|
-
|
122
|
-
|
123
|
-
extend PacketGen::Header::HeaderClassMethods
|
124
|
-
|
125
|
-
def initialize(options={})
|
126
|
-
super Int32.new(options[:field1]), Int32.new(options[:field2])
|
127
|
-
end
|
128
|
-
|
129
|
-
def read(str)
|
130
|
-
self[:field1].read str[0, 4]
|
131
|
-
self[:field2].read str[4, 4]
|
132
|
-
end
|
107
|
+
class MyHeader < PacketGen::Header::Base
|
108
|
+
define_field :field1, PacketGen::Types::Int32
|
109
|
+
define_field :field2, PacketGen::Types::Int32
|
133
110
|
end
|
134
111
|
end
|
135
112
|
```
|
136
113
|
|
137
114
|
Then, class must be declared to PacketGen:
|
138
115
|
|
139
|
-
```
|
116
|
+
```ruby
|
140
117
|
PacketGen::Header.add_class MyModule::MyHeader
|
141
118
|
```
|
142
119
|
|
143
120
|
Finally, bindings must be declared:
|
144
121
|
|
145
|
-
```
|
146
|
-
# bind MyHeader as IP protocol number 254 (needed by Packet#parse)
|
122
|
+
```ruby
|
123
|
+
# bind MyHeader as IP protocol number 254 (needed by Packet#parse and Packet#add)
|
147
124
|
PacketGen::Header::IP.bind_header MyModule::MyHeader, protocol: 254
|
148
125
|
```
|
149
126
|
|
150
127
|
And use it:
|
151
128
|
|
152
|
-
```
|
129
|
+
```ruby
|
153
130
|
pkt = Packet.gen('IP').add('MyHeader', field1: 0x12345678)
|
154
131
|
pkt.myheader.field2.read 0x01
|
155
132
|
```
|
@@ -166,5 +143,5 @@ Copyright © 2016 Sylvain Daubert
|
|
166
143
|
### Other sources
|
167
144
|
All original code maintains its copyright from its original authors and licensing.
|
168
145
|
|
169
|
-
This is mainly for PcapNG (copied from [PacketFu](https://github.com/packetfu/packetfu),
|
146
|
+
This is mainly for PcapNG (originally copied from [PacketFu](https://github.com/packetfu/packetfu),
|
170
147
|
but i am the original author).
|
data/lib/packetgen.rb
CHANGED
data/lib/packetgen/header.rb
CHANGED
@@ -11,20 +11,14 @@ module PacketGen
|
|
11
11
|
# First, define the new header class. By example:
|
12
12
|
# module MyModule
|
13
13
|
# class MyHeader < PacketGen::Header::Base
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# end
|
17
|
-
#
|
18
|
-
# def read(str)
|
19
|
-
# self[:field1].read str[0, 4]
|
20
|
-
# self[:field2].read str[4, 4]
|
21
|
-
# end
|
14
|
+
# define_field :field1, PacketGen::Types::Int32
|
15
|
+
# define_field :field2, PacketGen::Types::Int32
|
22
16
|
# end
|
23
17
|
# end
|
24
18
|
# Then, class must be declared to PacketGen:
|
25
19
|
# PacketGen::Header.add_class MyModule::MyHeader
|
26
20
|
# Finally, bindings must be declared:
|
27
|
-
# # bind MyHeader as IP protocol number 254 (needed by Packet#parse)
|
21
|
+
# # bind MyHeader as IP protocol number 254 (needed by Packet#parse and Packet#add)
|
28
22
|
# PacketGen::Header::IP.bind_header MyModule::MyHeader, protocol: 254
|
29
23
|
# And use it:
|
30
24
|
# pkt = Packet.gen('IP').add('MyHeader', field1: 0x12345678)
|
@@ -32,6 +26,13 @@ module PacketGen
|
|
32
26
|
# @author Sylvain Daubert
|
33
27
|
module Header
|
34
28
|
|
29
|
+
# @private snap length for PCAPRUB
|
30
|
+
PCAP_SNAPLEN = 0xffff
|
31
|
+
# @private promiscuous (or not) for PCAPRUB
|
32
|
+
PCAP_PROMISC = false
|
33
|
+
# @private timeout for PCAPRUB
|
34
|
+
PCAP_TIMEOUT = 1
|
35
|
+
|
35
36
|
@added_header_classes = {}
|
36
37
|
|
37
38
|
# Get known header classes
|
@@ -79,6 +80,10 @@ end
|
|
79
80
|
|
80
81
|
require_relative 'header/base'
|
81
82
|
require_relative 'header/eth'
|
83
|
+
require_relative 'header/dot11'
|
84
|
+
require_relative 'header/llc'
|
85
|
+
require_relative 'header/dot1q'
|
86
|
+
require_relative 'header/dot1x'
|
82
87
|
require_relative 'header/ip'
|
83
88
|
require_relative 'header/icmp'
|
84
89
|
require_relative 'header/arp'
|
data/lib/packetgen/header/arp.rb
CHANGED
@@ -86,7 +86,7 @@ module PacketGen
|
|
86
86
|
def check?(fields)
|
87
87
|
case @op
|
88
88
|
when :or
|
89
|
-
@bindings.any? { |binding| binding.check?(fields) }
|
89
|
+
empty? || @bindings.any? { |binding| binding.check?(fields) }
|
90
90
|
when :and
|
91
91
|
@bindings.all? { |binding| binding.check?(fields) }
|
92
92
|
end
|
@@ -100,6 +100,41 @@ module PacketGen
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
+
# @api private
|
104
|
+
# Class to handle header associations
|
105
|
+
class Bindings
|
106
|
+
include Enumerable
|
107
|
+
|
108
|
+
# op type
|
109
|
+
# @return [:or,:and]
|
110
|
+
attr_accessor :op
|
111
|
+
# @return [Array<Binding>]
|
112
|
+
attr_accessor :bindings
|
113
|
+
|
114
|
+
# @param [:or, :and] op
|
115
|
+
def initialize(op)
|
116
|
+
@op = op
|
117
|
+
@bindings = []
|
118
|
+
end
|
119
|
+
|
120
|
+
# @param [Object] arg
|
121
|
+
# @return [Bindings] self
|
122
|
+
def <<(arg)
|
123
|
+
@bindings << arg
|
124
|
+
end
|
125
|
+
|
126
|
+
# each iterator
|
127
|
+
# @return [void]
|
128
|
+
def each
|
129
|
+
@bindings.each { |b| yield b }
|
130
|
+
end
|
131
|
+
|
132
|
+
# @return [Boolean]
|
133
|
+
def empty?
|
134
|
+
@bindings.empty?
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
103
138
|
# @api private
|
104
139
|
# Reference on packet which owns this header
|
105
140
|
attr_accessor :packet
|
@@ -144,6 +179,20 @@ module PacketGen
|
|
144
179
|
@known_headers
|
145
180
|
end
|
146
181
|
|
182
|
+
# Return header protocol name
|
183
|
+
# @return [String]
|
184
|
+
def protocol_name
|
185
|
+
self.class.to_s.sub(/.*::/, '')
|
186
|
+
end
|
187
|
+
|
188
|
+
# @abstract Should be redefined by subclasses. This method should check invariant
|
189
|
+
# fields from header.
|
190
|
+
# Call by {Packet#parse} when guessing first header to check if header is correct
|
191
|
+
# @return [Boolean]
|
192
|
+
def parse?
|
193
|
+
true
|
194
|
+
end
|
195
|
+
|
147
196
|
# @api private
|
148
197
|
# Get +header+ id in packet headers array
|
149
198
|
# @param [Header] header
|
@@ -0,0 +1,349 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# This file is part of PacketGen
|
3
|
+
# See https://github.com/sdaubert/packetgen for more informations
|
4
|
+
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
5
|
+
# This program is published under MIT license.
|
6
|
+
require 'zlib'
|
7
|
+
|
8
|
+
module PacketGen
|
9
|
+
module Header
|
10
|
+
|
11
|
+
# PPI (Per-Packet Information) packet
|
12
|
+
#@author Sylvain Daubert
|
13
|
+
class PPI < Base
|
14
|
+
# @!attribute version
|
15
|
+
# @return [Integer] 8-bit PPI version
|
16
|
+
define_field :version, Types::Int8, default: 0
|
17
|
+
# @!attribute flags
|
18
|
+
# @return [Integer] 8-bit PPI flags
|
19
|
+
define_field :flags, Types::Int8
|
20
|
+
# @!attribute length
|
21
|
+
# @return [Integer] 16-bit PPI header length
|
22
|
+
define_field :length, Types::Int16le, default: 8
|
23
|
+
# @!attribute dlt
|
24
|
+
# @return [Integer] 32-bit PPI data link type
|
25
|
+
define_field :dlt, Types::Int32le
|
26
|
+
# @!attribute ppi_fields
|
27
|
+
# @return [Type::String] concatenation of PPI fields
|
28
|
+
define_field :ppi_fields, Types::String
|
29
|
+
# @!attribute body
|
30
|
+
# @return [Type::String]
|
31
|
+
define_field :body, Types::String
|
32
|
+
# @!attribute align
|
33
|
+
# @return [Boolean] align flag from {#flags} attribute
|
34
|
+
define_bit_fields_on :flags, :reserved, 7, :align
|
35
|
+
|
36
|
+
# @param [String] str
|
37
|
+
# @return [PPI] self
|
38
|
+
def read(str)
|
39
|
+
return self if str.nil?
|
40
|
+
force_binary str
|
41
|
+
self[:version].read str[0, 1]
|
42
|
+
self[:flags].read str[1, 1]
|
43
|
+
self[:length].read str[2, 2]
|
44
|
+
self[:dlt].read str[4, 4]
|
45
|
+
self[:ppi_fields].read str[8, length - 8]
|
46
|
+
self[:body].read str[length, str.size]
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Check version field
|
51
|
+
# @see [Base#parse?]
|
52
|
+
def parse?
|
53
|
+
version == 0
|
54
|
+
end
|
55
|
+
|
56
|
+
# send PPI packet on wire. Dot11 FCS trailer should be set.
|
57
|
+
# @param [String] iface interface name
|
58
|
+
# @return [void]
|
59
|
+
def to_w(iface)
|
60
|
+
pcap = PCAPRUB::Pcap.open_live(iface, PCAP_SNAPLEN, PCAP_PROMISC,
|
61
|
+
PCAP_TIMEOUT)
|
62
|
+
pcap.inject self.to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
self.add_class PPI
|
66
|
+
|
67
|
+
# Radiotap header
|
68
|
+
# @author Sylvain Daubert
|
69
|
+
class RadioTap < Base
|
70
|
+
# @!attribute version
|
71
|
+
# @return [Integer] 8-bit version
|
72
|
+
define_field :version, Types::Int8, default: 0
|
73
|
+
# @!attribute pad
|
74
|
+
# @return [Integer] 8-bit pad
|
75
|
+
define_field :pad, Types::Int8, default: 0
|
76
|
+
# @!attribute length
|
77
|
+
# @return [Integer] 16-bit RadioTap header length
|
78
|
+
define_field :length, Types::Int16le
|
79
|
+
# @!attribute present_flags
|
80
|
+
# @return [Integer] 32-bit integer
|
81
|
+
define_field :present_flags, Types::Int32le
|
82
|
+
# @!attribute radio_fields
|
83
|
+
# @return [Type::String] concatenation of RadioTap fields
|
84
|
+
define_field :radio_fields, Types::String
|
85
|
+
# @!attribute body
|
86
|
+
# @return [Type::String]
|
87
|
+
define_field :body, Types::String
|
88
|
+
|
89
|
+
# @param [String] str
|
90
|
+
# @return [RadioTap] self
|
91
|
+
def read(str)
|
92
|
+
return self if str.nil?
|
93
|
+
force_binary str
|
94
|
+
self[:version].read str[0, 1]
|
95
|
+
self[:pad].read str[1, 1]
|
96
|
+
self[:length].read str[2, 2]
|
97
|
+
self[:present_flags].read str[4, 4]
|
98
|
+
self[:radio_fields].read str[8, length - 8]
|
99
|
+
self[:body].read str[length, str.size]
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
# Check version field
|
104
|
+
# @see [Base#parse?]
|
105
|
+
def parse?
|
106
|
+
version == 0
|
107
|
+
end
|
108
|
+
|
109
|
+
# send RadioTap packet on wire. Dot11 FCS trailer should be set.
|
110
|
+
# @param [String] iface interface name
|
111
|
+
# @return [void]
|
112
|
+
def to_w(iface)
|
113
|
+
pcap = PCAPRUB::Pcap.open_live(iface, PCAP_SNAPLEN, PCAP_PROMISC,
|
114
|
+
PCAP_TIMEOUT)
|
115
|
+
pcap.inject self.to_s
|
116
|
+
end
|
117
|
+
end
|
118
|
+
self.add_class RadioTap
|
119
|
+
|
120
|
+
# IEEE 802.11 header
|
121
|
+
# @abstract This is a base class to demultiplex different IEEE 802.11 frames when
|
122
|
+
# parsing.
|
123
|
+
# A IEEE 802.11 header may consists of at least:
|
124
|
+
# * a {#frame_ctrl} ({Types::Int16}),
|
125
|
+
# * a {#id}/duration ({Types::Int16le}),
|
126
|
+
# * and a {#mac1} ({Eth::MacAddr}).
|
127
|
+
# Depending on frame type and subtype, it may also contains:
|
128
|
+
# * a {#mac2} ({Eth::MacAddr}),
|
129
|
+
# * a {#mac3} ({Eth::MacAddr}),
|
130
|
+
# * a {#sequence_ctrl} ({Types::Int16}),
|
131
|
+
# * a {#mac4} ({Eth::MacAddr}),
|
132
|
+
# * a {#qos_ctrl} ({Types::Int16}),
|
133
|
+
# * a {#ht_ctrl} ({Types::Int32}),
|
134
|
+
# * a {#body} (a {Types::String} or another {Base} class),
|
135
|
+
# * a Frame check sequence ({#fcs}, of type {Types::Int32le})
|
136
|
+
# @author Sylvain Daubert
|
137
|
+
class Dot11 < Base
|
138
|
+
|
139
|
+
# Frame types
|
140
|
+
TYPES = %w(Management Control Data Reserved).freeze
|
141
|
+
|
142
|
+
class << self
|
143
|
+
# Set a flag for parsing Dot11 packets. If set to +true+, parse FCS field,
|
144
|
+
# else don't. Default is +true+.
|
145
|
+
# @return [Boolean]
|
146
|
+
attr_accessor :has_fcs
|
147
|
+
end
|
148
|
+
Dot11.has_fcs = true
|
149
|
+
|
150
|
+
# @!attribute frame_ctrl
|
151
|
+
# @return [Integer] 16-bit frame control word
|
152
|
+
define_field :frame_ctrl, Types::Int16, default: 0
|
153
|
+
# @!attribute id
|
154
|
+
# @return [Integer] 16-bit ID/Duration word
|
155
|
+
define_field :id, Types::Int16le, default: 0
|
156
|
+
# @!attribute mac1
|
157
|
+
# @return [Eth::MacAddr]
|
158
|
+
define_field :mac1, Eth::MacAddr
|
159
|
+
# @!attribute mac2
|
160
|
+
# @return [Eth::MacAddr]
|
161
|
+
define_field :mac2, Eth::MacAddr
|
162
|
+
# @!attribute mac3
|
163
|
+
# @return [Eth::MacAddr]
|
164
|
+
define_field :mac3, Eth::MacAddr
|
165
|
+
# @!attribute sequence_ctrl
|
166
|
+
# @return [Integer] 16-bit sequence control word
|
167
|
+
define_field :sequence_ctrl, Types::Int16le, default: 0
|
168
|
+
# @!attribute mac4
|
169
|
+
# @return [Eth::MacAddr]
|
170
|
+
define_field :mac4, Eth::MacAddr
|
171
|
+
# @!attribute qos_ctrl
|
172
|
+
# @return [Integer] 16-bit QoS control word
|
173
|
+
define_field :qos_ctrl, Types::Int16
|
174
|
+
# @!attribute ht_ctrl
|
175
|
+
# @return [Integer] 16-bit HT control word
|
176
|
+
define_field :ht_ctrl, Types::Int32
|
177
|
+
# @!attribute body
|
178
|
+
# @return [Types::String]
|
179
|
+
define_field :body, Types::String
|
180
|
+
# @!attribute fcs
|
181
|
+
# @return [Types::Int32le]
|
182
|
+
define_field :fcs, Types::Int32le
|
183
|
+
|
184
|
+
# @!attribute subtype
|
185
|
+
# @return [Integer] 4-bit frame subtype from {#frame_ctrl}
|
186
|
+
# @!attribute type
|
187
|
+
# @return [Integer] 2-bit frame type from {#frame_ctrl}
|
188
|
+
# @!attribute proto_version
|
189
|
+
# @return [Integer] 2-bit protocol version from {#frame_ctrl}
|
190
|
+
# @!attribute order
|
191
|
+
# @return [Boolean] order flag from {#frame_ctrl}
|
192
|
+
# @!attribute wep
|
193
|
+
# @return [Boolean] wep flag from {#frame_ctrl}
|
194
|
+
# @!attribute md
|
195
|
+
# @return [Boolean] md flag from {#frame_ctrl}
|
196
|
+
# @!attribute pwmngt
|
197
|
+
# @return [Boolean] pwmngt flag from {#frame_ctrl}
|
198
|
+
# @!attribute retry
|
199
|
+
# @return [Boolean] retry flag from {#frame_ctrl}
|
200
|
+
# @!attribute mf
|
201
|
+
# @return [Boolean] mf flag from {#frame_ctrl}
|
202
|
+
# @!attribute from_ds
|
203
|
+
# @return [Boolean] from_ds flag from {#frame_ctrl}
|
204
|
+
# @!attribute to_ds
|
205
|
+
# @return [Boolean] to_ds flag from {#frame_ctrl}
|
206
|
+
define_bit_fields_on :frame_ctrl, :subtype, 4, :type, 2, :proto_version, 2,
|
207
|
+
:order, :wep, :md, :pwmngt, :retry, :mf, :from_ds, :to_ds
|
208
|
+
|
209
|
+
alias duration id
|
210
|
+
# @private
|
211
|
+
alias old_fields fields
|
212
|
+
|
213
|
+
# @param [Hash] options
|
214
|
+
# @see Base#initialize
|
215
|
+
def initialize(options={})
|
216
|
+
super
|
217
|
+
@applicable_fields = old_fields
|
218
|
+
end
|
219
|
+
|
220
|
+
# Get all used field names
|
221
|
+
# @return [Array<Symbol>]
|
222
|
+
def fields
|
223
|
+
@applicable_fields
|
224
|
+
end
|
225
|
+
|
226
|
+
# @private
|
227
|
+
alias old_read read
|
228
|
+
|
229
|
+
# Populate object from a binary string
|
230
|
+
# @param [String] str
|
231
|
+
# @return [Dot11] may return a subclass object if a more specific class
|
232
|
+
# may be determined
|
233
|
+
def read(str)
|
234
|
+
has_fcs = Dot11.has_fcs
|
235
|
+
|
236
|
+
if self.class == Dot11
|
237
|
+
return self if str.nil?
|
238
|
+
force_binary str
|
239
|
+
self[:frame_ctrl].read str[0, 2]
|
240
|
+
|
241
|
+
case type
|
242
|
+
when 0
|
243
|
+
Dot11::Management.new.read str
|
244
|
+
when 1
|
245
|
+
Dot11::Control.new.read str
|
246
|
+
when 2
|
247
|
+
Dot11::Data.new.read str
|
248
|
+
else
|
249
|
+
private_read str, has_fcs
|
250
|
+
end
|
251
|
+
else
|
252
|
+
private_read str, has_fcs
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Compute checksum and set +fcs+ field
|
257
|
+
# @return [Integer]
|
258
|
+
def calc_checksum
|
259
|
+
fcs = Zlib.crc32(to_s[0...-4])
|
260
|
+
self.fcs = fcs
|
261
|
+
fcs
|
262
|
+
end
|
263
|
+
|
264
|
+
# @return [String]
|
265
|
+
def to_s
|
266
|
+
define_applicable_fields
|
267
|
+
@applicable_fields.map { |f| force_binary @fields[f].to_s }.join
|
268
|
+
end
|
269
|
+
|
270
|
+
# Get human readable type
|
271
|
+
# @return [String]
|
272
|
+
def human_type
|
273
|
+
TYPES[type]
|
274
|
+
end
|
275
|
+
|
276
|
+
# @return [String]
|
277
|
+
def inspect
|
278
|
+
str = if self.class == Dot11
|
279
|
+
Inspect.dashed_line("#{self.class} #{human_type}", 2)
|
280
|
+
elsif self.respond_to? :human_subtype
|
281
|
+
Inspect.dashed_line("#{self.class} #{human_subtype}", 2)
|
282
|
+
else
|
283
|
+
Inspect.dashed_line("#{self.class}", 2)
|
284
|
+
end
|
285
|
+
define_applicable_fields
|
286
|
+
@applicable_fields.each do |attr|
|
287
|
+
next if attr == :body
|
288
|
+
str << Inspect.inspect_attribute(attr, @fields[attr], 2)
|
289
|
+
end
|
290
|
+
str
|
291
|
+
end
|
292
|
+
|
293
|
+
# send Dot11 packet on wire.
|
294
|
+
# @param [String] iface interface name
|
295
|
+
# @return [void]
|
296
|
+
def to_w(iface)
|
297
|
+
pcap = PCAPRUB::Pcap.open_live(iface, PCAP_SNAPLEN, PCAP_PROMISC,
|
298
|
+
PCAP_TIMEOUT)
|
299
|
+
str = self.to_s
|
300
|
+
pcap.inject str << [crc32].pack('V')
|
301
|
+
end
|
302
|
+
|
303
|
+
private
|
304
|
+
|
305
|
+
def define_applicable_fields
|
306
|
+
if to_ds? and from_ds?
|
307
|
+
@applicable_fields[6, 0] = :mac4 unless @applicable_fields.include? :mac4
|
308
|
+
else
|
309
|
+
@applicable_fields -= %i(mac4)
|
310
|
+
end
|
311
|
+
if order?
|
312
|
+
unless @applicable_fields.include? :ht_ctrl
|
313
|
+
idx = @applicable_fields.index(:body)
|
314
|
+
@applicable_fields[idx, 0] = :ht_ctrl
|
315
|
+
end
|
316
|
+
else
|
317
|
+
@applicable_fields -= %i(ht_ctrl)
|
318
|
+
end
|
319
|
+
if Dot11.has_fcs
|
320
|
+
@applicable_fields << :fcs unless @applicable_fields.include? :fcs
|
321
|
+
else
|
322
|
+
@applicable_fields -= %i(fcs)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def private_read(str, has_fcs)
|
327
|
+
self[:frame_ctrl].read str[0, 2]
|
328
|
+
define_applicable_fields
|
329
|
+
if has_fcs
|
330
|
+
old_read str[0...-4]
|
331
|
+
self[:fcs].read str[-4..-1]
|
332
|
+
else
|
333
|
+
old_read str
|
334
|
+
end
|
335
|
+
self
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
self.add_class Dot11
|
340
|
+
PPI.bind_header Dot11, dlt: PcapNG::LINKTYPE_IEEE802_11
|
341
|
+
RadioTap.bind_header Dot11
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
require_relative 'dot11/element'
|
346
|
+
require_relative 'dot11/management'
|
347
|
+
require_relative 'dot11/sub_mngt'
|
348
|
+
require_relative 'dot11/control'
|
349
|
+
require_relative 'dot11/data'
|