Add popular IP protocols for security group

Add these additional protocols listed below to
security groups brings convenience to operators
on configuring these protocols. In addition, make
the security group rules more readable.

The added protocols are: ah, dccp, egp, esp, gre,
ipv6-encap, ipv6-frag, ipv6-nonxt, ipv6-opts,
ipv6-route, ospf, pgm, rsvp, sctp, udplite, vrrp.

A related patch is submitted to neutron-lib project:
https://review.openstack.org/259037

DocImpact: You can specify protocol names rather than
protocol number in API and CLI commands. I'll update
the documentation when it is merged.

APIImpact

Change-Id: Iaef9b650449b4d9d362a59305c45e0aa3831507c
Closes-Bug: #1475717
This commit is contained in:
Li Ma 2015-12-02 10:30:22 +08:00 committed by Sreekumar S
parent 47499d291c
commit 592b548bb6
11 changed files with 117 additions and 33 deletions

View File

@ -138,7 +138,7 @@ def icmpv6_header_match_supported():
table=ovs_const.ARP_SPOOF_TABLE,
priority=1,
dl_type=n_consts.ETHERTYPE_IPV6,
nw_proto=n_consts.PROTO_NUM_ICMP_V6,
nw_proto=n_consts.PROTO_NUM_IPV6_ICMP,
icmp_type=n_consts.ICMPV6_TYPE_NA,
nd_target='fdf8:f53b:82e4::10',
actions="NORMAL")

View File

@ -125,14 +125,71 @@ SUBNET_ALLOCATION_EXT_ALIAS = 'subnet_allocation'
ETHERTYPE_IPV6 = 0x86DD
# Protocol names and numbers for Security Groups/Firewalls
PROTO_NAME_TCP = 'tcp'
PROTO_NAME_AH = 'ah'
PROTO_NAME_DCCP = 'dccp'
PROTO_NAME_EGP = 'egp'
PROTO_NAME_ESP = 'esp'
PROTO_NAME_GRE = 'gre'
PROTO_NAME_ICMP = 'icmp'
PROTO_NAME_ICMP_V6 = 'icmpv6'
PROTO_NAME_IGMP = 'igmp'
PROTO_NAME_IPV6_ENCAP = 'ipv6-encap'
PROTO_NAME_IPV6_FRAG = 'ipv6-frag'
PROTO_NAME_IPV6_ICMP = 'ipv6-icmp'
PROTO_NAME_IPV6_NONXT = 'ipv6-nonxt'
PROTO_NAME_IPV6_OPTS = 'ipv6-opts'
PROTO_NAME_IPV6_ROUTE = 'ipv6-route'
PROTO_NAME_OSPF = 'ospf'
PROTO_NAME_PGM = 'pgm'
PROTO_NAME_RSVP = 'rsvp'
PROTO_NAME_SCTP = 'sctp'
PROTO_NAME_TCP = 'tcp'
PROTO_NAME_UDP = 'udp'
PROTO_NUM_TCP = 6
PROTO_NAME_UDPLITE = 'udplite'
PROTO_NAME_VRRP = 'vrrp'
PROTO_NUM_AH = 51
PROTO_NUM_DCCP = 33
PROTO_NUM_EGP = 8
PROTO_NUM_ESP = 50
PROTO_NUM_GRE = 47
PROTO_NUM_ICMP = 1
PROTO_NUM_ICMP_V6 = 58
PROTO_NUM_IGMP = 2
PROTO_NUM_IPV6_ENCAP = 41
PROTO_NUM_IPV6_FRAG = 44
PROTO_NUM_IPV6_ICMP = 58
PROTO_NUM_IPV6_NONXT = 59
PROTO_NUM_IPV6_OPTS = 60
PROTO_NUM_IPV6_ROUTE = 43
PROTO_NUM_OSPF = 89
PROTO_NUM_PGM = 113
PROTO_NUM_RSVP = 46
PROTO_NUM_SCTP = 132
PROTO_NUM_TCP = 6
PROTO_NUM_UDP = 17
PROTO_NUM_UDPLITE = 136
PROTO_NUM_VRRP = 112
IP_PROTOCOL_MAP = {PROTO_NAME_AH: PROTO_NUM_AH,
PROTO_NAME_DCCP: PROTO_NUM_DCCP,
PROTO_NAME_EGP: PROTO_NUM_EGP,
PROTO_NAME_ESP: PROTO_NUM_ESP,
PROTO_NAME_GRE: PROTO_NUM_GRE,
PROTO_NAME_ICMP: PROTO_NUM_ICMP,
PROTO_NAME_IGMP: PROTO_NUM_IGMP,
PROTO_NAME_IPV6_ENCAP: PROTO_NUM_IPV6_ENCAP,
PROTO_NAME_IPV6_FRAG: PROTO_NUM_IPV6_FRAG,
PROTO_NAME_IPV6_ICMP: PROTO_NUM_IPV6_ICMP,
PROTO_NAME_IPV6_NONXT: PROTO_NUM_IPV6_NONXT,
PROTO_NAME_IPV6_OPTS: PROTO_NUM_IPV6_OPTS,
PROTO_NAME_IPV6_ROUTE: PROTO_NUM_IPV6_ROUTE,
PROTO_NAME_OSPF: PROTO_NUM_OSPF,
PROTO_NAME_PGM: PROTO_NUM_PGM,
PROTO_NAME_RSVP: PROTO_NUM_RSVP,
PROTO_NAME_SCTP: PROTO_NUM_SCTP,
PROTO_NAME_TCP: PROTO_NUM_TCP,
PROTO_NAME_UDP: PROTO_NUM_UDP,
PROTO_NAME_UDPLITE: PROTO_NUM_UDPLITE,
PROTO_NAME_VRRP: PROTO_NUM_VRRP}
# List of ICMPv6 types that should be allowed by default:
# Multicast Listener Query (130),

