packetgen 2.8.7 → 3.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.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -1
  3. data/README.md +5 -4
  4. data/lib/packetgen.rb +6 -12
  5. data/lib/packetgen/capture.rb +43 -39
  6. data/lib/packetgen/config.rb +0 -1
  7. data/lib/packetgen/deprecation.rb +1 -1
  8. data/lib/packetgen/header.rb +9 -9
  9. data/lib/packetgen/header/asn1_base.rb +10 -10
  10. data/lib/packetgen/header/base.rb +42 -101
  11. data/lib/packetgen/header/dhcp/option.rb +5 -11
  12. data/lib/packetgen/header/dhcpv6/duid.rb +2 -0
  13. data/lib/packetgen/header/dhcpv6/option.rb +2 -19
  14. data/lib/packetgen/header/dhcpv6/options.rb +7 -0
  15. data/lib/packetgen/header/dns.rb +5 -23
  16. data/lib/packetgen/header/dns/name.rb +1 -0
  17. data/lib/packetgen/header/dns/qdsection.rb +1 -0
  18. data/lib/packetgen/header/dns/question.rb +3 -7
  19. data/lib/packetgen/header/dns/rr.rb +3 -0
  20. data/lib/packetgen/header/dns/rrsection.rb +1 -0
  21. data/lib/packetgen/header/dot11.rb +1 -17
  22. data/lib/packetgen/header/dot1x.rb +1 -0
  23. data/lib/packetgen/header/eap.rb +4 -7
  24. data/lib/packetgen/header/eth.rb +2 -0
  25. data/lib/packetgen/header/http/headers.rb +3 -0
  26. data/lib/packetgen/header/http/request.rb +5 -4
  27. data/lib/packetgen/header/http/response.rb +5 -4
  28. data/lib/packetgen/header/icmp.rb +6 -0
  29. data/lib/packetgen/header/icmpv6.rb +6 -0
  30. data/lib/packetgen/header/igmpv3/mq.rb +2 -0
  31. data/lib/packetgen/header/ip.rb +32 -30
  32. data/lib/packetgen/header/ip/addr.rb +1 -0
  33. data/lib/packetgen/header/ip/option.rb +23 -20
  34. data/lib/packetgen/header/ip/options.rb +11 -24
  35. data/lib/packetgen/header/ipv6.rb +45 -34
  36. data/lib/packetgen/header/ipv6/addr.rb +2 -0
  37. data/lib/packetgen/header/ipv6/hop_by_hop.rb +7 -31
  38. data/lib/packetgen/header/mdns.rb +1 -0
  39. data/lib/packetgen/header/mldv2/mlq.rb +2 -0
  40. data/lib/packetgen/header/ospfv2/lsa.rb +15 -25
  41. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +1 -1
  42. data/lib/packetgen/header/ospfv3/lsa.rb +8 -25
  43. data/lib/packetgen/header/snmp.rb +2 -0
  44. data/lib/packetgen/header/tcp.rb +23 -2
  45. data/lib/packetgen/header/tcp/option.rb +51 -52
  46. data/lib/packetgen/header/tcp/options.rb +17 -52
  47. data/lib/packetgen/header/tftp.rb +3 -0
  48. data/lib/packetgen/header/udp.rb +8 -0
  49. data/lib/packetgen/packet.rb +119 -102
  50. data/lib/packetgen/pcapng/block.rb +4 -10
  51. data/lib/packetgen/pcapng/epb.rb +4 -4
  52. data/lib/packetgen/pcapng/file.rb +7 -3
  53. data/lib/packetgen/pcapng/idb.rb +2 -2
  54. data/lib/packetgen/pcapng/shb.rb +3 -3
  55. data/lib/packetgen/pcapng/spb.rb +1 -8
  56. data/lib/packetgen/pcapng/unknown_block.rb +0 -7
  57. data/lib/packetgen/types.rb +1 -0
  58. data/lib/packetgen/types/array.rb +73 -71
  59. data/lib/packetgen/types/cstring.rb +1 -1
  60. data/lib/packetgen/types/enum.rb +3 -3
  61. data/lib/packetgen/types/fields.rb +66 -106
  62. data/lib/packetgen/types/int.rb +9 -5
  63. data/lib/packetgen/types/length_from.rb +45 -0
  64. data/lib/packetgen/types/oui.rb +2 -0
  65. data/lib/packetgen/types/string.rb +10 -16
  66. data/lib/packetgen/types/tlv.rb +7 -15
  67. data/lib/packetgen/utils.rb +8 -8
  68. data/lib/packetgen/utils/arp_spoofer.rb +1 -2
  69. data/lib/packetgen/version.rb +1 -1
  70. metadata +3 -21
  71. data/lib/packetgen/header/crypto.rb +0 -62
  72. data/lib/packetgen/header/esp.rb +0 -413
  73. data/lib/packetgen/header/ike.rb +0 -243
  74. data/lib/packetgen/header/ike/auth.rb +0 -165
  75. data/lib/packetgen/header/ike/cert.rb +0 -76
  76. data/lib/packetgen/header/ike/certreq.rb +0 -66
  77. data/lib/packetgen/header/ike/id.rb +0 -99
  78. data/lib/packetgen/header/ike/ke.rb +0 -79
  79. data/lib/packetgen/header/ike/nonce.rb +0 -40
  80. data/lib/packetgen/header/ike/notify.rb +0 -176
  81. data/lib/packetgen/header/ike/payload.rb +0 -315
  82. data/lib/packetgen/header/ike/sa.rb +0 -561
  83. data/lib/packetgen/header/ike/sk.rb +0 -261
  84. data/lib/packetgen/header/ike/ts.rb +0 -270
  85. data/lib/packetgen/header/ike/vendor_id.rb +0 -39
  86. data/lib/packetgen/header/netbios.rb +0 -20
  87. data/lib/packetgen/header/netbios/datagram.rb +0 -105
  88. data/lib/packetgen/header/netbios/name.rb +0 -67
  89. data/lib/packetgen/header/netbios/session.rb +0 -64
