Merge "Make ARP protection commands compatible with "ebtables-nft"" into stable/wallaby

This commit is contained in:
Zuul 2021-04-19 21:37:49 +00:00 committed by Gerrit Code Review
commit 02ce13761b
2 changed files with 36 additions and 69 deletions

View File

@ -73,12 +73,6 @@ def delete_arp_spoofing_protection(vifs):
_delete_arp_spoofing_protection(vifs, current_rules, table='nat', _delete_arp_spoofing_protection(vifs, current_rules, table='nat',
chain='PREROUTING') chain='PREROUTING')
# TODO(haleyb) this can go away in "R" cycle, it's here to cleanup
# old chains in the filter table
current_rules = ebtables(['-L'], table='filter').splitlines()
_delete_arp_spoofing_protection(vifs, current_rules, table='filter',
chain='FORWARD')
def _delete_arp_spoofing_protection(vifs, current_rules, table, chain): def _delete_arp_spoofing_protection(vifs, current_rules, table, chain):
# delete the jump rule and then delete the whole chain # delete the jump rule and then delete the whole chain
@ -92,10 +86,11 @@ def _delete_arp_spoofing_protection(vifs, current_rules, table, chain):
chain=chain) chain=chain)
def _delete_unreferenced_arp_protection(current_vifs, table, chain): @lockutils.synchronized('ebtables')
def delete_unreferenced_arp_protection(current_vifs):
# deletes all jump rules and chains that aren't in current_vifs but match # deletes all jump rules and chains that aren't in current_vifs but match
# the spoof prefix # the spoof prefix
current_rules = ebtables(['-L'], table=table).splitlines() current_rules = ebtables(['-L'], table='nat').splitlines()
to_delete = [] to_delete = []
for line in current_rules: for line in current_rules:
# we're looking to find and turn the following: # we're looking to find and turn the following:
@ -107,19 +102,8 @@ def _delete_unreferenced_arp_protection(current_vifs, table, chain):
to_delete.append(devname) to_delete.append(devname)
LOG.info("Clearing orphaned ARP spoofing entries for devices %s", LOG.info("Clearing orphaned ARP spoofing entries for devices %s",
to_delete) to_delete)
_delete_arp_spoofing_protection(to_delete, current_rules, table=table, _delete_arp_spoofing_protection(to_delete, current_rules, table='nat',
chain=chain) chain='PREROUTING')
@lockutils.synchronized('ebtables')
def delete_unreferenced_arp_protection(current_vifs):
_delete_unreferenced_arp_protection(current_vifs,
table='nat', chain='PREROUTING')
# TODO(haleyb) this can go away in "R" cycle, it's here to cleanup
# old chains in the filter table
_delete_unreferenced_arp_protection(current_vifs,
table='filter', chain='FORWARD')
@lockutils.synchronized('ebtables') @lockutils.synchronized('ebtables')
@ -133,12 +117,17 @@ def _install_arp_spoofing_protection(vif, addresses, current_rules):
vif_chain = chain_name(vif) vif_chain = chain_name(vif)
if not chain_exists(vif_chain, current_rules): if not chain_exists(vif_chain, current_rules):
ebtables(['-N', vif_chain, '-P', 'DROP']) ebtables(['-N', vif_chain, '-P', 'DROP'])
# flush the chain to clear previous accepts. this will cause dropped ARP # Append a default DROP rule at the end of the chain. This will
# packets until the allows are installed, but that's better than leaked # avoid "ebtables-nft" error when listing the chain.
# spoofed packets and ARP can handle losses. ebtables(['-A', vif_chain, '-j', 'DROP'])
ebtables(['-F', vif_chain]) else:
# Flush the chain to clear previous accepts. This will cause dropped
# ARP packets until the allows are installed, but that's better than
# leaked spoofed packets and ARP can handle losses.
ebtables(['-F', vif_chain])
ebtables(['-A', vif_chain, '-j', 'DROP'])
for addr in sorted(addresses): for addr in sorted(addresses):
ebtables(['-A', vif_chain, '-p', 'ARP', '--arp-ip-src', addr, ebtables(['-I', vif_chain, '-p', 'ARP', '--arp-ip-src', addr,
'-j', 'ACCEPT']) '-j', 'ACCEPT'])
# check if jump rule already exists, if not, install it # check if jump rule already exists, if not, install it
if not vif_jump_present(vif, current_rules): if not vif_jump_present(vif, current_rules):
@ -178,17 +167,22 @@ def _install_mac_spoofing_protection(vif, port_details, current_rules):
# mac filter chain for each vif which has a default deny # mac filter chain for each vif which has a default deny
if not chain_exists(vif_chain, current_rules): if not chain_exists(vif_chain, current_rules):
ebtables(['-N', vif_chain, '-P', 'DROP']) ebtables(['-N', vif_chain, '-P', 'DROP'])
# Append a default DROP rule at the end of the chain. This will
# avoid "ebtables-nft" error when listing the chain.
ebtables(['-A', vif_chain, '-j', 'DROP'])
# check if jump rule already exists, if not, install it # check if jump rule already exists, if not, install it
if not _mac_vif_jump_present(vif, current_rules): if not _mac_vif_jump_present(vif, current_rules):
ebtables(['-A', 'PREROUTING', '-i', vif, '-j', vif_chain]) ebtables(['-I', 'PREROUTING', '-i', vif, '-j', vif_chain])
_delete_vif_mac_rules(vif, current_rules)
# we can't just feed all allowed macs at once because we can exceed # we can't just feed all allowed macs at once because we can exceed
# the maximum argument size. limit to 500 per rule. # the maximum argument size. limit to 500 per rule.
for chunk in (mac_addresses[i:i + 500] for chunk in (mac_addresses[i:i + 500]
for i in range(0, len(mac_addresses), 500)): for i in range(0, len(mac_addresses), 500)):
new_rule = ['-A', vif_chain, '-i', vif, new_rule = ['-I', vif_chain, '-i', vif,
'--among-src', ','.join(sorted(chunk)), '-j', 'RETURN'] '--among-src', ','.join(sorted(chunk)), '-j', 'RETURN']
ebtables(new_rule) ebtables(new_rule)
_delete_vif_mac_rules(vif, current_rules)
def _mac_vif_jump_present(vif, current_rules): def _mac_vif_jump_present(vif, current_rules):
@ -227,7 +221,7 @@ NAMESPACE = None
@tenacity.retry( @tenacity.retry(
wait=tenacity.wait_exponential(multiplier=0.02), wait=tenacity.wait_exponential(multiplier=0.02),
retry=tenacity.retry_if_exception(lambda e: e.returncode == 255), retry=tenacity.retry_if_exception(lambda e: e.returncode in [255, 4]),
reraise=True reraise=True
) )
def ebtables(comm, table='nat'): def ebtables(comm, table='nat'):

