packetgen 2.3.0 → 2.4.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 +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
|