packetgen 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,40 @@
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
+
1
6
  module PacketGen
2
7
  module Header
3
8
 
4
- # ICMPv6 header class
9
+ # A ICMPv6 header consists of:
10
+ # * a +type+ field ({Int8} type),
11
+ # * a +code+ field ({Int8} type),
12
+ # * a +checksum+ field ({Int16} type),
13
+ # * and a +body+.
14
+ #
15
+ # == Create a ICMPv6 header
16
+ # # standalone
17
+ # icmpv6 = PacketGen::Header::ICMPv6.new
18
+ # # in a packet
19
+ # pkt = PacketGen.gen('IPv6').add('ICMPv6')
20
+ # # access to ICMPv6 header
21
+ # pkt.icmpv6 # => PacketGen::Header::ICMPv6
22
+ #
23
+ # == ICMPv6 attributes
24
+ # icmpv6.code = 0
25
+ # icmpv6.type = 200
26
+ # icmpv6.checksum = 0x248a
27
+ # icmpv6.body.read 'this is a body'
5
28
  # @author Sylvain Daubert
6
29
  class ICMPv6 < ICMP
7
30
 
8
31
  # ICMPv6 internet protocol number
9
32
  IP_PROTOCOL = 58
10
33
 
11
- # Compute checksum and set +sum+ field
34
+ # Compute checksum and set +checksum+ field
12
35
  # @return [Integer]
13
- def calc_sum
14
- sum = ip_header(self).pseudo_header_sum
36
+ def calc_checksum
37
+ sum = ip_header(self).pseudo_header_checksum
15
38
  sum += self.sz
16
39
  sum += IP_PROTOCOL
17
40
  sum +=(type << 8) | code
@@ -24,7 +47,7 @@ module PacketGen
24
47
  sum = (sum & 0xffff) + (sum >> 16)
25
48
  end
26
49
  sum = ~sum & 0xffff
27
- self[:sum].value = (sum == 0) ? 0xffff : sum
50
+ self[:checksum].value = (sum == 0) ? 0xffff : sum
28
51
  end
29
52
  end
30
53
 
@@ -1,12 +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.
1
5
  require 'socket'
2
6
 
3
7
  module PacketGen
4
8
  module Header
5
9
 
6
- # IP header class
10
+ # A IP header consists of:
11
+ # * a first byte ({#u8} of {Int8} type) composed of:
12
+ # * a 4-bit {#version} field,
13
+ # * a 4-bit IP header length ({#ihl}) field,
14
+ # * a total length ({#length}, {Int16} type),
15
+ # * a ID ({#id}, +Int16+ type),
16
+ # * a {#frag} worg (+Int16+) composed of:
17
+ # * 3 1-bit flags ({#flag_rsv}, {#flag_df} and {#flag_mf}),
18
+ # * a 13-bit {#fragment_offset} field,
19
+ # * a Time-to-Live ({#ttl}) field (+Int8+),
20
+ # * a {#protocol} field (+Int8+),
21
+ # * a {#checksum} field (+Int16+),
22
+ # * a source IP address ({#src}, {Addr} type),
23
+ # * a destination IP ddress ({#dst}, +Addr+ type),
24
+ # * and a {#body} ({String} type).
25
+ #
26
+ # == Create a IP header
27
+ # # standalone
28
+ # ip = PacketGen::Header::IP.new
29
+ # # in a packet
30
+ # pkt = PacketGen.gen('IP')
31
+ # # access to IP header
32
+ # pkt.ip # => PacketGen::Header::IP
33
+ #
34
+ # == IP attributes
35
+ # ip.u8 = 0x45
36
+ # # the same as
37
+ # ip.version = 4
38
+ # ip.ihl = 5
39
+ #
40
+ # ip.length = 0x43
41
+ # ip.id = 0x1234
42
+ #
43
+ # ip.frag = 0x2031
44
+ # # the same as:
45
+ # ip.flag_mf = true
46
+ # ip.fragment_offset = 0x31
47
+ #
48
+ # ip.flag_rsv? # => Boolean
49
+ # ip.flag_df? # => Boolean
50
+ # ip.flag_mf? # => Boolean
51
+ #
52
+ # ip.ttl = 0x40
53
+ # ip.protocol = 6
54
+ # ip.checksum = 0xffff
55
+ # ip.src = '127.0.0.1'
56
+ # ip.src # => "127.0.0.1"
57
+ # ip[:src] # => PacketGen::Header::IP::Addr
58
+ # ip.dst = '127.0.0.2'
59
+ # ip.body.read 'this is a body'
7
60
  # @author Sylvain Daubert
