packetgen 3.1.4 → 3.1.5
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/lib/packetgen.rb +8 -1
- data/lib/packetgen/capture.rb +16 -8
- data/lib/packetgen/config.rb +15 -9
- data/lib/packetgen/deprecation.rb +1 -1
- data/lib/packetgen/header/dhcpv6/option.rb +1 -11
- data/lib/packetgen/header/dns/qdsection.rb +1 -1
- data/lib/packetgen/header/dot11.rb +2 -32
- data/lib/packetgen/header/dot1x.rb +1 -14
- data/lib/packetgen/header/eap.rb +11 -15
- data/lib/packetgen/header/eth.rb +3 -0
- data/lib/packetgen/header/http/headers.rb +3 -0
- data/lib/packetgen/header/ip/addr.rb +3 -0
- data/lib/packetgen/header/ipv6/addr.rb +3 -0
- data/lib/packetgen/header/tcp.rb +1 -20
- data/lib/packetgen/inspect.rb +1 -17
- data/lib/packetgen/inspectable.rb +20 -0
- data/lib/packetgen/pcaprub_wrapper.rb +18 -4
- data/lib/packetgen/types/abstract_tlv.rb +4 -1
- data/lib/packetgen/types/array.rb +4 -3
- data/lib/packetgen/types/cstring.rb +3 -0
- data/lib/packetgen/types/enum.rb +4 -0
- data/lib/packetgen/types/fields.rb +121 -89
- data/lib/packetgen/types/int.rb +14 -0
- data/lib/packetgen/types/int_string.rb +3 -0
- data/lib/packetgen/types/oui.rb +3 -0
- data/lib/packetgen/types/string.rb +1 -0
- data/lib/packetgen/types/tlv.rb +3 -0
- data/lib/packetgen/utils.rb +1 -1
- data/lib/packetgen/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 476cbc2bd2b9fb254ea40fe0a07a4d703a0fc36e4accc823de7e83b640d95a2d
|
4
|
+
data.tar.gz: 375f31ea90589b2b0c0b257b8a3efaa66715e8b369c542124fe3dcac283d9221
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c518ff72cf70227c8f9ef38cce97ba5036eb3229051d38172ba947898b9051f508670d1e0106bac0f5edf577fe1450d5f71db557aa6968b7e3998945a87177d7
|
7
|
+
data.tar.gz: 9382d6f8a7542c8988dec0aaf92940f1518885133bf9e7c835478e787546309410c7e7bcfb4525c13fb7479a9cad9a7ece3e30a3d3c6bbb4f081c1cc48b7f02e
|
data/lib/packetgen.rb
CHANGED
@@ -97,6 +97,12 @@ module PacketGen
|
|
97
97
|
Interfacez.default
|
98
98
|
end
|
99
99
|
|
100
|
+
# Get loopback network interface
|
101
|
+
# @return [String]
|
102
|
+
def self.loopback_iface
|
103
|
+
Interfacez.loopback
|
104
|
+
end
|
105
|
+
|
100
106
|
# Shortcut to get a header class
|
101
107
|
# @example builtin class
|
102
108
|
# # same as PacketGen::Header::Dot11:Data.new(id: 0xfedc)
|
@@ -114,8 +120,9 @@ module PacketGen
|
|
114
120
|
end
|
115
121
|
|
116
122
|
require 'packetgen/deprecation'
|
117
|
-
require 'packetgen/types'
|
118
123
|
require 'packetgen/inspect'
|
124
|
+
require 'packetgen/inspectable'
|
125
|
+
require 'packetgen/types'
|
119
126
|
require 'packetgen/pcapng'
|
120
127
|
require 'packetgen/pcap'
|
121
128
|
require 'packetgen/packet'
|
data/lib/packetgen/capture.rb
CHANGED
@@ -13,7 +13,7 @@ module PacketGen
|
|
13
13
|
class Capture
|
14
14
|
private
|
15
15
|
|
16
|
-
attr_reader :filter, :cap_thread, :snaplen, :promisc
|
16
|
+
attr_reader :filter, :cap_thread, :snaplen, :promisc, :monitor
|
17
17
|
|
18
18
|
public
|
19
19
|
|
@@ -41,14 +41,18 @@ module PacketGen
|
|
41
41
|
# yielding. Default: +true+
|
42
42
|
# @param [Integer] snaplen maximum number of bytes to capture for
|
43
43
|
# each packet.
|
44
|
+
# @param [Boolean] monitor enable or disable monitor mode on interface (if supported by +iface+).
|
44
45
|
# @since 2.0.0 remove old 1.x API
|
45
46
|
# @since 3.0.0 arguments are kwargs and no more a hash
|
46
|
-
|
47
|
-
|
47
|
+
# @since 3.1.5 add monitor argument
|
48
|
+
# @author Sylvain Daubert
|
49
|
+
# @author optix2000 - add monitor argument
|
50
|
+
def initialize(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, monitor: nil)
|
51
|
+
@iface = iface || PacketGen.default_iface || PacketGen.loopback_iface
|
48
52
|
|
49
53
|
@packets = []
|
50
54
|
@raw_packets = []
|
51
|
-
set_options iface, max, timeout, filter, promisc, parse, snaplen
|
55
|
+
set_options iface, max, timeout, filter, promisc, parse, snaplen, monitor
|
52
56
|
end
|
53
57
|
|
54
58
|
# Start capture
|
@@ -56,8 +60,11 @@ module PacketGen
|
|
56
60
|
# @yieldparam [Packet,String] packet if a block is given, yield each
|
57
61
|
# captured packet (Packet or raw data String, depending on +:parse+ option)
|
58
62
|
# @since 3.0.0 arguments are kwargs and no more a hash
|
59
|
-
|
60
|
-
|
63
|
+
# @since 3.1.5 add monitor argument
|
64
|
+
# @author Sylvain Daubert
|
65
|
+
# @author optix2000 - add monitor argument
|
66
|
+
def start(iface: nil, max: nil, timeout: nil, filter: nil, promisc: false, parse: true, snaplen: nil, monitor: nil, &block)
|
67
|
+
set_options iface, max, timeout, filter, promisc, parse, snaplen, monitor
|
61
68
|
|
62
69
|
@cap_thread = Thread.new do
|
63
70
|
PCAPRUBWrapper.capture(**capture_args) do |packet_data|
|
@@ -79,7 +86,7 @@ module PacketGen
|
|
79
86
|
|
80
87
|
private
|
81
88
|
|
82
|
-
def set_options(iface, max, timeout, filter, promisc, parse, snaplen)
|
89
|
+
def set_options(iface, max, timeout, filter, promisc, parse, snaplen, monitor)
|
83
90
|
@max = max if max
|
84
91
|
@filter = filter unless filter.nil?
|
85
92
|
@timeout = timeout unless timeout.nil?
|
@@ -87,10 +94,11 @@ module PacketGen
|
|
87
94
|
@snaplen = snaplen unless snaplen.nil?
|
88
95
|
@parse = parse unless parse.nil?
|
89
96
|
@iface = iface unless iface.nil?
|
97
|
+
@monitor = monitor unless monitor.nil?
|
90
98
|
end
|
91
99
|
|
92
100
|
def capture_args
|
93
|
-
h = { iface: iface, filter: filter }
|
101
|
+
h = { iface: iface, filter: filter, monitor: monitor }
|
94
102
|
h[:snaplen] = snaplen unless snaplen.nil?
|
95
103
|
h[:promisc] = promisc unless promisc.nil?
|
96
104
|
h
|
data/lib/packetgen/config.rb
CHANGED
@@ -22,37 +22,43 @@ module PacketGen
|
|
22
22
|
attr_reader :default_iface
|
23
23
|
|
24
24
|
def initialize
|
25
|
-
@default_iface =
|
25
|
+
@default_iface = PacketGen.default_iface || PacketGen.loopback_iface
|
26
26
|
@hwaddr = {}
|
27
27
|
@ipaddr = {}
|
28
28
|
@ip6addr = {}
|
29
29
|
|
30
|
-
|
31
|
-
@hwaddr[iface_name] = Interfacez.mac_address_of(iface_name)
|
32
|
-
@ipaddr[iface_name] = Interfacez.ipv4_address_of(iface_name)
|
33
|
-
@ip6addr[iface_name] = Interfacez.ipv6_addresses_of(iface_name)
|
34
|
-
end
|
30
|
+
initialize_local_addresses
|
35
31
|
end
|
36
32
|
|
37
33
|
# Get MAC address for given network interface
|
38
34
|
# @param [String,nil] iface network interface. If +nil+, use default one.
|
39
35
|
# @return [String]
|
40
36
|
def hwaddr(iface=nil)
|
41
|
-
@hwaddr[iface ||
|
37
|
+
@hwaddr[iface || default_iface]
|
42
38
|
end
|
43
39
|
|
44
40
|
# Get IP address for given network interface
|
45
41
|
# @param [String,nil] iface network interface. If +nil+, use default one.
|
46
42
|
# @return [String]
|
47
43
|
def ipaddr(iface=nil)
|
48
|
-
@ipaddr[iface ||
|
44
|
+
@ipaddr[iface || default_iface]
|
49
45
|
end
|
50
46
|
|
51
47
|
# Get IPv6 addresses for given network interface
|
52
48
|
# @param [String,nil] iface network interface. If +nil+, use default one.
|
53
49
|
# @return [Array<String>]
|
54
50
|
def ip6addr(iface=nil)
|
55
|
-
@ip6addr[iface ||
|
51
|
+
@ip6addr[iface || default_iface]
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def initialize_local_addresses
|
57
|
+
Interfacez.all do |iface_name|
|
58
|
+
@hwaddr[iface_name] = Interfacez.mac_address_of(iface_name)
|
59
|
+
@ipaddr[iface_name] = Interfacez.ipv4_address_of(iface_name)
|
60
|
+
@ip6addr[iface_name] = Interfacez.ipv6_addresses_of(iface_name)
|
61
|
+
end
|
56
62
|
end
|
57
63
|
end
|
58
64
|
end
|
@@ -31,7 +31,7 @@ module PacketGen
|
|
31
31
|
complete_deprecated_method_name = "#{base_name}#{deprecated_method}"
|
32
32
|
complete_new_method_name = "#{base_name}#{new_method}" unless new_method.nil?
|
33
33
|
|
34
|
-
file, line = caller(2..2).split(':')[0, 2]
|
34
|
+
file, line = caller(2..2).first.split(':')[0, 2]
|
35
35
|
message = +"#{file}:#{line}: #{complete_deprecated_method_name} is deprecated"
|
36
36
|
message << " in favor of #{complete_new_method_name}" unless new_method.nil?
|
37
37
|
message << '. ' << self.removed(remove_version)
|
@@ -230,17 +230,7 @@ module PacketGen
|
|
230
230
|
|
231
231
|
# @!attribute options
|
232
232
|
# @return [RequestedOptions]
|
233
|
-
define_field :options, RequestedOptions
|
234
|
-
|
235
|
-
# Populate object from +str+
|
236
|
-
# @param [String] str
|
237
|
-
# @return [self]
|
238
|
-
def read(str)
|
239
|
-
self[:type].read str[0, 2]
|
240
|
-
self[:length].read str[2, 2]
|
241
|
-
self[:options].read str[4, self.length]
|
242
|
-
self
|
243
|
-
end
|
233
|
+
define_field :options, RequestedOptions, builder: ->(h, t) { t.new(length_from: h[:length]) }
|
244
234
|
|
245
235
|
# Get human-readable data
|
246
236
|
# @return [String]
|
@@ -28,7 +28,7 @@ module PacketGen
|
|
28
28
|
define_field :dlt, Types::Int32le
|
29
29
|
# @!attribute ppi_fields
|
30
30
|
# @return [Type::String] concatenation of PPI fields
|
31
|
-
define_field :ppi_fields, Types::String
|
31
|
+
define_field :ppi_fields, Types::String, builder: ->(h, t) { t.new(length_from: -> { h.length - 8 }) }
|
32
32
|
# @!attribute body
|
33
33
|
# @return [Type::String]
|
34
34
|
define_field :body, Types::String
|
@@ -36,21 +36,6 @@ module PacketGen
|
|
36
36
|
# @return [Boolean] align flag from {#flags} attribute
|
37
37
|
define_bit_fields_on :flags, :reserved, 7, :align
|
38
38
|
|
39
|
-
# @param [String] str
|
40
|
-
# @return [PPI] self
|
41
|
-
def read(str)
|
42
|
-
return self if str.nil?
|
43
|
-
|
44
|
-
force_binary str
|
45
|
-
self[:version].read str[0, 1]
|
46
|
-
self[:flags].read str[1, 1]
|
47
|
-
self[:length].read str[2, 2]
|
48
|
-
self[:dlt].read str[4, 4]
|
49
|
-
self[:ppi_fields].read str[8, length - 8]
|
50
|
-
self[:body].read str[length, str.size]
|
51
|
-
self
|
52
|
-
end
|
53
|
-
|
54
39
|
# Check version field
|
55
40
|
# @see [Base#parse?]
|
56
41
|
def parse?
|
@@ -91,26 +76,11 @@ module PacketGen
|
|
91
76
|
define_field :present_flags, Types::Int32le
|
92
77
|
# @!attribute radio_fields
|
93
78
|
# @return [Type::String] concatenation of RadioTap fields
|
94
|
-
define_field :radio_fields, Types::String
|
79
|
+
define_field :radio_fields, Types::String, builder: ->(h, t) { t.new(length_from: -> { h.length - 8 }) }
|
95
80
|
# @!attribute body
|
96
81
|
# @return [Type::String]
|
97
82
|
define_field :body, Types::String
|
98
83
|
|
99
|
-
# @param [String] str
|
100
|
-
# @return [RadioTap] self
|
101
|
-
def read(str)
|
102
|
-
return self if str.nil?
|
103
|
-
|
104
|
-
force_binary str
|
105
|
-
self[:version].read str[0, 1]
|
106
|
-
self[:pad].read str[1, 1]
|
107
|
-
self[:length].read str[2, 2]
|
108
|
-
self[:present_flags].read str[4, 4]
|
109
|
-
self[:radio_fields].read str[8, length - 8]
|
110
|
-
self[:body].read str[length, str.size]
|
111
|
-
self
|
112
|
-
end
|
113
|
-
|
114
84
|
# Check version field
|
115
85
|
# @see [Base#parse?]
|
116
86
|
def parse?
|
@@ -45,20 +45,7 @@ module PacketGen
|
|
45
45
|
define_field :length, Types::Int16
|
46
46
|
# @!attribute body
|
47
47
|
# @return [Types::String,Header::Base]
|
48
|
-
define_field :body, Types::String
|
49
|
-
|
50
|
-
# Populate object from string
|
51
|
-
# @param [String] str
|
52
|
-
# @return [self]
|
53
|
-
def read(str)
|
54
|
-
return self if str.nil?
|
55
|
-
|
56
|
-
self[:version].read(str[0, 1])
|
57
|
-
self[:type].read(str[1, 1])
|
58
|
-
self[:length].read(str[2, 2])
|
59
|
-
self[:body].read(str[4, self.length])
|
60
|
-
self
|
61
|
-
end
|
48
|
+
define_field :body, Types::String, builder: ->(h, t) { t.new(length_from: h[:length]) }
|
62
49
|
|
63
50
|
# Get human readable type
|
64
51
|
# @return [String]
|
data/lib/packetgen/header/eap.rb
CHANGED
@@ -132,21 +132,17 @@ module PacketGen
|
|
132
132
|
def read(str)
|
133
133
|
super str
|
134
134
|
return self unless self.class == EAP
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
else
|
147
|
-
self
|
148
|
-
end
|
149
|
-
obj
|
135
|
+
return self unless type?
|
136
|
+
|
137
|
+
case self.type
|
138
|
+
when 4
|
139
|
+
EAP::MD5.new.read(str)
|
140
|
+
when 13
|
141
|
+
EAP::TLS.new.read(str)
|
142
|
+
when 21
|
143
|
+
EAP::TTLS.new.read(str)
|
144
|
+
when 43
|
145
|
+
EAP::FAST.new.read(str)
|
150
146
|
else
|
151
147
|
self
|
152
148
|
end
|
data/lib/packetgen/header/eth.rb
CHANGED
@@ -33,6 +33,8 @@ module PacketGen
|
|
33
33
|
# Ethernet MAC address, as a group of 6 bytes
|
34
34
|
# @author Sylvain Daubert
|
35
35
|
class MacAddr < Types::Fields
|
36
|
+
include Inspectable
|
37
|
+
|
36
38
|
# @!attribute a0
|
37
39
|
# @return [Integer] first byte from MacAddr
|
38
40
|
define_field :a0, Types::Int8
|
@@ -75,6 +77,7 @@ module PacketGen
|
|
75
77
|
def to_human
|
76
78
|
fields.map { |m| '%02x' % self[m] }.join(':')
|
77
79
|
end
|
80
|
+
alias format_inspect to_human
|
78
81
|
|
79
82
|
def ==(other)
|
80
83
|
other.is_a?(self.class) &&
|
@@ -12,6 +12,8 @@ module PacketGen
|
|
12
12
|
# @abstract Base class for HTTP headers.
|
13
13
|
# @author Kent 'picat' Gruber
|
14
14
|
class Headers
|
15
|
+
include Inspectable
|
16
|
+
|
15
17
|
# Underlying Headers data (or nil).
|
16
18
|
# @return [Hash, nil]
|
17
19
|
attr_reader :data
|
@@ -56,6 +58,7 @@ module PacketGen
|
|
56
58
|
def to_human
|
57
59
|
@data
|
58
60
|
end
|
61
|
+
alias format_inspect to_human
|
59
62
|
|
60
63
|
# Read human-readable data to populate header data.
|
61
64
|
# @param [String, Hash] data
|
@@ -11,6 +11,8 @@ module PacketGen
|
|
11
11
|
# IP address, as a group of 4 bytes
|
12
12
|
# @author Sylvain Daubert
|
13
13
|
class Addr < Types::Fields
|
14
|
+
include Inspectable
|
15
|
+
|
14
16
|
# @!attribute a1
|
15
17
|
# @return [Integer] IP address first byte
|
16
18
|
define_field :a1, Types::Int8
|
@@ -47,6 +49,7 @@ module PacketGen
|
|
47
49
|
def to_human
|
48
50
|
fields.map { |f| self[f].to_i.to_s }.join('.')
|
49
51
|
end
|
52
|
+
alias format_inspect to_human
|
50
53
|
|
51
54
|
# Addr as an integer
|
52
55
|
# @return [Integer]
|
@@ -14,6 +14,8 @@ module PacketGen
|
|
14
14
|
# IPv6 address, as a group of 8 2-byte words
|
15
15
|
# @author Sylvain Daubert
|
16
16
|
class Addr < Types::Fields
|
17
|
+
include Inspectable
|
18
|
+
|
17
19
|
# @!attribute a1
|
18
20
|
# 1st 2-byte word of IPv6 address
|
19
21
|
# @return [Integer]
|
@@ -73,6 +75,7 @@ module PacketGen
|
|
73
75
|
def to_human
|
74
76
|
IPAddr.new(to_a.map { |a| a.to_i.to_s(16) }.join(':')).to_s
|
75
77
|
end
|
78
|
+
alias format_inspect to_human
|
76
79
|
|
77
80
|
# Return an array of address 16-bit words
|
78
81
|
# @return [Array<Integer>]
|
data/lib/packetgen/header/tcp.rb
CHANGED
@@ -121,7 +121,7 @@ module PacketGen
|
|
121
121
|
# @!attribute options
|
122
122
|
# TCP options
|
123
123
|
# @return [Options]
|
124
|
-
define_field :options, TCP::Options
|
124
|
+
define_field :options, TCP::Options, builder: ->(h, t) { t.new(length_from: -> { h.data_offset > 5 ? (h.data_offset - 5) * 4 : 0 }) }
|
125
125
|
# @!attribute body
|
126
126
|
# @return [Types::String,Header::Base]
|
127
127
|
define_field :body, Types::String
|
@@ -189,25 +189,6 @@ module PacketGen
|
|
189
189
|
# @return [Boolean] 1-bit FIN flag
|
190
190
|
define_bit_fields_on :u16, :_, 7, :flag_ns, :flag_cwr, :flag_ece, :flag_urg,
|
191
191
|
:flag_ack, :flag_psh, :flag_rst, :flag_syn, :flag_fin
|
192
|
-
# Read a TCP header from a string
|
193
|
-
# @param [String] str binary string
|
194
|
-
# @return [self]
|
195
|
-
def read(str)
|
196
|
-
return self if str.nil?
|
197
|
-
|
198
|
-
force_binary str
|
199
|
-
self[:sport].read str[0, 2]
|
200
|
-
self[:dport].read str[2, 2]
|
201
|
-
self[:seqnum].read str[4, 4]
|
202
|
-
self[:acknum].read str[8, 4]
|
203
|
-
self[:u16].read str[12, 2]
|
204
|
-
self[:window].read str[14, 2]
|
205
|
-
self[:checksum].read str[16, 2]
|
206
|
-
self[:urg_pointer].read str[18, 2]
|
207
|
-
self[:options].read str[20, (self.data_offset - 5) * 4] if self.data_offset > 5
|
208
|
-
self[:body].read str[self.data_offset * 4..-1]
|
209
|
-
self
|
210
|
-
end
|
211
192
|
|
212
193
|
# Compute checksum and set +checksum+ field
|
213
194
|
# @return [Integer]
|
data/lib/packetgen/inspect.rb
CHANGED
@@ -68,23 +68,7 @@ module PacketGen
|
|
68
68
|
# @return [String]
|
69
69
|
def self.inspect_attribute(attr, value, level=1)
|
70
70
|
type = value.class.to_s.sub(/.*::/, '')
|
71
|
-
|
72
|
-
when Types::Enum
|
73
|
-
enum_human_hex(value.to_human, value.to_i, value.sz * 2)
|
74
|
-
when Types::Int
|
75
|
-
int_dec_hex(value, value.sz * 2)
|
76
|
-
when Integer
|
77
|
-
int_dec_hex(value, value.sz * 2)
|
78
|
-
when String
|
79
|
-
value.to_s.inspect
|
80
|
-
else
|
81
|
-
if value.respond_to? :to_human
|
82
|
-
value.to_human
|
83
|
-
else
|
84
|
-
value.to_s.inspect
|
85
|
-
end
|
86
|
-
end
|
87
|
-
self.format(type, attr, val, level)
|
71
|
+
self.format(type, attr, value.format_inspect, level)
|
88
72
|
end
|
89
73
|
|
90
74
|
# Format a ASN.1 attribute for +#inspect+.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is part of PacketGen
|
4
|
+
# See https://github.com/sdaubert/packetgen for more informations
|
5
|
+
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
6
|
+
# This program is published under MIT license.
|
7
|
+
|
8
|
+
module PacketGen
|
9
|
+
# Module to add common methods to format types when inspecting packets/headers.
|
10
|
+
# @author Sylvain Daubert
|
11
|
+
# @since 3.1.5
|
12
|
+
module Inspectable
|
13
|
+
# Format attribute for inspecting
|
14
|
+
# @abstract should be overriden by types.
|
15
|
+
# @return [String]
|
16
|
+
def format_inspect
|
17
|
+
to_s.inspect
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -23,9 +23,19 @@ module PacketGen
|
|
23
23
|
# @param [String] iface interface name
|
24
24
|
# @param [Integer] snaplen
|
25
25
|
# @param [Boolean] promisc
|
26
|
+
# @param [Boolean] monitor
|
26
27
|
# @return [PCAPRUB::Pcap]
|
27
|
-
|
28
|
-
|
28
|
+
# @author Sylvain Daubert
|
29
|
+
# @author optix2000 - add support for setting monitor mode
|
30
|
+
# @since 3.1.5 add monitor argument
|
31
|
+
def self.open_iface(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, monitor: nil)
|
32
|
+
pcap = PCAPRUB::Pcap.create(iface)
|
33
|
+
pcap.setsnaplen(snaplen)
|
34
|
+
pcap.setpromisc(promisc)
|
35
|
+
pcap.settimeout(TIMEOUT)
|
36
|
+
# Monitor MUST be set before pcap is activated
|
37
|
+
pcap.setmonitor monitor unless monitor.nil?
|
38
|
+
pcap.activate
|
29
39
|
end
|
30
40
|
|
31
41
|
# Capture packets from a network interface
|
@@ -33,10 +43,14 @@ module PacketGen
|
|
33
43
|
# @param [Integer] snaplen
|
34
44
|
# @param [Boolean] promisc
|
35
45
|
# @param [String] filter BPF filter
|
46
|
+
# @param [Boolean] monitor
|
36
47
|
# @yieldparam [String] packet_data binary packet data
|
37
48
|
# @return [void]
|
38
|
-
|
39
|
-
|
49
|
+
# @author Sylvain Daubert
|
50
|
+
# @author optix2000 - add support for setting monitor mode
|
51
|
+
# @since 3.1.5 add monitor argument
|
52
|
+
def self.capture(iface:, snaplen: DEFAULT_SNAPLEN, promisc: DEFAULT_PROMISC, filter: nil, monitor: nil)
|
53
|
+
pcap = self.open_iface(iface: iface, snaplen: snaplen, promisc: promisc, monitor: monitor)
|
40
54
|
pcap.setfilter filter unless filter.nil?
|
41
55
|
pcap.each do |packet_data|
|
42
56
|
yield packet_data
|
@@ -58,6 +58,8 @@ module PacketGen
|
|
58
58
|
# @since 3.1.0
|
59
59
|
# @since 3.1.1 add +:aliases+ keyword to {#initialize}
|
60
60
|
class AbstractTLV < Types::Fields
|
61
|
+
include Inspectable
|
62
|
+
|
61
63
|
class <<self
|
62
64
|
# @return [Hash]
|
63
65
|
attr_accessor :aliases
|
@@ -169,8 +171,9 @@ module PacketGen
|
|
169
171
|
# @return [String]
|
170
172
|
def to_human
|
171
173
|
my_value = self[:value].is_a?(String) ? self[:value].inspect : self[:value].to_human
|
172
|
-
"type:%s,length:%u,value
|
174
|
+
"type:%s,length:%u,value:%s" % [human_type, length, my_value]
|
173
175
|
end
|
176
|
+
alias format_inspect to_human
|
174
177
|
|
175
178
|
private
|
176
179
|
|
@@ -27,6 +27,9 @@ module PacketGen
|
|
27
27
|
# @author Sylvain Daubert
|
28
28
|
class Array
|
29
29
|
extend Forwardable
|
30
|
+
include Inspectable
|
31
|
+
include Enumerable
|
32
|
+
include LengthFrom
|
30
33
|
|
31
34
|
# @!method [](index)
|
32
35
|
# Return the element at +index+.
|
@@ -54,9 +57,6 @@ module PacketGen
|
|
54
57
|
def_delegators :@array, :[], :clear, :each, :empty?, :first, :last, :size
|
55
58
|
alias length size
|
56
59
|
|
57
|
-
include Enumerable
|
58
|
-
include LengthFrom
|
59
|
-
|
60
60
|
# Separator used in {#to_human}.
|
61
61
|
# May be ovverriden by subclasses
|
62
62
|
HUMAN_SEPARATOR = ','
|
@@ -198,6 +198,7 @@ module PacketGen
|
|
198
198
|
def to_human
|
199
199
|
@array.map(&:to_human).join(self.class::HUMAN_SEPARATOR)
|
200
200
|
end
|
201
|
+
alias format_inspect to_human
|
201
202
|
|
202
203
|
private
|
203
204
|
|
@@ -11,6 +11,8 @@ module PacketGen
|
|
11
11
|
# This class handles null-terminated strings (aka C strings).
|
12
12
|
# @author Sylvain Daubert
|
13
13
|
class CString < ::String
|
14
|
+
include Inspectable
|
15
|
+
|
14
16
|
# @param [Hash] options
|
15
17
|
# @option options [Integer] :static_length set a static length for this string
|
16
18
|
def initialize(options={})
|
@@ -66,6 +68,7 @@ module PacketGen
|
|
66
68
|
idx = self.index(+"\x00".encode(self.encoding)) || self.sz
|
67
69
|
self[0, idx]
|
68
70
|
end
|
71
|
+
alias format_inspect to_human
|
69
72
|
end
|
70
73
|
end
|
71
74
|
end
|
data/lib/packetgen/types/enum.rb
CHANGED
@@ -119,6 +119,10 @@ module PacketGen
|
|
119
119
|
# @return [Hash]
|
120
120
|
# @since 3.1.0
|
121
121
|
attr_reader :field_defs
|
122
|
+
# Get bit fields defintions for this class
|
123
|
+
# @return [Hash]
|
124
|
+
# @since 3.1.5
|
125
|
+
attr_reader :bit_fields
|
122
126
|
|
123
127
|
# On inheritage, create +@field_defs+ class variable
|
124
128
|
# @param [Class] klass
|
@@ -129,7 +133,7 @@ module PacketGen
|
|
129
133
|
field_defs[k] = v.clone
|
130
134
|
end
|
131
135
|
ordered = @ordered_fields.clone
|
132
|
-
bf =
|
136
|
+
bf = bit_fields.clone
|
133
137
|
|
134
138
|
klass.class_eval do
|
135
139
|
@ordered_fields = ordered
|
@@ -172,37 +176,15 @@ module PacketGen
|
|
172
176
|
# Define enumeration: hash's keys are +String+, and values are +Integer+.
|
173
177
|
# @return [void]
|
174
178
|
def define_field(name, type, options={})
|
175
|
-
|
176
|
-
if type < Types::Enum
|
177
|
-
define << "def #{name}; self[:#{name}].to_i; end"
|
178
|
-
define << "def #{name}=(val) self[:#{name}].value = val; end"
|
179
|
-
else
|
180
|
-
define << "def #{name}\n" \
|
181
|
-
" if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
|
182
|
-
" self[:#{name}].to_human\n" \
|
183
|
-
" else\n" \
|
184
|
-
" self[:#{name}]\n" \
|
185
|
-
" end\n" \
|
186
|
-
'end'
|
187
|
-
define << "def #{name}=(val)\n" \
|
188
|
-
" if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
|
189
|
-
" self[:#{name}].from_human val\n" \
|
190
|
-
" else\n" \
|
191
|
-
" self[:#{name}].read val\n" \
|
192
|
-
" end\n" \
|
193
|
-
'end'
|
194
|
-
end
|
195
|
-
|
196
|
-
define.delete_at(1) if instance_methods.include? "#{name}=".to_sym
|
197
|
-
define.delete_at(0) if instance_methods.include? name
|
198
|
-
class_eval define.join("\n")
|
179
|
+
fields << name
|
199
180
|
field_defs[name] = FieldDef.new(type,
|
200
181
|
options.delete(:default),
|
201
182
|
options.delete(:builder),
|
202
183
|
options.delete(:optional),
|
203
184
|
options.delete(:enum),
|
204
185
|
options)
|
205
|
-
|
186
|
+
|
187
|
+
add_methods(name, type)
|
206
188
|
end
|
207
189
|
|
208
190
|
# Define a field, before another one
|
@@ -300,8 +282,8 @@ module PacketGen
|
|
300
282
|
total_size = type.new.width * 8
|
301
283
|
idx = total_size - 1
|
302
284
|
|
303
|
-
|
304
|
-
|
285
|
+
until args.empty?
|
286
|
+
field = args.shift
|
305
287
|
next unless field.is_a? Symbol
|
306
288
|
|
307
289
|
size = if args.first.is_a? Integer
|
@@ -309,41 +291,13 @@ module PacketGen
|
|
309
291
|
else
|
310
292
|
1
|
311
293
|
end
|
294
|
+
|
312
295
|
unless field == :_
|
313
|
-
|
314
|
-
|
315
|
-
clear_mask = (2**total_size - 1) & (~field_mask & (2**total_size - 1))
|
316
|
-
|
317
|
-
if size == 1
|
318
|
-
class_eval <<-METHODS
|
319
|
-
def #{field}?
|
320
|
-
val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
|
321
|
-
val != 0
|
322
|
-
end
|
323
|
-
def #{field}=(v)
|
324
|
-
val = v ? 1 : 0
|
325
|
-
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
|
326
|
-
self[:#{attr}].value |= val << #{shift}
|
327
|
-
end
|
328
|
-
METHODS
|
329
|
-
else
|
330
|
-
class_eval <<-METHODS
|
331
|
-
def #{field}
|
332
|
-
(self[:#{attr}].to_i & #{field_mask}) >> #{shift}
|
333
|
-
end
|
334
|
-
def #{field}=(v)
|
335
|
-
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
|
336
|
-
self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
|
337
|
-
end
|
338
|
-
METHODS
|
339
|
-
end
|
340
|
-
|
341
|
-
@bit_fields[attr] = {} if @bit_fields[attr].nil?
|
342
|
-
@bit_fields[attr][field] = size
|
296
|
+
add_bit_methods(attr, field, size, total_size, idx)
|
297
|
+
register_bit_field_size(attr, field, size)
|
343
298
|
end
|
344
299
|
|
345
300
|
idx -= size
|
346
|
-
field = args.shift
|
347
301
|
end
|
348
302
|
end
|
349
303
|
|
@@ -352,7 +306,7 @@ module PacketGen
|
|
352
306
|
# @return [void]
|
353
307
|
# @since 2.8.4
|
354
308
|
def remove_bit_fields_on(attr)
|
355
|
-
fields =
|
309
|
+
fields = bit_fields.delete(attr)
|
356
310
|
return if fields.nil?
|
357
311
|
|
358
312
|
fields.each do |field, size|
|
@@ -360,6 +314,62 @@ module PacketGen
|
|
360
314
|
undef_method(size == 1 ? "#{field}?" : field)
|
361
315
|
end
|
362
316
|
end
|
317
|
+
|
318
|
+
private
|
319
|
+
|
320
|
+
def add_methods(name, type)
|
321
|
+
define = []
|
322
|
+
if type < Types::Enum
|
323
|
+
define << "def #{name}; self[:#{name}].to_i; end"
|
324
|
+
define << "def #{name}=(val) self[:#{name}].value = val; end"
|
325
|
+
else
|
326
|
+
define << "def #{name}\n" \
|
327
|
+
" to_and_from_human?(:#{name}) ? self[:#{name}].to_human : self[:#{name}]\n" \
|
328
|
+
'end'
|
329
|
+
define << "def #{name}=(val)\n" \
|
330
|
+
" to_and_from_human?(:#{name}) ? self[:#{name}].from_human(val) : self[:#{name}].read(val)\n" \
|
331
|
+
'end'
|
332
|
+
end
|
333
|
+
|
334
|
+
define.delete_at(1) if instance_methods.include? "#{name}=".to_sym
|
335
|
+
define.delete_at(0) if instance_methods.include? name
|
336
|
+
class_eval define.join("\n")
|
337
|
+
end
|
338
|
+
|
339
|
+
def add_bit_methods(attr, name, size, total_size, idx)
|
340
|
+
shift = idx - (size - 1)
|
341
|
+
field_mask = (2**size - 1) << shift
|
342
|
+
clear_mask = (2**total_size - 1) & (~field_mask & (2**total_size - 1))
|
343
|
+
|
344
|
+
if size == 1
|
345
|
+
class_eval <<-METHODS
|
346
|
+
def #{name}?
|
347
|
+
val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
|
348
|
+
val != 0
|
349
|
+
end
|
350
|
+
def #{name}=(v)
|
351
|
+
val = v ? 1 : 0
|
352
|
+
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
|
353
|
+
self[:#{attr}].value |= val << #{shift}
|
354
|
+
end
|
355
|
+
METHODS
|
356
|
+
else
|
357
|
+
class_eval <<-METHODS
|
358
|
+
def #{name}
|
359
|
+
(self[:#{attr}].to_i & #{field_mask}) >> #{shift}
|
360
|
+
end
|
361
|
+
def #{name}=(v)
|
362
|
+
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
|
363
|
+
self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
|
364
|
+
end
|
365
|
+
METHODS
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
def register_bit_field_size(attr, field, size)
|
370
|
+
bit_fields[attr] = {} if bit_fields[attr].nil?
|
371
|
+
bit_fields[attr][field] = size
|
372
|
+
end
|
363
373
|
end
|
364
374
|
|
365
375
|
# Create a new fields object
|
@@ -369,36 +379,13 @@ module PacketGen
|
|
369
379
|
@fields = {}
|
370
380
|
@optional_fields = {}
|
371
381
|
|
372
|
-
field_defs = self.class.field_defs
|
373
382
|
self.class.fields.each do |field|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
builder = field_defs[field].builder
|
378
|
-
optional = field_defs[field].optional
|
379
|
-
enum = field_defs[field].enum
|
380
|
-
field_options = field_defs[field].options
|
381
|
-
|
382
|
-
@fields[field] = if builder
|
383
|
-
builder.call(self, type)
|
384
|
-
elsif enum
|
385
|
-
type.new(enum)
|
386
|
-
elsif !field_options.empty?
|
387
|
-
type.new(field_options)
|
388
|
-
else
|
389
|
-
type.new
|
390
|
-
end
|
391
|
-
|
392
|
-
value = options[field] || default
|
393
|
-
if value.class <= type
|
394
|
-
@fields[field] = value
|
395
|
-
elsif @fields[field].respond_to? :from_human
|
396
|
-
@fields[field].from_human(value)
|
397
|
-
end
|
398
|
-
|
399
|
-
@optional_fields[field] = optional if optional
|
383
|
+
build_field field
|
384
|
+
initialize_value field, options[field]
|
385
|
+
initialize_optional field
|
400
386
|
end
|
401
|
-
|
387
|
+
|
388
|
+
self.class.bit_fields.each do |_, hsh|
|
402
389
|
hsh.each_key do |bit_field|
|
403
390
|
self.send "#{bit_field}=", options[bit_field] if options[bit_field]
|
404
391
|
end
|
@@ -528,7 +515,7 @@ module PacketGen
|
|
528
515
|
# @return [Hash,nil] keys: bit fields, values: their size in bits
|
529
516
|
# @since 2.8.3
|
530
517
|
def bits_on(field)
|
531
|
-
self.class.
|
518
|
+
self.class.bit_fields[field]
|
532
519
|
end
|
533
520
|
|
534
521
|
private
|
@@ -547,6 +534,51 @@ module PacketGen
|
|
547
534
|
def force_binary(str)
|
548
535
|
PacketGen.force_binary(str)
|
549
536
|
end
|
537
|
+
|
538
|
+
# @param [Symbol] attr attribute
|
539
|
+
# @return [Boolean] +tru+e if #from_human and #to_human are both defined for given attribute
|
540
|
+
def to_and_from_human?(attr)
|
541
|
+
self[attr].respond_to?(:to_human) && self[attr].respond_to?(:from_human)
|
542
|
+
end
|
543
|
+
|
544
|
+
def field_defs
|
545
|
+
self.class.field_defs
|
546
|
+
end
|
547
|
+
|
548
|
+
def build_field(field)
|
549
|
+
type = field_defs[field].type
|
550
|
+
builder = field_defs[field].builder
|
551
|
+
enum = field_defs[field].enum
|
552
|
+
field_options = field_defs[field].options
|
553
|
+
|
554
|
+
@fields[field] = if builder
|
555
|
+
builder.call(self, type)
|
556
|
+
elsif enum
|
557
|
+
type.new(enum)
|
558
|
+
elsif !field_options.empty?
|
559
|
+
type.new(field_options)
|
560
|
+
else
|
561
|
+
type.new
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
def initialize_value(field, val)
|
566
|
+
type = field_defs[field].type
|
567
|
+
default = field_defs[field].default
|
568
|
+
default = default.to_proc.call(self) if default.is_a?(Proc)
|
569
|
+
|
570
|
+
value = val || default
|
571
|
+
if value.class <= type
|
572
|
+
@fields[field] = value
|
573
|
+
elsif @fields[field].respond_to? :from_human
|
574
|
+
@fields[field].from_human(value)
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
def initialize_optional(field)
|
579
|
+
optional = field_defs[field].optional
|
580
|
+
@optional_fields[field] = optional if optional
|
581
|
+
end
|
550
582
|
end
|
551
583
|
end
|
552
584
|
end
|
data/lib/packetgen/types/int.rb
CHANGED
@@ -12,6 +12,8 @@ module PacketGen
|
|
12
12
|
# @abstract
|
13
13
|
# @author Sylvain Daubert
|
14
14
|
class Int
|
15
|
+
include Inspectable
|
16
|
+
|
15
17
|
# Integer value
|
16
18
|
# @return [Integer]
|
17
19
|
attr_accessor :value
|
@@ -80,6 +82,18 @@ module PacketGen
|
|
80
82
|
def sz
|
81
83
|
width
|
82
84
|
end
|
85
|
+
|
86
|
+
# Format Int type when inspecting header or packet
|
87
|
+
# @return [String]
|
88
|
+
def format_inspect
|
89
|
+
format_str % [to_i.to_s, to_i]
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def format_str
|
95
|
+
"%-16s (0x%0#{width * 2}x)"
|
96
|
+
end
|
83
97
|
end
|
84
98
|
|
85
99
|
# One byte unsigned integer
|
@@ -12,6 +12,8 @@ module PacketGen
|
|
12
12
|
# By default, a null string will have one byte length (length byte set to 0).
|
13
13
|
# @author Sylvain Daubert
|
14
14
|
class IntString
|
15
|
+
include Inspectable
|
16
|
+
|
15
17
|
# internal string
|
16
18
|
# @return [String]
|
17
19
|
attr_reader :string
|
@@ -76,6 +78,7 @@ module PacketGen
|
|
76
78
|
def to_human
|
77
79
|
@string
|
78
80
|
end
|
81
|
+
alias format_inspect to_human
|
79
82
|
|
80
83
|
# Set length from internal string length
|
81
84
|
# @return [Integer]
|
data/lib/packetgen/types/oui.rb
CHANGED
@@ -14,6 +14,8 @@ module PacketGen
|
|
14
14
|
# oui.to_human # => "00:01:02"
|
15
15
|
# @author Sylvain Daubert
|
16
16
|
class OUI < Types::Fields
|
17
|
+
include Inspectable
|
18
|
+
|
17
19
|
# @attribute b2
|
18
20
|
# @return [Integer] left-most byte
|
19
21
|
define_field :b2, Types::Int8
|
@@ -44,6 +46,7 @@ module PacketGen
|
|
44
46
|
def to_human
|
45
47
|
fields.map { |m| '%02x' % self[m] }.join(':')
|
46
48
|
end
|
49
|
+
alias format_inspect to_human
|
47
50
|
end
|
48
51
|
end
|
49
52
|
end
|
data/lib/packetgen/types/tlv.rb
CHANGED
@@ -31,6 +31,8 @@ module PacketGen
|
|
31
31
|
# @deprecated Use {AbstractTLV} instead.
|
32
32
|
# @since 3.1.0 deprecated
|
33
33
|
class TLV < Fields
|
34
|
+
include Inspectable
|
35
|
+
|
34
36
|
# @!attribute type
|
35
37
|
# @return [Integer]
|
36
38
|
define_field :type, Int8
|
@@ -139,6 +141,7 @@ module PacketGen
|
|
139
141
|
"#{name} type:#{@typestr} length:#{@lenstr} value:#{value.inspect}" % [human_type,
|
140
142
|
length]
|
141
143
|
end
|
144
|
+
alias format_inspect to_human
|
142
145
|
|
143
146
|
private
|
144
147
|
|
data/lib/packetgen/utils.rb
CHANGED
@@ -53,7 +53,7 @@ module PacketGen
|
|
53
53
|
timeout = options[:timeout] || 1
|
54
54
|
my_hwaddr = Config.instance.hwaddr(iface)
|
55
55
|
arp_pkt = Packet.gen('Eth', dst: 'ff:ff:ff:ff:ff:ff', src: my_hwaddr)
|
56
|
-
arp_pkt.add('ARP', sha: Config.instance.hwaddr, spa: Config.instance.ipaddr,
|
56
|
+
arp_pkt.add('ARP', sha: Config.instance.hwaddr(iface), spa: Config.instance.ipaddr(iface),
|
57
57
|
tpa: ipaddr)
|
58
58
|
|
59
59
|
capture = Capture.new(iface: iface, timeout: timeout, max: 1,
|
data/lib/packetgen/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: packetgen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sylvain Daubert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03-
|
11
|
+
date: 2020-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: interfacez
|
@@ -208,6 +208,7 @@ files:
|
|
208
208
|
- lib/packetgen/headerable.rb
|
209
209
|
- lib/packetgen/inject.rb
|
210
210
|
- lib/packetgen/inspect.rb
|
211
|
+
- lib/packetgen/inspectable.rb
|
211
212
|
- lib/packetgen/packet.rb
|
212
213
|
- lib/packetgen/pcap.rb
|
213
214
|
- lib/packetgen/pcapng.rb
|
@@ -235,10 +236,11 @@ files:
|
|
235
236
|
- lib/packetgen/utils.rb
|
236
237
|
- lib/packetgen/utils/arp_spoofer.rb
|
237
238
|
- lib/packetgen/version.rb
|
238
|
-
homepage:
|
239
|
+
homepage:
|
239
240
|
licenses:
|
240
241
|
- MIT
|
241
242
|
metadata:
|
243
|
+
homepage_uri: https://github.com/sdaubert/packetgen
|
242
244
|
bug_tracker_uri: https://github.com/sdaubert/packetgen/issues
|
243
245
|
documentation_uri: https://www.rubydoc.info/gems/packetgen
|
244
246
|
source_code_uri: https://github.com/sdaubert/packetgen
|