View File

@ -38,11 +38,6 @@ from neutron.extensions import securitygroup as ext_sg
LOG = logging.getLogger(__name__)
IP_PROTOCOL_MAP = {constants.PROTO_NAME_TCP: constants.PROTO_NUM_TCP,
constants.PROTO_NAME_UDP: constants.PROTO_NUM_UDP,
constants.PROTO_NAME_ICMP: constants.PROTO_NUM_ICMP,
constants.PROTO_NAME_ICMP_V6: constants.PROTO_NUM_ICMP_V6}
class SecurityGroup(model_base.HasStandardAttributes, model_base.BASEV2,
model_base.HasId, model_base.HasTenant):
@ -420,7 +415,7 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
# problems with comparing int and string in PostgreSQL. Here this
# string is converted to int to give an opportunity to use it as
# before.
return int(IP_PROTOCOL_MAP.get(protocol, protocol))
return int(constants.IP_PROTOCOL_MAP.get(protocol, protocol))
def _validate_port_range(self, rule):
"""Check that port_range is valid."""
@ -452,7 +447,12 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase):
def _validate_ethertype_and_protocol(self, rule):
"""Check if given ethertype and protocol are valid or not"""
if rule['protocol'] == constants.PROTO_NAME_ICMP_V6:
if rule['protocol'] in [constants.PROTO_NAME_IPV6_ENCAP,
constants.PROTO_NAME_IPV6_FRAG,
constants.PROTO_NAME_IPV6_ICMP,
constants.PROTO_NAME_IPV6_NONXT,
constants.PROTO_NAME_IPV6_OPTS,
constants.PROTO_NAME_IPV6_ROUTE]:
if rule['ethertype'] == constants.IPv4:
raise ext_sg.SecurityGroupEthertypeConflictWithProtocol(
ethertype=rule['ethertype'], protocol=rule['protocol'])

View File

@ -432,7 +432,7 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
for ra_ip in ra_ips:
ra_rule = {'direction': 'ingress',
'ethertype': n_const.IPv6,
'protocol': n_const.PROTO_NAME_ICMP_V6,
'protocol': n_const.PROTO_NAME_IPV6_ICMP,
'source_ip_prefix': ra_ip,
'source_port_range_min': n_const.ICMPV6_TYPE_RA}
port['security_group_rules'].append(ra_rule)

View File

@ -211,8 +211,7 @@ def _validate_name_not_default(data, valid_values=None):
attr.validators['type:name_not_default'] = _validate_name_not_default
sg_supported_protocols = [None, const.PROTO_NAME_TCP, const.PROTO_NAME_UDP,
const.PROTO_NAME_ICMP, const.PROTO_NAME_ICMP_V6]
sg_supported_protocols = [None] + list(const.IP_PROTOCOL_MAP.keys())
sg_supported_ethertypes = ['IPv4', 'IPv6']
# Attribute Map

View File