@@ -74,6 +74,8 @@ module PacketGen
74
74
  # @return [Integer]
75
75
  define_bit_fields_on :u8, :resv, 4, :flag_s, :qrv, 3
76
76
 
77
+ undef qqic, qqic=
78
+
77
79
  # Get QQIC value
78
80
  # @note May return a different value from value previously set, as a
79
81
  # float encoding is used to encode big values. See {IGMPv3.decode}.
@@ -9,6 +9,22 @@ require 'socket'
9
9
 
10
10
  module PacketGen
11
11
  module Header
12
+ # IP protocol ({https://tools.ietf.org/html/rfc791 RFC 791})
13
+ # 0 1 2 3
14
+ # 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
15
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16
+ # |Version| IHL |Type of Service| Total Length |
17
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
18
+ # | Identification |Flags| Fragment Offset |
19
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20
+ # | Time to Live | Protocol | Header Checksum |
21
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22
+ # | Source Address |
23
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24
+ # | Destination Address |
25
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26
+ # | Options | Padding |
27
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12
28
  # A IP header consists of:
13
29
  # * a first byte ({#u8} of {Types::Int8} type) composed of:
14
30
  # * a 4-bit {#version} field,
@@ -24,7 +40,7 @@ module PacketGen
24
40
  # * a {#checksum} field (+Int16+),
25
41
  # * a source IP address ({#src}, {Addr} type),
26
42
  # * a destination IP address ({#dst}, +Addr+ type),
27
- # * an optional {#options} field ({Types::String} type),
43
+ # * an optional {#options} field ({Options} type),
28
44
  # * and a {#body} ({Types::String} type).
29
45
  #
30
46
  # == Create a IP header
@@ -61,6 +77,14 @@ module PacketGen
61
77
  # ip[:src] # => PacketGen::Header::IP::Addr
62
78
  # ip.dst = '127.0.0.2'
63
79
  # ip.body.read 'this is a body'
