packetgen 2.3.0 → 2.4.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 +1 -0
- data/README.md +5 -1
- data/lib/packetgen/header.rb +4 -0
- data/lib/packetgen/header/icmpv6.rb +0 -1
- data/lib/packetgen/header/igmp.rb +126 -0
- data/lib/packetgen/header/igmpv3.rb +150 -0
- data/lib/packetgen/header/igmpv3/group_record.rb +98 -0
- data/lib/packetgen/header/igmpv3/mq.rb +96 -0
- data/lib/packetgen/header/igmpv3/mr.rb +65 -0
- data/lib/packetgen/header/ip.rb +40 -70
- data/lib/packetgen/header/ip/addr.rb +58 -0
- data/lib/packetgen/header/ip/option.rb +194 -0
- data/lib/packetgen/header/ip/options.rb +53 -0
- data/lib/packetgen/header/ipv6.rb +24 -69
- data/lib/packetgen/header/ipv6/addr.rb +96 -0
- data/lib/packetgen/header/ipv6/extension.rb +65 -0
- data/lib/packetgen/header/ipv6/hop_by_hop.rb +132 -0
- data/lib/packetgen/header/mld.rb +100 -0
- data/lib/packetgen/header/mldv2.rb +50 -0
- data/lib/packetgen/header/mldv2/mcast_address_record.rb +103 -0
- data/lib/packetgen/header/mldv2/mlq.rb +144 -0
- data/lib/packetgen/header/mldv2/mlr.rb +65 -0
- data/lib/packetgen/packet.rb +18 -0
- data/lib/packetgen/types/array.rb +1 -0
- data/lib/packetgen/types/string.rb +14 -13
- data/lib/packetgen/version.rb +1 -1
- metadata +18 -2
@@ -0,0 +1,65 @@
|
|
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_relative 'group_record'
|
6
|
+
|
7
|
+
module PacketGen
|
8
|
+
module Header
|
9
|
+
class IGMPv3
|
10
|
+
# IGMPv3 Membership Report.
|
11
|
+
#
|
12
|
+
# This is a subpayload for IGMPv3 packets only. This kind of payload is
|
13
|
+
# sent by IP systems to report (to neighboring routers) the current multicast
|
14
|
+
# reception state, or changes in the multicast reception state, of their
|
15
|
+
# interfaces. Reports have the following format:
|
16
|
+
# 0 1 2 3
|
17
|
+
# 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
|
18
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
19
|
+
# | Reserved | Number of Group Records (M) |
|
20
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
21
|
+
# | |
|
22
|
+
# . .
|
23
|
+
# . Group Record [1] .
|
24
|
+
# . .
|
25
|
+
# | |
|
26
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
27
|
+
# | |
|
28
|
+
# . .
|
29
|
+
# . Group Record [2] .
|
30
|
+
# . .
|
31
|
+
# | |
|
32
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
33
|
+
# | . |
|
34
|
+
# . . .
|
35
|
+
# | . |
|
36
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
37
|
+
# | |
|
38
|
+
# . .
|
39
|
+
# . Group Record [M] .
|
40
|
+
# . .
|
41
|
+
# | |
|
42
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
43
|
+
# @author Sylvain Daubert
|
44
|
+
class MR < Base
|
45
|
+
# @!attribute reserved
|
46
|
+
# 16-bit reserved field
|
47
|
+
# @return [Integer]
|
48
|
+
define_field :reserved, Types::Int16, default: 0
|
49
|
+
# @!attribute number_of_gr
|
50
|
+
# 16-bit Number of group records in {#group_records}
|
51
|
+
# @return [Integer]
|
52
|
+
define_field :number_of_gr, Types::Int16, default: 0
|
53
|
+
|
54
|
+
# @!attribute group_records
|
55
|
+
# Array of group records
|
56
|
+
# @return [GroupRecords]
|
57
|
+
define_field :group_records, GroupRecords,
|
58
|
+
builder: ->(h, t) { t.new(counter: h[:number_of_gr]) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
self.add_class IGMPv3::MR
|
63
|
+
IGMPv3.bind_header IGMPv3::MR, type: 0x22
|
64
|
+
end
|
65
|
+
end
|
data/lib/packetgen/header/ip.rb
CHANGED
@@ -60,54 +60,13 @@ module PacketGen
|
|
60
60
|
# ip.dst = '127.0.0.2'
|
61
61
|
# ip.body.read 'this is a body'
|
62
62
|
# @author Sylvain Daubert
|
63
|
-
class IP < Base
|
63
|
+
class IP < Base;end
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
# @!attribute a1
|
69
|
-
# @return [Integer] IP address first byte
|
70
|
-
define_field :a1, Types::Int8
|
71
|
-
# @!attribute a2
|
72
|
-
# @return [Integer] IP address seconf byte
|
73
|
-
define_field :a2, Types::Int8
|
74
|
-
# @!attribute a3
|
75
|
-
# @return [Integer] IP address third byte
|
76
|
-
define_field :a3, Types::Int8
|
77
|
-
# @!attribute a4
|
78
|
-
# @return [Integer] IP address fourth byte
|
79
|
-
define_field :a4, Types::Int8
|
65
|
+
require_relative 'ip/addr'
|
66
|
+
require_relative 'ip/option'
|
67
|
+
require_relative 'ip/options'
|
80
68
|
|
81
|
-
|
82
|
-
|
83
|
-
# Read a dotted address
|
84
|
-
# @param [String] str
|
85
|
-
# @return [self]
|
86
|
-
def from_human(str)
|
87
|
-
return self if str.nil?
|
88
|
-
m = str.match(IPV4_ADDR_REGEX)
|
89
|
-
if m
|
90
|
-
self[:a1].read m[1].to_i
|
91
|
-
self[:a2].read m[2].to_i
|
92
|
-
self[:a3].read m[3].to_i
|
93
|
-
self[:a4].read m[4].to_i
|
94
|
-
end
|
95
|
-
self
|
96
|
-
end
|
97
|
-
|
98
|
-
# Addr in human readable form (dotted format)
|
99
|
-
# @return [String]
|
100
|
-
def to_human
|
101
|
-
fields.map { |f| "#{self[f].to_i}" }.join('.')
|
102
|
-
end
|
103
|
-
|
104
|
-
# Addr as an integer
|
105
|
-
# @return [Integer]
|
106
|
-
def to_i
|
107
|
-
(self.a1 << 24) | (self.a2 << 16) | (self.a3 << 8) |
|
108
|
-
self.a4
|
109
|
-
end
|
110
|
-
end
|
69
|
+
class IP
|
111
70
|
|
112
71
|
# IP Ether type
|
113
72
|
ETHERTYPE = 0x0800
|
@@ -146,8 +105,7 @@ module PacketGen
|
|
146
105
|
# @!attribute options
|
147
106
|
# @since 2.2.0
|
148
107
|
# @return [Types::String]
|
149
|
-
define_field :options,
|
150
|
-
builder: ->(h,t) { t.new(length_from: ->() { (h.ihl - 5) * 4 }) }
|
108
|
+
define_field :options, Options, optional: ->(h) { h.ihl > 5 }
|
151
109
|
# @!attribute body
|
152
110
|
# @return [Types::String,Header::Base]
|
153
111
|
define_field :body, Types::String
|
@@ -171,25 +129,27 @@ module PacketGen
|
|
171
129
|
# Populate object from a binary string
|
172
130
|
# @param [String] str
|
173
131
|
# @return [Fields] self
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
132
|
+
def read(str)
|
133
|
+
return self if str.nil?
|
134
|
+
force_binary str
|
135
|
+
self[:u8].read str[0, 1]
|
136
|
+
self[:tos].read str[1, 1]
|
137
|
+
self[:length].read str[2, 2]
|
138
|
+
self[:id].read str[4, 2]
|
139
|
+
self[:frag].read str[6, 2]
|
140
|
+
self[:ttl].read str[8, 1]
|
141
|
+
self[:protocol].read str[9, 1]
|
142
|
+
self[:checksum].read str[10, 2]
|
143
|
+
self[:src].read str[12, 4]
|
144
|
+
self[:dst].read str[16, 4]
|
145
|
+
opt_size = 0
|
146
|
+
if self.ihl > 5
|
147
|
+
opt_size = (self.ihl - 5) * 4
|
148
|
+
self[:options].read str[20, opt_size]
|
149
|
+
end
|
150
|
+
self[:body].read str[20+opt_size..-1]
|
151
|
+
self
|
152
|
+
end
|
193
153
|
|
194
154
|
# Compute checksum and set +checksum+ field
|
195
155
|
# @return [Integer]
|
@@ -203,8 +163,11 @@ module PacketGen
|
|
203
163
|
checksum += (self[:src].to_i & 0xffff)
|
204
164
|
checksum += self[:dst].to_i >> 16
|
205
165
|
checksum += self[:dst].to_i & 0xffff
|
206
|
-
|
207
|
-
|
166
|
+
options.to_s.unpack('n*').each { |x| checksum += x }
|
167
|
+
while checksum > 0xffff do
|
168
|
+
checksum = (checksum & 0xffff) + (checksum >> 16)
|
169
|
+
end
|
170
|
+
checksum = ~checksum & 0xffff
|
208
171
|
self[:checksum].value = (checksum == 0) ? 0xffff : checksum
|
209
172
|
end
|
210
173
|
|
@@ -260,7 +223,14 @@ module PacketGen
|
|
260
223
|
# Check version field
|
261
224
|
# @see [Base#parse?]
|
262
225
|
def parse?
|
263
|
-
version == 4
|
226
|
+
version == 4 and ihl >= 5
|
227
|
+
end
|
228
|
+
|
229
|
+
# Get binary string. Fixup IHL if needed (IP header has options, and IHL
|
230
|
+
# was not set by user).
|
231
|
+
def to_s
|
232
|
+
self.ihl = 5 + options.sz / 4 if self.ihl == 5
|
233
|
+
super
|
264
234
|
end
|
265
235
|
end
|
266
236
|
|
@@ -0,0 +1,58 @@
|
|
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
|
+
|
6
|
+
module PacketGen
|
7
|
+
module Header
|
8
|
+
class IP
|
9
|
+
|
10
|
+
# IP address, as a group of 4 bytes
|
11
|
+
# @author Sylvain Daubert
|
12
|
+
class Addr < Types::Fields
|
13
|
+
# @!attribute a1
|
14
|
+
# @return [Integer] IP address first byte
|
15
|
+
define_field :a1, Types::Int8
|
16
|
+
# @!attribute a2
|
17
|
+
# @return [Integer] IP address seconf byte
|
18
|
+
define_field :a2, Types::Int8
|
19
|
+
# @!attribute a3
|
20
|
+
# @return [Integer] IP address third byte
|
21
|
+
define_field :a3, Types::Int8
|
22
|
+
# @!attribute a4
|
23
|
+
# @return [Integer] IP address fourth byte
|
24
|
+
define_field :a4, Types::Int8
|
25
|
+
|
26
|
+
IPV4_ADDR_REGEX = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/
|
27
|
+
|
28
|
+
# Read a dotted address
|
29
|
+
# @param [String] str
|
30
|
+
# @return [self]
|
31
|
+
def from_human(str)
|
32
|
+
return self if str.nil?
|
33
|
+
m = str.match(IPV4_ADDR_REGEX)
|
34
|
+
if m
|
35
|
+
self[:a1].read m[1].to_i
|
36
|
+
self[:a2].read m[2].to_i
|
37
|
+
self[:a3].read m[3].to_i
|
38
|
+
self[:a4].read m[4].to_i
|
39
|
+
end
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Addr in human readable form (dotted format)
|
44
|
+
# @return [String]
|
45
|
+
def to_human
|
46
|
+
fields.map { |f| "#{self[f].to_i}" }.join('.')
|
47
|
+
end
|
48
|
+
|
49
|
+
# Addr as an integer
|
50
|
+
# @return [Integer]
|
51
|
+
def to_i
|
52
|
+
(self.a1 << 24) | (self.a2 << 16) | (self.a3 << 8) |
|
53
|
+
self.a4
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,194 @@
|
|
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
|
+
|
6
|
+
module PacketGen
|
7
|
+
module Header
|
8
|
+
class IP
|
9
|
+
|
10
|
+
# Class to handle series of IP addresses
|
11
|
+
# @author Sylvain Daubert
|
12
|
+
class ArrayOfAddr < Types::Array
|
13
|
+
set_of IP::Addr
|
14
|
+
|
15
|
+
# Push a IP address to the array
|
16
|
+
# @param [String,Addr] addr
|
17
|
+
# @return [self]
|
18
|
+
# array << '192.168.1.12'
|
19
|
+
def push(addr)
|
20
|
+
addr = addr.is_a?(Addr) ? addr : Addr.new.from_human(addr)
|
21
|
+
super(addr)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Base class for IP options
|
26
|
+
# @author Sylvain Daubert
|
27
|
+
class Option < Types::Fields
|
28
|
+
|
29
|
+
# EOL option type
|
30
|
+
EOL_TYPE = 0x00
|
31
|
+
# NOP option type
|
32
|
+
NOP_TYPE = 0x01
|
33
|
+
# LSRR option type
|
34
|
+
LSRR_TYPE = 0x83
|
35
|
+
# SSRR option type
|
36
|
+
SSRR_TYPE = 0x84
|
37
|
+
# RR option type
|
38
|
+
RR_TYPE = 0x07
|
39
|
+
# SI option type
|
40
|
+
SI_TYPE = 0x88
|
41
|
+
# RA option type
|
42
|
+
RA_TYPE = 0x94
|
43
|
+
|
44
|
+
# @!attribute type
|
45
|
+
# 8-bit option type
|
46
|
+
# @return [Integer]
|
47
|
+
define_field :type, Types::Int8
|
48
|
+
# @!attribute length
|
49
|
+
# 8-bit option length. If 0, there is no +length+ field in option
|
50
|
+
# @return [Integer]
|
51
|
+
define_field :length, Types::Int8, default: 0, optional: ->(h) { h.type > 1 }
|
52
|
+
# @!attribute data
|
53
|
+
# option data
|
54
|
+
# @return [String]
|
55
|
+
define_field :data, Types::String, optional: ->(h) { h.length > 2 },
|
56
|
+
builder: ->(h,t) { t.new(length_from: ->() { h.length - 2 }) }
|
57
|
+
|
58
|
+
# @!attribute copied
|
59
|
+
# 1-bit copied flag from {#type} field
|
60
|
+
# @return [Boolean]
|
61
|
+
# @!attribute option_class
|
62
|
+
# 2-bit option class (0: control, 2: debug and measurement, 1 and 3:
|
63
|
+
# reserved) from {#type} field
|
64
|
+
# @return [Integer]
|
65
|
+
# !@attribute number
|
66
|
+
# 5-bit option number from {#type} field
|
67
|
+
# @return [Integer]
|
68
|
+
define_bit_fields_on :type, :copied, :option_class, 2, :number, 5
|
69
|
+
|
70
|
+
# @return [Hash]
|
71
|
+
def self.types
|
72
|
+
return @types if defined? @types
|
73
|
+
@types = {}
|
74
|
+
Option.constants.each do |cst|
|
75
|
+
next unless cst.to_s.end_with? '_TYPE'
|
76
|
+
optname = cst.to_s.sub(/_TYPE/, '')
|
77
|
+
@types[Option.const_get(cst)] = IP.const_get(optname)
|
78
|
+
end
|
79
|
+
@types
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize(options={})
|
83
|
+
unless options[:type]
|
84
|
+
opt_name = self.class.to_s.gsub(/.*::/, '')
|
85
|
+
if Option.const_defined? "#{opt_name}_TYPE"
|
86
|
+
options[:type] = Option.const_get("#{opt_name}_TYPE")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
super
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get binary string. Set {#length} field.
|
93
|
+
# @return [String]
|
94
|
+
def to_s
|
95
|
+
if respond_to? :length
|
96
|
+
self.length = super.size
|
97
|
+
end
|
98
|
+
super
|
99
|
+
end
|
100
|
+
|
101
|
+
# Get a human readable string
|
102
|
+
# @return [String]
|
103
|
+
def to_human
|
104
|
+
str = self.class == Option ? "unk-#{type}" : self.class.to_s.sub(/.*::/, '')
|
105
|
+
if respond_to?(:length) and length > 2 and self[:data].to_s.size > 0
|
106
|
+
str << ":#{self[:data].to_s.inspect}"
|
107
|
+
end
|
108
|
+
str
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# End-of-option-List IP option
|
113
|
+
class EOL < Option
|
114
|
+
delete_field :length
|
115
|
+
undef length
|
116
|
+
delete_field :data
|
117
|
+
undef data
|
118
|
+
end
|
119
|
+
|
120
|
+
# No OPeration IP option
|
121
|
+
class NOP < EOL
|
122
|
+
end
|
123
|
+
|
124
|
+
# Loose Source and Record Route IP option
|
125
|
+
class LSRR < Option
|
126
|
+
delete_field :data
|
127
|
+
undef data
|
128
|
+
|
129
|
+
# @!attribute pointer
|
130
|
+
# 8-bit pointer on next address
|
131
|
+
# @return [Integer]
|
132
|
+
define_field :pointer, Types::Int8
|
133
|
+
# @!attribute data
|
134
|
+
# Array of IP addresses
|
135
|
+
# @return [Types::Array<IP::Addr>]
|
136
|
+
define_field :data, ArrayOfAddr,
|
137
|
+
builder: ->(h,t) { t.new(length_from: ->() { h.length - 2 }) }
|
138
|
+
|
139
|
+
# Populate object from a binary string
|
140
|
+
# @param [String] str
|
141
|
+
# @return [Fields] self
|
142
|
+
def read(str)
|
143
|
+
return self if str.nil?
|
144
|
+
force_binary str
|
145
|
+
self[:type].read str[0, 1]
|
146
|
+
self[:length].read str[1, 1]
|
147
|
+
self[:pointer].read str[2, 1]
|
148
|
+
self[:data].read str[3, length - 3]
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
# Get IP address pointer by {#pointer}
|
153
|
+
# @return [Addr]
|
154
|
+
def pointed_addr
|
155
|
+
data[pointer / 4 - 1]
|
156
|
+
end
|
157
|
+
|
158
|
+
# Get a human readable string
|
159
|
+
# @return [String]
|
160
|
+
def to_human
|
161
|
+
str = self.class.to_s.sub(/.*::/, '')
|
162
|
+
str << ':' << self[:data].to_human
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Strict Source and Record Route IP option
|
167
|
+
class SSRR < LSRR; end
|
168
|
+
# Record Route IP option
|
169
|
+
class RR < LSRR; end
|
170
|
+
|
171
|
+
# Stream Identifier IP option
|
172
|
+
class SI < Option
|
173
|
+
delete_field :data
|
174
|
+
undef data
|
175
|
+
|
176
|
+
# @!attribute id
|
177
|
+
# 16-bit stream ID
|
178
|
+
# @return [Integer]
|
179
|
+
define_field :id, Types::Int16
|
180
|
+
end
|
181
|
+
|
182
|
+
# Router Alert IP option
|
183
|
+
class RA < Option
|
184
|
+
delete_field :data
|
185
|
+
undef data
|
186
|
+
|
187
|
+
# @!attribute value
|
188
|
+
# 16-bit value. Should be 0.
|
189
|
+
# @return [Integer]
|
190
|
+
define_field :value, Types::Int16, default: 0
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|