@ -116,14 +116,15 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
for ip in ip_addresses:
self.install_normal(
table_id=constants.ARP_SPOOF_TABLE, priority=2,
dl_type=const.ETHERTYPE_IPV6, nw_proto=const.PROTO_NUM_ICMP_V6,
dl_type=const.ETHERTYPE_IPV6,
nw_proto=const.PROTO_NUM_IPV6_ICMP,
icmp_type=const.ICMPV6_TYPE_NA, nd_target=ip, in_port=port)
# Now that the rules are ready, direct icmpv6 neighbor advertisement
# traffic from the port into the anti-spoof table.
self.add_flow(table=constants.LOCAL_SWITCHING,
priority=10, dl_type=const.ETHERTYPE_IPV6,
nw_proto=const.PROTO_NUM_ICMP_V6,
nw_proto=const.PROTO_NUM_IPV6_ICMP,
icmp_type=const.ICMPV6_TYPE_NA, in_port=port,
actions=("resubmit(,%s)" % constants.ARP_SPOOF_TABLE))
@ -147,7 +148,7 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
self.delete_flows(table_id=constants.LOCAL_SWITCHING,
in_port=port, proto='arp')
self.delete_flows(table_id=constants.LOCAL_SWITCHING,
in_port=port, nw_proto=const.PROTO_NUM_ICMP_V6,
in_port=port, nw_proto=const.PROTO_NUM_IPV6_ICMP,
icmp_type=const.ICMPV6_TYPE_NA)
self.delete_arp_spoofing_allow_rules(port)

View File

@ -612,7 +612,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
'port_range_min': 23,
'source_ip_prefix': fake_prefix},
{'direction': 'ingress',
'protocol': const.PROTO_NAME_ICMP_V6,
'protocol': const.PROTO_NAME_IPV6_ICMP,
'ethertype': const.IPv6,
'source_ip_prefix': fake_gateway,
'source_port_range_min': const.ICMPV6_TYPE_RA},
@ -727,7 +727,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
'security_group_id': sg1_id,
'port_range_min': 22},
{'direction': 'ingress',
'protocol': const.PROTO_NAME_ICMP_V6,
'protocol': const.PROTO_NAME_IPV6_ICMP,
'ethertype': const.IPv6,
'source_ip_prefix': gateway_lla_ip,
'source_port_range_min': const.ICMPV6_TYPE_RA},
@ -801,7 +801,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
'security_group_id': sg1_id,
'port_range_min': 22},
{'direction': 'ingress',
'protocol': const.PROTO_NAME_ICMP_V6,
'protocol': const.PROTO_NAME_IPV6_ICMP,
'ethertype': const.IPv6,
'source_ip_prefix': gateway_lla_ip,
'source_port_range_min': const.ICMPV6_TYPE_RA},
@ -870,7 +870,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
'security_group_id': sg1_id,
'port_range_min': 22},
{'direction': 'ingress',
'protocol': const.PROTO_NAME_ICMP_V6,
'protocol': const.PROTO_NAME_IPV6_ICMP,
'ethertype': const.IPv6,
'source_ip_prefix': gateway_lla_ip,
'source_port_range_min': const.ICMPV6_TYPE_RA},
@ -926,7 +926,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
'security_group_id': sg1_id,
'port_range_min': 22},
{'direction': 'ingress',
'protocol': const.PROTO_NAME_ICMP_V6,
'protocol': const.PROTO_NAME_IPV6_ICMP,
'ethertype': const.IPv6,
'source_ip_prefix': fake_gateway,
'source_port_range_min': const.ICMPV6_TYPE_RA},
@ -1032,7 +1032,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
'port_range_min': 23,
'dest_ip_prefix': fake_prefix},
{'direction': 'ingress',
'protocol': const.PROTO_NAME_ICMP_V6,
'protocol': const.PROTO_NAME_IPV6_ICMP,
'ethertype': const.IPv6,
'source_ip_prefix': fake_gateway,
'source_port_range_min': const.ICMPV6_TYPE_RA},
@ -1097,7 +1097,7 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
'remote_group_id': sg2_id,
'security_group_id': sg1_id},
{'direction': 'ingress',
'protocol': const.PROTO_NAME_ICMP_V6,
'protocol': const.PROTO_NAME_IPV6_ICMP,
'ethertype': const.IPv6,
'source_ip_prefix': fake_gateway,
'source_port_range_min': const.ICMPV6_TYPE_RA},

View File

