Skip to content

Commit 5af68c3

Browse files
Teach the parser context-awareness
Not all arguments are globally unique across all iptables extensions, and we should not search the entire rule string for all arguments when each one is only ever valid within a known context. Define start and stop delimiters for existing known overlaps, and default to a full-string search if no start or end markers are defined. Also, stop being quite so terse; the Ruby authors do not charge by the letter for variable names, and readability is good.
1 parent 72c9567 commit 5af68c3

File tree

2 files changed

+71
-9
lines changed

2 files changed

+71
-9
lines changed

lib/puppet/provider/firewall/ip6tables.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,4 +345,28 @@ def self.iptables_save(*args)
345345
:hashlimit_mode, :hashlimit_srcmask, :hashlimit_dstmask, :hashlimit_htable_size,
346346
:hashlimit_htable_max, :hashlimit_htable_expire, :hashlimit_htable_gcinterval, :bytecode, :rpfilter, :condition, :name
347347
]
348+
349+
# Not all arguments are globally unique across all iptables extensions. For matchers we should
350+
# only find within a specific context, a start and end marker can be supplied here. Either a
351+
# plain string or a regex will work; these are passed as an argument to String#index(), and limit
352+
# the search scope. If the resource matches on or after the first matching character in
353+
# context_start, and before the first matching character in context_end, the match succeeds.
354+
@resource_parse_context = {
355+
synproxy_mss: {
356+
context_start: '-j SYNPROXY',
357+
},
358+
mss: {
359+
# Extra starting space because the matcher for :mss includes '-m tcpmss',
360+
# and the search for it prefixes the matcher with a space
361+
context_start: ' -m tcpmss',
362+
context_end: %r{ -[mgj] },
363+
},
364+
string_to: {
365+
context_start: '-m string',
366+
context_end: %r{ -[mgj] },
367+
},
368+
to: {
369+
context_start: '-j NETMAP',
370+
},
371+
}
348372
end

lib/puppet/provider/firewall/iptables.rb

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,30 @@ def munge_resource_map_from_resource(resource_map_original, compare)
382382
:hashlimit_htable_max, :hashlimit_htable_expire, :hashlimit_htable_gcinterval, :bytecode, :ipvs, :cgroup, :rpfilter, :condition, :name
383383
]
384384

385+
# Not all arguments are globally unique across all iptables extensions. For matchers we should
386+
# only find within a specific context, a start and end marker can be supplied here. Either a
387+
# plain string or a regex will work; these are passed as an argument to String#index(), and limit
388+
# the search scope. If the resource matches on or after the first matching character in
389+
# context_start, and before the first matching character in context_end, the match succeeds.
390+
@resource_parse_context = {
391+
synproxy_mss: {
392+
context_start: '-j SYNPROXY',
393+
},
394+
mss: {
395+
# Extra starting space because the matcher for :mss includes '-m tcpmss',
396+
# and the search for it prefixes the matcher with a space
397+
context_start: ' -m tcpmss',
398+
context_end: %r{ -[mgj] },
399+
},
400+
string_to: {
401+
context_start: '-m string',
402+
context_end: %r{ -[mgj] },
403+
},
404+
to: {
405+
context_start: '-j NETMAP',
406+
},
407+
}
408+
385409
def insert
386410
debug 'Inserting rule %s' % resource[:name]
387411
iptables insert_args
@@ -591,17 +615,31 @@ def self.rule_to_hash(line, table, counter)
591615
############
592616
# Populate parser_list with used value, in the correct order
593617
############
594-
map_index = {}
595-
resource_map.each_pair do |map_k, map_v|
596-
[map_v].flatten.each do |v|
597-
ind = values.index(%r{\s#{v}\s})
598-
next unless ind
599-
map_index[map_k] = ind
618+
index_map = resource_map.each_with_object({}) do |(resource_name, matchers), index_mapping|
619+
# Get the start and end markers. If none are defined, use start/end of the rule.
620+
context_start = @resource_parse_context.dig(resource_name, :context_start) || %r{^}
621+
context_end = @resource_parse_context.dig(resource_name, :context_end) || %r{$}
622+
623+
# Get the offset at which to start searching
624+
search_start = values.index(context_start)
625+
# If a context marker was defined but wasn't in the string, then the resource can't be here, either
626+
next unless search_start
627+
628+
# Get the offset at which to stop searching
629+
# Start the search for the end marker past the end of context_start, so end markers
630+
# like '-m' won't match against their own start marker (e.g., '-m tcpmss')
631+
context_start = values.match(context_start).to_s if context_start.is_a? Regexp
632+
# If an end marker was defined but wasn't in the string, the context may be the last jump or match
633+
# target in the rule; so if no end marker is found, just use something past the end of the string.
634+
search_end = values.index(context_end, (search_start + context_start.length)) || values.length
635+
636+
[matchers].flatten.each do |v|
637+
index = values.index(%r{\s#{v}\s}, search_start)
638+
next unless index && index < search_end
639+
index_mapping[resource_name] = index
600640
end
601641
end
602-
# Generate parser_list based on the index of the found option
603-
parser_list = []
604-
map_index.sort_by { |_k, v| v }.each { |mapi| parser_list << mapi.first }
642+
parser_list = index_map.sort_by { |_resource_name, index| index }.to_h.keys
605643

606644
############
607645
# MAIN PARSE

0 commit comments

Comments
 (0)