8
- class IP < Struct.new(:version, :ihl, :tos, :length, :id, :frag, :ttl,
9
- :proto,:sum, :src, :dst, :body)
61
+ class IP < Struct.new(:u8, :tos, :length, :id, :frag, :ttl,
62
+ :protocol, :checksum, :src, :dst, :body)
10
63
  include StructFu
11
64
  include HeaderMethods
12
65
  extend HeaderClassMethods
@@ -31,10 +84,10 @@ module PacketGen
31
84
 
32
85
  end
33
86
 
34
- # Parse a dotted address
87
+ # Read a dotted address
35
88
  # @param [String] str
36
89
  # @return [self]
37
- def parse(str)
90
+ def from_human(str)
38
91
  return self if str.nil?
39
92
  m = str.match(IPV4_ADDR_REGEX)
40
93
  if m
@@ -51,7 +104,7 @@ module PacketGen
51
104
  # @return [self]
52
105
  def read(str)
53
106
  return self if str.nil?
54
- raise ParseError, 'string too short for Eth' if str.size < self.sz
107
+ raise ParseError, 'string too short for IP::Addr' if str.size < self.sz
55
108
  force_binary str
56
109
  [:a1, :a2, :a3, :a4].each_with_index do |byte, i|
57
110
  self[byte].read str[i, 1]
@@ -65,7 +118,7 @@ module PacketGen
65
118
 
66
119
  # Addr in human readable form (dotted format)
67
120
  # @return [String]
68
- def to_x
121
+ def to_human
69
122
  members.map { |m| "#{self[m].to_i}" }.join('.')
70
123
  end
71
124
 
@@ -85,63 +138,76 @@ module PacketGen
85
138
  # @option options [Integer] :id
86
139
  # @option options [Integer] :frag
87
140
  # @option options [Integer] :ttl
88
- # @option options [Integer] :proto
89
- # @option options [Integer] :sum IP header checksum
141
+ # @option options [Integer] :protocol
142
+ # @option options [Integer] :checksum IP header checksum
90
143
  # @option options [String] :src IP source dotted address
91
144
  # @option options [String] :dst IP destination dotted address
92
145
  def initialize(options={})
93
- super options[:version] || 4,
94
- options[:ihl] || 5,
146
+ super Int8.new(((options[:version] || 4) << 4) | (options[:ihl] || 5)),
95
147
  Int8.new(options[:tos] || 0),
96
148
  Int16.new(options[:length] || 20),
97
149
  Int16.new(options[:id] || rand(65535)),
98
150
  Int16.new(options[:frag] || 0),
99
151
  Int8.new(options[:ttl] || 64),
100
- Int8.new(options[:proto]),
101
- Int16.new(options[:sum] || 0),
102
- Addr.new.parse(options[:src] || '127.0.0.1'),
103
- Addr.new.parse(options[:dst] || '127.0.0.1'),
152
+ Int8.new(options[:protocol]),
153
+ Int16.new(options[:checksum] || 0),
154
+ Addr.new.from_human(options[:src] || '127.0.0.1'),
155
+ Addr.new.from_human(options[:dst] || '127.0.0.1'),
104
156
  StructFu::String.new.read(options[:body])
105
157
  end
106
158
 
159
+ # @!attribute version
160
+ # @return [Integer] 4-bit version attribute
161
+ # @!attribute ihl
162
+ # @return [Integer] 4-bit IP header length attribute
163
+ define_bit_fields_on :u8, :version, 4, :ihl, 4
164
+
165
+ # @!attribute flag_rsv
166
+ # @return [Boolean] reserved bit from flags
167
+ # @!attribute flag_df
168
+ # @return [Boolean] Don't Fragment flag
169
+ # @!attribute flag_mf
170
+ # @return [Boolena] More Fragment flags
171
+ # @!attribute fragment_offset
172
+ # @return [Integer] 13-bit fragment offset
173
+ define_bit_fields_on :frag, :flag_rsv, :flag_df, :flag_mf, :fragment_offset, 13
174
+
107
175
  # Read a IP header from a string
