packetgen 1.4.3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|