# 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