Use diffs for iptables restore instead of all rules

This patch changes our iptables logic to generate a delta of
iptables commands (inserts + deletes) to get from the current
iptables state to the new state. This will significantly reduce
the amount of data that we have to shell out to iptables-restore
on every call (and reduce the amount of data iptables-restore has
to parse).

We no longer have to worry about preserving counters since
we are adding and deleting specific rules, so the rule modification
code got a nice cleanup to get rid of the old rule matching.

This also gives us a new method of functionally testing that we are
generating rules in the correct manner. After applying new rules
once, a subsequent call should always have no work to do. The new
functional tests added leverage that property heavily and should
protect us from regressions in how rules are formed.


Performance metrics relative to HEAD~1:
+====================================+============+=======+
|               Scenario             | This patch | HEAD~1|
|------------------------------------|------------|-------|
| 200 VMs*22 rules existing - startup|            |       |
|                       _modify_rules|   0.67s    | 1.05s |
|                 _apply_synchronized|   1.87s    | 2.89s |
|------------------------------------|------------|-------|
| 200 VMs*22 rules existing - add VM |            |       |
|                       _modify_rules|   0.68s    | 1.05s |
|                 _apply_synchronized|   2.07s    | 2.92s |
|------------------------------------+------------+-------+
|200 VMs*422 rules existing - startup|            |       |
|                       _modify_rules|   5.43s    | 8.17s |
|                 _apply_synchronized|  12.77s    |28.00s |
|------------------------------------|------------|-------|
|200 VMs*422 rules existing - add VM |            |       |
|                       _modify_rules|   6.41s    | 8.33s |
|                 _apply_synchronized|  33.09s    |33.80s |
+------------------------------------+------------+-------+

The _apply_synchronized times seem to converge when dealing
with ~85k rules. In the profile I can see that both approaches
seem to wait on iptables-restore for approximately the same
amount of time so it could be hitting the performance limits
of iptables-restore.

DocImpact
Partial-Bug: #1502297
Change-Id: Ia6470c85b6b71979006ffe5da9095fdcce3122c1
This commit is contained in:
Kevin Benton 2015-10-03 07:25:19 -07:00 committed by Kevin Benton
parent 292bdff78b
commit f066e46bb7
5 changed files with 1032 additions and 916 deletions

View File

@ -167,7 +167,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
self._enable_netfilter_for_bridges()
# each security group has it own chains
self._setup_chains()
self.iptables.apply()
return self.iptables.apply()
def update_port_filter(self, port):
LOG.debug("Updating device (%s) filter", port['device'])
@ -178,7 +178,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
self._remove_chains()
self._set_ports(port)
self._setup_chains()
self.iptables.apply()
return self.iptables.apply()
def remove_port_filter(self, port):
LOG.debug("Removing device (%s) filter", port['device'])
@ -189,7 +189,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
self._remove_chains()
self._unset_ports(port)
self._setup_chains()
self.iptables.apply()
return self.iptables.apply()
def _add_accept_rule_port_sec(self, port, direction):
self._update_port_sec_rules(port, direction, add=True)
@ -212,7 +212,10 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
def _setup_chains_apply(self, ports, unfiltered_ports):
self._add_chain_by_name_v4v6(SG_CHAIN)
for port in ports.values():
# sort by port so we always do this deterministically between
# agent restarts and don't cause unnecessary rule differences
for pname in sorted(ports):
port = ports[pname]
self._setup_chain(port, firewall.INGRESS_DIRECTION)
self._setup_chain(port, firewall.EGRESS_DIRECTION)
self.iptables.ipv4['filter'].add_rule(SG_CHAIN, '-j ACCEPT')

View File

