packetgen 2.8.7 → 3.0.0

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