packetgen 2.3.0 → 2.4.0

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