LCOV - code coverage report
Current view: top level - vnet/l2 - l2_fib.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 341 565 60.4 %
Date: 2023-10-26 01:39:38 Functions: 49 59 83.1 %

          Line data    Source code
       1             : /*
       2             :  * l2_fib.c : layer 2 forwarding table (aka mac table)
       3             :  *
       4             :  * Copyright (c) 2013 Cisco and/or its affiliates.
       5             :  * Licensed under the Apache License, Version 2.0 (the "License");
       6             :  * you may not use this file except in compliance with the License.
       7             :  * You may obtain a copy of the License at:
       8             :  *
       9             :  *     http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      11             :  * Unless required by applicable law or agreed to in writing, software
      12             :  * distributed under the License is distributed on an "AS IS" BASIS,
      13             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14             :  * See the License for the specific language governing permissions and
      15             :  * limitations under the License.
      16             :  */
      17             : 
      18             : 
      19             : #include <vlib/vlib.h>
      20             : #include <vnet/vnet.h>
      21             : #include <vnet/ethernet/ethernet.h>
      22             : #include <vlib/cli.h>
      23             : 
      24             : #include <vppinfra/error.h>
      25             : #include <vppinfra/hash.h>
      26             : #include <vnet/l2/l2_input.h>
      27             : #include <vnet/l2/l2_fib.h>
      28             : #include <vnet/l2/l2_learn.h>
      29             : #include <vnet/l2/l2_bd.h>
      30             : #include <vppinfra/bihash_template.c>
      31             : #include <vlibmemory/api.h>
      32             : 
      33             : #include <vnet/l2/l2.api_enum.h>
      34             : #include <vnet/l2/l2.api_types.h>
      35             : 
      36             : #define vl_endianfun
      37             : #include <vnet/l2/l2.api.h>
      38             : #undef vl_endianfun
      39             : 
      40             : /**
      41             :  * @file
      42             :  * @brief Ethernet MAC Address FIB Table Management.
      43             :  *
      44             :  * The MAC Address forwarding table for bridge-domains is called the l2fib.
      45             :  * Entries are added automatically as part of mac learning, but MAC Addresses
      46             :  * entries can also be added manually.
      47             :  *
      48             :  */
      49             : 
      50             : l2fib_main_t l2fib_main;
      51             : 
      52             : u8 *
      53        5296 : format_l2fib_entry_result_flags (u8 * s, va_list * args)
      54             : {
      55        5296 :   l2fib_entry_result_flags_t flags = va_arg (*args, int);
      56             : 
      57        5296 :   if (L2FIB_ENTRY_RESULT_FLAG_NONE == flags)
      58             :     {
      59        1059 :       s = format (s, "none");
      60             :     }
      61             :   else
      62             :     {
      63             : #define _(a,v,t) {                              \
      64             :       if (flags & L2FIB_ENTRY_RESULT_FLAG_##a)  \
      65             :         s = format (s, "%s ", t);               \
      66             :     }
      67        4237 :       foreach_l2fib_entry_result_attr
      68             : #undef _
      69             :     }
      70        5296 :   return (s);
      71             : }
      72             : 
      73             : static void
      74           0 : incr_mac_address (u8 * mac)
      75             : {
      76           0 :   u64 tmp = *((u64 *) mac);
      77           0 :   tmp = clib_net_to_host_u64 (tmp);
      78           0 :   tmp += 1 << 16;         /* skip unused (least significant) octets */
      79           0 :   tmp = clib_host_to_net_u64 (tmp);
      80             : 
      81           0 :   clib_memcpy_fast (mac, &tmp, 6);
      82           0 : }
      83             : 
      84             : /** Format sw_if_index. If the value is ~0, use the text "N/A" */
      85             : u8 *
      86        5235 : format_vnet_sw_if_index_name_with_NA (u8 * s, va_list * args)
      87             : {
      88        5235 :   vnet_main_t *vnm = va_arg (*args, vnet_main_t *);
      89        5235 :   u32 sw_if_index = va_arg (*args, u32);
      90        5235 :   if (sw_if_index == ~0)
      91         200 :     return format (s, "N/A");
      92             : 
      93             :   vnet_sw_interface_t *swif =
      94        5035 :     vnet_get_sw_interface_or_null (vnm, sw_if_index);
      95        5035 :   if (!swif)
      96           0 :     return format (s, "Stale");
      97             : 
      98        5035 :   return format (s, "%U", format_vnet_sw_interface_name, vnm,
      99             :                  vnet_get_sw_interface_or_null (vnm, sw_if_index));
     100             : }
     101             : 
     102             : typedef struct l2fib_dump_walk_ctx_t_
     103             : {
     104             :   u32 bd_index;
     105             :   l2fib_entry_key_t *l2fe_key;
     106             :   l2fib_entry_result_t *l2fe_res;
     107             : } l2fib_dump_walk_ctx_t;
     108             : 
     109             : static int
     110          58 : l2fib_dump_walk_cb (BVT (clib_bihash_kv) * kvp, void *arg)
     111             : {
     112          58 :   l2fib_dump_walk_ctx_t *ctx = arg;
     113             :   l2fib_entry_result_t result;
     114             :   l2fib_entry_key_t key;
     115             : 
     116          58 :   key.raw = kvp->key;
     117          58 :   result.raw = kvp->value;
     118             : 
     119          58 :   if ((ctx->bd_index == ~0) || (ctx->bd_index == key.fields.bd_index))
     120             :     {
     121          39 :       vec_add1 (ctx->l2fe_key, key);
     122          39 :       vec_add1 (ctx->l2fe_res, result);
     123             :     }
     124             : 
     125          58 :   return (BIHASH_WALK_CONTINUE);
     126             : }
     127             : 
     128             : void
     129          11 : l2fib_table_dump (u32 bd_index,
     130             :                   l2fib_entry_key_t ** l2fe_key,
     131             :                   l2fib_entry_result_t ** l2fe_res)
     132             : {
     133          11 :   l2fib_main_t *msm = &l2fib_main;
     134          11 :   l2fib_dump_walk_ctx_t ctx = {
     135             :     .bd_index = bd_index,
     136             :   };
     137             : 
     138          11 :   BV (clib_bihash_foreach_key_value_pair)
     139             :     (&msm->mac_table, l2fib_dump_walk_cb, &ctx);
     140             : 
     141          11 :   *l2fe_key = ctx.l2fe_key;
     142          11 :   *l2fe_res = ctx.l2fe_res;
     143          11 : }
     144             : 
     145             : void
     146        4990 : l2_fib_extract_seq_num (l2fib_seq_num_t sn, u8 * bd_sn, u8 * if_sn)
     147             : {
     148        4990 :   *bd_sn = sn >> 8;
     149        4990 :   *if_sn = sn & 0xff;
     150        4990 : }
     151             : 
     152             : u8 *
     153        4990 : format_l2_fib_seq_num (u8 * s, va_list * a)
     154             : {
     155        4990 :   l2fib_seq_num_t sn = va_arg (*a, int);
     156             :   u8 bd_sn, if_sn;
     157             : 
     158        4990 :   l2_fib_extract_seq_num (sn, &bd_sn, &if_sn);
     159             : 
     160        4990 :   s = format (s, "%3d/%-3d", bd_sn, if_sn);
     161             : 
     162        4990 :   return (s);
     163             : }
     164             : 
     165             : typedef struct l2fib_show_walk_ctx_t_
     166             : {
     167             :   u8 first_entry;
     168             :   u8 verbose;
     169             :   vlib_main_t *vm;
     170             :   vnet_main_t *vnm;
     171             :   u32 total_entries;
     172             :   u32 bd_index;
     173             :   u8 learn;
     174             :   u8 add;
     175             :   u8 now;
     176             : } l2fib_show_walk_ctx_t;
     177             : 
     178             : static int
     179        6035 : l2fib_show_walk_cb (BVT (clib_bihash_kv) * kvp, void *arg)
     180             : {
     181        6035 :   l2fib_show_walk_ctx_t *ctx = arg;
     182             :   l2_bridge_domain_t *bd_config;
     183             :   l2fib_entry_result_t result;
     184             :   l2fib_entry_key_t key;
     185             : 
     186        6035 :   if (ctx->verbose && ctx->first_entry)
     187             :     {
     188         111 :       ctx->first_entry = 0;
     189         111 :       vlib_cli_output (ctx->vm,
     190             :                        "%=19s%=7s%=7s%=8s%=9s%=7s%=7s%=5s%=30s",
     191             :                        "Mac-Address", "BD-Idx", "If-Idx",
     192             :                        "BSN-ISN", "Age(min)", "static", "filter",
     193             :                        "bvi", "Interface-Name");
     194             :     }
     195             : 
     196        6035 :   key.raw = kvp->key;
     197        6035 :   result.raw = kvp->value;
     198        6035 :   ctx->total_entries++;
     199             : 
     200        6035 :   if (ctx->verbose &&
     201        4990 :       ((ctx->bd_index >> 31) || (ctx->bd_index == key.fields.bd_index)))
     202             :     {
     203        4990 :       u8 *s = NULL;
     204             : 
     205        4990 :       if (ctx->learn && l2fib_entry_result_is_set_AGE_NOT (&result))
     206           0 :         return (BIHASH_WALK_CONTINUE);  /* skip provisioned macs */
     207             : 
     208        4990 :       if (ctx->add && !l2fib_entry_result_is_set_AGE_NOT (&result))
     209           0 :         return (BIHASH_WALK_CONTINUE);  /* skip learned macs */
     210             : 
     211        4990 :       bd_config = &vec_elt (l2input_main.bd_configs, key.fields.bd_index);
     212             : 
     213        4990 :       if (l2fib_entry_result_is_set_AGE_NOT (&result))
     214        3361 :         s = format (s, "no");
     215        1629 :       else if (bd_config->mac_age == 0)
     216        1629 :         s = format (s, "-");
     217             :       else
     218             :         {
     219           0 :           i16 delta = ctx->now - result.fields.timestamp;
     220           0 :           delta += delta < 0 ? 256 : 0;
     221           0 :           s = format (s, "%d", delta);
     222             :         }
     223             : 
     224       14970 :       vlib_cli_output (ctx->vm,
     225             :                        "%=19U%=7d%=7d %U%=9v%=7s%=7s%=5s%=30U",
     226             :                        format_ethernet_address, key.fields.mac,
     227        4990 :                        key.fields.bd_index,
     228             :                        result.fields.sw_if_index == ~0
     229             :                        ? -1 : result.fields.sw_if_index,
     230        4990 :                        format_l2_fib_seq_num, result.fields.sn, s,
     231        4990 :                        l2fib_entry_result_is_set_STATIC (&result) ? "*" : "-",
     232        4990 :                        l2fib_entry_result_is_set_FILTER (&result) ? "*" : "-",
     233        4990 :                        l2fib_entry_result_is_set_BVI (&result) ? "*" : "-",
     234             :                        format_vnet_sw_if_index_name_with_NA,
     235             :                        ctx->vnm, result.fields.sw_if_index);
     236        4990 :       vec_free (s);
     237             :     }
     238             : 
     239        6035 :   return (BIHASH_WALK_CONTINUE);
     240             : }
     241             : 
     242             : /** Display the contents of the l2fib. */
     243             : static clib_error_t *
     244         143 : show_l2fib (vlib_main_t * vm,
     245             :             unformat_input_t * input, vlib_cli_command_t * cmd)
     246             : {
     247         143 :   bd_main_t *bdm = &bd_main;
     248         143 :   l2fib_main_t *msm = &l2fib_main;
     249         143 :   u8 raw = 0;
     250             :   u32 bd_id;
     251         286 :   l2fib_show_walk_ctx_t ctx = {
     252             :     .first_entry = 1,
     253             :     .bd_index = ~0,
     254         286 :     .now = (u8) (vlib_time_now (vm) / 60),
     255             :     .vm = vm,
     256         143 :     .vnm = msm->vnet_main,
     257             :   };
     258             : 
     259         275 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     260             :     {
     261         132 :       if (unformat (input, "raw"))
     262             :         {
     263           0 :           raw = 1;
     264           0 :           ctx.verbose = 0;
     265           0 :           break;
     266             :         }
     267         132 :       else if (unformat (input, "verbose"))
     268         132 :         ctx.verbose = 1;
     269           0 :       else if (unformat (input, "all"))
     270           0 :         ctx.verbose = 1;
     271           0 :       else if (unformat (input, "bd_index %d", &ctx.bd_index))
     272           0 :         ctx.verbose = 1;
     273           0 :       else if (unformat (input, "learn"))
     274             :         {
     275           0 :           ctx.add = 0;
     276           0 :           ctx.learn = 1;
     277           0 :           ctx.verbose = 1;
     278             :         }
     279           0 :       else if (unformat (input, "add"))
     280             :         {
     281           0 :           ctx.learn = 0;
     282           0 :           ctx.add = 1;
     283           0 :           ctx.verbose = 1;
     284             :         }
     285           0 :       else if (unformat (input, "bd_id %d", &bd_id))
     286             :         {
     287           0 :           uword *p = hash_get (bdm->bd_index_by_bd_id, bd_id);
     288           0 :           if (p)
     289             :             {
     290           0 :               ctx.verbose = 1;
     291           0 :               ctx.bd_index = p[0];
     292             :             }
     293             :           else
     294           0 :             return clib_error_return (0,
     295             :                                       "bridge domain id %d doesn't exist\n",
     296             :                                       bd_id);
     297             :         }
     298             :       else
     299           0 :         break;
     300             :     }
     301             : 
     302         143 :   if (msm->mac_table_initialized == 0)
     303             :     {
     304           0 :       vlib_cli_output (vm, "no l2fib entries");
     305           0 :       return 0;
     306             :     }
     307             : 
     308         143 :   BV (clib_bihash_foreach_key_value_pair)
     309             :     (&msm->mac_table, l2fib_show_walk_cb, &ctx);
     310             : 
     311         143 :   if (ctx.total_entries == 0)
     312          21 :     vlib_cli_output (vm, "no l2fib entries");
     313             :   else
     314             :     {
     315         122 :       l2learn_main_t *lm = &l2learn_main;
     316         122 :       vlib_cli_output (vm, "L2FIB total/learned entries: %d/%d  "
     317             :                        "Last scan time: %.4esec  Learn limit: %d ",
     318             :                        ctx.total_entries, lm->global_learn_count,
     319             :                        msm->age_scan_duration, lm->global_learn_limit);
     320         122 :       if (lm->client_pid)
     321           4 :         vlib_cli_output (vm, "L2MAC events client PID: %d  "
     322             :                          "Last e-scan time: %.4esec  Delay: %.2esec  "
     323             :                          "Max macs in event: %d",
     324             :                          lm->client_pid, msm->evt_scan_duration,
     325             :                          msm->event_scan_delay, msm->max_macs_in_event);
     326             :     }
     327             : 
     328         143 :   if (raw)
     329           0 :     vlib_cli_output (vm, "Raw Hash Table:\n%U\n",
     330             :                      BV (format_bihash), &msm->mac_table, 1 /* verbose */ );
     331             : 
     332         143 :   return 0;
     333             : }
     334             : 
     335             : /*?
     336             :  * This command displays the MAC Address entries of the L2 FIB table.
     337             :  * Output can be filtered to just get the number of MAC Addresses or display
     338             :  * each MAC Address for all bridge domains or just a single bridge domain.
     339             :  *
     340             :  * @cliexpar
     341             :  * Example of how to display the number of MAC Address entries in the L2
     342             :  * FIB table:
     343             :  * @cliexstart{show l2fib}
     344             :  * 3 l2fib entries
     345             :  * @cliexend
     346             :  * Example of how to display all the MAC Address entries in the L2
     347             :  * FIB table:
     348             :  * @cliexstart{show l2fib all}
     349             :  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
     350             :  *  52:54:00:53:18:33    1      GigabitEthernet0/8/0.200      3       0       0     0      0         0
     351             :  *  52:54:00:53:18:55    1      GigabitEthernet0/8/0.200      3       1       0     0      0         0
     352             :  *  52:54:00:53:18:77    1                 N/A                -1      1       1     0      0         0
     353             :  * 3 l2fib entries
     354             :  * @cliexend
     355             : ?*/
     356             : /* *INDENT-OFF* */
     357      285289 : VLIB_CLI_COMMAND (show_l2fib_cli, static) = {
     358             :   .path = "show l2fib",
     359             :   .short_help = "show l2fib [all] | [bd_id <nn> | bd_index <nn>] [learn | add] | [raw]",
     360             :   .function = show_l2fib,
     361             : };
     362             : /* *INDENT-ON* */
     363             : 
     364             : void
     365          57 : l2fib_table_init (void)
     366             : {
     367          57 :   l2fib_main_t *mp = &l2fib_main;
     368             : 
     369          57 :   if (mp->mac_table_initialized == 1)
     370           0 :     return;
     371             : 
     372          57 :   BV (clib_bihash_init) (&mp->mac_table, "l2fib mac table",
     373          57 :                          mp->mac_table_n_buckets, mp->mac_table_memory_size);
     374          57 :   mp->mac_table_initialized = 1;
     375             : }
     376             : 
     377             : /* Remove all entries from the l2fib */
     378             : void
     379           0 : l2fib_clear_table (void)
     380             : {
     381           0 :   l2fib_main_t *mp = &l2fib_main;
     382             :   l2_bridge_domain_t *bd_config;
     383             : 
     384           0 :   if (mp->mac_table_initialized == 0)
     385           0 :     return;
     386             : 
     387           0 :   mp->mac_table_initialized = 0;
     388             : 
     389             :   /* Remove all entries */
     390           0 :   BV (clib_bihash_free) (&mp->mac_table);
     391           0 :   l2fib_table_init ();
     392           0 :   l2learn_main.global_learn_count = 0;
     393           0 :   vec_foreach (bd_config, l2input_main.bd_configs)
     394           0 :     bd_config->learn_count = 0;
     395             : }
     396             : 
     397             : /** Clear all entries in L2FIB.
     398             :  * @TODO: Later we may want a way to remove only the non-static entries
     399             :  */
     400             : static clib_error_t *
     401           0 : clear_l2fib (vlib_main_t * vm,
     402             :              unformat_input_t * input, vlib_cli_command_t * cmd)
     403             : {
     404           0 :   l2fib_clear_table ();
     405           0 :   return 0;
     406             : }
     407             : 
     408             : /*?
     409             :  * This command clears all the MAC Address entries from the L2 FIB table.
     410             :  *
     411             :  * @cliexpar
     412             :  * Example of how to clear the L2 FIB Table:
     413             :  * @cliexcmd{clear l2fib}
     414             :  * Example to show the L2 FIB Table has been cleared:
     415             :  * @cliexstart{show l2fib verbose}
     416             :  * no l2fib entries
     417             :  * @cliexend
     418             : ?*/
     419             : /* *INDENT-OFF* */
     420      285289 : VLIB_CLI_COMMAND (clear_l2fib_cli, static) = {
     421             :   .path = "clear l2fib",
     422             :   .short_help = "clear l2fib",
     423             :   .function = clear_l2fib,
     424             : };
     425             : /* *INDENT-ON* */
     426             : 
     427             : static l2fib_seq_num_t
     428        1615 : l2fib_cur_seq_num (u32 bd_index, u32 sw_if_index)
     429             : {
     430        1615 :   l2_bridge_domain_t *bd_config = l2input_bd_config (bd_index);
     431             : 
     432        1615 :   return l2_fib_mk_seq_num (bd_config->seq_num,
     433        1615 :                             l2_input_seq_num (sw_if_index));
     434             : }
     435             : 
     436             : /**
     437             :  * Add an entry to the l2fib.
     438             :  * If the entry already exists then overwrite it
     439             :  */
     440             : void
     441        1392 : l2fib_add_entry (const u8 * mac, u32 bd_index,
     442             :                  u32 sw_if_index, l2fib_entry_result_flags_t flags)
     443             : {
     444             :   l2fib_entry_key_t key;
     445             :   l2fib_entry_result_t result;
     446             :   __attribute__ ((unused)) u32 bucket_contents;
     447        1392 :   l2fib_main_t *fm = &l2fib_main;
     448        1392 :   l2learn_main_t *lm = &l2learn_main;
     449             :   BVT (clib_bihash_kv) kv;
     450             : 
     451        1392 :   if (fm->mac_table_initialized == 0)
     452           0 :     l2fib_table_init ();
     453             : 
     454             :   /* set up key */
     455        1392 :   key.raw = l2fib_make_key (mac, bd_index);
     456        1392 :   kv.key = key.raw;
     457             : 
     458             :   /* check if entry already exist */
     459        1392 :   if (BV (clib_bihash_search) (&fm->mac_table, &kv, &kv))
     460             :     {
     461             :       /* decrement counter if overwriting a learned mac  */
     462         792 :       result.raw = kv.value;
     463         792 :       if (!l2fib_entry_result_is_set_AGE_NOT (&result))
     464             :         {
     465          11 :           l2_bridge_domain_t *bd_config =
     466          11 :             vec_elt_at_index (l2input_main.bd_configs, bd_index);
     467             : 
     468             :           /* check if learn_count == 0 in case of race condition between 2
     469             :            * workers adding an entry simultaneously */
     470             :           /* learn_count variable may have little inaccuracy because they are
     471             :            * not incremented/decremented with atomic operations */
     472             :           /* l2fib_scan is call every 2sec fixing potential inaccuracy */
     473          11 :           if (lm->global_learn_count)
     474           0 :             lm->global_learn_count--;
     475          11 :           if (bd_config->learn_count)
     476           0 :             bd_config->learn_count--;
     477             :         }
     478             :     }
     479             : 
     480             :   /* set up result */
     481        1392 :   result.raw = 0;               /* clear all fields */
     482        1392 :   result.fields.sw_if_index = sw_if_index;
     483        1392 :   result.fields.flags = flags;
     484             : 
     485             :   /* no aging for provisioned entry */
     486        1392 :   l2fib_entry_result_set_AGE_NOT (&result);
     487             : 
     488        1392 :   kv.value = result.raw;
     489             : 
     490        1392 :   BV (clib_bihash_add_del) (&fm->mac_table, &kv, 1 /* is_add */ );
     491        1392 : }
     492             : 
     493             : /**
     494             :  * Add an entry to the L2FIB.
     495             :  * The CLI format is:
     496             :  *    l2fib add <mac> <bd> <intf> [static] [bvi]
     497             :  *    l2fib add <mac> <bd> filter
     498             :  * Note that filter and bvi entries are always static
     499             :  */
     500             : static clib_error_t *
     501           0 : l2fib_add (vlib_main_t * vm,
     502             :            unformat_input_t * input, vlib_cli_command_t * cmd)
     503             : {
     504           0 :   bd_main_t *bdm = &bd_main;
     505           0 :   vnet_main_t *vnm = vnet_get_main ();
     506           0 :   clib_error_t *error = 0;
     507             :   u8 mac[6];
     508             :   u32 bd_id;
     509             :   u32 bd_index;
     510           0 :   u32 sw_if_index = ~0;
     511             :   uword *p;
     512             :   l2fib_entry_result_flags_t flags;
     513             : 
     514           0 :   flags = L2FIB_ENTRY_RESULT_FLAG_NONE;
     515             : 
     516           0 :   if (!unformat (input, "%U", unformat_ethernet_address, mac))
     517             :     {
     518           0 :       error = clib_error_return (0, "expected mac address `%U'",
     519             :                                  format_unformat_error, input);
     520           0 :       goto done;
     521             :     }
     522             : 
     523           0 :   if (!unformat (input, "%d", &bd_id))
     524             :     {
     525           0 :       error = clib_error_return (0, "expected bridge domain ID `%U'",
     526             :                                  format_unformat_error, input);
     527           0 :       goto done;
     528             :     }
     529             : 
     530           0 :   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
     531           0 :   if (!p)
     532             :     {
     533           0 :       error = clib_error_return (0, "bridge domain ID %d invalid", bd_id);
     534           0 :       goto done;
     535             :     }
     536           0 :   bd_index = p[0];
     537             : 
     538           0 :   if (unformat (input, "filter"))
     539             :     {
     540           0 :       l2fib_add_filter_entry (mac, bd_index);
     541           0 :       return 0;
     542             :     }
     543             : 
     544           0 :   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
     545             :     {
     546           0 :       error = clib_error_return (0, "unknown interface `%U'",
     547             :                                  format_unformat_error, input);
     548           0 :       goto done;
     549             :     }
     550             : 
     551           0 :   if (unformat (input, "static"))
     552           0 :     flags |= L2FIB_ENTRY_RESULT_FLAG_STATIC;
     553           0 :   else if (unformat (input, "bvi"))
     554           0 :     flags |= (L2FIB_ENTRY_RESULT_FLAG_STATIC | L2FIB_ENTRY_RESULT_FLAG_BVI);
     555             : 
     556           0 :   if (vec_len (l2input_main.configs) <= sw_if_index)
     557             :     {
     558           0 :       error = clib_error_return (0, "Interface sw_if_index %d not in L2 mode",
     559             :                                  sw_if_index);
     560           0 :       goto done;
     561             :     }
     562             : 
     563           0 :   l2fib_add_entry (mac, bd_index, sw_if_index, flags);
     564             : 
     565           0 : done:
     566           0 :   return error;
     567             : }
     568             : 
     569             : /*?
     570             :  * This command adds a MAC Address entry to the L2 FIB table
     571             :  * of an existing bridge-domain. The MAC Address can be static
     572             :  * or dynamic. This command also allows a filter to be added,
     573             :  * such that packets with given MAC Addresses (source mac or
     574             :  * destination mac match) are dropped.
     575             :  *
     576             :  * @cliexpar
     577             :  * Example of how to add a dynamic MAC Address entry to the L2 FIB table
     578             :  * of a bridge-domain (where 200 is the bridge-domain-id):
     579             :  * @cliexcmd{l2fib add 52:54:00:53:18:33 200 GigabitEthernet0/8/0.200}
     580             :  * Example of how to add a static MAC Address entry to the L2 FIB table
     581             :  * of a bridge-domain (where 200 is the bridge-domain-id):
     582             :  * @cliexcmd{l2fib add 52:54:00:53:18:55 200 GigabitEthernet0/8/0.200 static}
     583             :  * Example of how to add a filter such that a packet with the given MAC
     584             :  * Address will be dropped in a given bridge-domain (where 200 is the
     585             :  * bridge-domain-id):
     586             :  * @cliexcmd{l2fib add 52:54:00:53:18:77 200 filter}
     587             :  * Example of show command of the provisioned MAC Addresses and filters:
     588             :  * @cliexstart{show l2fib verbose}
     589             :  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
     590             :  *  52:54:00:53:18:33    1      GigabitEthernet0/8/0.200      3       0       0     0      0         0
     591             :  *  52:54:00:53:18:55    1      GigabitEthernet0/8/0.200      3       1       0     0      0         0
     592             :  *  52:54:00:53:18:77    1                 N/A                -1      1       1     0      0         0
     593             :  * 3 l2fib entries
     594             :  * @cliexend
     595             : ?*/
     596             : /* *INDENT-OFF* */
     597      285289 : VLIB_CLI_COMMAND (l2fib_add_cli, static) = {
     598             :   .path = "l2fib add",
     599             :   .short_help = "l2fib add <mac> <bridge-domain-id> filter | <intf> [static | bvi]",
     600             :   .function = l2fib_add,
     601             : };
     602             : /* *INDENT-ON* */
     603             : 
     604             : 
     605             : static clib_error_t *
     606           0 : l2fib_test_command_fn (vlib_main_t * vm,
     607             :                        unformat_input_t * input, vlib_cli_command_t * cmd)
     608             : {
     609             :   u8 mac[8], save_mac[6];
     610           0 :   u32 bd_index = 0;
     611           0 :   u32 sw_if_index = 8;
     612           0 :   u32 is_add = 0;
     613           0 :   u32 is_del = 0;
     614           0 :   u32 is_check = 0;
     615           0 :   u32 count = 1;
     616           0 :   int mac_set = 0;
     617             :   int i;
     618             : 
     619           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     620             :     {
     621           0 :       if (unformat (input, "mac %U", unformat_ethernet_address, mac))
     622           0 :         mac_set = 1;
     623           0 :       else if (unformat (input, "add"))
     624           0 :         is_add = 1;
     625           0 :       else if (unformat (input, "del"))
     626           0 :         is_del = 1;
     627           0 :       else if (unformat (input, "check"))
     628           0 :         is_check = 1;
     629           0 :       else if (unformat (input, "count %d", &count))
     630             :         ;
     631             :       else
     632           0 :         break;
     633             :     }
     634             : 
     635           0 :   if (mac_set == 0)
     636           0 :     return clib_error_return (0, "mac not set");
     637             : 
     638           0 :   if (is_add == 0 && is_del == 0 && is_check == 0)
     639           0 :     return clib_error_return (0,
     640             :                               "noop: pick at least one of (add,del,check)");
     641             : 
     642           0 :   clib_memcpy_fast (save_mac, mac, 6);
     643             : 
     644           0 :   if (is_add)
     645             :     {
     646           0 :       for (i = 0; i < count; i++)
     647             :         {
     648           0 :           l2fib_add_entry (mac, bd_index, sw_if_index,
     649             :                            L2FIB_ENTRY_RESULT_FLAG_NONE);
     650           0 :           incr_mac_address (mac);
     651             :         }
     652             :     }
     653             : 
     654           0 :   if (is_check)
     655             :     {
     656             :       BVT (clib_bihash_kv) kv;
     657           0 :       l2fib_main_t *mp = &l2fib_main;
     658             : 
     659           0 :       if (mp->mac_table_initialized == 0)
     660           0 :         return clib_error_return (0, "mac table is not initialized");
     661             : 
     662           0 :       clib_memcpy_fast (mac, save_mac, 6);
     663             : 
     664           0 :       for (i = 0; i < count; i++)
     665             :         {
     666           0 :           kv.key = l2fib_make_key (mac, bd_index);
     667           0 :           if (BV (clib_bihash_search) (&mp->mac_table, &kv, &kv))
     668             :             {
     669           0 :               clib_warning ("key %U AWOL", format_ethernet_address, mac);
     670           0 :               break;
     671             :             }
     672           0 :           incr_mac_address (mac);
     673             :         }
     674             :     }
     675             : 
     676           0 :   if (is_del)
     677             :     {
     678           0 :       clib_memcpy_fast (mac, save_mac, 6);
     679             : 
     680           0 :       for (i = 0; i < count; i++)
     681             :         {
     682           0 :           l2fib_del_entry (mac, bd_index, 0);
     683           0 :           incr_mac_address (mac);
     684             :         }
     685             :     }
     686             : 
     687           0 :   return 0;
     688             : }
     689             : 
     690             : /*?
     691             :  * The set of '<em>test l2fib</em>' commands allow the L2 FIB table of the default
     692             :  * bridge domain (bridge-domain-id of 0) to be modified.
     693             :  *
     694             :  * @cliexpar
     695             :  * @parblock
     696             :  * Example of how to add a set of 4 sequential MAC Address entries to L2
     697             :  * FIB table of the default bridge-domain:
     698             :  * @cliexcmd{test l2fib add mac 52:54:00:53:00:00 count 4}
     699             :  *
     700             :  * Show the set of 4 sequential MAC Address entries that were added:
     701             :  * @cliexstart{show l2fib verbose}
     702             :  *     Mac Address     BD Idx           Interface           Index  static  filter  bvi  refresh  timestamp
     703             :  * 52:54:00:53:00:00    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
     704             :  * 52:54:00:53:00:01    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
     705             :  * 52:54:00:53:00:03    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
     706             :  * 52:54:00:53:00:02    0       GigabitEthernet0/8/0.300     8       0       0     0      0         0
     707             :  * 4 l2fib entries
     708             :  * @cliexend
     709             :  *
     710             :  * Example of how to check that the set of 4 sequential MAC Address
     711             :  * entries were added to L2 FIB table of the default
     712             :  * bridge-domain. Used a count of 5 to produce an error:
     713             :  *
     714             :  * @cliexcmd{test l2fib check mac 52:54:00:53:00:00 count 5}
     715             :  * The output of the check command is in the log files. Log file
     716             :  * location may vary based on your OS and Version:
     717             :  *
     718             :  * <b><em># tail -f /var/log/messages | grep l2fib_test_command_fn</em></b>
     719             :  *
     720             :  * Sep  7 17:15:24 localhost vnet[4952]: l2fib_test_command_fn:446: key 52:54:00:53:00:04 AWOL
     721             :  *
     722             :  * Example of how to delete a set of 4 sequential MAC Address entries
     723             :  * from L2 FIB table of the default bridge-domain:
     724             :  * @cliexcmd{test l2fib del mac 52:54:00:53:00:00 count 4}
     725             :  * @endparblock
     726             : ?*/
     727             : /* *INDENT-OFF* */
     728      285289 : VLIB_CLI_COMMAND (l2fib_test_command, static) = {
     729             :   .path = "test l2fib",
     730             :   .short_help = "test l2fib [add|del|check] mac <base-addr> count <nn>",
     731             :   .function = l2fib_test_command_fn,
     732             : };
     733             : /* *INDENT-ON* */
     734             : 
     735             : 
     736             : /**
     737             :  * Delete an entry from the l2fib.
     738             :  * Return 0 if the entry was deleted, or 1 it was not found or if
     739             :  * sw_if_index is non-zero and does not match that in the entry.
     740             :  */
     741             : u32
     742          39 : l2fib_del_entry (const u8 * mac, u32 bd_index, u32 sw_if_index)
     743             : {
     744             :   l2fib_entry_result_t result;
     745          39 :   l2fib_main_t *mp = &l2fib_main;
     746             :   BVT (clib_bihash_kv) kv;
     747             : 
     748          39 :   if (mp->mac_table_initialized == 0)
     749           0 :     return 1;
     750             : 
     751             :   /* set up key */
     752          39 :   kv.key = l2fib_make_key (mac, bd_index);
     753             : 
     754          39 :   if (BV (clib_bihash_search) (&mp->mac_table, &kv, &kv))
     755           0 :     return 1;
     756             : 
     757          39 :   result.raw = kv.value;
     758             : 
     759             :   /*  check if sw_if_index of entry match */
     760          39 :   if ((sw_if_index != 0) && (sw_if_index != result.fields.sw_if_index))
     761           0 :     return 1;
     762             : 
     763             :   /* decrement counter if dynamically learned mac */
     764          39 :   if (!l2fib_entry_result_is_set_AGE_NOT (&result))
     765             :     {
     766           0 :       l2_bridge_domain_t *bd_config =
     767           0 :         vec_elt_at_index (l2input_main.bd_configs, bd_index);
     768           0 :       if (l2learn_main.global_learn_count)
     769           0 :         l2learn_main.global_learn_count--;
     770           0 :       if (bd_config->learn_count)
     771           0 :         bd_config->learn_count--;
     772             :     }
     773             : 
     774             :   /* Remove entry from hash table */
     775          39 :   BV (clib_bihash_add_del) (&mp->mac_table, &kv, 0 /* is_add */ );
     776          39 :   return 0;
     777             : }
     778             : 
     779             : /**
     780             :  * Delete an entry from the L2FIB.
     781             :  * The CLI format is:
     782             :  *    l2fib del <mac> <bd-id>
     783             :  */
     784             : static clib_error_t *
     785           0 : l2fib_del (vlib_main_t * vm,
     786             :            unformat_input_t * input, vlib_cli_command_t * cmd)
     787             : {
     788           0 :   bd_main_t *bdm = &bd_main;
     789           0 :   clib_error_t *error = 0;
     790             :   u8 mac[6];
     791             :   u32 bd_id;
     792             :   u32 bd_index;
     793             :   uword *p;
     794             : 
     795           0 :   if (!unformat (input, "%U", unformat_ethernet_address, mac))
     796             :     {
     797           0 :       error = clib_error_return (0, "expected mac address `%U'",
     798             :                                  format_unformat_error, input);
     799           0 :       goto done;
     800             :     }
     801             : 
     802           0 :   if (!unformat (input, "%d", &bd_id))
     803             :     {
     804           0 :       error = clib_error_return (0, "expected bridge domain ID `%U'",
     805             :                                  format_unformat_error, input);
     806           0 :       goto done;
     807             :     }
     808             : 
     809           0 :   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
     810           0 :   if (!p)
     811             :     {
     812           0 :       error = clib_error_return (0, "bridge domain ID %d invalid", bd_id);
     813           0 :       goto done;
     814             :     }
     815           0 :   bd_index = p[0];
     816             : 
     817             :   /* Delete the entry */
     818           0 :   if (l2fib_del_entry (mac, bd_index, 0))
     819             :     {
     820           0 :       error = clib_error_return (0, "mac entry not found");
     821           0 :       goto done;
     822             :     }
     823             : 
     824           0 : done:
     825           0 :   return error;
     826             : }
     827             : 
     828             : /*?
     829             :  * This command deletes an existing MAC Address entry from the L2 FIB
     830             :  * table of an existing bridge-domain.
     831             :  *
     832             :  * @cliexpar
     833             :  * Example of how to delete a MAC Address entry from the L2 FIB table of a bridge-domain (where 200 is the bridge-domain-id):
     834             :  * @cliexcmd{l2fib del 52:54:00:53:18:33 200}
     835             : ?*/
     836             : /* *INDENT-OFF* */
     837      285289 : VLIB_CLI_COMMAND (l2fib_del_cli, static) = {
     838             :   .path = "l2fib del",
     839             :   .short_help = "l2fib del <mac> <bridge-domain-id> []",
     840             :   .function = l2fib_del,
     841             : };
     842             : /* *INDENT-ON* */
     843             : 
     844             : static clib_error_t *
     845           0 : l2fib_set_scan_delay (vlib_main_t *vm, unformat_input_t *input,
     846             :                       vlib_cli_command_t *cmd)
     847             : {
     848           0 :   clib_error_t *error = 0;
     849             :   u32 scan_delay;
     850           0 :   l2fib_main_t *fm = &l2fib_main;
     851             : 
     852           0 :   if (!unformat (input, "%d", &scan_delay))
     853             :     {
     854           0 :       error = clib_error_return (0, "expecting delay but got `%U'",
     855             :                                  format_unformat_error, input);
     856           0 :       goto done;
     857             :     }
     858           0 :   fm->event_scan_delay = (f64) (scan_delay) *10e-3;
     859           0 :   l2fib_flush_all_mac (vlib_get_main ());
     860           0 : done:
     861           0 :   return error;
     862             : }
     863             : 
     864             : /*?
     865             :  * This command set scan delay (in 1/10s unit)
     866             :  *
     867             : ?*/
     868      285289 : VLIB_CLI_COMMAND (l2fib_set_scan_delay_cli, static) = {
     869             :   .path = "set l2fib scan-delay",
     870             :   .short_help = "set l2fib scan-delay <delay>",
     871             :   .function = l2fib_set_scan_delay,
     872             : };
     873             : 
     874             : /**
     875             :     Kick off ager to scan MACs to age/delete MAC entries
     876             : */
     877             : void
     878        2102 : l2fib_start_ager_scan (vlib_main_t * vm)
     879             : {
     880        2102 :   uword evt = L2_MAC_AGE_PROCESS_EVENT_ONE_PASS;
     881             : 
     882             :   /* check if there is at least one bd with mac aging enabled */
     883             :   l2_bridge_domain_t *bd_config;
     884        8756 :   vec_foreach (bd_config, l2input_main.bd_configs)
     885             :   {
     886        6654 :     if (bd_config->bd_id != ~0 && bd_config->mac_age != 0)
     887             :       {
     888           0 :         evt = L2_MAC_AGE_PROCESS_EVENT_START;
     889           0 :         break;
     890             :       }
     891             :   }
     892             : 
     893        2102 :   vlib_process_signal_event (vm, l2fib_mac_age_scanner_process_node.index,
     894             :                              evt, 0);
     895        2102 : }
     896             : 
     897             : /**
     898             :     Flush all non static MACs from an interface
     899             : */
     900             : void
     901        2032 : l2fib_flush_int_mac (vlib_main_t * vm, u32 sw_if_index)
     902             : {
     903        2032 :   l2_input_seq_num_inc (sw_if_index);
     904        2032 :   l2fib_start_ager_scan (vm);
     905        2032 : }
     906             : 
     907             : /**
     908             :     Flush all non static MACs in a bridge domain
     909             : */
     910             : void
     911          61 : l2fib_flush_bd_mac (vlib_main_t * vm, u32 bd_index)
     912             : {
     913          61 :   l2_bridge_domain_t *bd_config = l2input_bd_config (bd_index);
     914          61 :   bd_config->seq_num += 1;
     915          61 :   l2fib_start_ager_scan (vm);
     916          61 : }
     917             : 
     918             : /**
     919             :     Flush all non static MACs - flushes all valid BDs
     920             : */
     921             : void
     922           9 : l2fib_flush_all_mac (vlib_main_t * vm)
     923             : {
     924             :   l2_bridge_domain_t *bd_config;
     925          36 :   vec_foreach (bd_config, l2input_main.bd_configs)
     926          27 :     if (bd_is_valid (bd_config))
     927          27 :     bd_config->seq_num += 1;
     928             : 
     929           9 :   l2fib_start_ager_scan (vm);
     930           9 : }
     931             : 
     932             : 
     933             : /**
     934             :     Flush MACs, except static ones, associated with an interface
     935             :     The CLI format is:
     936             :     l2fib flush-mac interface <if-name>
     937             : */
     938             : static clib_error_t *
     939           0 : l2fib_flush_mac_int (vlib_main_t * vm,
     940             :                      unformat_input_t * input, vlib_cli_command_t * cmd)
     941             : {
     942           0 :   vnet_main_t *vnm = vnet_get_main ();
     943           0 :   clib_error_t *error = 0;
     944             :   u32 sw_if_index;
     945             : 
     946           0 :   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
     947             :     {
     948           0 :       error = clib_error_return (0, "unknown interface `%U'",
     949             :                                  format_unformat_error, input);
     950           0 :       goto done;
     951             :     }
     952             : 
     953           0 :   l2fib_flush_int_mac (vm, sw_if_index);
     954             : 
     955           0 : done:
     956           0 :   return error;
     957             : }
     958             : 
     959             : /**
     960             :     Flush all MACs, except static ones
     961             :     The CLI format is:
     962             :     l2fib flush-mac all
     963             : */
     964             : static clib_error_t *
     965           0 : l2fib_flush_mac_all (vlib_main_t * vm,
     966             :                      unformat_input_t * input, vlib_cli_command_t * cmd)
     967             : {
     968           0 :   l2fib_flush_all_mac (vm);
     969           0 :   return 0;
     970             : }
     971             : 
     972             : /*?
     973             :  * This command kick off ager to delete all existing MAC Address entries,
     974             :  * except static ones, associated with an interface from the L2 FIB table.
     975             :  *
     976             :  * @cliexpar
     977             :  * Example of how to flush MAC Address entries learned on an interface from the L2 FIB table:
     978             :  * @cliexcmd{l2fib flush-mac interface GigabitEthernet2/1/0}
     979             : ?*/
     980             : /* *INDENT-OFF* */
     981      285289 : VLIB_CLI_COMMAND (l2fib_flush_mac_all_cli, static) = {
     982             :   .path = "l2fib flush-mac all",
     983             :   .short_help = "l2fib flush-mac all",
     984             :   .function = l2fib_flush_mac_all,
     985             : };
     986             : /* *INDENT-ON* */
     987             : 
     988             : /*?
     989             :  * This command kick off ager to delete all existing MAC Address entries,
     990             :  * except static ones, associated with an interface from the L2 FIB table.
     991             :  *
     992             :  * @cliexpar
     993             :  * Example of how to flush MAC Address entries learned on an interface from the L2 FIB table:
     994             :  * @cliexcmd{l2fib flush-mac interface GigabitEthernet2/1/0}
     995             : ?*/
     996             : /* *INDENT-OFF* */
     997      285289 : VLIB_CLI_COMMAND (l2fib_flush_mac_int_cli, static) = {
     998             :   .path = "l2fib flush-mac interface",
     999             :   .short_help = "l2fib flush-mac interface <if-name>",
    1000             :   .function = l2fib_flush_mac_int,
    1001             : };
    1002             : /* *INDENT-ON* */
    1003             : 
    1004             : /**
    1005             :     Flush bridge-domain MACs except static ones.
    1006             :     The CLI format is:
    1007             :     l2fib flush-mac bridge-domain <bd-id>
    1008             : */
    1009             : static clib_error_t *
    1010           0 : l2fib_flush_mac_bd (vlib_main_t * vm,
    1011             :                     unformat_input_t * input, vlib_cli_command_t * cmd)
    1012             : {
    1013           0 :   bd_main_t *bdm = &bd_main;
    1014           0 :   clib_error_t *error = 0;
    1015             :   u32 bd_index, bd_id;
    1016             :   uword *p;
    1017             : 
    1018           0 :   if (!unformat (input, "%d", &bd_id))
    1019             :     {
    1020           0 :       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
    1021             :                                  format_unformat_error, input);
    1022           0 :       goto done;
    1023             :     }
    1024             : 
    1025           0 :   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
    1026           0 :   if (p)
    1027           0 :     bd_index = *p;
    1028             :   else
    1029           0 :     return clib_error_return (0, "No such bridge domain %d", bd_id);
    1030             : 
    1031           0 :   l2fib_flush_bd_mac (vm, bd_index);
    1032             : 
    1033           0 : done:
    1034           0 :   return error;
    1035             : }
    1036             : 
    1037             : /*?
    1038             :  * This command kick off ager to delete all existing MAC Address entries,
    1039             :  * except static ones, in a bridge domain from the L2 FIB table.
    1040             :  *
    1041             :  * @cliexpar
    1042             :  * Example of how to flush MAC Address entries learned in a bridge domain from the L2 FIB table:
    1043             :  * @cliexcmd{l2fib flush-mac bridge-domain 1000}
    1044             : ?*/
    1045             : /* *INDENT-OFF* */
    1046      285289 : VLIB_CLI_COMMAND (l2fib_flush_mac_bd_cli, static) = {
    1047             :   .path = "l2fib flush-mac bridge-domain",
    1048             :   .short_help = "l2fib flush-mac bridge-domain <bd-id>",
    1049             :   .function = l2fib_flush_mac_bd,
    1050             : };
    1051             : /* *INDENT-ON* */
    1052             : 
    1053             : clib_error_t *
    1054       13514 : l2fib_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
    1055             : {
    1056       13514 :   l2_input_config_t *config = l2input_intf_config (sw_if_index);
    1057       20018 :   if ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0 &&
    1058        6504 :       l2_input_is_bridge (config))
    1059         895 :     l2fib_flush_int_mac (vnm->vlib_main, sw_if_index);
    1060       13514 :   return 0;
    1061             : }
    1062             : 
    1063        2881 : VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (l2fib_sw_interface_up_down);
    1064             : 
    1065        1150 : BVT (clib_bihash) * get_mac_table (void)
    1066             : {
    1067        1150 :   l2fib_main_t *mp = &l2fib_main;
    1068        1150 :   return &mp->mac_table;
    1069             : }
    1070             : 
    1071             : static_always_inline void *
    1072          50 : allocate_mac_evt_buf (u32 client, u32 client_index)
    1073             : {
    1074          50 :   l2fib_main_t *fm = &l2fib_main;
    1075          50 :   vl_api_l2_macs_event_t *mp = vl_msg_api_alloc
    1076          50 :     (sizeof (*mp) + (fm->max_macs_in_event * sizeof (vl_api_mac_entry_t)));
    1077          50 :   mp->_vl_msg_id = htons (l2input_main.msg_id_base + VL_API_L2_MACS_EVENT);
    1078          50 :   mp->pid = htonl (client);
    1079          50 :   mp->client_index = client_index;
    1080          50 :   return mp;
    1081             : }
    1082             : 
    1083             : static_always_inline f64
    1084         730 : l2fib_scan (vlib_main_t * vm, f64 start_time, u8 event_only)
    1085             : {
    1086         730 :   l2fib_main_t *fm = &l2fib_main;
    1087         730 :   l2learn_main_t *lm = &l2learn_main;
    1088             : 
    1089         730 :   BVT (clib_bihash) * h = &fm->mac_table;
    1090             :   int i, j, k;
    1091         730 :   f64 last_start = start_time;
    1092         730 :   f64 accum_t = 0;
    1093         730 :   f64 delta_t = 0;
    1094         730 :   u32 evt_idx = 0;
    1095         730 :   u32 learn_count = 0;
    1096         730 :   u32 client = lm->client_pid;
    1097         730 :   u32 cl_idx = lm->client_index;
    1098         730 :   vl_api_l2_macs_event_t *mp = 0;
    1099         730 :   vl_api_registration_t *reg = 0;
    1100             :   u32 bd_index;
    1101             :   static u32 *bd_learn_counts = 0;
    1102             : 
    1103             :   /* Don't scan the l2 fib if it hasn't been instantiated yet */
    1104         730 :   if (alloc_arena (h) == 0)
    1105           0 :     return 0.0;
    1106             : 
    1107         730 :   vec_reset_length (bd_learn_counts);
    1108         730 :   vec_validate (bd_learn_counts, vec_len (l2input_main.bd_configs) - 1);
    1109             : 
    1110         730 :   if (client)
    1111             :     {
    1112          43 :       mp = allocate_mac_evt_buf (client, cl_idx);
    1113          43 :       reg = vl_api_client_index_to_registration (lm->client_index);
    1114             :     }
    1115             : 
    1116   188778000 :   for (i = 0; i < h->nbuckets; i++)
    1117             :     {
    1118             :       /* allow no more than 20us without a pause */
    1119   188777000 :       delta_t = vlib_time_now (vm) - last_start;
    1120   188777000 :       if (delta_t > 20e-6)
    1121             :         {
    1122      507747 :           vlib_process_suspend (vm, 100e-6);    /* suspend for 100 us */
    1123             :           /* in case a new bd was created while sleeping */
    1124      507734 :           vec_validate (bd_learn_counts,
    1125             :                         vec_len (l2input_main.bd_configs) - 1);
    1126      507734 :           last_start = vlib_time_now (vm);
    1127      507734 :           accum_t += delta_t;
    1128             :         }
    1129             : 
    1130   188777000 :       if (i < (h->nbuckets - 3))
    1131             :         {
    1132             :           BVT (clib_bihash_bucket) * b =
    1133   188775000 :             BV (clib_bihash_get_bucket) (h, i + 3);
    1134   188775000 :           clib_prefetch_load (b);
    1135   188775000 :           b = BV (clib_bihash_get_bucket) (h, i + 1);
    1136   188775000 :           if (!BV (clib_bihash_bucket_is_empty) (b))
    1137             :             {
    1138             :               BVT (clib_bihash_value) * v =
    1139        6920 :                 BV (clib_bihash_get_value) (h, b->offset);
    1140        6920 :               clib_prefetch_load (v);
    1141             :             }
    1142             :         }
    1143             : 
    1144   188777000 :       BVT (clib_bihash_bucket) * b = BV (clib_bihash_get_bucket) (h, i);
    1145   188777000 :       if (BV (clib_bihash_bucket_is_empty) (b))
    1146   188770000 :         continue;
    1147        6920 :       BVT (clib_bihash_value) * v = BV (clib_bihash_get_value) (h, b->offset);
    1148       12847 :       for (j = 0; j < (1 << b->log2_pages); j++)
    1149             :         {
    1150       48409 :           for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
    1151             :             {
    1152       42482 :               if (BV (clib_bihash_is_free) (&v->kvp[k]))
    1153       41489 :                 continue;
    1154             : 
    1155        6920 :               l2fib_entry_key_t key = {.raw = v->kvp[k].key };
    1156        6920 :               l2fib_entry_result_t result = {.raw = v->kvp[k].value };
    1157             : 
    1158        6920 :               if (!l2fib_entry_result_is_set_AGE_NOT (&result))
    1159             :                 {
    1160        2330 :                   learn_count++;
    1161        2330 :                   vec_elt (bd_learn_counts, key.fields.bd_index)++;
    1162             :                 }
    1163             : 
    1164        6920 :               if (client)
    1165             :                 {
    1166        4371 :                   if (PREDICT_FALSE (evt_idx >= fm->max_macs_in_event))
    1167             :                     {
    1168             :                       /* event message full, send it and start a new one */
    1169           7 :                       if (reg && vl_api_can_send_msg (reg))
    1170             :                         {
    1171           7 :                           mp->n_macs = htonl (evt_idx);
    1172           7 :                           vl_api_send_msg (reg, (u8 *) mp);
    1173           7 :                           mp = allocate_mac_evt_buf (client, cl_idx);
    1174             :                         }
    1175             :                       else
    1176             :                         {
    1177           0 :                           if (reg)
    1178           0 :                             clib_warning ("MAC event to pid %d queue stuffed!"
    1179             :                                           " %d MAC entries lost", client,
    1180             :                                           evt_idx);
    1181             :                         }
    1182           7 :                       evt_idx = 0;
    1183             :                     }
    1184             : 
    1185        4371 :                   if (l2fib_entry_result_is_set_LRN_EVT (&result))
    1186             :                     {
    1187             :                       /* copy mac entry to event msg */
    1188         139 :                       clib_memcpy_fast (mp->mac[evt_idx].mac_addr,
    1189             :                                         key.fields.mac, 6);
    1190         139 :                       mp->mac[evt_idx].action =
    1191         139 :                         l2fib_entry_result_is_set_LRN_MOV (&result) ?
    1192             :                         (vl_api_mac_event_action_t) MAC_EVENT_ACTION_MOVE
    1193         139 :                         : (vl_api_mac_event_action_t) MAC_EVENT_ACTION_ADD;
    1194         139 :                       mp->mac[evt_idx].action =
    1195         139 :                         htonl (mp->mac[evt_idx].action);
    1196         139 :                       mp->mac[evt_idx].sw_if_index =
    1197         139 :                         htonl (result.fields.sw_if_index);
    1198             :                       /* clear event bits and update mac entry */
    1199         139 :                       l2fib_entry_result_clear_LRN_EVT (&result);
    1200         139 :                       l2fib_entry_result_clear_LRN_MOV (&result);
    1201             :                       BVT (clib_bihash_kv) kv;
    1202         139 :                       kv.key = key.raw;
    1203         139 :                       kv.value = result.raw;
    1204         139 :                       BV (clib_bihash_add_del) (&fm->mac_table, &kv, 1);
    1205         139 :                       evt_idx++;
    1206         139 :                       continue; /* skip aging */
    1207             :                     }
    1208             :                 }
    1209             : 
    1210        6781 :               if (event_only || l2fib_entry_result_is_set_AGE_NOT (&result))
    1211        5166 :                 continue;       /* skip aging - static_mac always age_not */
    1212             : 
    1213             :               /* start aging processing */
    1214        1615 :               u32 bd_index = key.fields.bd_index;
    1215        1615 :               u32 sw_if_index = result.fields.sw_if_index;
    1216        1615 :               u16 sn = l2fib_cur_seq_num (bd_index, sw_if_index);
    1217        1615 :               if (result.fields.sn != sn)
    1218         993 :                 goto age_out;   /* stale mac */
    1219             : 
    1220         622 :               l2_bridge_domain_t *bd_config =
    1221         622 :                 vec_elt_at_index (l2input_main.bd_configs, bd_index);
    1222             : 
    1223         622 :               if (bd_config->mac_age == 0)
    1224         622 :                 continue;       /* skip aging */
    1225             : 
    1226           0 :               i16 delta = (u8) (start_time / 60) - result.fields.timestamp;
    1227           0 :               delta += delta < 0 ? 256 : 0;
    1228             : 
    1229           0 :               if (delta < bd_config->mac_age)
    1230           0 :                 continue;       /* still valid */
    1231             : 
    1232           0 :             age_out:
    1233         993 :               if (client)
    1234             :                 {
    1235             :                   /* copy mac entry to event msg */
    1236          52 :                   clib_memcpy_fast (mp->mac[evt_idx].mac_addr, key.fields.mac,
    1237             :                                     6);
    1238          52 :                   mp->mac[evt_idx].action =
    1239             :                     (vl_api_mac_event_action_t) MAC_EVENT_ACTION_DELETE;
    1240          52 :                   mp->mac[evt_idx].action = htonl (mp->mac[evt_idx].action);
    1241          52 :                   mp->mac[evt_idx].sw_if_index =
    1242          52 :                     htonl (result.fields.sw_if_index);
    1243          52 :                   evt_idx++;
    1244             :                 }
    1245             :               /* delete mac entry */
    1246             :               BVT (clib_bihash_kv) kv;
    1247         993 :               kv.key = key.raw;
    1248         993 :               BV (clib_bihash_add_del) (&fm->mac_table, &kv, 0);
    1249         993 :               learn_count--;
    1250         993 :               vec_elt (bd_learn_counts, key.fields.bd_index)--;
    1251             :               /*
    1252             :                * Note: we may have just freed the bucket's backing
    1253             :                * storage, so check right here...
    1254             :                */
    1255         993 :               if (BV (clib_bihash_bucket_is_empty) (b))
    1256         993 :                 goto doublebreak;
    1257             :             }
    1258        5927 :           v++;
    1259             :         }
    1260   188777000 :     doublebreak:
    1261             :       ;
    1262             :     }
    1263             : 
    1264             :   /* keep learn count consistent */
    1265         717 :   l2learn_main.global_learn_count = learn_count;
    1266        2599 :   vec_foreach_index (bd_index, l2input_main.bd_configs)
    1267             :     {
    1268        1882 :       vec_elt (l2input_main.bd_configs, bd_index).learn_count =
    1269        3764 :         vec_elt (bd_learn_counts, bd_index);
    1270             :     }
    1271             : 
    1272         717 :   if (mp)
    1273             :     {
    1274             :       /*  send any outstanding mac event message else free message buffer */
    1275          41 :       if (evt_idx)
    1276             :         {
    1277          12 :           if (reg && vl_api_can_send_msg (reg))
    1278             :             {
    1279          12 :               mp->n_macs = htonl (evt_idx);
    1280          12 :               vl_api_send_msg (reg, (u8 *) mp);
    1281             :             }
    1282             :           else
    1283             :             {
    1284           0 :               if (reg)
    1285           0 :                 clib_warning ("MAC event to pid %d queue stuffed!"
    1286             :                               " %d MAC entries lost", client, evt_idx);
    1287           0 :               vl_msg_api_free (mp);
    1288             :             }
    1289             :         }
    1290             :       else
    1291          29 :         vl_msg_api_free (mp);
    1292             :     }
    1293         717 :   return delta_t + accum_t;
    1294             : }
    1295             : 
    1296             : static uword
    1297         575 : l2fib_mac_age_scanner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
    1298             :                                vlib_frame_t * f)
    1299             : {
    1300         575 :   uword event_type, *event_data = 0;
    1301         575 :   l2fib_main_t *fm = &l2fib_main;
    1302         575 :   l2learn_main_t *lm = &l2learn_main;
    1303         575 :   bool enabled = 0;
    1304         575 :   f64 start_time, next_age_scan_time = CLIB_TIME_MAX;
    1305             : 
    1306             :   while (1)
    1307         784 :     {
    1308        1359 :       if (lm->client_pid)
    1309          44 :         vlib_process_wait_for_event_or_clock (vm, fm->event_scan_delay);
    1310        1315 :       else if (enabled)
    1311             :         {
    1312           0 :           f64 t = next_age_scan_time - vlib_time_now (vm);
    1313           0 :           vlib_process_wait_for_event_or_clock (vm, t);
    1314             :         }
    1315             :       else
    1316        1315 :         vlib_process_wait_for_event (vm);
    1317             : 
    1318         797 :       event_type = vlib_process_get_events (vm, &event_data);
    1319         797 :       vec_reset_length (event_data);
    1320             : 
    1321         797 :       start_time = vlib_time_now (vm);
    1322             :       enum
    1323         797 :       { SCAN_MAC_AGE, SCAN_MAC_EVENT, SCAN_DISABLE } scan = SCAN_MAC_AGE;
    1324             : 
    1325         797 :       switch (event_type)
    1326             :         {
    1327          39 :         case ~0:                /* timer expired */
    1328          39 :           if (lm->client_pid != 0 && start_time < next_age_scan_time)
    1329          35 :             scan = SCAN_MAC_EVENT;
    1330          39 :           break;
    1331             : 
    1332           0 :         case L2_MAC_AGE_PROCESS_EVENT_START:
    1333           0 :           enabled = 1;
    1334           0 :           break;
    1335             : 
    1336          67 :         case L2_MAC_AGE_PROCESS_EVENT_STOP:
    1337          67 :           enabled = 0;
    1338          67 :           scan = SCAN_DISABLE;
    1339          67 :           break;
    1340             : 
    1341         691 :         case L2_MAC_AGE_PROCESS_EVENT_ONE_PASS:
    1342         691 :           break;
    1343             : 
    1344           0 :         default:
    1345           0 :           ASSERT (0);
    1346             :         }
    1347             : 
    1348         797 :       if (scan == SCAN_MAC_EVENT)
    1349          35 :         l2fib_main.evt_scan_duration = l2fib_scan (vm, start_time, 1);
    1350             :       else
    1351             :         {
    1352         762 :           if (scan == SCAN_MAC_AGE)
    1353         695 :             l2fib_main.age_scan_duration = l2fib_scan (vm, start_time, 0);
    1354         750 :           if (scan == SCAN_DISABLE)
    1355             :             {
    1356          67 :               l2fib_main.age_scan_duration = 0;
    1357          67 :               l2fib_main.evt_scan_duration = 0;
    1358             :             }
    1359             :           /* schedule next scan */
    1360         750 :           if (enabled)
    1361           0 :             next_age_scan_time = start_time + L2FIB_AGE_SCAN_INTERVAL;
    1362             :           else
    1363         750 :             next_age_scan_time = CLIB_TIME_MAX;
    1364             :         }
    1365             :     }
    1366             :   return 0;
    1367             : }
    1368             : 
    1369             : /* *INDENT-OFF* */
    1370      183788 : VLIB_REGISTER_NODE (l2fib_mac_age_scanner_process_node) = {
    1371             :     .function = l2fib_mac_age_scanner_process,
    1372             :     .type = VLIB_NODE_TYPE_PROCESS,
    1373             :     .name = "l2fib-mac-age-scanner-process",
    1374             : };
    1375             : /* *INDENT-ON* */
    1376             : 
    1377             : clib_error_t *
    1378         575 : l2fib_init (vlib_main_t * vm)
    1379             : {
    1380         575 :   l2fib_main_t *mp = &l2fib_main;
    1381             :   l2fib_entry_key_t test_key;
    1382             :   u8 test_mac[6];
    1383             : 
    1384         575 :   mp->vlib_main = vm;
    1385         575 :   mp->vnet_main = vnet_get_main ();
    1386         575 :   if (mp->mac_table_n_buckets == 0)
    1387         575 :     mp->mac_table_n_buckets = L2FIB_NUM_BUCKETS;
    1388         575 :   if (mp->mac_table_memory_size == 0)
    1389         575 :     mp->mac_table_memory_size = L2FIB_MEMORY_SIZE;
    1390         575 :   mp->mac_table_initialized = 0;
    1391             : 
    1392             :   /* verify the key constructor is good, since it is endian-sensitive */
    1393         575 :   clib_memset (test_mac, 0, sizeof (test_mac));
    1394         575 :   test_mac[0] = 0x11;
    1395         575 :   test_key.raw = 0;
    1396         575 :   test_key.raw = l2fib_make_key ((u8 *) & test_mac, 0x1234);
    1397         575 :   ASSERT (test_key.fields.mac[0] == 0x11);
    1398         575 :   ASSERT (test_key.fields.bd_index == 0x1234);
    1399             : 
    1400         575 :   return 0;
    1401             : }
    1402             : 
    1403       17855 : VLIB_INIT_FUNCTION (l2fib_init);
    1404             : 
    1405             : static clib_error_t *
    1406         575 : lfib_config (vlib_main_t * vm, unformat_input_t * input)
    1407             : {
    1408         575 :   l2fib_main_t *lm = &l2fib_main;
    1409         575 :   uword table_size = ~0;
    1410         575 :   u32 n_buckets = ~0;
    1411             : 
    1412         575 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1413             :     {
    1414           0 :       if (unformat (input, "table-size %U", unformat_memory_size,
    1415             :                     &table_size))
    1416             :         ;
    1417           0 :       else if (unformat (input, "num-buckets %u", &n_buckets))
    1418             :         ;
    1419             :       else
    1420           0 :         return clib_error_return (0, "unknown input `%U'",
    1421             :                                   format_unformat_error, input);
    1422             :     }
    1423             : 
    1424         575 :   if (n_buckets != ~0)
    1425             :     {
    1426           0 :       if (!is_pow2 (n_buckets))
    1427           0 :         return clib_error_return (0, "num-buckets must be power of 2");
    1428           0 :       lm->mac_table_n_buckets = n_buckets;
    1429             :     }
    1430             : 
    1431         575 :   if (table_size != ~0)
    1432           0 :     lm->mac_table_memory_size = table_size;
    1433         575 :   return 0;
    1434             : }
    1435             : 
    1436        7514 : VLIB_CONFIG_FUNCTION (lfib_config, "l2fib");
    1437             : 
    1438             : /*
    1439             :  * fd.io coding-style-patch-verification: ON
    1440             :  *
    1441             :  * Local Variables:
    1442             :  * eval: (c-set-style "gnu")
    1443             :  * End:
    1444             :  */

Generated by: LCOV version 1.14