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