Stop making IP-specific provider rules in SG code
Setting up rules to allow DHCPv6, DHCP, and RAs from specific IP addresses based on Neutron resources has a few issues: 1. It violates separation of concerns. We are implementing logic to calculate where an IPv6 RA advertisement or DHCP advertisement should be coming from in the security group code. This code should not be trying to guess IPv6 LLAs, know about subnet modes, DHCP server implementations, or the type of L3 plugin being used. Currently all of these assumptions are baked into code that should only be filtering, which makes it very rigid and brittle when it comes to other implementations for DHCP and/or RAs. 2. It has scaling issues on large networks. Every time one of these provider rules is updated, it triggers every L2 agent to refresh all of the security group rules for ports in that network, which puts significant load on the server. 3. It's main purpose: preventing spoofing of RA[1,2] and DHCP packets, has long been superceded by preventing VMs from acting as DHCP/RA servers[3][4]. This patch completely removes all of this logic and just returns static provider rules to the agents that allow all DHCP server and RA traffic ingress to the client. This addresses the issues highlighted above since the code is significantly simplified and the provider rules don't require refreshes on the agents. Now that the provider rules never change, the RPC notification listener on the agent-side for 'notify_provider_updated' is now just a NOOP that doesn't trigger any refreshes. The notification was left in place on the server side for older version agents that have stale IP-specific provider rules. The entire notification can be removed in the future. The one open concern with this approach is that VMs will now be able to receive DHCP offers from other DHCP servers on the same network that aren't being filtered (e.g. a VM with port security disabled or another device on a provider network). In order to address this for DHCP, this patch adds two rules that only allow DHCP offers targeted to either the broadcast or the correct client IP. This prevents incorrect offers from ever reaching the client. For RAs, this patch just allows all RAs so we may pick up advertisements from other v6 routers attached to a network; however, the instance won't actually be allowed to use bad addresses. 1. https://bugs.launchpad.net/neutron/+bug/1262759 2. I1d5c7aaa8e4cf057204eb746c0faab2c70409a94 3. Ice1c9dd349864da28806c5053e38ef86f43b7771 4. https://git.openstack.org/cgit/openstack/neutron/tree/ neutron/agent/linux/iptables_firewall.py ?h=521b1074f17574a5234843bce68f3810995e0e1d#n475 Closes-Bug: #1653830 Closes-Bug: #1663077 Change-Id: Ibfbf011284cbde396f74db9d982993f994082731
This commit is contained in:
parent
ff2c051429
commit
ae9d1160bd
@ -156,6 +156,7 @@ class SecurityGroupAgentRpcApiMixin(object):
|
||||
def security_groups_provider_updated(self, context,
|
||||
devices_to_update=None):
|
||||
"""Notify provider updated security groups."""
|
||||
# TODO(kevinbenton): remove in Queens
|
||||
# NOTE(ihrachys) the version here should really be 1.3, but since we
|
||||
# don't support proper version pinning yet, we leave it intact to allow
|
||||
# to work with older agents. The reason why we should not require the
|
||||
@ -214,9 +215,9 @@ class SecurityGroupAgentRpcCallbackMixin(object):
|
||||
self.sg_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")
|
||||
devices_to_update = kwargs.get('devices_to_update')
|
||||
if not self.sg_agent:
|
||||
return self._security_groups_agent_not_set()
|
||||
self.sg_agent.security_groups_provider_updated(devices_to_update)
|
||||
"""Callback for security group provider update.
|
||||
|
||||
This is now a NOOP since provider rules are static. The server just
|
||||
generates the notification for agents running older versions that have
|
||||
IP-specific rules.
|
||||
"""
|
||||
|
@ -17,10 +17,8 @@ import netaddr
|
||||
from neutron_lib import constants as const
|
||||
from neutron_lib.utils import helpers
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import netutils
|
||||
from sqlalchemy.orm import exc
|
||||
|
||||
from neutron._i18n import _, _LW
|
||||
from neutron._i18n import _
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db.models import allowed_address_pair as aap_models
|
||||
from neutron.db.models import securitygroup as sg_models
|
||||
@ -300,92 +298,6 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
|
||||
remote_group_ids.append(remote_group_id)
|
||||
return remote_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.mac_address,
|
||||
models_v2.Port.network_id,
|
||||
models_v2.IPAllocation.ip_address)
|
||||
query = query.join(models_v2.IPAllocation)
|
||||
query = query.filter(models_v2.Port.network_id.in_(network_ids))
|
||||
owner = const.DEVICE_OWNER_DHCP
|
||||
query = query.filter(models_v2.Port.device_owner == owner)
|
||||
ips = {}
|
||||
|
||||
for network_id in network_ids:
|
||||
ips[network_id] = []
|
||||
|
||||
for mac_address, network_id, ip in query:
|
||||
if (netaddr.IPAddress(ip).version == 6
|
||||
and not netaddr.IPAddress(ip).is_link_local()):
|
||||
ip = str(netutils.get_ipv6_addr_by_EUI64(const.IPv6_LLA_PREFIX,
|
||||
mac_address))
|
||||
if ip not in ips[network_id]:
|
||||
ips[network_id].append(ip)
|
||||
|
||||
return ips
|
||||
|
||||
def _select_ra_ips_for_network_ids(self, context, network_ids):
|
||||
"""Select IP addresses to allow sending router advertisement from.
|
||||
|
||||
If the OpenStack managed radvd process sends an RA, get link local
|
||||
address of gateway and allow RA from this Link Local address.
|
||||
The gateway port link local address will only be obtained
|
||||
when router is created before VM instance is booted and
|
||||
subnet is attached to router.
|
||||
|
||||
If OpenStack doesn't send RA, allow RA from gateway IP.
|
||||
Currently, the gateway IP needs to be link local to be able
|
||||
to send RA to VM.
|
||||
"""
|
||||
if not network_ids:
|
||||
return {}
|
||||
ips = {}
|
||||
for network_id in network_ids:
|
||||
ips[network_id] = set([])
|
||||
query = context.session.query(models_v2.Subnet)
|
||||
subnets = query.filter(models_v2.Subnet.network_id.in_(network_ids))
|
||||
for subnet in subnets:
|
||||
gateway_ip = subnet['gateway_ip']
|
||||
if subnet['ip_version'] != 6 or not gateway_ip:
|
||||
continue
|
||||
if not netaddr.IPAddress(gateway_ip).is_link_local():
|
||||
if subnet['ipv6_ra_mode']:
|
||||
gateway_ip = self._get_lla_gateway_ip_for_subnet(context,
|
||||
subnet)
|
||||
else:
|
||||
# TODO(xuhanp):Figure out how to allow gateway IP from
|
||||
# existing device to be global address and figure out the
|
||||
# link local address by other method.
|
||||
continue
|
||||
if gateway_ip:
|
||||
ips[subnet['network_id']].add(gateway_ip)
|
||||
|
||||
return ips
|
||||
|
||||
def _get_lla_gateway_ip_for_subnet(self, context, subnet):
|
||||
query = context.session.query(models_v2.Port.mac_address)
|
||||
query = query.join(models_v2.IPAllocation)
|
||||
query = query.filter(
|
||||
models_v2.IPAllocation.subnet_id == subnet['id'])
|
||||
query = query.filter(
|
||||
models_v2.IPAllocation.ip_address == subnet['gateway_ip'])
|
||||
query = query.filter(
|
||||
models_v2.Port.device_owner.in_(const.ROUTER_INTERFACE_OWNERS))
|
||||
try:
|
||||
mac_address = query.one()[0]
|
||||
except (exc.NoResultFound, exc.MultipleResultsFound):
|
||||
LOG.warning(_LW('No valid gateway port on subnet %s is '
|
||||
'found for IPv6 RA'), subnet['id'])
|
||||
return
|
||||
lla_ip = str(netutils.get_ipv6_addr_by_EUI64(
|
||||
const.IPv6_LLA_PREFIX,
|
||||
mac_address))
|
||||
return lla_ip
|
||||
|
||||
def _convert_remote_group_id_to_ip_prefix(self, context, ports):
|
||||
remote_group_ids = self._select_remote_group_ids(ports)
|
||||
ips = self._select_ips_for_remote_group(context, remote_group_ids)
|
||||
@ -415,11 +327,18 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
|
||||
port['security_group_rules'] = updated_rule
|
||||
return ports
|
||||
|
||||
def _add_ingress_dhcp_rule(self, port, ips):
|
||||
dhcp_ips = ips.get(port['network_id'])
|
||||
for dhcp_ip in dhcp_ips:
|
||||
source_port, dest_port, ethertype = DHCP_RULE_PORT[
|
||||
netaddr.IPAddress(dhcp_ip).version]
|
||||
def _add_ingress_dhcp_rule(self, port):
|
||||
for ip_version in (4, 6):
|
||||
# only allow DHCP servers to talk to the appropriate IP address
|
||||
# to avoid getting leases that don't match the Neutron IPs
|
||||
prefix = '32' if ip_version == 4 else '128'
|
||||
dests = ['%s/%s' % (ip, prefix) for ip in port['fixed_ips']
|
||||
if netaddr.IPNetwork(ip).version == ip_version]
|
||||
if ip_version == 4:
|
||||
# v4 dhcp servers can also talk to broadcast
|
||||
dests.append('255.255.255.255/32')
|
||||
source_port, dest_port, ethertype = DHCP_RULE_PORT[ip_version]
|
||||
for dest in dests:
|
||||
dhcp_rule = {'direction': 'ingress',
|
||||
'ethertype': ethertype,
|
||||
'protocol': 'udp',
|
||||
@ -427,26 +346,24 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
|
||||
'port_range_max': dest_port,
|
||||
'source_port_range_min': source_port,
|
||||
'source_port_range_max': source_port,
|
||||
'source_ip_prefix': dhcp_ip}
|
||||
'dest_ip_prefix': dest}
|
||||
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:
|
||||
def _add_ingress_ra_rule(self, port):
|
||||
has_v6 = [ip for ip in port['fixed_ips']
|
||||
if netaddr.IPNetwork(ip).version == 6]
|
||||
if not has_v6:
|
||||
return
|
||||
ra_rule = {'direction': 'ingress',
|
||||
'ethertype': const.IPv6,
|
||||
'protocol': const.PROTO_NAME_IPV6_ICMP,
|
||||
'source_ip_prefix': ra_ip,
|
||||
'source_port_range_min': const.ICMPV6_TYPE_RA}
|
||||
port['security_group_rules'].append(ra_rule)
|
||||
|
||||
def _apply_provider_rule(self, context, ports):
|
||||
network_ids = self._select_network_ids(ports)
|
||||
ips_dhcp = self._select_dhcp_ips_for_network_ids(context, network_ids)
|
||||
ips_ra = self._select_ra_ips_for_network_ids(context, network_ids)
|
||||
for port in ports.values():
|
||||
self._add_ingress_ra_rule(port, ips_ra)
|
||||
self._add_ingress_dhcp_rule(port, ips_dhcp)
|
||||
self._add_ingress_ra_rule(port)
|
||||
self._add_ingress_dhcp_rule(port)
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def security_group_rules_for_ports(self, context, ports):
|
||||
|
@ -17,11 +17,11 @@ import collections
|
||||
import contextlib
|
||||
|
||||
import mock
|
||||
import netaddr
|
||||
from neutron_lib import constants as const
|
||||
from neutron_lib.plugins import directory
|
||||
from oslo_config import cfg
|
||||
import oslo_messaging
|
||||
from oslo_utils import netutils
|
||||
from testtools import matchers
|
||||
import webob.exc
|
||||
|
||||
@ -57,6 +57,38 @@ FIREWALL_HYBRID_DRIVER = (FIREWALL_BASE_PACKAGE +
|
||||
FIREWALL_NOOP_DRIVER = 'neutron.agent.firewall.NoopFirewallDriver'
|
||||
|
||||
|
||||
def ingress_address_assignment_rules(port):
|
||||
rules = []
|
||||
v4_addrs = [ip['ip_address'] for ip in port['port']['fixed_ips']
|
||||
if netaddr.IPNetwork(ip['ip_address']).version == 4]
|
||||
v6_addrs = [ip['ip_address'] for ip in port['port']['fixed_ips']
|
||||
if netaddr.IPNetwork(ip['ip_address']).version == 6]
|
||||
if v6_addrs:
|
||||
rules.append({'direction': 'ingress',
|
||||
'ethertype': 'IPv6',
|
||||
'protocol': 'ipv6-icmp',
|
||||
'source_port_range_min': 134})
|
||||
for dest in v4_addrs + ['255.255.255.255']:
|
||||
rules.append({'direction': 'ingress',
|
||||
'ethertype': 'IPv4',
|
||||
'port_range_max': 68,
|
||||
'port_range_min': 68,
|
||||
'protocol': 'udp',
|
||||
'source_port_range_max': 67,
|
||||
'source_port_range_min': 67,
|
||||
'dest_ip_prefix': '%s/32' % dest})
|
||||
for dest in v6_addrs:
|
||||
rules.append({'direction': 'ingress',
|
||||
'ethertype': 'IPv6',
|
||||
'port_range_max': 546,
|
||||
'port_range_min': 546,
|
||||
'protocol': 'udp',
|
||||
'source_port_range_max': 547,
|
||||
'source_port_range_min': 547,
|
||||
'dest_ip_prefix': '%s/128' % dest})
|
||||
return rules
|
||||
|
||||
|
||||
def set_enable_security_groups(enabled):
|
||||
cfg.CONF.set_override('enable_security_group', enabled,
|
||||
group='SECURITYGROUP')
|
||||
@ -252,7 +284,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
|
||||
'port_range_max': 23, 'security_group_id': sg1_id,
|
||||
'port_range_min': 23,
|
||||
'source_ip_prefix': fake_prefix},
|
||||
]
|
||||
] + ingress_address_assignment_rules(ports_rest1)
|
||||
self.assertEqual(port_rpc['security_group_rules'],
|
||||
expected)
|
||||
self._delete('ports', port_id1)
|
||||
@ -368,7 +400,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
|
||||
'port_range_max': 23, 'security_group_id': sg_id,
|
||||
'port_range_min': 23,
|
||||
'source_ip_prefix': fake_prefix},
|
||||
]
|
||||
] + ingress_address_assignment_rules(port)
|
||||
expected = tools.UnorderedList(expected)
|
||||
self.assertEqual(expected,
|
||||
port_rpc['security_group_rules'])
|
||||
@ -424,7 +456,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
|
||||
'port_range_max': 23, 'security_group_id': sg1_id,
|
||||
'port_range_min': 23,
|
||||
'dest_ip_prefix': fake_prefix},
|
||||
]
|
||||
] + ingress_address_assignment_rules(ports_rest1)
|
||||
self.assertEqual(port_rpc['security_group_rules'],
|
||||
expected)
|
||||
self._delete('ports', port_id1)
|
||||
@ -481,7 +513,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
|
||||
'port_range_max': 25, 'port_range_min': 24,
|
||||
'remote_group_id': sg2_id,
|
||||
'security_group_id': sg1_id},
|
||||
]
|
||||
] + ingress_address_assignment_rules(ports_rest1)
|
||||
self.assertEqual(port_rpc['security_group_rules'],
|
||||
expected)
|
||||
self._delete('ports', port_id1)
|
||||
@ -570,17 +602,12 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
|
||||
self.deserialize(self.fmt, res)
|
||||
self.assertEqual(webob.exc.HTTPCreated.code, res.status_int)
|
||||
|
||||
dhcp_port = self._create_port(
|
||||
self._create_port(
|
||||
self.fmt, n['network']['id'],
|
||||
fixed_ips=[{'subnet_id': subnet_v6['subnet']['id'],
|
||||
'ip_address': FAKE_IP['IPv6_DHCP']}],
|
||||
device_owner=const.DEVICE_OWNER_DHCP,
|
||||
security_groups=[sg1_id])
|
||||
dhcp_rest = self.deserialize(self.fmt, dhcp_port)
|
||||
dhcp_mac = dhcp_rest['port']['mac_address']
|
||||
dhcp_lla_ip = str(netutils.get_ipv6_addr_by_EUI64(
|
||||
const.IPv6_LLA_PREFIX,
|
||||
dhcp_mac))
|
||||
|
||||
res1 = self._create_port(
|
||||
self.fmt, n['network']['id'],
|
||||
@ -612,20 +639,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
|
||||
'security_group_id': sg1_id,
|
||||
'port_range_min': 23,
|
||||
'source_ip_prefix': fake_prefix},
|
||||
{'direction': 'ingress',
|
||||
'protocol': const.PROTO_NAME_IPV6_ICMP,
|
||||
'ethertype': const.IPv6,
|
||||
'source_ip_prefix': fake_gateway,
|
||||
'source_port_range_min': const.ICMPV6_TYPE_RA},
|
||||
{'direction': 'ingress',
|
||||
'ethertype': ethertype,
|
||||
'port_range_max': dest_port,
|
||||
'port_range_min': dest_port,
|
||||
'protocol': const.PROTO_NAME_UDP,
|
||||
'source_ip_prefix': dhcp_lla_ip,
|
||||
'source_port_range_max': source_port,
|
||||
'source_port_range_min': source_port}
|
||||
]
|
||||
] + ingress_address_assignment_rules(ports_rest1)
|
||||
self.assertEqual(port_rpc['security_group_rules'],
|
||||
expected)
|
||||
self._delete('ports', port_id1)
|
||||
@ -675,310 +689,6 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
|
||||
ports_rpc['sg_member_ips'][sg1_id]['IPv6'])
|
||||
self._delete('ports', port_id1)
|
||||
|
||||
def test_security_group_ra_rules_for_devices_ipv6_gateway_global(self):
|
||||
fake_prefix = FAKE_PREFIX[const.IPv6]
|
||||
fake_gateway = FAKE_IP['IPv6_GLOBAL']
|
||||
with self.network() as n,\
|
||||
self.subnet(n, gateway_ip=fake_gateway,
|
||||
cidr=fake_prefix, ip_version=6,
|
||||
ipv6_ra_mode=const.IPV6_SLAAC
|
||||
) as subnet_v6,\
|
||||
self.security_group() as sg1:
|
||||
sg1_id = sg1['security_group']['id']
|
||||
rule1 = self._build_security_group_rule(
|
||||
sg1_id,
|
||||
'ingress', const.PROTO_NAME_TCP, '22',
|
||||
'22',
|
||||
ethertype=const.IPv6)
|
||||
rules = {
|
||||
'security_group_rules': [rule1['security_group_rule']]}
|
||||
self._make_security_group_rule(self.fmt, rules)
|
||||
|
||||
# Create gateway port
|
||||
gateway_res = self._make_port(
|
||||
self.fmt, n['network']['id'],
|
||||
fixed_ips=[{'subnet_id': subnet_v6['subnet']['id'],
|
||||
'ip_address': fake_gateway}],
|
||||
device_owner=const.DEVICE_OWNER_ROUTER_INTF)
|
||||
gateway_mac = gateway_res['port']['mac_address']
|
||||
gateway_port_id = gateway_res['port']['id']
|
||||
gateway_lla_ip = str(netutils.get_ipv6_addr_by_EUI64(
|
||||
const.IPv6_LLA_PREFIX,
|
||||
gateway_mac))
|
||||
|
||||
ports_rest1 = self._make_port(
|
||||
self.fmt, n['network']['id'],
|
||||
fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}],
|
||||
security_groups=[sg1_id])
|
||||
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', 'ethertype': const.IPv4,
|
||||
'security_group_id': sg1_id},
|
||||
{'direction': 'egress', 'ethertype': const.IPv6,
|
||||
'security_group_id': sg1_id},
|
||||
{'direction': 'ingress',
|
||||
'protocol': const.PROTO_NAME_TCP,
|
||||
'ethertype': const.IPv6,
|
||||
'port_range_max': 22,
|
||||
'security_group_id': sg1_id,
|
||||
'port_range_min': 22},
|
||||
{'direction': 'ingress',
|
||||
'protocol': const.PROTO_NAME_IPV6_ICMP,
|
||||
'ethertype': const.IPv6,
|
||||
'source_ip_prefix': gateway_lla_ip,
|
||||
'source_port_range_min': const.ICMPV6_TYPE_RA},
|
||||
]
|
||||
self.assertEqual(port_rpc['security_group_rules'],
|
||||
expected)
|
||||
self._delete('ports', port_id1)
|
||||
# Note(xuhanp): remove gateway port's fixed_ips or gateway port
|
||||
# deletion will be prevented.
|
||||
data = {'port': {'fixed_ips': []}}
|
||||
req = self.new_update_request('ports', data, gateway_port_id)
|
||||
self.deserialize(self.fmt, req.get_response(self.api))
|
||||
self._delete('ports', gateway_port_id)
|
||||
|
||||
def test_security_group_rule_for_device_ipv6_multi_router_interfaces(self):
|
||||
fake_prefix = FAKE_PREFIX[const.IPv6]
|
||||
fake_gateway = FAKE_IP['IPv6_GLOBAL']
|
||||
with self.network() as n,\
|
||||
self.subnet(n, gateway_ip=fake_gateway,
|
||||
cidr=fake_prefix, ip_version=6,
|
||||
ipv6_ra_mode=const.IPV6_SLAAC
|
||||
) as subnet_v6,\
|
||||
self.security_group() as sg1:
|
||||
sg1_id = sg1['security_group']['id']
|
||||
rule1 = self._build_security_group_rule(
|
||||
sg1_id,
|
||||
'ingress', const.PROTO_NAME_TCP, '22',
|
||||
'22',
|
||||
ethertype=const.IPv6)
|
||||
rules = {
|
||||
'security_group_rules': [rule1['security_group_rule']]}
|
||||
self._make_security_group_rule(self.fmt, rules)
|
||||
|
||||
# Create gateway port
|
||||
gateway_res = self._make_port(
|
||||
self.fmt, n['network']['id'],
|
||||
fixed_ips=[{'subnet_id': subnet_v6['subnet']['id'],
|
||||
'ip_address': fake_gateway}],
|
||||
device_owner=const.DEVICE_OWNER_ROUTER_INTF)
|
||||
gateway_mac = gateway_res['port']['mac_address']
|
||||
gateway_port_id = gateway_res['port']['id']
|
||||
gateway_lla_ip = str(netutils.get_ipv6_addr_by_EUI64(
|
||||
const.IPv6_LLA_PREFIX,
|
||||
gateway_mac))
|
||||
# Create another router interface port
|
||||
interface_res = self._make_port(
|
||||
self.fmt, n['network']['id'],
|
||||
fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}],
|
||||
device_owner=const.DEVICE_OWNER_ROUTER_INTF)
|
||||
interface_port_id = interface_res['port']['id']
|
||||
|
||||
ports_rest1 = self._make_port(
|
||||
self.fmt, n['network']['id'],
|
||||
fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}],
|
||||
security_groups=[sg1_id])
|
||||
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', 'ethertype': const.IPv4,
|
||||
'security_group_id': sg1_id},
|
||||
{'direction': 'egress', 'ethertype': const.IPv6,
|
||||
'security_group_id': sg1_id},
|
||||
{'direction': 'ingress',
|
||||
'protocol': const.PROTO_NAME_TCP,
|
||||
'ethertype': const.IPv6,
|
||||
'port_range_max': 22,
|
||||
'security_group_id': sg1_id,
|
||||
'port_range_min': 22},
|
||||
{'direction': 'ingress',
|
||||
'protocol': const.PROTO_NAME_IPV6_ICMP,
|
||||
'ethertype': const.IPv6,
|
||||
'source_ip_prefix': gateway_lla_ip,
|
||||
'source_port_range_min': const.ICMPV6_TYPE_RA},
|
||||
]
|
||||
self.assertEqual(port_rpc['security_group_rules'],
|
||||
expected)
|
||||
self._delete('ports', port_id1)
|
||||
data = {'port': {'fixed_ips': []}}
|
||||
req = self.new_update_request('ports', data, gateway_port_id)
|
||||
self.deserialize(self.fmt, req.get_response(self.api))
|
||||
req = self.new_update_request('ports', data, interface_port_id)
|
||||
self.deserialize(self.fmt, req.get_response(self.api))
|
||||
self._delete('ports', gateway_port_id)
|
||||
self._delete('ports', interface_port_id)
|
||||
|
||||
def test_security_group_ra_rules_for_devices_ipv6_dvr(self):
|
||||
fake_prefix = FAKE_PREFIX[const.IPv6]
|
||||
fake_gateway = FAKE_IP['IPv6_GLOBAL']
|
||||
with self.network() as n,\
|
||||
self.subnet(n, gateway_ip=fake_gateway,
|
||||
cidr=fake_prefix, ip_version=6,
|
||||
ipv6_ra_mode=const.IPV6_SLAAC
|
||||
) as subnet_v6,\
|
||||
self.security_group() as sg1:
|
||||
sg1_id = sg1['security_group']['id']
|
||||
rule1 = self._build_security_group_rule(
|
||||
sg1_id,
|
||||
'ingress', const.PROTO_NAME_TCP, '22',
|
||||
'22',
|
||||
ethertype=const.IPv6)
|
||||
rules = {
|
||||
'security_group_rules': [rule1['security_group_rule']]}
|
||||
self._make_security_group_rule(self.fmt, rules)
|
||||
|
||||
# Create DVR router interface port
|
||||
gateway_res = self._make_port(
|
||||
self.fmt, n['network']['id'],
|
||||
fixed_ips=[{'subnet_id': subnet_v6['subnet']['id'],
|
||||
'ip_address': fake_gateway}],
|
||||
device_owner=const.DEVICE_OWNER_DVR_INTERFACE)
|
||||
gateway_mac = gateway_res['port']['mac_address']
|
||||
gateway_port_id = gateway_res['port']['id']
|
||||
gateway_lla_ip = str(netutils.get_ipv6_addr_by_EUI64(
|
||||
const.IPv6_LLA_PREFIX,
|
||||
gateway_mac))
|
||||
|
||||
ports_rest1 = self._make_port(
|
||||
self.fmt, n['network']['id'],
|
||||
fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}],
|
||||
security_groups=[sg1_id])
|
||||
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', 'ethertype': const.IPv4,
|
||||
'security_group_id': sg1_id},
|
||||
{'direction': 'egress', 'ethertype': const.IPv6,
|
||||
'security_group_id': sg1_id},
|
||||
{'direction': 'ingress',
|
||||
'protocol': const.PROTO_NAME_TCP,
|
||||
'ethertype': const.IPv6,
|
||||
'port_range_max': 22,
|
||||
'security_group_id': sg1_id,
|
||||
'port_range_min': 22},
|
||||
{'direction': 'ingress',
|
||||
'protocol': const.PROTO_NAME_IPV6_ICMP,
|
||||
'ethertype': const.IPv6,
|
||||
'source_ip_prefix': gateway_lla_ip,
|
||||
'source_port_range_min': const.ICMPV6_TYPE_RA},
|
||||
]
|
||||
self.assertEqual(port_rpc['security_group_rules'],
|
||||
expected)
|
||||
self._delete('ports', port_id1)
|
||||
# Note(xuhanp): remove gateway port's fixed_ips or gateway port
|
||||
# deletion will be prevented.
|
||||
data = {'port': {'fixed_ips': []}}
|
||||
req = self.new_update_request('ports', data, gateway_port_id)
|
||||
self.deserialize(self.fmt, req.get_response(self.api))
|
||||
self._delete('ports', gateway_port_id)
|
||||
|
||||
def test_security_group_ra_rules_for_devices_ipv6_gateway_lla(self):
|
||||
fake_prefix = FAKE_PREFIX[const.IPv6]
|
||||
fake_gateway = FAKE_IP['IPv6_LLA']
|
||||
with self.network() as n,\
|
||||
self.subnet(n, gateway_ip=fake_gateway,
|
||||
cidr=fake_prefix, ip_version=6,
|
||||
ipv6_ra_mode=const.IPV6_SLAAC
|
||||
) as subnet_v6,\
|
||||
self.security_group() as sg1:
|
||||
sg1_id = sg1['security_group']['id']
|
||||
rule1 = self._build_security_group_rule(
|
||||
sg1_id,
|
||||
'ingress', const.PROTO_NAME_TCP, '22',
|
||||
'22',
|
||||
ethertype=const.IPv6)
|
||||
rules = {
|
||||
'security_group_rules': [rule1['security_group_rule']]}
|
||||
self._make_security_group_rule(self.fmt, rules)
|
||||
|
||||
ports_rest1 = self._make_port(
|
||||
self.fmt, n['network']['id'],
|
||||
fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}],
|
||||
security_groups=[sg1_id])
|
||||
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', 'ethertype': const.IPv4,
|
||||
'security_group_id': sg1_id},
|
||||
{'direction': 'egress', 'ethertype': const.IPv6,
|
||||
'security_group_id': sg1_id},
|
||||
{'direction': 'ingress',
|
||||
'protocol': const.PROTO_NAME_TCP,
|
||||
'ethertype': const.IPv6,
|
||||
'port_range_max': 22,
|
||||
'security_group_id': sg1_id,
|
||||
'port_range_min': 22},
|
||||
{'direction': 'ingress',
|
||||
'protocol': const.PROTO_NAME_IPV6_ICMP,
|
||||
'ethertype': const.IPv6,
|
||||
'source_ip_prefix': fake_gateway,
|
||||
'source_port_range_min': const.ICMPV6_TYPE_RA},
|
||||
]
|
||||
self.assertEqual(port_rpc['security_group_rules'],
|
||||
expected)
|
||||
self._delete('ports', port_id1)
|
||||
|
||||
def test_security_group_ra_rules_for_devices_ipv6_no_gateway_port(self):
|
||||
fake_prefix = FAKE_PREFIX[const.IPv6]
|
||||
with self.network() as n,\
|
||||
self.subnet(n, gateway_ip=None, cidr=fake_prefix,
|
||||
ip_version=6, ipv6_ra_mode=const.IPV6_SLAAC
|
||||
) as subnet_v6,\
|
||||
self.security_group() as sg1:
|
||||
sg1_id = sg1['security_group']['id']
|
||||
rule1 = self._build_security_group_rule(
|
||||
sg1_id,
|
||||
'ingress', const.PROTO_NAME_TCP, '22',
|
||||
'22',
|
||||
ethertype=const.IPv6)
|
||||
rules = {
|
||||
'security_group_rules': [rule1['security_group_rule']]}
|
||||
self._make_security_group_rule(self.fmt, rules)
|
||||
|
||||
ports_rest1 = self._make_port(
|
||||
self.fmt, n['network']['id'],
|
||||
fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}],
|
||||
security_groups=[sg1_id])
|
||||
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', 'ethertype': const.IPv4,
|
||||
'security_group_id': sg1_id},
|
||||
{'direction': 'egress', 'ethertype': const.IPv6,
|
||||
'security_group_id': sg1_id},
|
||||
{'direction': 'ingress',
|
||||
'protocol': const.PROTO_NAME_TCP,
|
||||
'ethertype': const.IPv6,
|
||||
'port_range_max': 22,
|
||||
'security_group_id': sg1_id,
|
||||
'port_range_min': 22},
|
||||
]
|
||||
self.assertEqual(port_rpc['security_group_rules'],
|
||||
expected)
|
||||
self._delete('ports', port_id1)
|
||||
|
||||
def test_security_group_rules_for_devices_ipv6_egress(self):
|
||||
fake_prefix = FAKE_PREFIX[const.IPv6]
|
||||
fake_gateway = FAKE_IP[const.IPv6]
|
||||
@ -1032,12 +742,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
|
||||
'security_group_id': sg1_id,
|
||||
'port_range_min': 23,
|
||||
'dest_ip_prefix': fake_prefix},
|
||||
{'direction': 'ingress',
|
||||
'protocol': const.PROTO_NAME_IPV6_ICMP,
|
||||
'ethertype': const.IPv6,
|
||||
'source_ip_prefix': fake_gateway,
|
||||
'source_port_range_min': const.ICMPV6_TYPE_RA},
|
||||
]
|
||||
] + ingress_address_assignment_rules(ports_rest1)
|
||||
self.assertEqual(port_rpc['security_group_rules'],
|
||||
expected)
|
||||
self._delete('ports', port_id1)
|
||||
@ -1098,12 +803,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
|
||||
'port_range_max': 25, 'port_range_min': 24,
|
||||
'remote_group_id': sg2_id,
|
||||
'security_group_id': sg1_id},
|
||||
{'direction': 'ingress',
|
||||
'protocol': const.PROTO_NAME_IPV6_ICMP,
|
||||
'ethertype': const.IPv6,
|
||||
'source_ip_prefix': fake_gateway,
|
||||
'source_port_range_min': const.ICMPV6_TYPE_RA},
|
||||
]
|
||||
] + ingress_address_assignment_rules(ports_rest1)
|
||||
self.assertEqual(port_rpc['security_group_rules'],
|
||||
expected)
|
||||
self._delete('ports', port_id1)
|
||||
|
@ -55,5 +55,5 @@ class SGAgentRpcCallBackMixinTestCase(base.BaseTestCase):
|
||||
|
||||
def test_security_groups_provider_updated(self):
|
||||
self.rpc.security_groups_provider_updated(None)
|
||||
self.rpc.sg_agent.assert_has_calls(
|
||||
[mock.call.security_groups_provider_updated(None)])
|
||||
# this is now a NOOP on the agent side. provider rules don't change
|
||||
self.assertFalse(self.rpc.sg_agent.called)
|
||||
|
Loading…
Reference in New Issue
Block a user