packetgen 1.3.0 → 1.4.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.
- 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'
|