108
176
  # @param [String] str binary string
109
177
  # @return [self]
110
178
  def read(str)
111
179
  return self if str.nil?
112
- raise ParseError, 'string too short for Eth' if str.size < self.sz
180
+ raise ParseError, 'string too short for IP' if str.size < self.sz
113
181
  force_binary str
114
- vihl = str[0, 1].unpack('C').first
115
- self[:version] = vihl >> 4
116
- self[:ihl] = vihl & 0x0f
182
+ self[:u8].read str[0, 1]
117
183
  self[:tos].read str[1, 1]
118
184
  self[:length].read str[2, 2]
119
185
  self[:id].read str[4, 2]
120
186
  self[:frag].read str[6, 2]
121
187
  self[:ttl].read str[8, 1]
122
- self[:proto].read str[9, 1]
123
- self[:sum].read str[10, 2]
188
+ self[:protocol].read str[9, 1]
189
+ self[:checksum].read str[10, 2]
124
190
  self[:src].read str[12, 4]
125
191
  self[:dst].read str[16, 4]
126
192
  self[:body].read str[20..-1]
127
193
  self
128
194
  end
129
195
 
130
- # Compute checksum and set +sum+ field
196
+ # Compute checksum and set +checksum+ field
131
197
  # @return [Integer]
132
- def calc_sum
133
- checksum = (self.version << 12) | (self.ihl << 8) | self.tos
198
+ def calc_checksum
199
+ checksum = (self[:u8].to_i << 8) | self.tos
134
200
  checksum += self.length
135
201
  checksum += self.id
136
202
  checksum += self.frag
137
- checksum += (self.ttl << 8) | self.proto
203
+ checksum += (self.ttl << 8) | self.protocol
138
204
  checksum += (self[:src].to_i >> 16)
139
205
  checksum += (self[:src].to_i & 0xffff)
140
206
  checksum += self[:dst].to_i >> 16
141
207
  checksum += self[:dst].to_i & 0xffff
142
208
  checksum = (checksum & 0xffff) + (checksum >> 16)
143
209
  checksum = ~(checksum % 0xffff ) & 0xffff
144
- self[:sum].value = (checksum == 0) ? 0xffff : checksum
210
+ self[:checksum].value = (checksum == 0) ? 0xffff : checksum
145
211
  end
146
212
 
147
213
  # Compute length and set +length+ field
@@ -215,36 +281,36 @@ module PacketGen
215
281
  self[:ttl].value = ttl
216
282
  end
217
283
 
218
- # Getter for proto attribute
284
+ # Getter for protocol attribute
219
285
  # @return [Integer]
220
- def proto
221
- self[:proto].to_i
286
+ def protocol
287
+ self[:protocol].to_i
222
288
  end
223
289
 
224
- # Setter for proto attribute
225
- # @param [Integer] proto
290
+ # Setter for protocol attribute
291
+ # @param [Integer] protocol
226
292
  # @return [Integer]
227
- def proto=(proto)
228
- self[:proto].value = proto
293
+ def protocol=(protocol)
294
+ self[:protocol].value = protocol
229
295
  end
230
296
 
231
- # Getter for sum attribute
297
+ # Getter for checksum attribute
232
298
  # @return [Integer]
233
- def sum
234
- self[:sum].to_i
299
+ def checksum
300
+ self[:checksum].to_i
235
301
  end
236
302
 
237
- # Setter for sum attribute
238
- # @param [Integer] sum
303
+ # Setter for checksum attribute
304
+ # @param [Integer] checksum
239
305
  # @return [Integer]
240
- def sum=(sum)
241
- self[:sum].value = sum
306
+ def checksum=(checksum)
307
+ self[:checksum].value = checksum
242
308
  end
243
309
 
244
310
  # Get IP source address
245
311
  # @return [String] dotted address
246
312
  def src
247
- self[:src].to_x
313
+ self[:src].to_human
248
314
  end
249
315
  alias :source :src
250
316
 
@@ -252,14 +318,14 @@ module PacketGen
252
318
  # @param [String] addr dotted IP address
253
319
  # @return [String]
254
320
  def src=(addr)
255
- self[:src].parse addr
321
+ self[:src].from_human addr
256
322
  end
