/* * l2_bd.c : layer 2 bridge domain * * Copyright (c) 2013 Cisco and/or its affiliates. * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * @file * @brief Ethernet Bridge Domain. * * Code in this file manages Layer 2 bridge domains. * */ bd_main_t bd_main; /** Init bridge domain if not done already. For feature bitmap, set all bits except ARP termination */ void bd_validate (l2_bridge_domain_t * bd_config) { if (!bd_is_valid (bd_config)) { bd_config->feature_bitmap = ~L2INPUT_FEAT_ARP_TERM; bd_config->bvi_sw_if_index = ~0; bd_config->members = 0; bd_config->flood_count = 0; bd_config->tun_master_count = 0; bd_config->tun_normal_count = 0; bd_config->mac_by_ip4 = 0; bd_config->mac_by_ip6 = hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword)); } } u32 bd_find_or_add_bd_index (bd_main_t * bdm, u32 bd_id) { uword *p; u32 rv; if (bd_id == ~0) { bd_id = 0; while (hash_get (bdm->bd_index_by_bd_id, bd_id)) bd_id++; } else { p = hash_get (bdm->bd_index_by_bd_id, bd_id); if (p) return (p[0]); } rv = clib_bitmap_first_clear (bdm->bd_index_bitmap); /* mark this index busy */ bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, rv, 1); hash_set (bdm->bd_index_by_bd_id, bd_id, rv); vec_validate (l2input_main.bd_configs, rv); l2input_main.bd_configs[rv].bd_id = bd_id; return rv; } int bd_delete_bd_index (bd_main_t * bdm, u32 bd_id) { uword *p; u32 bd_index; p = hash_get (bdm->bd_index_by_bd_id, bd_id); if (p == 0) return -1; bd_index = p[0]; /* mark this index clear */ bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, bd_index, 0); hash_unset (bdm->bd_index_by_bd_id, bd_id); l2input_main.bd_configs[bd_index].bd_id = ~0; l2input_main.bd_configs[bd_index].feature_bitmap = 0; return 0; } static void update_flood_count (l2_bridge_domain_t * bd_config) { bd_config->flood_count = vec_len (bd_config->members) - (bd_config->tun_master_count ? bd_config->tun_normal_count : 0); } void bd_add_member (l2_bridge_domain_t * bd_config, l2_flood_member_t * member) { u32 ix; vnet_sw_interface_t *sw_if = vnet_get_sw_interface (vnet_get_main (), member->sw_if_index); /* * Add one element to the vector * vector is ordered [ bvi, normal/tun_masters..., tun_normals... ] * When flooding, the bvi interface (if present) must be the last member * processed due to how BVI processing can change the packet. To enable * this order, we make the bvi interface the first in the vector and * flooding walks the vector in reverse. */ switch (sw_if->flood_class) { case VNET_FLOOD_CLASS_TUNNEL_MASTER: bd_config->tun_master_count++; /* Fall through */ default: /* Fall through */ case VNET_FLOOD_CLASS_NORMAL: ix = (member->flags & L2_FLOOD_MEMBER_BVI) ? 0 : vec_len (bd_config->members) - bd_config->tun_normal_count; break; case VNET_FLOOD_CLASS_TUNNEL_NORMAL: ix = vec_len (bd_config->members); bd_config->tun_normal_count++; break; } vec_insert_elts (bd_config->members, member, 1, ix); update_flood_count (bd_config); } #define BD_REMOVE_ERROR_OK 0 #define BD_REMOVE_ERROR_NOT_FOUND 1 u32 bd_remove_member (l2_bridge_domain_t * bd_config, u32 sw_if_index) { u32 ix; /* Find and delete the member */ vec_foreach_index (ix, bd_config->members) { l2_flood_member_t *m = vec_elt_at_index (bd_config->members, ix); if (m->sw_if_index == sw_if_index) { vnet_sw_interface_t *sw_if = vnet_get_sw_interface (vnet_get_main (), sw_if_index); if (sw_if->flood_class != VNET_FLOOD_CLASS_NORMAL) { if (sw_if->flood_class == VNET_FLOOD_CLASS_TUNNEL_MASTER) bd_config->tun_master_count--; else if (sw_if->flood_class == VNET_FLOOD_CLASS_TUNNEL_NORMAL) bd_config->tun_normal_count--; } vec_delete (bd_config->members, 1, ix); update_flood_count (bd_config); return BD_REMOVE_ERROR_OK; } } return BD_REMOVE_ERROR_NOT_FOUND; } clib_error_t * l2bd_init (vlib_main_t * vm) { bd_main_t *bdm = &bd_main; u32 bd_index; bdm->bd_index_by_bd_id = hash_create (0, sizeof (uword)); /* * create a dummy bd with bd_id of 0 and bd_index of 0 with feature set * to packet drop only. Thus, packets received from any L2 interface with * uninitialized bd_index of 0 can be dropped safely. */ bd_index = bd_find_or_add_bd_index (bdm, 0); ASSERT (bd_index == 0); l2input_main.bd_configs[0].feature_bitmap = L2INPUT_FEAT_DROP; return 0; } VLIB_INIT_FUNCTION (l2bd_init); /** Set the learn/forward/flood flags for the bridge domain. Return 0 if ok, non-zero if for an error. */ u32 bd_set_flags (vlib_main_t * vm, u32 bd_index, u32 flags, u32 enable) { l2_bridge_domain_t *bd_config; u32 feature_bitmap = 0; vec_validate (l2input_main.bd_configs, bd_index); bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index); bd_validate (bd_config); if (flags & L2_LEARN) { feature_bitmap |= L2INPUT_FEAT_LEARN; } if (flags & L2_FWD) { feature_bitmap |= L2INPUT_FEAT_FWD; } if (flags & L2_FLOOD) { feature_bitmap |= L2INPUT_FEAT_FLOOD; } if (flags & L2_UU_FLOOD) { feature_bitmap |= L2INPUT_FEAT_UU_FLOOD; } if (flags & L2_ARP_TERM) { feature_bitmap |= L2INPUT_FEAT_ARP_TERM; } if (enable) { bd_config->feature_bitmap |= feature_bitmap; } else { bd_config->feature_bitmap &= ~feature_bitmap; } return 0; } /** Set the mac age for the bridge domain. */ void bd_set_mac_age (vlib_main_t * vm, u32 bd_index, u8 age) { l2_bridge_domain_t *bd_config; int enable = 0; vec_validate (l2input_main.bd_configs, bd_index); bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index); bd_config->mac_age = age; /* check if there is at least one bd with mac aging enabled */ vec_foreach (bd_config, l2input_main.bd_configs) if (bd_config->bd_id != ~0 && bd_config->mac_age != 0) enable = 1; vlib_process_signal_event (vm, l2fib_mac_age_scanner_process_node.index, enable ? L2_MAC_AGE_PROCESS_EVENT_START : L2_MAC_AGE_PROCESS_EVENT_STOP, 0); } /** Set bridge-domain learn enable/disable. The CLI format is: set bridge-domain learn [disable] */ static clib_error_t * bd_learn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { bd_main_t *bdm = &bd_main; clib_error_t *error = 0; u32 bd_index, bd_id; u32 enable; uword *p; if (!unformat (input, "%d", &bd_id)) { error = clib_error_return (0, "expecting bridge-domain id but got `%U'", format_unformat_error, input); goto done; } p = hash_get (bdm->bd_index_by_bd_id, bd_id); if (p == 0) return clib_error_return (0, "No such bridge domain %d", bd_id); bd_index = p[0]; enable = 1; if (unformat (input, "disable")) { enable = 0; } /* set the bridge domain flag */ if (bd_set_flags (vm, bd_index, L2_LEARN, enable)) { error = clib_error_return (0, "bridge-domain id %d out of range", bd_index); goto done; } done: return error; } /*? * Layer 2 learning can be enabled and disabled on each * interface and on each bridge-domain. Use this command to * manage bridge-domains. It is enabled by default. * * @cliexpar * Example of how to enable learning (where 200 is the bridge-domain-id): * @cliexcmd{set bridge-domain learn 200} * Example of how to disable learning (where 200 is the bridge-domain-id): * @cliexcmd{set bridge-domain learn 200 disable} ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (bd_learn_cli, static) = { .path = "set bridge-domain learn", .short_help = "set bridge-domain learn [disable]", .function = bd_learn, }; /* *INDENT-ON* */ /** Set bridge-domain forward enable/disable. The CLI format is: set bridge-domain forward [disable] */ static clib_error_t * bd_fwd (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { bd_main_t *bdm = &bd_main; clib_error_t *error = 0; u32 bd_index, bd_id; u32 enable; uword *p; if (!unformat (input, "%d", &bd_id)) { error = clib_error_return (0, "expecting bridge-domain id but got `%U'", format_unformat_error, input); goto done; } p = hash_get (bdm->bd_index_by_bd_id, bd_id); if (p == 0) return clib_error_return (0, "No such bridge domain %d", bd_id); bd_index = p[0]; enable = 1; if (unformat (input, "disable")) { enable = 0; } /* set the bridge domain flag */ if (bd_set_flags (vm, bd_index, L2_FWD, enable)) { error = clib_error_return (0, "bridge-domain id %d out of range", bd_index); goto done; } done: return error; } /*? * Layer 2 unicast forwarding can be enabled and disabled on each * interface and on each bridge-domain. Use this command to * manage bridge-domains. It is enabled by default. * * @cliexpar * Example of how to enable forwarding (where 200 is the bridge-domain-id): * @cliexcmd{set bridge-domain forward 200} * Example of how to disable forwarding (where 200 is the bridge-domain-id): * @cliexcmd{set bridge-domain forward 200 disable} ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (bd_fwd_cli, static) = { .path = "set bridge-domain forward", .short_help = "set bridge-domain forward [disable]", .function = bd_fwd, }; /* *INDENT-ON* */ /** Set bridge-domain flood enable/disable. The CLI format is: set bridge-domain flood [disable] */ static clib_error_t * bd_flood (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { bd_main_t *bdm = &bd_main; clib_error_t *error = 0; u32 bd_index
/*
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * 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.
 */
