Merge "Changes to support FWaaS in a DVR based environment"

This commit is contained in:
Jenkins 2014-09-11 00:28:51 +00:00 committed by Gerrit Code Review
commit 48c40d682e
5 changed files with 148 additions and 58 deletions

View File

@ -733,7 +733,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
for c, r in self.metadata_nat_rules():
ri.iptables_manager.ipv4['nat'].add_rule(c, r)
ri.iptables_manager.apply()
super(L3NATAgent, self).process_router_add(ri)
self.process_router_add(ri)
if self.conf.enable_metadata_proxy:
self._spawn_metadata_proxy(ri.router_id, ri.ns_name)
@ -1202,6 +1202,8 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
root_helper=self.root_helper,
namespace=snat_ns_name,
use_ipv6=self.use_ipv6)
# kicks the FW Agent to add rules for the snat namespace
self.process_router_add(ri)
def external_gateway_added(self, ri, ex_gw_port, interface_name):
if ri.router['distributed']:
@ -1523,6 +1525,8 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager):
device.route.add_gateway(str(fip_2_rtr.ip), table=FIP_RT_TBL)
#setup the NAT rules and chains
self._handle_router_fip_nat_rules(ri, rtr_2_fip_name, 'add_rules')
# kicks the FW Agent to add rules for the IR namespace if configured
self.process_router_add(ri)
def floating_ip_added_dist(self, ri, fip):
"""Add floating IP to FIP namespace."""

View File