80
+ #
81
+ # == Add IP options
82
+ # IP has an {#options} attribute used to store datagram options.
83
+ # pkt = PacketGen.gen('IP')
84
+ # # add option from class
85
+ # pkt.ip.options << PacketGen::Header::IP::RA.new
86
+ # # or use a hash
87
+ # pkt.ip.options << { type: 'RR', data: ['192.168.16.4']}
64
88
  # @author Sylvain Daubert
65
89
  class IP < Base; end
66
90
 
@@ -84,7 +108,7 @@ module PacketGen
84
108
  define_field :length, Types::Int16, default: 20
85
109
  # @!attribute id
86
110
  # @return [Integer] 16-bit ID
87
- define_field :id, Types::Int16, default: ->(h) { rand(65_535) }
111
+ define_field :id, Types::Int16, default: ->(_) { rand(65_535) }
88
112
  # @!attribute frag
89
113
  # @return [Integer] 16-bit frag word
90
114
  define_field :frag, Types::Int16, default: 0
@@ -106,7 +130,8 @@ module PacketGen
106
130
  # @!attribute options
107
131
  # @since 2.2.0
108
132
  # @return [Types::String]
109
- define_field :options, Options, optional: ->(h) { h.ihl > 5 }
133
+ define_field :options, Options, optional: ->(h) { h.ihl > 5 },
134
+ builder: ->(h, t) { t.new(length_from: -> { (h.ihl - 5) * 4 }) }
110
135
  # @!attribute body
111
136
  # @return [Types::String,Header::Base]
112
137
  define_field :body, Types::String
@@ -164,31 +189,6 @@ module PacketGen
164
189
  checksum.zero? ? 0xffff : checksum
165
190
  end
166
191
 
167
- # Populate object from a binary string
168
- # @param [String] str
169
- # @return [Fields] self
170
- def read(str)
171
- return self if str.nil?
172
- force_binary str
173
- self[:u8].read str[0, 1]
174
- self[:tos].read str[1, 1]
175
- self[:length].read str[2, 2]
176
- self[:id].read str[4, 2]
177
- self[:frag].read str[6, 2]
178
- self[:ttl].read str[8, 1]
179
- self[:protocol].read str[9, 1]
180
- self[:checksum].read str[10, 2]
181
- self[:src].read str[12, 4]
182
- self[:dst].read str[16, 4]
183
- opt_size = 0
184
- if self.ihl > 5
185
- opt_size = (self.ihl - 5) * 4
186
- self[:options].read str[20, opt_size]
187
- end
188
- self[:body].read str[20 + opt_size..-1]
189
- self
190
- end
191
-
192
192
  # Compute checksum and set +checksum+ field
193
193
  # @return [Integer]
194
194
  def calc_checksum
@@ -207,10 +207,12 @@ module PacketGen
207
207
  self[:checksum].value = IP.reduce_checksum(checksum)
208
208
  end
209
209
 
210
- # Compute length and set +length+ field
210
+ # Compute and set +length+ and +ihl+ field
211
211
  # @return [Integer]
212
+ # @since 3.0.0 add +ihl+ calculation
212
213
  def calc_length
213
214
  Base.calculate_and_set_length self
215
+ self.ihl = 5 + self[:options].sz / 4
214
216
  end
215
217
 
216
218
  # Get IP part of pseudo header checksum.
@@ -265,7 +267,7 @@ module PacketGen
265
267
  # Get binary string. Fixup IHL if needed (IP header has options, and IHL
266
268
  # was not set by user).
267
269
  def to_s
268
- self.ihl = 5 + options.sz / 4 if self.ihl == 5
270
+ self.ihl = 5 + self[:options].sz / 4 if self.ihl == 5
269
271
  super
270
272
  end
271
273
 
@@ -31,6 +31,7 @@ module PacketGen
31
31
  # @return [self]
32
32
  def from_human(str)
33
33
  return self if str.nil?
34
+
34
35
  m = str.match(IPV4_ADDR_REGEX)
35
36
  if m
36
37
  self[:a1].read m[1].to_i
@@ -70,15 +70,32 @@ module PacketGen
70
70
  # @return [Hash]
71
71
  def self.types
72
72
  return @types if defined? @types
73
+
73
74
  @types = {}
74
75
  Option.constants.each do |cst|
