# Controls if neutron security group is enabled or not.
# It should be false when you use nova security group.
# enable_security_group = True
+
+# Use ipset to speed-up the iptables security groups. Enabling ipset support
+# requires that ipset is installed on L2 agent node.
+# enable_ipset = True
--- /dev/null
+# neutron-rootwrap command filters for nodes on which neutron is
+# expected to control network
+#
+# This file should be owned by (and only-writeable by) the root user
+
+# format seems to be
+# cmd-name: filter-name, raw-command, user, args
+
+[Filters]
+# neutron/agent/linux/iptables_firewall.py
+# "ipset", "-A", ...
+ipset: CommandFilter, ipset, root
--- /dev/null
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from neutron.agent.linux import utils as linux_utils
+from neutron.common import utils
+
+
+class IpsetManager(object):
+ """Wrapper for ipset."""
+
+ def __init__(self, execute=None, root_helper=None):
+ self.execute = execute or linux_utils.execute
+ self.root_helper = root_helper
+
+ @utils.synchronized('ipset', external=True)
+ def create_ipset_chain(self, chain_name, ethertype):
+ cmd = ['ipset', 'create', '-exist', chain_name, 'hash:ip', 'family',
+ self._get_ipset_chain_type(ethertype)]
+ self._apply(cmd)
+
+ @utils.synchronized('ipset', external=True)
+ def add_member_to_ipset_chain(self, chain_name, member_ip):
+ cmd = ['ipset', 'add', '-exist', chain_name, member_ip]
+ self._apply(cmd)
+
+ @utils.synchronized('ipset', external=True)
+ def refresh_ipset_chain_by_name(self, chain_name, member_ips, ethertype):
+ new_chain_name = chain_name + '-new'
+ chain_type = self._get_ipset_chain_type(ethertype)
+ process_input = ["create %s hash:ip family %s" % (new_chain_name,
+ chain_type)]
+ for ip in member_ips:
+ process_input.append("add %s %s" % (new_chain_name, ip))
+
+ self._restore_ipset_chains(process_input)
+ self._swap_ipset_chains(new_chain_name, chain_name)
+ self._destroy_ipset_chain(new_chain_name)
+
+ @utils.synchronized('ipset', external=True)
+ def del_ipset_chain_member(self, chain_name, member_ip):
+ cmd = ['ipset', 'del', chain_name, member_ip]
+ self._apply(cmd)
+
+ @utils.synchronized('ipset', external=True)
+ def destroy_ipset_chain_by_name(self, chain_name):
+ self._destroy_ipset_chain(chain_name)
+
+ def _apply(self, cmd, input=None):
+ input = '\n'.join(input) if input else None
+ self.execute(cmd, root_helper=self.root_helper, process_input=input)
+
+ def _get_ipset_chain_type(self, ethertype):
+ return 'inet6' if ethertype == 'IPv6' else 'inet'
+
+ def _restore_ipset_chains(self, process_input):
+ cmd = ['ipset', 'restore', '-exist']
+ self._apply(cmd, process_input)
+
+ def _swap_ipset_chains(self, src_chain, dest_chain):
+ cmd = ['ipset', 'swap', src_chain, dest_chain]
+ self._apply(cmd)
+
+ def _destroy_ipset_chain(self, chain_name):
+ cmd = ['ipset', 'destroy', chain_name]
+ self._apply(cmd)
from oslo.config import cfg
from neutron.agent import firewall
+from neutron.agent.linux import ipset_manager
from neutron.agent.linux import iptables_manager
from neutron.common import constants
from neutron.common import ipv6_utils
SPOOF_FILTER: 's'}
DIRECTION_IP_PREFIX = {'ingress': 'source_ip_prefix',
'egress': 'dest_ip_prefix'}
+IPSET_DIRECTION = {INGRESS_DIRECTION: 'src',
+ EGRESS_DIRECTION: 'dst'}
LINUX_DEV_LEN = 14
+IPSET_CHAIN_LEN = 20
+IPSET_CHANGE_BULK_THRESHOLD = 10
+IPSET_ADD_BULK_THRESHOLD = 5
class IptablesFirewallDriver(firewall.FirewallDriver):
EGRESS_DIRECTION: 'physdev-in'}
def __init__(self):
+ self.root_helper = cfg.CONF.AGENT.root_helper
self.iptables = iptables_manager.IptablesManager(
- root_helper=cfg.CONF.AGENT.root_helper,
+ root_helper=self.root_helper,
use_ipv6=ipv6_utils.is_enabled())
+ # TODO(majopela, shihanzhang): refactor out ipset to a separate
+ # driver composed over this one
+ self.ipset = ipset_manager.IpsetManager(root_helper=self.root_helper)
# list of port which has security group
self.filtered_ports = {}
self._add_fallback_chain_v4v6()
# List of security group member ips for ports residing on this host
self.sg_members = {}
self.pre_sg_members = None
+ self.ipset_chains = {}
+ self.enable_ipset = cfg.CONF.SECURITYGROUP.enable_ipset
@property
def ports(self):
for sg_id in sg_ids:
for rule in self.sg_rules.get(sg_id, []):
if rule['direction'] == direction:
+ if self.enable_ipset:
+ port_rules.append(rule)
+ continue
remote_group_id = rule.get('remote_group_id')
if not remote_group_id:
port_rules.append(rule)
port_rules.append(ip_rule)
return port_rules
+ def _get_remote_sg_ids(self, port, direction):
+ sg_ids = port.get('security_groups', [])
+ remote_sg_ids = []
+ for sg_id in sg_ids:
+ remote_sg_ids.extend([rule['remote_group_id']
+ for rule in self.sg_rules.get(sg_id, []) if
+ rule['direction'] == direction
+ and rule.get('remote_group_id')])
+ return remote_sg_ids
+
def _add_rule_by_security_group(self, port, direction):
chain_name = self._port_chain_name(port, direction)
# select rules for current direction
security_group_rules = self._select_sgr_by_direction(port, direction)
security_group_rules += self._select_sg_rules_for_port(port, direction)
+ if self.enable_ipset:
+ remote_sg_ids = self._get_remote_sg_ids(port, direction)
+ # update the corresponding ipset chain member
+ self._update_ipset_chain_member(remote_sg_ids)
# split groups by ip version
# for ipv4, iptables command is used
# for ipv6, iptables6 command is used
ipv4_iptables_rule,
ipv6_iptables_rule)
+ def _get_cur_sg_member_ips(self, sg_id, ethertype):
+ return self.sg_members.get(sg_id, {}).get(ethertype, [])
+
+ def _get_pre_sg_member_ips(self, sg_id, ethertype):
+ return self.pre_sg_members.get(sg_id, {}).get(ethertype, [])
+
+ def _get_new_sg_member_ips(self, sg_id, ethertype):
+ add_member_ips = (set(self._get_cur_sg_member_ips(sg_id, ethertype)) -
+ set(self._get_pre_sg_member_ips(sg_id, ethertype)))
+ return list(add_member_ips)
+
+ def _get_deleted_sg_member_ips(self, sg_id, ethertype):
+ del_member_ips = (set(self._get_pre_sg_member_ips(sg_id, ethertype)) -
+ set(self._get_cur_sg_member_ips(sg_id, ethertype)))
+ return list(del_member_ips)
+
+ def _bulk_set_ips_to_chain(self, chain_name, member_ips, ethertype):
+ self.ipset.refresh_ipset_chain_by_name(chain_name, member_ips,
+ ethertype)
+ self.ipset_chains[chain_name] = member_ips
+
+ def _add_ips_to_ipset_chain(self, chain_name, add_ips):
+ for ip in add_ips:
+ if ip not in self.ipset_chains[chain_name]:
+ self.ipset.add_member_to_ipset_chain(chain_name, ip)
+ self.ipset_chains[chain_name].append(ip)
+
+ def _del_ips_from_ipset_chain(self, chain_name, del_ips):
+ if chain_name in self.ipset_chains:
+ for del_ip in del_ips:
+ if del_ip in self.ipset_chains[chain_name]:
+ self.ipset.del_ipset_chain_member(chain_name, del_ip)
+ self.ipset_chains[chain_name].remove(del_ip)
+
+ def _update_ipset_chain_member(self, security_group_ids):
+ for sg_id in security_group_ids or []:
+ for ethertype in ['IPv4', 'IPv6']:
+ add_ips = self._get_new_sg_member_ips(sg_id, ethertype)
+ del_ips = self._get_deleted_sg_member_ips(sg_id, ethertype)
+ cur_member_ips = self._get_cur_sg_member_ips(sg_id, ethertype)
+ chain_name = ethertype + sg_id[:IPSET_CHAIN_LEN]
+ if chain_name not in self.ipset_chains:
+ self.ipset_chains[chain_name] = []
+ self.ipset.create_ipset_chain(
+ chain_name, ethertype)
+ self._bulk_set_ips_to_chain(chain_name,
+ cur_member_ips, ethertype)
+ elif (len(add_ips) + len(del_ips)
+ < IPSET_CHANGE_BULK_THRESHOLD):
+ self._add_ips_to_ipset_chain(chain_name, add_ips)
+ self._del_ips_from_ipset_chain(chain_name, del_ips)
+ else:
+ self._bulk_set_ips_to_chain(chain_name,
+ cur_member_ips, ethertype)
+
+ def _generate_ipset_chain(self, sg_rule, remote_gid):
+ iptables_rules = []
+ args = self._protocol_arg(sg_rule.get('protocol'))
+ args += self._port_arg('sport',
+ sg_rule.get('protocol'),
+ sg_rule.get('source_port_range_min'),
+ sg_rule.get('source_port_range_max'))
+ args += self._port_arg('dport',
+ sg_rule.get('protocol'),
+ sg_rule.get('port_range_min'),
+ sg_rule.get('port_range_max'))
+ direction = sg_rule.get('direction')
+ ethertype = sg_rule.get('ethertype')
+ # the length of ipset chain name require less than 31
+ # characters
+ ipset_chain_name = (ethertype + remote_gid[:IPSET_CHAIN_LEN])
+ if ipset_chain_name in self.ipset_chains:
+ args += ['-m set', '--match-set',
+ ipset_chain_name,
+ IPSET_DIRECTION[direction]]
+ args += ['-j RETURN']
+ iptables_rules += [' '.join(args)]
+ return iptables_rules
+
def _convert_sgr_to_iptables_rules(self, security_group_rules):
iptables_rules = []
self._drop_invalid_packets(iptables_rules)
self._allow_established(iptables_rules)
for rule in security_group_rules:
+ if self.enable_ipset:
+ remote_gid = rule.get('remote_group_id')
+ if remote_gid:
+ iptables_rules.extend(
+ self._generate_ipset_chain(rule, remote_gid))
+ continue
# These arguments MUST be in the format iptables-save will
# display them: source/dest, protocol, sport, dport, target
# Otherwise the iptables_manager code won't be able to find
for remove_chain_id in need_removed_ipset_chains:
if remove_chain_id in self.sg_members:
self.sg_members.pop(remove_chain_id, None)
+ if self.enable_ipset:
+ for ethertype in ['IPv4', 'IPv6']:
+ removed_chain = (
+ ethertype + remove_chain_id[:IPSET_CHAIN_LEN])
+ if removed_chain in self.ipset_chains:
+ self.ipset.destroy_ipset_chain_by_name(removed_chain)
+ self.ipset_chains.pop(removed_chain, None)
# Remove unused security group rules
for remove_group_id in need_removed_security_groups:
help=_(
'Controls whether the neutron security group API is enabled '
'in the server. It should be false when using no security '
- 'groups or using the nova security group API.'))
+ 'groups or using the nova security group API.')),
+ cfg.BoolOpt(
+ 'enable_ipset',
+ default=True,
+ help=_('Use ipset to speed-up the iptables based security groups.'))
]
cfg.CONF.register_opts(security_group_opts, 'SECURITYGROUP')
from neutron.agent.common import config as a_cfg
from neutron.agent.linux import iptables_firewall
+from neutron.agent import securitygroups_rpc as sg_cfg
from neutron.common import constants
from neutron.tests import base
from neutron.tests.unit import test_api_v2
FAKE_IP = {'IPv4': '10.0.0.1',
'IPv6': 'fe80::1'}
+TEST_IP_RANGE = ['10.0.0.1', '10.0.0.2', '10.0.0.3', '10.0.0.4',
+ '10.0.0.5', '10.0.0.6', '10.0.0.7', '10.0.0.8',
+ '10.0.0.9', '10.0.0.10']
-class IptablesFirewallTestCase(base.BaseTestCase):
+
+class BaseIptablesFirewallTestCase(base.BaseTestCase):
def setUp(self):
- super(IptablesFirewallTestCase, self).setUp()
+ super(BaseIptablesFirewallTestCase, self).setUp()
cfg.CONF.register_opts(a_cfg.ROOT_HELPER_OPTS, 'AGENT')
+ cfg.CONF.register_opts(sg_cfg.security_group_opts, 'SECURITYGROUP')
self.utils_exec_p = mock.patch(
'neutron.agent.linux.utils.execute')
self.utils_exec = self.utils_exec_p.start()
self.firewall = iptables_firewall.IptablesFirewallDriver()
self.firewall.iptables = self.iptables_inst
+
+class IptablesFirewallTestCase(BaseIptablesFirewallTestCase):
+
def _fake_port(self):
return {'device': 'tapfake_dev',
'mac_address': 'ff:ff:ff:ff:ff:ff',
mock.call.add_rule('ofake_dev', '-j $sg-fallback'),
mock.call.add_rule('sg-chain', '-j ACCEPT')]
self.v4filter_inst.assert_has_calls(calls)
+
+
+class IptablesFirewallEnhancedIpsetTestCase(BaseIptablesFirewallTestCase):
+ def setUp(self):
+ super(IptablesFirewallEnhancedIpsetTestCase, self).setUp()
+ self.firewall.ipset = mock.Mock()
+
+ def _fake_port(self):
+ return {'device': 'tapfake_dev',
+ 'mac_address': 'ff:ff:ff:ff:ff:ff',
+ 'fixed_ips': [FAKE_IP['IPv4'],
+ FAKE_IP['IPv6']],
+ 'security_groups': ['fake_sgid'],
+ 'security_group_source_groups': ['fake_sgid']}
+
+ def _fake_sg_rule(self):
+ return {'fake_sgid': [
+ {'direction': 'ingress', 'remote_group_id': 'fake_sgid'}]}
+
+ def test_prepare_port_filter_with_default_sg(self):
+ self.firewall.sg_rules = self._fake_sg_rule()
+ self.firewall.sg_members = {'fake_sgid': {
+ 'IPv4': ['10.0.0.1', '10.0.0.2'], 'IPv6': ['fe80::1']}}
+ self.firewall.pre_sg_members = {}
+ port = self._fake_port()
+ self.firewall.prepare_port_filter(port)
+ calls = [mock.call.create_ipset_chain('IPv4fake_sgid', 'IPv4'),
+ mock.call.refresh_ipset_chain_by_name(
+ 'IPv4fake_sgid', ['10.0.0.1', '10.0.0.2'], 'IPv4'),
+ mock.call.create_ipset_chain('IPv6fake_sgid', 'IPv6'),
+ mock.call.refresh_ipset_chain_by_name(
+ 'IPv6fake_sgid', ['fe80::1'], 'IPv6')]
+
+ self.firewall.ipset.assert_has_calls(calls)
+
+ def test_prepare_port_filter_with_add_members_beyond_4(self):
+ self.firewall.sg_rules = self._fake_sg_rule()
+ self.firewall.sg_members = {'fake_sgid': {
+ 'IPv4': TEST_IP_RANGE[:5],
+ 'IPv6': ['fe80::1']}}
+ self.firewall.pre_sg_members = {}
+ port = self._fake_port()
+ self.firewall.prepare_port_filter(port)
+ calls = [mock.call.create_ipset_chain('IPv4fake_sgid', 'IPv4'),
+ mock.call.refresh_ipset_chain_by_name(
+ 'IPv4fake_sgid', TEST_IP_RANGE[:5], 'IPv4'),
+ mock.call.create_ipset_chain('IPv6fake_sgid', 'IPv6'),
+ mock.call.refresh_ipset_chain_by_name(
+ 'IPv6fake_sgid', ['fe80::1'], 'IPv6')]
+
+ self.firewall.ipset.assert_has_calls(calls)
+
+ def test_prepare_port_filter_with_ipset_chain_exist(self):
+ self.firewall.sg_rules = self._fake_sg_rule()
+ self.firewall.ipset_chains = {'IPv4fake_sgid': ['10.0.0.2']}
+ self.firewall.sg_members = {'fake_sgid': {
+ 'IPv4': TEST_IP_RANGE[:5],
+ 'IPv6': ['fe80::1']}}
+ self.firewall.pre_sg_members = {'fake_sgid': {
+ 'IPv4': ['10.0.0.2'],
+ 'IPv6': ['fe80::1']}}
+ port = self._fake_port()
+ self.firewall.prepare_port_filter(port)
+ calls = [
+ mock.call.add_member_to_ipset_chain('IPv4fake_sgid', '10.0.0.1'),
+ mock.call.add_member_to_ipset_chain('IPv4fake_sgid', '10.0.0.3'),
+ mock.call.add_member_to_ipset_chain('IPv4fake_sgid', '10.0.0.4'),
+ mock.call.add_member_to_ipset_chain('IPv4fake_sgid', '10.0.0.5'),
+ mock.call.create_ipset_chain('IPv6fake_sgid', 'IPv6'),
+ mock.call.refresh_ipset_chain_by_name(
+ 'IPv6fake_sgid', ['fe80::1'], 'IPv6')]
+
+ self.firewall.ipset.assert_has_calls(calls, True)
+
+ def test_prepare_port_filter_with_del_member(self):
+ self.firewall.sg_rules = self._fake_sg_rule()
+ self.firewall.ipset_chains = {'IPv4fake_sgid': ['10.0.0.2']}
+ self.firewall.sg_members = {'fake_sgid': {
+ 'IPv4': [
+ '10.0.0.1', '10.0.0.3', '10.0.0.4', '10.0.0.5'],
+ 'IPv6': ['fe80::1']}}
+ self.firewall.pre_sg_members = {'fake_sgid': {
+ 'IPv4': ['10.0.0.2'],
+ 'IPv6': ['fe80::1']}}
+ port = self._fake_port()
+ self.firewall.prepare_port_filter(port)
+ calls = [
+ mock.call.add_member_to_ipset_chain('IPv4fake_sgid', '10.0.0.1'),
+ mock.call.add_member_to_ipset_chain('IPv4fake_sgid', '10.0.0.3'),
+ mock.call.add_member_to_ipset_chain('IPv4fake_sgid', '10.0.0.4'),
+ mock.call.add_member_to_ipset_chain('IPv4fake_sgid', '10.0.0.5'),
+ mock.call.del_ipset_chain_member('IPv4fake_sgid', '10.0.0.2'),
+ mock.call.create_ipset_chain('IPv6fake_sgid', 'IPv6'),
+ mock.call.refresh_ipset_chain_by_name(
+ 'IPv6fake_sgid', ['fe80::1'], 'IPv6')]
+
+ self.firewall.ipset.assert_has_calls(calls, True)
+
+ def test_prepare_port_filter_change_beyond_9(self):
+ self.firewall.sg_rules = self._fake_sg_rule()
+ self.firewall.ipset_chains = {'IPv4fake_sgid': TEST_IP_RANGE[5:]}
+ self.firewall.sg_members = {'fake_sgid': {
+ 'IPv4': TEST_IP_RANGE[:5],
+ 'IPv6': ['fe80::1']}}
+ self.firewall.pre_sg_members = {'fake_sgid': {
+ 'IPv4': TEST_IP_RANGE[5:],
+ 'IPv6': ['fe80::1']}}
+ port = self._fake_port()
+ self.firewall.prepare_port_filter(port)
+ calls = [
+ mock.call.refresh_ipset_chain_by_name('IPv4fake_sgid',
+ TEST_IP_RANGE[:5], 'IPv4'),
+ mock.call.create_ipset_chain('IPv6fake_sgid', 'IPv6'),
+ mock.call.refresh_ipset_chain_by_name(
+ 'IPv6fake_sgid', ['fe80::1'], 'IPv6')]
+
+ self.firewall.ipset.assert_has_calls(calls)
IPTABLES_ARG['chains'] = CHAINS_1
+IPSET_FILTER_1 = """# Generated by iptables_manager
+*filter
+:neutron-filter-top - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+[0:0] -A FORWARD -j neutron-filter-top
+[0:0] -A OUTPUT -j neutron-filter-top
+[0:0] -A neutron-filter-top -j %(bn)s-local
+[0:0] -A INPUT -j %(bn)s-INPUT
+[0:0] -A OUTPUT -j %(bn)s-OUTPUT
+[0:0] -A FORWARD -j %(bn)s-FORWARD
+[0:0] -A %(bn)s-sg-fallback -j DROP
+[0:0] -A %(bn)s-FORWARD %(physdev_mod)s --physdev-INGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-sg-chain
+[0:0] -A %(bn)s-sg-chain %(physdev_mod)s --physdev-INGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-i_port1
+[0:0] -A %(bn)s-i_port1 -m state --state INVALID -j DROP
+[0:0] -A %(bn)s-i_port1 -m state --state RELATED,ESTABLISHED -j RETURN
+[0:0] -A %(bn)s-i_port1 -s 10.0.0.2/32 -p udp -m udp --sport 67 --dport 68 \
+-j RETURN
+[0:0] -A %(bn)s-i_port1 -p tcp -m tcp --dport 22 -j RETURN
+[0:0] -A %(bn)s-i_port1 -m set --match-set IPv4security_group1 src -j \
+RETURN
+[0:0] -A %(bn)s-i_port1 -j %(bn)s-sg-fallback
+[0:0] -A %(bn)s-FORWARD %(physdev_mod)s --physdev-EGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-sg-chain
+[0:0] -A %(bn)s-sg-chain %(physdev_mod)s --physdev-EGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-o_port1
+[0:0] -A %(bn)s-INPUT %(physdev_mod)s --physdev-EGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-o_port1
+[0:0] -A %(bn)s-s_port1 -m mac --mac-source 12:34:56:78:9a:bc -s 10.0.0.3/32 \
+-j RETURN
+[0:0] -A %(bn)s-s_port1 -j DROP
+[0:0] -A %(bn)s-o_port1 -p udp -m udp --sport 68 --dport 67 -j RETURN
+[0:0] -A %(bn)s-o_port1 -j %(bn)s-s_port1
+[0:0] -A %(bn)s-o_port1 -p udp -m udp --sport 67 --dport 68 -j DROP
+[0:0] -A %(bn)s-o_port1 -m state --state INVALID -j DROP
+[0:0] -A %(bn)s-o_port1 -m state --state RELATED,ESTABLISHED -j RETURN
+[0:0] -A %(bn)s-o_port1 -j RETURN
+[0:0] -A %(bn)s-o_port1 -j %(bn)s-sg-fallback
+[0:0] -A %(bn)s-sg-chain -j ACCEPT
+COMMIT
+# Completed by iptables_manager
+""" % IPTABLES_ARG
+
IPTABLES_FILTER_1 = """# Generated by iptables_manager
*filter
:neutron-filter-top - [0:0]
IPTABLES_ARG['chains'] = CHAINS_2
+IPSET_FILTER_2 = """# Generated by iptables_manager
+*filter
+:neutron-filter-top - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+[0:0] -A FORWARD -j neutron-filter-top
+[0:0] -A OUTPUT -j neutron-filter-top
+[0:0] -A neutron-filter-top -j %(bn)s-local
+[0:0] -A INPUT -j %(bn)s-INPUT
+[0:0] -A OUTPUT -j %(bn)s-OUTPUT
+[0:0] -A FORWARD -j %(bn)s-FORWARD
+[0:0] -A %(bn)s-sg-fallback -j DROP
+[0:0] -A %(bn)s-FORWARD %(physdev_mod)s --physdev-INGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-sg-chain
+[0:0] -A %(bn)s-sg-chain %(physdev_mod)s --physdev-INGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-i_port1
+[0:0] -A %(bn)s-i_port1 -m state --state INVALID -j DROP
+[0:0] -A %(bn)s-i_port1 -m state --state RELATED,ESTABLISHED -j RETURN
+[0:0] -A %(bn)s-i_port1 -s 10.0.0.2/32 -p udp -m udp --sport 67 --dport 68 \
+-j RETURN
+[0:0] -A %(bn)s-i_port1 -p tcp -m tcp --dport 22 -j RETURN
+[0:0] -A %(bn)s-i_port1 -m set --match-set IPv4security_group1 src -j \
+RETURN
+[0:0] -A %(bn)s-i_port1 -j %(bn)s-sg-fallback
+[0:0] -A %(bn)s-FORWARD %(physdev_mod)s --physdev-EGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-sg-chain
+[0:0] -A %(bn)s-sg-chain %(physdev_mod)s --physdev-EGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-o_port1
+[0:0] -A %(bn)s-INPUT %(physdev_mod)s --physdev-EGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-o_port1
+[0:0] -A %(bn)s-s_port1 -m mac --mac-source 12:34:56:78:9a:bc -s 10.0.0.3/32 \
+-j RETURN
+[0:0] -A %(bn)s-s_port1 -j DROP
+[0:0] -A %(bn)s-o_port1 -p udp -m udp --sport 68 --dport 67 -j RETURN
+[0:0] -A %(bn)s-o_port1 -j %(bn)s-s_port1
+[0:0] -A %(bn)s-o_port1 -p udp -m udp --sport 67 --dport 68 -j DROP
+[0:0] -A %(bn)s-o_port1 -m state --state INVALID -j DROP
+[0:0] -A %(bn)s-o_port1 -m state --state RELATED,ESTABLISHED -j RETURN
+[0:0] -A %(bn)s-o_port1 -j RETURN
+[0:0] -A %(bn)s-o_port1 -j %(bn)s-sg-fallback
+[0:0] -A %(bn)s-FORWARD %(physdev_mod)s --physdev-INGRESS tap_port2 \
+%(physdev_is_bridged)s -j %(bn)s-sg-chain
+[0:0] -A %(bn)s-sg-chain %(physdev_mod)s --physdev-INGRESS tap_port2 \
+%(physdev_is_bridged)s -j %(bn)s-i_port2
+[0:0] -A %(bn)s-i_port2 -m state --state INVALID -j DROP
+[0:0] -A %(bn)s-i_port2 -m state --state RELATED,ESTABLISHED -j RETURN
+[0:0] -A %(bn)s-i_port2 -s 10.0.0.2/32 -p udp -m udp --sport 67 --dport 68 \
+-j RETURN
+[0:0] -A %(bn)s-i_port2 -p tcp -m tcp --dport 22 -j RETURN
+[0:0] -A %(bn)s-i_port2 -m set --match-set IPv4security_group1 src -j \
+RETURN
+[0:0] -A %(bn)s-i_port2 -j %(bn)s-sg-fallback
+[0:0] -A %(bn)s-FORWARD %(physdev_mod)s --physdev-EGRESS tap_port2 \
+%(physdev_is_bridged)s -j %(bn)s-sg-chain
+[0:0] -A %(bn)s-sg-chain %(physdev_mod)s --physdev-EGRESS tap_port2 \
+%(physdev_is_bridged)s -j %(bn)s-o_port2
+[0:0] -A %(bn)s-INPUT %(physdev_mod)s --physdev-EGRESS tap_port2 \
+%(physdev_is_bridged)s -j %(bn)s-o_port2
+[0:0] -A %(bn)s-s_port2 -m mac --mac-source 12:34:56:78:9a:bd -s 10.0.0.4/32 \
+-j RETURN
+[0:0] -A %(bn)s-s_port2 -j DROP
+[0:0] -A %(bn)s-o_port2 -p udp -m udp --sport 68 --dport 67 -j RETURN
+[0:0] -A %(bn)s-o_port2 -j %(bn)s-s_port2
+[0:0] -A %(bn)s-o_port2 -p udp -m udp --sport 67 --dport 68 -j DROP
+[0:0] -A %(bn)s-o_port2 -m state --state INVALID -j DROP
+[0:0] -A %(bn)s-o_port2 -m state --state RELATED,ESTABLISHED -j RETURN
+[0:0] -A %(bn)s-o_port2 -j RETURN
+[0:0] -A %(bn)s-o_port2 -j %(bn)s-sg-fallback
+[0:0] -A %(bn)s-sg-chain -j ACCEPT
+COMMIT
+# Completed by iptables_manager
+""" % IPTABLES_ARG
+
+IPSET_FILTER_2_3 = """# Generated by iptables_manager
+*filter
+:neutron-filter-top - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+:%(bn)s-(%(chains)s) - [0:0]
+[0:0] -A FORWARD -j neutron-filter-top
+[0:0] -A OUTPUT -j neutron-filter-top
+[0:0] -A neutron-filter-top -j %(bn)s-local
+[0:0] -A INPUT -j %(bn)s-INPUT
+[0:0] -A OUTPUT -j %(bn)s-OUTPUT
+[0:0] -A FORWARD -j %(bn)s-FORWARD
+[0:0] -A %(bn)s-sg-fallback -j DROP
+[0:0] -A %(bn)s-FORWARD %(physdev_mod)s --physdev-INGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-sg-chain
+[0:0] -A %(bn)s-sg-chain %(physdev_mod)s --physdev-INGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-i_port1
+[0:0] -A %(bn)s-i_port1 -m state --state INVALID -j DROP
+[0:0] -A %(bn)s-i_port1 -m state --state RELATED,ESTABLISHED -j RETURN
+[0:0] -A %(bn)s-i_port1 -s 10.0.0.2/32 -p udp -m udp --sport 67 --dport 68 \
+-j RETURN
+[0:0] -A %(bn)s-i_port1 -p tcp -m tcp --dport 22 -j RETURN
+[0:0] -A %(bn)s-i_port1 -m set --match-set IPv4security_group1 src -j \
+RETURN
+[0:0] -A %(bn)s-i_port1 -p icmp -j RETURN
+[0:0] -A %(bn)s-i_port1 -j %(bn)s-sg-fallback
+[0:0] -A %(bn)s-FORWARD %(physdev_mod)s --physdev-EGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-sg-chain
+[0:0] -A %(bn)s-sg-chain %(physdev_mod)s --physdev-EGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-o_port1
+[0:0] -A %(bn)s-INPUT %(physdev_mod)s --physdev-EGRESS tap_port1 \
+%(physdev_is_bridged)s -j %(bn)s-o_port1
+[0:0] -A %(bn)s-s_port1 -m mac --mac-source 12:34:56:78:9a:bc -s 10.0.0.3/32 \
+-j RETURN
+[0:0] -A %(bn)s-s_port1 -j DROP
+[0:0] -A %(bn)s-o_port1 -p udp -m udp --sport 68 --dport 67 -j RETURN
+[0:0] -A %(bn)s-o_port1 -j %(bn)s-s_port1
+[0:0] -A %(bn)s-o_port1 -p udp -m udp --sport 67 --dport 68 -j DROP
+[0:0] -A %(bn)s-o_port1 -m state --state INVALID -j DROP
+[0:0] -A %(bn)s-o_port1 -m state --state RELATED,ESTABLISHED -j RETURN
+[0:0] -A %(bn)s-o_port1 -j RETURN
+[0:0] -A %(bn)s-o_port1 -j %(bn)s-sg-fallback
+[0:0] -A %(bn)s-FORWARD %(physdev_mod)s --physdev-INGRESS tap_port2 \
+%(physdev_is_bridged)s -j %(bn)s-sg-chain
+[0:0] -A %(bn)s-sg-chain %(physdev_mod)s --physdev-INGRESS tap_port2 \
+%(physdev_is_bridged)s -j %(bn)s-i_port2
+[0:0] -A %(bn)s-i_port2 -m state --state INVALID -j DROP
+[0:0] -A %(bn)s-i_port2 -m state --state RELATED,ESTABLISHED -j RETURN
+[0:0] -A %(bn)s-i_port2 -s 10.0.0.2/32 -p udp -m udp --sport 67 --dport 68 \
+-j RETURN
+[0:0] -A %(bn)s-i_port2 -p tcp -m tcp --dport 22 -j RETURN
+[0:0] -A %(bn)s-i_port2 -m set --match-set IPv4security_group1 src -j \
+RETURN
+[0:0] -A %(bn)s-i_port2 -p icmp -j RETURN
+[0:0] -A %(bn)s-i_port2 -j %(bn)s-sg-fallback
+[0:0] -A %(bn)s-FORWARD %(physdev_mod)s --physdev-EGRESS tap_port2 \
+%(physdev_is_bridged)s -j %(bn)s-sg-chain
+[0:0] -A %(bn)s-sg-chain %(physdev_mod)s --physdev-EGRESS tap_port2 \
+%(physdev_is_bridged)s -j %(bn)s-o_port2
+[0:0] -A %(bn)s-INPUT %(physdev_mod)s --physdev-EGRESS tap_port2 \
+%(physdev_is_bridged)s -j %(bn)s-o_port2
+[0:0] -A %(bn)s-s_port2 -m mac --mac-source 12:34:56:78:9a:bd -s 10.0.0.4/32 \
+-j RETURN
+[0:0] -A %(bn)s-s_port2 -j DROP
+[0:0] -A %(bn)s-o_port2 -p udp -m udp --sport 68 --dport 67 -j RETURN
+[0:0] -A %(bn)s-o_port2 -j %(bn)s-s_port2
+[0:0] -A %(bn)s-o_port2 -p udp -m udp --sport 67 --dport 68 -j DROP
+[0:0] -A %(bn)s-o_port2 -m state --state INVALID -j DROP
+[0:0] -A %(bn)s-o_port2 -m state --state RELATED,ESTABLISHED -j RETURN
+[0:0] -A %(bn)s-o_port2 -j RETURN
+[0:0] -A %(bn)s-o_port2 -j %(bn)s-sg-fallback
+[0:0] -A %(bn)s-sg-chain -j ACCEPT
+COMMIT
+# Completed by iptables_manager
+""" % IPTABLES_ARG
+
IPTABLES_FILTER_2 = """# Generated by iptables_manager
*filter
:neutron-filter-top - [0:0]
'lock_path',
'$state_path/lock')
set_firewall_driver(self.FIREWALL_DRIVER)
+ cfg.CONF.set_override('enable_ipset', False, group='SECURITYGROUP')
self.agent = sg_rpc.SecurityGroupAgentRpcMixin()
self.agent.context = None
self.agent.init_firewall(
defer_refresh_firewall=defer_refresh_firewall)
-
self.iptables = self.agent.firewall.iptables
# TODO(jlibosva) Get rid of mocking iptables execute and mock out
# firewall instead
self._verify_mock_calls()
+class TestSecurityGroupAgentEnhancedIpsetWithIptables(
+ TestSecurityGroupAgentEnhancedRpcWithIptables):
+ def setUp(self, defer_refresh_firewall=False):
+ super(TestSecurityGroupAgentEnhancedIpsetWithIptables, self).setUp(
+ defer_refresh_firewall)
+ self.agent.firewall.enable_ipset = True
+ self.ipset = self.agent.firewall.ipset
+ self.ipset_execute = mock.patch.object(self.ipset,
+ "execute").start()
+
+ def test_prepare_remove_port(self):
+ self.sg_info.return_value = self.devices_info1
+ self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1)
+ self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY)
+
+ self.agent.prepare_devices_filter(['tap_port1'])
+ self.agent.remove_devices_filter(['tap_port1'])
+
+ self._verify_mock_calls()
+
+ def test_security_group_member_updated(self):
+ self.sg_info.return_value = self.devices_info1
+ self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1)
+ self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1)
+ self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2)
+ self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2)
+ self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1)
+ self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY)
+
+ self.agent.prepare_devices_filter(['tap_port1'])
+ self.sg_info.return_value = self.devices_info2
+ self.agent.security_groups_member_updated(['security_group1'])
+ self.agent.prepare_devices_filter(['tap_port2'])
+ self.sg_info.return_value = self.devices_info1
+ self.agent.security_groups_member_updated(['security_group1'])
+ self.agent.remove_devices_filter(['tap_port2'])
+ self.agent.remove_devices_filter(['tap_port1'])
+
+ self._verify_mock_calls()
+
+ def test_security_group_rule_updated(self):
+ self.sg_info.return_value = self.devices_info2
+ self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2)
+ self._replay_iptables(IPSET_FILTER_2_3, IPTABLES_FILTER_V6_2)
+
+ self.agent.prepare_devices_filter(['tap_port1', 'tap_port3'])
+ self.sg_info.return_value = self.devices_info3
+ self.agent.security_groups_rule_updated(['security_group1'])
+
+ self._verify_mock_calls()
+
+
class SGNotificationTestMixin():
def test_security_group_rule_updated(self):
name = 'webservers'
etc/neutron/rootwrap.d/debug.filters
etc/neutron/rootwrap.d/dhcp.filters
etc/neutron/rootwrap.d/iptables-firewall.filters
+ etc/neutron/rootwrap.d/ipset-firewall.filters
etc/neutron/rootwrap.d/l3.filters
etc/neutron/rootwrap.d/lbaas-haproxy.filters
etc/neutron/rootwrap.d/linuxbridge-plugin.filters