/*
 * plugin.c: plugin handling
 */

#include <vat/vat.h>
#include <vat/plugin.h>
#include <dlfcn.h>
#include <dirent.h>

plugin_main_t vat_plugin_main;

static int 
load_one_plugin (plugin_main_t *pm, plugin_info_t *pi)
{
  void *handle, *register_handle;
  clib_error_t * (*fp)(vat_main_t *);
  clib_error_t * error;
  
  handle = dlopen ((char *)pi->name, RTLD_LAZY);

  /* 
   * Note: this can happen if the plugin has an undefined symbol reference,
   * so print a warning. Otherwise, the poor slob won't know what happened.
   * Ask me how I know that...
   */
  if (handle == 0)
    {
      clib_warning ("%s", dlerror());
      return -1;
    }
  
  pi->handle = handle;

  register_handle = dlsym (pi->handle, "vat_plugin_register");
  if (register_handle == 0)
    return 0;

  fp = register_handle;

  error = (*fp)(pm->vat_main);

  if (error)
    {
      clib_error_report (error);
      dlclose (handle);
      return 1;
    }

  clib_warning ("Loaded plugin: %s", pi->name);

  return 0;
}

static u8 **split_plugin_path (plugin_main_t *pm)
{
  int i;
  u8 **rv = 0;
  u8 *path = pm->plugin_path;
  u8 *this = 0;

  for (i = 0; i < vec_len (pm->plugin_path); i++)
    {
      if (path[i] != ':')
        {
          vec_add1(this, path[i]);
          continue;
        }
      vec_add1(this, 0);
      vec_add1 (rv, this);
      this = 0;
    }
  if (this)
    {
      vec_add1 (this, 0);
      vec_add1 (rv, this);
    }
  return rv;
}

int vat_load_new_plugins (plugin_main_t *pm)
{
  DIR *dp;
  struct dirent *entry;
  struct stat statb;
  uword *p;
  plugin_info_t *pi;
  u8 **plugin_path;
  int i;

  plugin_path = split_plugin_path (pm);
  
  for (i = 0; i < vec_len (plugin_path); i++)
    {
      dp = opendir ((char *)plugin_path[i]);
  
      if (dp == 0)
        continue;
      
      while ((entry = readdir (dp)))
        {
          u8 *plugin_name;
          
          if (pm->plugin_name_filter)
            {
              int j;
              for (j = 0; j < vec_len (pm->plugin_name_filter); j++)
                if (entry->d_name[j] != pm->plugin_name_filter[j])
                  goto next;
            }

          plugin_name = format (0, "%s/%s%c", plugin_path[i],