packetgen 1.0.1 → 1.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.
- checksums.yaml +4 -4
- data/README.md +48 -6
- data/lib/packetgen/header.rb +70 -2
- data/lib/packetgen/packet.rb +66 -33
- data/lib/packetgen/version.rb +1 -1
- data/lib/packetgen.rb +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21f7a146ddb2ba2b7e8b86933360a97d95344fef
|
4
|
+
data.tar.gz: e26c103bc3ef15ca5d7064163cd747716215a71b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 304723a5e11714b3092606fd7f7529fd1a4d6ec0b427e364d3603b63cca20fbfc410a996d80a1a3cfb58106e855dcf5fbc76dd957b589eee6b97c1fa70379312
|
7
|
+
data.tar.gz: d95e4b3fa118e33ae9296626fd2855d6d3eaad9a4d2e0fd4605cfca5bdfd193b4397fd4411164a018dfbbf37868a35026134acf01cd2fdb52729cb90c05bc7fd
|
data/README.md
CHANGED
@@ -94,11 +94,10 @@ pkt.is? 'TCP' # => true
|
|
94
94
|
pkt.is? 'IP' # => true
|
95
95
|
pkt.is? 'UDP' # => false
|
96
96
|
|
97
|
-
# encapulsate/decapsulate packets
|
98
|
-
pkt2 = PacketGen.gen('IP')
|
99
|
-
|
100
|
-
|
101
|
-
pkt.decap('IP', 'ESP') # pkt is now inner IP/TCP packet
|
97
|
+
# encapulsate/decapsulate packets
|
98
|
+
pkt2 = PacketGen.gen('IP')
|
99
|
+
pkt2.encapsulate pkt # pkt2 is now a IP/IP/TCP packet
|
100
|
+
pkt2.decapsulate(pkt2.ip) # pkt2 is now inner IP/TCP packet
|
102
101
|
```
|
103
102
|
|
104
103
|
### Read/write PcapNG files
|
@@ -112,12 +111,55 @@ pkt.write('one_packet.pcapng')
|
|
112
111
|
PacketGen.write('more_packets.pcapng', packets)
|
113
112
|
```
|
114
113
|
|
114
|
+
### Add custom header/protocol
|
115
|
+
Since v1.1.0, PacketGen permits adding you own header classes.
|
116
|
+
First, define the new header class. By example:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
module MyModule
|
120
|
+
class MyHeader < Struct.new(:field1, :field2)
|
121
|
+
include PacketGen::StructFu
|
122
|
+
include PacketGen::Header::HeaderMethods
|
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
|
133
|
+
end
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
137
|
+
Then, class must be declared to PacketGen:
|
138
|
+
|
139
|
+
```
|
140
|
+
PacketGen::Header.add_class MyModule::MyHeader
|
141
|
+
```
|
142
|
+
|
143
|
+
Finally, bindings must be declared:
|
144
|
+
|
145
|
+
```
|
146
|
+
# bind MyHeader as IP protocol number 254 (needed by Packet#parse)
|
147
|
+
PacketGen::Header::IP.bind_header MyModule::MyHeader, protocol: 254
|
148
|
+
```
|
149
|
+
|
150
|
+
And use it:
|
151
|
+
|
152
|
+
```
|
153
|
+
pkt = Packet.gen('IP').add('MyHeader', field1: 0x12345678)
|
154
|
+
pkt.myheader.field2.read 0x01
|
155
|
+
```
|
156
|
+
|
115
157
|
## Pull requests?
|
116
158
|
|
117
159
|
yes
|
118
160
|
|
119
161
|
## License
|
120
|
-
MIT License (see [LICENSE](https://github.com/sdaubert/packetgen/LICENSE))
|
162
|
+
MIT License (see [LICENSE](https://github.com/sdaubert/packetgen/blob/master/LICENSE))
|
121
163
|
|
122
164
|
Copyright © 2016 Sylvain Daubert
|
123
165
|
|
data/lib/packetgen/header.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
# This file is part of PacketGen
|
2
3
|
# See https://github.com/sdaubert/packetgen for more informations
|
3
4
|
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
@@ -5,14 +6,81 @@
|
|
5
6
|
|
6
7
|
module PacketGen
|
7
8
|
# Namespace for protocol header classes
|
9
|
+
# == Add a foreign header class
|
10
|
+
# Since v1.1.0, PacketGen permits adding you own header classes.
|
11
|
+
# First, define the new header class. By example:
|
12
|
+
# module MyModule
|
13
|
+
# class MyHeader < Struct.new(:field1, :field2)
|
14
|
+
# include PacketGen::StructFu
|
15
|
+
# include PacketGen::Header::HeaderMethods
|
16
|
+
# extend PacketGen::Header::HeaderClassMethods
|
17
|
+
#
|
18
|
+
# def initialize(options={})
|
19
|
+
# super Int32.new(options[:field1]), Int32.new(options[:field2])
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def read(str)
|
23
|
+
# self[:field1].read str[0, 4]
|
24
|
+
# self[:field2].read str[4, 4]
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
# Then, class must be declared to PacketGen:
|
29
|
+
# PacketGen::Header.add_class MyModule::MyHeader
|
30
|
+
# Finally, bindings must be declared:
|
31
|
+
# # bind MyHeader as IP protocol number 254 (needed by Packet#parse)
|
32
|
+
# PacketGen::Header::IP.bind_header MyModule::MyHeader, protocol: 254
|
33
|
+
# And use it:
|
34
|
+
# pkt = Packet.gen('IP').add('MyHeader', field1: 0x12345678)
|
35
|
+
# pkt.myheader.field2.read 0x01
|
8
36
|
# @author Sylvain Daubert
|
9
37
|
module Header
|
10
38
|
|
39
|
+
@added_header_classes = {}
|
40
|
+
|
11
41
|
# Get known header classes
|
12
42
|
# @return [Array<Class>]
|
13
43
|
def self.all
|
14
|
-
|
15
|
-
|
44
|
+
return @header_classes if @header_classes
|
45
|
+
|
46
|
+
@builtin ||= constants.map { |sym| const_get sym }.
|
47
|
+
select { |klass| klass < Struct && klass < HeaderMethods }
|
48
|
+
@header_classes = @builtin + @added_header_classes.values
|
49
|
+
end
|
50
|
+
|
51
|
+
# Add a foreign header class to known header classes. This is
|
52
|
+
# needed by {Packet.gen} and {Packet#add}.
|
53
|
+
# @param [Class] klass a header class, which should include
|
54
|
+
# {Header::HeaderMethods} and {Header::HeaderClassMethods}
|
55
|
+
# @return [void]
|
56
|
+
# @since 1.1.0
|
57
|
+
def self.add_class(klass)
|
58
|
+
protocol_name = klass.to_s.sub(/.*::/, '')
|
59
|
+
@added_header_classes[protocol_name] = klass
|
60
|
+
@header_classes = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
# Remove a foreign header (previously added by {.add_header_class}à
|
64
|
+
# from known header classes.
|
65
|
+
# @param [Class] klass
|
66
|
+
# @return [void]
|
67
|
+
# @since 1.1.0
|
68
|
+
def self.remove_class(klass)
|
69
|
+
protocol_name = klass.to_s.sub(/.*::/, '')
|
70
|
+
@added_header_classes.delete protocol_name
|
71
|
+
@header_classes = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# Get header class from its name
|
75
|
+
# @param [String] name
|
76
|
+
# @return [Class,nil]
|
77
|
+
# @since 1.1.0
|
78
|
+
def self.get_header_class_by_name(name)
|
79
|
+
if Header.const_defined? name
|
80
|
+
Header.const_get name
|
81
|
+
else
|
82
|
+
@added_header_classes[name]
|
83
|
+
end
|
16
84
|
end
|
17
85
|
end
|
18
86
|
end
|
data/lib/packetgen/packet.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
# This file is part of PacketGen
|
2
3
|
# See https://github.com/sdaubert/packetgen for more informations
|
3
4
|
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
@@ -163,25 +164,7 @@ module PacketGen
|
|
163
164
|
klass = check_protocol(protocol)
|
164
165
|
|
165
166
|
header = klass.new(options)
|
166
|
-
|
167
|
-
if prev_header
|
168
|
-
binding = prev_header.class.known_headers[klass]
|
169
|
-
if binding.nil?
|
170
|
-
msg = "#{prev_header.class} knowns no layer association with #{protocol}. "
|
171
|
-
msg << "Try #{prev_header.class}.bind_layer(PacketGen::Header::#{protocol}, "
|
172
|
-
msg << "#{prev_header.protocol_name.downcase}_proto_field: "
|
173
|
-
msg << "value_for_#{protocol.downcase})"
|
174
|
-
raise ArgumentError, msg
|
175
|
-
end
|
176
|
-
prev_header[binding.key].read binding.value
|
177
|
-
prev_header.body = header
|
178
|
-
end
|
179
|
-
header.packet = self
|
180
|
-
@headers << header
|
181
|
-
unless respond_to? protocol.downcase
|
182
|
-
self.class.class_eval "def #{protocol.downcase}(arg=nil);" \
|
183
|
-
"header('#{protocol}', arg); end"
|
184
|
-
end
|
167
|
+
add_header header
|
185
168
|
self
|
186
169
|
end
|
187
170
|
|
@@ -257,6 +240,34 @@ module PacketGen
|
|
257
240
|
end
|
258
241
|
end
|
259
242
|
|
243
|
+
# Encapulate another packet in +self+
|
244
|
+
# @param [Packet] other
|
245
|
+
# @return [self] +self+ with new headers from +other+
|
246
|
+
# @since 1.1.0
|
247
|
+
def encapsulate(other)
|
248
|
+
other.headers.each { |h| add_header h }
|
249
|
+
end
|
250
|
+
|
251
|
+
# Remove headers from +self+
|
252
|
+
# @param [Array<Header>] headers
|
253
|
+
# @return [self] +self+ with some headers removed
|
254
|
+
# @raise [FormatError] any headers not in +self+
|
255
|
+
# @raise [FormatError] removed headers result in an unknown binding
|
256
|
+
# @since 1.1.0
|
257
|
+
def decapsulate(*headers)
|
258
|
+
headers.each do |header|
|
259
|
+
idx = @headers.index(header)
|
260
|
+
raise FormatError, 'header not in packet!' if idx.nil?
|
261
|
+
|
262
|
+
prev_header = idx > 0 ? @headers[idx - 1] : nil
|
263
|
+
next_header = (idx+1) < @headers.size ? @headers[idx + 1] : nil
|
264
|
+
@headers.delete_at(idx)
|
265
|
+
add_header(next_header, prev_header) if prev_header and next_header
|
266
|
+
end
|
267
|
+
rescue ArgumentError => ex
|
268
|
+
raise FormatError, ex.message
|
269
|
+
end
|
270
|
+
|
260
271
|
# @return [String]
|
261
272
|
def inspect
|
262
273
|
str = Inspect.dashed_line(self.class)
|
@@ -274,17 +285,15 @@ module PacketGen
|
|
274
285
|
|
275
286
|
private
|
276
287
|
|
277
|
-
# @overload header(
|
278
|
-
# @param [
|
288
|
+
# @overload header(klass, layer=1)
|
289
|
+
# @param [Class] klass
|
279
290
|
# @param [Integer] layer
|
280
|
-
# @overload header(
|
281
|
-
# @param [String]
|
291
|
+
# @overload header(klass, options={})
|
292
|
+
# @param [String] klass
|
282
293
|
# @param [Hash] options
|
294
|
+
# @raise [ArgumentError] unknown option
|
283
295
|
# @return [Header::Base]
|
284
|
-
|
285
|
-
def header(protocol, arg)
|
286
|
-
klass = check_protocol protocol
|
287
|
-
|
296
|
+
def header(klass, arg)
|
288
297
|
headers = @headers.select { |h| h.is_a? klass }
|
289
298
|
layer = arg.is_a?(Integer) ? arg : 1
|
290
299
|
header = headers[layer - 1]
|
@@ -292,7 +301,7 @@ module PacketGen
|
|
292
301
|
if arg.is_a? Hash
|
293
302
|
arg.each do |key, value|
|
294
303
|
unless header.respond_to? "#{key}="
|
295
|
-
raise ArgumentError, "unknown #{key} attribute for #{
|
304
|
+
raise ArgumentError, "unknown #{key} attribute for #{klass}"
|
296
305
|
end
|
297
306
|
header.send "#{key}=", value
|
298
307
|
end
|
@@ -305,13 +314,37 @@ module PacketGen
|
|
305
314
|
# @param [String] protocol
|
306
315
|
# @raise [ArgumentError] unknown protocol
|
307
316
|
def check_protocol(protocol)
|
308
|
-
|
309
|
-
|
310
|
-
end
|
311
|
-
klass = Header.const_get(protocol)
|
312
|
-
raise ArgumentError, "unknown #{protocol} protocol" unless klass.is_a? Class
|
317
|
+
klass = Header.get_header_class_by_name(protocol)
|
318
|
+
raise ArgumentError, "unknown #{protocol} protocol" if klass.nil?
|
313
319
|
klass
|
314
320
|
end
|
321
|
+
|
322
|
+
# Add a header to packet
|
323
|
+
# @param [Header::HeaderMethods] header
|
324
|
+
# @param [Header::HeaderMethods] previous_header
|
325
|
+
# @return [void]
|
326
|
+
def add_header(header, previous_header=nil)
|
327
|
+
protocol = header.protocol_name
|
328
|
+
prev_header = previous_header || @headers.last
|
329
|
+
if prev_header
|
330
|
+
binding = prev_header.class.known_headers[header.class]
|
331
|
+
if binding.nil?
|
332
|
+
msg = "#{prev_header.class} knowns no layer association with #{protocol}. "
|
333
|
+
msg << "Try #{prev_header.class}.bind_layer(PacketGen::Header::#{protocol}, "
|
334
|
+
msg << "#{prev_header.protocol_name.downcase}_proto_field: "
|
335
|
+
msg << "value_for_#{protocol.downcase})"
|
336
|
+
raise ArgumentError, msg
|
337
|
+
end
|
338
|
+
prev_header[binding.key].read binding.value
|
339
|
+
prev_header.body = header
|
340
|
+
end
|
341
|
+
header.packet = self
|
342
|
+
@headers << header unless previous_header
|
343
|
+
unless respond_to? protocol.downcase
|
344
|
+
self.class.class_eval "def #{protocol.downcase}(arg=nil);" \
|
345
|
+
"header(#{header.class}, arg); end"
|
346
|
+
end
|
347
|
+
end
|
315
348
|
end
|
316
349
|
end
|
317
350
|
|
data/lib/packetgen/version.rb
CHANGED
data/lib/packetgen.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: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sylvain Daubert
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pcaprub
|