--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012, Nachi Ueno, NTT MCL, Inc.
+# All Rights Reserved.
+#
+# 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.
+
+import abc
+import contextlib
+
+
+class FirewallDriver(object):
+ """ Firewall Driver base class.
+
+ Defines methods that any driver providing security groups
+ and provider firewall functionality should implement.
+ Note port attribute should have information of security group ids and
+ security group rules.
+
+ the dict of port should have
+ device : interface name
+ fixed_ips: ips of the device
+ mac_address: mac_address of the device
+ security_groups: [sgid, sgid]
+ security_group_rules : [ rule, rule ]
+ the rule must contain ethertype and direction
+ the rule may contain security_group_id,
+ protocol, port_min, port_max
+ source_ip_prefix, source_port_min,
+ source_port_max, dest_ip_prefix,
+ Note: source_group_ip in REST API should be converted by this rule
+ if direction is ingress:
+ source_group_ip will be a soruce_prefix_ip
+ if direction is egress:
+ source_group_ip will be a dest_prefix_ip
+ Note: source_group_id in REST API should be converted by this rule
+ if direction is ingress:
+ source_group_id will be a list of soruce_prefix_ip
+ if direction is egress:
+ source_group_id will be a list of dest_prefix_ip
+ """
+
+ __metaclass__ = abc.ABCMeta
+
+ def prepare_port_filter(self, port):
+ """Prepare filters for the port.
+
+ This method should be called before the port is created.
+ """
+ raise NotImplementedError()
+
+ def apply_port_filter(self, port):
+ """Apply port filter.
+
+ Once this method returns, the port should be firewalled
+ appropriately. This method should as far as possible be a
+ no-op. It's vastly preferred to get everything set up in
+ prepare_port_filter.
+ """
+ raise NotImplementedError()
+
+ def update_port_filter(self, port):
+ """Refresh security group rules from data store
+
+ Gets called when an port gets added to or removed from
+ the security group the port is a member of or if the
+ group gains or looses a rule.
+ """
+ raise NotImplementedError()
+
+ def remove_port_filter(self, port):
+ """Stop filtering port"""
+ raise NotImplementedError()
+
+ def filter_defer_apply_on(self):
+ """Defer application of filtering rule"""
+ pass
+
+ def filter_defer_apply_off(self):
+ """Turn off deferral of rules and apply the rules now"""
+ pass
+
+ @property
+ def ports(self):
+ """ returns filterd ports"""
+ pass
+
+ @contextlib.contextmanager
+ def defer_apply(self):
+ """defer apply context"""
+ self.filter_defer_apply_on()
+ try:
+ yield
+ finally:
+ self.filter_defer_apply_off()
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012, Nachi Ueno, NTT MCL, Inc.
+# All Rights Reserved.
+#
+# 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.
+
+import netaddr
+
+from quantum.agent import firewall
+from quantum.common import constants
+from quantum.openstack.common import log as logging
+
+
+LOG = logging.getLogger(__name__)
+SG_CHAIN = 'sg-chain'
+INGRESS_DIRECTION = 'ingress'
+EGRESS_DIRECTION = 'egress'
+CHAIN_NAME_PREFIX = {INGRESS_DIRECTION: 'i',
+ EGRESS_DIRECTION: 'o'}
+IPTABLES_DIRECTION = {INGRESS_DIRECTION: 'physdev-out',
+ EGRESS_DIRECTION: 'physdev-in'}
+
+
+class IptablesFirewallDriver(firewall.FirewallDriver):
+ """Driver which enforces security groups through iptables rules."""
+
+ def __init__(self, iptables_manager):
+ self.iptables = iptables_manager
+
+ # list of port which has security group
+ self.filtered_ports = {}
+ self._add_fallback_chain_v4v6()
+
+ @property
+ def ports(self):
+ return self.filtered_ports
+
+ def prepare_port_filter(self, port):
+ LOG.debug(_("Preparing device (%s) filter"), port['device'])
+ self._remove_chains()
+ self.filtered_ports[port['device']] = port
+ # each security group has it own chains
+ self._setup_chains()
+ self.iptables.apply()
+
+ def update_port_filter(self, port):
+ LOG.debug(_("Updating device (%s) filter"), port['device'])
+ if not port['device'] in self.filtered_ports:
+ LOG.info(_('Attempted to update port filter which is not '
+ 'filtered %s') % port['device'])
+ return
+ self._remove_chains()
+ self.filtered_ports[port['device']] = port
+ self._setup_chains()
+ self.iptables.apply()
+
+ def remove_port_filter(self, port):
+ LOG.debug(_("Removing device (%s) filter"), port['device'])
+ if not self.filtered_ports.get(port['device']):
+ LOG.info(_('Attempted to remove port filter which is not '
+ 'filtered %r'), port)
+ return
+ self._remove_chains()
+ self.filtered_ports.pop(port['device'], None)
+ self._setup_chains()
+ self.iptables.apply()
+
+ def _setup_chains(self):
+ """Setup ingress and egress chain for a port. """
+ self._add_chain_by_name_v4v6(SG_CHAIN)
+ for port in self.filtered_ports.values():
+ self._setup_chain(port, INGRESS_DIRECTION)
+ self._setup_chain(port, EGRESS_DIRECTION)
+ self.iptables.ipv4['filter'].add_rule(SG_CHAIN, '-j ACCEPT')
+ self.iptables.ipv6['filter'].add_rule(SG_CHAIN, '-j ACCEPT')
+
+ def _remove_chains(self):
+ """Remove ingress and egress chain for a port"""
+ for port in self.filtered_ports.values():
+ self._remove_chain(port, INGRESS_DIRECTION)
+ self._remove_chain(port, EGRESS_DIRECTION)
+ self._remove_chain_by_name_v4v6(SG_CHAIN)
+
+ def _setup_chain(self, port, DIRECTION):
+ self._add_chain(port, DIRECTION)
+ self._add_rule_by_security_group(port, DIRECTION)
+
+ def _remove_chain(self, port, DIRECTION):
+ chain_name = self._port_chain_name(port, DIRECTION)
+ self._remove_chain_by_name_v4v6(chain_name)
+
+ def _add_fallback_chain_v4v6(self):
+ self.iptables.ipv4['filter'].add_chain('sg-fallback')
+ self.iptables.ipv4['filter'].add_rule('sg-fallback', '-j DROP')
+ self.iptables.ipv6['filter'].add_chain('sg-fallback')
+ self.iptables.ipv6['filter'].add_rule('sg-fallback', '-j DROP')
+
+ def _add_chain_by_name_v4v6(self, chain_name):
+ self.iptables.ipv6['filter'].add_chain(chain_name)
+ self.iptables.ipv4['filter'].add_chain(chain_name)
+
+ def _remove_chain_by_name_v4v6(self, chain_name):
+ self.iptables.ipv4['filter'].ensure_remove_chain(chain_name)
+ self.iptables.ipv6['filter'].ensure_remove_chain(chain_name)
+
+ def _add_rule_to_chain_v4v6(self, chain_name, ipv4_rules, ipv6_rules):
+ for rule in ipv4_rules:
+ self.iptables.ipv4['filter'].add_rule(chain_name, rule)
+
+ for rule in ipv6_rules:
+ self.iptables.ipv6['filter'].add_rule(chain_name, rule)
+
+ def _add_chain(self, port, direction):
+ chain_name = self._port_chain_name(port, direction)
+ self._add_chain_by_name_v4v6(chain_name)
+
+ # Note(nati) jump to the security group chain (SG_CHAIN)
+ # This is needed because the packet may much two rule in port
+ # if the two port is in the same host
+ # We accept the packet at the end of SG_CHAIN.
+
+ # jump to the security group chain
+ device = port['device']
+ jump_rule = ['-m physdev --physdev-is-bridged --%s '
+ '%s -j $%s' % (IPTABLES_DIRECTION[direction],
+ device,
+ SG_CHAIN)]
+ self._add_rule_to_chain_v4v6('FORWARD', jump_rule, jump_rule)
+
+ # jump to the chain based on the device
+ jump_rule = ['-m physdev --physdev-is-bridged --%s '
+ '%s -j $%s' % (IPTABLES_DIRECTION[direction],
+ device,
+ chain_name)]
+ self._add_rule_to_chain_v4v6(SG_CHAIN, jump_rule, jump_rule)
+
+ if direction == EGRESS_DIRECTION:
+ self._add_rule_to_chain_v4v6('INPUT', jump_rule, jump_rule)
+
+ def _split_sgr_by_ethertype(self, security_group_rules):
+ ipv4_sg_rules = []
+ ipv6_sg_rules = []
+ for rule in security_group_rules:
+ if rule.get('ethertype') == constants.IPv4:
+ ipv4_sg_rules.append(rule)
+ elif rule.get('ethertype') == constants.IPv6:
+ if rule.get('protocol') == 'icmp':
+ rule['protocol'] = 'icmpv6'
+ ipv6_sg_rules.append(rule)
+ return ipv4_sg_rules, ipv6_sg_rules
+
+ def _select_sgr_by_direction(self, port, direction):
+ return [rule
+ for rule in port.get('security_group_rules', [])
+ if rule['direction'] == direction]
+
+ def _arp_spoofing_rule(self, port):
+ return ['-m mac ! --mac-source %s -j DROP' % port['mac_address']]
+
+ def _ip_spoofing_rule(self, port, ipv4_rules, ipv6_rules):
+ #Note(nati) allow dhcp or RA packet
+ ipv4_rules += ['-p udp --sport 68 --dport 67 -j RETURN']
+ ipv6_rules += ['-p icmpv6 -j RETURN']
+ for ip in port['fixed_ips']:
+ if netaddr.IPAddress(ip).version == 4:
+ ipv4_rules += ['! -s %s -j DROP' % ip]
+ else:
+ ipv6_rules += ['! -s %s -j DROP' % ip]
+
+ def _drop_dhcp_rule(self):
+ #Note(nati) Drop dhcp packet from VM
+ return ['-p udp --sport 67 --dport 68 -j DROP']
+
+ 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)
+ # split groups by ip version
+ # for ipv4, iptables command is used
+ # for ipv6, iptables6 command is used
+ ipv4_sg_rules, ipv6_sg_rules = self._split_sgr_by_ethertype(
+ security_group_rules)
+ ipv4_iptables_rule = []
+ ipv6_iptables_rule = []
+ if direction == EGRESS_DIRECTION:
+ ipv4_iptables_rule += self._arp_spoofing_rule(port)
+ ipv6_iptables_rule += self._arp_spoofing_rule(port)
+ self._ip_spoofing_rule(port,
+ ipv4_iptables_rule,
+ ipv6_iptables_rule)
+ ipv4_iptables_rule += self._drop_dhcp_rule()
+ ipv4_iptables_rule += self._convert_sgr_to_iptables_rules(
+ ipv4_sg_rules)
+ ipv6_iptables_rule += self._convert_sgr_to_iptables_rules(
+ ipv6_sg_rules)
+ self._add_rule_to_chain_v4v6(chain_name,
+ ipv4_iptables_rule,
+ ipv6_iptables_rule)
+
+ 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:
+ args = ['-j RETURN']
+ args += self._protocol_arg(rule.get('protocol'))
+ args += self._port_arg('dport',
+ rule.get('protocol'),
+ rule.get('port_range_min'),
+ rule.get('port_range_max'))
+ args += self._port_arg('sport',
+ rule.get('protocol'),
+ rule.get('source_port_range_min'),
+ rule.get('source_port_range_max'))
+ args += self._ip_prefix_arg('s',
+ rule.get('source_ip_prefix'))
+ args += self._ip_prefix_arg('d',
+ rule.get('dest_ip_prefix'))
+ iptables_rules += [' '.join(args)]
+
+ iptables_rules += ['-j $sg-fallback']
+
+ return iptables_rules
+
+ def _drop_invalid_packets(self, iptables_rules):
+ # Always drop invalid packets
+ iptables_rules += ['-m state --state ' 'INVALID -j DROP']
+ return iptables_rules
+
+ def _allow_established(self, iptables_rules):
+ # Allow established connections
+ iptables_rules += ['-m state --state ESTABLISHED,RELATED -j RETURN']
+ return iptables_rules
+
+ def _protocol_arg(self, protocol):
+ if protocol:
+ return ['-p', protocol]
+ return []
+
+ def _port_arg(self, direction, protocol, port_range_min, port_range_max):
+ if not (protocol in ['udp', 'tcp'] and port_range_min):
+ return []
+
+ if port_range_min == port_range_max:
+ return ['--%s' % direction, '%s' % (port_range_min,)]
+ else:
+ return ['-m', 'multiport',
+ '--%ss' % direction,
+ '%s:%s' % (port_range_min, port_range_max)]
+
+ def _ip_prefix_arg(self, direction, ip_prefix):
+ #NOTE (nati) : source_group_id is converted to list of source_
+ # ip_prefix in server side
+ if ip_prefix:
+ return ['-%s' % direction, ip_prefix]
+ return []
+
+ def _port_chain_name(self, port, direction):
+ #Note (nati) make chain name short less than 28 char
+ # with extra prefix
+ # ( see comment in iptables_manager )
+ return '%s%s' % (CHAIN_NAME_PREFIX[direction],
+ port['device'][3:13])
+
+ def filter_defer_apply_on(self):
+ self.iptables.defer_apply_on()
+
+ def filter_defer_apply_off(self):
+ self.iptables.defer_apply_off()
import os
from quantum.agent.linux import utils
+from quantum.openstack.common import lockutils
from quantum.openstack.common import log as logging
LOG = logging.getLogger(__name__)
else:
self.unwrapped_chains.add(name)
+ def _select_chain_set(self, wrap):
+ if wrap:
+ return self.chains
+ else:
+ return self.unwrapped_chains
+
+ def ensure_remove_chain(self, name, wrap=True):
+ """Ensure the chain is removed.
+
+ This removal "cascades". All rule in the chain are removed, as are
+ all rules in other chains that jump to it.
+ """
+ chain_set = self._select_chain_set(wrap)
+ if name not in chain_set:
+ return
+
+ self.remove_chain(name, wrap)
+
def remove_chain(self, name, wrap=True):
"""Remove named chain.
If the chain is not found, this is merely logged.
"""
- if wrap:
- chain_set = self.chains
- else:
- chain_set = self.unwrapped_chains
+ chain_set = self._select_chain_set(wrap)
if name not in chain_set:
LOG.warn(('Attempted to remove chain %s which does not exist'),
chain_set.remove(name)
self.rules = filter(lambda r: r.chain != name, self.rules)
-
if wrap:
jump_snippet = '-j %s-%s' % (binary_name, name)
else:
self.use_ipv6 = use_ipv6
self.root_helper = root_helper
self.namespace = namespace
+ self.iptables_apply_deferred = False
self.ipv4 = {'filter': IptablesTable()}
self.ipv6 = {'filter': IptablesTable()}
self.ipv4['nat'].add_chain('float-snat')
self.ipv4['nat'].add_rule('snat', '-j $float-snat')
+ def defer_apply_on(self):
+ self.iptables_apply_deferred = True
+
+ def defer_apply_off(self):
+ self.iptables_apply_deferred = False
+ self._apply()
+
def apply(self):
+ if self.iptables_apply_deferred:
+ return
+
+ self._apply()
+
+ @lockutils.synchronized('iptables', 'quantum-', external=True)
+ def _apply(self):
"""Apply the current in-memory set of iptables rules.
This will blow away any rules left over from previous runs of the
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012, Nachi Ueno, NTT MCL, Inc.
+# All Rights Reserved.
+#
+# 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 quantum.agent.linux import iptables_firewall
+from quantum.agent.linux import iptables_manager
+from quantum.common import topics
+from quantum.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+SG_RPC_VERSION = "1.1"
+
+
+class SecurityGroupServerRpcApiMixin(object):
+ """A mix-in that enable SecurityGroup support in plugin rpc
+ """
+ def security_group_rules_for_devices(self, context, devices):
+ LOG.debug(_("Get security group rules "
+ "for devices via rpc %r"), devices)
+ return self.call(context,
+ self.make_msg('security_group_rules_for_devices',
+ devices=devices),
+ version=SG_RPC_VERSION,
+ topic=self.topic)
+
+
+class SecurityGroupAgentRpcCallbackMixin(object):
+ """A mix-in that enable SecurityGroup agent
+ support in agent implementations.
+ """
+
+ def security_groups_rule_updated(self, context, **kwargs):
+ """ callback for security group rule update
+
+ :param security_groups: list of updated security_groups
+ """
+ security_groups = kwargs.get('security_groups', [])
+ LOG.debug(
+ _("Security group rule updated on remote: %s"), security_groups)
+ self.agent.security_groups_rule_updated(security_groups)
+
+ def security_groups_member_updated(self, context, **kwargs):
+ """ callback for security group member update
+
+ :param security_groups: list of updated security_groups
+ """
+ security_groups = kwargs.get('security_groups', [])
+ LOG.debug(
+ _("Security group member updated on remote: %s"), security_groups)
+ self.agent.security_groups_member_updated(security_groups)
+
+ def security_groups_provider_updated(self, context, **kwargs):
+ """ callback for security group provider update
+
+ """
+ LOG.debug(_("Provider rule updated"))
+ self.agent.security_groups_provider_updated()
+
+
+class SecurityGroupAgentRpcMixin(object):
+ """A mix-in that enable SecurityGroup agent
+ support in agent implementations.
+ """
+
+ def init_firewall(self):
+ LOG.debug(_("Init firewall settings"))
+ ip_manager = iptables_manager.IptablesManager(
+ root_helper=self.root_helper,
+ use_ipv6=True)
+ self.firewall = iptables_firewall.IptablesFirewallDriver(ip_manager)
+
+ def prepare_devices_filter(self, device_ids):
+ if not device_ids:
+ return
+ LOG.info(_("Preparing filters for devices %s"), device_ids)
+ devices = self.plugin_rpc.security_group_rules_for_devices(
+ self.context, list(device_ids))
+ with self.firewall.defer_apply():
+ for device in devices.values():
+ self.firewall.prepare_port_filter(device)
+
+ def security_groups_rule_updated(self, security_groups):
+ LOG.info(_("Security group "
+ "rule updated %r"), security_groups)
+ self._security_group_updated(
+ security_groups,
+ 'security_groups')
+
+ def security_groups_member_updated(self, security_groups):
+ LOG.info(_("Security group "
+ "member updated %r"), security_groups)
+ self._security_group_updated(
+ security_groups,
+ 'security_group_source_groups')
+
+ def _security_group_updated(self, security_groups, attribute):
+ #check need update or not
+ for device in self.firewall.ports.values():
+ if set(device.get(attribute,
+ [])).intersection(
+ set(security_groups)):
+ self.refresh_firewall()
+ return
+
+ def security_groups_provider_updated(self):
+ LOG.info(_("Provider rule updated"))
+ self.refresh_firewall()
+
+ def remove_devices_filter(self, device_ids):
+ if not device_ids:
+ return
+ LOG.info(_("Remove device filter for %r"), device_ids)
+ with self.firewall.defer_apply():
+ for device_id in device_ids:
+ device = self.firewall.ports.get(device_id)
+ if not device:
+ continue
+ self.firewall.remove_port_filter(device)
+
+ def refresh_firewall(self):
+ LOG.info(_("Refresh firewall rules"))
+ device_ids = self.firewall.ports.keys()
+ if not device_ids:
+ return
+ devices = self.plugin_rpc.security_group_rules_for_devices(
+ self.context, device_ids)
+ with self.firewall.defer_apply():
+ for device in devices.values():
+ LOG.debug(_("Update port filter for %s"), device)
+ self.firewall.update_port_filter(device)
+
+
+class SecurityGroupAgentRpcApiMixin(object):
+
+ def _get_security_group_topic(self):
+ return topics.get_topic_name(self.topic,
+ topics.SECURITY_GROUP,
+ topics.UPDATE)
+
+ def security_groups_rule_updated(self, context, security_groups):
+ """ notify rule updated security groups """
+ if not security_groups:
+ return
+ self.fanout_cast(context,
+ self.make_msg('security_groups_rule_updated',
+ security_groups=security_groups),
+ version=SG_RPC_VERSION,
+ topic=self._get_security_group_topic())
+
+ def security_groups_member_updated(self, context, security_groups):
+ """ notify member updated security groups """
+ if not security_groups:
+ return
+ self.fanout_cast(context,
+ self.make_msg('security_groups_member_updated',
+ security_groups=security_groups),
+ version=SG_RPC_VERSION,
+ topic=self._get_security_group_topic())
+
+ def security_groups_provider_updated(self, context):
+ """ notify provider updated security groups """
+ self.fanout_cast(context,
+ self.make_msg('security_groups_provider_updated'),
+ version=SG_RPC_VERSION,
+ topic=self._get_security_group_topic())
DEVICE_OWNER_ROUTER_INTF = "network:router_interface"
DEVICE_OWNER_ROUTER_GW = "network:router_gateway"
DEVICE_OWNER_FLOATINGIP = "network:floatingip"
+DEVICE_OWNER_DHCP = "network:dhcp"
+
FLOATINGIP_KEY = '_floatingips'
INTERFACE_KEY = '_interfaces'
+
+IPv4 = 'IPv4'
+IPv6 = 'IPv6'
NETWORK = 'network'
SUBNET = 'subnet'
PORT = 'port'
+SECURITY_GROUP = 'security_group'
CREATE = 'create'
DELETE = 'delete'
def get_hostname():
return socket.getfqdn()
+
+
+def compare_elements(a, b):
+ """ compare elements if a and b have same elements
+
+ This method doesn't consider ordering
+ """
+ if a is None:
+ a = []
+ if b is None:
+ b = []
+ return set(a) == set(b)
from sqlalchemy.orm import exc
from sqlalchemy.orm import scoped_session
+from quantum.api.v2 import attributes as attr
+from quantum.common import utils
from quantum.db import model_base
from quantum.db import models_v2
from quantum.extensions import securitygroup as ext_sg
def _extend_port_dict_security_group(self, context, port):
filters = {'port_id': [port['id']]}
fields = {'security_group_id': None}
- port[ext_sg.SECURITYGROUP] = []
security_group_id = self._get_port_security_group_bindings(
context, filters, fields)
+
+ port[ext_sg.SECURITYGROUP] = []
for security_group_id in security_group_id:
port[ext_sg.SECURITYGROUP].append(
security_group_id['security_group_id'])
def _process_port_create_security_group(self, context, port_id,
security_group_id):
- if not security_group_id:
+ if not attr.is_attr_set(security_group_id):
return
for security_group_id in security_group_id:
self._create_port_security_group_binding(context, port_id,
def _validate_security_groups_on_port(self, context, port):
p = port['port']
- if not p.get(ext_sg.SECURITYGROUP):
+ if not attr.is_attr_set(p.get(ext_sg.SECURITYGROUP)):
return
+ if p.get('device_owner') and p['device_owner'].startswith('network:'):
+ raise ext_sg.SecurityGroupInvalidDeviceOwner()
valid_groups = self.get_security_groups(context, fields={'id': None})
valid_groups_set = set([x['id'] for x in valid_groups])
if invalid_sg_set:
msg = ' '.join(str(x) for x in invalid_sg_set)
raise ext_sg.SecurityGroupNotFound(id=msg)
+
+ def _ensure_default_security_group_on_port(self, context, port):
+ # we don't apply security groups for dhcp, router
+ if (port['port'].get('device_owner') and
+ port['port']['device_owner'].startswith('network:')):
+ return
+ tenant_id = self._get_tenant_id_for_create(context,
+ port['port'])
+ default_sg = self._ensure_default_security_group(context, tenant_id)
+ if attr.is_attr_set(port['port'].get(ext_sg.SECURITYGROUP)):
+ sgids = port['port'].get(ext_sg.SECURITYGROUP)
+ else:
+ sgids = [default_sg]
+ port['port'][ext_sg.SECURITYGROUP] = sgids
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012, Nachi Ueno, NTT MCL, Inc.
+# All Rights Reserved.
+#
+# 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.
+
+import netaddr
+
+from quantum.common import constants as q_const
+from quantum.db import models_v2
+from quantum.db import securitygroups_db as sg_db
+from quantum.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+IP_MASK = {q_const.IPv4: 32,
+ q_const.IPv6: 128}
+
+
+DIRECTION_IP_PREFIX = {'ingress': 'source_ip_prefix',
+ 'egress': 'dest_ip_prefix'}
+
+
+class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
+
+ def create_security_group_rule(self, context, security_group_rule):
+ bulk_rule = {'security_group_rules': [security_group_rule]}
+ rule = self.create_security_group_rule_bulk_native(context,
+ bulk_rule)[0]
+ sgids = [rule['security_group_id']]
+ self.notifier.security_groups_rule_updated(context, sgids)
+ return rule
+
+ def create_security_group_rule_bulk(self, context,
+ security_group_rule):
+ rules = super(SecurityGroupServerRpcMixin,
+ self).create_security_group_rule_bulk_native(
+ context, security_group_rule)
+ sgids = set([r['security_group_id'] for r in rules])
+ self.notifier.security_groups_rule_updated(context, list(sgids))
+ return rules
+
+ def delete_security_group_rule(self, context, sgrid):
+ rule = self.get_security_group_rule(context, sgrid)
+ super(SecurityGroupServerRpcMixin,
+ self).delete_security_group_rule(context, sgrid)
+ self.notifier.security_groups_rule_updated(context,
+ [rule['security_group_id']])
+
+
+class SecurityGroupServerRpcCallbackMixin(object):
+ """A mix-in that enable SecurityGroup agent
+
+ support in plugin implementations.
+ """
+
+ def security_group_rules_for_devices(self, context, **kwargs):
+ """ return security group rules for each port
+
+ also convert source_group_id rule
+ to source_ip_prefix rule
+
+ :params devices: list of devices
+ :returns: port correspond to the devices with security group rules
+ """
+ devices = kwargs.get('devices')
+
+ ports = {}
+ for device in devices:
+ port = self.get_port_from_device(device)
+ if not port:
+ continue
+ if port['device_owner'].startswith('network:'):
+ continue
+ ports[port['id']] = port
+ return self._security_group_rules_for_ports(context, ports)
+
+ def _select_rules_for_ports(self, context, ports):
+ if not ports:
+ return []
+ sg_binding_port = sg_db.SecurityGroupPortBinding.port_id
+ sg_binding_sgid = sg_db.SecurityGroupPortBinding.security_group_id
+
+ sgr_sgid = sg_db.SecurityGroupRule.security_group_id
+
+ query = context.session.query(sg_db.SecurityGroupPortBinding,
+ sg_db.SecurityGroupRule)
+ query = query.join(sg_db.SecurityGroupRule,
+ sgr_sgid == sg_binding_sgid)
+ query = query.filter(sg_binding_port.in_(ports.keys()))
+ return query.all()
+
+ def _select_ips_for_source_group(self, context, source_group_ids):
+ ips_by_group = {}
+ if not source_group_ids:
+ return ips_by_group
+ for source_group_id in source_group_ids:
+ ips_by_group[source_group_id] = []
+
+ ip_port = models_v2.IPAllocation.port_id
+ sg_binding_port = sg_db.SecurityGroupPortBinding.port_id
+ sg_binding_sgid = sg_db.SecurityGroupPortBinding.security_group_id
+
+ query = context.session.query(sg_binding_sgid,
+ models_v2.IPAllocation.ip_address)
+ query = query.join(models_v2.IPAllocation,
+ ip_port == sg_binding_port)
+ query = query.filter(sg_binding_sgid.in_(source_group_ids))
+ ip_in_db = query.all()
+ for security_group_id, ip_address in ip_in_db:
+ ips_by_group[security_group_id].append(ip_address)
+ return ips_by_group
+
+ def _select_source_group_ids(self, ports):
+ source_group_ids = []
+ for port in ports.values():
+ for rule in port.get('security_group_rules'):
+ source_group_id = rule.get('source_group_id')
+ if source_group_id:
+ source_group_ids.append(source_group_id)
+ return source_group_ids
+
+ def _select_network_ids(self, ports):
+ return set((port['network_id'] for port in ports.values()))
+
+ def _select_dhcp_ips_for_network_ids(self, context, network_ids):
+ if not network_ids:
+ return {}
+ query = context.session.query(models_v2.Port,
+ models_v2.IPAllocation.ip_address)
+ query = query.join(models_v2.IPAllocation)
+ query = query.filter(models_v2.Port.network_id.in_(network_ids))
+ owner = q_const.DEVICE_OWNER_DHCP
+ query = query.filter(models_v2.Port.device_owner == owner)
+ ips = {}
+
+ for network_id in network_ids:
+ ips[network_id] = []
+
+ for port, ip in query.all():
+ ips[port['network_id']].append(ip)
+ return ips
+
+ def _convert_source_group_id_to_ip_prefix(self, context, ports):
+ source_group_ids = self._select_source_group_ids(ports)
+ ips = self._select_ips_for_source_group(context, source_group_ids)
+ for port in ports.values():
+ updated_rule = []
+ for rule in port.get('security_group_rules'):
+ source_group_id = rule.get('source_group_id')
+ direction = rule.get('direction')
+ direction_ip_prefix = DIRECTION_IP_PREFIX[direction]
+ if not source_group_id:
+ updated_rule.append(rule)
+ continue
+
+ port['security_group_source_groups'].append(source_group_id)
+ base_rule = rule
+ for ip in ips[source_group_id]:
+ if ip in port.get('fixed_ips', []):
+ continue
+ ip_rule = base_rule.copy()
+ version = netaddr.IPAddress(ip).version
+ ethertype = 'IPv%s' % version
+ if base_rule['ethertype'] != ethertype:
+ continue
+ ip_rule[direction_ip_prefix] = "%s/%s" % (
+ ip, IP_MASK[ethertype])
+ updated_rule.append(ip_rule)
+ port['security_group_rules'] = updated_rule
+ return ports
+
+ def _add_default_egress_rule(self, port, ethertype, ips):
+ """ Adding default egress rule which allows all egress traffic. """
+ egress_rule = [r for r in port['security_group_rules']
+ if (r['direction'] == 'egress' and
+ r['ethertype'] == ethertype)]
+ if len(egress_rule) > 0:
+ return
+ for ip in port['fixed_ips']:
+ version = netaddr.IPAddress(ip).version
+ if "IPv%s" % version == ethertype:
+ default_egress_rule = {'direction': 'egress',
+ 'ethertype': ethertype}
+ port['security_group_rules'].append(default_egress_rule)
+ return
+
+ def _add_ingress_dhcp_rule(self, port, ips):
+ dhcp_ips = ips.get(port['network_id'])
+ for dhcp_ip in dhcp_ips:
+ if not netaddr.IPAddress(dhcp_ip).version == 4:
+ return
+
+ dhcp_rule = {'direction': 'ingress',
+ 'ethertype': q_const.IPv4,
+ 'protocol': 'udp',
+ 'port_range_min': 68,
+ 'port_range_max': 68,
+ 'source_port_range_min': 67,
+ 'source_port_range_max': 67}
+ dhcp_rule['source_ip_prefix'] = "%s/%s" % (dhcp_ip,
+ IP_MASK[q_const.IPv4])
+ port['security_group_rules'].append(dhcp_rule)
+
+ def _add_ingress_ra_rule(self, port, ips):
+ ra_ips = ips.get(port['network_id'])
+ for ra_ip in ra_ips:
+ if not netaddr.IPAddress(ra_ip).version == 6:
+ return
+
+ ra_rule = {'direction': 'ingress',
+ 'ethertype': q_const.IPv6,
+ 'protocol': 'icmp'}
+ ra_rule['source_ip_prefix'] = "%s/%s" % (ra_ip,
+ IP_MASK[q_const.IPv6])
+ port['security_group_rules'].append(ra_rule)
+
+ def _apply_provider_rule(self, context, ports):
+ network_ids = self._select_network_ids(ports)
+ ips = self._select_dhcp_ips_for_network_ids(context, network_ids)
+ for port in ports.values():
+ self._add_default_egress_rule(port, q_const.IPv4, ips)
+ self._add_default_egress_rule(port, q_const.IPv6, ips)
+ self._add_ingress_ra_rule(port, ips)
+ self._add_ingress_dhcp_rule(port, ips)
+
+ def _security_group_rules_for_ports(self, context, ports):
+ rules_in_db = self._select_rules_for_ports(context, ports)
+ for (binding, rule_in_db) in rules_in_db:
+ port_id = binding['port_id']
+ port = ports[port_id]
+ direction = rule_in_db['direction']
+ rule_dict = {
+ 'security_group_id': rule_in_db['security_group_id'],
+ 'direction': direction,
+ 'ethertype': rule_in_db['ethertype'],
+ }
+ for key in ('protocol', 'port_range_min', 'port_range_max',
+ 'source_ip_prefix', 'source_group_id'):
+ if rule_in_db.get(key):
+ if key == 'source_ip_prefix' and direction == 'egress':
+ rule_dict['dest_ip_prefix'] = rule_in_db[key]
+ continue
+ rule_dict[key] = rule_in_db[key]
+ port['security_group_rules'].append(rule_dict)
+ self._apply_provider_rule(context, ports)
+ return self._convert_source_group_id_to_ip_prefix(context, ports)
message = _("Invalid value for port %(port)s")
+class SecurityGroupInvalidDeviceOwner(qexception.InvalidInput):
+ message = _("Security Group can't be applied to network ports.")
+
+
class SecurityGroupInUse(qexception.InUse):
message = _("Security Group %(id)s in use.")
'ports': {SECURITYGROUP: {'allow_post': True,
'allow_put': True,
'is_visible': True,
- 'default': None}}}
+ 'default': attr.ATTR_NOT_SPECIFIED}}}
security_group_quota_opts = [
cfg.IntOpt('quota_security_group',
default=10,
from quantum.agent.linux import ip_lib
from quantum.agent.linux import utils
from quantum.agent import rpc as agent_rpc
+from quantum.agent import securitygroups_rpc as sg_rpc
from quantum.common import config as logging_config
from quantum.common import topics
from quantum.common import utils as q_utils
LOG.debug(_("Done deleting subinterface %s"), interface)
-class LinuxBridgeRpcCallbacks():
+class LinuxBridgeRpcCallbacks(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
# Set RPC API version to 1.0 by default.
- RPC_API_VERSION = '1.0'
+ # history
+ # 1.1 Support Security Group RPC
+ RPC_API_VERSION = '1.1'
- def __init__(self, context, linux_br):
+ def __init__(self, context, agent):
self.context = context
- self.linux_br = linux_br
+ self.agent = agent
+ self.linux_br = agent.linux_br
def network_delete(self, context, **kwargs):
LOG.debug(_("network_delete received"))
def port_update(self, context, **kwargs):
LOG.debug(_("port_update received"))
port = kwargs.get('port')
+ if 'security_groups' in port:
+ self.agent.refresh_firewall()
+
if port['admin_state_up']:
vlan_id = kwargs.get('vlan_id')
physical_network = kwargs.get('physical_network')
return dispatcher.RpcDispatcher([self])
-class LinuxBridgeQuantumAgentRPC:
+class LinuxBridgePluginApi(agent_rpc.PluginApi,
+ sg_rpc.SecurityGroupServerRpcApiMixin):
+ pass
+
+
+class LinuxBridgeQuantumAgentRPC(sg_rpc.SecurityGroupAgentRpcMixin):
def __init__(self, interface_mappings, polling_interval,
root_helper):
self.root_helper = root_helper
self.setup_linux_bridge(interface_mappings)
self.setup_rpc(interface_mappings.values())
+ self.init_firewall()
def setup_rpc(self, physical_interfaces):
if physical_interfaces:
LOG.info(_("RPC agent_id: %s"), self.agent_id)
self.topic = topics.AGENT
- self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
+ self.plugin_rpc = LinuxBridgePluginApi(topics.PLUGIN)
# RPC network init
self.context = context.get_admin_context_without_session()
# Handle updates from service
self.callbacks = LinuxBridgeRpcCallbacks(self.context,
- self.linux_br)
+ self)
self.dispatcher = self.callbacks.create_rpc_dispatcher()
# Define the listening consumers for the agent
consumers = [[topics.PORT, topics.UPDATE],
- [topics.NETWORK, topics.DELETE]]
+ [topics.NETWORK, topics.DELETE],
+ [topics.SECURITY_GROUP, topics.UPDATE]]
self.connection = agent_rpc.create_consumers(self.dispatcher,
self.topic,
consumers)
def treat_devices_added(self, devices):
resync = False
+ self.prepare_devices_filter(devices)
for device in devices:
LOG.debug(_("Port %s added"), device)
try:
def treat_devices_removed(self, devices):
resync = False
+ self.remove_devices_filter(devices)
for device in devices:
LOG.info(_("Attachment %s removed"), device)
try:
def main():
eventlet.monkey_patch()
cfg.CONF(args=sys.argv, project='quantum')
-
- # (TODO) gary - swap with common logging
logging_config.setup_logging(cfg.CONF)
-
try:
interface_mappings = q_utils.parse_mappings(
cfg.CONF.LINUX_BRIDGE.physical_interface_mappings)
from quantum.common import exceptions as q_exc
import quantum.db.api as db
+from quantum import manager
from quantum.db import models_v2
+from quantum.db import securitygroups_db as sg_db
from quantum.openstack.common import cfg
from quantum.openstack.common import log as logging
from quantum.plugins.linuxbridge.common import config
"""Get port from database"""
LOG.debug(_("get_port_from_device() called"))
session = db.get_session()
- ports = session.query(models_v2.Port).all()
- if not ports:
+ sg_binding_port = sg_db.SecurityGroupPortBinding.port_id
+
+ query = session.query(models_v2.Port,
+ sg_db.SecurityGroupPortBinding.security_group_id)
+ query = query.outerjoin(sg_db.SecurityGroupPortBinding,
+ models_v2.Port.id == sg_binding_port)
+ query = query.filter(models_v2.Port.id.startswith(device))
+ port_and_sgs = query.all()
+ if not port_and_sgs:
return
- for port in ports:
- if port['id'].startswith(device):
- return port
- return
+ port = port_and_sgs[0][0]
+ plugin = manager.QuantumManager.get_plugin()
+ port_dict = plugin._make_port_dict(port)
+ port_dict['security_groups'] = []
+ for port_in_db, sg_id in port_and_sgs:
+ if sg_id:
+ port_dict['security_groups'].append(sg_id)
+ port_dict['security_group_rules'] = []
+ port_dict['security_group_source_groups'] = []
+ port_dict['fixed_ips'] = [ip['ip_address']
+ for ip in port['fixed_ips']]
+ return port_dict
def set_port_status(port_id, status):
import sys
+from quantum.agent import securitygroups_rpc as sg_rpc
from quantum.api.v2 import attributes
from quantum.common import constants as q_const
from quantum.common import exceptions as q_exc
from quantum.common import rpc as q_rpc
from quantum.common import topics
+from quantum.common import utils
from quantum.db import api as db_api
from quantum.db import db_base_plugin_v2
from quantum.db import dhcp_rpc_base
from quantum.db import l3_db
from quantum.db import l3_rpc_base
from quantum.db import quota_db
+from quantum.db import securitygroups_rpc_base as sg_db_rpc
from quantum.extensions import portbindings
from quantum.extensions import providernet as provider
+from quantum.extensions import securitygroup as ext_sg
from quantum.openstack.common import cfg
from quantum.openstack.common import log as logging
from quantum.openstack.common import rpc
class LinuxBridgeRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin,
- l3_rpc_base.L3RpcCallbackMixin):
+ l3_rpc_base.L3RpcCallbackMixin,
+ sg_db_rpc.SecurityGroupServerRpcCallbackMixin):
- # Set RPC API version to 1.0 by default.
- RPC_API_VERSION = '1.0'
+ RPC_API_VERSION = '1.1'
# Device names start with "tap"
+ # history
+ # 1.1 Support Security Group RPC
TAP_PREFIX_LEN = 3
def create_rpc_dispatcher(self):
'''
return q_rpc.PluginRpcDispatcher([self])
+ @classmethod
+ def get_port_from_device(cls, device):
+ port = db.get_port_from_device(device[cls.TAP_PREFIX_LEN:])
+ if port:
+ port['device'] = device
+ return port
+
def get_device_details(self, rpc_context, **kwargs):
"""Agent requests device details"""
agent_id = kwargs.get('agent_id')
device = kwargs.get('device')
LOG.debug(_("Device %(device)s details requested from %(agent_id)s"),
locals())
- port = db.get_port_from_device(device[self.TAP_PREFIX_LEN:])
+ port = self.get_port_from_device(device)
if port:
binding = db.get_network_binding(db_api.get_session(),
port['network_id'])
device = kwargs.get('device')
LOG.debug(_("Device %(device)s no longer exists on %(agent_id)s"),
locals())
- port = db.get_port_from_device(device[self.TAP_PREFIX_LEN:])
+ port = self.get_port_from_device(device)
if port:
entry = {'device': device,
'exists': True}
return entry
-class AgentNotifierApi(proxy.RpcProxy):
+class AgentNotifierApi(proxy.RpcProxy,
+ sg_rpc.SecurityGroupAgentRpcApiMixin):
'''Agent side of the linux bridge rpc API.
API version history:
def __init__(self, topic):
super(AgentNotifierApi, self).__init__(
topic=topic, default_version=self.BASE_RPC_API_VERSION)
+ self.topic = topic
self.topic_network_delete = topics.get_topic_name(topic,
topics.NETWORK,
topics.DELETE)
class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
- l3_db.L3_NAT_db_mixin):
+ l3_db.L3_NAT_db_mixin,
+ sg_db_rpc.SecurityGroupServerRpcMixin):
"""Implement the Quantum abstractions using Linux bridging.
A new VLAN is created for each network. An agent is relied upon
# is qualified by class
__native_bulk_support = True
- supported_extension_aliases = ["provider", "router", "binding", "quotas"]
+ supported_extension_aliases = ["provider", "router", "binding", "quotas",
+ "security-group"]
network_view = "extension:provider_network:view"
network_set = "extension:provider_network:set"
session = context.session
with session.begin(subtransactions=True):
+ #set up default security groups
+ tenant_id = self._get_tenant_id_for_create(
+ context, network['network'])
+ self._ensure_default_security_group(context, tenant_id)
+
if not network_type:
# tenant network
network_type = self.tenant_network_type
session = context.session
with session.begin(subtransactions=True):
binding = db.get_network_binding(session, id)
- result = super(LinuxBridgePluginV2, self).delete_network(context,
- id)
+ super(LinuxBridgePluginV2, self).delete_network(context, id)
if binding.vlan_id != constants.LOCAL_VLAN_ID:
db.release_network(session, binding.physical_network,
binding.vlan_id, self.network_vlan_ranges)
port[portbindings.VIF_TYPE] = portbindings.VIF_TYPE_BRIDGE
return port
- def create_port(self, context, port):
- port = super(LinuxBridgePluginV2, self).create_port(context, port)
- return self._extend_port_dict_binding(context, port)
-
def get_port(self, context, id, fields=None):
port = super(LinuxBridgePluginV2, self).get_port(context, id, fields)
- return self._fields(self._extend_port_dict_binding(context, port),
- fields)
+ self._extend_port_dict_security_group(context, port)
+ self._extend_port_dict_binding(context, port),
+ return self._fields(port, fields)
def get_ports(self, context, filters=None, fields=None):
ports = super(LinuxBridgePluginV2, self).get_ports(context, filters,
fields)
+ #TODO(nati) filter by security group
+ for port in ports:
+ self._extend_port_dict_security_group(context, port)
return [self._fields(self._extend_port_dict_binding(context, port),
fields) for port in ports]
+ def create_port(self, context, port):
+ session = context.session
+ with session.begin(subtransactions=True):
+ self._ensure_default_security_group_on_port(context, port)
+ self._validate_security_groups_on_port(context, port)
+ sgids = port['port'].get(ext_sg.SECURITYGROUP)
+ port = super(LinuxBridgePluginV2,
+ self).create_port(context, port)
+ self._process_port_create_security_group(
+ context, port['id'], sgids)
+ self._extend_port_dict_security_group(context, port)
+ if port['device_owner'] == q_const.DEVICE_OWNER_DHCP:
+ self.notifier.security_groups_provider_updated(context)
+ else:
+ self.notifier.security_groups_member_updated(
+ context, port.get(ext_sg.SECURITYGROUP))
+ return self._extend_port_dict_binding(context, port)
+
def update_port(self, context, id, port):
- original_port = super(LinuxBridgePluginV2, self).get_port(context,
- id)
- port = super(LinuxBridgePluginV2, self).update_port(context, id, port)
+ self._validate_security_groups_on_port(context, port)
+ original_port = self.get_port(context, id)
+ session = context.session
+ port_updated = False
+ with session.begin(subtransactions=True):
+ # delete the port binding and read it with the new rules
+ if ext_sg.SECURITYGROUP in port['port']:
+ self._delete_port_security_group_bindings(context, id)
+ self._process_port_create_security_group(
+ context,
+ id,
+ port['port'][ext_sg.SECURITYGROUP])
+ port_updated = True
+
+ port = super(LinuxBridgePluginV2, self).update_port(
+ context, id, port)
+ self._extend_port_dict_security_group(context, port)
+
if original_port['admin_state_up'] != port['admin_state_up']:
- binding = db.get_network_binding(context.session,
- port['network_id'])
- self.notifier.port_update(context, port,
- binding.physical_network,
- binding.vlan_id)
+ port_updated = True
+
+ if (original_port['fixed_ips'] != port['fixed_ips'] or
+ not utils.compare_elements(
+ original_port.get(ext_sg.SECURITYGROUP),
+ port.get(ext_sg.SECURITYGROUP))):
+ self.notifier.security_groups_member_updated(
+ context, port.get(ext_sg.SECURITYGROUP))
+
+ if port_updated:
+ self._notify_port_updated(context, port)
return self._extend_port_dict_binding(context, port)
def delete_port(self, context, id, l3_port_check=True):
# and l3-router. If so, we should prevent deletion.
if l3_port_check:
self.prevent_l3_port_deletion(context, id)
- self.disassociate_floatingips(context, id)
- return super(LinuxBridgePluginV2, self).delete_port(context, id)
+
+ session = context.session
+ with session.begin(subtransactions=True):
+ self.disassociate_floatingips(context, id)
+ port = self.get_port(context, id)
+ self._delete_port_security_group_bindings(context, id)
+ super(LinuxBridgePluginV2, self).delete_port(context, id)
+ self.notifier.security_groups_member_updated(
+ context, port.get(ext_sg.SECURITYGROUP))
+
+ def _notify_port_updated(self, context, port):
+ binding = db.get_network_binding(context.session,
+ port['network_id'])
+ self.notifier.port_update(context, port,
+ binding.physical_network,
+ binding.vlan_id)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012, Nachi Ueno, NTT MCL, Inc.
+# All Rights Reserved.
+#
+# 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.
+
+import mock
+from mock import call
+
+from quantum.api.v2 import attributes
+from quantum.extensions import securitygroup as ext_sg
+from quantum.plugins.linuxbridge.db import l2network_db_v2 as lb_db
+from quantum.tests.unit import test_extension_security_group as test_sg
+
+PLUGIN_NAME = ('quantum.plugins.linuxbridge.'
+ 'lb_quantum_plugin.LinuxBridgePluginV2')
+AGENT_NAME = ('quantum.plugins.linuxbridge.'
+ 'agent.linuxbridg_quantum_agent.LinuxBridgeQuantumAgentRPC')
+NOTIFIER = ('quantum.plugins.linuxbridge.'
+ 'lb_quantum_plugin.AgentNotifierApi')
+
+
+class LinuxBridgeSecurityGroupsTestCase(test_sg.SecurityGroupDBTestCase):
+ _plugin_name = PLUGIN_NAME
+
+ def setUp(self, plugin=None):
+ self.addCleanup(mock.patch.stopall)
+ notifier_p = mock.patch(NOTIFIER)
+ notifier_cls = notifier_p.start()
+ self.notifier = mock.Mock()
+ notifier_cls.return_value = self.notifier
+ self._attribute_map_bk_ = {}
+ for item in attributes.RESOURCE_ATTRIBUTE_MAP:
+ self._attribute_map_bk_[item] = (attributes.
+ RESOURCE_ATTRIBUTE_MAP[item].
+ copy())
+ super(LinuxBridgeSecurityGroupsTestCase, self).setUp(PLUGIN_NAME)
+
+ def tearDown(self):
+ super(LinuxBridgeSecurityGroupsTestCase, self).tearDown()
+ attributes.RESOURCE_ATTRIBUTE_MAP = self._attribute_map_bk_
+
+
+class TestLinuxBridgeSecurityGroups(LinuxBridgeSecurityGroupsTestCase,
+ test_sg.TestSecurityGroups):
+
+ def test_security_group_rule_updated(self):
+ name = 'webservers'
+ description = 'my webservers'
+ with self.security_group(name, description) as sg:
+ with self.security_group(name, description) as sg2:
+ security_group_id = sg['security_group']['id']
+ direction = "ingress"
+ source_group_id = sg2['security_group']['id']
+ protocol = 'tcp'
+ port_range_min = 88
+ port_range_max = 88
+ with self.security_group_rule(security_group_id, direction,
+ protocol, port_range_min,
+ port_range_max,
+ source_group_id=source_group_id
+ ):
+ pass
+ self.notifier.assert_has_calls(
+ [call.security_groups_rule_updated(mock.ANY,
+ [security_group_id]),
+ call.security_groups_rule_updated(mock.ANY,
+ [security_group_id])])
+
+ def test_security_group_member_updated(self):
+ with self.network() as n:
+ with self.subnet(n):
+ with self.security_group() as sg:
+ security_group_id = sg['security_group']['id']
+ res = self._create_port('json', n['network']['id'])
+ port = self.deserialize('json', res)
+
+ data = {'port': {'fixed_ips': port['port']['fixed_ips'],
+ 'name': port['port']['name'],
+ ext_sg.SECURITYGROUP:
+ [security_group_id]}}
+
+ req = self.new_update_request('ports', data,
+ port['port']['id'])
+ res = self.deserialize('json', req.get_response(self.api))
+ self.assertEquals(res['port'][ext_sg.SECURITYGROUP][0],
+ security_group_id)
+ self._delete('ports', port['port']['id'])
+ self.notifier.assert_has_calls(
+ [call.security_groups_member_updated(
+ mock.ANY, [mock.ANY]),
+ call.security_groups_member_updated(
+ mock.ANY, [security_group_id])])
+
+
+class TestLinuxBridgeSecurityGroupsDB(LinuxBridgeSecurityGroupsTestCase):
+ def test_security_group_get_port_from_device(self):
+ with self.network() as n:
+ with self.subnet(n):
+ with self.security_group() as sg:
+ security_group_id = sg['security_group']['id']
+ res = self._create_port('json', n['network']['id'])
+ port = self.deserialize('json', res)
+ fixed_ips = port['port']['fixed_ips']
+ data = {'port': {'fixed_ips': fixed_ips,
+ 'name': port['port']['name'],
+ ext_sg.SECURITYGROUP:
+ [security_group_id]}}
+
+ req = self.new_update_request('ports', data,
+ port['port']['id'])
+ res = self.deserialize('json', req.get_response(self.api))
+ port_id = res['port']['id']
+ device_id = port_id[:8]
+ port_dict = lb_db.get_port_from_device(device_id)
+ self.assertEqual(port_id, port_dict['id'])
+ self.assertEqual([security_group_id],
+ port_dict[ext_sg.SECURITYGROUP])
+ self.assertEqual([], port_dict['security_group_rules'])
+ self.assertEqual([fixed_ips[0]['ip_address']],
+ port_dict['fixed_ips'])
+ self._delete('ports', port['port']['id'])
+
+ def test_security_group_get_port_from_device_with_no_port(self):
+ port_dict = lb_db.get_port_from_device('bad_device_id')
+ self.assertEqual(None, port_dict)
from quantum.openstack.common import cfg
from quantum.tests.unit import test_db_plugin as test_plugin
+PLUGIN_NAME = ('quantum.plugins.linuxbridge.'
+ 'lb_quantum_plugin.LinuxBridgePluginV2')
-class LinuxBridgePluginV2TestCase(test_plugin.QuantumDbPluginV2TestCase):
- _plugin_name = ('quantum.plugins.linuxbridge.'
- 'lb_quantum_plugin.LinuxBridgePluginV2')
+class LinuxBridgePluginV2TestCase(test_plugin.QuantumDbPluginV2TestCase):
+ _plugin_name = PLUGIN_NAME
def setUp(self):
- super(LinuxBridgePluginV2TestCase, self).setUp(self._plugin_name)
+ super(LinuxBridgePluginV2TestCase, self).setUp(PLUGIN_NAME)
class TestLinuxBridgeBasicGet(test_plugin.TestBasicGet,
data = {'security_group_rule': {'security_group_id': security_group_id,
'direction': direction,
'protocol': protocol,
+ 'ethertype': ethertype,
'port_range_min': port_range_min,
'port_range_max': port_range_max,
'tenant_id': tenant_id,
def update_port(self, context, id, port):
session = context.session
with session.begin(subtransactions=True):
- self._validate_security_groups_on_port(context, port)
- # delete the port binding and read it with the new rules
- self._delete_port_security_group_bindings(context, id)
- self._process_port_create_security_group(context, id,
- port['port'].get(
- ext_sg.SECURITYGROUP))
+ if ext_sg.SECURITYGROUP in port['port']:
+ self._validate_security_groups_on_port(context, port)
+ # delete the port binding and read it with the new rules
+ self._delete_port_security_group_bindings(context, id)
+ self._process_port_create_security_group(context, id,
+ port['port'].get(
+ ext_sg.SECURITYGROUP))
port = super(SecurityGroupTestPlugin, self).update_port(
context, id, port)
self._extend_port_dict_security_group(context, port)
test_config['plugin_name_v2'] = DB_PLUGIN_KLASS
ext_mgr = SecurityGroupTestExtensionManager()
test_config['extension_manager'] = ext_mgr
- super(SecurityGroupDBTestCase, self).setUp()
+ super(SecurityGroupDBTestCase, self).setUp(plugin)
class TestSecurityGroups(SecurityGroupDBTestCase):
res = self.deserialize('json', req.get_response(self.api))
self.assertEqual(res['port'][ext_sg.SECURITYGROUP][0],
sg['security_group']['id'])
+
+ # Test update port without security group
+ data = {'port': {'fixed_ips': port['port']['fixed_ips'],
+ 'name': port['port']['name']}}
+
+ req = self.new_update_request('ports', data,
+ port['port']['id'])
+ res = self.deserialize('json', req.get_response(self.api))
+ self.assertEqual(res['port'][ext_sg.SECURITYGROUP][0],
+ sg['security_group']['id'])
+
self._delete('ports', port['port']['id'])
def test_update_port_with_multiple_security_groups(self):
port = self.deserialize('json', res)
data = {'port': {'fixed_ips': port['port']['fixed_ips'],
- 'name': port['port']['name']}}
+ 'name': port['port']['name'],
+ 'security_groups': []}}
req = self.new_update_request('ports', data,
port['port']['id'])
res = self.deserialize('json', req.get_response(self.api))
- self.assertEqual(res['port'][ext_sg.SECURITYGROUP], [])
+ self.assertEqual(res['port'].get(ext_sg.SECURITYGROUP),
+ [])
self._delete('ports', port['port']['id'])
def test_create_port_with_bad_security_group(self):
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012, Nachi Ueno, NTT MCL, Inc.
+# All Rights Reserved.
+#
+# 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.
+
+import mock
+from mock import call
+import unittest2 as unittest
+
+from quantum.agent.linux.iptables_firewall import IptablesFirewallDriver
+from quantum.tests.unit import test_api_v2
+
+_uuid = test_api_v2._uuid
+FAKE_PREFIX = {'IPv4': '10.0.0.0/24',
+ 'IPv6': 'fe80::0/48'}
+FAKE_IP = {'IPv4': '10.0.0.1',
+ 'IPv6': 'fe80::1'}
+
+
+class IptablesFirewallTestCase(unittest.TestCase):
+ def setUp(self):
+ self.utils_exec_p = mock.patch(
+ 'quantum.agent.linux.utils.execute')
+ self.utils_exec = self.utils_exec_p.start()
+ self.iptables_cls_p = mock.patch(
+ 'quantum.agent.linux.iptables_manager.IptablesManager')
+ iptables_cls = self.iptables_cls_p.start()
+ self.iptables_inst = mock.Mock()
+ self.v4filter_inst = mock.Mock()
+ self.v6filter_inst = mock.Mock()
+ self.iptables_inst.ipv4 = {'filter': self.v4filter_inst}
+ self.iptables_inst.ipv6 = {'filter': self.v6filter_inst}
+ iptables_cls.return_value = self.iptables_inst
+
+ self.firewall = IptablesFirewallDriver(self.iptables_inst)
+
+ def tearDown(self):
+ self.iptables_cls_p.stop()
+ self.utils_exec_p.stop()
+
+ def _fake_port(self):
+ return {'device': 'tapfake_dev',
+ 'mac_address': 'ff:ff:ff:ff',
+ 'fixed_ips': [FAKE_IP['IPv4'],
+ FAKE_IP['IPv6']]}
+
+ def test_prepare_port_filter_with_no_sg(self):
+ port = self._fake_port()
+ self.firewall.prepare_port_filter(port)
+ calls = [call.add_chain('sg-fallback'),
+ call.add_rule('sg-fallback', '-j DROP'),
+ call.ensure_remove_chain('sg-chain'),
+ call.add_chain('sg-chain'),
+ call.add_chain('ifake_dev'),
+ call.add_rule('FORWARD',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-out tapfake_dev '
+ '-j $sg-chain'),
+ call.add_rule('sg-chain',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-out tapfake_dev '
+ '-j $ifake_dev'),
+ call.add_rule(
+ 'ifake_dev', '-m state --state INVALID -j DROP'),
+ call.add_rule(
+ 'ifake_dev',
+ '-m state --state ESTABLISHED,RELATED -j RETURN'),
+ call.add_rule('ifake_dev', '-j $sg-fallback'),
+ call.add_chain('ofake_dev'),
+ call.add_rule('FORWARD',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-in tapfake_dev '
+ '-j $sg-chain'),
+ call.add_rule('sg-chain',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-in tapfake_dev '
+ '-j $ofake_dev'),
+ call.add_rule('INPUT',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-in tapfake_dev '
+ '-j $ofake_dev'),
+ call.add_rule(
+ 'ofake_dev', '-m mac ! --mac-source ff:ff:ff:ff -j DROP'),
+ call.add_rule(
+ 'ofake_dev',
+ '-p udp --sport 68 --dport 67 -j RETURN'),
+ call.add_rule('ofake_dev', '! -s 10.0.0.1 -j DROP'),
+ call.add_rule(
+ 'ofake_dev',
+ '-p udp --sport 67 --dport 68 -j DROP'),
+ call.add_rule(
+ 'ofake_dev', '-m state --state INVALID -j DROP'),
+ call.add_rule(
+ 'ofake_dev',
+ '-m state --state ESTABLISHED,RELATED -j RETURN'),
+ call.add_rule('ofake_dev', '-j $sg-fallback'),
+ call.add_rule('sg-chain', '-j ACCEPT')]
+
+ self.v4filter_inst.assert_has_calls(calls)
+
+ def test_filter_ipv4_ingress(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress'}
+ ingress = call.add_rule('ifake_dev', '-j RETURN')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_prefix(self):
+ prefix = FAKE_PREFIX['IPv4']
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'source_ip_prefix': prefix}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -s %s' % prefix)
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_tcp(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'protocol': 'tcp'}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p tcp')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_tcp_prefix(self):
+ prefix = FAKE_PREFIX['IPv4']
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'protocol': 'tcp',
+ 'source_ip_prefix': prefix}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p tcp -s %s' % prefix)
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_icmp(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'protocol': 'icmp'}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p icmp')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_icmp_prefix(self):
+ prefix = FAKE_PREFIX['IPv4']
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'protocol': 'icmp',
+ 'source_ip_prefix': prefix}
+ ingress = call.add_rule(
+ 'ifake_dev', '-j RETURN -p icmp -s %s' % prefix)
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_tcp_port(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'protocol': 'tcp',
+ 'port_range_min': 10,
+ 'port_range_max': 10}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p tcp --dport 10')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_tcp_mport(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'protocol': 'tcp',
+ 'port_range_min': 10,
+ 'port_range_max': 100}
+ ingress = call.add_rule(
+ 'ifake_dev',
+ '-j RETURN -p tcp -m multiport --dports 10:100')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_tcp_mport_prefix(self):
+ prefix = FAKE_PREFIX['IPv4']
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'protocol': 'tcp',
+ 'port_range_min': 10,
+ 'port_range_max': 100,
+ 'source_ip_prefix': prefix}
+ ingress = call.add_rule(
+ 'ifake_dev',
+ '-j RETURN -p tcp -m multiport '
+ '--dports 10:100 -s %s' % prefix)
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_udp(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'protocol': 'udp'}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p udp')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_udp_prefix(self):
+ prefix = FAKE_PREFIX['IPv4']
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'protocol': 'udp',
+ 'source_ip_prefix': prefix}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p udp -s %s' % prefix)
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_udp_port(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'protocol': 'udp',
+ 'port_range_min': 10,
+ 'port_range_max': 10}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p udp --dport 10')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_udp_mport(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'protocol': 'udp',
+ 'port_range_min': 10,
+ 'port_range_max': 100}
+ ingress = call.add_rule(
+ 'ifake_dev',
+ '-j RETURN -p udp -m multiport --dports 10:100')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_ingress_udp_mport_prefix(self):
+ prefix = FAKE_PREFIX['IPv4']
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'ingress',
+ 'protocol': 'udp',
+ 'port_range_min': 10,
+ 'port_range_max': 100,
+ 'source_ip_prefix': prefix}
+ ingress = call.add_rule(
+ 'ifake_dev',
+ '-j RETURN -p udp -m multiport '
+ '--dports 10:100 -s %s' % prefix)
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress'}
+ egress = call.add_rule('ofake_dev', '-j RETURN')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_prefix(self):
+ prefix = FAKE_PREFIX['IPv4']
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'source_ip_prefix': prefix}
+ egress = call.add_rule('ofake_dev', '-j RETURN -s %s' % prefix)
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_tcp(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'protocol': 'tcp'}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p tcp')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_tcp_prefix(self):
+ prefix = FAKE_PREFIX['IPv4']
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'protocol': 'tcp',
+ 'source_ip_prefix': prefix}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p tcp -s %s' % prefix)
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_icmp(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'protocol': 'icmp'}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p icmp')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_icmp_prefix(self):
+ prefix = FAKE_PREFIX['IPv4']
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'protocol': 'icmp',
+ 'source_ip_prefix': prefix}
+ egress = call.add_rule(
+ 'ofake_dev', '-j RETURN -p icmp -s %s' % prefix)
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_tcp_port(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'protocol': 'tcp',
+ 'port_range_min': 10,
+ 'port_range_max': 10}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p tcp --dport 10')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_tcp_mport(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'protocol': 'tcp',
+ 'port_range_min': 10,
+ 'port_range_max': 100}
+ egress = call.add_rule(
+ 'ofake_dev',
+ '-j RETURN -p tcp -m multiport --dports 10:100')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_tcp_mport_prefix(self):
+ prefix = FAKE_PREFIX['IPv4']
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'protocol': 'tcp',
+ 'port_range_min': 10,
+ 'port_range_max': 100,
+ 'source_ip_prefix': prefix}
+ egress = call.add_rule(
+ 'ofake_dev',
+ '-j RETURN -p tcp -m multiport '
+ '--dports 10:100 -s %s' % prefix)
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_udp(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'protocol': 'udp'}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p udp')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_udp_prefix(self):
+ prefix = FAKE_PREFIX['IPv4']
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'protocol': 'udp',
+ 'source_ip_prefix': prefix}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p udp -s %s' % prefix)
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_udp_port(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'protocol': 'udp',
+ 'port_range_min': 10,
+ 'port_range_max': 10}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p udp --dport 10')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_udp_mport(self):
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'protocol': 'udp',
+ 'port_range_min': 10,
+ 'port_range_max': 100}
+ egress = call.add_rule(
+ 'ofake_dev',
+ '-j RETURN -p udp -m multiport --dports 10:100')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv4_egress_udp_mport_prefix(self):
+ prefix = FAKE_PREFIX['IPv4']
+ rule = {'ethertype': 'IPv4',
+ 'direction': 'egress',
+ 'protocol': 'udp',
+ 'port_range_min': 10,
+ 'port_range_max': 100,
+ 'source_ip_prefix': prefix}
+ egress = call.add_rule(
+ 'ofake_dev',
+ '-j RETURN -p udp -m multiport '
+ '--dports 10:100 -s %s' % prefix)
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress'}
+ ingress = call.add_rule('ifake_dev', '-j RETURN')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_prefix(self):
+ prefix = FAKE_PREFIX['IPv6']
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'source_ip_prefix': prefix}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -s %s' % prefix)
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_tcp(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'protocol': 'tcp'}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p tcp')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_tcp_prefix(self):
+ prefix = FAKE_PREFIX['IPv6']
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'protocol': 'tcp',
+ 'source_ip_prefix': prefix}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p tcp -s %s' % prefix)
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_tcp_port(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'protocol': 'tcp',
+ 'port_range_min': 10,
+ 'port_range_max': 10}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p tcp --dport 10')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_icmp(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'protocol': 'icmp'}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p icmpv6')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_icmp_prefix(self):
+ prefix = FAKE_PREFIX['IPv6']
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'protocol': 'icmp',
+ 'source_ip_prefix': prefix}
+ ingress = call.add_rule(
+ 'ifake_dev', '-j RETURN -p icmpv6 -s %s' % prefix)
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_tcp_mport(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'protocol': 'tcp',
+ 'port_range_min': 10,
+ 'port_range_max': 100}
+ ingress = call.add_rule(
+ 'ifake_dev',
+ '-j RETURN -p tcp -m multiport --dports 10:100')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_tcp_mport_prefix(self):
+ prefix = FAKE_PREFIX['IPv6']
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'protocol': 'tcp',
+ 'port_range_min': 10,
+ 'port_range_max': 100,
+ 'source_ip_prefix': prefix}
+ ingress = call.add_rule(
+ 'ifake_dev',
+ '-j RETURN -p tcp -m multiport '
+ '--dports 10:100 -s %s' % prefix)
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_udp(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'protocol': 'udp'}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p udp')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_udp_prefix(self):
+ prefix = FAKE_PREFIX['IPv6']
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'protocol': 'udp',
+ 'source_ip_prefix': prefix}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p udp -s %s' % prefix)
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_udp_port(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'protocol': 'udp',
+ 'port_range_min': 10,
+ 'port_range_max': 10}
+ ingress = call.add_rule('ifake_dev', '-j RETURN -p udp --dport 10')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_udp_mport(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'protocol': 'udp',
+ 'port_range_min': 10,
+ 'port_range_max': 100}
+ ingress = call.add_rule(
+ 'ifake_dev',
+ '-j RETURN -p udp -m multiport --dports 10:100')
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_ingress_udp_mport_prefix(self):
+ prefix = FAKE_PREFIX['IPv6']
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'ingress',
+ 'protocol': 'udp',
+ 'port_range_min': 10,
+ 'port_range_max': 100,
+ 'source_ip_prefix': prefix}
+ ingress = call.add_rule(
+ 'ifake_dev',
+ '-j RETURN -p udp -m multiport '
+ '--dports 10:100 -s %s' % prefix)
+ egress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress'}
+ egress = call.add_rule('ofake_dev', '-j RETURN')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_prefix(self):
+ prefix = FAKE_PREFIX['IPv6']
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'source_ip_prefix': prefix}
+ egress = call.add_rule('ofake_dev', '-j RETURN -s %s' % prefix)
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_tcp(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'protocol': 'tcp'}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p tcp')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_tcp_prefix(self):
+ prefix = FAKE_PREFIX['IPv6']
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'protocol': 'tcp',
+ 'source_ip_prefix': prefix}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p tcp -s %s' % prefix)
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_icmp(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'protocol': 'icmp'}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p icmpv6')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_icmp_prefix(self):
+ prefix = FAKE_PREFIX['IPv6']
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'protocol': 'icmp',
+ 'source_ip_prefix': prefix}
+ egress = call.add_rule(
+ 'ofake_dev', '-j RETURN -p icmpv6 -s %s' % prefix)
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_tcp_port(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'protocol': 'tcp',
+ 'port_range_min': 10,
+ 'port_range_max': 10}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p tcp --dport 10')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_tcp_mport(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'protocol': 'tcp',
+ 'port_range_min': 10,
+ 'port_range_max': 100}
+ egress = call.add_rule(
+ 'ofake_dev',
+ '-j RETURN -p tcp -m multiport --dports 10:100')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_tcp_mport_prefix(self):
+ prefix = FAKE_PREFIX['IPv6']
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'protocol': 'tcp',
+ 'port_range_min': 10,
+ 'port_range_max': 100,
+ 'source_ip_prefix': prefix}
+ egress = call.add_rule(
+ 'ofake_dev',
+ '-j RETURN -p tcp -m multiport '
+ '--dports 10:100 -s %s' % prefix)
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_udp(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'protocol': 'udp'}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p udp')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_udp_prefix(self):
+ prefix = FAKE_PREFIX['IPv6']
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'protocol': 'udp',
+ 'source_ip_prefix': prefix}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p udp -s %s' % prefix)
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_udp_port(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'protocol': 'udp',
+ 'port_range_min': 10,
+ 'port_range_max': 10}
+ egress = call.add_rule('ofake_dev', '-j RETURN -p udp --dport 10')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_udp_mport(self):
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'protocol': 'udp',
+ 'port_range_min': 10,
+ 'port_range_max': 100}
+ egress = call.add_rule(
+ 'ofake_dev',
+ '-j RETURN -p udp -m multiport --dports 10:100')
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def test_filter_ipv6_egress_udp_mport_prefix(self):
+ prefix = FAKE_PREFIX['IPv6']
+ rule = {'ethertype': 'IPv6',
+ 'direction': 'egress',
+ 'protocol': 'udp',
+ 'port_range_min': 10,
+ 'port_range_max': 100,
+ 'source_ip_prefix': prefix}
+ egress = call.add_rule(
+ 'ofake_dev',
+ '-j RETURN -p udp -m multiport '
+ '--dports 10:100 -s %s' % prefix)
+ ingress = None
+ self._test_prepare_port_filter(rule, ingress, egress)
+
+ def _test_prepare_port_filter(self,
+ rule,
+ ingress_expected_call=None,
+ egress_expected_call=None):
+ port = self._fake_port()
+ ethertype = rule['ethertype']
+ prefix = FAKE_IP[ethertype]
+ filter_inst = self.v4filter_inst
+ dhcp_rule = call.add_rule(
+ 'ofake_dev',
+ '-p udp --sport 68 --dport 67 -j RETURN')
+
+ if ethertype == 'IPv6':
+ filter_inst = self.v6filter_inst
+ dhcp_rule = call.add_rule('ofake_dev', '-p icmpv6 -j RETURN')
+
+ sg = [rule]
+ port['security_group_rules'] = sg
+ self.firewall.prepare_port_filter(port)
+ calls = [call.add_chain('sg-fallback'),
+ call.add_rule('sg-fallback', '-j DROP'),
+ call.ensure_remove_chain('sg-chain'),
+ call.add_chain('sg-chain'),
+ call.add_chain('ifake_dev'),
+ call.add_rule('FORWARD',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-out tapfake_dev '
+ '-j $sg-chain'),
+ call.add_rule('sg-chain',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-out tapfake_dev '
+ '-j $ifake_dev'),
+ call.add_rule(
+ 'ifake_dev', '-m state --state INVALID -j DROP'),
+ call.add_rule(
+ 'ifake_dev',
+ '-m state --state ESTABLISHED,RELATED -j RETURN')]
+
+ if ingress_expected_call:
+ calls.append(ingress_expected_call)
+
+ calls += [call.add_rule('ifake_dev', '-j $sg-fallback'),
+ call.add_chain('ofake_dev'),
+ call.add_rule('FORWARD',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-in tapfake_dev '
+ '-j $sg-chain'),
+ call.add_rule('sg-chain',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-in tapfake_dev '
+ '-j $ofake_dev'),
+ call.add_rule('INPUT',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-in tapfake_dev '
+ '-j $ofake_dev'),
+ call.add_rule(
+ 'ofake_dev',
+ '-m mac ! --mac-source ff:ff:ff:ff -j DROP'),
+ dhcp_rule,
+ call.add_rule('ofake_dev', '! -s %s -j DROP' % prefix)]
+
+ if ethertype == 'IPv4':
+ calls.append(call.add_rule(
+ 'ofake_dev',
+ '-p udp --sport 67 --dport 68 -j DROP'))
+
+ calls += [call.add_rule(
+ 'ofake_dev', '-m state --state INVALID -j DROP'),
+ call.add_rule(
+ 'ofake_dev',
+ '-m state --state ESTABLISHED,RELATED -j RETURN')]
+
+ if egress_expected_call:
+ calls.append(egress_expected_call)
+
+ calls += [call.add_rule('ofake_dev', '-j $sg-fallback'),
+ call.add_rule('sg-chain', '-j ACCEPT')]
+
+ filter_inst.assert_has_calls(calls)
+
+ def test_update_delete_port_filter(self):
+ port = self._fake_port()
+ port['security_group_rules'] = [{'ethertype': 'IPv4',
+ 'direction': 'ingress'}]
+ self.firewall.prepare_port_filter(port)
+ port['security_group_rules'] = [{'ethertype': 'IPv4',
+ 'direction': 'egress'}]
+ self.firewall.update_port_filter(port)
+ self.firewall.update_port_filter({'device': 'no-exist-device'})
+ self.firewall.remove_port_filter(port)
+ self.firewall.remove_port_filter({'device': 'no-exist-device'})
+ calls = [call.add_chain('sg-fallback'),
+ call.add_rule('sg-fallback', '-j DROP'),
+ call.ensure_remove_chain('sg-chain'),
+ call.add_chain('sg-chain'),
+ call.add_chain('ifake_dev'),
+ call.add_rule(
+ 'FORWARD',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-out tapfake_dev -j $sg-chain'),
+ call.add_rule(
+ 'sg-chain',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-out tapfake_dev -j $ifake_dev'),
+ call.add_rule(
+ 'ifake_dev', '-m state --state INVALID -j DROP'),
+ call.add_rule(
+ 'ifake_dev',
+ '-m state --state ESTABLISHED,RELATED -j RETURN'),
+ call.add_rule('ifake_dev', '-j RETURN'),
+ call.add_rule('ifake_dev', '-j $sg-fallback'),
+ call.add_chain('ofake_dev'),
+ call.add_rule(
+ 'FORWARD',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-in tapfake_dev -j $sg-chain'),
+ call.add_rule(
+ 'sg-chain',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-in tapfake_dev -j $ofake_dev'),
+ call.add_rule(
+ 'INPUT',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-in tapfake_dev -j $ofake_dev'),
+ call.add_rule(
+ 'ofake_dev',
+ '-m mac ! --mac-source ff:ff:ff:ff -j DROP'),
+ call.add_rule(
+ 'ofake_dev',
+ '-p udp --sport 68 --dport 67 -j RETURN'),
+ call.add_rule(
+ 'ofake_dev',
+ '! -s 10.0.0.1 -j DROP'),
+ call.add_rule(
+ 'ofake_dev',
+ '-p udp --sport 67 --dport 68 -j DROP'),
+ call.add_rule(
+ 'ofake_dev', '-m state --state INVALID -j DROP'),
+ call.add_rule(
+ 'ofake_dev',
+ '-m state --state ESTABLISHED,RELATED -j RETURN'),
+ call.add_rule('ofake_dev', '-j $sg-fallback'),
+ call.add_rule('sg-chain', '-j ACCEPT'),
+ call.ensure_remove_chain('ifake_dev'),
+ call.ensure_remove_chain('ofake_dev'),
+ call.ensure_remove_chain('sg-chain'),
+ call.add_chain('sg-chain'),
+ call.add_chain('ifake_dev'),
+ call.add_rule(
+ 'FORWARD',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-out tapfake_dev -j $sg-chain'),
+ call.add_rule(
+ 'sg-chain',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-out tapfake_dev -j $ifake_dev'),
+ call.add_rule(
+ 'ifake_dev',
+ '-m state --state INVALID -j DROP'),
+ call.add_rule(
+ 'ifake_dev',
+ '-m state --state ESTABLISHED,RELATED -j RETURN'),
+ call.add_rule('ifake_dev', '-j $sg-fallback'),
+ call.add_chain('ofake_dev'),
+ call.add_rule(
+ 'FORWARD',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-in tapfake_dev -j $sg-chain'),
+ call.add_rule(
+ 'sg-chain',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-in tapfake_dev -j $ofake_dev'),
+ call.add_rule(
+ 'INPUT',
+ '-m physdev --physdev-is-bridged '
+ '--physdev-in tapfake_dev -j $ofake_dev'),
+ call.add_rule(
+ 'ofake_dev',
+ '-m mac ! --mac-source ff:ff:ff:ff -j DROP'),
+ call.add_rule(
+ 'ofake_dev', '-p udp --sport 68 --dport 67 -j RETURN'),
+ call.add_rule(
+ 'ofake_dev', '! -s 10.0.0.1 -j DROP'),
+ call.add_rule(
+ 'ofake_dev', '-p udp --sport 67 --dport 68 -j DROP'),
+ call.add_rule(
+ 'ofake_dev', '-m state --state INVALID -j DROP'),
+ call.add_rule(
+ 'ofake_dev',
+ '-m state --state ESTABLISHED,RELATED -j RETURN'),
+ call.add_rule('ofake_dev', '-j RETURN'),
+ call.add_rule('ofake_dev', '-j $sg-fallback'),
+ call.add_rule('sg-chain', '-j ACCEPT'),
+ call.ensure_remove_chain('ifake_dev'),
+ call.ensure_remove_chain('ofake_dev'),
+ call.ensure_remove_chain('sg-chain'),
+ call.add_chain('sg-chain')]
+
+ self.v4filter_inst.assert_has_calls(calls)
+
+ def test_remove_unknown_port(self):
+ port = self._fake_port()
+ self.firewall.remove_port_filter(port)
+ # checking no exception occures
+ self.v4filter_inst.assert_has_calls([])
+
+ def test_defer_apply(self):
+ with self.firewall.defer_apply():
+ pass
+ self.iptables_inst.assert_has_calls([call.defer_apply_on(),
+ call.defer_apply_off()])
+
+ def test_filter_defer_with_exception(self):
+ try:
+ with self.firewall.defer_apply():
+ raise Exception("same exception")
+ except:
+ pass
+ self.iptables_inst.assert_has_calls([call.defer_apply_on(),
+ call.defer_apply_off()])
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012, Nachi Ueno, NTT MCL, Inc.
+# All Rights Reserved.
+#
+# 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 contextlib import nested
+import mock
+from mock import call
+import unittest2 as unittest
+
+import mox
+
+from quantum.agent import firewall as firewall_base
+from quantum.agent.linux import iptables_manager
+from quantum.agent import rpc as agent_rpc
+from quantum.agent import securitygroups_rpc as sg_rpc
+from quantum import context
+from quantum.db import securitygroups_rpc_base as sg_db_rpc
+from quantum.openstack.common.rpc import proxy
+from quantum.tests.unit import test_extension_security_group as test_sg
+from quantum.tests.unit import test_iptables_firewall as test_fw
+
+
+class FakeSGCallback(sg_db_rpc.SecurityGroupServerRpcCallbackMixin):
+ def get_port_from_device(self, device):
+ device = self.devices.get(device)
+ if device:
+ device['security_group_rules'] = []
+ device['security_group_source_groups'] = []
+ device['fixed_ips'] = [ip['ip_address']
+ for ip in device['fixed_ips']]
+ return device
+
+
+class SGServerRpcCallBackMixinTestCase(test_sg.SecurityGroupDBTestCase):
+ def setUp(self):
+ super(SGServerRpcCallBackMixinTestCase, self).setUp()
+ self.rpc = FakeSGCallback()
+
+ def test_security_group_rules_for_devices_ipv4_ingress(self):
+ fake_prefix = test_fw.FAKE_PREFIX['IPv4']
+ with self.network() as n:
+ with nested(self.subnet(n),
+ self.security_group()) as (subnet_v4,
+ sg1):
+ sg1_id = sg1['security_group']['id']
+ rule1 = self._build_security_group_rule(
+ sg1_id,
+ 'ingress', 'tcp', '22',
+ '22')
+ rule2 = self._build_security_group_rule(
+ sg1_id,
+ 'ingress', 'tcp', '23',
+ '23', fake_prefix)
+ rules = {
+ 'security_group_rules': [rule1['security_group_rule'],
+ rule2['security_group_rule']]}
+ res = self._create_security_group_rule('json', rules)
+ self.deserialize('json', res)
+ self.assertEquals(res.status_int, 201)
+
+ res1 = self._create_port(
+ 'json', n['network']['id'],
+ security_groups=[sg1_id])
+ ports_rest1 = self.deserialize('json', res1)
+ port_id1 = ports_rest1['port']['id']
+ self.rpc.devices = {port_id1: ports_rest1['port']}
+ devices = [port_id1, 'no_exist_device']
+ ctx = context.get_admin_context()
+ ports_rpc = self.rpc.security_group_rules_for_devices(
+ ctx, devices=devices)
+ port_rpc = ports_rpc[port_id1]
+ expected = [{'direction': 'ingress',
+ 'protocol': 'tcp', 'ethertype': 'IPv4',
+ 'port_range_max': 22,
+ 'security_group_id': sg1_id,
+ 'port_range_min': 22},
+ {'direction': 'ingress', 'protocol': 'tcp',
+ 'ethertype': 'IPv4',
+ 'port_range_max': 23, 'security_group_id': sg1_id,
+ 'port_range_min': 23,
+ 'source_ip_prefix': fake_prefix},
+ {'ethertype': 'IPv4', 'direction': 'egress'},
+ ]
+ self.assertEquals(port_rpc['security_group_rules'],
+ expected)
+ self._delete('ports', port_id1)
+
+ def test_security_group_rules_for_devices_ipv4_egress(self):
+ fake_prefix = test_fw.FAKE_PREFIX['IPv4']
+ with self.network() as n:
+ with nested(self.subnet(n),
+ self.security_group()) as (subnet_v4,
+ sg1):
+ sg1_id = sg1['security_group']['id']
+ rule1 = self._build_security_group_rule(
+ sg1_id,
+ 'egress', 'tcp', '22',
+ '22')
+ rule2 = self._build_security_group_rule(
+ sg1_id,
+ 'egress', 'udp', '23',
+ '23', fake_prefix)
+ rules = {
+ 'security_group_rules': [rule1['security_group_rule'],
+ rule2['security_group_rule']]}
+ res = self._create_security_group_rule('json', rules)
+ self.deserialize('json', res)
+ self.assertEquals(res.status_int, 201)
+
+ res1 = self._create_port(
+ 'json', n['network']['id'],
+ security_groups=[sg1_id])
+ ports_rest1 = self.deserialize('json', res1)
+ port_id1 = ports_rest1['port']['id']
+ self.rpc.devices = {port_id1: ports_rest1['port']}
+ devices = [port_id1, 'no_exist_device']
+ ctx = context.get_admin_context()
+ ports_rpc = self.rpc.security_group_rules_for_devices(
+ ctx, devices=devices)
+ port_rpc = ports_rpc[port_id1]
+ expected = [{'direction': 'egress',
+ 'protocol': 'tcp', 'ethertype': 'IPv4',
+ 'port_range_max': 22,
+ 'security_group_id': sg1_id,
+ 'port_range_min': 22},
+ {'direction': 'egress', 'protocol': 'udp',
+ 'ethertype': 'IPv4',
+ 'port_range_max': 23, 'security_group_id': sg1_id,
+ 'port_range_min': 23,
+ 'dest_ip_prefix': fake_prefix},
+ ]
+ self.assertEquals(port_rpc['security_group_rules'],
+ expected)
+ self._delete('ports', port_id1)
+
+ def test_security_group_rules_for_devices_ipv4_source_group(self):
+
+ with self.network() as n:
+ with nested(self.subnet(n),
+ self.security_group(),
+ self.security_group()) as (subnet_v4,
+ sg1,
+ sg2):
+ sg1_id = sg1['security_group']['id']
+ sg2_id = sg2['security_group']['id']
+ rule1 = self._build_security_group_rule(
+ sg1_id,
+ 'ingress', 'tcp', '24',
+ '25', source_group_id=sg2['security_group']['id'])
+ rules = {
+ 'security_group_rules': [rule1['security_group_rule']]}
+ res = self._create_security_group_rule('json', rules)
+ self.deserialize('json', res)
+ self.assertEquals(res.status_int, 201)
+
+ res1 = self._create_port(
+ 'json', n['network']['id'],
+ security_groups=[sg1_id,
+ sg2_id])
+ ports_rest1 = self.deserialize('json', res1)
+ port_id1 = ports_rest1['port']['id']
+ self.rpc.devices = {port_id1: ports_rest1['port']}
+ devices = [port_id1, 'no_exist_device']
+
+ res2 = self._create_port(
+ 'json', n['network']['id'],
+ security_groups=[sg2_id])
+ ports_rest2 = self.deserialize('json', res2)
+ port_id2 = ports_rest2['port']['id']
+ ctx = context.get_admin_context()
+ ports_rpc = self.rpc.security_group_rules_for_devices(
+ ctx, devices=devices)
+ port_rpc = ports_rpc[port_id1]
+ expected = [{'direction': u'ingress',
+ 'source_ip_prefix': u'10.0.0.3/32',
+ 'protocol': u'tcp', 'ethertype': u'IPv4',
+ 'port_range_max': 25, 'port_range_min': 24,
+ 'source_group_id': sg2_id,
+ 'security_group_id': sg1_id},
+ {'ethertype': 'IPv4', 'direction': 'egress'},
+ ]
+ self.assertEquals(port_rpc['security_group_rules'],
+ expected)
+ self._delete('ports', port_id1)
+ self._delete('ports', port_id2)
+
+ def test_security_group_rules_for_devices_ipv6_ingress(self):
+ fake_prefix = test_fw.FAKE_PREFIX['IPv6']
+ with self.network() as n:
+ with nested(self.subnet(n,
+ cidr=fake_prefix,
+ ip_version=6),
+ self.security_group()) as (subnet_v6,
+ sg1):
+ sg1_id = sg1['security_group']['id']
+ rule1 = self._build_security_group_rule(
+ sg1_id,
+ 'ingress', 'tcp', '22',
+ '22',
+ ethertype='IPv6')
+ rule2 = self._build_security_group_rule(
+ sg1_id,
+ 'ingress', 'udp', '23',
+ '23', fake_prefix,
+ ethertype='IPv6')
+ rules = {
+ 'security_group_rules': [rule1['security_group_rule'],
+ rule2['security_group_rule']]}
+ res = self._create_security_group_rule('json', rules)
+ self.deserialize('json', res)
+ self.assertEquals(res.status_int, 201)
+
+ res1 = self._create_port(
+ 'json', n['network']['id'],
+ fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}],
+ security_groups=[sg1_id])
+ ports_rest1 = self.deserialize('json', res1)
+ port_id1 = ports_rest1['port']['id']
+ self.rpc.devices = {port_id1: ports_rest1['port']}
+ devices = [port_id1, 'no_exist_device']
+ ctx = context.get_admin_context()
+ ports_rpc = self.rpc.security_group_rules_for_devices(
+ ctx, devices=devices)
+ port_rpc = ports_rpc[port_id1]
+ expected = [{'direction': 'ingress',
+ 'protocol': 'tcp', 'ethertype': 'IPv6',
+ 'port_range_max': 22,
+ 'security_group_id': sg1_id,
+ 'port_range_min': 22},
+ {'direction': 'ingress', 'protocol': 'udp',
+ 'ethertype': 'IPv6',
+ 'port_range_max': 23, 'security_group_id': sg1_id,
+ 'port_range_min': 23,
+ 'source_ip_prefix': fake_prefix},
+ {'ethertype': 'IPv6', 'direction': 'egress'},
+ ]
+ self.assertEquals(port_rpc['security_group_rules'],
+ expected)
+ self._delete('ports', port_id1)
+
+ def test_security_group_rules_for_devices_ipv6_egress(self):
+ fake_prefix = test_fw.FAKE_PREFIX['IPv6']
+ with self.network() as n:
+ with nested(self.subnet(n,
+ cidr=fake_prefix,
+ ip_version=6),
+ self.security_group()) as (subnet_v6,
+ sg1):
+ sg1_id = sg1['security_group']['id']
+ rule1 = self._build_security_group_rule(
+ sg1_id,
+ 'egress', 'tcp', '22',
+ '22',
+ ethertype='IPv6')
+ rule2 = self._build_security_group_rule(
+ sg1_id,
+ 'egress', 'udp', '23',
+ '23', fake_prefix,
+ ethertype='IPv6')
+ rules = {
+ 'security_group_rules': [rule1['security_group_rule'],
+ rule2['security_group_rule']]}
+ res = self._create_security_group_rule('json', rules)
+ self.deserialize('json', res)
+ self.assertEquals(res.status_int, 201)
+
+ res1 = self._create_port(
+ 'json', n['network']['id'],
+ fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}],
+ security_groups=[sg1_id])
+ ports_rest1 = self.deserialize('json', res1)
+ port_id1 = ports_rest1['port']['id']
+ self.rpc.devices = {port_id1: ports_rest1['port']}
+ devices = [port_id1, 'no_exist_device']
+
+ ctx = context.get_admin_context()
+ ports_rpc = self.rpc.security_group_rules_for_devices(
+ ctx, devices=devices)
+ port_rpc = ports_rpc[port_id1]
+ expected = [{'direction': 'egress',
+ 'protocol': 'tcp', 'ethertype': 'IPv6',
+ 'port_range_max': 22,
+ 'security_group_id': sg1_id,
+ 'port_range_min': 22},
+ {'direction': 'egress', 'protocol': 'udp',
+ 'ethertype': 'IPv6',
+ 'port_range_max': 23, 'security_group_id': sg1_id,
+ 'port_range_min': 23,
+ 'dest_ip_prefix': fake_prefix},
+ ]
+ self.assertEquals(port_rpc['security_group_rules'],
+ expected)
+ self._delete('ports', port_id1)
+
+ def test_security_group_rules_for_devices_ipv6_source_group(self):
+ fake_prefix = test_fw.FAKE_PREFIX['IPv6']
+ with self.network() as n:
+ with nested(self.subnet(n,
+ cidr=fake_prefix,
+ ip_version=6),
+ self.security_group(),
+ self.security_group()) as (subnet_v6,
+ sg1,
+ sg2):
+ sg1_id = sg1['security_group']['id']
+ sg2_id = sg2['security_group']['id']
+ rule1 = self._build_security_group_rule(
+ sg1_id,
+ 'ingress', 'tcp', '24',
+ '25',
+ ethertype='IPv6',
+ source_group_id=sg2['security_group']['id'])
+ rules = {
+ 'security_group_rules': [rule1['security_group_rule']]}
+ res = self._create_security_group_rule('json', rules)
+ self.deserialize('json', res)
+ self.assertEquals(res.status_int, 201)
+
+ res1 = self._create_port(
+ 'json', n['network']['id'],
+ fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}],
+ security_groups=[sg1_id,
+ sg2_id])
+ ports_rest1 = self.deserialize('json', res1)
+ port_id1 = ports_rest1['port']['id']
+ self.rpc.devices = {port_id1: ports_rest1['port']}
+ devices = [port_id1, 'no_exist_device']
+
+ res2 = self._create_port(
+ 'json', n['network']['id'],
+ fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}],
+ security_groups=[sg2_id])
+ ports_rest2 = self.deserialize('json', res2)
+ port_id2 = ports_rest2['port']['id']
+
+ ctx = context.get_admin_context()
+ ports_rpc = self.rpc.security_group_rules_for_devices(
+ ctx, devices=devices)
+ port_rpc = ports_rpc[port_id1]
+ expected = [{'direction': 'ingress',
+ 'source_ip_prefix': 'fe80::3/128',
+ 'protocol': 'tcp', 'ethertype': 'IPv6',
+ 'port_range_max': 25, 'port_range_min': 24,
+ 'source_group_id': sg2_id,
+ 'security_group_id': sg1_id},
+ {'ethertype': 'IPv6', 'direction': 'egress'},
+ ]
+ self.assertEquals(port_rpc['security_group_rules'],
+ expected)
+ self._delete('ports', port_id1)
+ self._delete('ports', port_id2)
+
+
+class SGAgentRpcCallBackMixinTestCase(unittest.TestCase):
+ def setUp(self):
+ self.rpc = sg_rpc.SecurityGroupAgentRpcCallbackMixin()
+ self.rpc.agent = mock.Mock()
+
+ def test_security_groups_rule_updated(self):
+ self.rpc.security_groups_rule_updated(None,
+ security_groups=['fake_sgid'])
+ self.rpc.agent.assert_has_calls(
+ [call.security_groups_rule_updated(['fake_sgid'])])
+
+ def test_security_groups_member_updated(self):
+ self.rpc.security_groups_member_updated(None,
+ security_groups=['fake_sgid'])
+ self.rpc.agent.assert_has_calls(
+ [call.security_groups_member_updated(['fake_sgid'])])
+
+ def test_security_groups_provider_updated(self):
+ self.rpc.security_groups_provider_updated(None)
+ self.rpc.agent.assert_has_calls(
+ [call.security_groups_provider_updated()])
+
+
+class SecurityGroupAgentRpcTestCase(unittest.TestCase):
+ def setUp(self):
+ self.agent = sg_rpc.SecurityGroupAgentRpcMixin()
+ self.agent.context = None
+ self.addCleanup(mock.patch.stopall)
+ mock.patch('quantum.agent.linux.iptables_manager').start()
+ self.agent.root_helper = 'sudo'
+ self.agent.init_firewall()
+ self.firewall = mock.Mock()
+ firewall_object = firewall_base.FirewallDriver()
+ self.firewall.defer_apply.side_effect = firewall_object.defer_apply
+ self.agent.firewall = self.firewall
+ rpc = mock.Mock()
+ self.agent.plugin_rpc = rpc
+ self.fake_device = {'device': 'fake_device',
+ 'security_groups': ['fake_sgid1', 'fake_sgid2'],
+ 'security_group_source_groups': ['fake_sgid2'],
+ 'security_group_rules': [{'security_group_id':
+ 'fake_sgid1',
+ 'source_group_id':
+ 'fake_sgid2'}]}
+ fake_devices = {'fake_device': self.fake_device}
+ self.firewall.ports = fake_devices
+ rpc.security_group_rules_for_devices.return_value = fake_devices
+
+ def test_prepare_and_remove_devices_filter(self):
+ self.agent.prepare_devices_filter(['fake_device'])
+ self.agent.remove_devices_filter(['fake_device'])
+ # ignore device which is not filtered
+ self.firewall.assert_has_calls([call.defer_apply(),
+ call.prepare_port_filter(
+ self.fake_device),
+ call.defer_apply(),
+ call.remove_port_filter(
+ self.fake_device),
+ ])
+
+ def test_security_groups_rule_updated(self):
+ self.agent.refresh_firewall = mock.Mock()
+ self.agent.prepare_devices_filter(['fake_port_id'])
+ self.agent.security_groups_rule_updated(['fake_sgid1', 'fake_sgid3'])
+ self.agent.refresh_firewall.assert_has_calls(
+ [call.refresh_firewall()])
+
+ def test_security_groups_rule_not_updated(self):
+ self.agent.refresh_firewall = mock.Mock()
+ self.agent.prepare_devices_filter(['fake_port_id'])
+ self.agent.security_groups_rule_updated(['fake_sgid3', 'fake_sgid4'])
+ self.agent.refresh_firewall.assert_has_calls([])
+
+ def test_security_groups_member_updated(self):
+ self.agent.refresh_firewall = mock.Mock()
+ self.agent.prepare_devices_filter(['fake_port_id'])
+ self.agent.security_groups_member_updated(['fake_sgid2', 'fake_sgid3'])
+ self.agent.refresh_firewall.assert_has_calls(
+ [call.refresh_firewall()])
+
+ def test_security_groups_member_not_updated(self):
+ self.agent.refresh_firewall = mock.Mock()
+ self.agent.prepare_devices_filter(['fake_port_id'])
+ self.agent.security_groups_member_updated(['fake_sgid3', 'fake_sgid4'])
+ self.agent.refresh_firewall.assert_has_calls([])
+
+ def test_security_groups_provider_updated(self):
+ self.agent.refresh_firewall = mock.Mock()
+ self.agent.security_groups_provider_updated()
+ self.agent.refresh_firewall.assert_has_calls(
+ [call.refresh_firewall()])
+
+ def test_refresh_firewall(self):
+ self.agent.prepare_devices_filter(['fake_port_id'])
+ self.agent.refresh_firewall()
+ calls = [call.defer_apply(),
+ call.prepare_port_filter(self.fake_device),
+ call.defer_apply(),
+ call.update_port_filter(self.fake_device)]
+ self.firewall.assert_has_calls(calls)
+
+
+class FakeSGRpcApi(agent_rpc.PluginApi,
+ sg_rpc.SecurityGroupServerRpcApiMixin):
+ pass
+
+
+class SecurityGroupServerRpcApiTestCase(unittest.TestCase):
+ def setUp(self):
+ self.rpc = FakeSGRpcApi('fake_topic')
+ self.rpc.call = mock.Mock()
+
+ def test_security_group_rules_for_devices(self):
+ self.rpc.security_group_rules_for_devices(None, ['fake_device'])
+ self.rpc.call.assert_has_calls(
+ [call(None,
+ {'args':
+ {'devices': ['fake_device']},
+ 'method':
+ 'security_group_rules_for_devices'},
+ version=sg_rpc.SG_RPC_VERSION,
+ topic='fake_topic')])
+
+
+class FakeSGNotifierAPI(proxy.RpcProxy,
+ sg_rpc.SecurityGroupAgentRpcApiMixin):
+ pass
+
+
+class SecurityGroupAgentRpcApiTestCase(unittest.TestCase):
+ def setUp(self):
+ self.notifier = FakeSGNotifierAPI(topic='fake',
+ default_version='1.0')
+ self.notifier.fanout_cast = mock.Mock()
+
+ def test_security_groups_rule_updated(self):
+ self.notifier.security_groups_rule_updated(
+ None, security_groups=['fake_sgid'])
+ self.notifier.fanout_cast.assert_has_calls(
+ [call(None,
+ {'args':
+ {'security_groups': ['fake_sgid']},
+ 'method': 'security_groups_rule_updated'},
+ version=sg_rpc.SG_RPC_VERSION,
+ topic='fake-security_group-update')])
+
+ def test_security_groups_member_updated(self):
+ self.notifier.security_groups_member_updated(
+ None, security_groups=['fake_sgid'])
+ self.notifier.fanout_cast.assert_has_calls(
+ [call(None,
+ {'args':
+ {'security_groups': ['fake_sgid']},
+ 'method': 'security_groups_member_updated'},
+ version=sg_rpc.SG_RPC_VERSION,
+ topic='fake-security_group-update')])
+
+ def test_security_groups_rule_not_updated(self):
+ self.notifier.security_groups_rule_updated(
+ None, security_groups=[])
+ self.assertEquals(False, self.notifier.fanout_cast.called)
+
+ def test_security_groups_member_not_updated(self):
+ self.notifier.security_groups_member_updated(
+ None, security_groups=[])
+ self.assertEquals(False, self.notifier.fanout_cast.called)
+
+#Note(nati) bn -> binary_name
+# id -> device_id
+
+PHYSDEV_RULE = '-m physdev --physdev-is-bridged'
+
+IPTABLES_ARG = {'bn': iptables_manager.binary_name,
+ 'physdev': PHYSDEV_RULE}
+
+CHAINS_NAT = 'OUTPUT|POSTROUTING|PREROUTING|float-snat|snat'
+IPTABLES_ARG['chains'] = CHAINS_NAT
+
+IPTABLES_NAT = """:%(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]
+:quantum-postrouting-bottom - [0:0]
+-A PREROUTING -j %(bn)s-PREROUTING
+-A OUTPUT -j %(bn)s-OUTPUT
+-A POSTROUTING -j %(bn)s-POSTROUTING
+-A POSTROUTING -j quantum-postrouting-bottom
+-A quantum-postrouting-bottom -j %(bn)s-snat
+-A %(bn)s-snat -j %(bn)s-float-snat
+""" % IPTABLES_ARG
+
+CHAINS_EMPTY = 'FORWARD|INPUT|OUTPUT|local|sg-chain|sg-fallback'
+CHAINS_1 = CHAINS_EMPTY + '|i_port1|o_port1'
+CHAINS_2 = CHAINS_1 + '|i_port2|o_port2'
+
+IPTABLES_ARG['chains'] = CHAINS_1
+
+IPTABLES_FILTER_1 = """:%(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]
+:quantum-filter-top - [0:0]
+-A FORWARD -j quantum-filter-top
+-A OUTPUT -j quantum-filter-top
+-A quantum-filter-top -j %(bn)s-local
+-A INPUT -j %(bn)s-INPUT
+-A OUTPUT -j %(bn)s-OUTPUT
+-A FORWARD -j %(bn)s-FORWARD
+-A %(bn)s-sg-fallback -j DROP
+-A %(bn)s-FORWARD %(physdev)s --physdev-out tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-out tap_port1 -j %(bn)s-i_port1
+-A %(bn)s-i_port1 -m state --state INVALID -j DROP
+-A %(bn)s-i_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-i_port1 -j RETURN -p udp --dport 68 --sport 67 -s 10.0.0.2
+-A %(bn)s-i_port1 -j RETURN -p tcp --dport 22
+-A %(bn)s-i_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-in tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-INPUT %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-o_port1 -m mac ! --mac-source 12:34:56:78:9a:bc -j DROP
+-A %(bn)s-o_port1 -p udp --sport 68 --dport 67 -j RETURN
+-A %(bn)s-o_port1 ! -s 10.0.0.3 -j DROP
+-A %(bn)s-o_port1 -p udp --sport 67 --dport 68 -j DROP
+-A %(bn)s-o_port1 -m state --state INVALID -j DROP
+-A %(bn)s-o_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-o_port1 -j RETURN
+-A %(bn)s-o_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-sg-chain -j ACCEPT
+""" % IPTABLES_ARG
+
+IPTABLES_FILTER_1_2 = """:%(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]
+:quantum-filter-top - [0:0]
+-A FORWARD -j quantum-filter-top
+-A OUTPUT -j quantum-filter-top
+-A quantum-filter-top -j %(bn)s-local
+-A INPUT -j %(bn)s-INPUT
+-A OUTPUT -j %(bn)s-OUTPUT
+-A FORWARD -j %(bn)s-FORWARD
+-A %(bn)s-sg-fallback -j DROP
+-A %(bn)s-FORWARD %(physdev)s --physdev-out tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-out tap_port1 -j %(bn)s-i_port1
+-A %(bn)s-i_port1 -m state --state INVALID -j DROP
+-A %(bn)s-i_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-i_port1 -j RETURN -p udp --dport 68 --sport 67 -s 10.0.0.2
+-A %(bn)s-i_port1 -j RETURN -p tcp --dport 22
+-A %(bn)s-i_port1 -j RETURN -s 10.0.0.4
+-A %(bn)s-i_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-in tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-INPUT %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-o_port1 -m mac ! --mac-source 12:34:56:78:9a:bc -j DROP
+-A %(bn)s-o_port1 -p udp --sport 68 --dport 67 -j RETURN
+-A %(bn)s-o_port1 ! -s 10.0.0.3 -j DROP
+-A %(bn)s-o_port1 -p udp --sport 67 --dport 68 -j DROP
+-A %(bn)s-o_port1 -m state --state INVALID -j DROP
+-A %(bn)s-o_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-o_port1 -j RETURN
+-A %(bn)s-o_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-sg-chain -j ACCEPT
+""" % IPTABLES_ARG
+
+IPTABLES_ARG['chains'] = CHAINS_2
+
+IPTABLES_FILTER_2 = """:%(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]
+:quantum-filter-top - [0:0]
+-A FORWARD -j quantum-filter-top
+-A OUTPUT -j quantum-filter-top
+-A quantum-filter-top -j %(bn)s-local
+-A INPUT -j %(bn)s-INPUT
+-A OUTPUT -j %(bn)s-OUTPUT
+-A FORWARD -j %(bn)s-FORWARD
+-A %(bn)s-sg-fallback -j DROP
+-A %(bn)s-FORWARD %(physdev)s --physdev-out tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-out tap_port1 -j %(bn)s-i_port1
+-A %(bn)s-i_port1 -m state --state INVALID -j DROP
+-A %(bn)s-i_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-i_port1 -j RETURN -p udp --dport 68 --sport 67 -s 10.0.0.2
+-A %(bn)s-i_port1 -j RETURN -p tcp --dport 22
+-A %(bn)s-i_port1 -j RETURN -s 10.0.0.4
+-A %(bn)s-i_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-in tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-INPUT %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-o_port1 -m mac ! --mac-source 12:34:56:78:9a:bc -j DROP
+-A %(bn)s-o_port1 -p udp --sport 68 --dport 67 -j RETURN
+-A %(bn)s-o_port1 ! -s 10.0.0.3 -j DROP
+-A %(bn)s-o_port1 -p udp --sport 67 --dport 68 -j DROP
+-A %(bn)s-o_port1 -m state --state INVALID -j DROP
+-A %(bn)s-o_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-o_port1 -j RETURN
+-A %(bn)s-o_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-out tap_port2 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-out tap_port2 -j %(bn)s-i_port2
+-A %(bn)s-i_port2 -m state --state INVALID -j DROP
+-A %(bn)s-i_port2 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-i_port2 -j RETURN -p udp --dport 68 --sport 67 -s 10.0.0.2
+-A %(bn)s-i_port2 -j RETURN -p tcp --dport 22
+-A %(bn)s-i_port2 -j RETURN -s 10.0.0.3
+-A %(bn)s-i_port2 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-in tap_port2 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-in tap_port2 -j %(bn)s-o_port2
+-A %(bn)s-INPUT %(physdev)s --physdev-in tap_port2 -j %(bn)s-o_port2
+-A %(bn)s-o_port2 -m mac ! --mac-source 12:34:56:78:9a:bd -j DROP
+-A %(bn)s-o_port2 -p udp --sport 68 --dport 67 -j RETURN
+-A %(bn)s-o_port2 ! -s 10.0.0.4 -j DROP
+-A %(bn)s-o_port2 -p udp --sport 67 --dport 68 -j DROP
+-A %(bn)s-o_port2 -m state --state INVALID -j DROP
+-A %(bn)s-o_port2 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-o_port2 -j RETURN
+-A %(bn)s-o_port2 -j %(bn)s-sg-fallback
+-A %(bn)s-sg-chain -j ACCEPT
+""" % IPTABLES_ARG
+
+IPTABLES_FILTER_2_2 = """:%(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]
+:quantum-filter-top - [0:0]
+-A FORWARD -j quantum-filter-top
+-A OUTPUT -j quantum-filter-top
+-A quantum-filter-top -j %(bn)s-local
+-A INPUT -j %(bn)s-INPUT
+-A OUTPUT -j %(bn)s-OUTPUT
+-A FORWARD -j %(bn)s-FORWARD
+-A %(bn)s-sg-fallback -j DROP
+-A %(bn)s-FORWARD %(physdev)s --physdev-out tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-out tap_port1 -j %(bn)s-i_port1
+-A %(bn)s-i_port1 -m state --state INVALID -j DROP
+-A %(bn)s-i_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-i_port1 -j RETURN -p udp --dport 68 --sport 67 -s 10.0.0.2
+-A %(bn)s-i_port1 -j RETURN -p tcp --dport 22
+-A %(bn)s-i_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-in tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-INPUT %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-o_port1 -m mac ! --mac-source 12:34:56:78:9a:bc -j DROP
+-A %(bn)s-o_port1 -p udp --sport 68 --dport 67 -j RETURN
+-A %(bn)s-o_port1 ! -s 10.0.0.3 -j DROP
+-A %(bn)s-o_port1 -p udp --sport 67 --dport 68 -j DROP
+-A %(bn)s-o_port1 -m state --state INVALID -j DROP
+-A %(bn)s-o_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-o_port1 -j RETURN
+-A %(bn)s-o_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-out tap_port2 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-out tap_port2 -j %(bn)s-i_port2
+-A %(bn)s-i_port2 -m state --state INVALID -j DROP
+-A %(bn)s-i_port2 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-i_port2 -j RETURN -p udp --dport 68 --sport 67 -s 10.0.0.2
+-A %(bn)s-i_port2 -j RETURN -p tcp --dport 22
+-A %(bn)s-i_port2 -j RETURN -s 10.0.0.3
+-A %(bn)s-i_port2 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-in tap_port2 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-in tap_port2 -j %(bn)s-o_port2
+-A %(bn)s-INPUT %(physdev)s --physdev-in tap_port2 -j %(bn)s-o_port2
+-A %(bn)s-o_port2 -m mac ! --mac-source 12:34:56:78:9a:bd -j DROP
+-A %(bn)s-o_port2 -p udp --sport 68 --dport 67 -j RETURN
+-A %(bn)s-o_port2 ! -s 10.0.0.4 -j DROP
+-A %(bn)s-o_port2 -p udp --sport 67 --dport 68 -j DROP
+-A %(bn)s-o_port2 -m state --state INVALID -j DROP
+-A %(bn)s-o_port2 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-o_port2 -j RETURN
+-A %(bn)s-o_port2 -j %(bn)s-sg-fallback
+-A %(bn)s-sg-chain -j ACCEPT
+""" % IPTABLES_ARG
+
+IPTABLES_FILTER_2_3 = """:%(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]
+:quantum-filter-top - [0:0]
+-A FORWARD -j quantum-filter-top
+-A OUTPUT -j quantum-filter-top
+-A quantum-filter-top -j %(bn)s-local
+-A INPUT -j %(bn)s-INPUT
+-A OUTPUT -j %(bn)s-OUTPUT
+-A FORWARD -j %(bn)s-FORWARD
+-A %(bn)s-sg-fallback -j DROP
+-A %(bn)s-FORWARD %(physdev)s --physdev-out tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-out tap_port1 -j %(bn)s-i_port1
+-A %(bn)s-i_port1 -m state --state INVALID -j DROP
+-A %(bn)s-i_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-i_port1 -j RETURN -p udp --dport 68 --sport 67 -s 10.0.0.2
+-A %(bn)s-i_port1 -j RETURN -p tcp --dport 22
+-A %(bn)s-i_port1 -j RETURN -s 10.0.0.4
+-A %(bn)s-i_port1 -j RETURN -p icmp
+-A %(bn)s-i_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-in tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-INPUT %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-o_port1 -m mac ! --mac-source 12:34:56:78:9a:bc -j DROP
+-A %(bn)s-o_port1 -p udp --sport 68 --dport 67 -j RETURN
+-A %(bn)s-o_port1 ! -s 10.0.0.3 -j DROP
+-A %(bn)s-o_port1 -p udp --sport 67 --dport 68 -j DROP
+-A %(bn)s-o_port1 -m state --state INVALID -j DROP
+-A %(bn)s-o_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-o_port1 -j RETURN
+-A %(bn)s-o_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-out tap_port2 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-out tap_port2 -j %(bn)s-i_port2
+-A %(bn)s-i_port2 -m state --state INVALID -j DROP
+-A %(bn)s-i_port2 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-i_port2 -j RETURN -p udp --dport 68 --sport 67 -s 10.0.0.2
+-A %(bn)s-i_port2 -j RETURN -p tcp --dport 22
+-A %(bn)s-i_port2 -j RETURN -s 10.0.0.3
+-A %(bn)s-i_port2 -j RETURN -p icmp
+-A %(bn)s-i_port2 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-in tap_port2 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-in tap_port2 -j %(bn)s-o_port2
+-A %(bn)s-INPUT %(physdev)s --physdev-in tap_port2 -j %(bn)s-o_port2
+-A %(bn)s-o_port2 -m mac ! --mac-source 12:34:56:78:9a:bd -j DROP
+-A %(bn)s-o_port2 -p udp --sport 68 --dport 67 -j RETURN
+-A %(bn)s-o_port2 ! -s 10.0.0.4 -j DROP
+-A %(bn)s-o_port2 -p udp --sport 67 --dport 68 -j DROP
+-A %(bn)s-o_port2 -m state --state INVALID -j DROP
+-A %(bn)s-o_port2 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-o_port2 -j RETURN
+-A %(bn)s-o_port2 -j %(bn)s-sg-fallback
+-A %(bn)s-sg-chain -j ACCEPT
+""" % IPTABLES_ARG
+
+IPTABLES_ARG['chains'] = CHAINS_EMPTY
+IPTABLES_FILTER_EMPTY = """:%(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]
+:quantum-filter-top - [0:0]
+-A FORWARD -j quantum-filter-top
+-A OUTPUT -j quantum-filter-top
+-A quantum-filter-top -j %(bn)s-local
+-A INPUT -j %(bn)s-INPUT
+-A OUTPUT -j %(bn)s-OUTPUT
+-A FORWARD -j %(bn)s-FORWARD
+-A %(bn)s-sg-fallback -j DROP
+""" % IPTABLES_ARG
+
+IPTABLES_ARG['chains'] = CHAINS_1
+IPTABLES_FILTER_V6_1 = """:%(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]
+:quantum-filter-top - [0:0]
+-A FORWARD -j quantum-filter-top
+-A OUTPUT -j quantum-filter-top
+-A quantum-filter-top -j %(bn)s-local
+-A INPUT -j %(bn)s-INPUT
+-A OUTPUT -j %(bn)s-OUTPUT
+-A FORWARD -j %(bn)s-FORWARD
+-A %(bn)s-sg-fallback -j DROP
+-A %(bn)s-FORWARD %(physdev)s --physdev-out tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-out tap_port1 -j %(bn)s-i_port1
+-A %(bn)s-i_port1 -m state --state INVALID -j DROP
+-A %(bn)s-i_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-i_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-in tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-INPUT %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-o_port1 -m mac ! --mac-source 12:34:56:78:9a:bc -j DROP
+-A %(bn)s-o_port1 -p icmpv6 -j RETURN
+-A %(bn)s-o_port1 -m state --state INVALID -j DROP
+-A %(bn)s-o_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-o_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-sg-chain -j ACCEPT
+""" % IPTABLES_ARG
+
+IPTABLES_ARG['chains'] = CHAINS_2
+IPTABLES_FILTER_V6_2 = """:%(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]
+:quantum-filter-top - [0:0]
+-A FORWARD -j quantum-filter-top
+-A OUTPUT -j quantum-filter-top
+-A quantum-filter-top -j %(bn)s-local
+-A INPUT -j %(bn)s-INPUT
+-A OUTPUT -j %(bn)s-OUTPUT
+-A FORWARD -j %(bn)s-FORWARD
+-A %(bn)s-sg-fallback -j DROP
+-A %(bn)s-FORWARD %(physdev)s --physdev-out tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-out tap_port1 -j %(bn)s-i_port1
+-A %(bn)s-i_port1 -m state --state INVALID -j DROP
+-A %(bn)s-i_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-i_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-in tap_port1 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-INPUT %(physdev)s --physdev-in tap_port1 -j %(bn)s-o_port1
+-A %(bn)s-o_port1 -m mac ! --mac-source 12:34:56:78:9a:bc -j DROP
+-A %(bn)s-o_port1 -p icmpv6 -j RETURN
+-A %(bn)s-o_port1 -m state --state INVALID -j DROP
+-A %(bn)s-o_port1 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-o_port1 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-out tap_port2 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-out tap_port2 -j %(bn)s-i_port2
+-A %(bn)s-i_port2 -m state --state INVALID -j DROP
+-A %(bn)s-i_port2 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-i_port2 -j %(bn)s-sg-fallback
+-A %(bn)s-FORWARD %(physdev)s --physdev-in tap_port2 -j %(bn)s-sg-chain
+-A %(bn)s-sg-chain %(physdev)s --physdev-in tap_port2 -j %(bn)s-o_port2
+-A %(bn)s-INPUT %(physdev)s --physdev-in tap_port2 -j %(bn)s-o_port2
+-A %(bn)s-o_port2 -m mac ! --mac-source 12:34:56:78:9a:bd -j DROP
+-A %(bn)s-o_port2 -p icmpv6 -j RETURN
+-A %(bn)s-o_port2 -m state --state INVALID -j DROP
+-A %(bn)s-o_port2 -m state --state ESTABLISHED,RELATED -j RETURN
+-A %(bn)s-o_port2 -j %(bn)s-sg-fallback
+-A %(bn)s-sg-chain -j ACCEPT
+""" % IPTABLES_ARG
+
+IPTABLES_ARG['chains'] = CHAINS_EMPTY
+IPTABLES_FILTER_V6_EMPTY = """:%(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]
+:quantum-filter-top - [0:0]
+-A FORWARD -j quantum-filter-top
+-A OUTPUT -j quantum-filter-top
+-A quantum-filter-top -j %(bn)s-local
+-A INPUT -j %(bn)s-INPUT
+-A OUTPUT -j %(bn)s-OUTPUT
+-A FORWARD -j %(bn)s-FORWARD
+-A %(bn)s-sg-fallback -j DROP
+""" % IPTABLES_ARG
+
+
+class TestSecurityGroupAgentWithIptables(unittest.TestCase):
+ def setUp(self):
+ self.mox = mox.Mox()
+
+ self.addCleanup(mock.patch.stopall)
+ self.addCleanup(self.mox.UnsetStubs)
+
+ self.agent = sg_rpc.SecurityGroupAgentRpcMixin()
+ self.agent.context = None
+
+ self.root_helper = 'sudo'
+ self.agent.root_helper = 'sudo'
+ self.agent.init_firewall()
+
+ self.iptables = self.agent.firewall.iptables
+ self.mox.StubOutWithMock(self.iptables, "execute")
+
+ self.rpc = mock.Mock()
+ self.agent.plugin_rpc = self.rpc
+ rule1 = [{'direction': 'ingress',
+ 'protocol': 'udp',
+ 'ethertype': 'IPv4',
+ 'source_ip_prefix': '10.0.0.2',
+ 'source_port_range_min': 67,
+ 'source_port_range_max': 67,
+ 'port_range_min': 68,
+ 'port_range_max': 68},
+ {'direction': 'ingress',
+ 'protocol': 'tcp',
+ 'ethertype': 'IPv4',
+ 'port_range_min': 22,
+ 'port_range_max': 22},
+ {'direction': 'egress',
+ 'ethertype': 'IPv4'}]
+ rule2 = rule1[:]
+ rule2 += [{'direction': 'ingress',
+ 'source_ip_prefix': '10.0.0.4',
+ 'ethertype': 'IPv4'}]
+ rule3 = rule2[:]
+ rule3 += [{'direction': 'ingress',
+ 'protocol': 'icmp',
+ 'ethertype': 'IPv4'}]
+ rule4 = rule1[:]
+ rule4 += [{'direction': 'ingress',
+ 'source_ip_prefix': '10.0.0.3',
+ 'ethertype': 'IPv4'}]
+ rule5 = rule4[:]
+ rule5 += [{'direction': 'ingress',
+ 'protocol': 'icmp',
+ 'ethertype': 'IPv4'}]
+ self.devices1 = {'tap_port1': self._device('tap_port1',
+ '10.0.0.3',
+ '12:34:56:78:9a:bc',
+ rule1)}
+ self.devices2 = {'tap_port1': self._device('tap_port1',
+ '10.0.0.3',
+ '12:34:56:78:9a:bc',
+ rule2),
+ 'tap_port2': self._device('tap_port2',
+ '10.0.0.4',
+ '12:34:56:78:9a:bd',
+ rule4)}
+ self.devices3 = {'tap_port1': self._device('tap_port1',
+ '10.0.0.3',
+ '12:34:56:78:9a:bc',
+ rule3),
+ 'tap_port2': self._device('tap_port2',
+ '10.0.0.4',
+ '12:34:56:78:9a:bd',
+ rule5)}
+
+ def _device(self, device, ip, mac_address, rule):
+ return {'device': device,
+ 'fixed_ips': [ip],
+ 'mac_address': mac_address,
+ 'security_groups': ['security_group1'],
+ 'security_group_rules': rule,
+ 'security_group_source_groups': [
+ 'security_group1']}
+
+ def _regex(self, value):
+ value = value.replace('\n', '\\n')
+ value = value.replace('[', '\[')
+ value = value.replace(']', '\]')
+ return mox.Regex(value)
+
+ def _replay_iptables(self, v4_filter, v6_filter):
+ self.iptables.execute(
+ ['iptables-save', '-t', 'filter'],
+ root_helper=self.root_helper).AndReturn('')
+
+ self.iptables.execute(
+ ['iptables-restore'],
+ process_input=self._regex(v4_filter),
+ root_helper=self.root_helper).AndReturn('')
+
+ self.iptables.execute(
+ ['iptables-save', '-t', 'nat'],
+ root_helper=self.root_helper).AndReturn('')
+
+ self.iptables.execute(
+ ['iptables-restore'],
+ process_input=self._regex(IPTABLES_NAT),
+ root_helper=self.root_helper).AndReturn('')
+
+ self.iptables.execute(
+ ['ip6tables-save', '-t', 'filter'],
+ root_helper=self.root_helper).AndReturn('')
+
+ self.iptables.execute(
+ ['ip6tables-restore'],
+ process_input=self._regex(v6_filter),
+ root_helper=self.root_helper).AndReturn('')
+
+ def test_prepare_remove_port(self):
+ self.rpc.security_group_rules_for_devices.return_value = self.devices1
+ self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1)
+ self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY)
+ self.mox.ReplayAll()
+
+ self.agent.prepare_devices_filter(['tap_port1'])
+ self.agent.remove_devices_filter(['tap_port1'])
+ self.mox.VerifyAll()
+
+ 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)
+ self._replay_iptables(IPTABLES_FILTER_1_2, IPTABLES_FILTER_V6_1)
+ self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2)
+ self._replay_iptables(IPTABLES_FILTER_2_2, IPTABLES_FILTER_V6_2)
+ self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1)
+ self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY)
+ self.mox.ReplayAll()
+
+ self.agent.prepare_devices_filter(['tap_port1'])
+ self.rpc.security_group_rules_for_devices.return_value = self.devices2
+ self.agent.security_groups_member_updated(['security_group1'])
+ self.agent.prepare_devices_filter(['tap_port2'])
+ self.rpc.security_group_rules_for_devices.return_value = self.devices1
+ self.agent.security_groups_member_updated(['security_group1'])
+ self.agent.remove_devices_filter(['tap_port2'])
+ self.agent.remove_devices_filter(['tap_port1'])
+
+ self.mox.VerifyAll()
+
+ def test_security_group_rule_udpated(self):
+ self.rpc.security_group_rules_for_devices.return_value = self.devices2
+ self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2)
+ self._replay_iptables(IPTABLES_FILTER_2_3, IPTABLES_FILTER_V6_2)
+ self.mox.ReplayAll()
+
+ self.agent.prepare_devices_filter(['tap_port1', 'tap_port3'])
+ self.rpc.security_group_rules_for_devices.return_value = self.devices3
+ self.agent.security_groups_rule_updated(['security_group1'])
+
+ self.mox.VerifyAll()