257
323
  alias :source= :src=
258
324
 
259
325
  # Get IP destination address
260
326
  # @return [String] dotted address
261
327
  def dst
262
- self[:dst].to_x
328
+ self[:dst].to_human
263
329
  end
264
330
  alias :destination :dst
265
331
 
@@ -267,27 +333,20 @@ module PacketGen
267
333
  # @param [String] addr dotted IP address
268
334
  # @return [String]
269
335
  def dst=(addr)
270
- self[:dst].parse addr
336
+ self[:dst].from_human addr
271
337
  end
272
338
  alias :destination= :dst=
273
339
 
274
- # Get binary string
275
- # @return [String]
276
- def to_s
277
- first_byte = [(version << 4) | ihl].pack('C')
278
- first_byte << to_a[2..-1].map { |field| field.to_s }.join
279
- end
280
-
281
- # Get IP part of pseudo header sum.
340
+ # Get IP part of pseudo header checksum.
282
341
  # @return [Integer]
283
- def pseudo_header_sum
284
- sum = self[:src].to_i + self[:dst].to_i
285
- (sum >> 16) + (sum & 0xffff)
342
+ def pseudo_header_checksum
343
+ checksum = self[:src].to_i + self[:dst].to_i
344
+ (checksum >> 16) + (checksum & 0xffff)
286
345
  end
287
346
 
288
347
  # Send IP packet on wire.
289
348
  #
290
- # When sending packet at IP level, +sum+ and +length+ fields are set by
349
+ # When sending packet at IP level, +checksum+ and +length+ fields are set by
291
350
  # kernel, so bad IP packets cannot be sent this way. To do so, use {Eth#to_w}.
292
351
  # @param [String,nil] iface interface name. Not used
293
352
  # @return [void]
@@ -296,9 +355,32 @@ module PacketGen
296
355
  sockaddrin = Socket.sockaddr_in(0, dst)
297
356
  sock.send to_s, 0, sockaddrin
298
357
  end
358
+
359
+ # @return [String]
360
+ def inspect
361
+ str = Inspect.dashed_line(self.class, 2)
362
+ shift = Inspect.shift_level(2)
363
+ to_h.each do |attr, value|
364
+ next if attr == :body
365
+ str << Inspect.inspect_attribute(attr, value, 2)
366
+ if attr == :u8
367
+ str << shift + Inspect::INSPECT_FMT_ATTR % ['', 'version', version]
368
+ str << shift + Inspect::INSPECT_FMT_ATTR % ['', 'ihl', ihl]
369
+ elsif attr == :frag
370
+ flags = flag_rsv? ? %w(RSV) : []
371
+ flags << 'DF' if flag_df?
372
+ flags << 'MF' if flag_mf?
373
+ flags_str = flags.empty? ? 'none' : flags.join(',')
374
+ str << shift + Inspect::INSPECT_FMT_ATTR % ['', 'flags', flags_str]
375
+ foff = Inspect.int_dec_hex(fragment_offset, 4)
376
+ str << shift + Inspect::INSPECT_FMT_ATTR % ['', 'frag_offset', foff]
377
+ end
378
+ end
379
+ str
380
+ end
299
381
  end
300
382
 
301
- Eth.bind_header IP, proto: 0x800
302
- IP.bind_header IP, proto: 4
383
+ Eth.bind_header IP, ethertype: 0x800
384
+ IP.bind_header IP, protocol: 4
303
385
  end
304
386
  end
@@ -1,13 +1,49 @@
1
1
  # coding: utf-8
2
+ # This file is part of PacketGen
3
+ # See https://github.com/sdaubert/packetgen for more informations
4
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
5
+ # This program is published under MIT license.
2
6
  require 'ipaddr'
3
7
 
4
8
  module PacketGen
5
9
  module Header
6
10
 
