vmware-nsx/vmware_nsx/services/fwaas/nsx_v/fwaas_callbacks_v2.py
asarfaty 7fc8e0da9a NSX|V: fix rule ID for FWaaS v2 rules
The DB rule id must contain the vnic as well so there are no DB
duplications when adding the same rule to multiple router interfaces.

Change-Id: I817d9434715d7bd3cba266575321d4c89bf173e4
2020-11-11 10:00:15 +00:00

188 lines
7.7 KiB
Python

# Copyright 2018 VMware, Inc.
# All Rights Reserved
#
# 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.
from oslo_log import log as logging
from vmware_nsx.db import nsxv_db
from vmware_nsx.extensions import projectpluginmap
from vmware_nsx.plugins.nsx_v.vshield import edge_firewall_driver
from vmware_nsx.services.fwaas.common import fwaas_callbacks_v2 as \
com_callbacks
from vmware_nsx.services.fwaas.nsx_tv import edge_fwaas_driver_v2 as tv_driver
LOG = logging.getLogger(__name__)
RULE_NAME_PREFIX = 'Fwaas-'
class NsxvFwaasCallbacksV2(com_callbacks.NsxFwaasCallbacksV2):
"""NSX-V RPC callbacks for Firewall As A Service - V2."""
def __init__(self, with_rpc):
super(NsxvFwaasCallbacksV2, self).__init__(with_rpc)
# update the fwaas driver in case of TV plugin
self.internal_driver = None
if self.fwaas_enabled:
if self.fwaas_driver.driver_name == tv_driver.FWAAS_DRIVER_NAME:
self.internal_driver = self.fwaas_driver.get_V_driver()
else:
self.internal_driver = self.fwaas_driver
@property
def plugin_type(self):
return projectpluginmap.NsxPlugins.NSX_V
def should_apply_firewall_to_router(self, context, router, router_id):
"""Return True if the FWaaS rules should be added to this router."""
# in case of a distributed-router:
# router['id'] is the id of the neutron router (=tlr)
# and router_id is the plr/tlr (the one that is being updated)
# First check if there are rules attached to this router
if not super(NsxvFwaasCallbacksV2,
self).should_apply_firewall_to_router(
context, router['id']):
return False
# get all the relevant router info
# ("router" does not have all the fields)
ctx_elevated = context.elevated()
router_data = self.core_plugin.get_router(ctx_elevated, router['id'])
if not router_data:
LOG.error("Couldn't read router %s data", router['id'])
return False
if router_data.get('distributed'):
if router_id == router['id']:
# Do not add firewall rules on the tlr router.
return False
# Check if the FWaaS driver supports this router
if not self.internal_driver.should_apply_firewall_to_router(
router_data, raise_exception=False):
return False
return True
def get_fwaas_rules_for_router(self, context, router_id, router_db,
edge_id):
"""Return the list of (translated) FWaaS rules for this router."""
ctx_elevated = context.elevated()
router_interfaces = self.core_plugin._get_router_interfaces(
ctx_elevated, router_id)
fw_rules = []
# Add firewall rules per port attached to a firewall group
for port in router_interfaces:
fwg = self.get_port_fwg(ctx_elevated, port['id'])
if fwg:
router_dict = {}
self.core_plugin._extend_nsx_router_dict(
router_dict, router_db)
if router_dict['distributed']:
# The vnic_id is ignored for distributed routers, so
# each rule will be applied to all the interfaces.
vnic_id = None
else:
# get the interface vnic
edge_vnic_bind = nsxv_db.get_edge_vnic_binding(
context.session, edge_id, port['network_id'])
vnic_id = 'vnic-index-%s' % edge_vnic_bind.vnic_index
# Add the FWaaS rules for this port
fw_rules.extend(
self.get_port_translated_rules(vnic_id, fwg))
return fw_rules
def get_port_translated_rules(self, vnic_id, firewall_group):
"""Return the list of translated rules per port
Ingress/Egress firewall rules + default ingress/egress drop
"""
port_rules = []
logged = False
# Add the firewall group ingress/egress rules only if the fw is up
if firewall_group['admin_state_up']:
port_rules.extend(self.translate_rules(
firewall_group['ingress_rule_list'],
replace_dest=vnic_id,
logged=logged,
is_ingress=True))
port_rules.extend(self.translate_rules(
firewall_group['egress_rule_list'],
replace_src=vnic_id,
logged=logged,
is_ingress=False))
# Add ingress/egress block rules for this port
default_ingress = {'name': "Block port ingress",
'action': edge_firewall_driver.FWAAS_DENY,
'logged': logged}
default_egress = {'name': "Block port egress",
'action': edge_firewall_driver.FWAAS_DENY,
'logged': logged}
if vnic_id:
default_ingress['destination_vnic_groups'] = [vnic_id]
default_egress['source_vnic_groups'] = [vnic_id]
port_rules.extend([default_ingress, default_egress])
return port_rules
def translate_rules(self, fwaas_rules, replace_dest=None, replace_src=None,
logged=False, is_ingress=True):
translated_rules = []
for rule in fwaas_rules:
if not rule['enabled']:
# skip disabled rules
continue
# Make sure the rule has a name, and it starts with the prefix
# (backend max name length is 30)
if rule.get('name'):
rule['name'] = RULE_NAME_PREFIX + rule['name']
else:
rule['name'] = RULE_NAME_PREFIX + rule['id']
rule['name'] = rule['name'][:30]
if rule.get('id'):
# update rules ID to prevent DB duplications in
# NsxvEdgeFirewallRuleBinding
if is_ingress:
rule['id'] = ('ingress-%s-%s' % (replace_dest,
rule['id']))[:36]
else:
rule['id'] = ('egress-%s-%s' % (replace_src,
rule['id']))[:36]
# source & destination should be lists
if (rule.get('destination_ip_address') and
not rule['destination_ip_address'].startswith('0.0.0.0')):
rule['destination_ip_address'] = [
rule['destination_ip_address']]
else:
if replace_dest:
rule['destination_vnic_groups'] = [replace_dest]
if 'destination_ip_address' in rule:
del rule['destination_ip_address']
if (rule.get('source_ip_address') and
not rule['source_ip_address'].startswith('0.0.0.0')):
rule['source_ip_address'] = [rule['source_ip_address']]
else:
if replace_src:
rule['source_vnic_groups'] = [replace_src]
if 'source_ip_address' in rule:
del rule['source_ip_address']
if logged:
rule['logged'] = True
translated_rules.append(rule)
return translated_rules