75
76
  next unless cst.to_s.end_with? '_TYPE'
77
+
76
78
  optname = cst.to_s.sub(/_TYPE/, '')
77
- @types[Option.const_get(cst)] = IP.const_get(optname)
79
+ @types[optname] = Option.const_get(cst)
78
80
  end
79
81
  @types
80
82
  end
81
83
 
84
+ # Factory to build an option from its type
85
+ # @return [Option]
86
+ def Option.build(options={})
87
+ type = options.delete(:type)
88
+ klass = case type
89
+ when String
90
+ types.key?(type) ? IP.const_get(type) : self
91
+ when Integer
92
+ types.value?(type) ? IP.const_get(types.key(type)) : self
93
+ else
94
+ self
95
+ end
96
+ klass.new(options)
97
+ end
98
+
82
99
  def initialize(options={})
83
100
  unless options[:type]
84
101
  opt_name = self.class.to_s.gsub(/.*::/, '')
@@ -87,6 +104,7 @@ module PacketGen
87
104
  end
88
105
  end
89
106
  super
107
+ self.length = sz if respond_to?(:length) && options[:length].nil?
90
108
  end
91
109
 
92
110
  # Get binary string. Set {#length} field.
@@ -99,7 +117,7 @@ module PacketGen
99
117
  # Get a human readable string
100
118
  # @return [String]
101
119
  def to_human
102
- str = self.class == Option ? "unk-#{type}" : self.class.to_s.sub(/.*::/, '')
120
+ str = self.class == Option ? +"unk-#{type}" : self.class.to_s.sub(/.*::/, '')
103
121
  if respond_to?(:length) && (length > 2) && !self[:data].to_s.empty?
104
122
  str << ":#{self[:data].to_s.inspect}"
105
123
  end
@@ -114,8 +132,7 @@ module PacketGen
114
132
  end
115
133
 
116
134
  # No OPeration IP option
117
- class NOP < EOL
118
- end
135
+ class NOP < EOL; end
119
136
 
120
137
  # Loose Source and Record Route IP option
121
138
  class LSRR < Option
@@ -124,25 +141,11 @@ module PacketGen
124
141
  # @!attribute pointer
125
142
  # 8-bit pointer on next address
126
143
  # @return [Integer]
127
- define_field :pointer, Types::Int8
144
+ define_field :pointer, Types::Int8, default: 4
128
145
  # @!attribute data
129
146
  # Array of IP addresses
130
147
  # @return [Types::Array<IP::Addr>]
131
- define_field :data, ArrayOfAddr,
132
- builder: ->(h, t) { t.new(length_from: -> { h.length - 2 }) }
133
-
134
- # Populate object from a binary string
135
- # @param [String] str
136
- # @return [Fields] self
137
- def read(str)
138
- return self if str.nil?
139
- force_binary str
140
- self[:type].read str[0, 1]
141
- self[:length].read str[1, 1]
142
- self[:pointer].read str[2, 1]
143
- self[:data].read str[3, length - 3]
144
- self
145
- end
148
+ define_field :data, ArrayOfAddr, builder: ->(h, t) { t.new(length_from: -> { h.length - 3 }) }
146
149
 
147
150
  # Get IP address pointer by {#pointer}
148
151
  # @return [Addr]
@@ -15,30 +15,6 @@ module PacketGen
15
15
 
16
16
  HUMAN_SEPARATOR = ';'
17
17
 
18
- # Read IP header options from a string
19
- # @param [String] str binary string
20
- # @return [self]
21
- def read(str)
22
- clear
23
- return self if str.nil?
24
- PacketGen.force_binary str
25
-
26
- i = 0
27
- types = Option.types
28
- while i < str.to_s.length
29
- type = str[i, 1].unpack('C').first
30
- this_option = if types[type].nil?
31
- Option.new
32
- else
33
- types[type].new
34
- end
35
- this_option.read str[i, str.size]
36
- self << this_option
37
- i += this_option.sz
38
- end
39
- self
40
- end
41
-
42
18
  # Get binary string
43
19
  # @return [String]
44
20
  def to_s
