From c76164c058a0cfeee3eb46b523a9ad012f93dd51 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Fri, 3 Mar 2017 11:18:28 -0800 Subject: [PATCH] Move conntrack zones to IPTablesFirewall The regular IPTablesFirewall needs zones to support safely clearly conntrack entries. In order to support the single bridge use case, the conntrack manager had to be refactored slightly to allow zones to be either unique to ports or unique to networks. Since all ports in a network share a bridge in the IPTablesDriver use case, a zone per port cannot be used since there is no way to distinguish which zone traffic should be checked against when traffic enters the bridge from outside the system. A zone per network is adequate for the single bridge per network solution since it implicitly does not suffer from the double-bridge cross in a single network that led to per port usage in OVS.[1] This had to adjust the functional firewall tests to use the correct bridge name now that it's relevant in the non hybrid IPTables case. 1. Ibe9e49653b2a280ea72cb95c2da64cd94c7739da Closes-Bug: #1668958 Closes-Bug: #1657260 Change-Id: Ie88237d3fe4807b712a7ec61eb932748c38952cc --- neutron/agent/linux/ip_conntrack.py | 46 ++++--- neutron/agent/linux/iptables_firewall.py | 78 +++++++----- neutron/tests/common/conn_testers.py | 11 +- .../tests/functional/agent/test_firewall.py | 19 ++- .../unit/agent/linux/test_ip_conntrack.py | 5 +- .../agent/linux/test_iptables_firewall.py | 65 ++++++++-- .../unit/agent/test_securitygroups_rpc.py | 120 +++++++++++++----- 7 files changed, 236 insertions(+), 108 deletions(-) diff --git a/neutron/agent/linux/ip_conntrack.py b/neutron/agent/linux/ip_conntrack.py index 9549d5baf2b..33c08678184 100644 --- a/neutron/agent/linux/ip_conntrack.py +++ b/neutron/agent/linux/ip_conntrack.py @@ -29,14 +29,13 @@ MAX_CONNTRACK_ZONES = 65535 @lockutils.synchronized('conntrack') def get_conntrack(get_rules_for_table_func, filtered_ports, unfiltered_ports, - execute=None, namespace=None): - + execute=None, namespace=None, zone_per_port=False): try: return CONTRACK_MGRS[namespace] except KeyError: ipconntrack = IpConntrackManager(get_rules_for_table_func, filtered_ports, unfiltered_ports, - execute, namespace) + execute, namespace, zone_per_port) CONTRACK_MGRS[namespace] = ipconntrack return CONTRACK_MGRS[namespace] @@ -45,12 +44,14 @@ class IpConntrackManager(object): """Smart wrapper for ip conntrack.""" def __init__(self, get_rules_for_table_func, filtered_ports, - unfiltered_ports, execute=None, namespace=None): + unfiltered_ports, execute=None, namespace=None, + zone_per_port=False): self.get_rules_for_table_func = get_rules_for_table_func self.execute = execute or linux_utils.execute self.namespace = namespace self.filtered_ports = filtered_ports self.unfiltered_ports = unfiltered_ports + self.zone_per_port = zone_per_port # zone per port vs per network self._populate_initial_zone_map() @staticmethod @@ -74,8 +75,7 @@ class IpConntrackManager(object): cmd = self._generate_conntrack_cmd_by_rule(rule, self.namespace) ethertype = rule.get('ethertype') for device_info in device_info_list: - zone_id = self._device_zone_map.get( - self._port_key(device_info['device']), None) + zone_id = self.get_device_zone(device_info, create=False) if not zone_id: LOG.debug("No zone for device %(dev)s. Will not try to " "clear conntrack state. Zone map: %(zm)s", @@ -139,26 +139,30 @@ class IpConntrackManager(object): self._device_zone_map[short_port_id] = int(match.group('zone')) LOG.debug("Populated conntrack zone map: %s", self._device_zone_map) - @staticmethod - def _port_key(port_id): - # we have to key the device_zone_map based on the fragment of the port + def _device_key(self, port): + # we have to key the device_zone_map based on the fragment of the # UUID that shows up in the interface name. This is because the initial # map is populated strictly based on interface names that we don't know # the full UUID of. - return port_id[:(n_const.LINUX_DEV_LEN - - n_const.LINUX_DEV_PREFIX_LEN)] + if self.zone_per_port: + identifier = port['device'][n_const.LINUX_DEV_PREFIX_LEN:] + else: + identifier = port['network_id'] + return identifier[:(n_const.LINUX_DEV_LEN - + n_const.LINUX_DEV_PREFIX_LEN)] - def get_device_zone(self, port_id): - short_port_id = self._port_key(port_id) + def get_device_zone(self, port, create=True): + device_key = self._device_key(port) try: - return self._device_zone_map[short_port_id] + return self._device_zone_map[device_key] except KeyError: - return self._generate_device_zone(short_port_id) + if create: + return self._generate_device_zone(device_key) def _free_zones_from_removed_ports(self): """Clears any entries from the zone map of removed ports.""" existing_ports = [ - self._port_key(port['device']) + self._device_key(port) for port in (list(self.filtered_ports.values()) + list(self.unfiltered_ports.values())) ] @@ -166,7 +170,7 @@ class IpConntrackManager(object): for dev in removed: self._device_zone_map.pop(dev, None) - def _generate_device_zone(self, short_port_id): + def _generate_device_zone(self, short_device_id): """Generates a unique conntrack zone for the passed in ID.""" try: zone = self._find_open_zone() @@ -175,10 +179,10 @@ class IpConntrackManager(object): self._free_zones_from_removed_ports() zone = self._find_open_zone() - self._device_zone_map[short_port_id] = zone - LOG.debug("Assigned CT zone %(z)s to port %(dev)s.", - {'z': zone, 'dev': short_port_id}) - return self._device_zone_map[short_port_id] + self._device_zone_map[short_device_id] = zone + LOG.debug("Assigned CT zone %(z)s to device %(dev)s.", + {'z': zone, 'dev': short_device_id}) + return self._device_zone_map[short_device_id] def _find_open_zone(self): # call set to dedup because old ports may be mapped to the same zone. diff --git a/neutron/agent/linux/iptables_firewall.py b/neutron/agent/linux/iptables_firewall.py index daa400d23d7..82ae44907f7 100644 --- a/neutron/agent/linux/iptables_firewall.py +++ b/neutron/agent/linux/iptables_firewall.py @@ -57,6 +57,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver): """Driver which enforces security groups through iptables rules.""" IPTABLES_DIRECTION = {firewall.INGRESS_DIRECTION: 'physdev-out', firewall.EGRESS_DIRECTION: 'physdev-in'} + CONNTRACK_ZONE_PER_PORT = False def __init__(self, namespace=None): self.iptables = iptables_manager.IptablesManager(state_less=True, @@ -70,7 +71,8 @@ class IptablesFirewallDriver(firewall.FirewallDriver): self.unfiltered_ports = {} self.ipconntrack = ip_conntrack.get_conntrack( self.iptables.get_rules_for_table, self.filtered_ports, - self.unfiltered_ports, namespace=namespace) + self.unfiltered_ports, namespace=namespace, + zone_per_port=self.CONNTRACK_ZONE_PER_PORT) self._add_fallback_chain_v4v6() self._defer_apply = False self._pre_defer_filtered_ports = None @@ -204,6 +206,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver): # agent restarts and don't cause unnecessary rule differences for pname in sorted(ports): port = ports[pname] + self._add_conntrack_jump(port) self._setup_chain(port, firewall.INGRESS_DIRECTION) self._setup_chain(port, firewall.EGRESS_DIRECTION) self.iptables.ipv4['filter'].add_rule(SG_CHAIN, '-j ACCEPT') @@ -224,6 +227,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver): self._remove_chain(port, firewall.INGRESS_DIRECTION) self._remove_chain(port, firewall.EGRESS_DIRECTION) self._remove_chain(port, SPOOF_FILTER) + self._remove_conntrack_jump(port) for port in unfiltered_ports.values(): self._remove_rule_port_sec(port, firewall.INGRESS_DIRECTION) self._remove_rule_port_sec(port, firewall.EGRESS_DIRECTION) @@ -325,6 +329,43 @@ class IptablesFirewallDriver(firewall.FirewallDriver): self._add_rules_to_chain_v4v6('INPUT', jump_rule, jump_rule, comment=ic.INPUT_TO_SG) + def _get_br_device_name(self, port): + return ('brq' + port['network_id'])[:n_const.LINUX_DEV_LEN] + + def _get_jump_rules(self, port): + zone = self.ipconntrack.get_device_zone(port) + br_dev = self._get_br_device_name(port) + port_dev = self._get_device_name(port) + # match by interface for bridge input + match_interface = '-i %s' + match_physdev = '-m physdev --physdev-in %s' + # comment to prevent duplicate warnings for different devices using + # same bridge. truncate start to remove prefixes + comment = '-m comment --comment "Set zone for %s"' % port['device'][4:] + rules = [] + for dev, match in ((br_dev, match_physdev), (br_dev, match_interface), + (port_dev, match_physdev)): + match = match % dev + rule = '%s %s -j CT --zone %s' % (match, comment, zone) + rules.append(rule) + return rules + + def _add_conntrack_jump(self, port): + for jump_rule in self._get_jump_rules(port): + self._add_raw_rule('PREROUTING', jump_rule) + + def _remove_conntrack_jump(self, port): + for jump_rule in self._get_jump_rules(port): + self._remove_raw_rule('PREROUTING', jump_rule) + + def _add_raw_rule(self, chain, rule, comment=None): + self.iptables.ipv4['raw'].add_rule(chain, rule, comment=comment) + self.iptables.ipv6['raw'].add_rule(chain, rule, comment=comment) + + def _remove_raw_rule(self, chain, rule): + self.iptables.ipv4['raw'].remove_rule(chain, rule) + self.iptables.ipv6['raw'].remove_rule(chain, rule) + def _split_sgr_by_ethertype(self, security_group_rules): ipv4_sg_rules = [] ipv6_sg_rules = [] @@ -828,6 +869,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver): class OVSHybridIptablesFirewallDriver(IptablesFirewallDriver): OVS_HYBRID_PLUG_REQUIRED = True + CONNTRACK_ZONE_PER_PORT = True def _port_chain_name(self, port, direction): return iptables_manager.get_chain_name( @@ -838,37 +880,3 @@ class OVSHybridIptablesFirewallDriver(IptablesFirewallDriver): def _get_device_name(self, port): return get_hybrid_port_name(port['device']) - - def _get_jump_rule(self, port, direction): - if direction == firewall.INGRESS_DIRECTION: - device = self._get_br_device_name(port) - else: - device = self._get_device_name(port) - jump_rule = '-m physdev --physdev-in %s -j CT --zone %s' % ( - device, self.ipconntrack.get_device_zone( - port['device'])) - return jump_rule - - def _add_raw_chain_rules(self, port, direction): - jump_rule = self._get_jump_rule(port, direction) - self.iptables.ipv4['raw'].add_rule('PREROUTING', jump_rule) - self.iptables.ipv6['raw'].add_rule('PREROUTING', jump_rule) - - def _remove_raw_chain_rules(self, port, direction): - jump_rule = self._get_jump_rule(port, direction) - self.iptables.ipv4['raw'].remove_rule('PREROUTING', jump_rule) - self.iptables.ipv6['raw'].remove_rule('PREROUTING', jump_rule) - - def _add_chain(self, port, direction): - super(OVSHybridIptablesFirewallDriver, self)._add_chain(port, - direction) - if direction in [firewall.INGRESS_DIRECTION, - firewall.EGRESS_DIRECTION]: - self._add_raw_chain_rules(port, direction) - - def _remove_chain(self, port, direction): - super(OVSHybridIptablesFirewallDriver, self)._remove_chain(port, - direction) - if direction in [firewall.INGRESS_DIRECTION, - firewall.EGRESS_DIRECTION]: - self._remove_raw_chain_rules(port, direction) diff --git a/neutron/tests/common/conn_testers.py b/neutron/tests/common/conn_testers.py index 5b5973fde2f..ada66c49567 100644 --- a/neutron/tests/common/conn_testers.py +++ b/neutron/tests/common/conn_testers.py @@ -513,9 +513,18 @@ class LinuxBridgeConnectionTester(ConnectionTester): """ + def __init__(self, *args, **kwargs): + self.bridge_name = kwargs.pop('bridge_name', None) + super(LinuxBridgeConnectionTester, self).__init__(*args, **kwargs) + def _setUp(self): super(LinuxBridgeConnectionTester, self)._setUp() - self.bridge = self.useFixture(net_helpers.LinuxBridgeFixture()).bridge + bridge_args = {} + if self.bridge_name: + bridge_args = {'prefix': self.bridge_name, + 'prefix_is_full_name': True} + self.bridge = self.useFixture( + net_helpers.LinuxBridgeFixture(**bridge_args)).bridge machines = self.useFixture( machine_fixtures.PeerMachines( self.bridge, self.ip_cidr)).machines diff --git a/neutron/tests/functional/agent/test_firewall.py b/neutron/tests/functional/agent/test_firewall.py index fcc5e6c4a73..6c2604db817 100644 --- a/neutron/tests/functional/agent/test_firewall.py +++ b/neutron/tests/functional/agent/test_firewall.py @@ -24,12 +24,14 @@ import netaddr from neutron_lib import constants from oslo_config import cfg from oslo_log import log as logging +from oslo_utils import uuidutils import testscenarios from neutron.agent import firewall from neutron.agent.linux import iptables_firewall from neutron.agent.linux import openvswitch_firewall from neutron.cmd.sanity import checks +from neutron.common import constants as n_const from neutron.conf.agent import securitygroups_rpc as security_config from neutron.tests.common import conn_testers from neutron.tests.common import helpers @@ -95,6 +97,7 @@ class BaseFirewallTestCase(base.BaseSudoTestCase): def setUp(self): security_config.register_securitygroups_opts() + self.net_id = uuidutils.generate_uuid() super(BaseFirewallTestCase, self).setUp() self.tester, self.firewall = getattr(self, self.initialize)() if self.firewall_name == "openvswitch": @@ -103,7 +106,8 @@ class BaseFirewallTestCase(base.BaseSudoTestCase): self.tester.vm_port_id, [self.tester.vm_ip_address], self.tester.vm_mac_address, - [self.FAKE_SECURITY_GROUP_ID]) + [self.FAKE_SECURITY_GROUP_ID], + self.net_id) # FIXME(jlibosva): We should consider to call prepare_port_filter with # deferred bridge depending on its performance self.firewall.prepare_port_filter(self.src_port_desc) @@ -111,8 +115,10 @@ class BaseFirewallTestCase(base.BaseSudoTestCase): def initialize_iptables(self): cfg.CONF.set_override('enable_ipset', self.enable_ipset, 'SECURITYGROUP') + br_name = ('brq' + self.net_id)[:n_const.LINUX_DEV_LEN] tester = self.useFixture( - conn_testers.LinuxBridgeConnectionTester(self.ip_cidr)) + conn_testers.LinuxBridgeConnectionTester(self.ip_cidr, + bridge_name=br_name)) firewall_drv = iptables_firewall.IptablesFirewallDriver( namespace=tester.bridge_namespace) return tester, firewall_drv @@ -140,7 +146,8 @@ class BaseFirewallTestCase(base.BaseSudoTestCase): self.tester.set_peer_tag(vlan) @staticmethod - def _create_port_description(port_id, ip_addresses, mac_address, sg_ids): + def _create_port_description(port_id, ip_addresses, mac_address, sg_ids, + net_id): return {'admin_state_up': True, 'device': port_id, 'device_owner': DEVICE_OWNER_COMPUTE, @@ -148,7 +155,8 @@ class BaseFirewallTestCase(base.BaseSudoTestCase): 'mac_address': mac_address, 'port_security_enabled': True, 'security_groups': sg_ids, - 'status': 'ACTIVE'} + 'status': 'ACTIVE', + 'network_id': net_id} def _apply_security_group_rules(self, sg_id, sg_rules): with self.firewall.defer_apply(): @@ -539,7 +547,8 @@ class FirewallTestCase(BaseFirewallTestCase): self.tester.peer_port_id, [self.tester.peer_ip_address], self.tester.peer_mac_address, - [remote_sg_id]) + [remote_sg_id], + self.net_id) vm_sg_members = {'IPv4': [self.tester.peer_ip_address]} peer_sg_rules = [{'ethertype': 'IPv4', 'direction': 'egress', diff --git a/neutron/tests/unit/agent/linux/test_ip_conntrack.py b/neutron/tests/unit/agent/linux/test_ip_conntrack.py index 3771d9ae7cc..163b06dc2c9 100644 --- a/neutron/tests/unit/agent/linux/test_ip_conntrack.py +++ b/neutron/tests/unit/agent/linux/test_ip_conntrack.py @@ -27,14 +27,15 @@ class IPConntrackTestCase(base.BaseTestCase): self.unfiltered_port = {} self.mgr = ip_conntrack.IpConntrackManager( self._get_rule_for_table, self.filtered_port, - self.unfiltered_port, self.execute) + self.unfiltered_port, self.execute, + zone_per_port=True) def _get_rule_for_table(self, table): return ['test --physdev-in tapdevice -j CT --zone 100'] def test_delete_conntrack_state_dedupes(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress'} - dev_info = {'device': 'device', 'fixed_ips': ['1.2.3.4']} + dev_info = {'device': 'tapdevice', 'fixed_ips': ['1.2.3.4']} dev_info_list = [dev_info for _ in range(10)] self.mgr._delete_conntrack_state(dev_info_list, rule) self.assertEqual(1, len(self.execute.mock_calls)) diff --git a/neutron/tests/unit/agent/linux/test_iptables_firewall.py b/neutron/tests/unit/agent/linux/test_iptables_firewall.py index 06f28e5e0cf..e3b862338dd 100644 --- a/neutron/tests/unit/agent/linux/test_iptables_firewall.py +++ b/neutron/tests/unit/agent/linux/test_iptables_firewall.py @@ -126,6 +126,12 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): 'sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP), mock.call.add_chain('sg-chain'), + mock.call.add_rule('PREROUTING', mock.ANY, # zone set + comment=None), + mock.call.add_rule('PREROUTING', mock.ANY, # zone set + comment=None), + mock.call.add_rule('PREROUTING', mock.ANY, # zone set + comment=None), mock.call.add_chain('ifake_dev'), mock.call.add_rule('FORWARD', '-m physdev --physdev-out tapfake_dev ' @@ -1005,6 +1011,12 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): '-j DROP', comment=ic.UNMATCH_DROP), mock.call.add_chain('sg-chain'), + mock.call.add_rule('PREROUTING', mock.ANY, # zone set + comment=None), + mock.call.add_rule('PREROUTING', mock.ANY, # zone set + comment=None), + mock.call.add_rule('PREROUTING', mock.ANY, # zone set + comment=None), mock.call.add_chain('ifake_dev'), mock.call.add_rule('FORWARD', '-m physdev --physdev-out tapfake_dev ' @@ -1127,7 +1139,7 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): 'protocol': protocol}] with mock.patch.dict(self.firewall.ipconntrack._device_zone_map, - {port['device']: ct_zone}): + {port['network_id']: ct_zone}): self.firewall.filter_defer_apply_on() self.firewall.sg_rules['fake_sg_id'] = [] self.firewall.filter_defer_apply_off() @@ -1212,7 +1224,7 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): self.firewall.filtered_ports[port['device']] = port self.firewall.updated_sg_members = set(['tapfake_dev']) with mock.patch.dict(self.firewall.ipconntrack._device_zone_map, - {port['device']: ct_zone}): + {port['network_id']: ct_zone}): self.firewall.filter_defer_apply_on() new_port = copy.deepcopy(port) new_port['security_groups'] = ['fake_sg_id2'] @@ -1274,7 +1286,7 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): members_after_delete = {'IPv6': ['fe80::3']} with mock.patch.dict(self.firewall.ipconntrack._device_zone_map, - {port['device']: ct_zone}): + {port['network_id']: ct_zone}): # add ['10.0.0.2', '10.0.0.3'] or ['fe80::2', 'fe80::3'] self.firewall.security_group_updated('sg_member', ['fake_sg_id2']) self.firewall.update_security_group_members( @@ -1331,6 +1343,12 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): '-j DROP', comment=ic.UNMATCH_DROP), mock.call.add_chain('sg-chain'), + mock.call.add_rule('PREROUTING', mock.ANY, + comment=None), # zone set + mock.call.add_rule('PREROUTING', mock.ANY, + comment=None), # zone set + mock.call.add_rule('PREROUTING', mock.ANY, + comment=None), # zone set mock.call.add_chain('ifake_dev'), mock.call.add_rule( 'FORWARD', @@ -1408,8 +1426,17 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): mock.call.remove_chain('ifake_dev'), mock.call.remove_chain('ofake_dev'), mock.call.remove_chain('sfake_dev'), + mock.call.remove_rule('PREROUTING', mock.ANY), # zone set + mock.call.remove_rule('PREROUTING', mock.ANY), # zone set + mock.call.remove_rule('PREROUTING', mock.ANY), # zone set mock.call.remove_chain('sg-chain'), mock.call.add_chain('sg-chain'), + mock.call.add_rule('PREROUTING', mock.ANY, + comment=None), # zone set + mock.call.add_rule('PREROUTING', mock.ANY, + comment=None), # zone set + mock.call.add_rule('PREROUTING', mock.ANY, + comment=None), # zone set mock.call.add_chain('ifake_dev'), mock.call.add_rule( 'FORWARD', @@ -1487,6 +1514,9 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): mock.call.remove_chain('ifake_dev'), mock.call.remove_chain('ofake_dev'), mock.call.remove_chain('sfake_dev'), + mock.call.remove_rule('PREROUTING', mock.ANY), # zone set + mock.call.remove_rule('PREROUTING', mock.ANY), # zone set + mock.call.remove_rule('PREROUTING', mock.ANY), # zone set mock.call.remove_chain('sg-chain'), mock.call.add_chain('sg-chain')] @@ -1510,11 +1540,8 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): new_port['fixed_ips'] = ['10.0.0.2', 'fe80::2'] self.firewall.sg_members['fake_sg_id2'] = {'IPv4': ['10.0.0.2'], 'IPv6': ['fe80::2']} - if ct_zone: - self.firewall.ipconntrack._device_zone_map['tapfake_dev'] = ct_zone - else: - self.firewall.ipconntrack._device_zone_map.pop('tapfake_dev', None) - + mock.patch.object(self.firewall.ipconntrack, 'get_device_zone', + return_value=ct_zone).start() self.firewall.remove_port_filter(port) if not ct_zone: self.assertFalse(self.utils_exec.called) @@ -1564,8 +1591,10 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): def test_mock_chain_applies(self): chain_applies = self._mock_chain_applies() - port_prepare = {'device': 'd1', 'mac_address': 'prepare'} - port_update = {'device': 'd1', 'mac_address': 'update'} + port_prepare = {'device': 'd1', 'mac_address': 'prepare', + 'network_id': 'fake_net'} + port_update = {'device': 'd1', 'mac_address': 'update', + 'network_id': 'fake_net'} self.firewall.prepare_port_filter(port_prepare) self.firewall.update_port_filter(port_update) self.firewall.remove_port_filter(port_update) @@ -1619,6 +1648,12 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): 'sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP), mock.call.add_chain('sg-chain'), + mock.call.add_rule('PREROUTING', mock.ANY, # zone set + comment=None), + mock.call.add_rule('PREROUTING', mock.ANY, # zone set + comment=None), + mock.call.add_rule('PREROUTING', mock.ANY, # zone set + comment=None), mock.call.add_chain('ifake_dev'), mock.call.add_rule('FORWARD', '-m physdev --physdev-out tapfake_dev ' @@ -1703,6 +1738,12 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): 'sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP), mock.call.add_chain('sg-chain'), + mock.call.add_rule('PREROUTING', mock.ANY, # zone set + comment=None), + mock.call.add_rule('PREROUTING', mock.ANY, # zone set + comment=None), + mock.call.add_rule('PREROUTING', mock.ANY, # zone set + comment=None), mock.call.add_chain('ifake_dev'), mock.call.add_rule('FORWARD', '-m physdev --physdev-out tapfake_dev ' @@ -2088,9 +2129,9 @@ class OVSHybridIptablesFirewallTestCase(BaseIptablesFirewallTestCase): self.firewall.ipconntrack._device_zone_map) def test_get_device_zone(self): + dev = {'device': 'tap1234', 'network_id': '12345678901234567'} # initial data has 1, 2, and 9 in use. - self.assertEqual(10, - self.firewall.ipconntrack.get_device_zone('12345678901234567')) + self.assertEqual(10, self.firewall.ipconntrack.get_device_zone(dev)) # should have been truncated to 11 chars self._dev_zone_map.update({'12345678901': 10}) self.assertEqual(self._dev_zone_map, diff --git a/neutron/tests/unit/agent/test_securitygroups_rpc.py b/neutron/tests/unit/agent/test_securitygroups_rpc.py index 67b0e7d3427..390b2b0c444 100644 --- a/neutron/tests/unit/agent/test_securitygroups_rpc.py +++ b/neutron/tests/unit/agent/test_securitygroups_rpc.py @@ -27,6 +27,7 @@ from testtools import matchers import webob.exc from neutron.agent import firewall as firewall_base +from neutron.agent.linux import ip_conntrack from neutron.agent.linux import iptables_manager from neutron.agent import securitygroups_rpc as sg_rpc from neutron.api.rpc.handlers import securitygroups_rpc @@ -1729,6 +1730,48 @@ COMMIT # Completed by iptables_manager """ % IPTABLES_ARG +IPTABLES_RAW_BRIDGE_NET_1 = """# Generated by iptables_manager +*raw +:OUTPUT - [0:0] +:PREROUTING - [0:0] +:%(bn)s-OUTPUT - [0:0] +:%(bn)s-PREROUTING - [0:0] +-I OUTPUT 1 -j %(bn)s-OUTPUT +-I PREROUTING 1 -j %(bn)s-PREROUTING +-I %(bn)s-PREROUTING 1 -m physdev --physdev-in brqfakenet1 \ +-m comment --comment "Set zone for port1" -j CT --zone 1 +-I %(bn)s-PREROUTING 2 -i brqfakenet1 \ +-m comment --comment "Set zone for port1" -j CT --zone 1 +-I %(bn)s-PREROUTING 3 -m physdev --physdev-in tap_port1 \ +-m comment --comment "Set zone for port1" -j CT --zone 1 +COMMIT +# Completed by iptables_manager +""" % IPTABLES_ARG + +IPTABLES_RAW_BRIDGE_NET_2 = """# Generated by iptables_manager +*raw +:OUTPUT - [0:0] +:PREROUTING - [0:0] +:%(bn)s-OUTPUT - [0:0] +:%(bn)s-PREROUTING - [0:0] +-I OUTPUT 1 -j %(bn)s-OUTPUT +-I PREROUTING 1 -j %(bn)s-PREROUTING +-I %(bn)s-PREROUTING 1 -m physdev --physdev-in brqfakenet1 \ +-m comment --comment "Set zone for port1" -j CT --zone 1 +-I %(bn)s-PREROUTING 2 -i brqfakenet1 \ +-m comment --comment "Set zone for port1" -j CT --zone 1 +-I %(bn)s-PREROUTING 3 -m physdev --physdev-in tap_port1 \ +-m comment --comment "Set zone for port1" -j CT --zone 1 +-I %(bn)s-PREROUTING 4 -m physdev --physdev-in brqfakenet2 \ +-m comment --comment "Set zone for port2" -j CT --zone 2 +-I %(bn)s-PREROUTING 5 -i brqfakenet2 \ +-m comment --comment "Set zone for port2" -j CT --zone 2 +-I %(bn)s-PREROUTING 6 -m physdev --physdev-in tap_port2 \ +-m comment --comment "Set zone for port2" -j CT --zone 2 +COMMIT +# Completed by iptables_manager +""" % IPTABLES_ARG + IPTABLES_RAW_DEVICE_1 = """# Generated by iptables_manager *raw :OUTPUT - [0:0] @@ -1737,8 +1780,12 @@ IPTABLES_RAW_DEVICE_1 = """# Generated by iptables_manager :%(bn)s-PREROUTING - [0:0] -I OUTPUT 1 -j %(bn)s-OUTPUT -I PREROUTING 1 -j %(bn)s-PREROUTING --I %(bn)s-PREROUTING 1 -m physdev --physdev-in qvbtap_port1 -j CT --zone 1 --I %(bn)s-PREROUTING 2 -m physdev --physdev-in tap_port1 -j CT --zone 1 +-I %(bn)s-PREROUTING 1 -m physdev --physdev-in qvbtap_port1 \ +-m comment --comment "Set zone for %(port1)s" -j CT --zone 1 +-I %(bn)s-PREROUTING 2 -i qvbtap_port1 \ +-m comment --comment "Set zone for %(port1)s" -j CT --zone 1 +-I %(bn)s-PREROUTING 3 -m physdev --physdev-in tap_port1 \ +-m comment --comment "Set zone for %(port1)s" -j CT --zone 1 COMMIT # Completed by iptables_manager """ % IPTABLES_ARG @@ -1752,11 +1799,17 @@ IPTABLES_RAW_DEVICE_2 = """# Generated by iptables_manager -I OUTPUT 1 -j %(bn)s-OUTPUT -I PREROUTING 1 -j %(bn)s-PREROUTING -I %(bn)s-PREROUTING 1 -m physdev --physdev-in qvbtap_%(port1)s \ --j CT --zone 1 --I %(bn)s-PREROUTING 2 -m physdev --physdev-in tap_%(port1)s -j CT --zone 1 --I %(bn)s-PREROUTING 3 -m physdev --physdev-in qvbtap_%(port2)s \ --j CT --zone 2 --I %(bn)s-PREROUTING 4 -m physdev --physdev-in tap_%(port2)s -j CT --zone 2 +-m comment --comment "Set zone for %(port1)s" -j CT --zone 1 +-I %(bn)s-PREROUTING 2 -i qvbtap_%(port1)s \ +-m comment --comment "Set zone for %(port1)s" -j CT --zone 1 +-I %(bn)s-PREROUTING 3 -m physdev --physdev-in tap_%(port1)s \ +-m comment --comment "Set zone for %(port1)s" -j CT --zone 1 +-I %(bn)s-PREROUTING 4 -m physdev --physdev-in qvbtap_%(port2)s \ +-m comment --comment "Set zone for %(port2)s" -j CT --zone 2 +-I %(bn)s-PREROUTING 5 -i qvbtap_%(port2)s \ +-m comment --comment "Set zone for %(port2)s" -j CT --zone 2 +-I %(bn)s-PREROUTING 6 -m physdev --physdev-in tap_%(port2)s \ +-m comment --comment "Set zone for %(port2)s" -j CT --zone 2 COMMIT # Completed by iptables_manager """ % IPTABLES_ARG @@ -2592,6 +2645,9 @@ class TestSecurityGroupAgentWithIptables(base.BaseTestCase): PHYSDEV_EGRESS = 'physdev-in' def setUp(self, defer_refresh_firewall=False, test_rpc_v1_1=True): + clear_mgrs = lambda: ip_conntrack.CONTRACK_MGRS.clear() + self.addCleanup(clear_mgrs) + clear_mgrs() # clear before start in case other tests didn't clean up super(TestSecurityGroupAgentWithIptables, self).setUp() set_firewall_driver(self.FIREWALL_DRIVER) cfg.CONF.set_override('enable_ipset', False, group='SECURITYGROUP') @@ -2695,7 +2751,7 @@ class TestSecurityGroupAgentWithIptables(base.BaseTestCase): def _device(self, device, ip, mac_address, rule): return {'device': device, - 'network_id': 'fakenet', + 'network_id': 'fakenet%s' % device[-1:], 'fixed_ips': [ip], 'mac_address': mac_address, 'security_groups': ['security_group1'], @@ -2767,7 +2823,7 @@ class TestSecurityGroupAgentWithIptables(base.BaseTestCase): self.ipconntrack._device_zone_map = {} self.rpc.security_group_rules_for_devices.return_value = self.devices1 self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) @@ -2779,15 +2835,15 @@ class TestSecurityGroupAgentWithIptables(base.BaseTestCase): def test_security_group_member_updated(self): self.rpc.security_group_rules_for_devices.return_value = self.devices1 self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_1_2, IPTABLES_FILTER_V6_1, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPTABLES_FILTER_2_2, IPTABLES_FILTER_V6_2, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) @@ -2805,9 +2861,9 @@ class TestSecurityGroupAgentWithIptables(base.BaseTestCase): def test_security_group_rule_updated(self): self.rpc.security_group_rules_for_devices.return_value = self.devices2 self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPTABLES_FILTER_2_3, IPTABLES_FILTER_V6_2, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_2) self.agent.prepare_devices_filter(['tap_port1', 'tap_port3']) self.rpc.security_group_rules_for_devices.return_value = self.devices3 @@ -2882,7 +2938,7 @@ class TestSecurityGroupAgentEnhancedRpcWithIptables( self.ipconntrack._device_zone_map = {} self.sg_info.return_value = self.devices_info1 self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) @@ -2894,15 +2950,15 @@ class TestSecurityGroupAgentEnhancedRpcWithIptables( def test_security_group_member_updated(self): self.sg_info.return_value = self.devices_info1 self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_1_2, IPTABLES_FILTER_V6_1, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPTABLES_FILTER_2_2, IPTABLES_FILTER_V6_2, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) @@ -2922,9 +2978,9 @@ class TestSecurityGroupAgentEnhancedRpcWithIptables( def test_security_group_rule_updated(self): self.sg_info.return_value = self.devices_info2 self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPTABLES_FILTER_2_3, IPTABLES_FILTER_V6_2, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_2) self.agent.prepare_devices_filter(['tap_port1', 'tap_port3']) self.sg_info.return_value = self.devices_info3 @@ -2949,7 +3005,7 @@ class TestSecurityGroupAgentEnhancedIpsetWithIptables( self.ipconntrack._device_zone_map = {} self.sg_info.return_value = self.devices_info1 self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) @@ -2963,15 +3019,15 @@ class TestSecurityGroupAgentEnhancedIpsetWithIptables( self.ipset._get_new_set_ips = mock.Mock(return_value=['10.0.0.3']) self.ipset._get_deleted_set_ips = mock.Mock(return_value=[]) self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) @@ -2993,9 +3049,9 @@ class TestSecurityGroupAgentEnhancedIpsetWithIptables( self.ipset._get_deleted_set_ips = mock.Mock(return_value=[]) self.sg_info.return_value = self.devices_info2 self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPSET_FILTER_2_3, IPTABLES_FILTER_V6_2, - IPTABLES_RAW_DEFAULT) + IPTABLES_RAW_BRIDGE_NET_2) self.agent.prepare_devices_filter(['tap_port1', 'tap_port3']) self.sg_info.return_value = self.devices_info3