vxlan 0.2.2 → 0.2.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec6112b96a159497c2b87ac94ef257e7a3baf710a43b9449ff450f757afe3c36
4
- data.tar.gz: c8766a7269d4dab9bbacc40026a045fcaf9522fb8052145e22f7d61619919dfb
3
+ metadata.gz: 715f96e8f586f8a7bacd62d8a7d5e47d4665884cdc20c3953d75c6282184955d
4
+ data.tar.gz: c3e70fb64cb7677d3e960d05f11622720f0a5842fc0171c7775047d0fd6b8d8a
5
5
  SHA512:
6
- metadata.gz: d40b5df068d87188cf5eda73ac5cbd4cbb70bc4133424caf641f9cc0a4a6f7bdbe0b7a1439682a0b7b54d266de4c425f712fcc13d721a017b7fefa9d2edf596a
7
- data.tar.gz: c4dc37ebef89f10a0692b26510f46b74f65e54a31bf7be3f134e84732bc45327239568bdc4acbdedc85fba25d188c53ef3f4addfec8bfef6f9c522bab6acdc5c
6
+ metadata.gz: c13eb74ea3ec0e75a8985a3bdf92503db849a6fc55e90af1a7a8ce051eaa810cdf50b25ab90869dae9696ca85bec142a83f0b7bbd649c5e6a7c6aea4019a09b6
7
+ data.tar.gz: 2bbb1204ab00cfcc50998a878b3d61c8614b03cc158833fecb1c3f1a2835672751bdd89d36e9dd79eca3cd893f91dfb4870c5ff8499817a2fe53bc758ec5eee4
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  Changes to the public contract are recorded here. Versions follow Semantic Versioning.
4
4
 
5
+ ## 0.2.3 (2026-10-18)
6
+
7
+ - Validate optional IPv4 next hops following static-route output interfaces without changing route fields or option text.
8
+
9
+ - Clear Bundler bootstrap variables when verifying packages in an isolated gem installation, including calls made through bundle exec.
10
+
11
+ - Reject unsupported or misplaced VRF address families before projecting targets and policy consumers, and validate anycast gateway MAC values and static-route next-hop forms.
12
+
13
+ - Parse runtime line boundaries and wrapped records independently of the host Ruby input/output record separators.
14
+
5
15
  ## 0.2.2 (2026-10-18)
6
16
 
7
17
  ### Changed
@@ -22,7 +22,7 @@ module Vxlan
22
22
  type = self.class::TYPE
23
23
  @totals = []
24
24
  fields = previous = next_hop_index = nil
25
- text.each_line.with_index(1) do |source, number|
25
+ text.each_line("\n").with_index(1) do |source, number|
26
26
  line = source.strip
27
27
  next if line.empty? || line.match?(/\A[-=\s]+\z/) || line.match?(/\A(?:Flags\s*-\s*)?(?:\([A-Za-z0-9]+\):[A-Za-z0-9 ,\/-]+\s*)+\z/)
28
28
  if line.match?(/\ATopology\b/i)
@@ -25,7 +25,7 @@ module Vxlan
25
25
  return CtxParser::Result.new(status: :partial, issues: [ { code: :invalid_encoding } ])
26
26
  end
27
27
  if Terminal.invalid_output?(text)
28
- line = text.each_line.with_index(1).find { |source, _number| Terminal.invalid_output?(source) }.last
28
+ line = text.each_line("\n").with_index(1).find { |source, _number| Terminal.invalid_output?(source) }.last
29
29
  return CtxParser::Result.new(status: :partial, issues: [ { code: :invalid_terminal_output, line: line } ])
30
30
  end
31
31
  if self.class::UNSUPPORTED&.match?(text)
@@ -72,7 +72,7 @@ module Vxlan
72
72
 
73
73
  # 逐行提供有效表格内容,并记录明确空表证据。
74
74
  def each_content_line(text, separators: false)
75
- text.each_line.with_index(1) do |source, number|
75
+ text.each_line("\n").with_index(1) do |source, number|
76
76
  line = source.strip
77
77
  next if line.empty? || (!separators && line.match?(/\A[-=+\s]+\z/))