@ -140,6 +140,7 @@ class FWaaSL3AgentRpcCallback(api.FWaaSAgentRpcCallbackMixin):
# call into the driver
try:
self.fwaas_driver.__getattribute__(func_name)(
self.conf.agent_mode,
router_info_list,
fw)
if fw['admin_state_up']:
@ -174,7 +175,10 @@ class FWaaSL3AgentRpcCallback(api.FWaaSAgentRpcCallbackMixin):
"""
if fw['status'] == constants.PENDING_DELETE:
try:
self.fwaas_driver.delete_firewall(router_info_list, fw)
self.fwaas_driver.delete_firewall(
self.conf.agent_mode,
router_info_list,
fw)
self.fwplugin_rpc.firewall_deleted(
ctx,
fw['id'])
@ -189,7 +193,10 @@ class FWaaSL3AgentRpcCallback(api.FWaaSAgentRpcCallbackMixin):
else:
# PENDING_UPDATE, PENDING_CREATE, ...
try:
self.fwaas_driver.update_firewall(router_info_list, fw)
self.fwaas_driver.update_firewall(
self.conf.agent_mode,
router_info_list,
fw)
if fw['admin_state_up']:
status = constants.ACTIVE
else:

View File

@ -39,6 +39,10 @@ IPV6 = 'ipv6'
IP_VER_TAG = {IPV4: 'v4',
IPV6: 'v6'}
INTERNAL_DEV_PREFIX = 'qr-'
SNAT_INT_DEV_PREFIX = 'sg-'
ROUTER_2_FIP_DEV_PREFIX = 'rfp-'
class IptablesFwaasDriver(fwaas_base.FwaasDriverBase):
"""IPTables driver for Firewall As A Service."""
@ -46,99 +50,132 @@ class IptablesFwaasDriver(fwaas_base.FwaasDriverBase):
def __init__(self):
LOG.debug(_("Initializing fwaas iptables driver"))
def create_firewall(self, apply_list, firewall):
def create_firewall(self, agent_mode, apply_list, firewall):
LOG.debug(_('Creating firewall %(fw_id)s for tenant %(tid)s)'),
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
try:
if firewall['admin_state_up']:
self._setup_firewall(apply_list, firewall)
self._setup_firewall(agent_mode, apply_list, firewall)
else:
self.apply_default_policy(apply_list, firewall)
self.apply_default_policy(agent_mode, apply_list, firewall)
except (LookupError, RuntimeError):
# catch known library exceptions and raise Fwaas generic exception
LOG.exception(_("Failed to create firewall: %s"), firewall['id'])
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def delete_firewall(self, apply_list, firewall):
def _get_ipt_mgrs_with_if_prefix(self, agent_mode, router_info):
"""Gets the iptables manager along with the if prefix to apply rules.
With DVR we can have differing namespaces depending on which agent
(on Network or Compute node). Also, there is an associated i/f for
each namespace. The iptables on the relevant namespace and matching
i/f are provided. On the Network node we could have both the snat
namespace and a fip so this is provided back as a list - so in that
scenario rules can be applied on both.
"""
if not router_info.router['distributed']:
return [{'ipt': router_info.iptables_manager,
'if_prefix': INTERNAL_DEV_PREFIX}]
ipt_mgrs = []
# TODO(sridar): refactor to get strings to a common location.
if agent_mode == 'dvr_snat':
if router_info.snat_iptables_manager:
ipt_mgrs.append({'ipt': router_info.snat_iptables_manager,
'if_prefix': SNAT_INT_DEV_PREFIX})
if router_info.dist_fip_count:
# handle the fip case on n/w or compute node.
ipt_mgrs.append({'ipt': router_info.iptables_manager,
'if_prefix': ROUTER_2_FIP_DEV_PREFIX})
return ipt_mgrs
def delete_firewall(self, agent_mode, apply_list, firewall):
LOG.debug(_('Deleting firewall %(fw_id)s for tenant %(tid)s)'),
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
fwid = firewall['id']
try:
for router_info in apply_list:
ipt_mgr = router_info.iptables_manager
self._remove_chains(fwid, ipt_mgr)
self._remove_default_chains(ipt_mgr)
# apply the changes immediately (no defer in firewall path)
ipt_mgr.defer_apply_off()
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, router_info)
for ipt_if_prefix in ipt_if_prefix_list:
ipt_mgr = ipt_if_prefix['ipt']
self._remove_chains(fwid, ipt_mgr)
self._remove_default_chains(ipt_mgr)
# apply the changes immediately (no defer in firewall path)
ipt_mgr.defer_apply_off()
except (LookupError, RuntimeError):
# catch known library exceptions and raise Fwaas generic exception
LOG.exception(_("Failed to delete firewall: %s"), fwid)
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def update_firewall(self, apply_list, firewall):
def update_firewall(self, agent_mode, apply_list, firewall):
LOG.debug(_('Updating firewall %(fw_id)s for tenant %(tid)s)'),
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
try:
if firewall['admin_state_up']:
self._setup_firewall(apply_list, firewall)
self._setup_firewall(agent_mode, apply_list, firewall)
else:
self.apply_default_policy(apply_list, firewall)
self.apply_default_policy(agent_mode, apply_list, firewall)
except (LookupError, RuntimeError):
# catch known library exceptions and raise Fwaas generic exception
LOG.exception(_("Failed to update firewall: %s"), firewall['id'])
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def apply_default_policy(self, apply_list, firewall):
def apply_default_policy(self, agent_mode, apply_list, firewall):
LOG.debug(_('Applying firewall %(fw_id)s for tenant %(tid)s)'),
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
fwid = firewall['id']
try:
for router_info in apply_list:
ipt_mgr = router_info.iptables_manager
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, router_info)
for ipt_if_prefix in ipt_if_prefix_list:
# the following only updates local memory; no hole in FW
ipt_mgr = ipt_if_prefix['ipt']
self._remove_chains(fwid, ipt_mgr)
self._remove_default_chains(ipt_mgr)
# the following only updates local memory; no hole in FW
self._remove_chains(fwid, ipt_mgr)
self._remove_default_chains(ipt_mgr)
# create default 'DROP ALL' policy chain
self._add_default_policy_chain_v4v6(ipt_mgr)
self._enable_policy_chain(fwid, ipt_if_prefix)
# create default 'DROP ALL' policy chain
self._add_default_policy_chain_v4v6(ipt_mgr)
self._enable_policy_chain(fwid, ipt_mgr)
# apply the changes immediately (no defer in firewall path)
ipt_mgr.defer_apply_off()
# apply the changes immediately (no defer in firewall path)
ipt_mgr.defer_apply_off()
except (LookupError, RuntimeError):
# catch known library exceptions and raise Fwaas generic exception
LOG.exception(_("Failed to apply default policy on firewall: %s"),
fwid)
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def _setup_firewall(self, apply_list, firewall):
def _setup_firewall(self, agent_mode, apply_list, firewall):
fwid = firewall['id']
for router_info in apply_list:
ipt_mgr = router_info.iptables_manager
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, router_info)
for ipt_if_prefix in ipt_if_prefix_list:
ipt_mgr = ipt_if_prefix['ipt']
# the following only updates local memory; no hole in FW
self._remove_chains(fwid, ipt_mgr)
self._remove_default_chains(ipt_mgr)
# the following only updates local memory; no hole in FW
self._remove_chains(fwid, ipt_mgr)
self._remove_default_chains(ipt_mgr)
# create default 'DROP ALL' policy chain
self._add_default_policy_chain_v4v6(ipt_mgr)
#create chain based on configured policy
self._setup_chains(firewall, ipt_if_prefix)
# create default 'DROP ALL' policy chain
self._add_default_policy_chain_v4v6(ipt_mgr)
#create chain based on configured policy
self._setup_chains(firewall, ipt_mgr)
# apply the changes immediately (no defer in firewall path)
ipt_mgr.defer_apply_off()
# apply the changes immediately (no defer in firewall path)
ipt_mgr.defer_apply_off()
def _get_chain_name(self, fwid, ver, direction):
return '%s%s%s' % (CHAIN_NAME_PREFIX[direction],
IP_VER_TAG[ver],
fwid)
def _setup_chains(self, firewall, ipt_mgr):
def _setup_chains(self, firewall, ipt_if_prefix):
"""Create Fwaas chain using the rules in the policy
"""
fw_rules_list = firewall['firewall_rule_list']
fwid = firewall['id']
ipt_mgr = ipt_if_prefix['ipt']
#default rules for invalid packets and established sessions
invalid_rule = self._drop_invalid_packets_rule()
@ -170,7 +207,7 @@ class IptablesFwaasDriver(fwaas_base.FwaasDriverBase):
ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
table.add_rule(ichain_name, iptbl_rule)
table.add_rule(ochain_name, iptbl_rule)
self._enable_policy_chain(fwid, ipt_mgr)
self._enable_policy_chain(fwid, ipt_if_prefix)
def _remove_default_chains(self, nsid):
"""Remove fwaas default policy chain."""
@ -204,8 +241,10 @@ class IptablesFwaasDriver(fwaas_base.FwaasDriverBase):
for rule in rules:
table.add_rule(chain_name, rule)
def _enable_policy_chain(self, fwid, ipt_mgr):
def _enable_policy_chain(self, fwid, ipt_if_prefix):
bname = iptables_manager.binary_name
ipt_mgr = ipt_if_prefix['ipt']
if_prefix = ipt_if_prefix['if_prefix']
for (ver, tbl) in [(IPV4, ipt_mgr.ipv4['filter']),
(IPV6, ipt_mgr.ipv6['filter'])]:
@ -213,20 +252,20 @@ class IptablesFwaasDriver(fwaas_base.FwaasDriverBase):
chain_name = self._get_chain_name(fwid, ver, direction)
chain_name = iptables_manager.get_chain_name(chain_name)
if chain_name in tbl.chains:
jump_rule = ['%s qr-+ -j %s-%s' % (IPTABLES_DIR[direction],
bname, chain_name)]
self._add_rules_to_chain(ipt_mgr, ver, 'FORWARD',
jump_rule)
jump_rule = ['%s %s+ -j %s-%s' % (IPTABLES_DIR[direction],
if_prefix, bname, chain_name)]
self._add_rules_to_chain(ipt_mgr,
ver, 'FORWARD', jump_rule)
#jump to DROP_ALL policy
chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
jump_rule = ['-o qr-+ -j %s-%s' % (bname, chain_name)]
jump_rule = ['-o %s+ -j %s-%s' % (if_prefix, bname, chain_name)]
self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)
#jump to DROP_ALL policy
chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
jump_rule = ['-i qr-+ -j %s-%s' % (bname, chain_name)]
jump_rule = ['-i %s+ -j %s-%s' % (if_prefix, bname, chain_name)]
self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)

View File

@ -249,6 +249,7 @@ class TestFwaasL3AgentRpcCallback(base.BaseTestCase):
'admin_state_up': True}]
fake_router = {'id': 1111, 'tenant_id': 2}
self.api.plugin_rpc = mock.Mock()
agent_mode = 'legacy'
ri = mock.Mock()
ri.router = fake_router
routers = [ri.router]
@ -280,6 +281,7 @@ class TestFwaasL3AgentRpcCallback(base.BaseTestCase):
ri.router['tenant_id'])
mock_get_firewalls_for_tenant.assert_called_once_with(ctx)
mock_driver_update_firewall.assert_called_once_with(
agent_mode,
routers,
fake_firewall_list[0])
@ -292,6 +294,7 @@ class TestFwaasL3AgentRpcCallback(base.BaseTestCase):
fake_firewall_list = [{'id': 0, 'tenant_id': 1,
'status': constants.PENDING_DELETE}]
fake_router = {'id': 1111, 'tenant_id': 2}
agent_mode = 'legacy'
self.api.plugin_rpc = mock.Mock()
ri = mock.Mock()
ri.router = fake_router
@ -324,6 +327,7 @@ class TestFwaasL3AgentRpcCallback(base.BaseTestCase):
ri.router['tenant_id'])
mock_get_firewalls_for_tenant.assert_called_once_with(ctx)
mock_driver_delete_firewall.assert_called_once_with(
agent_mode,
routers,
fake_firewall_list[0])

View File

@ -90,10 +90,12 @@ class IptablesFwaasTestCase(base.BaseTestCase):
'firewall_rule_list': rule_list}
return fw_inst
def _fake_apply_list(self, router_count=1):
def _fake_apply_list(self, router_count=1, distributed=False,
distributed_mode=None):
apply_list = []
while router_count > 0:
iptables_inst = mock.Mock()
router_inst = {'distributed': distributed}
v4filter_inst = mock.Mock()
v6filter_inst = mock.Mock()
v4filter_inst.chains = []
@ -102,15 +104,29 @@ class IptablesFwaasTestCase(base.BaseTestCase):
iptables_inst.ipv6 = {'filter': v6filter_inst}
router_info_inst = mock.Mock()
router_info_inst.iptables_manager = iptables_inst
router_info_inst.snat_iptables_manager = iptables_inst
if distributed_mode == 'dvr':
router_info_inst.dist_fip_count = 1
router_info_inst.router = router_inst
apply_list.append(router_info_inst)
router_count -= 1
return apply_list
def _setup_firewall_with_rules(self, func, router_count=1):
apply_list = self._fake_apply_list(router_count=router_count)
def _setup_firewall_with_rules(self, func, router_count=1,
distributed=False, distributed_mode=None):
apply_list = self._fake_apply_list(router_count=router_count,
distributed=distributed, distributed_mode=distributed_mode)
rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list)
firewall = self._fake_firewall(rule_list)
func(apply_list, firewall)
if distributed:
if distributed_mode == 'dvr_snat':
if_prefix = 'sg-+'
if distributed_mode == 'dvr':
if_prefix = 'rfp-+'
else:
if_prefix = 'qr-+'
distributed_mode = 'legacy'
func(distributed_mode, apply_list, firewall)
invalid_rule = '-m state --state INVALID -j DROP'
est_rule = '-m state --state ESTABLISHED,RELATED -j ACCEPT'
rule1 = '-p tcp --dport 80 -s 10.24.4.2 -j ACCEPT'
@ -138,19 +154,23 @@ class IptablesFwaasTestCase(base.BaseTestCase):
mock.call.add_rule(ingress_chain, rule2),
mock.call.add_rule(egress_chain, rule2),
mock.call.add_rule('FORWARD',
'-o qr-+ -j %s' % ipt_mgr_ichain),
'-o %s -j %s' % (if_prefix,
ipt_mgr_ichain)),
mock.call.add_rule('FORWARD',
'-i qr-+ -j %s' % ipt_mgr_echain),
'-i %s -j %s' % (if_prefix,
ipt_mgr_echain)),
mock.call.add_rule('FORWARD',
'-o qr-+ -j %s-fwaas-defau' % bname),
'-o %s -j %s-fwaas-defau' % (if_prefix,
bname)),
mock.call.add_rule('FORWARD',
'-i qr-+ -j %s-fwaas-defau' % bname)]
'-i %s -j %s-fwaas-defau' % (if_prefix,
bname))]
v4filter_inst.assert_has_calls(calls)
def test_create_firewall_no_rules(self):
apply_list = self._fake_apply_list()
firewall = self._fake_firewall_no_rule()
self.firewall.create_firewall(apply_list, firewall)
self.firewall.create_firewall('legacy', apply_list, firewall)
invalid_rule = '-m state --state INVALID -j DROP'
est_rule = '-m state --state ESTABLISHED,RELATED -j ACCEPT'
bname = fwaas.iptables_manager.binary_name
@ -195,7 +215,7 @@ class IptablesFwaasTestCase(base.BaseTestCase):
def test_delete_firewall(self):
apply_list = self._fake_apply_list()
firewall = self._fake_firewall_no_rule()
self.firewall.delete_firewall(apply_list, firewall)
self.firewall.delete_firewall('legacy', apply_list, firewall)
ingress_chain = 'iv4%s' % firewall['id']
egress_chain = 'ov4%s' % firewall['id']
calls = [mock.call.ensure_remove_chain(ingress_chain),
@ -207,10 +227,26 @@ class IptablesFwaasTestCase(base.BaseTestCase):
apply_list = self._fake_apply_list()
rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list)
firewall = self._fake_firewall_with_admin_down(rule_list)
self.firewall.create_firewall(apply_list, firewall)
self.firewall.create_firewall('legacy', apply_list, firewall)
calls = [mock.call.ensure_remove_chain('iv4fake-fw-uuid'),
mock.call.ensure_remove_chain('ov4fake-fw-uuid'),
mock.call.ensure_remove_chain('fwaas-default-policy'),
mock.call.add_chain('fwaas-default-policy'),
mock.call.add_rule('fwaas-default-policy', '-j DROP')]
apply_list[0].iptables_manager.ipv4['filter'].assert_has_calls(calls)
def test_create_firewall_with_rules_dvr_snat(self):
self._setup_firewall_with_rules(self.firewall.create_firewall,
distributed=True, distributed_mode='dvr_snat')
def test_update_firewall_with_rules_dvr_snat(self):
self._setup_firewall_with_rules(self.firewall.update_firewall,
distributed=True, distributed_mode='dvr_snat')
def test_create_firewall_with_rules_dvr(self):
self._setup_firewall_with_rules(self.firewall.create_firewall,
distributed=True, distributed_mode='dvr')
def test_update_firewall_with_rules_dvr(self):
self._setup_firewall_with_rules(self.firewall.update_firewall,
distributed=True, distributed_mode='dvr')