@@ -48,6 +24,17 @@ module PacketGen
48
24
  end
49
25
  str
50
26
  end
27
+
28
+ private
29
+
30
+ def record_from_hash(hsh)
31
+ Option.build(hsh)
32
+ end
33
+
34
+ def real_type(opt)
35
+ types = Option.types
36
+ types.value?(opt.type) ? IP.const_get(types.key(opt.type)) : opt.class
37
+ end
51
38
  end
52
39
  end
53
40
  end
@@ -10,8 +10,33 @@ require 'ipaddr'
10
10
 
11
11
  module PacketGen
12
12
  module Header
13
+ # IPv6 ({https://tools.ietf.org/html/rfc8200 RFC 8200})
14
+ # 0 1 2 3
15
+ # 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
16
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17
+ # |Version| Traffic Class | Flow Label |
18
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19
+ # | Payload Length | Next Header | Hop Limit |
20
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21
+ # | |
22
+ # + +
23
+ # | |
24
+ # + Source Address +
25
+ # | |
26
+ # + +
27
+ # | |
28
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
29
+ # | |
30
+ # + +
31
+ # | |
32
+ # + Destination Address +
33
+ # | |
34
+ # + +
35
+ # | |
36
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37
+ #
13
38
  # A IPv6 header consists of:
14
- # * a first 32-bit word ({#u32}, of {Types::Int32} type) composoed of:
39
+ # * a first 32-bit word ({#u32}, of {Types::Int32} type) composed of:
15
40
  # * a 4-bit {#version} field,
16
41
  # * a 8-bit {#traffic_class} field,
17
42
  # * a 20-bit {#flow_label} field,
@@ -20,6 +45,7 @@ module PacketGen
20
45
  # * a hop-limit field ({#hop}, +Int8+ type),
21
46
  # * a source address field ({#src}, {IPv6::Addr} type),
22
47
  # * a destination address field ({#dst}, +IPv6::Addr+ type),
48
+ # * and a {#body} ({Types::String} type).
23
49
  #
24
50
  # == Create a IPv6 header
25
51
  # # standalone
@@ -44,6 +70,18 @@ module PacketGen
44
70
  # ipv6[:src] # => PacketGen::Header::IPv6::Addr
45
71
  # ipv6.dst = '2001:1234:5678:abcd::123'
46
72
  # ipv6.body.read 'this is a body'
73
+ #
74
+ # == Add IPv6 extensions
75
+ # In IPv6, optional extensions are encoded in separate headers that
76
+ # may be placed between the IPv6 header and the upper-layer header.
77
+ #
78
+ # In PacketGen, a IPv6 extension is processedf as a classical header:
79
+ # pkt = PacketGen.gen('IPv6')
80
+ # # Add a HopByHop extension
81
+ # pkt.add('IPv6::HopByHop')
82
+ # pkt.ipv6_hopbyhop.options << { type: 'router_alert', value: [0].pack('n') }
83
+ # # Add another header
84
+ # pkt.add('UDP')
47
85
  # @author Sylvain Daubert
48
86
  class IPv6 < Base; end
49
87
 
@@ -104,34 +142,14 @@ module PacketGen
104
142
  sum
105
143
  end
106
144
 
107
- # Send IPv6 packet on wire.
108
- #
109
- # When sending packet at IPv6 level, +version+, +flow_label+ and +length+
110
- # fields are set by kernel. Source address should be a unicast address
111
- # assigned to the host. To set any of this fields, use {Eth#to_w}.
112
- # @param [String] iface interface name
145
+ # Send IPv6 packet on wire. All fields may be set (even {#version}).
146
+ # @param [String] _iface interface name (not used)
113
147
  # @return [void]
114
- def to_w(iface=nil)
115
- sock = Socket.new(Socket::AF_INET6, Socket::SOCK_RAW, self.next)
148
+ # @since 3.0.0 no more limitations on +flow_label+, +length+ and +src+ fields.
149
+ def to_w(_iface=nil)
150
+ sock = Socket.new(Socket::AF_INET6, Socket::SOCK_RAW, Socket::IPPROTO_RAW)
116
151
  sockaddrin = Socket.sockaddr_in(0, dst)
