LCOV - code coverage report
Current view: top level - vnet/l2 - l2_bd.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 321 631 50.9 %
Date: 2023-10-26 01:39:38 Functions: 47 59 79.7 %

          Line data    Source code
       1             : /*
       2             :  * l2_bd.c : layer 2 bridge domain
       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             : #include <vlib/vlib.h>
      19             : #include <vnet/vnet.h>
      20             : #include <vlib/cli.h>
      21             : #include <vnet/ethernet/ethernet.h>
      22             : #include <vnet/ip/format.h>
      23             : #include <vnet/l2/l2_input.h>
      24             : #include <vnet/l2/feat_bitmap.h>
      25             : #include <vnet/l2/l2_bd.h>
      26             : #include <vnet/l2/l2_learn.h>
      27             : #include <vnet/l2/l2_fib.h>
      28             : #include <vnet/l2/l2_vtr.h>
      29             : #include <vnet/ip/ip4_packet.h>
      30             : #include <vnet/ip/ip6_packet.h>
      31             : 
      32             : #include <vppinfra/error.h>
      33             : #include <vppinfra/hash.h>
      34             : #include <vppinfra/vec.h>
      35             : 
      36             : /**
      37             :  * @file
      38             :  * @brief Ethernet Bridge Domain.
      39             :  *
      40             :  * Code in this file manages Layer 2 bridge domains.
      41             :  *
      42             :  */
      43             : 
      44             : bd_main_t bd_main;
      45             : 
      46             : /**
      47             :   Init bridge domain if not done already.
      48             :   For feature bitmap, set all bits except ARP termination
      49             : */
      50             : void
      51        1461 : bd_validate (l2_bridge_domain_t * bd_config)
      52             : {
      53        1461 :   if (bd_is_valid (bd_config))
      54        1365 :     return;
      55          96 :   bd_config->feature_bitmap =
      56             :     ~(L2INPUT_FEAT_ARP_TERM | L2INPUT_FEAT_UU_FWD | L2INPUT_FEAT_ARP_UFWD);
      57          96 :   bd_config->bvi_sw_if_index = ~0;
      58          96 :   bd_config->uu_fwd_sw_if_index = ~0;
      59          96 :   bd_config->members = 0;
      60          96 :   bd_config->flood_count = 0;
      61          96 :   bd_config->tun_master_count = 0;
      62          96 :   bd_config->tun_normal_count = 0;
      63          96 :   bd_config->no_flood_count = 0;
      64          96 :   bd_config->mac_by_ip4 = 0;
      65          96 :   bd_config->mac_by_ip6 = hash_create_mem (0, sizeof (ip6_address_t),
      66             :                                            sizeof (uword));
      67             : }
      68             : 
      69             : u32
      70        1893 : bd_find_index (bd_main_t * bdm, u32 bd_id)
      71             : {
      72        1893 :   u32 *p = (u32 *) hash_get (bdm->bd_index_by_bd_id, bd_id);
      73        1893 :   if (!p)
      74         144 :     return ~0;
      75        1749 :   return p[0];
      76             : }
      77             : 
      78             : u32
      79         671 : bd_add_bd_index (bd_main_t * bdm, u32 bd_id)
      80             : {
      81         671 :   ASSERT (!hash_get (bdm->bd_index_by_bd_id, bd_id));
      82         671 :   u32 rv = clib_bitmap_first_clear (bdm->bd_index_bitmap);
      83             : 
      84             :   /* mark this index taken */
      85         671 :   bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, rv, 1);
      86             : 
      87         671 :   hash_set (bdm->bd_index_by_bd_id, bd_id, rv);
      88             : 
      89         671 :   vec_validate (l2input_main.bd_configs, rv);
      90         671 :   l2input_main.bd_configs[rv].bd_id = bd_id;
      91         671 :   l2input_main.bd_configs[rv].learn_limit =
      92         671 :     l2learn_main.bd_default_learn_limit;
      93         671 :   l2input_main.bd_configs[rv].learn_count = 0;
      94             : 
      95         671 :   return rv;
      96             : }
      97             : 
      98             : static inline void
      99          60 : bd_free_ip_mac_tables (l2_bridge_domain_t * bd)
     100             : {
     101             :   u64 mac_addr;
     102             :   ip6_address_t *ip6_addr_key;
     103             : 
     104          60 :   hash_free (bd->mac_by_ip4);
     105             :   /* *INDENT-OFF* */
     106        3925 :   hash_foreach_mem (ip6_addr_key, mac_addr, bd->mac_by_ip6,
     107             :   ({
     108             :     clib_mem_free (ip6_addr_key); /* free memory used for ip6 addr key */
     109             :   }));
     110             :   /* *INDENT-ON* */
     111          60 :   hash_free (bd->mac_by_ip6);
     112          60 : }
     113             : 
     114             : static int
     115          60 : bd_delete (bd_main_t * bdm, u32 bd_index)
     116             : {
     117          60 :   l2_bridge_domain_t *bd = &l2input_main.bd_configs[bd_index];
     118          60 :   u32 bd_id = bd->bd_id;
     119             : 
     120             :   /* flush non-static MACs in BD and removed bd_id from hash table */
     121          60 :   l2fib_flush_bd_mac (vlib_get_main (), bd_index);
     122          60 :   hash_unset (bdm->bd_index_by_bd_id, bd_id);
     123             : 
     124             :   /* mark this index clear */
     125          60 :   bdm->bd_index_bitmap = clib_bitmap_set (bdm->bd_index_bitmap, bd_index, 0);
     126             : 
     127             :   /* clear BD config for reuse: bd_id to -1 and clear feature_bitmap */
     128          60 :   bd->bd_id = ~0;
     129          60 :   bd->feature_bitmap = 0;
     130          60 :   bd->learn_limit = 0;
     131          60 :   bd->learn_count = ~0;
     132             : 
     133             :   /* free BD tag */
     134          60 :   vec_free (bd->bd_tag);
     135             : 
     136             :   /* free memory used by BD */
     137          60 :   vec_free (bd->members);
     138          60 :   bd_free_ip_mac_tables (bd);
     139             : 
     140          60 :   return 0;
     141             : }
     142             : 
     143             : static void
     144        2388 : update_flood_count (l2_bridge_domain_t * bd_config)
     145             : {
     146        2388 :   bd_config->flood_count = (vec_len (bd_config->members) -
     147        2388 :                             (bd_config->tun_master_count ?
     148        2388 :                              bd_config->tun_normal_count : 0));
     149        2388 :   bd_config->flood_count -= bd_config->no_flood_count;
     150        2388 : }
     151             : 
     152             : void
     153        1254 : bd_add_member (l2_bridge_domain_t * bd_config, l2_flood_member_t * member)
     154             : {
     155        1254 :   u32 ix = 0;
     156        1254 :   vnet_sw_interface_t *sw_if = vnet_get_sw_interface
     157             :     (vnet_get_main (), member->sw_if_index);
     158             : 
     159             :   /*
     160             :    * Add one element to the vector
     161             :    * vector is ordered [ bvi, normal/tun_masters..., tun_normals... no_flood]
     162             :    * When flooding, the bvi interface (if present) must be the last member
     163             :    * processed due to how BVI processing can change the packet. To enable
     164             :    * this order, we make the bvi interface the first in the vector and
     165             :    * flooding walks the vector in reverse. The flood-count determines where
     166             :    * in the member list to start the walk from.
     167             :    */
     168        1254 :   switch (sw_if->flood_class)
     169             :     {
     170           0 :     case VNET_FLOOD_CLASS_NO_FLOOD:
     171           0 :       bd_config->no_flood_count++;
     172           0 :       ix = vec_len (bd_config->members);
     173           0 :       break;
     174          11 :     case VNET_FLOOD_CLASS_BVI:
     175          11 :       ix = 0;
     176          11 :       break;
     177          24 :     case VNET_FLOOD_CLASS_TUNNEL_MASTER:
     178          24 :       bd_config->tun_master_count++;
     179             :       /* Fall through */
     180         734 :     case VNET_FLOOD_CLASS_NORMAL:
     181         734 :       ix = (vec_len (bd_config->members) -
     182         734 :             bd_config->tun_normal_count - bd_config->no_flood_count);
     183         734 :       break;
     184         509 :     case VNET_FLOOD_CLASS_TUNNEL_NORMAL:
     185         509 :       ix = (vec_len (bd_config->members) - bd_config->no_flood_count);
     186         509 :       bd_config->tun_normal_count++;
     187         509 :       break;
     188             :     }
     189             : 
     190        1254 :   vec_insert_elts (bd_config->members, member, 1, ix);
     191        1254 :   update_flood_count (bd_config);
     192        1254 : }
     193             : 
     194             : #define BD_REMOVE_ERROR_OK        0
     195             : #define BD_REMOVE_ERROR_NOT_FOUND 1
     196             : 
     197             : u32
     198        1136 : bd_remove_member (l2_bridge_domain_t * bd_config, u32 sw_if_index)
     199             : {
     200             :   u32 ix;
     201             : 
     202             :   /* Find and delete the member */
     203        3752 :   vec_foreach_index (ix, bd_config->members)
     204             :   {
     205        3750 :     l2_flood_member_t *m = vec_elt_at_index (bd_config->members, ix);
     206        3750 :     if (m->sw_if_index == sw_if_index)
     207             :       {
     208        1134 :         vnet_sw_interface_t *sw_if = vnet_get_sw_interface
     209             :           (vnet_get_main (), sw_if_index);
     210             : 
     211        1134 :         if (sw_if->flood_class != VNET_FLOOD_CLASS_NORMAL)
     212             :           {
     213         491 :             if (sw_if->flood_class == VNET_FLOOD_CLASS_TUNNEL_MASTER)
     214          22 :               bd_config->tun_master_count--;
     215         469 :             else if (sw_if->flood_class == VNET_FLOOD_CLASS_TUNNEL_NORMAL)
     216         467 :               bd_config->tun_normal_count--;
     217           2 :             else if (sw_if->flood_class == VNET_FLOOD_CLASS_NO_FLOOD)
     218           0 :               bd_config->no_flood_count--;
     219             :           }
     220        1134 :         vec_delete (bd_config->members, 1, ix);
     221        1134 :         update_flood_count (bd_config);
     222             : 
     223        1134 :         return BD_REMOVE_ERROR_OK;
     224             :       }
     225             :   }
     226             : 
     227           2 :   return BD_REMOVE_ERROR_NOT_FOUND;
     228             : }
     229             : 
     230             : 
     231             : clib_error_t *
     232         575 : l2bd_init (vlib_main_t * vm)
     233             : {
     234         575 :   bd_main_t *bdm = &bd_main;
     235         575 :   bdm->bd_index_by_bd_id = hash_create (0, sizeof (uword));
     236             :   /*
     237             :    * create a placeholder bd with bd_id of 0 and bd_index of 0 with feature set
     238             :    * to packet drop only. Thus, packets received from any L2 interface with
     239             :    * uninitialized bd_index of 0 can be dropped safely.
     240             :    */
     241         575 :   u32 bd_index = bd_add_bd_index (bdm, 0);
     242         575 :   ASSERT (bd_index == 0);
     243         575 :   l2input_main.bd_configs[0].feature_bitmap = L2INPUT_FEAT_DROP;
     244             : 
     245         575 :   bdm->vlib_main = vm;
     246         575 :   return 0;
     247             : }
     248             : 
     249       15551 : VLIB_INIT_FUNCTION (l2bd_init);
     250             : 
     251             : l2_bridge_domain_t *
     252       12827 : bd_get (u32 bd_index)
     253             : {
     254       12827 :   if (bd_index < vec_len (l2input_main.bd_configs))
     255       12827 :     return (vec_elt_at_index (l2input_main.bd_configs, bd_index));
     256           0 :   return (NULL);
     257             : }
     258             : 
     259             : u32
     260        2712 : bd_input_walk (u32 bd_index, bd_input_walk_fn_t fn, void *data)
     261             : {
     262             :   l2_flood_member_t *member;
     263             :   l2_bridge_domain_t *bd;
     264             :   u32 sw_if_index;
     265             : 
     266        2712 :   sw_if_index = ~0;
     267        2712 :   bd = bd_get (bd_index);
     268             : 
     269        2712 :   ASSERT (bd);
     270             : 
     271       11691 :   vec_foreach (member, bd->members)
     272             :   {
     273        8979 :     if (WALK_STOP == fn (bd_index, member->sw_if_index))
     274             :       {
     275           0 :         sw_if_index = member->sw_if_index;
     276           0 :         break;
     277             :       }
     278             :   }
     279             : 
     280        2712 :   return (sw_if_index);
     281             : }
     282             : 
     283             : static void
     284         272 : b2_input_recache (u32 bd_index)
     285             : {
     286         272 :   bd_input_walk (bd_index, l2input_recache, NULL);
     287         272 : }
     288             : 
     289             : /**
     290             :     Set the learn/forward/flood flags for the bridge domain.
     291             :     Return 0 if ok, non-zero if for an error.
     292             : */
     293             : u32
     294         205 : bd_set_flags (vlib_main_t * vm, u32 bd_index, bd_flags_t flags, u32 enable)
     295             : {
     296             : 
     297         205 :   l2_bridge_domain_t *bd_config = l2input_bd_config (bd_index);
     298         205 :   bd_validate (bd_config);
     299         205 :   u32 feature_bitmap = 0;
     300             : 
     301         205 :   if (flags & L2_LEARN)
     302             :     {
     303         107 :       feature_bitmap |= L2INPUT_FEAT_LEARN;
     304             :     }
     305         205 :   if (flags & L2_FWD)
     306             :     {
     307          69 :       feature_bitmap |= L2INPUT_FEAT_FWD;
     308             :     }
     309         205 :   if (flags & L2_FLOOD)
     310             :     {
     311          77 :       feature_bitmap |= L2INPUT_FEAT_FLOOD;
     312             :     }
     313         205 :   if (flags & L2_UU_FLOOD)
     314             :     {
     315          78 :       feature_bitmap |= L2INPUT_FEAT_UU_FLOOD;
     316             :     }
     317         205 :   if (flags & L2_ARP_TERM)
     318             :     {
     319          75 :       feature_bitmap |= L2INPUT_FEAT_ARP_TERM;
     320             :     }
     321         205 :   if (flags & L2_ARP_UFWD)
     322             :     {
     323          67 :       feature_bitmap |= L2INPUT_FEAT_ARP_UFWD;
     324             :     }
     325             : 
     326         205 :   if (enable)
     327             :     {
     328          91 :       bd_config->feature_bitmap |= feature_bitmap;
     329             :     }
     330             :   else
     331             :     {
     332         114 :       bd_config->feature_bitmap &= ~feature_bitmap;
     333             :     }
     334             : 
     335         205 :   b2_input_recache (bd_index);
     336             : 
     337         205 :   return bd_config->feature_bitmap;
     338             : }
     339             : 
     340             : /**
     341             :     Set the mac age for the bridge domain.
     342             : */
     343             : void
     344          67 : bd_set_mac_age (vlib_main_t * vm, u32 bd_index, u8 age)
     345             : {
     346             :   l2_bridge_domain_t *bd_config;
     347          67 :   int enable = 0;
     348             : 
     349          67 :   vec_validate (l2input_main.bd_configs, bd_index);
     350          67 :   bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index);
     351          67 :   bd_config->mac_age = age;
     352          67 :   b2_input_recache (bd_index);
     353             : 
     354             :   /* check if there is at least one bd with mac aging enabled */
     355         285 :   vec_foreach (bd_config, l2input_main.bd_configs)
     356         218 :     enable |= bd_config->bd_id != ~0 && bd_config->mac_age != 0;
     357             : 
     358          67 :   vlib_process_signal_event (vm, l2fib_mac_age_scanner_process_node.index,
     359             :                              enable ? L2_MAC_AGE_PROCESS_EVENT_START :
     360             :                              L2_MAC_AGE_PROCESS_EVENT_STOP, 0);
     361          67 : }
     362             : 
     363             : /**
     364             :     Set learn limit for the bridge domain.
     365             : */
     366             : void
     367          68 : bd_set_learn_limit (vlib_main_t *vm, u32 bd_index, u32 learn_limit)
     368             : {
     369             :   l2_bridge_domain_t *bd_config;
     370          68 :   vec_validate (l2input_main.bd_configs, bd_index);
     371          68 :   bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index);
     372          68 :   bd_config->learn_limit = learn_limit;
     373          68 : }
     374             : 
     375             : /**
     376             :     Set the tag for the bridge domain.
     377             : */
     378             : static void
     379          67 : bd_set_bd_tag (vlib_main_t * vm, u32 bd_index, u8 * bd_tag)
     380             : {
     381             :   u8 *old;
     382             :   l2_bridge_domain_t *bd_config;
     383          67 :   vec_validate (l2input_main.bd_configs, bd_index);
     384          67 :   bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index);
     385             : 
     386          67 :   old = bd_config->bd_tag;
     387             : 
     388          67 :   if (bd_tag[0])
     389             :     {
     390           0 :       bd_config->bd_tag = format (0, "%s%c", bd_tag, 0);
     391             :     }
     392             :   else
     393             :     {
     394          67 :       bd_config->bd_tag = NULL;
     395             :     }
     396             : 
     397          67 :   vec_free (old);
     398          67 : }
     399             : 
     400             : /**
     401             :    Set bridge-domain learn enable/disable.
     402             :    The CLI format is:
     403             :    set bridge-domain learn <bd_id> [disable]
     404             : */
     405             : static clib_error_t *
     406           0 : bd_learn (vlib_main_t * vm,
     407             :           unformat_input_t * input, vlib_cli_command_t * cmd)
     408             : {
     409           0 :   bd_main_t *bdm = &bd_main;
     410           0 :   clib_error_t *error = 0;
     411             :   u32 bd_index, bd_id;
     412             :   u32 enable;
     413             :   uword *p;
     414             : 
     415           0 :   if (!unformat (input, "%d", &bd_id))
     416             :     {
     417           0 :       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
     418             :                                  format_unformat_error, input);
     419           0 :       goto done;
     420             :     }
     421             : 
     422           0 :   if (bd_id == 0)
     423           0 :     return clib_error_return (0,
     424             :                               "No operations on the default bridge domain are supported");
     425             : 
     426           0 :   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
     427             : 
     428           0 :   if (p == 0)
     429           0 :     return clib_error_return (0, "No such bridge domain %d", bd_id);
     430             : 
     431           0 :   bd_index = p[0];
     432             : 
     433           0 :   enable = 1;
     434           0 :   if (unformat (input, "disable"))
     435             :     {
     436           0 :       enable = 0;
     437             :     }
     438             : 
     439             :   /* set the bridge domain flag */
     440           0 :   bd_set_flags (vm, bd_index, L2_LEARN, enable);
     441             : 
     442           0 : done:
     443           0 :   return error;
     444             : }
     445             : 
     446             : /*?
     447             :  * Layer 2 learning can be enabled and disabled on each
     448             :  * interface and on each bridge-domain. Use this command to
     449             :  * manage bridge-domains. It is enabled by default.
     450             :  *
     451             :  * @cliexpar
     452             :  * Example of how to enable learning (where 200 is the bridge-domain-id):
     453             :  * @cliexcmd{set bridge-domain learn 200}
     454             :  * Example of how to disable learning (where 200 is the bridge-domain-id):
     455             :  * @cliexcmd{set bridge-domain learn 200 disable}
     456             : ?*/
     457             : /* *INDENT-OFF* */
     458      285289 : VLIB_CLI_COMMAND (bd_learn_cli, static) = {
     459             :   .path = "set bridge-domain learn",
     460             :   .short_help = "set bridge-domain learn <bridge-domain-id> [disable]",
     461             :   .function = bd_learn,
     462             : };
     463             : /* *INDENT-ON* */
     464             : 
     465             : static clib_error_t *
     466           0 : bd_default_learn_limit (vlib_main_t *vm, unformat_input_t *input,
     467             :                         vlib_cli_command_t *cmd)
     468             : {
     469           0 :   l2learn_main_t *l2m = &l2learn_main;
     470           0 :   clib_error_t *error = 0;
     471             :   u32 learn_limit;
     472             : 
     473           0 :   if (!unformat (input, "%d", &learn_limit))
     474             :     {
     475           0 :       error = clib_error_return (
     476             :         0, "expecting per bridge-domain max entry number got`%U'",
     477             :         format_unformat_error, input);
     478           0 :       goto done;
     479             :     }
     480             : 
     481           0 :   l2m->bd_default_learn_limit = learn_limit;
     482             : 
     483           0 : done:
     484           0 :   return error;
     485             : }
     486             : 
     487      285289 : VLIB_CLI_COMMAND (bd_default_learn_limit_cli, static) = {
     488             :   .path = "set bridge-domain default-learn-limit",
     489             :   .short_help = "set bridge-domain default-learn-limit <maxentries>",
     490             :   .function = bd_default_learn_limit,
     491             : };
     492             : 
     493             : /**
     494             :     Set bridge-domain forward enable/disable.
     495             :     The CLI format is:
     496             :     set bridge-domain forward <bd_index> [disable]
     497             : */
     498             : static clib_error_t *
     499           0 : bd_fwd (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
     500             : {
     501           0 :   bd_main_t *bdm = &bd_main;
     502           0 :   clib_error_t *error = 0;
     503             :   u32 bd_index, bd_id;
     504             :   u32 enable;
     505             :   uword *p;
     506             : 
     507           0 :   if (!unformat (input, "%d", &bd_id))
     508             :     {
     509           0 :       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
     510             :                                  format_unformat_error, input);
     511           0 :       goto done;
     512             :     }
     513             : 
     514           0 :   if (bd_id == 0)
     515           0 :     return clib_error_return (0,
     516             :                               "No operations on the default bridge domain are supported");
     517             : 
     518           0 :   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
     519             : 
     520           0 :   if (p == 0)
     521           0 :     return clib_error_return (0, "No such bridge domain %d", bd_id);
     522             : 
     523           0 :   bd_index = p[0];
     524             : 
     525           0 :   enable = 1;
     526           0 :   if (unformat (input, "disable"))
     527             :     {
     528           0 :       enable = 0;
     529             :     }
     530             : 
     531             :   /* set the bridge domain flag */
     532           0 :   bd_set_flags (vm, bd_index, L2_FWD, enable);
     533             : 
     534           0 : done:
     535           0 :   return error;
     536             : }
     537             : 
     538             : 
     539             : /*?
     540             :  * Layer 2 unicast forwarding can be enabled and disabled on each
     541             :  * interface and on each bridge-domain. Use this command to
     542             :  * manage bridge-domains. It is enabled by default.
     543             :  *
     544             :  * @cliexpar
     545             :  * Example of how to enable forwarding (where 200 is the bridge-domain-id):
     546             :  * @cliexcmd{set bridge-domain forward 200}
     547             :  * Example of how to disable forwarding (where 200 is the bridge-domain-id):
     548             :  * @cliexcmd{set bridge-domain forward 200 disable}
     549             : ?*/
     550             : /* *INDENT-OFF* */
     551      285289 : VLIB_CLI_COMMAND (bd_fwd_cli, static) = {
     552             :   .path = "set bridge-domain forward",
     553             :   .short_help = "set bridge-domain forward <bridge-domain-id> [disable]",
     554             :   .function = bd_fwd,
     555             : };
     556             : /* *INDENT-ON* */
     557             : 
     558             : /**
     559             :     Set bridge-domain flood enable/disable.
     560             :     The CLI format is:
     561             :     set bridge-domain flood <bd_index> [disable]
     562             : */
     563             : static clib_error_t *
     564           0 : bd_flood (vlib_main_t * vm,
     565             :           unformat_input_t * input, vlib_cli_command_t * cmd)
     566             : {
     567           0 :   bd_main_t *bdm = &bd_main;
     568           0 :   clib_error_t *error = 0;
     569             :   u32 bd_index, bd_id;
     570             :   u32 enable;
     571             :   uword *p;
     572             : 
     573           0 :   if (!unformat (input, "%d", &bd_id))
     574             :     {
     575           0 :       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
     576             :                                  format_unformat_error, input);
     577           0 :       goto done;
     578             :     }
     579             : 
     580           0 :   if (bd_id == 0)
     581           0 :     return clib_error_return (0,
     582             :                               "No operations on the default bridge domain are supported");
     583             : 
     584           0 :   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
     585             : 
     586           0 :   if (p == 0)
     587           0 :     return clib_error_return (0, "No such bridge domain %d", bd_id);
     588             : 
     589           0 :   bd_index = p[0];
     590             : 
     591           0 :   enable = 1;
     592           0 :   if (unformat (input, "disable"))
     593             :     {
     594           0 :       enable = 0;
     595             :     }
     596             : 
     597             :   /* set the bridge domain flag */
     598           0 :   bd_set_flags (vm, bd_index, L2_FLOOD, enable);
     599             : 
     600           0 : done:
     601           0 :   return error;
     602             : }
     603             : 
     604             : /*?
     605             :  * Layer 2 flooding can be enabled and disabled on each
     606             :  * interface and on each bridge-domain. Use this command to
     607             :  * manage bridge-domains. It is enabled by default.
     608             :  *
     609             :  * @cliexpar
     610             :  * Example of how to enable flooding (where 200 is the bridge-domain-id):
     611             :  * @cliexcmd{set bridge-domain flood 200}
     612             :  * Example of how to disable flooding (where 200 is the bridge-domain-id):
     613             :  * @cliexcmd{set bridge-domain flood 200 disable}
     614             : ?*/
     615             : /* *INDENT-OFF* */
     616      285289 : VLIB_CLI_COMMAND (bd_flood_cli, static) = {
     617             :   .path = "set bridge-domain flood",
     618             :   .short_help = "set bridge-domain flood <bridge-domain-id> [disable]",
     619             :   .function = bd_flood,
     620             : };
     621             : /* *INDENT-ON* */
     622             : 
     623             : /**
     624             :     Set bridge-domain unknown-unicast flood enable/disable.
     625             :     The CLI format is:
     626             :     set bridge-domain uu-flood <bd_index> [disable]
     627             : */
     628             : static clib_error_t *
     629           0 : bd_uu_flood (vlib_main_t * vm,
     630             :              unformat_input_t * input, vlib_cli_command_t * cmd)
     631             : {
     632           0 :   bd_main_t *bdm = &bd_main;
     633           0 :   clib_error_t *error = 0;
     634             :   u32 bd_index, bd_id;
     635             :   u32 enable;
     636             :   uword *p;
     637             : 
     638           0 :   if (!unformat (input, "%d", &bd_id))
     639             :     {
     640           0 :       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
     641             :                                  format_unformat_error, input);
     642           0 :       goto done;
     643             :     }
     644             : 
     645           0 :   if (bd_id == 0)
     646           0 :     return clib_error_return (0,
     647             :                               "No operations on the default bridge domain are supported");
     648             : 
     649           0 :   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
     650             : 
     651           0 :   if (p == 0)
     652           0 :     return clib_error_return (0, "No such bridge domain %d", bd_id);
     653             : 
     654           0 :   bd_index = p[0];
     655             : 
     656           0 :   enable = 1;
     657           0 :   if (unformat (input, "disable"))
     658             :     {
     659           0 :       enable = 0;
     660             :     }
     661             : 
     662             :   /* set the bridge domain flag */
     663           0 :   bd_set_flags (vm, bd_index, L2_UU_FLOOD, enable);
     664             : 
     665           0 : done:
     666           0 :   return error;
     667             : }
     668             : 
     669             : /*?
     670             :  * Layer 2 unknown-unicast flooding can be enabled and disabled on each
     671             :  * bridge-domain. It is enabled by default.
     672             :  *
     673             :  * @cliexpar
     674             :  * Example of how to enable unknown-unicast flooding (where 200 is the
     675             :  * bridge-domain-id):
     676             :  * @cliexcmd{set bridge-domain uu-flood 200}
     677             :  * Example of how to disable unknown-unicast flooding (where 200 is the bridge-domain-id):
     678             :  * @cliexcmd{set bridge-domain uu-flood 200 disable}
     679             : ?*/
     680             : /* *INDENT-OFF* */
     681      285289 : VLIB_CLI_COMMAND (bd_uu_flood_cli, static) = {
     682             :   .path = "set bridge-domain uu-flood",
     683             :   .short_help = "set bridge-domain uu-flood <bridge-domain-id> [disable]",
     684             :   .function = bd_uu_flood,
     685             : };
     686             : /* *INDENT-ON* */
     687             : 
     688             : /**
     689             :     Set bridge-domain arp-unicast forward enable/disable.
     690             :     The CLI format is:
     691             :     set bridge-domain arp-ufwd <bd_index> [disable]
     692             : */
     693             : static clib_error_t *
     694           0 : bd_arp_ufwd (vlib_main_t * vm,
     695             :              unformat_input_t * input, vlib_cli_command_t * cmd)
     696             : {
     697           0 :   bd_main_t *bdm = &bd_main;
     698           0 :   clib_error_t *error = 0;
     699             :   u32 bd_index, bd_id;
     700             :   u32 enable;
     701             :   uword *p;
     702             : 
     703           0 :   if (!unformat (input, "%d", &bd_id))
     704             :     {
     705           0 :       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
     706             :                                  format_unformat_error, input);
     707           0 :       goto done;
     708             :     }
     709             : 
     710           0 :   if (bd_id == 0)
     711           0 :     return clib_error_return (0,
     712             :                               "No operations on the default bridge domain are supported");
     713             : 
     714           0 :   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
     715             : 
     716           0 :   if (p == 0)
     717           0 :     return clib_error_return (0, "No such bridge domain %d", bd_id);
     718             : 
     719           0 :   bd_index = p[0];
     720             : 
     721           0 :   enable = 1;
     722           0 :   if (unformat (input, "disable"))
     723             :     {
     724           0 :       enable = 0;
     725             :     }
     726             : 
     727             :   /* set the bridge domain flag */
     728           0 :   bd_set_flags (vm, bd_index, L2_ARP_UFWD, enable);
     729             : 
     730           0 : done:
     731           0 :   return error;
     732             : }
     733             : 
     734             : /*?
     735             :  * Layer 2 arp-unicast forwarding can be enabled and disabled on each
     736             :  * bridge-domain. It is disabled by default.
     737             :  *
     738             :  * @cliexpar
     739             :  * Example of how to enable arp-unicast forwarding (where 200 is the
     740             :  * bridge-domain-id):
     741             :  * @cliexcmd{set bridge-domain arp-ufwd 200}
     742             :  * Example of how to disable arp-unicast forwarding (where 200 is the bridge-domain-id):
     743             :  * @cliexcmd{set bridge-domain arp-ufwd 200 disable}
     744             : ?*/
     745             : /* *INDENT-OFF* */
     746      285289 : VLIB_CLI_COMMAND (bd_arp_ufwd_cli, static) = {
     747             :   .path = "set bridge-domain arp-ufwd",
     748             :   .short_help = "set bridge-domain arp-ufwd <bridge-domain-id> [disable]",
     749             :   .function = bd_arp_ufwd,
     750             : };
     751             : /* *INDENT-ON* */
     752             : 
     753             : /**
     754             :     Set bridge-domain arp term enable/disable.
     755             :     The CLI format is:
     756             :     set bridge-domain arp term <bridge-domain-id> [disable]
     757             : */
     758             : static clib_error_t *
     759           0 : bd_arp_term (vlib_main_t * vm,
     760             :              unformat_input_t * input, vlib_cli_command_t * cmd)
     761             : {
     762           0 :   bd_main_t *bdm = &bd_main;
     763           0 :   clib_error_t *error = 0;
     764             :   u32 bd_index, bd_id;
     765             :   u32 enable;
     766             :   uword *p;
     767             : 
     768           0 :   if (!unformat (input, "%d", &bd_id))
     769             :     {
     770           0 :       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
     771             :                                  format_unformat_error, input);
     772           0 :       goto done;
     773             :     }
     774             : 
     775           0 :   if (bd_id == 0)
     776           0 :     return clib_error_return (0,
     777             :                               "No operations on the default bridge domain are supported");
     778             : 
     779           0 :   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
     780           0 :   if (p)
     781           0 :     bd_index = *p;
     782             :   else
     783           0 :     return clib_error_return (0, "No such bridge domain %d", bd_id);
     784             : 
     785           0 :   enable = 1;
     786           0 :   if (unformat (input, "disable"))
     787           0 :     enable = 0;
     788             : 
     789             :   /* set the bridge domain flag */
     790           0 :   bd_set_flags (vm, bd_index, L2_ARP_TERM, enable);
     791             : 
     792           0 : done:
     793           0 :   return error;
     794             : }
     795             : 
     796             : static clib_error_t *
     797           0 : bd_mac_age (vlib_main_t * vm,
     798             :             unformat_input_t * input, vlib_cli_command_t * cmd)
     799             : {
     800           0 :   bd_main_t *bdm = &bd_main;
     801           0 :   clib_error_t *error = 0;
     802             :   u32 bd_index, bd_id;
     803             :   u32 age;
     804             :   uword *p;
     805             : 
     806           0 :   if (!unformat (input, "%d", &bd_id))
     807             :     {
     808           0 :       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
     809             :                                  format_unformat_error, input);
     810           0 :       goto done;
     811             :     }
     812             : 
     813           0 :   if (bd_id == 0)
     814           0 :     return clib_error_return (0,
     815             :                               "No operations on the default bridge domain are supported");
     816             : 
     817           0 :   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
     818             : 
     819           0 :   if (p == 0)
     820           0 :     return clib_error_return (0, "No such bridge domain %d", bd_id);
     821             : 
     822           0 :   bd_index = p[0];
     823             : 
     824           0 :   if (!unformat (input, "%u", &age))
     825             :     {
     826             :       error =
     827           0 :         clib_error_return (0, "expecting ageing time in minutes but got `%U'",
     828             :                            format_unformat_error, input);
     829           0 :       goto done;
     830             :     }
     831             : 
     832             :   /* set the bridge domain flag */
     833           0 :   if (age > 255)
     834             :     {
     835             :       error =
     836           0 :         clib_error_return (0, "mac aging time cannot be bigger than 255");
     837           0 :       goto done;
     838             :     }
     839           0 :   bd_set_mac_age (vm, bd_index, (u8) age);
     840             : 
     841           0 : done:
     842           0 :   return error;
     843             : }
     844             : 
     845             : /*?
     846             :  * Layer 2 mac aging can be enabled and disabled on each
     847             :  * bridge-domain. Use this command to set or disable mac aging
     848             :  * on specific bridge-domains. It is disabled by default.
     849             :  *
     850             :  * @cliexpar
     851             :  * Example of how to set mac aging (where 200 is the bridge-domain-id and
     852             :  * 5 is aging time in minutes):
     853             :  * @cliexcmd{set bridge-domain mac-age 200 5}
     854             :  * Example of how to disable mac aging (where 200 is the bridge-domain-id):
     855             :  * @cliexcmd{set bridge-domain flood 200 0}
     856             : ?*/
     857             : /* *INDENT-OFF* */
     858      285289 : VLIB_CLI_COMMAND (bd_mac_age_cli, static) = {
     859             :   .path = "set bridge-domain mac-age",
     860             :   .short_help = "set bridge-domain mac-age <bridge-domain-id> <mins>",
     861             :   .function = bd_mac_age,
     862             : };
     863             : /* *INDENT-ON* */
     864             : 
     865             : static clib_error_t *
     866           0 : bd_learn_limit (vlib_main_t *vm, unformat_input_t *input,
     867             :                 vlib_cli_command_t *cmd)
     868             : {
     869           0 :   bd_main_t *bdm = &bd_main;
     870           0 :   clib_error_t *error = 0;
     871             :   u32 bd_index, bd_id;
     872             :   u32 learn_limit;
     873             :   uword *p;
     874             : 
     875           0 :   if (!unformat (input, "%d", &bd_id))
     876             :     {
     877           0 :       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
     878             :                                  format_unformat_error, input);
     879           0 :       goto done;
     880             :     }
     881             : 
     882           0 :   if (bd_id == 0)
     883           0 :     return clib_error_return (
     884             :       0, "No operations on the default bridge domain are supported");
     885             : 
     886           0 :   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
     887             : 
     888           0 :   if (p == 0)
     889           0 :     return clib_error_return (0, "No such bridge domain %d", bd_id);
     890             : 
     891           0 :   bd_index = p[0];
     892             : 
     893           0 :   if (!unformat (input, "%u", &learn_limit))
     894             :     {
     895           0 :       error = clib_error_return (
     896             :         0, "expecting maxium number of learned entries but got `%U'",
     897             :         format_unformat_error, input);
     898           0 :       goto done;
     899             :     }
     900             : 
     901           0 :   bd_set_learn_limit (vm, bd_index, learn_limit);
     902             : 
     903           0 : done:
     904           0 :   return error;
     905             : }
     906             : 
     907      285289 : VLIB_CLI_COMMAND (bd_learn_limit_cli, static) = {
     908             :   .path = "set bridge-domain learn-limit",
     909             :   .short_help =
     910             :     "set bridge-domain learn-limit <bridge-domain-id> <learn-limit>",
     911             :   .function = bd_learn_limit,
     912             : };
     913             : 
     914             : /*?
     915             :  * Modify whether or not an existing bridge-domain should terminate and respond
     916             :  * to ARP Requests. ARP Termination is disabled by default.
     917             :  *
     918             :  * @cliexpar
     919             :  * Example of how to enable ARP termination (where 200 is the bridge-domain-id):
     920             :  * @cliexcmd{set bridge-domain arp term 200}
     921             :  * Example of how to disable ARP termination (where 200 is the bridge-domain-id):
     922             :  * @cliexcmd{set bridge-domain arp term 200 disable}
     923             : ?*/
     924             : /* *INDENT-OFF* */
     925      285289 : VLIB_CLI_COMMAND (bd_arp_term_cli, static) = {
     926             :   .path = "set bridge-domain arp term",
     927             :   .short_help = "set bridge-domain arp term <bridge-domain-id> [disable]",
     928             :   .function = bd_arp_term,
     929             : };
     930             : /* *INDENT-ON* */
     931             : 
     932             : 
     933             : /**
     934             :  * Add/delete IP address to MAC address mapping.
     935             :  *
     936             :  * The clib hash implementation stores uword entries in the hash table.
     937             :  * The hash table mac_by_ip4 is keyed via IP4 address and store the
     938             :  * 6-byte MAC address directly in the hash table entry uword.
     939             :  *
     940             :  * @warning This only works for 64-bit processor with 8-byte uword;
     941             :  * which means this code *WILL NOT WORK* for a 32-bit processor with
     942             :  * 4-byte uword.
     943             :  */
     944             : u32
     945          85 : bd_add_del_ip_mac (u32 bd_index,
     946             :                    ip46_type_t type,
     947             :                    const ip46_address_t * ip,
     948             :                    const mac_address_t * mac, u8 is_add)
     949             : {
     950          85 :   l2_bridge_domain_t *bd_cfg = l2input_bd_config (bd_index);
     951          85 :   u64 new_mac = mac_address_as_u64 (mac);
     952             :   u64 *old_mac;
     953             : 
     954             :   /* make sure uword is 8 bytes */
     955             :   ASSERT (sizeof (uword) == sizeof (u64));
     956          85 :   ASSERT (bd_is_valid (bd_cfg));
     957             : 
     958          85 :   if (IP46_TYPE_IP6 == type)
     959             :     {
     960             :       ip6_address_t *ip6_addr_key;
     961             :       hash_pair_t *hp;
     962          90 :       old_mac = (u64 *) hash_get_mem (bd_cfg->mac_by_ip6, &ip->ip6);
     963          45 :       if (is_add)
     964             :         {
     965          40 :           if (old_mac == NULL)
     966             :             {
     967             :               /* new entry - allocate and create ip6 address key */
     968          30 :               ip6_addr_key = clib_mem_alloc (sizeof (ip6_address_t));
     969          30 :               clib_memcpy (ip6_addr_key, &ip->ip6, sizeof (ip6_address_t));
     970             :             }
     971          10 :           else if (*old_mac == new_mac)
     972             :             {
     973             :               /* same mac entry already exist for ip6 address */
     974           0 :               return 0;
     975             :             }
     976             :           else
     977             :             {
     978             :               /* update mac for ip6 address */
     979          10 :               hp = hash_get_pair (bd_cfg->mac_by_ip6, &ip->ip6);
     980          10 :               ip6_addr_key = (ip6_address_t *) hp->key;
     981             :             }
     982          80 :           hash_set_mem (bd_cfg->mac_by_ip6, ip6_addr_key, new_mac);
     983             :         }
     984             :       else
     985             :         {
     986           5 :           if (old_mac && (*old_mac == new_mac))
     987             :             {
     988           5 :               hp = hash_get_pair (bd_cfg->mac_by_ip6, &ip->ip6);
     989           5 :               ip6_addr_key = (ip6_address_t *) hp->key;
     990          10 :               hash_unset_mem (bd_cfg->mac_by_ip6, &ip->ip6);
     991           5 :               clib_mem_free (ip6_addr_key);
     992             :             }
     993             :           else
     994           0 :             return 1;
     995             :         }
     996             :     }
     997             :   else
     998             :     {
     999          40 :       old_mac = (u64 *) hash_get (bd_cfg->mac_by_ip4, ip->ip4.as_u32);
    1000          40 :       if (is_add)
    1001             :         {
    1002          38 :           if (old_mac && (*old_mac == new_mac))
    1003             :             /* mac entry already exist */
    1004           0 :             return 0;
    1005          38 :           hash_set (bd_cfg->mac_by_ip4, ip->ip4.as_u32, new_mac);
    1006             :         }
    1007             :       else
    1008             :         {
    1009           2 :           if (old_mac && (*old_mac == new_mac))
    1010           2 :             hash_unset (bd_cfg->mac_by_ip4, ip->ip4.as_u32);
    1011             :           else
    1012           0 :             return 1;
    1013             :         }
    1014             :     }
    1015          85 :   return 0;
    1016             : }
    1017             : 
    1018             : /**
    1019             :  * Flush IP address to MAC address mapping tables in a BD.
    1020             :  */
    1021             : void
    1022           0 : bd_flush_ip_mac (u32 bd_index)
    1023             : {
    1024           0 :   l2_bridge_domain_t *bd = l2input_bd_config (bd_index);
    1025           0 :   ASSERT (bd_is_valid (bd));
    1026           0 :   bd_free_ip_mac_tables (bd);
    1027           0 :   bd->mac_by_ip4 = 0;
    1028           0 :   bd->mac_by_ip6 =
    1029           0 :     hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword));
    1030           0 : }
    1031             : 
    1032             : /**
    1033             :     Set bridge-domain arp entry add/delete.
    1034             :     The CLI format is:
    1035             :     set bridge-domain arp entry <bridge-domain-id> <ip-addr> <mac-addr> [del]
    1036             : */
    1037             : static clib_error_t *
    1038           0 : bd_arp_entry (vlib_main_t * vm,
    1039             :               unformat_input_t * input, vlib_cli_command_t * cmd)
    1040             : {
    1041           0 :   ip46_address_t ip_addr = ip46_address_initializer;
    1042           0 :   ip46_type_t type = IP46_TYPE_IP4;
    1043           0 :   bd_main_t *bdm = &bd_main;
    1044           0 :   clib_error_t *error = 0;
    1045             :   u32 bd_index, bd_id;
    1046             :   mac_address_t mac;
    1047           0 :   u8 is_add = 1;
    1048             :   uword *p;
    1049             : 
    1050           0 :   if (!unformat (input, "%d", &bd_id))
    1051             :     {
    1052           0 :       error = clib_error_return (0, "expecting bridge-domain id but got `%U'",
    1053             :                                  format_unformat_error, input);
    1054           0 :       goto done;
    1055             :     }
    1056             : 
    1057           0 :   if (bd_id == 0)
    1058           0 :     return clib_error_return (0,
    1059             :                               "No operations on the default bridge domain are supported");
    1060             : 
    1061           0 :   p = hash_get (bdm->bd_index_by_bd_id, bd_id);
    1062             : 
    1063           0 :   if (p)
    1064           0 :     bd_index = *p;
    1065             :   else
    1066           0 :     return clib_error_return (0, "No such bridge domain %d", bd_id);
    1067             : 
    1068           0 :   if (unformat (input, "%U", unformat_ip4_address, &ip_addr.ip4))
    1069             :     {
    1070           0 :       type = IP46_TYPE_IP4;
    1071             :     }
    1072           0 :   else if (unformat (input, "%U", unformat_ip6_address, &ip_addr.ip6))
    1073             :     {
    1074           0 :       type = IP46_TYPE_IP6;
    1075             :     }
    1076           0 :   else if (unformat (input, "del-all"))
    1077             :     {
    1078           0 :       bd_flush_ip_mac (bd_index);
    1079           0 :       goto done;
    1080             :     }
    1081             :   else
    1082             :     {
    1083           0 :       error = clib_error_return (0, "expecting IP address but got `%U'",
    1084             :                                  format_unformat_error, input);
    1085           0 :       goto done;
    1086             :     }
    1087             : 
    1088           0 :   if (!unformat (input, "%U", unformat_mac_address_t, &mac))
    1089             :     {
    1090           0 :       error = clib_error_return (0, "expecting MAC address but got `%U'",
    1091             :                                  format_unformat_error, input);
    1092           0 :       goto done;
    1093             :     }
    1094             : 
    1095           0 :   if (unformat (input, "del"))
    1096             :     {
    1097           0 :       is_add = 0;
    1098             :     }
    1099             : 
    1100             :   /* set the bridge domain flagAdd IP-MAC entry into bridge domain */
    1101           0 :   if (bd_add_del_ip_mac (bd_index, type, &ip_addr, &mac, is_add))
    1102             :     {
    1103           0 :       error = clib_error_return (0, "MAC %s for IP %U and MAC %U failed",
    1104             :                                  is_add ? "add" : "del",
    1105             :                                  format_ip46_address, &ip_addr, IP46_TYPE_ANY,
    1106             :                                  format_mac_address_t, &mac);
    1107             :     }
    1108             : 
    1109           0 : done:
    1110           0 :   return error;
    1111             : }
    1112             : 
    1113             : /*?
    1114             :  * Add an ARP entry to an existing bridge-domain.
    1115             :  *
    1116             :  * @cliexpar
    1117             :  * Example of how to add an ARP entry (where 200 is the bridge-domain-id):
    1118             :  * @cliexcmd{set bridge-domain arp entry 200 192.168.72.45 52:54:00:3b:83:1a}
    1119             :  * Example of how to delete an ARP entry (where 200 is the bridge-domain-id):
    1120             :  * @cliexcmd{set bridge-domain arp entry 200 192.168.72.45 52:54:00:3b:83:1a del}
    1121             : ?*/
    1122             : /* *INDENT-OFF* */
    1123      285289 : VLIB_CLI_COMMAND (bd_arp_entry_cli, static) = {
    1124             :   .path = "set bridge-domain arp entry",
    1125             :   .short_help = "set bridge-domain arp entry <bridge-domain-id> [<ip-addr> <mac-addr> [del] | del-all]",
    1126             :   .function = bd_arp_entry,
    1127             : };
    1128             : /* *INDENT-ON* */
    1129             : 
    1130             : static u8 *
    1131         244 : format_uu_cfg (u8 * s, va_list * args)
    1132             : {
    1133         244 :   l2_bridge_domain_t *bd_config = va_arg (*args, l2_bridge_domain_t *);
    1134             : 
    1135         244 :   if (bd_config->feature_bitmap & L2INPUT_FEAT_UU_FWD)
    1136           1 :     return (format (s, "%U", format_vnet_sw_if_index_name_with_NA,
    1137             :                     vnet_get_main (), bd_config->uu_fwd_sw_if_index));
    1138         243 :   else if (bd_config->feature_bitmap & L2INPUT_FEAT_UU_FLOOD)
    1139         221 :     return (format (s, "flood"));
    1140             :   else
    1141          22 :     return (format (s, "drop"));
    1142             : }
    1143             : 
    1144             : /**
    1145             :    Show bridge-domain state.
    1146             :    The CLI format is:
    1147             :    show bridge-domain [<bd_index>]
    1148             : */
    1149             : static clib_error_t *
    1150         232 : bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
    1151             : {
    1152         232 :   vnet_main_t *vnm = vnet_get_main ();
    1153         232 :   bd_main_t *bdm = &bd_main;
    1154         232 :   clib_error_t *error = 0;
    1155         232 :   u32 bd_index = ~0;
    1156             :   l2_bridge_domain_t *bd_config;
    1157             :   u32 start, end;
    1158         232 :   u32 detail = 0;
    1159         232 :   u32 intf = 0;
    1160         232 :   u32 arp = 0;
    1161         232 :   u32 bd_tag = 0;
    1162         232 :   u32 bd_id = ~0;
    1163             :   uword *p;
    1164             : 
    1165         232 :   start = 1;
    1166         232 :   end = vec_len (l2input_main.bd_configs);
    1167             : 
    1168         232 :   if (unformat (input, "%d", &bd_id))
    1169             :     {
    1170         222 :       if (unformat (input, "detail"))
    1171         222 :         detail = 1;
    1172           0 :       else if (unformat (input, "det"))
    1173           0 :         detail = 1;
    1174         222 :       if (unformat (input, "int"))
    1175           0 :         intf = 1;
    1176         222 :       if (unformat (input, "arp"))
    1177           0 :         arp = 1;
    1178         222 :       if (unformat (input, "bd-tag"))
    1179           0 :         bd_tag = 1;
    1180             : 
    1181         222 :       if (bd_id == 0)
    1182           0 :         return clib_error_return (0,
    1183             :                                   "No operations on the default bridge domain are supported");
    1184             : 
    1185         222 :       p = hash_get (bdm->bd_index_by_bd_id, bd_id);
    1186         222 :       if (p)
    1187         222 :         bd_index = *p;
    1188             :       else
    1189           0 :         return clib_error_return (0, "No such bridge domain %d", bd_id);
    1190             : 
    1191         222 :       vec_validate (l2input_main.bd_configs, bd_index);
    1192         222 :       bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index);
    1193         222 :       if (bd_is_valid (bd_config))
    1194             :         {
    1195         222 :           start = bd_index;
    1196         222 :           end = start + 1;
    1197             :         }
    1198             :       else
    1199             :         {
    1200           0 :           vlib_cli_output (vm, "bridge-domain %d not in use", bd_id);
    1201           0 :           goto done;
    1202             :         }
    1203             :     }
    1204             : 
    1205             :   /* Show all bridge-domains that have been initialized */
    1206         232 :   u32 printed = 0;
    1207         232 :   u8 *as = 0;
    1208         504 :   for (bd_index = start; bd_index < end; bd_index++)
    1209             :     {
    1210         272 :       bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index);
    1211         272 :       if (bd_is_valid (bd_config))
    1212             :         {
    1213         244 :           if (!printed)
    1214             :             {
    1215         227 :               printed = 1;
    1216         227 :               vlib_cli_output (vm,
    1217             :                                "%=8s %=7s %=4s %=9s %=9s %=9s %=11s %=9s %=9s "
    1218             :                                "%=9s %=8s %=8s %=11s",
    1219             :                                "BD-ID", "Index", "BSN", "Age(min)", "Learning",
    1220             :                                "U-Forwrd", "UU-Flood", "Flooding", "ARP-Term",
    1221             :                                "arp-ufwd", "Learn-count", "Learn-limit",
    1222             :                                "BVI-Intf");
    1223             :             }
    1224             : 
    1225         244 :           if (bd_config->mac_age)
    1226           0 :             as = format (as, "%d", bd_config->mac_age);
    1227             :           else
    1228         244 :             as = format (as, "off");
    1229        1220 :           vlib_cli_output (
    1230             :             vm,
    1231             :             "%=8d %=7d %=4d %=9v %=9s %=9s %=11U %=9s %=9s %=9s %=8d %=8d "
    1232             :             "%=11U",
    1233         244 :             bd_config->bd_id, bd_index, bd_config->seq_num, as,
    1234         244 :             bd_config->feature_bitmap & L2INPUT_FEAT_LEARN ? "on" : "off",
    1235         244 :             bd_config->feature_bitmap & L2INPUT_FEAT_FWD ? "on" : "off",
    1236             :             format_uu_cfg, bd_config,
    1237         244 :             bd_config->feature_bitmap & L2INPUT_FEAT_FLOOD ? "on" : "off",
    1238         244 :             bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM ? "on" : "off",
    1239         244 :             bd_config->feature_bitmap & L2INPUT_FEAT_ARP_UFWD ? "on" : "off",
    1240             :             bd_config->learn_count, bd_config->learn_limit,
    1241             :             format_vnet_sw_if_index_name_with_NA, vnm,
    1242             :             bd_config->bvi_sw_if_index);
    1243         244 :           if (detail)
    1244         222 :             vlib_cli_output (vm, "%U", format_l2_input_feature_bitmap,
    1245             :                              bd_config->feature_bitmap);
    1246         244 :           vec_reset_length (as);
    1247             : 
    1248         244 :           if (detail || intf)
    1249             :             {
    1250             :               /* Show all member interfaces */
    1251             :               int i;
    1252        1376 :               vec_foreach_index (i, bd_config->members)
    1253             :               {
    1254        1154 :                 l2_flood_member_t *member =
    1255        1154 :                   vec_elt_at_index (bd_config->members, i);
    1256        1154 :                 u8 swif_seq_num = l2_input_seq_num (member->sw_if_index);
    1257             :                 u32 vtr_opr, dot1q, tag1, tag2;
    1258        1154 :                 if (i == 0)
    1259             :                   {
    1260         222 :                     vlib_cli_output (vm, "\n%=30s%=7s%=5s%=5s%=5s%=9s%=30s",
    1261             :                                      "Interface", "If-idx", "ISN", "SHG",
    1262             :                                      "BVI", "TxFlood", "VLAN-Tag-Rewrite");
    1263             :                   }
    1264        1154 :                 l2vtr_get (vm, vnm, member->sw_if_index, &vtr_opr, &dot1q,
    1265             :                            &tag1, &tag2);
    1266        2308 :                 vlib_cli_output (vm, "%=30U%=7d%=5d%=5d%=5s%=9s%=30U",
    1267             :                                  format_vnet_sw_if_index_name, vnm,
    1268             :                                  member->sw_if_index, member->sw_if_index,
    1269        1154 :                                  swif_seq_num, member->shg,
    1270        1154 :                                  member->flags & L2_FLOOD_MEMBER_BVI ? "*" :
    1271        1154 :                                  "-", i < bd_config->flood_count ? "*" : "-",
    1272             :                                  format_vtr, vtr_opr, dot1q, tag1, tag2);
    1273             :               }
    1274         222 :               if (~0 != bd_config->uu_fwd_sw_if_index)
    1275           1 :                 vlib_cli_output (vm, "%=30U%=7d%=5d%=5d%=5s%=9s%=30s",
    1276             :                                  format_vnet_sw_if_index_name, vnm,
    1277             :                                  bd_config->uu_fwd_sw_if_index,
    1278             :                                  bd_config->uu_fwd_sw_if_index,
    1279             :                                  0, 0, "uu", "-", "None");
    1280             : 
    1281             :             }
    1282             : 
    1283         244 :           if ((detail || arp) &&
    1284         222 :               (bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM))
    1285             :             {
    1286             :               u32 ip4_addr;
    1287             :               ip6_address_t *ip6_addr;
    1288             :               u64 mac_addr;
    1289           0 :               vlib_cli_output (vm,
    1290             :                                "\n  IP4/IP6 to MAC table for ARP Termination");
    1291             : 
    1292             :               /* *INDENT-OFF* */
    1293           0 :               hash_foreach (ip4_addr, mac_addr, bd_config->mac_by_ip4,
    1294             :               ({
    1295             :                 vlib_cli_output (vm, "%=40U => %=20U",
    1296             :                                  format_ip4_address, &ip4_addr,
    1297             :                                  format_ethernet_address, &mac_addr);
    1298             :               }));
    1299             : 
    1300           0 :               hash_foreach_mem (ip6_addr, mac_addr, bd_config->mac_by_ip6,
    1301             :               ({
    1302             :                 vlib_cli_output (vm, "%=40U => %=20U",
    1303             :                                  format_ip6_address, ip6_addr,
    1304             :                                  format_ethernet_address, &mac_addr);
    1305             :               }));
    1306             :               /* *INDENT-ON* */
    1307             :             }
    1308             : 
    1309         244 :           if ((detail || bd_tag) && (bd_config->bd_tag))
    1310             :             {
    1311           0 :               vlib_cli_output (vm, "\n  BD-Tag: %s", bd_config->bd_tag);
    1312             : 
    1313             :             }
    1314             :         }
    1315             :     }
    1316         232 :   vec_free (as);
    1317             : 
    1318         232 :   if (!printed)
    1319             :     {
    1320           5 :       vlib_cli_output (vm, "no bridge-domains in use");
    1321             :     }
    1322             : 
    1323         227 : done:
    1324         232 :   return error;
    1325             : }
    1326             : 
    1327             : /*?
    1328             :  * Show a summary of all the bridge-domain instances or detailed view of a
    1329             :  * single bridge-domain. Bridge-domains are created by adding an interface
    1330             :  * to a bridge using the '<em>set interface l2 bridge</em>' command.
    1331             :  *
    1332             :  * @cliexpar
    1333             :  * @parblock
    1334             :  * Example of displaying all bridge-domains:
    1335             :  * @cliexstart{show bridge-domain}
    1336             :  *  ID   Index   Learning   U-Forwrd   UU-Flood   Flooding   ARP-Term     BVI-Intf
    1337             :  *  0      0        off        off        off        off        off        local0
    1338             :  * 200     1        on         on         on         on         off          N/A
    1339             :  * @cliexend
    1340             :  *
    1341             :  * Example of displaying details of a single bridge-domains:
    1342             :  * @cliexstart{show bridge-domain 200 detail}
    1343             :  *  ID   Index   Learning   U-Forwrd   UU-Flood   Flooding   ARP-Term     BVI-Intf
    1344             :  * 200     1        on         on         on         on         off          N/A
    1345             :  *
    1346             :  *          Interface           Index  SHG  BVI        VLAN-Tag-Rewrite
    1347             :  *  GigabitEthernet0/8/0.200      3     0    -               none
    1348             :  *  GigabitEthernet0/9/0.200      4     0    -               none
    1349             :  * @cliexend
    1350             :  * @endparblock
    1351             : ?*/
    1352             : /* *INDENT-OFF* */
    1353      285289 : VLIB_CLI_COMMAND (bd_show_cli, static) = {
    1354             :   .path = "show bridge-domain",
    1355             :   .short_help = "show bridge-domain [bridge-domain-id [detail|int|arp|bd-tag]]",
    1356             :   .function = bd_show,
    1357             : };
    1358             : /* *INDENT-ON* */
    1359             : 
    1360             : int
    1361         127 : bd_add_del (l2_bridge_domain_add_del_args_t * a)
    1362             : {
    1363         127 :   bd_main_t *bdm = &bd_main;
    1364         127 :   l2fib_main_t *fm = &l2fib_main;
    1365         127 :   vlib_main_t *vm = bdm->vlib_main;
    1366         127 :   int rv = 0;
    1367             : 
    1368         127 :   if (fm->mac_table_initialized == 0)
    1369          20 :     l2fib_table_init ();
    1370             : 
    1371         127 :   u32 bd_index = bd_find_index (bdm, a->bd_id);
    1372         127 :   if (a->is_add)
    1373             :     {
    1374          67 :       if (bd_index != ~0)
    1375           0 :         return VNET_API_ERROR_BD_ALREADY_EXISTS;
    1376          67 :       if (a->bd_id > L2_BD_ID_MAX)
    1377           0 :         return VNET_API_ERROR_BD_ID_EXCEED_MAX;
    1378          67 :       bd_index = bd_add_bd_index (bdm, a->bd_id);
    1379             : 
    1380          67 :       bd_flags_t enable_flags = 0, disable_flags = 0;
    1381          67 :       if (a->flood)
    1382          67 :         enable_flags |= L2_FLOOD;
    1383             :       else
    1384           0 :         disable_flags |= L2_FLOOD;
    1385             : 
    1386          67 :       if (a->uu_flood)
    1387          65 :         enable_flags |= L2_UU_FLOOD;
    1388             :       else
    1389           2 :         disable_flags |= L2_UU_FLOOD;
    1390             : 
    1391          67 :       if (a->forward)
    1392          67 :         enable_flags |= L2_FWD;
    1393             :       else
    1394           0 :         disable_flags |= L2_FWD;
    1395             : 
    1396          67 :       if (a->learn)
    1397          65 :         enable_flags |= L2_LEARN;
    1398             :       else
    1399           2 :         disable_flags |= L2_LEARN;
    1400             : 
    1401          67 :       if (a->arp_term)
    1402          20 :         enable_flags |= L2_ARP_TERM;
    1403             :       else
    1404          47 :         disable_flags |= L2_ARP_TERM;
    1405             : 
    1406          67 :       if (a->arp_ufwd)
    1407           0 :         enable_flags |= L2_ARP_UFWD;
    1408             :       else
    1409          67 :         disable_flags |= L2_ARP_UFWD;
    1410             : 
    1411          67 :       if (enable_flags)
    1412          67 :         bd_set_flags (vm, bd_index, enable_flags, 1 /* enable */ );
    1413             : 
    1414          67 :       if (disable_flags)
    1415          67 :         bd_set_flags (vm, bd_index, disable_flags, 0 /* disable */ );
    1416             : 
    1417          67 :       bd_set_mac_age (vm, bd_index, a->mac_age);
    1418             : 
    1419          67 :       if (a->bd_tag)
    1420          67 :         bd_set_bd_tag (vm, bd_index, a->bd_tag);
    1421             : 
    1422          67 :       bd_set_learn_limit (vm, bd_index, l2learn_main.bd_default_learn_limit);
    1423          67 :       vec_elt_at_index (l2input_main.bd_configs, bd_index)->learn_count = 0;
    1424             :     }
    1425             :   else
    1426             :     {
    1427          60 :       if (bd_index == ~0)
    1428           0 :         return VNET_API_ERROR_NO_SUCH_ENTRY;
    1429          60 :       if (bd_index == 0)
    1430           0 :         return VNET_API_ERROR_BD_NOT_MODIFIABLE;
    1431          60 :       if (vec_len (l2input_main.bd_configs[bd_index].members))
    1432           0 :         return VNET_API_ERROR_BD_IN_USE;
    1433          60 :       rv = bd_delete (bdm, bd_index);
    1434             :     }
    1435             : 
    1436         127 :   return rv;
    1437             : }
    1438             : 
    1439             : /**
    1440             :    Create or delete bridge-domain.
    1441             :    The CLI format:
    1442             :    create bridge-domain <bd_index> [learn <0|1>] [forward <0|1>] [uu-flood <0|1>] [flood <0|1>]
    1443             :                                         [arp-term <0|1>] [mac-age <nn>] [bd-tag <tag>] [del]
    1444             : */
    1445             : 
    1446             : static clib_error_t *
    1447           0 : bd_add_del_command_fn (vlib_main_t * vm, unformat_input_t * input,
    1448             :                        vlib_cli_command_t * cmd)
    1449             : {
    1450           0 :   unformat_input_t _line_input, *line_input = &_line_input;
    1451           0 :   clib_error_t *error = 0;
    1452           0 :   u8 is_add = 1;
    1453           0 :   u32 bd_id = ~0;
    1454           0 :   u32 flood = 1, forward = 1, learn = 1, uu_flood = 1, arp_term =
    1455           0 :     0, arp_ufwd = 0;
    1456           0 :   u32 mac_age = 0;
    1457           0 :   u8 *bd_tag = NULL;
    1458           0 :   l2_bridge_domain_add_del_args_t _a, *a = &_a;
    1459             :   int rv;
    1460             : 
    1461             :   /* Get a line of input. */
    1462           0 :   if (!unformat_user (input, unformat_line_input, line_input))
    1463           0 :     return 0;
    1464             : 
    1465           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    1466             :     {
    1467           0 :       if (unformat (line_input, "%d", &bd_id))
    1468             :         ;
    1469           0 :       else if (unformat (line_input, "flood %d", &flood))
    1470             :         ;
    1471           0 :       else if (unformat (line_input, "uu-flood %d", &uu_flood))
    1472             :         ;
    1473           0 :       else if (unformat (line_input, "forward %d", &forward))
    1474             :         ;
    1475           0 :       else if (unformat (line_input, "learn %d", &learn))
    1476             :         ;
    1477           0 :       else if (unformat (line_input, "arp-term %d", &arp_term))
    1478             :         ;
    1479           0 :       else if (unformat (line_input, "arp-ufwd %d", &arp_ufwd))
    1480             :         ;
    1481           0 :       else if (unformat (line_input, "mac-age %d", &mac_age))
    1482             :         ;
    1483           0 :       else if (unformat (line_input, "bd-tag %s", &bd_tag))
    1484             :         ;
    1485           0 :       else if (unformat (line_input, "del"))
    1486             :         {
    1487           0 :           is_add = 0;
    1488           0 :           flood = uu_flood = forward = learn = 0;
    1489             :         }
    1490             :       else
    1491           0 :         break;
    1492             :     }
    1493             : 
    1494           0 :   if (bd_id == ~0)
    1495             :     {
    1496           0 :       if (is_add)
    1497             :         {
    1498           0 :           bd_id = bd_get_unused_id ();
    1499             :         }
    1500             :       else
    1501             :         {
    1502           0 :           error = clib_error_return (0, "bridge-domain-id not specified");
    1503           0 :           goto done;
    1504             :         }
    1505             :     }
    1506             : 
    1507           0 :   if (bd_id == 0)
    1508             :     {
    1509           0 :       error = clib_error_return (0, "bridge domain 0 can not be modified");
    1510           0 :       goto done;
    1511             :     }
    1512             : 
    1513           0 :   if (mac_age > 255)
    1514             :     {
    1515           0 :       error = clib_error_return (0, "mac age must be less than 256");
    1516           0 :       goto done;
    1517             :     }
    1518           0 :   if ((bd_tag) && (strlen ((char *) bd_tag) > 63))
    1519             :     {
    1520           0 :       error = clib_error_return (0, "bd-tag cannot be longer than 63");
    1521           0 :       goto done;
    1522             :     }
    1523             : 
    1524           0 :   clib_memset (a, 0, sizeof (*a));
    1525           0 :   a->is_add = is_add;
    1526           0 :   a->bd_id = bd_id;
    1527           0 :   a->flood = (u8) flood;
    1528           0 :   a->uu_flood = (u8) uu_flood;
    1529           0 :   a->forward = (u8) forward;
    1530           0 :   a->learn = (u8) learn;
    1531           0 :   a->arp_term = (u8) arp_term;
    1532           0 :   a->arp_ufwd = (u8) arp_ufwd;
    1533           0 :   a->mac_age = (u8) mac_age;
    1534           0 :   a->bd_tag = bd_tag;
    1535             : 
    1536           0 :   rv = bd_add_del (a);
    1537             : 
    1538           0 :   switch (rv)
    1539             :     {
    1540           0 :     case 0:
    1541           0 :       if (is_add)
    1542           0 :         vlib_cli_output (vm, "bridge-domain %d", bd_id);
    1543           0 :       break;
    1544           0 :     case VNET_API_ERROR_BD_IN_USE:
    1545           0 :       error = clib_error_return (0, "bridge domain in use - remove members");
    1546           0 :       goto done;
    1547           0 :     case VNET_API_ERROR_NO_SUCH_ENTRY:
    1548           0 :       error = clib_error_return (0, "bridge domain ID does not exist");
    1549           0 :       goto done;
    1550           0 :     case VNET_API_ERROR_BD_NOT_MODIFIABLE:
    1551           0 :       error = clib_error_return (0, "bridge domain 0 can not be modified");
    1552           0 :       goto done;
    1553           0 :     case VNET_API_ERROR_BD_ID_EXCEED_MAX:
    1554           0 :       error = clib_error_return (0, "bridge domain ID exceed 16M limit");
    1555           0 :       goto done;
    1556           0 :     default:
    1557           0 :       error = clib_error_return (0, "bd_add_del returned %d", rv);
    1558           0 :       goto done;
    1559             :     }
    1560             : 
    1561           0 : done:
    1562           0 :   vec_free (bd_tag);
    1563           0 :   unformat_free (line_input);
    1564             : 
    1565           0 :   return error;
    1566             : }
    1567             : 
    1568             : 
    1569             : /*?
    1570             :  * Create/Delete bridge-domain instance
    1571             :  *
    1572             :  * @cliexpar
    1573             :  * @parblock
    1574             :  * Example of creating bridge-domain 1:
    1575             :  * @cliexstart{create bridge-domain 1}
    1576             :  * bridge-domain 1
    1577             :  * @cliexend
    1578             :  *
    1579             :  * Example of creating bridge-domain 2 with enabling arp-term, mac-age 60:
    1580             :  * @cliexstart{create bridge-domain 2 arp-term 1 mac-age 60}
    1581             :  * bridge-domain 2
    1582             :  *
    1583             :  * vpp# show bridge-domain
    1584             :  *   ID   Index   BSN  Age(min)  Learning  U-Forwrd  UU-Flood  Flooding  ARP-Term  BVI-Intf
    1585             :  *   0      0      0     off       off       off       off       off       off      local0
    1586             :  *   1      1      0     off        on        on       off        on       off       N/A
    1587             :  *   2      2      0      60        on        on       off        on        on       N/A
    1588             :  *
    1589             :  * @cliexend
    1590             :  *
    1591             :  * Example of delete bridge-domain 1:
    1592             :  * @cliexstart{create bridge-domain 1 del}
    1593             :  * @cliexend
    1594             :  * @endparblock
    1595             : ?*/
    1596             : 
    1597             : /* *INDENT-OFF* */
    1598      285289 : VLIB_CLI_COMMAND (bd_create_cli, static) = {
    1599             :   .path = "create bridge-domain",
    1600             :   .short_help = "create bridge-domain <bridge-domain-id>"
    1601             :                 " [learn <0|1>] [forward <0|1>] [uu-flood <0|1>] [flood <0|1>] [arp-term <0|1>]"
    1602             :                 " [arp-ufwd <0|1>] [mac-age <nn>] [bd-tag <tag>] [del]",
    1603             :   .function = bd_add_del_command_fn,
    1604             : };
    1605             : /* *INDENT-ON* */
    1606             : 
    1607             : /*
    1608             :  * Returns an unused bridge domain id, and ~0 if it can't find one.
    1609             :  */
    1610             : u32
    1611          17 : bd_get_unused_id (void)
    1612             : {
    1613          17 :   bd_main_t *bdm = &bd_main;
    1614             :   int i, j;
    1615             :   static u32 seed = 0;
    1616             : 
    1617             :   /* limit to 1M tries */
    1618          17 :   for (j = 0; j < 1 << 10; j++)
    1619             :     {
    1620          17 :       seed = random_u32 (&seed);
    1621          17 :       for (i = 0; i < 1 << 10; i++)
    1622             :         {
    1623             :           /*
    1624             :            * iterate seed+0, seed+1, seed-1, seed+2, seed-2, ... to generate id
    1625             :            */
    1626          17 :           seed += (2 * (i % 2) - 1) * i;
    1627             :           /* bd_id must be (1 <= bd_id <= L2_BD_ID_MAX) */
    1628          17 :           seed &= L2_BD_ID_MAX;
    1629          17 :           if (seed == 0)
    1630           0 :             continue;
    1631          17 :           if (bd_find_index (bdm, seed) == ~0)
    1632          17 :             return seed;
    1633             :         }
    1634             :     }
    1635             : 
    1636           0 :   return ~0;
    1637             : }
    1638             : 
    1639             : /*
    1640             :  * fd.io coding-style-patch-verification: ON
    1641             :  *
    1642             :  * Local Variables:
    1643             :  * eval: (c-set-style "gnu")
    1644             :  * End:
    1645             :  */

Generated by: LCOV version 1.14