]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Changes to support FWaaS in a DVR based environment
authorSridar Kandaswamy <skandasw@cisco.com>
Mon, 11 Aug 2014 19:39:13 +0000 (12:39 -0700)
committerSridar Kandaswamy <skandasw@cisco.com>
Tue, 9 Sep 2014 07:44:53 +0000 (00:44 -0700)
Implementation of Spec to address the changes required for FWaaS
to work with DVR to handle:
* Perimeter Firewall support on N - S traffic
* Ensure that E - W DVR traffic is not broken.

DocImpact

Change-Id: Iba78e534ccf347ea6270aabc939a489dd40a7b9e
Implements: blueprint neutron-dvr-fwaas

neutron/agent/l3_agent.py
neutron/services/firewall/agents/l3reference/firewall_l3_agent.py
neutron/services/firewall/drivers/linux/iptables_fwaas.py
neutron/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_agent.py
neutron/tests/unit/services/firewall/drivers/linux/test_iptables_fwaas.py

index 11fcbdbf2c80adfbb62daa8bf25a2d06c60b02f2..fdde3c9b6b71c48f2af41013158ef11bea1e4145 100644 (file)
@@ -732,7 +732,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)
 
@@ -1198,6 +1198,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']:
@@ -1520,6 +1522,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."""
index cfcdb4b17db4336dc1ab29ac13dd77df0c0c2578..689209419a5afff6040fb8316a04751365d343ae 100644 (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:
index 8dc3fd9f6f82097ac991e4cc4363034d84efcf0e..1a04a77bc9a515994bfcd58828d8d89e8e5642a7 100644 (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
-
-                # 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_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:
+                    # 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)
+
+                    # create default 'DROP ALL' policy chain
+                    self._add_default_policy_chain_v4v6(ipt_mgr)
+                    self._enable_policy_chain(fwid, ipt_if_prefix)
+
+                    # 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
-
-            # the following only updates local memory; no hole in FW
-            self._remove_chains(fwid, ipt_mgr)
-            self._remove_default_chains(ipt_mgr)
+            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)
 
-            # 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)
+                # 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)
 
-            # 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)
 
index a69ef54a9a64670a924bb42bb71dbd34a98a299a..113377b462a28601fd729f56117bbda88a62d24f 100644 (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])
 
index 472586547c5e39ab91eaf5ccc190c0d6f14af540..29f532220bba82dc5b4be8b22e20db2313005a48 100644 (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')