@ -20,6 +20,7 @@
import collections
import contextlib
import difflib
import os
import re
import sys
@ -180,7 +181,8 @@ class IptablesTable(object):
self.remove_chains.add(name)
# first, add rules to remove that have a matching chain name
self.remove_rules += [r for r in self.rules if r.chain == name]
self.remove_rules += [str(r) for r in self.rules
if r.chain == name]
# next, remove rules from list that have a matching chain name
self.rules = [r for r in self.rules if r.chain != name]
@ -188,7 +190,7 @@ class IptablesTable(object):
if not wrap:
jump_snippet = '-j %s' % name
# next, add rules to remove that have a matching jump chain
self.remove_rules += [r for r in self.rules
self.remove_rules += [str(r) for r in self.rules
if jump_snippet in r.rule]
else:
jump_snippet = '-j %s-%s' % (self.wrap_name, name)
@ -244,9 +246,9 @@ class IptablesTable(object):
self.wrap_name,
comment=comment))
if not wrap:
self.remove_rules.append(IptablesRule(chain, rule, wrap, top,
self.wrap_name,
comment=comment))
self.remove_rules.append(str(IptablesRule(chain, rule, wrap,
top, self.wrap_name,
comment=comment)))
except ValueError:
LOG.warn(_LW('Tried to remove rule that was not there:'
' %(chain)r %(rule)r %(wrap)r %(top)r'),
@ -418,7 +420,7 @@ class IptablesManager(object):
if self.iptables_apply_deferred:
return
self._apply()
return self._apply()
def _apply(self):
lock_name = 'iptables'
@ -438,33 +440,50 @@ class IptablesManager(object):
def _apply_synchronized(self):
"""Apply the current in-memory set of iptables rules.
This will blow away any rules left over from previous runs of the
same component of Nova, and replace them with our current set of
rules. This happens atomically, thanks to iptables-restore.
This will create a diff between the rules from the previous runs
and replace them with the current set of rules.
This happens atomically, thanks to iptables-restore.
Returns a list of the changes that were sent to iptables-save.
"""
s = [('iptables', self.ipv4)]
if self.use_ipv6:
s += [('ip6tables', self.ipv6)]
all_commands = [] # variable to keep track all commands for return val
for cmd, tables in s:
args = ['%s-save' % (cmd,), '-c']
args = ['%s-save' % (cmd,)]
if self.namespace:
args = ['ip', 'netns', 'exec', self.namespace] + args
all_tables = self.execute(args, run_as_root=True)
all_lines = all_tables.split('\n')
save_output = self.execute(args, run_as_root=True)
all_lines = save_output.split('\n')
commands = []
# Traverse tables in sorted order for predictable dump output
for table_name in sorted(tables):
table = tables[table_name]
# isolate the lines of the table we are modifying
start, end = self._find_table(all_lines, table_name)
all_lines[start:end] = self._modify_rules(
all_lines[start:end], table, table_name)
args = ['%s-restore' % (cmd,), '-c']
old_rules = all_lines[start:end]
# generate the new table state we want
new_rules = self._modify_rules(old_rules, table, table_name)
# generate the iptables commands to get between the old state
# and the new state
changes = _generate_path_between_rules(old_rules, new_rules)
if changes:
# if there are changes to the table, we put on the header
# and footer that iptables-save needs
commands += (['# Generated by iptables_manager'] +
['*%s' % table_name] + changes +
['COMMIT', '# Completed by iptables_manager'])
if not commands:
continue
all_commands += commands
args = ['%s-restore' % (cmd,), '-n']
if self.namespace:
args = ['ip', 'netns', 'exec', self.namespace] + args
try:
self.execute(args, process_input='\n'.join(all_lines),
# always end with a new line
commands.append('')
self.execute(args, process_input='\n'.join(commands),
run_as_root=True)
except RuntimeError as r_error:
with excutils.save_and_reraise_exception():
@ -478,28 +497,30 @@ class IptablesManager(object):
except AttributeError:
# line error wasn't found, print all lines instead
log_start = 0
log_end = len(all_lines)
log_end = len(commands)
log_lines = ('%7d. %s' % (idx, l)
for idx, l in enumerate(
all_lines[log_start:log_end],
commands[log_start:log_end],
log_start + 1)
)
LOG.error(_LE("IPTablesManager.apply failed to apply the "
"following set of iptables rules:\n%s"),
'\n'.join(log_lines))
LOG.debug("IPTablesManager.apply completed with success")
LOG.debug("IPTablesManager.apply completed with success. %d iptables "
"commands were issued", len(all_commands))
return all_commands
def _find_table(self, lines, table_name):
if len(lines) < 3:
# length only <2 when fake iptables
return (0, 0)
try:
start = lines.index('*%s' % table_name) - 1
start = lines.index('*%s' % table_name)
except ValueError:
# Couldn't find table_name
LOG.debug('Unable to find table %s', table_name)
return (0, 0)
end = lines[start:].index('COMMIT') + start + 2
end = lines[start:].index('COMMIT') + start + 1
return (start, end)
def _find_rules_index(self, lines):
@ -518,172 +539,92 @@ class IptablesManager(object):
return rules_index
def _find_last_entry(self, filter_map, match_str):
# find last matching entry
try:
return filter_map[match_str][-1]
except KeyError:
pass
def _modify_rules(self, current_lines, table, table_name):
# Chains are stored as sets to avoid duplicates.
# Sort the output chains here to make their order predictable.
unwrapped_chains = sorted(table.unwrapped_chains)
chains = sorted(table.chains)
remove_chains = table.remove_chains
rules = table.rules
remove_rules = table.remove_rules
if not current_lines:
fake_table = ['# Generated by iptables_manager',
'*' + table_name, 'COMMIT',
'# Completed by iptables_manager']
current_lines = fake_table
# we don't want to change any rules that don't belong to us so we start
# the new_filter with these rules
new_filter = [line.strip() for line in current_lines
if self.wrap_name not in line]
# Fill old_filter with any chains or rules we might have added,
# they could have a [packet:byte] count we want to preserve.
# Fill new_filter with any chains or rules without our name in them.
old_filter, new_filter = [], []
for line in current_lines:
(old_filter if self.wrap_name in line else
new_filter).append(line.strip())
# generate our list of chain names
our_chains = [':%s-%s' % (self.wrap_name, name) for name in chains]
old_filter_map = make_filter_map(old_filter)
new_filter_map = make_filter_map(new_filter)
# the unwrapped chains (e.g. neutron-filter-top) may already exist in
# the new_filter since they aren't marked by the wrap_name so we only
# want to add them if they arent' already there
our_chains += [':%s' % name for name in unwrapped_chains
if not any(':%s' % name in s for s in new_filter)]
rules_index = self._find_rules_index(new_filter)
all_chains = [':%s' % name for name in unwrapped_chains]
all_chains += [':%s-%s' % (self.wrap_name, name) for name in chains]
# Iterate through all the chains, trying to find an existing
# match.
our_chains = []
for chain in all_chains:
chain_str = str(chain).strip()
old = self._find_last_entry(old_filter_map, chain_str)
if not old:
dup = self._find_last_entry(new_filter_map, chain_str)
new_filter = [s for s in new_filter if chain_str not in s.strip()]
# if no old or duplicates, use original chain
if old or dup:
chain_str = str(old or dup)
else:
# add-on the [packet:bytes]
chain_str += ' - [0:0]'
our_chains += [chain_str]
# Iterate through all the rules, trying to find an existing
# match.
our_rules = []
bot_rules = []
for rule in rules:
rule_str = str(rule).strip()
# Further down, we weed out duplicates from the bottom of the
# list, so here we remove the dupes ahead of time.
old = self._find_last_entry(old_filter_map, rule_str)
if not old:
dup = self._find_last_entry(new_filter_map, rule_str)
new_filter = [s for s in new_filter if rule_str not in s.strip()]
# if no old or duplicates, use original rule
if old or dup:
rule_str = str(old or dup)
# backup one index so we write the array correctly
if not old:
rules_index -= 1
else:
# add-on the [packet:bytes]
rule_str = '[0:0] ' + rule_str
our_top_rules = []
our_bottom_rules = []
for rule in table.rules:
rule_str = str(rule)
# similar to the unwrapped chains, there are some rules that belong
# to us but they don't have the wrap name. we want to remove them
# from the new_filter and then add them in the right location in
# case our new rules changed the order.
# (e.g. '-A FORWARD -j neutron-filter-top')
new_filter = [s for s in new_filter if rule_str not in s]
if rule.top:
# rule.top == True means we want this rule to be at the top.
our_rules += [rule_str]
our_top_rules += [rule_str]
else:
bot_rules += [rule_str]
our_bottom_rules += [rule_str]
our_rules += bot_rules
our_chains_and_rules = our_chains + our_top_rules + our_bottom_rules
new_filter[rules_index:rules_index] = our_rules
new_filter[rules_index:rules_index] = our_chains
def _strip_packets_bytes(line):
# strip any [packet:byte] counts at start or end of lines
if line.startswith(':'):
# it's a chain, for example, ":neutron-billing - [0:0]"
line = line.split(':')[1]
line = line.split(' - [', 1)[0]
elif line.startswith('['):
# it's a rule, for example, "[0:0] -A neutron-billing..."
line = line.split('] ', 1)[1]
line = line.strip()
return line
seen_chains = set()
def _weed_out_duplicate_chains(line):
# ignore [packet:byte] counts at end of lines
if line.startswith(':'):
line = _strip_packets_bytes(line)
if line in seen_chains:
return False
else:
seen_chains.add(line)
# Leave it alone
return True
seen_rules = set()
def _weed_out_duplicate_rules(line):
if line.startswith('['):
line = _strip_packets_bytes(line)
if line in seen_rules:
return False
else:
seen_rules.add(line)
# Leave it alone
return True
# locate the position immediately after the existing chains to insert
# our chains and rules
rules_index = self._find_rules_index(new_filter)
new_filter[rules_index:rules_index] = our_chains_and_rules
def _weed_out_removes(line):
# We need to find exact matches here
# remove any rules or chains from the filter that were slated
# for removal
if line.startswith(':'):
line = _strip_packets_bytes(line)
for chain in remove_chains:
if chain == line:
remove_chains.remove(chain)
return False
elif line.startswith('['):
line = _strip_packets_bytes(line)
for rule in remove_rules:
rule_str = _strip_packets_bytes(str(rule))
if rule_str == line:
remove_rules.remove(rule)
return False
chain = line[1:]
if chain in table.remove_chains:
table.remove_chains.remove(chain)
return False
else:
if line in table.remove_rules:
table.remove_rules.remove(line)
return False
# Leave it alone
return True
seen_lines = set()
# TODO(kevinbenton): remove this function and the next one. They are
# just oversized brooms to sweep bugs under the rug!!! We generate the
# rules and we shouldn't be generating duplicates.
def _weed_out_duplicates(line):
if line in seen_lines:
thing = 'chain' if line.startswith(':') else 'rule'
LOG.warning(_LW("Duplicate iptables %(thing)s detected. This "
"may indicate a bug in the the iptables "
"%(thing)s generation code. Line: %(line)s"),
{'thing': thing, 'line': line})
return False
seen_lines.add(line)
# Leave it alone
return True
# We filter duplicates. Go through the chains and rules, letting
# the *last* occurrence take precedence since it could have a
# non-zero [packet:byte] count we want to preserve. We also filter
# out anything in the "remove" list.
new_filter.reverse()
new_filter = [line for line in new_filter
if _weed_out_duplicate_chains(line) and
_weed_out_duplicate_rules(line) and
if _weed_out_duplicates(line) and
_weed_out_removes(line)]
new_filter.reverse()
# flush lists, just in case we didn't find something
remove_chains.clear()
for rule in remove_rules:
remove_rules.remove(rule)
# flush lists, just in case a rule or chain marked for removal
# was already gone. (chains is a set, rules is a list)
table.remove_chains.clear()
table.remove_rules = []
return new_filter
@ -735,27 +676,73 @@ class IptablesManager(object):
return acc
def make_filter_map(filter_list):
filter_map = collections.defaultdict(list)
for data in filter_list:
# strip any [packet:byte] counts at start or end of lines,
# for example, chains look like ":neutron-foo - [0:0]"
# and rules look like "[0:0] -A neutron-foo..."
if data.startswith('['):
key = data.rpartition('] ')[2]
elif data.endswith(']'):
key = data.rsplit(' [', 1)[0]
if key.endswith(' -'):
key = key[:-2]
def _generate_path_between_rules(old_rules, new_rules):
"""Generates iptables commands to get from old_rules to new_rules.
This function diffs the two rule sets and then calculates the iptables
commands necessary to get from the old rules to the new rules using
insert and delete commands.
"""
old_by_chain = _get_rules_by_chain(old_rules)
new_by_chain = _get_rules_by_chain(new_rules)
old_chains, new_chains = set(old_by_chain.keys()), set(new_by_chain.keys())
# all referenced chains should be declared at the top before rules.
# NOTE(kevinbenton): sorting and grouping chains is for determinism in
# tests. iptables doesn't care about the order here
statements = [':%s - [0:0]' % c for c in sorted(new_chains - old_chains)]
sg_chains = []
other_chains = []
for chain in sorted(old_chains | new_chains):
if '-sg-' in chain:
sg_chains.append(chain)
else:
# things like COMMIT, *filter, and *nat land here
other_chains.append(chain)
for chain in other_chains + sg_chains:
statements += _generate_chain_diff_iptables_commands(
chain, old_by_chain[chain], new_by_chain[chain])
# unreferenced chains get the axe
for chain in sorted(old_chains - new_chains):
statements += ['-X %s' % chain]
return statements
def _get_rules_by_chain(rules):
by_chain = collections.defaultdict(list)
for line in rules:
if line.startswith(':'):
chain = line[1:].split(' ', 1)[0]
# even though this is a default dict, we need to manually add
# chains to ensure that ones without rules are included because
# they might be a jump reference
if chain not in by_chain:
by_chain[chain] = []
elif line.startswith('-A'):
chain = line[3:].split(' ', 1)[0]
by_chain[chain].append(line)
return by_chain
def _generate_chain_diff_iptables_commands(chain, old_chain_rules,
new_chain_rules):
# keep track of the old index because we have to insert rules
# in the right position
old_index = 1
statements = []
for line in difflib.ndiff(old_chain_rules, new_chain_rules):
if line.startswith('?'):
# skip ? because that's a guide string for intraline differences
continue
filter_map[key].append(data)
# regular IP(v6) entries are translated into /32s or /128s so we
# include a lookup without the CIDR here to match as well
for cidr in ('/32', '/128'):
if cidr in key:
alt_key = key.replace(cidr, '')
filter_map[alt_key].append(data)
# return a regular dict so readers don't accidentally add entries
return dict(filter_map)
elif line.startswith('-'): # line deleted
statements.append('-D %s %d' % (chain, old_index))
# since we are removing a line from the old rules, we
# backup the index by 1
old_index -= 1
elif line.startswith('+'): # line added
# strip the chain name since we have to add it before the index
rule = line[5:].split(' ', 1)[-1]
# rule inserted at this position
statements.append('-I %s %d %s' % (chain, old_index, rule))
old_index += 1
return statements

View File

@ -15,6 +15,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
from neutron.agent.linux import iptables_firewall
from neutron.agent import securitygroups_rpc as sg_cfg
@ -82,3 +83,112 @@ class IptablesFirewallTestCase(base.BaseSudoTestCase):
self.src_port_desc['port_security_enabled'] = False
self.firewall.update_port_filter(self.src_port_desc)
self.client.assert_ping(self.server.ip)
def test_rule_application_converges(self):
sg_rules = [{'ethertype': 'IPv4', 'direction': 'egress'},
{'ethertype': 'IPv6', 'direction': 'egress'},
{'ethertype': 'IPv4', 'direction': 'ingress',
'source_ip_prefix': '0.0.0.0/0', 'protocol': 'icmp'},
{'ethertype': 'IPv6', 'direction': 'ingress',
'source_ip_prefix': '0::0/0', 'protocol': 'ipv6-icmp'}]
# make sure port ranges converge on all protocols with and without
# port ranges (prevents regression of bug 1502924)
for proto in ('tcp', 'udp', 'icmp'):
for version in ('IPv4', 'IPv6'):
if proto == 'icmp' and version == 'IPv6':
proto = 'ipv6-icmp'
base = {'ethertype': version, 'direction': 'ingress',
'protocol': proto}
sg_rules.append(copy.copy(base))
base['port_range_min'] = 50
base['port_range_max'] = 50
sg_rules.append(copy.copy(base))
base['port_range_max'] = 55
sg_rules.append(copy.copy(base))
base['source_port_range_min'] = 60
base['source_port_range_max'] = 60
sg_rules.append(copy.copy(base))
base['source_port_range_max'] = 65
sg_rules.append(copy.copy(base))
# add some single-host rules to prevent regression of bug 1502917
sg_rules.append({'ethertype': 'IPv4', 'direction': 'ingress',
'source_ip_prefix': '77.77.77.77/32'})
sg_rules.append({'ethertype': 'IPv6', 'direction': 'ingress',
'source_ip_prefix': 'fe80::1/128'})
self.firewall.update_security_group_rules(
self.FAKE_SECURITY_GROUP_ID, sg_rules)
self.firewall.prepare_port_filter(self.src_port_desc)
# after one prepare call, another apply should be a NOOP
self.assertEqual([], self.firewall.iptables._apply())
orig_sg_rules = copy.copy(sg_rules)
for proto in ('tcp', 'udp', 'icmp'):
for version in ('IPv4', 'IPv6'):
if proto == 'icmp' and version == 'IPv6':
proto = 'ipv6-icmp'
# make sure firewall is in converged state
self.firewall.update_security_group_rules(
self.FAKE_SECURITY_GROUP_ID, orig_sg_rules)
self.firewall.update_port_filter(self.src_port_desc)
sg_rules = copy.copy(orig_sg_rules)
# remove one rule and add another to make sure it results in
# exactly one delete and insert
sg_rules.pop(0 if version == 'IPv4' else 1)
sg_rules.append({'ethertype': version, 'direction': 'egress',
'protocol': proto})
self.firewall.update_security_group_rules(
self.FAKE_SECURITY_GROUP_ID, sg_rules)
result = self.firewall.update_port_filter(self.src_port_desc)
deletes = [r for r in result if r.startswith('-D ')]
creates = [r for r in result if r.startswith('-I ')]
self.assertEqual(1, len(deletes))
self.assertEqual(1, len(creates))
# quick sanity check to make sure the insert was for the
# correct proto
self.assertIn('-p %s' % proto, creates[0])
# another apply should be a NOOP if the right rule was removed
# and the new one was inserted in the correct position
self.assertEqual([], self.firewall.iptables._apply())
def test_rule_ordering_correct(self):
sg_rules = [
{'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'tcp',
'port_range_min': i, 'port_range_max': i}
for i in range(50, 61)
]
self.firewall.update_security_group_rules(
self.FAKE_SECURITY_GROUP_ID, sg_rules)
self.firewall.prepare_port_filter(self.src_port_desc)
self._assert_sg_out_tcp_rules_appear_in_order(sg_rules)
# remove a rule and add a new one
sg_rules.pop(5)
sg_rules.insert(8, {'ethertype': 'IPv4', 'direction': 'egress',
'protocol': 'tcp', 'port_range_min': 400,
'port_range_max': 400})
self.firewall.update_security_group_rules(
self.FAKE_SECURITY_GROUP_ID, sg_rules)
self.firewall.prepare_port_filter(self.src_port_desc)
self._assert_sg_out_tcp_rules_appear_in_order(sg_rules)
# reverse all of the rules (requires lots of deletes and inserts)
sg_rules = list(reversed(sg_rules))
self.firewall.update_security_group_rules(
self.FAKE_SECURITY_GROUP_ID, sg_rules)
self.firewall.prepare_port_filter(self.src_port_desc)
self._assert_sg_out_tcp_rules_appear_in_order(sg_rules)
def _assert_sg_out_tcp_rules_appear_in_order(self, sg_rules):
outgoing_rule_pref = '-A %s-o%s' % (self.firewall.iptables.wrap_name,
self.src_port_desc['device'][3:13])
rules = [
r for r in self.firewall.iptables.get_rules_for_table('filter')
if r.startswith(outgoing_rule_pref)
]
# we want to ensure the rules went in in the same order we sent
indexes = [rules.index('%s -p tcp -m tcp --dport %s -j RETURN' %
(outgoing_rule_pref, i['port_range_min']))
for i in sg_rules]
# all indexes should be in order with no unexpected rules in between
self.assertEqual(range(indexes[0], indexes[-1] + 1), indexes)

View File

@ -35,18 +35,21 @@ IPTABLES_ARG = {'bn': iptables_manager.binary_name,
NAT_TEMPLATE = ('# Generated by iptables_manager\n'
'*nat\n'
':OUTPUT - [0:0]\n'
':POSTROUTING - [0:0]\n'
':PREROUTING - [0:0]\n'
':neutron-postrouting-bottom - [0:0]\n'
':%(bn)s-OUTPUT - [0:0]\n'
':%(bn)s-POSTROUTING - [0:0]\n'
':%(bn)s-PREROUTING - [0:0]\n'
':%(bn)s-float-snat - [0:0]\n'
':%(bn)s-snat - [0:0]\n'
'[0:0] -A PREROUTING -j %(bn)s-PREROUTING\n'
'[0:0] -A OUTPUT -j %(bn)s-OUTPUT\n'
'[0:0] -A POSTROUTING -j %(bn)s-POSTROUTING\n'
'[0:0] -A POSTROUTING -j neutron-postrouting-bottom\n'
'[0:0] -A neutron-postrouting-bottom -j %(bn)s-snat\n'
'[0:0] -A %(bn)s-snat -j '
'-I OUTPUT 1 -j %(bn)s-OUTPUT\n'
'-I POSTROUTING 1 -j %(bn)s-POSTROUTING\n'
'-I POSTROUTING 2 -j neutron-postrouting-bottom\n'
'-I PREROUTING 1 -j %(bn)s-PREROUTING\n'
'-I neutron-postrouting-bottom 1 -j %(bn)s-snat\n'
'-I %(bn)s-snat 1 -j '
'%(bn)s-float-snat\n'
'COMMIT\n'
'# Completed by iptables_manager\n')
@ -55,17 +58,20 @@ NAT_DUMP = NAT_TEMPLATE % IPTABLES_ARG
FILTER_TEMPLATE = ('# Generated by iptables_manager\n'
'*filter\n'
':FORWARD - [0:0]\n'
':INPUT - [0:0]\n'
':OUTPUT - [0:0]\n'
':neutron-filter-top - [0:0]\n'
':%(bn)s-FORWARD - [0:0]\n'
':%(bn)s-INPUT - [0:0]\n'
':%(bn)s-OUTPUT - [0:0]\n'
':%(bn)s-local - [0:0]\n'
'[0:0] -A FORWARD -j neutron-filter-top\n'
'[0:0] -A OUTPUT -j neutron-filter-top\n'
'[0:0] -A neutron-filter-top -j %(bn)s-local\n'
'[0:0] -A INPUT -j %(bn)s-INPUT\n'
'[0:0] -A OUTPUT -j %(bn)s-OUTPUT\n'
'[0:0] -A FORWARD -j %(bn)s-FORWARD\n'
'-I FORWARD 1 -j neutron-filter-top\n'
'-I FORWARD 2 -j %(bn)s-FORWARD\n'
'-I INPUT 1 -j %(bn)s-INPUT\n'
'-I OUTPUT 1 -j neutron-filter-top\n'
'-I OUTPUT 2 -j %(bn)s-OUTPUT\n'
'-I neutron-filter-top 1 -j %(bn)s-local\n'
'COMMIT\n'
'# Completed by iptables_manager\n')
@ -74,18 +80,21 @@ FILTER_DUMP = FILTER_TEMPLATE % IPTABLES_ARG
FILTER_WITH_RULES_TEMPLATE = (
'# Generated by iptables_manager\n'
'*filter\n'
':FORWARD - [0:0]\n'
':INPUT - [0:0]\n'
':OUTPUT - [0:0]\n'
':neutron-filter-top - [0:0]\n'
':%(bn)s-FORWARD - [0:0]\n'
':%(bn)s-INPUT - [0:0]\n'
':%(bn)s-OUTPUT - [0:0]\n'
':%(bn)s-filter - [0:0]\n'
':%(bn)s-local - [0:0]\n'
'[0:0] -A FORWARD -j neutron-filter-top\n'
'[0:0] -A OUTPUT -j neutron-filter-top\n'
'[0:0] -A neutron-filter-top -j %(bn)s-local\n'
'[0:0] -A INPUT -j %(bn)s-INPUT\n'
'[0:0] -A OUTPUT -j %(bn)s-OUTPUT\n'
'[0:0] -A FORWARD -j %(bn)s-FORWARD\n'
'-I FORWARD 1 -j neutron-filter-top\n'
'-I FORWARD 2 -j %(bn)s-FORWARD\n'
'-I INPUT 1 -j %(bn)s-INPUT\n'
'-I OUTPUT 1 -j neutron-filter-top\n'
'-I OUTPUT 2 -j %(bn)s-OUTPUT\n'
'-I neutron-filter-top 1 -j %(bn)s-local\n'
'%(filter_rules)s'
'COMMIT\n'
'# Completed by iptables_manager\n')
@ -93,19 +102,22 @@ FILTER_WITH_RULES_TEMPLATE = (
COMMENTED_NAT_DUMP = (
'# Generated by iptables_manager\n'
'*nat\n'
':OUTPUT - [0:0]\n'
':POSTROUTING - [0:0]\n'
':PREROUTING - [0:0]\n'
':neutron-postrouting-bottom - [0:0]\n'
':%(bn)s-OUTPUT - [0:0]\n'
':%(bn)s-POSTROUTING - [0:0]\n'
':%(bn)s-PREROUTING - [0:0]\n'
':%(bn)s-float-snat - [0:0]\n'
':%(bn)s-snat - [0:0]\n'
'[0:0] -A PREROUTING -j %(bn)s-PREROUTING\n'
'[0:0] -A OUTPUT -j %(bn)s-OUTPUT\n'
'[0:0] -A POSTROUTING -j %(bn)s-POSTROUTING\n'
'[0:0] -A POSTROUTING -j neutron-postrouting-bottom\n'
'[0:0] -A neutron-postrouting-bottom '
'-I OUTPUT 1 -j %(bn)s-OUTPUT\n'
'-I POSTROUTING 1 -j %(bn)s-POSTROUTING\n'
'-I POSTROUTING 2 -j neutron-postrouting-bottom\n'
'-I PREROUTING 1 -j %(bn)s-PREROUTING\n'
'-I neutron-postrouting-bottom 1 '
'-m comment --comment "%(snat_out_comment)s" -j %(bn)s-snat\n'
'[0:0] -A %(bn)s-snat -j '
'-I %(bn)s-snat 1 -j '
'%(bn)s-float-snat\n'
'COMMIT\n'
'# Completed by iptables_manager\n' % IPTABLES_ARG)
@ -160,9 +172,9 @@ class IptablesCommentsTestCase(base.BaseTestCase):
def test_add_filter_rule(self):
iptables_args = {}
iptables_args.update(IPTABLES_ARG)
filter_rules = ('[0:0] -A %(bn)s-filter -j DROP\n'
'[0:0] -A %(bn)s-INPUT -s 0/0 -d 192.168.0.2 -j '
'%(bn)s-filter\n' % iptables_args)
filter_rules = ('-I %(bn)s-INPUT 1 -s 0/0 -d 192.168.0.2 -j '
'%(bn)s-filter\n-I %(bn)s-filter 1 -j DROP\n'
% iptables_args)
iptables_args['filter_rules'] = filter_rules
filter_dump_mod = FILTER_WITH_RULES_TEMPLATE % iptables_args
@ -170,20 +182,20 @@ class IptablesCommentsTestCase(base.BaseTestCase):
mangle_dump = _generate_mangle_dump(IPTABLES_ARG)
expected_calls_and_values = [
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(raw_dump + COMMENTED_NAT_DUMP +
mangle_dump + filter_dump_mod),
(mock.call(['iptables-restore', '-n'],
process_input=(filter_dump_mod + mangle_dump +
COMMENTED_NAT_DUMP + raw_dump),
run_as_root=True),
None),
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(raw_dump + COMMENTED_NAT_DUMP +
mangle_dump + FILTER_DUMP),
(mock.call(['iptables-restore', '-n'],
process_input=(FILTER_DUMP + mangle_dump +
COMMENTED_NAT_DUMP + raw_dump),
run_as_root=True
),
None),
@ -212,18 +224,23 @@ class IptablesCommentsTestCase(base.BaseTestCase):
def _generate_mangle_dump(iptables_args):
return ('# Generated by iptables_manager\n'
'*mangle\n'
':FORWARD - [0:0]\n'
':INPUT - [0:0]\n'
':OUTPUT - [0:0]\n'
':POSTROUTING - [0:0]\n'
':PREROUTING - [0:0]\n'
':%(bn)s-FORWARD - [0:0]\n'
':%(bn)s-INPUT - [0:0]\n'
':%(bn)s-OUTPUT - [0:0]\n'
':%(bn)s-POSTROUTING - [0:0]\n'
':%(bn)s-PREROUTING - [0:0]\n'
':%(bn)s-mark - [0:0]\n'
'[0:0] -A PREROUTING -j %(bn)s-PREROUTING\n'
'[0:0] -A INPUT -j %(bn)s-INPUT\n'
'[0:0] -A FORWARD -j %(bn)s-FORWARD\n'
'[0:0] -A OUTPUT -j %(bn)s-OUTPUT\n'
'[0:0] -A POSTROUTING -j %(bn)s-POSTROUTING\n'
'[0:0] -A %(bn)s-PREROUTING -j %(bn)s-mark\n'
'-I FORWARD 1 -j %(bn)s-FORWARD\n'
'-I INPUT 1 -j %(bn)s-INPUT\n'
'-I OUTPUT 1 -j %(bn)s-OUTPUT\n'
'-I POSTROUTING 1 -j %(bn)s-POSTROUTING\n'
'-I PREROUTING 1 -j %(bn)s-PREROUTING\n'
'-I %(bn)s-PREROUTING 1 -j %(bn)s-mark\n'
'COMMIT\n'
'# Completed by iptables_manager\n' % iptables_args)
@ -231,13 +248,16 @@ def _generate_mangle_dump(iptables_args):
def _generate_raw_dump(iptables_args):
return ('# Generated by iptables_manager\n'
'*raw\n'
':OUTPUT - [0:0]\n'
':PREROUTING - [0:0]\n'
':%(bn)s-OUTPUT - [0:0]\n'
':%(bn)s-PREROUTING - [0:0]\n'
'[0:0] -A PREROUTING -j %(bn)s-PREROUTING\n'
'[0:0] -A OUTPUT -j %(bn)s-OUTPUT\n'
'-I OUTPUT 1 -j %(bn)s-OUTPUT\n'
'-I PREROUTING 1 -j %(bn)s-PREROUTING\n'
'COMMIT\n'
'# Completed by iptables_manager\n' % iptables_args)
MANGLE_DUMP = _generate_mangle_dump(IPTABLES_ARG)
RAW_DUMP = _generate_raw_dump(IPTABLES_ARG)
@ -272,25 +292,25 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
def _extend_with_ip6tables_filter(self, expected_calls, filter_dump):
expected_calls.insert(2, (
mock.call(['ip6tables-save', '-c'],
mock.call(['ip6tables-save'],
run_as_root=True),
''))
expected_calls.insert(3, (
mock.call(['ip6tables-restore', '-c'],
mock.call(['ip6tables-restore', '-n'],
process_input=filter_dump,
run_as_root=True),
None))
expected_calls.extend([
(mock.call(['ip6tables-save', '-c'],
(mock.call(['ip6tables-save'],
run_as_root=True),
''),
(mock.call(['ip6tables-restore', '-c'],
(mock.call(['ip6tables-restore', '-n'],
process_input=filter_dump,
run_as_root=True),
None)])
def _test_add_and_remove_chain_custom_binary_name_helper(self, use_ipv6):
bn = ("abcdef" * 5)
bn = ("xbcdef" * 5)
self.iptables = iptables_manager.IptablesManager(
binary_name=bn,
@ -311,26 +331,26 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
mangle_dump = _generate_mangle_dump(iptables_args)
expected_calls_and_values = [
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(raw_dump + nat_dump + mangle_dump +
filter_dump_mod),
(mock.call(['iptables-restore', '-n'],
process_input=(filter_dump_mod + mangle_dump +
nat_dump + raw_dump),
run_as_root=True),
None),
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(raw_dump + nat_dump + mangle_dump +
filter_dump),
(mock.call(['iptables-restore', '-n'],
process_input=(filter_dump + mangle_dump +
nat_dump + raw_dump),
run_as_root=True),
None),
]
if use_ipv6:
self._extend_with_ip6tables_filter(expected_calls_and_values,
raw_dump + filter_dump_ipv6)
filter_dump_ipv6 + raw_dump)
tools.setup_mock_calls(self.execute, expected_calls_and_values)
@ -349,7 +369,7 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
self._test_add_and_remove_chain_custom_binary_name_helper(True)
def _test_empty_chain_custom_binary_name_helper(self, use_ipv6):
bn = ("abcdef" * 5)[:16]
bn = ("xbcdef" * 5)[:16]
self.iptables = iptables_manager.IptablesManager(
binary_name=bn,
@ -360,7 +380,7 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
filter_dump = FILTER_TEMPLATE % iptables_args
filter_rules = ('[0:0] -A %(bn)s-filter -s 0/0 -d 192.168.0.2\n'
filter_rules = ('-I %(bn)s-filter 1 -s 0/0 -d 192.168.0.2\n'
% iptables_args)
iptables_args['filter_rules'] = filter_rules
filter_dump_mod = FILTER_WITH_RULES_TEMPLATE % iptables_args
@ -371,26 +391,26 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
mangle_dump = _generate_mangle_dump(iptables_args)
expected_calls_and_values = [
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(raw_dump + nat_dump + mangle_dump +
filter_dump_mod),
(mock.call(['iptables-restore', '-n'],
process_input=(filter_dump_mod + mangle_dump +
nat_dump + raw_dump),
run_as_root=True),
None),
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(raw_dump + nat_dump + mangle_dump +
filter_dump),
(mock.call(['iptables-restore', '-n'],
process_input=(filter_dump + mangle_dump +
nat_dump + raw_dump),
run_as_root=True),
None),
]
if use_ipv6:
self._extend_with_ip6tables_filter(expected_calls_and_values,
raw_dump + filter_dump)
filter_dump + raw_dump)
tools.setup_mock_calls(self.execute, expected_calls_and_values)
@ -418,26 +438,26 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
filter_dump_mod = FILTER_WITH_RULES_TEMPLATE % IPTABLES_ARG
expected_calls_and_values = [
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(RAW_DUMP + NAT_DUMP + MANGLE_DUMP +
filter_dump_mod),
(mock.call(['iptables-restore', '-n'],
process_input=(filter_dump_mod + MANGLE_DUMP +
NAT_DUMP + RAW_DUMP),
run_as_root=True),
None),
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(RAW_DUMP + NAT_DUMP + MANGLE_DUMP +
FILTER_DUMP),
(mock.call(['iptables-restore', '-n'],
process_input=(FILTER_DUMP + MANGLE_DUMP + NAT_DUMP +
RAW_DUMP),
run_as_root=True),
None),
]
if use_ipv6:
self._extend_with_ip6tables_filter(expected_calls_and_values,
RAW_DUMP + FILTER_DUMP)
FILTER_DUMP + RAW_DUMP)
tools.setup_mock_calls(self.execute, expected_calls_and_values)
@ -462,36 +482,36 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
iptables_args = {}
iptables_args.update(IPTABLES_ARG)
filter_rules = ('[0:0] -A %(bn)s-filter -j DROP\n'
'[0:0] -A %(bn)s-INPUT -s 0/0 -d 192.168.0.2 -j '
'%(bn)s-filter\n' % iptables_args)
filter_rules = ('-I %(bn)s-INPUT 1 -s 0/0 -d 192.168.0.2 -j '
'%(bn)s-filter\n-I %(bn)s-filter 1 -j DROP\n'
% iptables_args)
iptables_args['filter_rules'] = filter_rules
filter_dump_mod = FILTER_WITH_RULES_TEMPLATE % iptables_args
raw_dump = RAW_DUMP % IPTABLES_ARG
expected_calls_and_values = [
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(RAW_DUMP + NAT_DUMP + MANGLE_DUMP +
filter_dump_mod),
(mock.call(['iptables-restore', '-n'],
process_input=(filter_dump_mod + MANGLE_DUMP +
NAT_DUMP + RAW_DUMP),
run_as_root=True),
None),
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(RAW_DUMP + NAT_DUMP + MANGLE_DUMP +
FILTER_DUMP),
(mock.call(['iptables-restore', '-n'],
process_input=(FILTER_DUMP + MANGLE_DUMP + NAT_DUMP +
RAW_DUMP),
run_as_root=True
),
None),
]
if use_ipv6:
self._extend_with_ip6tables_filter(expected_calls_and_values,
raw_dump + FILTER_DUMP)
FILTER_DUMP + raw_dump)
tools.setup_mock_calls(self.execute, expected_calls_and_values)
@ -533,19 +553,22 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
filter_dump_mod = ('# Generated by iptables_manager\n'
'*filter\n'
':FORWARD - [0:0]\n'
':INPUT - [0:0]\n'
':OUTPUT - [0:0]\n'
':neutron-filter-top - [0:0]\n'
':%(wrap)s - [0:0]\n'
':%(bn)s-FORWARD - [0:0]\n'
':%(bn)s-INPUT - [0:0]\n'
':%(bn)s-OUTPUT - [0:0]\n'
':%(bn)s-local - [0:0]\n'
'[0:0] -A FORWARD -j neutron-filter-top\n'
'[0:0] -A OUTPUT -j neutron-filter-top\n'
'[0:0] -A neutron-filter-top -j %(bn)s-local\n'
'[0:0] -A INPUT -j %(bn)s-INPUT\n'
'[0:0] -A OUTPUT -j %(bn)s-OUTPUT\n'
'[0:0] -A FORWARD -j %(bn)s-FORWARD\n'
'[0:0] -A %(bn)s-INPUT -s 0/0 -d 192.168.0.2 -j '
'-I FORWARD 1 -j neutron-filter-top\n'
'-I FORWARD 2 -j %(bn)s-FORWARD\n'
'-I INPUT 1 -j %(bn)s-INPUT\n'
'-I OUTPUT 1 -j neutron-filter-top\n'
'-I OUTPUT 2 -j %(bn)s-OUTPUT\n'
'-I neutron-filter-top 1 -j %(bn)s-local\n'
'-I %(bn)s-INPUT 1 -s 0/0 -d 192.168.0.2 -j '
'%(wrap)s\n'
'COMMIT\n'
'# Completed by iptables_manager\n'
@ -554,26 +577,26 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
raw_dump = RAW_DUMP % IPTABLES_ARG
expected_calls_and_values = [
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(RAW_DUMP + NAT_DUMP + MANGLE_DUMP +
filter_dump_mod),
(mock.call(['iptables-restore', '-n'],
process_input=(filter_dump_mod + MANGLE_DUMP +
NAT_DUMP + RAW_DUMP),
run_as_root=True),
None),
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(RAW_DUMP + NAT_DUMP + MANGLE_DUMP +
FILTER_DUMP),
(mock.call(['iptables-restore', '-n'],
process_input=(FILTER_DUMP + MANGLE_DUMP +
NAT_DUMP + RAW_DUMP),
run_as_root=True),
None),
]
if use_ipv6:
self._extend_with_ip6tables_filter(expected_calls_and_values,
raw_dump + FILTER_DUMP)
FILTER_DUMP + raw_dump)
tools.setup_mock_calls(self.execute, expected_calls_and_values)
@ -606,6 +629,11 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
mangle_dump_mod = (
'# Generated by iptables_manager\n'
'*mangle\n'
':FORWARD - [0:0]\n'
':INPUT - [0:0]\n'
':OUTPUT - [0:0]\n'
':POSTROUTING - [0:0]\n'
':PREROUTING - [0:0]\n'
':%(bn)s-FORWARD - [0:0]\n'
':%(bn)s-INPUT - [0:0]\n'
':%(bn)s-OUTPUT - [0:0]\n'
@ -613,37 +641,37 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
':%(bn)s-PREROUTING - [0:0]\n'
':%(bn)s-mangle - [0:0]\n'
':%(bn)s-mark - [0:0]\n'
'[0:0] -A PREROUTING -j %(bn)s-PREROUTING\n'
'[0:0] -A INPUT -j %(bn)s-INPUT\n'
'[0:0] -A FORWARD -j %(bn)s-FORWARD\n'
'[0:0] -A OUTPUT -j %(bn)s-OUTPUT\n'
'[0:0] -A POSTROUTING -j %(bn)s-POSTROUTING\n'
'[0:0] -A %(bn)s-PREROUTING -j %(bn)s-mark\n'
'[0:0] -A %(bn)s-PREROUTING -j MARK --set-xmark 0x1/%(mark)s\n'
'-I FORWARD 1 -j %(bn)s-FORWARD\n'
'-I INPUT 1 -j %(bn)s-INPUT\n'
'-I OUTPUT 1 -j %(bn)s-OUTPUT\n'
'-I POSTROUTING 1 -j %(bn)s-POSTROUTING\n'
'-I PREROUTING 1 -j %(bn)s-PREROUTING\n'
'-I %(bn)s-PREROUTING 1 -j %(bn)s-mark\n'
'-I %(bn)s-PREROUTING 2 -j MARK --set-xmark 0x1/%(mark)s\n'
'COMMIT\n'
'# Completed by iptables_manager\n' % IPTABLES_ARG)
expected_calls_and_values = [
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(RAW_DUMP + NAT_DUMP + mangle_dump_mod +
FILTER_DUMP),
(mock.call(['iptables-restore', '-n'],
process_input=(FILTER_DUMP + mangle_dump_mod +
NAT_DUMP + RAW_DUMP),
run_as_root=True),
None),
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(RAW_DUMP + NAT_DUMP + MANGLE_DUMP +
FILTER_DUMP),
(mock.call(['iptables-restore', '-n'],
process_input=(FILTER_DUMP + MANGLE_DUMP +
NAT_DUMP + RAW_DUMP),
run_as_root=True),
None),
]
if use_ipv6:
self._extend_with_ip6tables_filter(expected_calls_and_values,
RAW_DUMP + FILTER_DUMP)
FILTER_DUMP + RAW_DUMP)
tools.setup_mock_calls(self.execute, expected_calls_and_values)
@ -678,6 +706,9 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
nat_dump_mod = ('# Generated by iptables_manager\n'
'*nat\n'
':OUTPUT - [0:0]\n'
':POSTROUTING - [0:0]\n'
':PREROUTING - [0:0]\n'
':neutron-postrouting-bottom - [0:0]\n'
':%(bn)s-OUTPUT - [0:0]\n'
':%(bn)s-POSTROUTING - [0:0]\n'
@ -685,43 +716,42 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
':%(bn)s-float-snat - [0:0]\n'
':%(bn)s-nat - [0:0]\n'
':%(bn)s-snat - [0:0]\n'
'[0:0] -A PREROUTING -j %(bn)s-PREROUTING\n'
'[0:0] -A OUTPUT -j %(bn)s-OUTPUT\n'
'[0:0] -A POSTROUTING -j %(bn)s-POSTROUTING\n'
'[0:0] -A POSTROUTING -j neutron-postrouting-bottom\n'
'[0:0] -A neutron-postrouting-bottom -j %(bn)s-snat\n'
'[0:0] -A %(bn)s-snat -j %(bn)s-float-snat\n'
'[0:0] -A %(bn)s-PREROUTING -d 192.168.0.3 -j '
'-I OUTPUT 1 -j %(bn)s-OUTPUT\n'
'-I POSTROUTING 1 -j %(bn)s-POSTROUTING\n'
'-I POSTROUTING 2 -j neutron-postrouting-bottom\n'
'-I PREROUTING 1 -j %(bn)s-PREROUTING\n'
'-I neutron-postrouting-bottom 1 -j %(bn)s-snat\n'
'-I %(bn)s-PREROUTING 1 -d 192.168.0.3 -j '
'%(bn)s-nat\n'
'[0:0] -A %(bn)s-nat -p tcp --dport 8080 -j '
'-I %(bn)s-nat 1 -p tcp --dport 8080 -j '
'REDIRECT --to-port 80\n'
'-I %(bn)s-snat 1 -j %(bn)s-float-snat\n'
'COMMIT\n'
'# Completed by iptables_manager\n'
% IPTABLES_ARG)
'# Completed by iptables_manager\n' % IPTABLES_ARG)
raw_dump = RAW_DUMP % IPTABLES_ARG
expected_calls_and_values = [
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(RAW_DUMP + nat_dump_mod + MANGLE_DUMP +
FILTER_DUMP),
(mock.call(['iptables-restore', '-n'],
process_input=(FILTER_DUMP + MANGLE_DUMP +
nat_dump_mod + RAW_DUMP),
run_as_root=True),
None),
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(RAW_DUMP + nat_dump + MANGLE_DUMP +
FILTER_DUMP),
(mock.call(['iptables-restore', '-n'],
process_input=(FILTER_DUMP + MANGLE_DUMP + nat_dump +
RAW_DUMP),
run_as_root=True),
None),
]
if use_ipv6:
self._extend_with_ip6tables_filter(expected_calls_and_values,
raw_dump + FILTER_DUMP)
FILTER_DUMP + raw_dump)
tools.setup_mock_calls(self.execute, expected_calls_and_values)
@ -760,37 +790,39 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
raw_dump_mod = ('# Generated by iptables_manager\n'
'*raw\n'
':OUTPUT - [0:0]\n'
':PREROUTING - [0:0]\n'
':%(bn)s-OUTPUT - [0:0]\n'
':%(bn)s-PREROUTING - [0:0]\n'
':%(bn)s-raw - [0:0]\n'
'[0:0] -A PREROUTING -j %(bn)s-PREROUTING\n'
'[0:0] -A OUTPUT -j %(bn)s-OUTPUT\n'
'[0:0] -A %(bn)s-PREROUTING -j CT --notrack\n'
'-I OUTPUT 1 -j %(bn)s-OUTPUT\n'
'-I PREROUTING 1 -j %(bn)s-PREROUTING\n'
'-I %(bn)s-PREROUTING 1 -j CT --notrack\n'
'COMMIT\n'
'# Completed by iptables_manager\n'
% IPTABLES_ARG)
expected_calls_and_values = [
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(raw_dump_mod + NAT_DUMP + MANGLE_DUMP +
FILTER_DUMP),
(mock.call(['iptables-restore', '-n'],
process_input=(FILTER_DUMP + MANGLE_DUMP + NAT_DUMP +
raw_dump_mod),
run_as_root=True),
None),
(mock.call(['iptables-save', '-c'],
(mock.call(['iptables-save'],
run_as_root=True),
''),
(mock.call(['iptables-restore', '-c'],
process_input=(RAW_DUMP + NAT_DUMP + MANGLE_DUMP +
FILTER_DUMP),
(mock.call(['iptables-restore', '-n'],
process_input=(FILTER_DUMP + MANGLE_DUMP + NAT_DUMP +
RAW_DUMP),
run_as_root=True),
None),
]
if use_ipv6:
self._extend_with_ip6tables_filter(expected_calls_and_values,
RAW_DUMP + FILTER_DUMP)
FILTER_DUMP + RAW_DUMP)
tools.setup_mock_calls(self.execute, expected_calls_and_values)
@ -1012,43 +1044,6 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase):
def test_get_traffic_counters_with_zero_with_ipv6(self):
self._test_get_traffic_counters_with_zero_helper(True)
def _test_find_last_entry(self, find_str):
filter_list = [':neutron-filter-top - [0:0]',
':%(bn)s-FORWARD - [0:0]',
':%(bn)s-INPUT - [0:0]',
':%(bn)s-local - [0:0]',
':%(wrap)s - [0:0]',
':%(bn)s-OUTPUT - [0:0]',
'[0:0] -A FORWARD -j neutron-filter-top',
'[0:0] -A OUTPUT -j neutron-filter-top'
% IPTABLES_ARG]
filter_map = iptables_manager.make_filter_map(filter_list)
return self.iptables._find_last_entry(filter_map, find_str)
def test_find_last_entry_old_dup(self):
find_str = '-A OUTPUT -j neutron-filter-top'
match_str = '[0:0] -A OUTPUT -j neutron-filter-top'
ret_str = self._test_find_last_entry(find_str)
self.assertEqual(ret_str, match_str)
def test_find_last_entry_none(self):
find_str = 'neutron-filter-NOTFOUND'
ret_str = self._test_find_last_entry(find_str)
self.assertIsNone(ret_str)
def test_make_filter_map_cidr_stripping(self):
filter_rules = ('[0:0] -A OUTPUT -j DROP',
'[0:0] -A INPUT -d 192.168.0.2/32 -j DROP',
'[0:0] -A INPUT -d 1234:31::001F/128 -j DROP',
'OUTPUT - [0:0]')
filter_map = iptables_manager.make_filter_map(filter_rules)
# make sure /128 works without CIDR
self.assertEqual(filter_rules[2],
filter_map['-A INPUT -d 1234:31::001F -j DROP'][0])
# make sure /32 works without CIDR
self.assertEqual(filter_rules[1],
filter_map['-A INPUT -d 192.168.0.2 -j DROP'][0])
class IptablesManagerStateLessTestCase(base.BaseTestCase):

File diff suppressed because it is too large Load Diff