diff options
author | Jerome Tollet <jtollet@cisco.com> | 2020-12-18 09:44:24 +0100 |
---|---|---|
committer | John Lo <loj@cisco.com> | 2021-01-06 04:17:09 +0000 |
commit | 5f93e3b7f85e2257406cf601e5f231a872100fcf (patch) | |
tree | a9ae2588b3d28ac380b0a97b827ac52101ebbd52 | |
parent | 1b576e0aa08c575a4409b50dc71e8f51d231f199 (diff) |
l2: add per bridge domain learn limit
Type: feature
Signed-off-by: Jerome Tollet <jtollet@cisco.com>
Change-Id: I57ed6699050445d9c9aec98eff3aab56735aca54
Signed-off-by: Jerome Tollet <jtollet@cisco.com>
-rw-r--r-- | src/vnet/l2/l2.api | 27 | ||||
-rw-r--r-- | src/vnet/l2/l2_api.c | 96 | ||||
-rw-r--r-- | src/vnet/l2/l2_bd.c | 141 | ||||
-rw-r--r-- | src/vnet/l2/l2_bd.h | 7 | ||||
-rw-r--r-- | src/vnet/l2/l2_fib.c | 55 | ||||
-rw-r--r-- | src/vnet/l2/l2_learn.c | 21 | ||||
-rw-r--r-- | src/vnet/l2/l2_learn.h | 3 | ||||
-rw-r--r-- | test/test_l2bd_learnlimit.py | 53 |
8 files changed, 337 insertions, 66 deletions
diff --git a/src/vnet/l2/l2.api b/src/vnet/l2/l2.api index 416874d94c2..ef830827e1c 100644 --- a/src/vnet/l2/l2.api +++ b/src/vnet/l2/l2.api @@ -247,6 +247,33 @@ autoreply define bridge_domain_set_mac_age u8 mac_age; }; +/** \brief L2 bridge domain set default learn limit + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param learn limit - maximum number of entries by default for bridge domains +*/ +autoreply define bridge_domain_set_default_learn_limit +{ + u32 client_index; + u32 context; + u32 learn_limit; +}; + + +/** \brief L2 bridge domain set learn limit + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param bd_id - the bridge domain idenntifier + @param learn limit - maximum number of entries for this bd +*/ +autoreply define bridge_domain_set_learn_limit +{ + u32 client_index; + u32 context; + u32 bd_id; + u32 learn_limit; +}; + /** \brief L2 bridge domain add or delete request @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request diff --git a/src/vnet/l2/l2_api.c b/src/vnet/l2/l2_api.c index 2c2f480b35f..97b429d0498 100644 --- a/src/vnet/l2/l2_api.c +++ b/src/vnet/l2/l2_api.c @@ -50,33 +50,36 @@ #include <vlibapi/api_helper_macros.h> -#define foreach_vpe_api_msg \ -_(L2_XCONNECT_DUMP, l2_xconnect_dump) \ -_(L2_FIB_CLEAR_TABLE, l2_fib_clear_table) \ -_(L2_FIB_TABLE_DUMP, l2_fib_table_dump) \ -_(L2FIB_FLUSH_ALL, l2fib_flush_all) \ -_(L2FIB_FLUSH_INT, l2fib_flush_int) \ -_(L2FIB_FLUSH_BD, l2fib_flush_bd) \ -_(L2FIB_ADD_DEL, l2fib_add_del) \ -_(WANT_L2_MACS_EVENTS, want_l2_macs_events) \ -_(L2_FLAGS, l2_flags) \ -_(SW_INTERFACE_SET_L2_XCONNECT, sw_interface_set_l2_xconnect) \ -_(SW_INTERFACE_SET_L2_BRIDGE, sw_interface_set_l2_bridge) \ -_(L2_PATCH_ADD_DEL, l2_patch_add_del) \ -_(L2_INTERFACE_EFP_FILTER, l2_interface_efp_filter) \ -_(BD_IP_MAC_ADD_DEL, bd_ip_mac_add_del) \ -_(BD_IP_MAC_FLUSH, bd_ip_mac_flush) \ -_(BD_IP_MAC_DUMP, bd_ip_mac_dump) \ -_(BRIDGE_DOMAIN_ADD_DEL, bridge_domain_add_del) \ -_(BRIDGE_DOMAIN_DUMP, bridge_domain_dump) \ -_(BRIDGE_FLAGS, bridge_flags) \ -_(L2_INTERFACE_VLAN_TAG_REWRITE, l2_interface_vlan_tag_rewrite) \ -_(L2_INTERFACE_PBB_TAG_REWRITE, l2_interface_pbb_tag_rewrite) \ -_(BRIDGE_DOMAIN_SET_MAC_AGE, bridge_domain_set_mac_age) \ -_(SW_INTERFACE_SET_VPATH, sw_interface_set_vpath) \ -_(BVI_CREATE, bvi_create) \ -_(BVI_DELETE, bvi_delete) \ -_(WANT_L2_ARP_TERM_EVENTS, want_l2_arp_term_events) +#define foreach_vpe_api_msg \ + _ (L2_XCONNECT_DUMP, l2_xconnect_dump) \ + _ (L2_FIB_CLEAR_TABLE, l2_fib_clear_table) \ + _ (L2_FIB_TABLE_DUMP, l2_fib_table_dump) \ + _ (L2FIB_FLUSH_ALL, l2fib_flush_all) \ + _ (L2FIB_FLUSH_INT, l2fib_flush_int) \ + _ (L2FIB_FLUSH_BD, l2fib_flush_bd) \ + _ (L2FIB_ADD_DEL, l2fib_add_del) \ + _ (WANT_L2_MACS_EVENTS, want_l2_macs_events) \ + _ (L2_FLAGS, l2_flags) \ + _ (SW_INTERFACE_SET_L2_XCONNECT, sw_interface_set_l2_xconnect) \ + _ (SW_INTERFACE_SET_L2_BRIDGE, sw_interface_set_l2_bridge) \ + _ (L2_PATCH_ADD_DEL, l2_patch_add_del) \ + _ (L2_INTERFACE_EFP_FILTER, l2_interface_efp_filter) \ + _ (BD_IP_MAC_ADD_DEL, bd_ip_mac_add_del) \ + _ (BD_IP_MAC_FLUSH, bd_ip_mac_flush) \ + _ (BD_IP_MAC_DUMP, bd_ip_mac_dump) \ + _ (BRIDGE_DOMAIN_ADD_DEL, bridge_domain_add_del) \ + _ (BRIDGE_DOMAIN_DUMP, bridge_domain_dump) \ + _ (BRIDGE_FLAGS, bridge_flags) \ + _ (L2_INTERFACE_VLAN_TAG_REWRITE, l2_interface_vlan_tag_rewrite) \ + _ (L2_INTERFACE_PBB_TAG_REWRITE, l2_interface_pbb_tag_rewrite) \ + _ (BRIDGE_DOMAIN_SET_MAC_AGE, bridge_domain_set_mac_age) \ + _ (SW_INTERFACE_SET_VPATH, sw_interface_set_vpath) \ + _ (BVI_CREATE, bvi_create) \ + _ (BVI_DELETE, bvi_delete) \ + _ (WANT_L2_ARP_TERM_EVENTS, want_l2_arp_term_events) \ + _ (BRIDGE_DOMAIN_SET_LEARN_LIMIT, bridge_domain_set_learn_limit) \ + _ (BRIDGE_DOMAIN_SET_DEFAULT_LEARN_LIMIT, \ + bridge_domain_set_default_learn_limit) static void send_l2_xconnect_details (vl_api_registration_t * reg, u32 context, @@ -409,6 +412,45 @@ vl_api_l2_flags_t_handler (vl_api_l2_flags_t * mp) } static void +vl_api_bridge_domain_set_default_learn_limit_t_handler ( + vl_api_bridge_domain_set_default_learn_limit_t *mp) +{ + vl_api_bridge_domain_set_default_learn_limit_reply_t *rmp; + int rv = 0; + + l2learn_main.bd_default_learn_limit = ntohl (mp->learn_limit); + REPLY_MACRO (VL_API_BRIDGE_DOMAIN_SET_DEFAULT_LEARN_LIMIT_REPLY); +} + +static void +vl_api_bridge_domain_set_learn_limit_t_handler ( + vl_api_bridge_domain_set_learn_limit_t *mp) +{ + vlib_main_t *vm = vlib_get_main (); + bd_main_t *bdm = &bd_main; + vl_api_bridge_domain_set_learn_limit_reply_t *rmp; + int rv = 0; + u32 bd_id = ntohl (mp->bd_id); + uword *p; + + if (bd_id == 0) + { + rv = VNET_API_ERROR_BD_NOT_MODIFIABLE; + goto out; + } + + p = hash_get (bdm->bd_index_by_bd_id, bd_id); + if (p == 0) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto out; + } + bd_set_learn_limit (vm, *p, ntohl (mp->learn_limit)); +out: + REPLY_MACRO (VL_API_BRIDGE_DOMAIN_SET_LEARN_LIMIT_REPLY); +} + +static void vl_api_bridge_domain_set_mac_age_t_handler (vl_api_bridge_domain_set_mac_age_t * mp) { diff --git a/src/vnet/l2/l2_bd.c b/src/vnet/l2/l2_bd.c index a3acc4b6d7e..7e6ea60b440 100644 --- a/src/vnet/l2/l2_bd.c +++ b/src/vnet/l2/l2_bd.c @@ -88,6 +88,9 @@ bd_add_bd_index (bd_main_t * bdm, u32 bd_id) vec_validate (l2input_main.bd_configs, rv); l2input_main.bd_configs[rv].bd_id = bd_id; + l2input_main.bd_configs[rv].learn_limit = + l2learn_main.bd_default_learn_limit; + l2input_main.bd_configs[rv].learn_count = 0; return rv; } @@ -124,6 +127,8 @@ bd_delete (bd_main_t * bdm, u32 bd_index) /* clear BD config for reuse: bd_id to -1 and clear feature_bitmap */ bd->bd_id = ~0; bd->feature_bitmap = 0; + bd->learn_limit = 0; + bd->learn_count = ~0; /* free BD tag */ vec_free (bd->bd_tag); @@ -356,9 +361,20 @@ bd_set_mac_age (vlib_main_t * vm, u32 bd_index, u8 age) } /** - Set the tag for the bridge domain. + Set learn limit for the bridge domain. */ +void +bd_set_learn_limit (vlib_main_t *vm, u32 bd_index, u32 learn_limit) +{ + l2_bridge_domain_t *bd_config; + vec_validate (l2input_main.bd_configs, bd_index); + bd_config = vec_elt_at_index (l2input_main.bd_configs, bd_index); + bd_config->learn_limit = learn_limit; +} +/** + Set the tag for the bridge domain. +*/ static void bd_set_bd_tag (vlib_main_t * vm, u32 bd_index, u8 * bd_tag) { @@ -446,6 +462,34 @@ VLIB_CLI_COMMAND (bd_learn_cli, static) = { }; /* *INDENT-ON* */ +static clib_error_t * +bd_default_learn_limit (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + l2learn_main_t *l2m = &l2learn_main; + clib_error_t *error = 0; + u32 learn_limit; + + if (!unformat (input, "%d", &learn_limit)) + { + error = clib_error_return ( + 0, "expecting per bridge-domain max entry number got`%U'", + format_unformat_error, input); + goto done; + } + + l2m->bd_default_learn_limit = learn_limit; + +done: + return error; +} + +VLIB_CLI_COMMAND (bd_default_learn_limit_cli, static) = { + .path = "set bridge-domain default-learn-limit", + .short_help = "set bridge-domain default-learn-limit <maxentries>", + .function = bd_default_learn_limit, +}; + /** Set bridge-domain forward enable/disable. The CLI format is: @@ -818,6 +862,55 @@ VLIB_CLI_COMMAND (bd_mac_age_cli, static) = { }; /* *INDENT-ON* */ +static clib_error_t * +bd_learn_limit (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + bd_main_t *bdm = &bd_main; + clib_error_t *error = 0; + u32 bd_index, bd_id; + u32 learn_limit; + uword *p; + + if (!unformat (input, "%d", &bd_id)) + { + error = clib_error_return (0, "expecting bridge-domain id but got `%U'", + format_unformat_error, input); + goto done; + } + + if (bd_id == 0) + return clib_error_return ( + 0, "No operations on the default bridge domain are supported"); + + p = hash_get (bdm->bd_index_by_bd_id, bd_id); + + if (p == 0) + return clib_error_return (0, "No such bridge domain %d", bd_id); + + bd_index = p[0]; + + if (!unformat (input, "%u", &learn_limit)) + { + error = clib_error_return ( + 0, "expecting maxium number of learned entries but got `%U'", + format_unformat_error, input); + goto done; + } + + bd_set_learn_limit (vm, bd_index, learn_limit); + +done: + return error; +} + +VLIB_CLI_COMMAND (bd_learn_limit_cli, static) = { + .path = "set bridge-domain learn-limit", + .short_help = + "set bridge-domain learn-limit <bridge-domain-id> <learn-limit>", + .function = bd_learn_limit, +}; + /*? * Modify whether or not an existing bridge-domain should terminate and respond * to ARP Requests. ARP Termination is disabled by default. @@ -1121,10 +1214,11 @@ bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { printed = 1; vlib_cli_output (vm, - "%=8s %=7s %=4s %=9s %=9s %=9s %=11s %=9s %=9s %=9s %=11s", - "BD-ID", "Index", "BSN", "Age(min)", - "Learning", "U-Forwrd", "UU-Flood", - "Flooding", "ARP-Term", "arp-ufwd", + "%=8s %=7s %=4s %=9s %=9s %=9s %=11s %=9s %=9s " + "%=9s %=8s %=8s %=11s", + "BD-ID", "Index", "BSN", "Age(min)", "Learning", + "U-Forwrd", "UU-Flood", "Flooding", "ARP-Term", + "arp-ufwd", "Learn-count", "Learn-limit", "BVI-Intf"); } @@ -1132,24 +1226,23 @@ bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) as = format (as, "%d", bd_config->mac_age); else as = format (as, "off"); - vlib_cli_output (vm, - "%=8d %=7d %=9v %=9s %=9s %=11U %=9s %=9s %=9s %=11U", - bd_config->bd_id, bd_index, as, - bd_config->feature_bitmap & L2INPUT_FEAT_LEARN ? - "on" : "off", - bd_config->feature_bitmap & L2INPUT_FEAT_FWD ? - "on" : "off", - format_uu_cfg, bd_config, - bd_config->feature_bitmap & L2INPUT_FEAT_FLOOD ? - "on" : "off", - bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM ? - "on" : "off", - bd_config->feature_bitmap & L2INPUT_FEAT_ARP_UFWD ? - "on" : "off", - format_vnet_sw_if_index_name_with_NA, - vnm, bd_config->bvi_sw_if_index); - vlib_cli_output (vm, "%U", format_l2_input_feature_bitmap, - bd_config->feature_bitmap); + vlib_cli_output ( + vm, + "%=8d %=7d %=4d %=9v %=9s %=9s %=11U %=9s %=9s %=9s %=8d %=8d " + "%=11U", + bd_config->bd_id, bd_index, bd_config->seq_num, as, + bd_config->feature_bitmap & L2INPUT_FEAT_LEARN ? "on" : "off", + bd_config->feature_bitmap & L2INPUT_FEAT_FWD ? "on" : "off", + format_uu_cfg, bd_config, + bd_config->feature_bitmap & L2INPUT_FEAT_FLOOD ? "on" : "off", + bd_config->feature_bitmap & L2INPUT_FEAT_ARP_TERM ? "on" : "off", + bd_config->feature_bitmap & L2INPUT_FEAT_ARP_UFWD ? "on" : "off", + bd_config->learn_count, bd_config->learn_limit, + format_vnet_sw_if_index_name_with_NA, vnm, + bd_config->bvi_sw_if_index); + if (detail) + vlib_cli_output (vm, "%U", format_l2_input_feature_bitmap, + bd_config->feature_bitmap); vec_reset_length (as); if (detail || intf) @@ -1326,6 +1419,8 @@ bd_add_del (l2_bridge_domain_add_del_args_t * a) if (a->bd_tag) bd_set_bd_tag (vm, bd_index, a->bd_tag); + bd_set_learn_limit (vm, bd_index, l2learn_main.bd_default_learn_limit); + vec_elt_at_index (l2input_main.bd_configs, bd_index)->learn_count = 0; } else { diff --git a/src/vnet/l2/l2_bd.h b/src/vnet/l2/l2_bd.h index 943358b4e15..0d77292519d 100644 --- a/src/vnet/l2/l2_bd.h +++ b/src/vnet/l2/l2_bd.h @@ -110,6 +110,12 @@ typedef struct /* Bridge domain tag (C string NULL terminated) */ u8 *bd_tag; + /* Maximum number of learned entries */ + u32 learn_limit; + + /* Current number of learned entries */ + u32 learn_count; + } l2_bridge_domain_t; /* Limit Bridge Domain ID to 24 bits to match 24-bit VNI range */ @@ -158,6 +164,7 @@ typedef enum bd_flags_t_ u32 bd_set_flags (vlib_main_t * vm, u32 bd_index, bd_flags_t flags, u32 enable); void bd_set_mac_age (vlib_main_t * vm, u32 bd_index, u8 age); +void bd_set_learn_limit (vlib_main_t *vm, u32 bd_index, u32 learn_limit); int bd_add_del (l2_bridge_domain_add_del_args_t * args); /** diff --git a/src/vnet/l2/l2_fib.c b/src/vnet/l2/l2_fib.c index c28c23bc41c..82be6bc0341 100644 --- a/src/vnet/l2/l2_fib.c +++ b/src/vnet/l2/l2_fib.c @@ -212,8 +212,7 @@ l2fib_show_walk_cb (BVT (clib_bihash_kv) * kvp, void *arg) if (ctx->add && !l2fib_entry_result_is_set_AGE_NOT (&result)) return (BIHASH_WALK_CONTINUE); /* skip learned macs */ - bd_config = vec_elt_at_index (l2input_main.bd_configs, - key.fields.bd_index); + bd_config = &vec_elt (l2input_main.bd_configs, key.fields.bd_index); if (l2fib_entry_result_is_set_AGE_NOT (&result)) s = format (s, "no"); @@ -384,6 +383,7 @@ void l2fib_clear_table (void) { l2fib_main_t *mp = &l2fib_main; + l2_bridge_domain_t *bd_config; if (mp->mac_table_initialized == 0) return; @@ -394,6 +394,8 @@ l2fib_clear_table (void) BV (clib_bihash_free) (&mp->mac_table); l2fib_table_init (); l2learn_main.global_learn_count = 0; + vec_foreach (bd_config, l2input_main.bd_configs) + bd_config->learn_count = 0; } /** Clear all entries in L2FIB. @@ -462,9 +464,21 @@ l2fib_add_entry (const u8 * mac, u32 bd_index, { /* decrement counter if overwriting a learned mac */ result.raw = kv.value; - if ((!l2fib_entry_result_is_set_AGE_NOT (&result)) - && (lm->global_learn_count)) - lm->global_learn_count--; + if (!l2fib_entry_result_is_set_AGE_NOT (&result)) + { + l2_bridge_domain_t *bd_config = + vec_elt_at_index (l2input_main.bd_configs, bd_index); + + /* check if learn_count == 0 in case of race condition between 2 + * workers adding an entry simultaneously */ + /* learn_count variable may have little inaccuracy because they are + * not incremented/decremented with atomic operations */ + /* l2fib_scan is call every 2sec fixing potential inaccuracy */ + if (lm->global_learn_count) + lm->global_learn_count--; + if (bd_config->learn_count) + bd_config->learn_count--; + } } /* set up result */ @@ -751,9 +765,15 @@ l2fib_del_entry (const u8 * mac, u32 bd_index, u32 sw_if_index) return 1; /* decrement counter if dynamically learned mac */ - if ((!l2fib_entry_result_is_set_AGE_NOT (&result)) && - (l2learn_main.global_learn_count)) - l2learn_main.global_learn_count--; + if (!l2fib_entry_result_is_set_AGE_NOT (&result)) + { + l2_bridge_domain_t *bd_config = + vec_elt_at_index (l2input_main.bd_configs, bd_index); + if (l2learn_main.global_learn_count) + l2learn_main.global_learn_count--; + if (bd_config->learn_count) + bd_config->learn_count--; + } /* Remove entry from hash table */ BV (clib_bihash_add_del) (&mp->mac_table, &kv, 0 /* is_add */ ); @@ -1051,11 +1071,16 @@ l2fib_scan (vlib_main_t * vm, f64 start_time, u8 event_only) u32 cl_idx = lm->client_index; vl_api_l2_macs_event_t *mp = 0; vl_api_registration_t *reg = 0; + u32 bd_index; + static u32 *bd_learn_counts = 0; /* Don't scan the l2 fib if it hasn't been instantiated yet */ if (alloc_arena (h) == 0) return 0.0; + vec_reset_length (bd_learn_counts); + vec_validate (bd_learn_counts, vec_len (l2input_main.bd_configs) - 1); + if (client) { mp = allocate_mac_evt_buf (client, cl_idx); @@ -1069,6 +1094,9 @@ l2fib_scan (vlib_main_t * vm, f64 start_time, u8 event_only) if (delta_t > 20e-6) { vlib_process_suspend (vm, 100e-6); /* suspend for 100 us */ + /* in case a new bd was created while sleeping */ + vec_validate (bd_learn_counts, + vec_len (l2input_main.bd_configs) - 1); last_start = vlib_time_now (vm); accum_t += delta_t; } @@ -1102,7 +1130,10 @@ l2fib_scan (vlib_main_t * vm, f64 start_time, u8 event_only) l2fib_entry_result_t result = {.raw = v->kvp[k].value }; if (!l2fib_entry_result_is_set_AGE_NOT (&result)) - learn_count++; + { + learn_count++; + vec_elt (bd_learn_counts, key.fields.bd_index)++; + } if (client) { @@ -1190,6 +1221,7 @@ l2fib_scan (vlib_main_t * vm, f64 start_time, u8 event_only) kv.key = key.raw; BV (clib_bihash_add_del) (&fm->mac_table, &kv, 0); learn_count--; + vec_elt (bd_learn_counts, key.fields.bd_index)--; /* * Note: we may have just freed the bucket's backing * storage, so check right here... @@ -1205,6 +1237,11 @@ l2fib_scan (vlib_main_t * vm, f64 start_time, u8 event_only) /* keep learn count consistent */ l2learn_main.global_learn_count = learn_count; + vec_foreach_index (bd_index, l2input_main.bd_configs) + { + vec_elt (l2input_main.bd_configs, bd_index).learn_count = + vec_elt (bd_learn_counts, bd_index); + } if (mp) { diff --git a/src/vnet/l2/l2_learn.c b/src/vnet/l2/l2_learn.c index 6f2201b3ade..3b9b1dbcaa9 100644 --- a/src/vnet/l2/l2_learn.c +++ b/src/vnet/l2/l2_learn.c @@ -119,6 +119,8 @@ l2learn_process (vlib_node_runtime_t * node, u32 * count, l2fib_entry_result_t * result0, u16 * next0, u8 timestamp) { + l2_bridge_domain_t *bd_config = + vec_elt_at_index (l2input_main.bd_configs, vnet_buffer (b0)->l2.bd_index); /* Set up the default next node (typically L2FWD) */ *next0 = vnet_l2_feature_next (b0, msm->feat_next_node_index, L2INPUT_FEAT_LEARN); @@ -137,6 +139,8 @@ l2learn_process (vlib_node_runtime_t * node, return; /* Static MAC always age_not */ if (msm->global_learn_count > msm->global_learn_limit) return; /* Above learn limit - do not update */ + if (bd_config->learn_count > bd_config->learn_limit) + return; /* Above bridge domain learn limit - do not update */ /* Limit updates per l2-learn node call to avoid prolonged update burst * as dtime advance over 1 minute mark, unless more than 1 min behind @@ -152,7 +156,8 @@ l2learn_process (vlib_node_runtime_t * node, /* Entry not in L2FIB - add it */ counter_base[L2LEARN_ERROR_MISS] += 1; - if (msm->global_learn_count >= msm->global_learn_limit) + if ((msm->global_learn_count >= msm->global_learn_limit) || + (bd_config->learn_count >= bd_config->learn_limit)) { /* * Global limit reached. Do not learn the mac but forward the packet. @@ -169,7 +174,11 @@ l2learn_process (vlib_node_runtime_t * node, return; /* It is ok to learn */ + /* learn_count variable may have little inaccuracy because they are not + * incremented/decremented with atomic operations */ + /* l2fib_scan is call every 2sec fixing potential inaccuracy */ msm->global_learn_count++; + bd_config->learn_count++; result0->raw = 0; /* clear all fields */ result0->fields.sw_if_index = sw_if_index0; if (msm->client_pid != 0) @@ -208,7 +217,12 @@ l2learn_process (vlib_node_runtime_t * node, if (l2fib_entry_result_is_set_AGE_NOT (result0)) { /* The mac was provisioned */ + /* learn_count variable may have little inaccuracy because they are + * not incremented/decremented with atomic operations */ + /* l2fib_scan is call every 2sec fixing potential inaccuracy */ msm->global_learn_count++; + bd_config->learn_count++; + l2fib_entry_result_clear_AGE_NOT (result0); } if (msm->client_pid != 0) @@ -470,6 +484,11 @@ l2learn_init (vlib_main_t * vm) */ mp->global_learn_limit = L2LEARN_DEFAULT_LIMIT; + /* + * Set the default number of dynamically learned macs to the number + * of buckets. + */ + mp->bd_default_learn_limit = L2LEARN_DEFAULT_LIMIT; return 0; } diff --git a/src/vnet/l2/l2_learn.h b/src/vnet/l2/l2_learn.h index eea34ee83d3..da654e628fc 100644 --- a/src/vnet/l2/l2_learn.h +++ b/src/vnet/l2/l2_learn.h @@ -35,6 +35,9 @@ typedef struct /* maximum number of dynamically learned mac entries */ u32 global_learn_limit; + /* maximum number of dynamically learned mac entries per bridge domain */ + u32 bd_default_learn_limit; + /* client waiting for L2 MAC events for learned and aged MACs */ u32 client_pid; u32 client_index; diff --git a/test/test_l2bd_learnlimit.py b/test/test_l2bd_learnlimit.py index 6a28664e522..e59496eebf0 100644 --- a/test/test_l2bd_learnlimit.py +++ b/test/test_l2bd_learnlimit.py @@ -17,7 +17,7 @@ class TestL2LearnLimit(VppTestCase): @classmethod def setUpClass(self): super(TestL2LearnLimit, self).setUpClass() - self.create_pg_interfaces(range(2)) + self.create_pg_interfaces(range(3)) @classmethod def tearDownClass(cls): @@ -94,6 +94,39 @@ class TestL2LearnLimit(VppTestCase): # check that bd2 was not able to learn self.assertEqual(len(lfs2), 0) + def test_l2bd_learnlimit03(self): + """ L2BD test with bridge domain limit + """ + self.vapi.bridge_domain_set_default_learn_limit(4) + self.vapi.bridge_domain_add_del(bd_id=3) + self.vapi.sw_interface_set_l2_bridge( + self.pg_interfaces[2].sw_if_index, bd_id=3) + + self.vapi.bridge_domain_set_learn_limit(2, 5) + + hosts = self.create_hosts(self.pg_interfaces[1], 20, 2) + fhosts = self.create_hosts(self.pg_interfaces[2], 20, 3) + + # inject 20 mac addresses on bd2 + self.learn_hosts(self.pg_interfaces[1], 2, hosts) + + # inject 20 macs address on bd3 + self.learn_hosts(self.pg_interfaces[2], 3, fhosts) + + lfs1 = self.vapi.l2_fib_table_dump(2) + lfs2 = self.vapi.l2_fib_table_dump(3) + + # check that only 5 macs are learned. + self.assertEqual(len(lfs1), 5) + + # check that only 4 macs are learned. + self.assertEqual(len(lfs2), 4) + + self.vapi.sw_interface_set_l2_bridge( + rx_sw_if_index=self.pg_interfaces[2].sw_if_index, + bd_id=3, enable=0) + self.vapi.bridge_domain_add_del(is_add=0, bd_id=3) + def setUp(self): super(TestL2LearnLimit, self).setUp() @@ -106,13 +139,21 @@ class TestL2LearnLimit(VppTestCase): self.pg_interfaces[1].sw_if_index, bd_id=2) def tearDown(self): - for i in self.pg_interfaces[:2]: - self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index, - bd_id=1, enable=0) + super(TestL2LearnLimit, self).tearDown() + self.vapi.sw_interface_set_l2_bridge( + rx_sw_if_index=self.pg_interfaces[0].sw_if_index, + bd_id=1, enable=0) + self.vapi.sw_interface_set_l2_bridge( + rx_sw_if_index=self.pg_interfaces[1].sw_if_index, + bd_id=2, enable=0) self.vapi.bridge_domain_add_del(bd_id=1, is_add=0) self.vapi.bridge_domain_add_del(bd_id=2, is_add=0) - - super(TestL2LearnLimit, self).tearDown() + i = 0 + while (len(self.vapi.l2_fib_table_dump(bd_id=0xffffffff)) != 0): + time.sleep(1) + i = i + 1 + if i == 30: + self.assertTrue(False) if __name__ == '__main__': |