From 592b548bb6720760efae4b10bec59e78a753f4d7 Mon Sep 17 00:00:00 2001 From: Li Ma Date: Wed, 2 Dec 2015 10:30:22 +0800 Subject: [PATCH] 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 --- neutron/cmd/sanity/checks.py | 2 +- neutron/common/constants.py | 65 +++++++++++++++++-- neutron/db/securitygroups_db.py | 14 ++-- neutron/db/securitygroups_rpc_base.py | 2 +- neutron/extensions/securitygroup.py | 3 +- .../agent/openflow/ovs_ofctl/br_int.py | 7 +- .../unit/agent/test_securitygroups_rpc.py | 14 ++-- .../tests/unit/db/test_securitygroups_db.py | 20 ++++++ .../unit/extensions/test_securitygroup.py | 10 +-- .../agent/openflow/ovs_ofctl/test_br_int.py | 6 +- ...d-ip-protocols-in-sg-60467a073e771aee.yaml | 7 ++ 11 files changed, 117 insertions(+), 33 deletions(-) create mode 100644 releasenotes/notes/add-ip-protocols-in-sg-60467a073e771aee.yaml diff --git a/neutron/cmd/sanity/checks.py b/neutron/cmd/sanity/checks.py index 87e17dace61..3825a680309 100644 --- a/neutron/cmd/sanity/checks.py +++ b/neutron/cmd/sanity/checks.py @@ -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") diff --git a/neutron/common/constants.py b/neutron/common/constants.py index b80d5c00254..1910ee66df5 100644 --- a/neutron/common/constants.py +++ b/neutron/common/constants.py @@ -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), diff --git a/neutron/db/securitygroups_db.py b/neutron/db/securitygroups_db.py index f462f1453c2..42b8040fe47 100644 --- a/neutron/db/securitygroups_db.py +++ b/neutron/db/securitygroups_db.py @@ -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']) diff --git a/neutron/db/securitygroups_rpc_base.py b/neutron/db/securitygroups_rpc_base.py index 7f151fef600..5cd22278c89 100644 --- a/neutron/db/securitygroups_rpc_base.py +++ b/neutron/db/securitygroups_rpc_base.py @@ -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) diff --git a/neutron/extensions/securitygroup.py b/neutron/extensions/securitygroup.py index 9ae3806da24..c9172d62816 100644 --- a/neutron/extensions/securitygroup.py +++ b/neutron/extensions/securitygroup.py @@ -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 diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_int.py b/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_int.py index d0aca03d75d..a7c55fc5e30 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_int.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_int.py @@ -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) diff --git a/neutron/tests/unit/agent/test_securitygroups_rpc.py b/neutron/tests/unit/agent/test_securitygroups_rpc.py index 132b3aaacb0..a46c15dacd9 100644 --- a/neutron/tests/unit/agent/test_securitygroups_rpc.py +++ b/neutron/tests/unit/agent/test_securitygroups_rpc.py @@ -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}, diff --git a/neutron/tests/unit/db/test_securitygroups_db.py b/neutron/tests/unit/db/test_securitygroups_db.py index db98f4622c0..59e40c74d03 100644 --- a/neutron/tests/unit/db/test_securitygroups_db.py +++ b/neutron/tests/unit/db/test_securitygroups_db.py @@ -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) diff --git a/neutron/tests/unit/extensions/test_securitygroup.py b/neutron/tests/unit/extensions/test_securitygroup.py index b94c4fd5833..25d90748491 100644 --- a/neutron/tests/unit/extensions/test_securitygroup.py +++ b/neutron/tests/unit/extensions/test_securitygroup.py @@ -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]} diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_int.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_int.py index 8c77e185ceb..51892fd10c0 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_int.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_int.py @@ -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)') ] diff --git a/releasenotes/notes/add-ip-protocols-in-sg-60467a073e771aee.yaml b/releasenotes/notes/add-ip-protocols-in-sg-60467a073e771aee.yaml new file mode 100644 index 00000000000..3df503fdeaa --- /dev/null +++ b/releasenotes/notes/add-ip-protocols-in-sg-60467a073e771aee.yaml @@ -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.