View File

@ -75,13 +75,19 @@ class TestLinuxBridgeARPSpoofing(base.BaseTestCase):
check_exit_code=True, extra_ok_codes=None, check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True, run_as_root=True, log_fail_as_error=True, run_as_root=True,
privsep_exec=True), privsep_exec=True),
mock.ANY,
mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A', mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A',
'neutronMAC-%s' % vif, '-j', 'DROP'],
check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True, run_as_root=True,
privsep_exec=True),
mock.ANY,
mock.call(['ebtables', '-t', 'nat', '--concurrent', '-I',
'PREROUTING', '-i', vif, '-j', mac_chain], 'PREROUTING', '-i', vif, '-j', mac_chain],
check_exit_code=True, extra_ok_codes=None, check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True, run_as_root=True, log_fail_as_error=True, run_as_root=True,
privsep_exec=True), privsep_exec=True),
mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A', mock.ANY,
mock.call(['ebtables', '-t', 'nat', '--concurrent', '-I',
mac_chain, '-i', vif, mac_chain, '-i', vif,
'--among-src', '%s' % ','.join(sorted(mac_addresses)), '--among-src', '%s' % ','.join(sorted(mac_addresses)),
'-j', 'RETURN'], '-j', 'RETURN'],
@ -89,21 +95,20 @@ class TestLinuxBridgeARPSpoofing(base.BaseTestCase):
log_fail_as_error=True, run_as_root=True, log_fail_as_error=True, run_as_root=True,
privsep_exec=True), privsep_exec=True),
mock.ANY, mock.ANY,
mock.ANY,
mock.call(['ebtables', '-t', 'nat', '--concurrent', '-N', mock.call(['ebtables', '-t', 'nat', '--concurrent', '-N',
spoof_chain, '-P', 'DROP'], spoof_chain, '-P', 'DROP'],
check_exit_code=True, extra_ok_codes=None, check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True, run_as_root=True, log_fail_as_error=True, run_as_root=True,
privsep_exec=True), privsep_exec=True),
mock.call(['ebtables', '-t', 'nat', '--concurrent', '-F', mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A',
spoof_chain], spoof_chain, '-j', 'DROP'],
check_exit_code=True, extra_ok_codes=None, check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True, run_as_root=True, log_fail_as_error=True, run_as_root=True,
privsep_exec=True), privsep_exec=True)
] ]
for addr in sorted(ip_addresses): for addr in sorted(ip_addresses):
expected.extend([ expected.extend([
mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A', mock.call(['ebtables', '-t', 'nat', '--concurrent', '-I',
spoof_chain, '-p', 'ARP', spoof_chain, '-p', 'ARP',
'--arp-ip-src', addr, '-j', 'ACCEPT'], '--arp-ip-src', addr, '-j', 'ACCEPT'],
check_exit_code=True, extra_ok_codes=None, check_exit_code=True, extra_ok_codes=None,
@ -167,38 +172,6 @@ class TestLinuxBridgeARPSpoofing(base.BaseTestCase):
check_exit_code=True, extra_ok_codes=None, check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True, run_as_root=True, log_fail_as_error=True, run_as_root=True,
privsep_exec=True), privsep_exec=True),
mock.call(['ebtables', '-t', 'filter', '--concurrent', '-L'],
check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True, run_as_root=True,
privsep_exec=True),
mock.ANY,
mock.call(['ebtables', '-t', 'filter', '--concurrent', '-D',
'FORWARD', '-i', VIF, '-j', spoof_chain,
'-p', 'ARP'],
check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True, run_as_root=True,
privsep_exec=True),
mock.call(['ebtables', '-t', 'filter', '--concurrent', '-F',
spoof_chain],
check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True, run_as_root=True,
privsep_exec=True),
mock.call(['ebtables', '-t', 'filter', '--concurrent', '-X',
spoof_chain],
check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True, run_as_root=True,
privsep_exec=True),
mock.ANY,
mock.call(['ebtables', '-t', 'filter', '--concurrent', '-F',
mac_chain],
check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True, run_as_root=True,
privsep_exec=True),
mock.call(['ebtables', '-t', 'filter', '--concurrent', '-X',
mac_chain],
check_exit_code=True, extra_ok_codes=None,
log_fail_as_error=True, run_as_root=True,
privsep_exec=True),
] ]
arp_protect.delete_arp_spoofing_protection([VIF]) arp_protect.delete_arp_spoofing_protection([VIF])