packetgen 0.3.0 → 1.0.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.
@@ -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