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:
parent
47499d291c
commit
592b548bb6
@ -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")
|
||||
|
@ -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),
|
||||
|
@ -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'])
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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},
|
||||
|
@ -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)
|
||||
|
@ -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]}
|
||||
|
@ -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)')
|
||||
]
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user