117
-
118
- # IPv6 RAW sockets don't have IPHDRINCL option to send IPv6 header.
119
- # So, header must be built using ancillary data.
120
- # Only src address, traffic_class and hop_limit can be set this way.
121
- hop_limit = Socket::AncillaryData.int(Socket::AF_INET6,
122
- Socket::IPPROTO_IPV6,
123
- Socket::IPV6_HOPLIMIT, hop)
124
- tc = Socket::AncillaryData.int(Socket::AF_INET6,
125
- Socket::IPPROTO_IPV6,
126
- Socket::IPV6_TCLASS,
127
- traffic_class)
128
-
129
- # src address is set through PKT_INFO, which needs interface index.
130
- ifaddr = Socket.getifaddrs.find { |ia| ia.name == iface }
131
- raise WireError, "unknown #{iface} interface" if ifaddr.nil?
132
- pkt_info = Socket::AncillaryData.ipv6_pktinfo(Addrinfo.ip(src), ifaddr.ifindex)
133
-
134
- sock.sendmsg body.to_s, 0, sockaddrin, hop_limit, tc, pkt_info
152
+ sock.send to_s, 0, sockaddrin
135
153
  sock.close
136
154
  end
137
155
 
@@ -190,13 +208,6 @@ module PacketGen
190
208
  klass.bind header_klass, args
191
209
  end
192
210
  end
193
- # Bind a upper header to IPv6 and its defined extension headers.
194
- # @see Base.bind_header
195
- # @deprecated USe {bind}.
196
- def bind_header(header_klass, args={})
197
- Deprecation.deprecated(self, __method__, 'bind', klass_method: true)
198
- bind header_klass, args
199
- end
200
211
  end
201
212
  end
202
213
  end
@@ -52,8 +52,10 @@ module PacketGen
52
52
  # @return [self]
53
53
  def from_human(str)
54
54
  return self if str.nil?
55
+
55
56
  addr = IPAddr.new(str)
56
57
  raise ArgumentError, 'string is not a IPv6 address' unless addr.ipv6?
58
+
57
59
  addri = addr.to_i
58
60
  self.a1 = addri >> 112
59
61
  self.a2 = addri >> 96 & 0xffff
@@ -46,23 +46,6 @@ module PacketGen
46
46
  class Options < Types::Array
47
47
  set_of Option
48
48
 
49
- # Populate object from a binary string
50
- # @param [String] str
51
- # @return [self]
52
- def read(str)
53
- clear
54
- return self if str.nil?
55
- force_binary str
56
- klass = self.class.class_eval { @klass }
57
- until str.empty?
58
- obj = klass.new.read(str)
59
- obj = Pad1.new.read(str) if obj.type.zero?
60
- self.push obj
61
- str.slice!(0, obj.sz)
62
- end
63
- self
64
- end
65
-
66
49
  # Get options as a binary string. Add padding if needed.
67
50
  # @return [String]
68
51
  def to_s
@@ -83,6 +66,12 @@ module PacketGen
83
66
  end
84
67
  str
85
68
  end
69
+
70
+ private
71
+
72
+ def real_type(opt)
73
+ opt.type.zero? ? Pad1 : opt.class
74
+ end
86
75
  end
87
76
 
88
77
  # Hop-by-hop IPv6 extension
@@ -111,20 +100,7 @@ module PacketGen
111
100
  # @!attribute options
112
101
  # Specific options of extension header
113
102
  # @return [Options]
114
- define_field_before :body, :options, Options
115
-
116
- # Populate object from a binary string
117
- # @param [String] str
118
- # @return [self]
119
- def read(str)
120
- return self if str.nil?
121
- force_binary str
122
- self[:next].read str[0, 1]
123
- self[:length].read str[1, 1]
124
- self[:options].read str[2, real_length - 2]
125
- self[:body].read str[real_length..-1]
126
- self
127
- end
103
+ define_field_before :body, :options, Options, builder: ->(h, t) { t.new(length_from: -> { h.real_length - 2} ) }
128
104
  end
129
105
  end
130
106