78
78
  if self.class::EMPTY && line.match?(/\A(?:No #{self.class::EMPTY}(?: found)?|0 #{self.class::EMPTY})\.?\z/i)
@@ -67,14 +67,14 @@ module Vxlan
67
67
  # 归一化平台值,同时保留未知或不支持的状态。
68
68
  def normalize(text)
69
69
  output = []
70
- super.each_line do |line|
70
+ super.each_line("\n") do |line|
71
71
  if output.last&.strip&.end_with?("State/") && line.strip == "PfxRcd"
72
72
  output[-1] = output.last.rstrip + line.strip + "\n"
73
73
  else
74
74
  output << line
75
75
  end
76
76
  end
77
- output.join
77
+ output.join("")
78
78
  end
79
79
  end
80
80
  end
@@ -31,7 +31,7 @@ module Vxlan
31
31
  # 归一化平台值,同时保留未知或不支持的状态。
32
32
  def normalize(text)
33
33
  output = []
34
- super.each_line do |line|
34
+ super.each_line("\n") do |line|
35
35
  tail, previous = line.strip, output.last&.strip
36
36
  header = previous && previous.end_with?("Route") && tail == "r-Mac"
37
37
  mac = previous && tail.match?(/\A[\da-f]{4}\.[\da-f]{4}\z/i) && previous.match?(/\Anve\d+\s+\d+(?:\.\d+){3}\s+(?:Up|Down)\s+\S+\s+\S+\s+[\da-f]{4}\.\z/i)
@@ -41,7 +41,7 @@ module Vxlan
41
41
  output << line
42
42
  end
43
43
  end
44
- output.join
44
+ output.join("")
45
45
  end
46
46
  end
47
47
  end
@@ -111,7 +111,7 @@ module Vxlan
111
111
  analyze_children(block) do |child|
112
112
  if (match = /\Avni (\d+) (l[23])\z/i.match(child.text))
113
113
  id = integer(match[1], 1..16_777_215)
114
- store_unique_fact(data[:evpn], id, child, path: [ :evpn, id ]) { vrf(child).merge(type: match[2].downcase) }
114
+ store_unique_fact(data[:evpn], id, child, path: [ :evpn, id ]) { vrf(child, address_families: false).merge(type: match[2].downcase) }
115
115
  else
116
116
  issue(child)
117
117
  end
@@ -156,7 +156,9 @@ module Vxlan
156
156
  assign(data, :nv_overlay_evpn, block.text == "nv overlay evpn", block)
157
157
  capture(block, data[:nv_overlay_evpn], [ :nv_overlay_evpn ]) if data.key?(:nv_overlay_evpn)
158
158
  when /\Afabric forwarding anycast-gateway-mac (\S+)\z/
159
- assign(data, :anycast_mac, $1, block)
159
+ mac = $1
160
+ CtxParser::Values.mac_address(mac)
161
+ assign(data, :anycast_mac, mac, block)
160
162
  capture(block, data[:anycast_mac], [ :anycast_mac ]) if data.key?(:anycast_mac)
161
163
  when /\A(?:interface|vlan|vrf|router|vpc|evpn|feature|ip prefix-list|ipv6 prefix-list|route-map)\b/ then issue(block)
162
164
  when /\A(?:%|ERROR:|Invalid command|--More--)/i then issue(block, :invalid_command_output)
@@ -166,7 +168,7 @@ module Vxlan
166
168
  end
167
169
 
168
170
  # 解析 VRF 定义及其 VNI 或 route-target 关联。
169
- def vrf(block)
171
+ def vrf(block, address_families: true)
170
172
  values = { route_targets: [], routes: [] }
171
173
  analyze_children(block) do |child|
172
174
  case child.text
@@ -175,6 +177,8 @@ module Vxlan
175
177
  when /\Aroute-target / then values[:route_targets] << route_target(child)
176
178
  when /\Aaddress-family (.+)\z/
177
179
  family = $1
180
+ next issue(child) unless address_families && %w[ipv4\ unicast ipv6\ unicast].include?(family)
181
+
178
182
  targets_start = values[:route_targets].size
179
183
  exports_start = values.fetch(:exports, []).size
180
184
  analyze_children(child) do |setting|
@@ -242,9 +246,31 @@ module Vxlan
242
246
  def static_route(block)
243
247
  words = block.text.split
244
248
  raise ArgumentError if words.size < 4
249
+ validate_static_next_hop(words.drop(3))
250
+
245
251
  { prefix: ipv4_prefix(words[2]), next_hop: words[3], options: words.drop(4).join(" ") }
246
252
  end
247
253
 
254
+ # IP route 的下一跳是 IPv4 或出接口;接口分词形式不能误当作地址拒绝。
255
+ def validate_static_next_hop(words)
256
+ hop = words.first
257
+ interface = /\A(?:ethernet\d+(?:\/\d+){1,2}(?:\.\d+)?|port-channel\d+(?:\.\d+)?|vlan\d+|mgmt\d+|null0)\z/i
258
+ offset = if interface.match?(hop)
259
+ 1
260
+ elsif /\A(?:ethernet|port-channel|vlan|mgmt|null)\z/i.match?(hop)
261
+ raise ArgumentError unless words[1] && interface.match?(hop + words[1])
262
+
263
+ 2
264
+ else
265
+ 0
266
+ end
267
+ hop = words[offset]
268
+ # 出接口之后可直接跟已有选项;其余词须先作为 IPv4 下一跳校验。
269
+ return if offset.positive? && (hop.nil? || /\A(?:name|tag|track|vrf|\d+)\z/.match?(hop))
270
+
271
+ raise ArgumentError unless IPAddr.new(ip_address(hop)).ipv4?
272
+ end
273
+
248
274
  # 解析十进制整数,并校验声明的边界。
249
275
  def integer(value, range)
250
276
  raise ArgumentError unless value.is_a?(String)
@@ -30,7 +30,7 @@ module Vxlan
30
30
  end
31
31
  row.merge!(consistency: member[1], reason: member[2], active_vlans_summary: member[3])
32
32
  elsif match[4].match?(/\A[\d, .-]+\z/)
33
- row[:active_vlans_summary] = rest.join
33
+ row[:active_vlans_summary] = rest.join("")
34
34
  else
35
35
  unparsed_records(number)
36
36
  next
data/lib/vxlan/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Vxlan
4
4
  # 公开包版本,供 RubyGems 元数据和运行时调用方共用。
5
- VERSION = "0.2.2"
5
+ VERSION = "0.2.3"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vxlan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gatework contributors