7
- # IPv6 header class
11
+ # A IPv6 header consists of:
12
+ # * a first 32-bit word ({#u32}, of {Int32} type) composoed of:
13
+ # * a 4-bit {#version} field,
14
+ # * a 8-bit {#traffic_class} field,
15
+ # * a 20-bit {#flow_label} field,
16
+ # * a payload length field ({#length}, {Int16} type}),
17
+ # * a next header field ({#next}, {Int8} type),
18
+ # * a hop-limit field ({#hop}, +Int8+ type),
19
+ # * a source address field ({#src}, {IPv6::Addr} type),
20
+ # * a destination address field ({#dst}, +IPv6::Addr+ type),
21
+ #
22
+ # == Create a IPv6 header
23
+ # # standalone
24
+ # ipv6 = PacketGen::Header::IPv6.new
25
+ # # in a packet
26
+ # pkt = PacketGen.gen('IPv6')
27
+ # # access to IPv6 header
28
+ # pkt.ipv6 # => PacketGen::Header::IPv6
29
+ #
30
+ # == IPv6 attributes
31
+ # ipv6.u32 = 0x60280001
32
+ # # the same as
33
+ # ipv6.version = 6
34
+ # ipv6.traffic_class = 2
35
+ # ipv6.flow_label = 0x80001
36
+ #
37
+ # ipv6.length = 0x43
38
+ # ipv6.hop = 0x40
39
+ # ipv6.next = 6
40
+ # ipv6.src = '::1'
41
+ # ipv6.src # => "::1"
42
+ # ipv6[:src] # => PacketGen::Header::IPv6::Addr
43
+ # ipv6.dst = '2001:1234:5678:abcd::123'
44
+ # ipv6.body.read 'this is a body'
8
45
  # @author Sylvain Daubert
9
- class IPv6 < Struct.new(:version, :traffic_class, :flow_label, :length,
10
- :next, :hop, :src, :dst, :body)
46
+ class IPv6 < Struct.new(:u32, :length, :next, :hop, :src, :dst, :body)
11
47
  include StructFu
12
48
  include HeaderMethods
13
49
  extend HeaderClassMethods
@@ -37,10 +73,10 @@ module PacketGen
37
73
  Int16.new(options[:a8])
38
74
  end
39
75
 
40
- # Parse a colon-delimited address
76
+ # Read a colon-delimited address
41
77
  # @param [String] str
42
78
  # @return [self]
43
- def parse(str)
79
+ def from_human(str)
44
80
  return self if str.nil?
45
81
  addr = IPAddr.new(str)
46
82
  raise ArgumentError, 'string is not a IPv6 address' unless addr.ipv6?
@@ -79,14 +115,14 @@ module PacketGen
79
115
 
80
116
  # Addr6 in human readable form (colon-delimited hex string)
81
117
  # @return [String]
82
- def to_x
118
+ def to_human
83
119
  IPAddr.new(to_a.map { |a| a.to_i.to_s(16) }.join(':')).to_s
84
120
  end
85
121
  end
86
122
 
87
123
  # @param [Hash] options
88
124
  # @option options [Integer] :version
89
- # @option options [Integer] :traffic_length
125
+ # @option options [Integer] :traffic_class
90
126
  # @option options [Integer] :flow_label
91
127
  # @option options [Integer] :length payload length
92
128
  # @option options [Integer] :next
@@ -95,23 +131,32 @@ module PacketGen
95
131
  # @option options [String] :dst colon-delimited destination address
96
132
  # @option options [String] :body binary string
97
133
  def initialize(options={})
98
- super options[:version] || 6,
99
- options[:traffic_class] || 0,
100
- options[:flow_label] || 0,
134
+ super Int32.new(0x60000000),
101
135
  Int16.new(options[:length]),
102
136
  Int8.new(options[:next]),
103
137
  Int8.new(options[:hop] || 64),
104
- Addr.new.parse(options[:src] || '::1'),
105
- Addr.new.parse(options[:dst] || '::1'),
138
+ Addr.new.from_human(options[:src] || '::1'),
139
+ Addr.new.from_human(options[:dst] || '::1'),
106
140
  StructFu::String.new.read(options[:body])
141
+ self.version = options[:version] if options[:version]
142
+ self.traffic_class = options[:traffic_class] if options[:traffic_class]
143
+ self.flow_label = options[:flow_label] if options[:flow_label]
107
144
  end
108
145
 
146
+ # @!attribute version
147
+ # @return [Integer] 4-bit version attribute
148
+ # @!attribute traffic_class
149
+ # @return [Integer] 8-bit traffic_class attribute
150
+ # @!attribute flow_label
151
+ # @return [Integer] 20-bit flow_label attribute
152
+ define_bit_fields_on :u32, :version, 4, :traffic_class, 8, :flow_label, 20
153
+
109
154
  # Read a IP header from a string
