packetgen 1.4.3 → 2.0.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/.travis.yml +3 -4
- data/README.md +6 -6
- data/lib/packetgen.rb +0 -1
- data/lib/packetgen/capture.rb +15 -37
- data/lib/packetgen/header.rb +6 -2
- data/lib/packetgen/header/asn1_base.rb +86 -0
- data/lib/packetgen/header/base.rb +68 -44
- data/lib/packetgen/header/crypto.rb +62 -0
- data/lib/packetgen/header/dns.rb +3 -3
- data/lib/packetgen/header/dot11.rb +5 -0
- data/lib/packetgen/header/esp.rb +7 -43
- data/lib/packetgen/header/ike.rb +235 -0
- data/lib/packetgen/header/ike/auth.rb +197 -0
- data/lib/packetgen/header/ike/cert.rb +105 -0
- data/lib/packetgen/header/ike/certreq.rb +69 -0
- data/lib/packetgen/header/ike/id.rb +131 -0
- data/lib/packetgen/header/ike/ke.rb +74 -0
- data/lib/packetgen/header/ike/nonce.rb +35 -0
- data/lib/packetgen/header/ike/notify.rb +220 -0
- data/lib/packetgen/header/ike/payload.rb +307 -0
- data/lib/packetgen/header/ike/sa.rb +577 -0
- data/lib/packetgen/header/ike/sk.rb +255 -0
- data/lib/packetgen/header/ike/ts.rb +287 -0
- data/lib/packetgen/header/ike/vendor_id.rb +34 -0
- data/lib/packetgen/header/ip.rb +4 -4
- data/lib/packetgen/header/ipv6.rb +3 -3
- data/lib/packetgen/header/snmp.rb +283 -0
- data/lib/packetgen/header/tcp.rb +3 -3
- data/lib/packetgen/inspect.rb +35 -9
- data/lib/packetgen/packet.rb +23 -9
- data/lib/packetgen/types/fields.rb +11 -3
- data/lib/packetgen/version.rb +1 -1
- data/packetgen.gemspec +2 -0
- metadata +52 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77561a943f0b532168700f1752631dd1f8080ebf
|
4
|
+
data.tar.gz: 36a505013a6ff0e37a35e64d62ec627dcba5dc22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f72762403800113f9816892b1d7d0df0e5fa417a28afbbc7cf56a5b15d340100721ce46bdc2fcb003a2818dec57780bdb8d3172cbe4bdcad2ba8a2dc4ed8ab16
|
7
|
+
data.tar.gz: cae3e091c5423afd66ab45a2ebea34a265e9b5a751b14bfb42e9a04bb92deb6945af0c5a85ed9520d96e721f33c815817b9898aca74bb6e51277eb3855809ed5
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -36,11 +36,11 @@ PacketGen.gen('IP').to_s
|
|
36
36
|
### Send packets on wire
|
37
37
|
```ruby
|
38
38
|
# send Ethernet packet
|
39
|
-
PacketGen.gen('Eth', src: '00:00:00:00:01', dst: '00:00:00:00:02').to_w
|
39
|
+
PacketGen.gen('Eth', src: '00:00:00:00:00:01', dst: '00:00:00:00:00:02').to_w
|
40
40
|
# send IP packet
|
41
41
|
PacketGen.gen('IP', src: '192.168.1.1', dst: '192.168.1.2').to_w
|
42
42
|
# send forged IP packet over Ethernet
|
43
|
-
PacketGen.gen('Eth', src: '00:00:00:00:01', dst: '00:00:00:00:02').add('IP').to_w('eth1')
|
43
|
+
PacketGen.gen('Eth', src: '00:00:00:00:00:01', dst: '00:00:00:00:00:02').add('IP').to_w('eth1')
|
44
44
|
```
|
45
45
|
|
46
46
|
### Parse packets from binary data
|
@@ -50,16 +50,16 @@ packet = PacketGen.parse(binary_data)
|
|
50
50
|
|
51
51
|
### Capture packets from wire
|
52
52
|
```ruby
|
53
|
-
# Capture packets, action from a block
|
54
|
-
PacketGen.capture
|
53
|
+
# Capture packets from first network interface, action from a block
|
54
|
+
PacketGen.capture do |packet|
|
55
55
|
do_stuffs_with_packet
|
56
56
|
end
|
57
57
|
|
58
58
|
# Capture some packets, and act on them afterward
|
59
|
-
packets = PacketGen.capture('eth0', max: 10) # return when 10 packets were captured
|
59
|
+
packets = PacketGen.capture(iface: 'eth0', max: 10) # return when 10 packets were captured
|
60
60
|
|
61
61
|
# Use filters
|
62
|
-
packets = PacketGen.capture('eth0', filter: 'ip src 1.1.1.2', max: 1)
|
62
|
+
packets = PacketGen.capture(iface: 'eth0', filter: 'ip src 1.1.1.2', max: 1)
|
63
63
|
```
|
64
64
|
|
65
65
|
### Easily manipulate packets
|
data/lib/packetgen.rb
CHANGED
data/lib/packetgen/capture.rb
CHANGED
@@ -25,49 +25,27 @@ module PacketGen
|
|
25
25
|
# @return [String]
|
26
26
|
attr_reader :iface
|
27
27
|
|
28
|
-
# @
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
|
43
|
-
# packets on. Default: Use default interface lookup. If no interface found,
|
44
|
-
# use loopback one.
|
45
|
-
# @option options [Integer] :max maximum number of packets to capture.
|
46
|
-
# @option options [Integer] :timeout maximum number of seconds before end
|
47
|
-
# of capture. Default: +nil+ (no timeout)
|
48
|
-
# @option options [String] :filter bpf filter
|
49
|
-
# @option options [Boolean] :promiscuous (default: +false+)
|
50
|
-
# @option options [Boolean] :parse parse raw data to generate packets before
|
51
|
-
# yielding. Default: +true+
|
52
|
-
# @option options [Integer] :snaplen maximum number of bytes to capture for
|
53
|
-
# each packet.
|
54
|
-
def initialize(iface_or_options={}, options={})
|
28
|
+
# @param [Hash] options
|
29
|
+
# @option options [String] :iface interface on which capture
|
30
|
+
# packets on. Default: Use default interface lookup. If no interface found,
|
31
|
+
# use loopback one.
|
32
|
+
# @option options [Integer] :max maximum number of packets to capture.
|
33
|
+
# @option options [Integer] :timeout maximum number of seconds before end
|
34
|
+
# of capture. Default: +nil+ (no timeout)
|
35
|
+
# @option options [String] :filter bpf filter
|
36
|
+
# @option options [Boolean] :promiscuous (default: +false+)
|
37
|
+
# @option options [Boolean] :parse parse raw data to generate packets before
|
38
|
+
# yielding. Default: +true+
|
39
|
+
# @option options [Integer] :snaplen maximum number of bytes to capture for
|
40
|
+
# each packet.
|
41
|
+
# @since 2.0.0 remove old 1.x API
|
42
|
+
def initialize(options={})
|
55
43
|
begin
|
56
44
|
@iface = Pcap.lookupdev
|
57
45
|
rescue PCAPRUB::BindingError
|
58
46
|
@iface = 'lo'
|
59
47
|
end
|
60
48
|
|
61
|
-
case iface_or_options
|
62
|
-
when Hash
|
63
|
-
options = iface_or_options
|
64
|
-
else
|
65
|
-
warn "[deprecation] use of PacketGen::Capture#initialize with iface name as\n" \
|
66
|
-
" first argument is deprecated. Instead, use:\n" \
|
67
|
-
' PacketGen::Capture.new(iface: \'name\').'
|
68
|
-
@iface = iface_or_options.to_s
|
69
|
-
end
|
70
|
-
|
71
49
|
@packets = []
|
72
50
|
@raw_packets = []
|
73
51
|
@promisc = false
|
data/lib/packetgen/header.rb
CHANGED
@@ -48,7 +48,7 @@ module PacketGen
|
|
48
48
|
# @return [void]
|
49
49
|
# @since 1.1.0
|
50
50
|
def self.add_class(klass)
|
51
|
-
protocol_name = klass.
|
51
|
+
protocol_name = klass.new.protocol_name
|
52
52
|
@added_header_classes[protocol_name] = klass
|
53
53
|
@header_classes = nil
|
54
54
|
end
|
@@ -59,7 +59,7 @@ module PacketGen
|
|
59
59
|
# @return [void]
|
60
60
|
# @since 1.1.0
|
61
61
|
def self.remove_class(klass)
|
62
|
-
protocol_name = klass.
|
62
|
+
protocol_name = klass.new.protocol_name
|
63
63
|
@added_header_classes.delete protocol_name
|
64
64
|
@header_classes = nil
|
65
65
|
end
|
@@ -78,6 +78,7 @@ module PacketGen
|
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
|
+
require_relative 'header/crypto'
|
81
82
|
require_relative 'header/base'
|
82
83
|
require_relative 'header/eth'
|
83
84
|
require_relative 'header/dot11'
|
@@ -92,4 +93,7 @@ require_relative 'header/icmpv6'
|
|
92
93
|
require_relative 'header/udp'
|
93
94
|
require_relative 'header/tcp'
|
94
95
|
require_relative 'header/esp'
|
96
|
+
require_relative 'header/ike'
|
95
97
|
require_relative 'header/dns'
|
98
|
+
require_relative 'header/asn1_base'
|
99
|
+
require_relative 'header/snmp'
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# This file is part of PacketGen
|
2
|
+
# See https://github.com/sdaubert/packetgen for more informations
|
3
|
+
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
4
|
+
# This program is published under MIT license.
|
5
|
+
require 'rasn1'
|
6
|
+
|
7
|
+
module PacketGen
|
8
|
+
module Header
|
9
|
+
|
10
|
+
# @abstract Base class for ASN.1 header types.
|
11
|
+
# Subclasses may define magic methods:
|
12
|
+
# * {#parse?}.
|
13
|
+
# @author Sylvain Daubert
|
14
|
+
class ASN1Base < RASN1::Model
|
15
|
+
|
16
|
+
# @api private
|
17
|
+
# Reference on packet which owns this header
|
18
|
+
attr_accessor :packet
|
19
|
+
|
20
|
+
# Give protocol name for this class
|
21
|
+
# @return [String]
|
22
|
+
# @since 2.0.0
|
23
|
+
def self.protocol_name
|
24
|
+
self.new.protocol_name
|
25
|
+
end
|
26
|
+
|
27
|
+
# Define some methods from given ASN.1 fields to mimic {Base} attributes
|
28
|
+
# @param [Array<Symbol>] attributes
|
29
|
+
# @return [void]
|
30
|
+
def self.define_attributes(*attributes)
|
31
|
+
@attributes = attributes
|
32
|
+
attributes.each do |attr|
|
33
|
+
class_eval "def #{attr}; @elements[:#{attr}].value; end\n" \
|
34
|
+
"def #{attr}=(v); @elements[:#{attr}].value = v; end"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return header protocol name
|
39
|
+
# @return [String]
|
40
|
+
def protocol_name
|
41
|
+
return @protocol_name if @protocol_name
|
42
|
+
|
43
|
+
classname = self.class.to_s
|
44
|
+
@protocol_name = if classname.start_with?('PacketGen::Header')
|
45
|
+
classname.sub(/.*Header::/, '')
|
46
|
+
else
|
47
|
+
classname.sub(/.*::/, '')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
# return header method name
|
51
|
+
# @return [String]
|
52
|
+
# @since 2.0.0
|
53
|
+
def method_name
|
54
|
+
return @method_name if @method_name
|
55
|
+
|
56
|
+
@method_name = protocol_name.downcase.sub(/::/, '_')
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [true]
|
60
|
+
def parse?
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
alias :parse :parse!
|
65
|
+
alias :to_s :to_der
|
66
|
+
|
67
|
+
# Read a BER string
|
68
|
+
# @param [String] str
|
69
|
+
# @return [ASN1Base] self
|
70
|
+
def read(str)
|
71
|
+
parse(str, ber: true)
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
# Common inspect method for ASN.1 headers
|
76
|
+
# @return [String]
|
77
|
+
def inspect
|
78
|
+
str = Inspect.dashed_line(self.class, 2)
|
79
|
+
self.class.class_eval { @attributes }.each do |attr|
|
80
|
+
str << Inspect.inspect_asn1_attribute(attr, self[attr], 2)
|
81
|
+
end
|
82
|
+
str
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -6,8 +6,9 @@
|
|
6
6
|
module PacketGen
|
7
7
|
module Header
|
8
8
|
|
9
|
-
# @abstract
|
10
|
-
#
|
9
|
+
# @abstract Base class for all header types.
|
10
|
+
# Subclasses may define magic methods:
|
11
|
+
# * {#parse?}.
|
11
12
|
# @author Sylvain Daubert
|
12
13
|
class Base < Types::Fields
|
13
14
|
|
@@ -39,6 +40,32 @@ module PacketGen
|
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
43
|
+
# @api private
|
44
|
+
# Class to handle a header association from procs
|
45
|
+
class ProcBinding
|
46
|
+
|
47
|
+
# @param [Array<Proc>] procs first proc is used to set fields, second proc is
|
48
|
+
# used to check binding
|
49
|
+
def initialize(procs)
|
50
|
+
@set = procs.shift
|
51
|
+
@check = procs.shift
|
52
|
+
end
|
53
|
+
|
54
|
+
# Check +fields+ responds to binding
|
55
|
+
# @param [Types::Fields] fields
|
56
|
+
# @return [Boolean]
|
57
|
+
def check?(fields)
|
58
|
+
@check.call(fields)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Set +fields+ field to binding value
|
62
|
+
# @param [Types::Fields] fields
|
63
|
+
# @return [void]
|
64
|
+
def set(fields)
|
65
|
+
@set.call(fields)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
42
69
|
# @api private
|
43
70
|
# Class to handle header associations
|
44
71
|
class Bindings
|
@@ -100,41 +127,6 @@ module PacketGen
|
|
100
127
|
end
|
101
128
|
end
|
102
129
|
|
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
|
-
|
138
130
|
# @api private
|
139
131
|
# Reference on packet which owns this header
|
140
132
|
attr_accessor :packet
|
@@ -149,9 +141,10 @@ module PacketGen
|
|
149
141
|
|
150
142
|
# Bind a upper header to current class
|
151
143
|
# Header1.bind_header Header2, field1: 43
|
152
|
-
# Header1.bind_header
|
153
|
-
# Header1.bind_header
|
154
|
-
# Header1.bind_header
|
144
|
+
# Header1.bind_header Header3, field1: 43, field2: 43
|
145
|
+
# Header1.bind_header Header4, op: :and, field1: 43, field2: 43
|
146
|
+
# Header1.bind_header Header5, field1: ->(v) { v.nil? ? 128 : v > 127 }
|
147
|
+
# Header1.bind_header Header6, proc: ->(pkt) { pkt.header1.field1 == 1 && pkt.header1.body[0..1] == "\x00\x00" }
|
155
148
|
# @param [Class] header_klass header class to bind to current class
|
156
149
|
# @param [Hash] args current class fields and their value when +header_klass+
|
157
150
|
# is embedded in current class. Given value may be a lambda, whose alone argument
|
@@ -165,13 +158,28 @@ module PacketGen
|
|
165
158
|
# @return [void]
|
166
159
|
def self.bind_header(header_klass, args={})
|
167
160
|
op = args.delete(:op) || :or
|
168
|
-
|
169
|
-
|
161
|
+
if @known_headers[header_klass].nil? || @known_headers[header_klass].op != op
|
162
|
+
bindings = Bindings.new(op)
|
163
|
+
@known_headers[header_klass] = bindings
|
164
|
+
else
|
165
|
+
bindings = @known_headers[header_klass]
|
166
|
+
end
|
170
167
|
args.each do |key, value|
|
171
|
-
|
168
|
+
if key == :procs
|
169
|
+
bindings << ProcBinding.new(value)
|
170
|
+
else
|
171
|
+
bindings << Binding.new(key, value)
|
172
|
+
end
|
172
173
|
end
|
173
174
|
end
|
174
175
|
|
176
|
+
# Give protocol name for this class
|
177
|
+
# @return [String]
|
178
|
+
# @since 2.0.0
|
179
|
+
def self.protocol_name
|
180
|
+
self.new.protocol_name
|
181
|
+
end
|
182
|
+
|
175
183
|
# @api private
|
176
184
|
# Get knwon headers
|
177
185
|
# @return [Hash] keys: header classes, values: hashes
|
@@ -182,7 +190,23 @@ module PacketGen
|
|
182
190
|
# Return header protocol name
|
183
191
|
# @return [String]
|
184
192
|
def protocol_name
|
185
|
-
|
193
|
+
return @protocol_name if @protocol_name
|
194
|
+
|
195
|
+
classname = self.class.to_s
|
196
|
+
@protocol_name = if classname.start_with?('PacketGen::Header')
|
197
|
+
classname.sub(/.*Header::/, '')
|
198
|
+
else
|
199
|
+
classname.sub(/.*::/, '')
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# return header method name
|
204
|
+
# @return [String]
|
205
|
+
# @since 2.0.0
|
206
|
+
def method_name
|
207
|
+
return @method_name if @method_name
|
208
|
+
|
209
|
+
@method_name = protocol_name.downcase.sub(/::/, '_')
|
186
210
|
end
|
187
211
|
|
188
212
|
# @abstract Should be redefined by subclasses. This method should check invariant
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module PacketGen
|
2
|
+
module Header
|
3
|
+
|
4
|
+
# Mixin for cryptographic classes
|
5
|
+
# @api private
|
6
|
+
# @author Sylvain Daubert
|
7
|
+
module Crypto
|
8
|
+
|
9
|
+
# Cryptographic error
|
10
|
+
class Error < PacketGen::Error; end
|
11
|
+
|
12
|
+
# Register cryptographic modes
|
13
|
+
# @param [OpenSSL::Cipher] conf
|
14
|
+
# @param [OpenSSL::HMAC] intg
|
15
|
+
# @return [void]
|
16
|
+
def set_crypto(conf, intg)
|
17
|
+
@conf, @intg = conf, intg
|
18
|
+
if conf.authenticated?
|
19
|
+
# #auth_tag_len only supported from ruby 2.4.0
|
20
|
+
@conf.auth_tag_len = @trunc if @conf.respond_to? :auth_tag_len
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get confidentiality mode name
|
25
|
+
# @return [String]
|
26
|
+
def confidentiality_mode
|
27
|
+
mode = @conf.name.match(/-([^-]*)$/)[1]
|
28
|
+
raise Error, 'unknown cipher mode' if mode.nil?
|
29
|
+
mode.downcase
|
30
|
+
end
|
31
|
+
|
32
|
+
# Say if crypto modes permit authentication
|
33
|
+
# @return [Boolean]
|
34
|
+
def authenticated?
|
35
|
+
@conf.authenticated? or !!@intg
|
36
|
+
end
|
37
|
+
|
38
|
+
def authenticate!
|
39
|
+
@conf.final
|
40
|
+
if @intg
|
41
|
+
@intg.update @esn.to_s if @esn
|
42
|
+
@intg.digest[0, @icv_length] == @icv
|
43
|
+
else
|
44
|
+
true
|
45
|
+
end
|
46
|
+
rescue OpenSSL::Cipher::CipherError
|
47
|
+
false
|
48
|
+
end
|
49
|
+
|
50
|
+
def encipher(data)
|
51
|
+
enciphered_data = @conf.update(data)
|
52
|
+
@intg.update(enciphered_data) if @intg
|
53
|
+
enciphered_data
|
54
|
+
end
|
55
|
+
|
56
|
+
def decipher(data)
|
57
|
+
@intg.update(data) if @intg
|
58
|
+
@conf.update(data)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|