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.
@@ -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
@@ -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
- # IP address, as a group of 4 bytes
66
- # @author Sylvain Daubert
67
- class Addr < Types::Fields
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
- IPV4_ADDR_REGEX = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/
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, Types::String, optional: ->(h) { h.ihl > 5 },
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
- #def read(str)
175
- # return self if str.nil?
176
- # force_binary str
177
- # self[:u8].read str[0, 1]
178
- # self[:tos].read str[1, 1]
179
- # self[:length].read str[2, 2]
180
- # self[:id].read str[4, 2]
181
- # self[:frag].read str[6, 2]
182
- # self[:ttl].read str[8, 1]
183
- # self[:protocol].read str[9, 1]
184
- # self[:checksum].read str[10, 2]
185
- # self[:src].read str[12, 4]
186
- # self[:dst].read str[16, 4]
187
- # if self.ihl > 5
188
- # opt_size = (self.ihl - 5) * 4
189
- # self[:options].read str[20, opt_size]
190
- # end
191
- # self
192
- #end
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
- checksum = (checksum & 0xffff) + (checksum >> 16)
207
- checksum = ~(checksum % 0xffff ) & 0xffff
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