@ -16,6 +16,7 @@ import testtools
from neutron.callbacks import exceptions
from neutron.callbacks import registry
from neutron.common import constants
from neutron import context
from neutron.db import common_db_mixin
from neutron.db import securitygroups_db
@ -83,3 +84,22 @@ class SecurityGroupDbMixinTestCase(testlib_api.SqlTestCase):
with testtools.ExpectedException(
securitygroup.SecurityGroupRuleNotFound):
self.mixin.delete_security_group_rule(self.ctx, 'foo_rule')
def test_validate_ethertype_and_protocol(self):
fake_ipv4_rules = [{'protocol': constants.PROTO_NAME_IPV6_ICMP,
'ethertype': constants.IPv4},
{'protocol': constants.PROTO_NAME_IPV6_ENCAP,
'ethertype': constants.IPv4},
{'protocol': constants.PROTO_NAME_IPV6_ROUTE,
'ethertype': constants.IPv4},
{'protocol': constants.PROTO_NAME_IPV6_FRAG,
'ethertype': constants.IPv4},
{'protocol': constants.PROTO_NAME_IPV6_NONXT,
'ethertype': constants.IPv4},
{'protocol': constants.PROTO_NAME_IPV6_OPTS,
'ethertype': constants.IPv4}]
# test wrong protocols
for rule in fake_ipv4_rules:
with testtools.ExpectedException(
securitygroup.SecurityGroupEthertypeConflictWithProtocol):
self.mixin._validate_ethertype_and_protocol(rule)

View File

@ -427,7 +427,7 @@ class TestSecurityGroups(SecurityGroupDBTestCase):
with self.security_group(name, description) as sg:
security_group_id = sg['security_group']['id']
rule = self._build_security_group_rule(
security_group_id, 'ingress', const.PROTO_NAME_ICMP_V6)
security_group_id, 'ingress', const.PROTO_NAME_IPV6_ICMP)
res = self._create_security_group_rule(self.fmt, rule)
self.deserialize(self.fmt, res)
self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int)
@ -813,7 +813,7 @@ class TestSecurityGroups(SecurityGroupDBTestCase):
direction = "ingress"
ethertype = const.IPv6
remote_ip_prefix = "2001::f401:56ff:fefe:d3dc/128"
protocol = const.PROTO_NAME_ICMP_V6
protocol = const.PROTO_NAME_IPV6_ICMP
# ICMPV6 type
port_range_min = const.ICMPV6_TYPE_RA
# ICMPV6 code
@ -1348,7 +1348,7 @@ class TestSecurityGroups(SecurityGroupDBTestCase):
with self.security_group() as sg:
rule = {'security_group_id': sg['security_group']['id'],
'direction': 'ingress',
'ethertype': 'IPv4',
'ethertype': const.IPv4,
'tenant_id': 'test-tenant'}
res = self._create_security_group_rule(
@ -1363,11 +1363,11 @@ class TestSecurityGroups(SecurityGroupDBTestCase):
with self.security_group() as sg:
rule_v4 = {'security_group_id': sg['security_group']['id'],
'direction': 'ingress',
'ethertype': 'IPv4',
'ethertype': const.IPv4,
'tenant_id': 'test-tenant'}
rule_v6 = {'security_group_id': sg['security_group']['id'],
'direction': 'ingress',
'ethertype': 'IPv6',
'ethertype': const.IPv6,
'tenant_id': 'test-tenant'}
rules = {'security_group_rules': [rule_v4, rule_v6]}

View File

@ -194,17 +194,17 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase):
expected = [
call.add_flow(dl_type=const.ETHERTYPE_IPV6, actions='normal',
icmp_type=const.ICMPV6_TYPE_NA,
nw_proto=const.PROTO_NUM_ICMP_V6,
nw_proto=const.PROTO_NUM_IPV6_ICMP,
nd_target='2001:db8::1',
priority=2, table=24, in_port=8888),
call.add_flow(dl_type=const.ETHERTYPE_IPV6, actions='normal',
icmp_type=const.ICMPV6_TYPE_NA,
nw_proto=const.PROTO_NUM_ICMP_V6,
nw_proto=const.PROTO_NUM_IPV6_ICMP,
nd_target='fdf8:f53b:82e4::1/128',
priority=2, table=24, in_port=8888),
call.add_flow(dl_type=const.ETHERTYPE_IPV6,
icmp_type=const.ICMPV6_TYPE_NA,
nw_proto=const.PROTO_NUM_ICMP_V6,
nw_proto=const.PROTO_NUM_IPV6_ICMP,
priority=10, table=0, in_port=8888,
actions='resubmit(,24)')
]

View File

@ -0,0 +1,7 @@
---
prelude: >
Add popular IP protocols to the security group code. End-users can
specify protocol names instead of protocol numbers in both RESTful
API and python-neutronclient CLI.
upgrade:
- Add popular IP protocols to security group code.