110
155
  # @param [String] str binary string
111
156
  # @return [self]
112
157
  def read(str)
113
158
  return self if str.nil?
114
- raise ParseError, 'string too short for Eth' if str.size < self.sz
159
+ raise ParseError, 'string too short for IPv6' if str.size < self.sz
115
160
  force_binary str
116
161
  first32 = str[0, 4].unpack('N').first
117
162
  self.version = first32 >> 28
@@ -133,13 +178,13 @@ module PacketGen
133
178
  self.length = body.sz
134
179
  end
135
180
 
136
- # Getter for length attribute
137
- # @return [Integer]
181
+ # @!attribute length
182
+ # 16-bit payload length attribute
183
+ # @return [Integer]
138
184
  def length
139
185
  self[:length].to_i
140
186
  end
141
187
 
142
- # Setter for length attribute
143
188
  # @param [Integer] i
144
189
  # @return [Integer]
145
190
  def length=(i)
@@ -175,7 +220,7 @@ module PacketGen
175
220
  # Getter for src attribute
176
221
  # @return [String]
177
222
  def src
178
- self[:src].to_x
223
+ self[:src].to_human
179
224
  end
180
225
  alias :source :src
181
226
 
@@ -183,14 +228,14 @@ module PacketGen
183
228
  # @param [String] addr
184
229
  # @return [Integer]
185
230
  def src=(addr)
186
- self[:src].parse addr
231
+ self[:src].from_human addr
187
232
  end
188
233
  alias :source= :src=
189
234
 
190
235
  # Getter for dst attribute
191
236
  # @return [String]
192
237
  def dst
193
- self[:dst].to_x
238
+ self[:dst].to_human
194
239
  end
195
240
  alias :destination :dst
196
241
 
@@ -198,20 +243,13 @@ module PacketGen
198
243
  # @param [String] addr
199
244
  # @return [Integer]
200
245
  def dst=(addr)
201
- self[:dst].parse addr
246
+ self[:dst].from_human addr
202
247
  end
203
248
  alias :destination= :dst=
204
249
 
205
- # Get binary string
206
- # @return [String]
207
- def to_s
208
- first32 = (version << 28) | (traffic_class << 20) | flow_label
209
- [first32].pack('N') << to_a[3..-1].map { |field| field.to_s }.join
210
- end
211
-
212
- # Get IPv6 part of pseudo header sum.
250
+ # Get IPv6 part of pseudo header checksum.
213
251
  # @return [Integer]
214
- def pseudo_header_sum
252
+ def pseudo_header_checksum
215
253
  sum = 0
216
254
  self[:src].each { |word| sum += word.to_i }
217
255
  self[:dst].each { |word| sum += word.to_i }
@@ -247,9 +285,27 @@ module PacketGen
247
285
 
248
286
  sock.sendmsg body.to_s, 0, sockaddrin, hop_limit, tc, pkt_info
249
287
  end
288
+
289
+ # @return [String]
290
+ def inspect
291
+ str = Inspect.dashed_line(self.class, 2)
292
+ to_h.each do |attr, value|
293
+ next if attr == :body
294
+ str << Inspect.inspect_attribute(attr, value, 2)
295
+ if attr == :u32
296
+ shift = Inspect.shift_level(2)
297
+ str << shift + Inspect::INSPECT_FMT_ATTR % ['', 'version', version]
298
+ tclass = Inspect.int_dec_hex(traffic_class, 2)
299
+ str << shift + Inspect::INSPECT_FMT_ATTR % ['', 'tclass', tclass]
300
+ fl_value = Inspect.int_dec_hex(flow_label, 5)
301
+ str << shift + Inspect::INSPECT_FMT_ATTR % ['', 'flow_label', fl_value]
302
+ end
303
+ end
304
+ str
305
+ end
250
306
  end
251
307
 
252
- Eth.bind_header IPv6, proto: 0x86DD
253
- IP.bind_header IPv6, proto: 41 # 6to4
308
+ Eth.bind_header IPv6, ethertype: 0x86DD
309
+ IP.bind_header IPv6, protocol: 41 # 6to4
254
310
  end
255
311
  end