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
@@ -0,0 +1,255 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module PacketGen
|
3
|
+
module Header
|
4
|
+
class IKE
|
5
|
+
|
6
|
+
# This class handles encrypted payloads, denoted SK.
|
7
|
+
#
|
8
|
+
# The encrypted payload contains other payloads in encrypted form.
|
9
|
+
# The Encrypted payload consists of the IKE generic payload header followed
|
10
|
+
# by individual fields as follows:
|
11
|
+
# 1 2 3
|
12
|
+
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
13
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
14
|
+
# | Next Payload |C| RESERVED | Payload Length |
|
15
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
16
|
+
# | Initialization Vector |
|
17
|
+
# | (length is block size for encryption algorithm) |
|
18
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
19
|
+
# ~ Encrypted IKE Payloads ~
|
20
|
+
# + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
21
|
+
# | | Padding (0-255 octets) |
|
22
|
+
# +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|
23
|
+
# | | Pad Length |
|
24
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
25
|
+
# ~ Integrity Checksum Data ~
|
26
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
27
|
+
# Encrypted payloads are set in {#content} field, as a {Types::String}.
|
28
|
+
# All others fields are only set when decrypting a previously read SK
|
29
|
+
# payload. They also may be set manually to encrypt IKE payloads.
|
30
|
+
#
|
31
|
+
# == Read and decrypt a SK payload
|
32
|
+
# # Read a IKE packet
|
33
|
+
# pkt = PacketGen.read(str)
|
34
|
+
# # decrypt SK payload
|
35
|
+
# cipher = OpenSSL::Cipher.new('aes-128-ctr')
|
36
|
+
# cipher.decrypt
|
37
|
+
# cipher_key = aes_key
|
38
|
+
# hmac = OpenSSL::HMAC.new(hmac_key, OpenSSL::Digest::SHA256.new)
|
39
|
+
# pkt.ike_sk.decrypt! cipher, intmode: hmac, icv_length: 16 # => true if authentication is verified
|
40
|
+
# pkt.ike_sk.body # => kind of PacketGen::Header::IKE::Payload
|
41
|
+
#
|
42
|
+
# == Set and encrypt a SK payload
|
43
|
+
# # Create a IKE packet
|
44
|
+
# pkt = PacketGen.gen('IP').add('IP').add('UDP').add('IKE', init_spi: 0x123456789, resp_spi: 0x987654321, type: 'IKE_AUTH', message_id: 1)
|
45
|
+
# # Add SK payload
|
46
|
+
# pkt.add('IKE::SK', icv_length: 16)
|
47
|
+
# # Add others unencrypted payloads
|
48
|
+
# pkt.add('IKE::IDi').add('IKE::Auth').add('IKE::SA').add('IKE::TSi').add('IKE::TSr')
|
49
|
+
# # encrypt SK payload
|
50
|
+
# cipher = OpenSSL::Cipher.new('aes-128-ctr')
|
51
|
+
# cipher.encrypt
|
52
|
+
# cipher_key = aes_key
|
53
|
+
# hmac = OpenSSL::HMAC.new(hmac_key, OpenSSL::Digest::SHA256.new)
|
54
|
+
# pkt.ike_sk.encrypt! cipher, iv, salt: salt, intmode: hmac
|
55
|
+
# pkt.ike_sk.body # => String
|
56
|
+
# pkt.calc_length
|
57
|
+
#
|
58
|
+
# @author Sylvain Daubert
|
59
|
+
class SK < Payload
|
60
|
+
include Crypto
|
61
|
+
|
62
|
+
# Payload type number
|
63
|
+
PAYLOAD_TYPE = 46
|
64
|
+
|
65
|
+
# ICV (Integrity Check Value) length
|
66
|
+
# @return [Integer]
|
67
|
+
attr_accessor :icv_length
|
68
|
+
|
69
|
+
# @param [Hash] options
|
70
|
+
# @option options [Integer] :icv_length ICV length
|
71
|
+
def initialize(options={})
|
72
|
+
@icv_length = options[:icv_length] || 0
|
73
|
+
super
|
74
|
+
end
|
75
|
+
|
76
|
+
# Decrypt in-place SK payload.
|
77
|
+
# @param [OpenSSL::Cipher] cipher keyed cipher
|
78
|
+
# This cipher is confidentiality-only one, or AEAD one. To use a second
|
79
|
+
# cipher to add integrity, use +:intmode+ option.
|
80
|
+
# @param [Hash] options
|
81
|
+
# @option options [Boolean] :parse parse deciphered payload to retrieve
|
82
|
+
# headers (default: +true+)
|
83
|
+
# @option options [Fixnum] :icv_length ICV length for captured packets,
|
84
|
+
# or read from PCapNG files
|
85
|
+
# @option options [String] :salt salt value for CTR and GCM modes
|
86
|
+
# @option options [OpenSSL::HMAC] :intmode integrity mode to use with a
|
87
|
+
# confidentiality-only cipher. Only HMAC are supported.
|
88
|
+
# @return [Boolean] +true+ if SK payload is authenticated
|
89
|
+
def decrypt!(cipher, options={})
|
90
|
+
opt = { salt: '', parse: true }.merge!(options)
|
91
|
+
|
92
|
+
set_crypto cipher, opt[:intmode]
|
93
|
+
|
94
|
+
case confidentiality_mode
|
95
|
+
when 'gcm'
|
96
|
+
iv = self.content.slice!(0, 8)
|
97
|
+
real_iv = force_binary(opt[:salt]) + iv
|
98
|
+
when 'cbc'
|
99
|
+
cipher.padding = 0
|
100
|
+
real_iv = iv = self.content.slice!(0, 16)
|
101
|
+
when 'ctr'
|
102
|
+
iv = self.content.slice!(0, 8)
|
103
|
+
real_iv = force_binary(opt[:salt]) + iv + [1].pack('N')
|
104
|
+
else
|
105
|
+
real_iv = iv = self.content.slice!(0, 16)
|
106
|
+
end
|
107
|
+
cipher.iv = real_iv
|
108
|
+
|
109
|
+
if authenticated?
|
110
|
+
if @icv_length == 0
|
111
|
+
@icv_length = opt[:icv_length].to_i if opt[:icv_length]
|
112
|
+
raise ParseError, 'unknown ICV size' if @icv_length == 0
|
113
|
+
end
|
114
|
+
icv = self.content.slice!(-@icv_length, @icv_length)
|
115
|
+
end
|
116
|
+
|
117
|
+
authenticate_if_needed opt, iv, icv
|
118
|
+
private_decrypt cipher, opt
|
119
|
+
end
|
120
|
+
|
121
|
+
# Encrypt in-place SK payload.
|
122
|
+
# @param [OpenSSL::Cipher] cipher keyed cipher
|
123
|
+
# This cipher is confidentiality-only one, or AEAD one. To use a second
|
124
|
+
# cipher to add integrity, use +:intmode+ option.
|
125
|
+
# @param [String] iv IV to encipher SK payload content
|
126
|
+
# * CTR and GCM modes: +iv+ is 8-bytes long.
|
127
|
+
# @param [Hash] options
|
128
|
+
# @option options [Fixnum] :icv_length ICV length for captured packets,
|
129
|
+
# or read from PCapNG files
|
130
|
+
# @option options [String] :salt salt value for CTR and GCM modes
|
131
|
+
# @option options [Fixnum] :pad_length set a padding length
|
132
|
+
# @option options [String] :padding set a padding. No check with
|
133
|
+
# +:pad_length+ is made. If +:pad_length+ is not set, +:padding+
|
134
|
+
# length is shortened to correct padding length
|
135
|
+
# @option options [OpenSSL::HMAC] :intmode integrity mode to use with a
|
136
|
+
# confidentiality-only cipher. Only HMAC are supported.
|
137
|
+
# @return [self]
|
138
|
+
def encrypt!(cipher, iv, options={})
|
139
|
+
opt = { salt: '' }.merge!(options)
|
140
|
+
|
141
|
+
set_crypto cipher, opt[:intmode]
|
142
|
+
|
143
|
+
real_iv = force_binary(opt[:salt]) + force_binary(iv)
|
144
|
+
real_iv += [1].pack('N') if confidentiality_mode == 'ctr'
|
145
|
+
cipher.iv = real_iv
|
146
|
+
|
147
|
+
authenticate_if_needed options, iv
|
148
|
+
|
149
|
+
if opt[:pad_length]
|
150
|
+
pad_length = opt[:pad_length]
|
151
|
+
padding = force_binary(opt[:padding] || ([0] * pad_length).pack('C*'))
|
152
|
+
else
|
153
|
+
pad_length = cipher.block_size
|
154
|
+
pad_length = 16 if cipher.block_size == 1 # Some AES mode returns 1...
|
155
|
+
pad_length -= (self.body.sz + iv.size + 1) % cipher.block_size
|
156
|
+
pad_length = 0 if pad_length == 16
|
157
|
+
padding = force_binary(opt[:padding] || ([0] * pad_length).pack('C*'))
|
158
|
+
padding = padding[0, pad_length]
|
159
|
+
end
|
160
|
+
msg = self.body.to_s + padding + Types::Int8.new(pad_length).to_s
|
161
|
+
encrypted_msg = encipher(msg)
|
162
|
+
cipher.final # message is already padded. No need for mode padding
|
163
|
+
|
164
|
+
if authenticated?
|
165
|
+
@icv_length = opt[:icv_length] if opt[:icv_length]
|
166
|
+
if @conf.authenticated?
|
167
|
+
encrypted_msg << @conf.auth_tag[0, @icv_length]
|
168
|
+
else
|
169
|
+
encrypted_msg << @intg.digest[0, @icv_length]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
self[:content].read (iv + encrypted_msg)
|
173
|
+
|
174
|
+
# Remove plain payloads
|
175
|
+
self[:body] = Types::String.new
|
176
|
+
|
177
|
+
# Remove enciphered payloads from packet
|
178
|
+
id = header_id(self)
|
179
|
+
if id < packet.headers.size - 1
|
180
|
+
(packet.headers.size-1).downto(id+1) do |index|
|
181
|
+
packet.headers.delete_at index
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
self.calc_length
|
186
|
+
self
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
|
191
|
+
def authenticate_if_needed(options, iv, icv=nil)
|
192
|
+
if @conf.authenticated?
|
193
|
+
@conf.auth_tag = icv if icv
|
194
|
+
@conf.auth_data = get_ad
|
195
|
+
elsif @intg
|
196
|
+
@intg.reset
|
197
|
+
@intg.update get_ad
|
198
|
+
@intg.update iv
|
199
|
+
@icv = icv
|
200
|
+
else
|
201
|
+
@icv = nil
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# From RFC 7206, §5.1: The associated data MUST consist of the partial
|
206
|
+
# contents of the IKEv2 message, starting from the first octet of the
|
207
|
+
# Fixed IKE Header through the last octet of the Payload Header of the
|
208
|
+
# Encrypted Payload (i.e., the fourth octet of the Encrypted Payload).
|
209
|
+
def get_ad
|
210
|
+
str = packet.ike.to_s[0, IKE.new.sz]
|
211
|
+
current_payload = packet.ike.body
|
212
|
+
until current_payload.is_a? SK do
|
213
|
+
str << current_payload.to_s[0, current_payload.length]
|
214
|
+
current_payload = current_payload.body
|
215
|
+
end
|
216
|
+
str << self.to_s[0, SK.new.sz]
|
217
|
+
end
|
218
|
+
|
219
|
+
def private_decrypt(cipher, options)
|
220
|
+
# decrypt
|
221
|
+
plain_msg = decipher(content.to_s)
|
222
|
+
# Remove cipher text
|
223
|
+
self.content.read ''
|
224
|
+
|
225
|
+
# check authentication tag
|
226
|
+
if authenticated?
|
227
|
+
return false unless authenticate!
|
228
|
+
end
|
229
|
+
|
230
|
+
# remove padding
|
231
|
+
pad_len = Types::Int8.new.read(plain_msg[-1]).to_i
|
232
|
+
payloads = plain_msg[0, plain_msg.size - 1 - pad_len]
|
233
|
+
|
234
|
+
# parse IKE payloads
|
235
|
+
if options[:parse]
|
236
|
+
klass = IKE.constants.select do |c|
|
237
|
+
cst = IKE.const_get(c)
|
238
|
+
cst.is_a?(Class) && (cst < Payload) && (cst::PAYLOAD_TYPE == self.next)
|
239
|
+
end
|
240
|
+
klass = klass.nil? ? Payload : IKE.const_get(klass.first)
|
241
|
+
firsth = klass.protocol_name
|
242
|
+
pkt = Packet.parse(payloads, first_header: firsth)
|
243
|
+
packet.encapsulate(pkt, parsing: true) unless pkt.nil?
|
244
|
+
else
|
245
|
+
self[:body].read payloads
|
246
|
+
end
|
247
|
+
|
248
|
+
true
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
self.add_class IKE::SK
|
254
|
+
end
|
255
|
+
end
|
@@ -0,0 +1,287 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'net-proto'
|
3
|
+
|
4
|
+
module PacketGen
|
5
|
+
module Header
|
6
|
+
class IKE
|
7
|
+
|
8
|
+
# TrafficSelector substructure, as defined in RFC 7296, §3.13.1:
|
9
|
+
# 1 2 3
|
10
|
+
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
11
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
12
|
+
# | TS Type |IP Protocol ID*| Selector Length |
|
13
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
14
|
+
# | Start Port* | End Port* |
|
15
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
16
|
+
# | |
|
17
|
+
# ~ Starting Address* ~
|
18
|
+
# | |
|
19
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
20
|
+
# | |
|
21
|
+
# ~ Ending Address* ~
|
22
|
+
# | |
|
23
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
24
|
+
# @author Sylvain Daubert
|
25
|
+
class TrafficSelector < Types::Fields
|
26
|
+
# IPv4 traffic selector type
|
27
|
+
TS_IPV4_ADDR_RANGE = 7
|
28
|
+
# IPv6 traffic selector type
|
29
|
+
TS_IPV6_ADDR_RANGE = 8
|
30
|
+
|
31
|
+
# @!attribute [r] type
|
32
|
+
# 8-bit TS type
|
33
|
+
# @return [Integer]
|
34
|
+
define_field :type, Types::Int8, default: 7
|
35
|
+
# @!attribute [r] protocol
|
36
|
+
# 8-bit protocol ID
|
37
|
+
# @return [Integer]
|
38
|
+
define_field :protocol, Types::Int8, default: 0
|
39
|
+
# @!attribute length
|
40
|
+
# 16-bit Selector Length
|
41
|
+
# @return [Integer]
|
42
|
+
define_field :length, Types::Int16
|
43
|
+
# @!attribute start_port
|
44
|
+
# 16-bit Start port
|
45
|
+
# @return [Integer]
|
46
|
+
define_field :start_port, Types::Int16, default: 0
|
47
|
+
# @!attribute end_port
|
48
|
+
# 16-bit End port
|
49
|
+
# @return [Integer]
|
50
|
+
define_field :end_port, Types::Int16, default: 65535
|
51
|
+
# @!attribute start_addr
|
52
|
+
# starting address
|
53
|
+
# @return [IP::Addr, IPv6::Addr]
|
54
|
+
define_field :start_addr, IP::Addr
|
55
|
+
# @!attribute end_addr
|
56
|
+
# starting address
|
57
|
+
# @return [IP::Addr, IPv6::Addr]
|
58
|
+
define_field :end_addr, IP::Addr
|
59
|
+
|
60
|
+
# @param [Hash] options
|
61
|
+
# @option [Range] :ports port range
|
62
|
+
# @option [Integer] :start_port start port
|
63
|
+
# @option [Integer] :end_port end port
|
64
|
+
def initialize(options={})
|
65
|
+
super
|
66
|
+
select_addr options
|
67
|
+
self[:start_addr].from_human(options[:start_addr]) if options[:start_addr]
|
68
|
+
self[:end_addr].from_human(options[:end_addr]) if options[:end_addr]
|
69
|
+
self[:length].value = sz unless options[:length]
|
70
|
+
self.type = options[:type] if options[:type]
|
71
|
+
self.protocol = options[:protocol] if options[:protocol]
|
72
|
+
if options[:ports]
|
73
|
+
self.start_port = options[:ports].begin
|
74
|
+
self.end_port = options[:ports].end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Populate object from a string
|
79
|
+
# @param [String] str
|
80
|
+
# @return [self]
|
81
|
+
def read(str)
|
82
|
+
super
|
83
|
+
select_addr_from_type type
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
# Set type
|
88
|
+
# @param [Integer,String] value
|
89
|
+
# @return [Integer]
|
90
|
+
def type=(value)
|
91
|
+
type = case value
|
92
|
+
when Integer
|
93
|
+
value
|
94
|
+
else
|
95
|
+
c = self.class.constants.grep(/TS_#{value.upcase}/).first
|
96
|
+
c ? self.class.const_get(c) : nil
|
97
|
+
end
|
98
|
+
raise ArgumentError, "unknown type #{value.inspect}" unless type
|
99
|
+
select_addr_from_type type
|
100
|
+
self[:type].value = type
|
101
|
+
end
|
102
|
+
|
103
|
+
# Set protocol
|
104
|
+
# @param [Integer,String] value
|
105
|
+
# @return [Integer]
|
106
|
+
def protocol=(value)
|
107
|
+
protocol = case value
|
108
|
+
when Integer
|
109
|
+
value
|
110
|
+
else
|
111
|
+
Net::Proto.getprotobyname(value)
|
112
|
+
end
|
113
|
+
raise ArgumentError, "unknown protocol #{value.inspect}" unless protocol
|
114
|
+
self[:protocol].value = protocol
|
115
|
+
end
|
116
|
+
|
117
|
+
# Get a human readable string
|
118
|
+
# @return [String]
|
119
|
+
def to_human
|
120
|
+
h = start_addr << '-' << end_addr
|
121
|
+
unless human_protocol.empty?
|
122
|
+
h << "/#{human_protocol}"
|
123
|
+
h << "[#{start_port}-#{end_port}]" if (start_port..end_port) != (0..65535)
|
124
|
+
end
|
125
|
+
h
|
126
|
+
end
|
127
|
+
|
128
|
+
# Get human readable protocol name. If protocol ID is 0, an empty string
|
129
|
+
# is returned.
|
130
|
+
# @return [String]
|
131
|
+
def human_protocol
|
132
|
+
if protocol == 0
|
133
|
+
''
|
134
|
+
else
|
135
|
+
Net::Proto.getprotobynumber(protocol) || "#{protocol}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Get human readable TS type
|
140
|
+
# @return [String]
|
141
|
+
def human_type
|
142
|
+
case type
|
143
|
+
when TS_IPV4_ADDR_RANGE
|
144
|
+
'IPv4'
|
145
|
+
when TS_IPV6_ADDR_RANGE
|
146
|
+
'IPv6'
|
147
|
+
else
|
148
|
+
"type #{type}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def select_addr_from_type(type)
|
155
|
+
case type
|
156
|
+
when TS_IPV4_ADDR_RANGE, 'IPV4', 'IPv4', 'ipv4', nil
|
157
|
+
self[:start_addr] = IP::Addr.new unless self[:start_addr].is_a?(IP::Addr)
|
158
|
+
self[:end_addr] = IP::Addr.new unless self[:end_addr].is_a?(IP::Addr)
|
159
|
+
when TS_IPV6_ADDR_RANGE, 'IPV6', 'IPv6', 'ipv6'
|
160
|
+
self[:start_addr] = IPv6::Addr.new unless self[:start_addr].is_a?(IPv6::Addr)
|
161
|
+
self[:end_addr] = IPv6::Addr.new unless self[:end_addr].is_a?(IPv6::Addr)
|
162
|
+
else
|
163
|
+
raise ArgumentError, "unknown type #{type}"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def select_addr(options)
|
168
|
+
if options[:type]
|
169
|
+
select_addr_from_type options[:type]
|
170
|
+
elsif options[:start_addr]
|
171
|
+
ipv4 = IPAddr.new(options[:start_addr]).ipv4?
|
172
|
+
self.type = ipv4 ? TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE
|
173
|
+
elsif options[:end_addr]
|
174
|
+
ipv4 = IPAddr.new(options[:end_addr]).ipv4?
|
175
|
+
self.type = ipv4 ? TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Set of {TrafficSelector}, used by {TSi} and {TSr}.
|
181
|
+
# @author Sylvain Daubert
|
182
|
+
class TrafficSelectors < Types::Array
|
183
|
+
set_of TrafficSelector
|
184
|
+
end
|
185
|
+
|
186
|
+
# This class handles Traffic Selector - Initiator payloads, denoted TSi.
|
187
|
+
#
|
188
|
+
# A TSi payload consists of the IKE generic payload header (see {Payload})
|
189
|
+
# and some specific fields:
|
190
|
+
# 1 2 3
|
191
|
+
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
192
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
193
|
+
# | Next Payload |C| RESERVED | Payload Length |
|
194
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
195
|
+
# | Number of TSs | RESERVED |
|
196
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
197
|
+
# | |
|
198
|
+
# ~ <Traffic Selectors> ~
|
199
|
+
# | |
|
200
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
201
|
+
# These specific fields are:
|
202
|
+
# * {#num_ts},
|
203
|
+
# * {#reserved},
|
204
|
+
# * and {#traffic_selectors}.
|
205
|
+
#
|
206
|
+
# == Create a TSi payload
|
207
|
+
# # Create a IKE packet with a TSi payload
|
208
|
+
# pkt = PacketGen.gen('IP').add('UDP').add('IKE').add('IKE::TSi')
|
209
|
+
# # add a traffic selector to this payload
|
210
|
+
# pkt.ike_tsi.traffic_selectors << { protocol: 'tcp', ports: 1..1024, start_addr: '20.0.0.1', end_addr: '21.255.255.254' }
|
211
|
+
# # add another traffic selector (IPv6, all protocols)
|
212
|
+
# pkt.ike_tsi.traffic_selectors << { start_addr: '2001::1', end_addr: '200a:ffff:ffff:ffff:ffff:ffff:ffff:ffff' }
|
213
|
+
# @author Sylvain Daubert
|
214
|
+
class TSi < Payload
|
215
|
+
|
216
|
+
# Payload type number
|
217
|
+
PAYLOAD_TYPE = 44
|
218
|
+
|
219
|
+
delete_field :content
|
220
|
+
# @!attribute num_ts
|
221
|
+
# 8-bit Number of TSs
|
222
|
+
# @return [Integer]
|
223
|
+
define_field_before :body, :num_ts, Types::Int8
|
224
|
+
# @!attribute rsv1
|
225
|
+
# First 8-bit RESERVED field
|
226
|
+
# @return [Integer]
|
227
|
+
define_field_before :body, :rsv1, Types::Int8
|
228
|
+
# @!attribute rsv1
|
229
|
+
# Last 16-bit RESERVED field
|
230
|
+
# @return [Integer]
|
231
|
+
define_field_before :body, :rsv2, Types::Int16
|
232
|
+
|
233
|
+
# @!attribute traffic_selectors
|
234
|
+
# Set of {TrafficSelector}
|
235
|
+
# @return {TrafficSelectors}
|
236
|
+
define_field_before :body, :traffic_selectors, TrafficSelectors,
|
237
|
+
builder: ->(ts) { TrafficSelectors.new(counter: ts[:num_ts]) }
|
238
|
+
alias :selectors :traffic_selectors
|
239
|
+
|
240
|
+
# Populate object from a string
|
241
|
+
# @param [String] str
|
242
|
+
# @return [self]
|
243
|
+
def read(str)
|
244
|
+
super(str[0, 8])
|
245
|
+
hlen = self.class.new.sz
|
246
|
+
tslen = length - hlen
|
247
|
+
selectors.read str[hlen, tslen]
|
248
|
+
body.read str[hlen+tslen..-1]
|
249
|
+
self
|
250
|
+
end
|
251
|
+
|
252
|
+
# Compute length and set {#length} field
|
253
|
+
# @return [Integer] new length
|
254
|
+
def calc_length
|
255
|
+
selectors.each { |p| p.calc_length }
|
256
|
+
super
|
257
|
+
end
|
258
|
+
|
259
|
+
# @return [String]
|
260
|
+
def inspect
|
261
|
+
str = Inspect.dashed_line(self.class, 2)
|
262
|
+
fields.each do |attr|
|
263
|
+
case attr
|
264
|
+
when :body, :rsv2
|
265
|
+
next
|
266
|
+
when :rsv1
|
267
|
+
str << Inspect.shift_level(2)
|
268
|
+
str << Inspect::FMT_ATTR % ['Int24', 'reserved',
|
269
|
+
(rsv1 << 16) | rsv2 ]
|
270
|
+
else
|
271
|
+
str << Inspect.inspect_attribute(attr, self[attr], 2)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
str
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
class TSr < TSi
|
279
|
+
# Payload type number
|
280
|
+
PAYLOAD_TYPE = 45
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
self.add_class IKE::TSi
|
285
|
+
self.add_class IKE::TSr
|
286
|
+
end
|
287
|
+
end
|