group-based-policy/gbpservice/neutron/services/grouppolicy/drivers/cisco/apic/apic_mapping_lib.py

230 lines
8.5 KiB
Python

# 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 copy
import six
from neutron_lib import constants as n_constants
from gbpservice.neutron.db.grouppolicy import group_policy_mapping_db as gpdb
from gbpservice.neutron.services.grouppolicy.common import constants as g_const
from gbpservice.neutron.services.grouppolicy.common import exceptions as gpexc
ALLOWING_ACTIONS = [g_const.GP_ACTION_ALLOW, g_const.GP_ACTION_REDIRECT]
REVERSE_PREFIX = 'reverse-'
SERVICE_PREFIX = 'Svc-'
IMPLICIT_PREFIX = 'implicit-'
PER_PROJECT = 'per-project'
REVERSIBLE_PROTOCOLS = [n_constants.PROTO_NAME_TCP.lower(),
n_constants.PROTO_NAME_UDP.lower(),
n_constants.PROTO_NAME_ICMP.lower(), None]
ICMP_REPLY_TYPES = ['echo-rep', 'dst-unreach', 'src-quench', 'time-exceeded']
CP_ENTRY = 'os-entry'
class ExplicitSubnetAssociationNotSupported(gpexc.GroupPolicyBadRequest):
message = _("Explicit subnet association not supported by APIC driver.")
class HierarchicalContractsNotSupported(gpexc.GroupPolicyBadRequest):
message = _("Hierarchical contracts not supported by APIC driver.")
class MultipleExternalPoliciesForL3Policy(gpexc.GroupPolicyBadRequest):
message = _("Potential association of multiple external policies to "
"an L3 Policy.")
class SharedExternalPolicyUnsupported(gpexc.GroupPolicyBadRequest):
message = _("APIC mapping driver does not support sharing of "
"external policies.")
class OnlyOneL3PolicyIsAllowedPerExternalSegment(gpexc.GroupPolicyBadRequest):
message = _("Only one L3 Policy per ES is supported when NAT is disabled "
"on the ES.")
class OnlyOneAddressIsAllowedPerExternalSegment(gpexc.GroupPolicyBadRequest):
message = _("Only one ip address on each ES is supported on "
"APIC GBP driver.")
def get_filter_entries_for_policy_classifier(classifier):
# forward_rules and reverse_rules is each a dict of filter_entries
# with each entry in the dict having the filter_entry name as the
# key and the filter_entry attributes as the value
entries = {'forward_rules': None, 'reverse_rules': None}
x = 0
port_min, port_max = (
gpdb.GroupPolicyMappingDbPlugin._get_min_max_ports_from_range(
classifier['port_range']))
f_attrs = {'etherT': 'unspecified'}
if classifier['protocol']:
f_attrs['etherT'] = 'ip'
f_attrs['prot'] = classifier['protocol'].lower()
if port_min and port_max:
f_attrs['dToPort'] = port_max
f_attrs['dFromPort'] = port_min
entries['forward_rules'] = {_get_filter_entry_name(x): f_attrs}
# Also create reverse rule
if not f_attrs.get('prot') or (
f_attrs.get('prot') in REVERSIBLE_PROTOCOLS):
r_entries = {}
if f_attrs.get('prot') == n_constants.PROTO_NAME_TCP.lower() or (
f_attrs.get('prot') == n_constants.PROTO_NAME_UDP.lower()):
r_attrs = copy.deepcopy(f_attrs)
if f_attrs.get('dToPort') and f_attrs.get('dFromPort'):
r_attrs.pop('dToPort')
r_attrs.pop('dFromPort')
r_attrs['sToPort'] = port_max
r_attrs['sFromPort'] = port_min
if f_attrs.get('prot') == n_constants.PROTO_NAME_TCP.lower():
# Only match on established sessions for tcp
r_attrs['tcpRules'] = 'est'
r_entries[_get_filter_entry_name(x)] = r_attrs
if not f_attrs.get('prot'):
# when no protocol is specified add reverse tcp rule
# only for established sessions
r_attrs = copy.deepcopy(f_attrs)
r_attrs['etherT'] = 'ip'
r_attrs['prot'] = n_constants.PROTO_NAME_TCP.lower()
r_attrs['tcpRules'] = 'est'
r_entries[_get_filter_entry_name(x)] = r_attrs
# add another reverse rulw for UDP
r_attrs = copy.deepcopy(f_attrs)
r_attrs['etherT'] = 'ip'
r_attrs['prot'] = n_constants.PROTO_NAME_UDP.lower()
x += 1
r_entries[_get_filter_entry_name(x)] = r_attrs
if f_attrs.get('prot') == n_constants.PROTO_NAME_ICMP.lower() or (
not f_attrs.get('prot')):
# create more entries for icmp and no protocol cases
for reply_type in ICMP_REPLY_TYPES:
x += 1
r_entry = copy.deepcopy(f_attrs)
r_entry['etherT'] = 'ip'
r_entry['prot'] = n_constants.PROTO_NAME_ICMP.lower()
r_entry['icmpv4T'] = reply_type
r_entries[_get_filter_entry_name(x)] = r_entry
entries['reverse_rules'] = r_entries
return entries
def get_filter_entries_for_policy_rule(context):
# forward_rules and reverse_rules is each a dict of filter_entries
# with each entry in the dict having the filter_entry name as the
# key and the filter_entry attributes as the value
entries = {'forward_rules': None, 'reverse_rules': None}
action = context._plugin.get_policy_action(
context._plugin_context, context.current['policy_actions'][0])
classifier = context._plugin.get_policy_classifier(
context._plugin_context,
context.current['policy_classifier_id'])
if action['action_type'] in ALLOWING_ACTIONS:
entries = get_filter_entries_for_policy_classifier(classifier)
return entries
def get_arp_filter_entry():
return {'arp': {'etherT': 'arp'}}
def get_service_contract_filter_entries():
entries = {}
# DNS
dns_attrs = {'etherT': 'ip',
'prot': 'udp',
'dToPort': 'dns',
'dFromPort': 'dns'}
entries['dns'] = dns_attrs
r_dns_attrs = {'etherT': 'ip',
'prot': 'udp',
'sToPort': 'dns',
'sFromPort': 'dns'}
entries['r-dns'] = r_dns_attrs
# HTTP
http_attrs = {'etherT': 'ip',
'prot': 'tcp',
'dToPort': 80,
'dFromPort': 80}
entries['http'] = http_attrs
r_http_attrs = {'etherT': 'ip',
'prot': 'tcp',
'sToPort': 80,
'sFromPort': 80}
entries['r-http'] = r_http_attrs
icmp_attrs = {'etherT': 'ip',
'prot': 'icmp'}
entries['icmp'] = icmp_attrs
icmpv6_attrs = {'etherT': 'ip',
'prot': 58}
entries['icmpv6'] = icmpv6_attrs
# DHCP
dhcp_attrs = {'etherT': 'ip',
'prot': 'udp',
'dToPort': 68,
'dFromPort': 68,
'sToPort': 67,
'sFromPort': 67}
entries['dhcp'] = dhcp_attrs
r_dhcp_attrs = {'etherT': 'ip',
'prot': 'udp',
'dToPort': 67,
'dFromPort': 67,
'sToPort': 68,
'sFromPort': 68}
entries['r-dhcp'] = r_dhcp_attrs
dhcpv6_attrs = {'etherT': 'ip',
'prot': 'udp',
'dToPort': 546,
'dFromPort': 546,
'sToPort': 547,
'sFromPort': 547}
entries['dhcpv6'] = dhcpv6_attrs
r_dhcpv6_attrs = {'etherT': 'ip',
'prot': 'udp',
'dToPort': 547,
'dFromPort': 547,
'sToPort': 546,
'sFromPort': 546}
entries['r-dhcpv6'] = r_dhcpv6_attrs
# ARP
arp_entries = get_arp_filter_entry()
for k, v in six.iteritems(arp_entries):
entries[k] = v
return entries
def map_to_aim_filter_entry(entry):
mapped_keys = {'etherT': 'ether_type',
'prot': 'ip_protocol',
'dToPort': 'dest_to_port',
'dFromPort': 'dest_from_port',
'sToPort': 'source_to_port',
'sFromPort': 'source_from_port',
'icmpv4T': 'icmpv4_type',
'tcpRules': 'tcp_flags'}
return dict((mapped_keys[k], v) for (k, v) in six.iteritems(entry))
def _get_filter_entry_name(entry_number):
return CP_ENTRY + '-' + str(entry_number)