vlib: improvement to automatic core pinning
[vpp.git] / src / vnet / l2 / l2_learn.c
1 /*
2  * l2_learn.c : layer 2 learning using l2fib
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 <vnet/ethernet/ethernet.h>
21 #include <vlib/cli.h>
22
23 #include <vnet/l2/l2_input.h>
24 #include <vnet/l2/feat_bitmap.h>
25 #include <vnet/l2/l2_fib.h>
26 #include <vnet/l2/l2_learn.h>
27
28 #include <vppinfra/error.h>
29 #include <vppinfra/hash.h>
30
31 #ifndef CLIB_MARCH_VARIANT
32 l2learn_main_t l2learn_main;
33 #endif
34
35 /**
36  * @file
37  * @brief Ethernet Bridge Learning.
38  *
39  * Populate the mac table with entries mapping the packet's source mac + bridge
40  * domain ID to the input sw_if_index.
41  *
42  * Note that learning and forwarding are separate graph nodes. This means that
43  * for a set of packets, all learning is performed first, then all nodes are
44  * forwarded. The forwarding is done based on the end-state of the mac table,
45  * instead of the state after each packet. Thus the forwarding results could
46  * differ in certain cases (mac move tests), but this not expected to cause
47  * problems in real-world networks. It is much simpler to separate learning
48  * and forwarding into separate nodes.
49  */
50
51
52 typedef struct
53 {
54   u8 src[6];
55   u8 dst[6];
56   u32 sw_if_index;
57   u16 bd_index;
58 } l2learn_trace_t;
59
60
61 /* packet trace format function */
62 static u8 *
63 format_l2learn_trace (u8 * s, va_list * args)
64 {
65   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
66   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
67   l2learn_trace_t *t = va_arg (*args, l2learn_trace_t *);
68
69   s = format (s, "l2-learn: sw_if_index %d dst %U src %U bd_index %d",
70               t->sw_if_index,
71               format_ethernet_address, t->dst,
72               format_ethernet_address, t->src, t->bd_index);
73   return s;
74 }
75
76 extern vlib_node_registration_t l2learn_node;
77
78 #define foreach_l2learn_error                           \
79 _(L2LEARN,           "L2 learn packets")                \
80 _(MISS,              "L2 learn misses")                 \
81 _(MAC_MOVE,          "L2 mac moves")                    \
82 _(MAC_MOVE_VIOLATE,  "L2 mac move violations")          \
83 _(LIMIT,             "L2 not learned due to limit")     \
84 _(HIT_UPDATE,        "L2 learn hit updates")            \
85 _(FILTER_DROP,       "L2 filter mac drops")
86
87 typedef enum
88 {
89 #define _(sym,str) L2LEARN_ERROR_##sym,
90   foreach_l2learn_error
91 #undef _
92     L2LEARN_N_ERROR,
93 } l2learn_error_t;
94
95 static char *l2learn_error_strings[] = {
96 #define _(sym,string) string,
97   foreach_l2learn_error
98 #undef _
99 };
100
101 typedef enum
102 {
103   L2LEARN_NEXT_L2FWD,
104   L2LEARN_NEXT_DROP,
105   L2LEARN_N_NEXT,
106 } l2learn_next_t;
107
108
109 /** Perform learning on one packet based on the mac table lookup result. */
110
111 static_always_inline void
112 l2learn_process (vlib_node_runtime_t * node,
113                  l2learn_main_t * msm,
114                  u64 * counter_base,
115                  vlib_buffer_t * b0,
116                  u32 sw_if_index0,
117                  l2fib_entry_key_t * key0,
118                  l2fib_entry_key_t * cached_key,
119                  u32 * count,
120                  l2fib_entry_result_t * result0, u16 * next0, u8 timestamp)
121 {
122   l2_bridge_domain_t *bd_config =
123     vec_elt_at_index (l2input_main.bd_configs, vnet_buffer (b0)->l2.bd_index);
124   /* Set up the default next node (typically L2FWD) */
125   *next0 = vnet_l2_feature_next (b0, msm->feat_next_node_index,
126                                  L2INPUT_FEAT_LEARN);
127
128   /* Check mac table lookup result */
129   if (PREDICT_TRUE (result0->fields.sw_if_index == sw_if_index0))
130     {
131       /* Entry in L2FIB with matching sw_if_index matched - normal fast path */
132       u32 dtime = timestamp - result0->fields.timestamp;
133       u32 dsn = (result0->fields.sn - vnet_buffer (b0)->l2.l2fib_sn);
134       u32 check = (dtime && vnet_buffer (b0)->l2.bd_age) || dsn;
135
136       if (PREDICT_TRUE (check == 0))
137         return;                 /* MAC entry up to date */
138       if (l2fib_entry_result_is_set_AGE_NOT (result0))
139         return;                 /* Static MAC always age_not */
140       if (msm->global_learn_count > msm->global_learn_limit)
141         return;                 /* Above learn limit - do not update */
142       if (bd_config->learn_count > bd_config->learn_limit)
143         return; /* Above bridge domain learn limit - do not update */
144
145       /* Limit updates per l2-learn node call to avoid prolonged update burst
146        * as dtime advance over 1 minute mark, unless more than 1 min behind
147        * or SN obsolete */
148       if ((*count > 2) && (dtime == 1) && (dsn == 0))
149         return;
150
151       counter_base[L2LEARN_ERROR_HIT_UPDATE] += 1;
152       *count += 1;
153     }
154   else if (result0->raw == ~0)
155     {
156       /* Entry not in L2FIB - add it  */
157       counter_base[L2LEARN_ERROR_MISS] += 1;
158
159       if ((msm->global_learn_count >= msm->global_learn_limit) ||
160           (bd_config->learn_count >= bd_config->learn_limit))
161         {
162           /*
163            * Global limit reached. Do not learn the mac but forward the packet.
164            * In the future, limits could also be per-interface or bridge-domain.
165            */
166           counter_base[L2LEARN_ERROR_LIMIT] += 1;
167           return;
168         }
169
170       /* Do not learn if mac is 0 */
171       l2fib_entry_key_t key = *key0;
172       key.fields.bd_index = 0;
173       if (key.raw == 0)
174         return;
175
176       /* It is ok to learn */
177       /* learn_count variable may have little inaccuracy because they are not
178        * incremented/decremented with atomic operations */
179       /* l2fib_scan is call every 2sec fixing potential inaccuracy */
180       msm->global_learn_count++;
181       bd_config->learn_count++;
182       result0->raw = 0;         /* clear all fields */
183       result0->fields.sw_if_index = sw_if_index0;
184       if (msm->client_pid != 0)
185         l2fib_entry_result_set_LRN_EVT (result0);
186       else
187         l2fib_entry_result_clear_LRN_EVT (result0);
188     }
189   else
190     {
191       /* Entry in L2FIB with different sw_if_index - mac move or filter */
192       if (l2fib_entry_result_is_set_FILTER (result0))
193         {
194           ASSERT (result0->fields.sw_if_index == ~0);
195           /* drop packet because lookup matched a filter mac entry */
196           b0->error = node->errors[L2LEARN_ERROR_FILTER_DROP];
197           *next0 = L2LEARN_NEXT_DROP;
198           return;
199         }
200
201       if (l2fib_entry_result_is_set_STATIC (result0))
202         {
203           /*
204            * Don't overwrite a static mac
205            * TODO: Check violation policy. For now drop the packet
206            */
207           b0->error = node->errors[L2LEARN_ERROR_MAC_MOVE_VIOLATE];
208           *next0 = L2LEARN_NEXT_DROP;
209           return;
210         }
211
212       /*
213        * TODO: may want to rate limit mac moves
214        * TODO: check global/bridge domain/interface learn limits
215        */
216       result0->fields.sw_if_index = sw_if_index0;
217       if (l2fib_entry_result_is_set_AGE_NOT (result0))
218         {
219           /* The mac was provisioned */
220           /* learn_count variable may have little inaccuracy because they are
221            * not incremented/decremented with atomic operations */
222           /* l2fib_scan is call every 2sec fixing potential inaccuracy */
223           msm->global_learn_count++;
224           bd_config->learn_count++;
225
226           l2fib_entry_result_clear_AGE_NOT (result0);
227         }
228       if (msm->client_pid != 0)
229         l2fib_entry_result_set_bits (result0,
230                                      (L2FIB_ENTRY_RESULT_FLAG_LRN_EVT |
231                                       L2FIB_ENTRY_RESULT_FLAG_LRN_MOV));
232       else
233         l2fib_entry_result_clear_bits (result0,
234                                        (L2FIB_ENTRY_RESULT_FLAG_LRN_EVT |
235                                         L2FIB_ENTRY_RESULT_FLAG_LRN_MOV));
236       counter_base[L2LEARN_ERROR_MAC_MOVE] += 1;
237     }
238
239   /* Update the entry */
240   result0->fields.timestamp = timestamp;
241   result0->fields.sn = vnet_buffer (b0)->l2.l2fib_sn;
242
243   BVT (clib_bihash_kv) kv;
244   kv.key = key0->raw;
245   kv.value = result0->raw;
246   BV (clib_bihash_add_del) (msm->mac_table, &kv, 1 /* is_add */ );
247
248   /* Invalidate the cache */
249   cached_key->raw = ~0;
250 }
251
252
253 static_always_inline uword
254 l2learn_node_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
255                      vlib_frame_t * frame, int do_trace)
256 {
257   u32 n_left, *from;
258   l2learn_main_t *msm = &l2learn_main;
259   vlib_node_t *n = vlib_get_node (vm, l2learn_node.index);
260   u32 node_counter_base_index = n->error_heap_index;
261   vlib_error_main_t *em = &vm->error_main;
262   l2fib_entry_key_t cached_key;
263   l2fib_entry_result_t cached_result;
264   u8 timestamp = (u8) (vlib_time_now (vm) / 60);
265   u32 count = 0;
266   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
267   u16 nexts[VLIB_FRAME_SIZE], *next;
268
269   from = vlib_frame_vector_args (frame);
270   n_left = frame->n_vectors;    /* number of packets to process */
271   vlib_get_buffers (vm, from, bufs, n_left);
272   next = nexts;
273   b = bufs;
274
275   /* Clear the one-entry cache in case mac table was updated */
276   cached_key.raw = ~0;
277   cached_result.raw = ~0;       /* warning be gone */
278
279   while (n_left > 8)
280     {
281       u32 sw_if_index0, sw_if_index1, sw_if_index2, sw_if_index3;
282       const ethernet_header_t *h0, *h1, *h2, *h3;
283       l2fib_entry_key_t key0, key1, key2, key3;
284       l2fib_entry_result_t result0, result1, result2, result3;
285
286       /* Prefetch next iteration. */
287       {
288         /* buffer header is read and written, so use LOAD
289          * prefetch */
290         vlib_prefetch_buffer_header (b[4], LOAD);
291         vlib_prefetch_buffer_header (b[5], LOAD);
292         vlib_prefetch_buffer_header (b[6], LOAD);
293         vlib_prefetch_buffer_header (b[7], LOAD);
294
295         clib_prefetch_load (b[4]->data);
296         clib_prefetch_load (b[5]->data);
297         clib_prefetch_load (b[6]->data);
298         clib_prefetch_load (b[7]->data);
299       }
300
301       /* RX interface handles */
302       sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
303       sw_if_index1 = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
304       sw_if_index2 = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
305       sw_if_index3 = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
306
307       /* Process 4 x pkts */
308
309       h0 = vlib_buffer_get_current (b[0]);
310       h1 = vlib_buffer_get_current (b[1]);
311       h2 = vlib_buffer_get_current (b[2]);
312       h3 = vlib_buffer_get_current (b[3]);
313
314       if (do_trace)
315         {
316           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
317             {
318               l2learn_trace_t *t =
319                 vlib_add_trace (vm, node, b[0], sizeof (*t));
320               t->sw_if_index = sw_if_index0;
321               t->bd_index = vnet_buffer (b[0])->l2.bd_index;
322               clib_memcpy_fast (t->src, h0->src_address, 6);
323               clib_memcpy_fast (t->dst, h0->dst_address, 6);
324             }
325           if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
326             {
327               l2learn_trace_t *t =
328                 vlib_add_trace (vm, node, b[1], sizeof (*t));
329               t->sw_if_index = sw_if_index1;
330               t->bd_index = vnet_buffer (b[1])->l2.bd_index;
331               clib_memcpy_fast (t->src, h1->src_address, 6);
332               clib_memcpy_fast (t->dst, h1->dst_address, 6);
333             }
334           if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
335             {
336               l2learn_trace_t *t =
337                 vlib_add_trace (vm, node, b[2], sizeof (*t));
338               t->sw_if_index = sw_if_index2;
339               t->bd_index = vnet_buffer (b[2])->l2.bd_index;
340               clib_memcpy_fast (t->src, h2->src_address, 6);
341               clib_memcpy_fast (t->dst, h2->dst_address, 6);
342             }
343           if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
344             {
345               l2learn_trace_t *t =
346                 vlib_add_trace (vm, node, b[3], sizeof (*t));
347               t->sw_if_index = sw_if_index3;
348               t->bd_index = vnet_buffer (b[3])->l2.bd_index;
349               clib_memcpy_fast (t->src, h3->src_address, 6);
350               clib_memcpy_fast (t->dst, h3->dst_address, 6);
351             }
352         }
353
354       /* process 4 pkts */
355       vlib_node_increment_counter (vm, l2learn_node.index,
356                                    L2LEARN_ERROR_L2LEARN, 4);
357
358       l2fib_lookup_4 (msm->mac_table, &cached_key, &cached_result,
359                       h0->src_address,
360                       h1->src_address,
361                       h2->src_address,
362                       h3->src_address,
363                       vnet_buffer (b[0])->l2.bd_index,
364                       vnet_buffer (b[1])->l2.bd_index,
365                       vnet_buffer (b[2])->l2.bd_index,
366                       vnet_buffer (b[3])->l2.bd_index,
367                       &key0, &key1, &key2, &key3,
368                       &result0, &result1, &result2, &result3);
369
370       l2learn_process (node, msm, &em->counters[node_counter_base_index],
371                        b[0], sw_if_index0, &key0, &cached_key,
372                        &count, &result0, next, timestamp);
373
374       l2learn_process (node, msm, &em->counters[node_counter_base_index],
375                        b[1], sw_if_index1, &key1, &cached_key,
376                        &count, &result1, next + 1, timestamp);
377
378       l2learn_process (node, msm, &em->counters[node_counter_base_index],
379                        b[2], sw_if_index2, &key2, &cached_key,
380                        &count, &result2, next + 2, timestamp);
381
382       l2learn_process (node, msm, &em->counters[node_counter_base_index],
383                        b[3], sw_if_index3, &key3, &cached_key,
384                        &count, &result3, next + 3, timestamp);
385
386       next += 4;
387       b += 4;
388       n_left -= 4;
389     }
390
391   while (n_left > 0)
392     {
393       u32 sw_if_index0;
394       ethernet_header_t *h0;
395       l2fib_entry_key_t key0;
396       l2fib_entry_result_t result0;
397
398       sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
399
400       h0 = vlib_buffer_get_current (b[0]);
401
402       if (do_trace && PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
403         {
404           l2learn_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
405           t->sw_if_index = sw_if_index0;
406           t->bd_index = vnet_buffer (b[0])->l2.bd_index;
407           clib_memcpy_fast (t->src, h0->src_address, 6);
408           clib_memcpy_fast (t->dst, h0->dst_address, 6);
409         }
410
411       /* process 1 pkt */
412       vlib_node_increment_counter (vm, l2learn_node.index,
413                                    L2LEARN_ERROR_L2LEARN, 1);
414
415
416       l2fib_lookup_1 (msm->mac_table, &cached_key, &cached_result,
417                       h0->src_address, vnet_buffer (b[0])->l2.bd_index,
418                       &key0, &result0);
419
420       l2learn_process (node, msm, &em->counters[node_counter_base_index],
421                        b[0], sw_if_index0, &key0, &cached_key,
422                        &count, &result0, next, timestamp);
423
424       next += 1;
425       b += 1;
426       n_left -= 1;
427     }
428
429   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
430
431   return frame->n_vectors;
432 }
433
434 VLIB_NODE_FN (l2learn_node) (vlib_main_t * vm,
435                              vlib_node_runtime_t * node, vlib_frame_t * frame)
436 {
437   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
438     return l2learn_node_inline (vm, node, frame, 1 /* do_trace */ );
439   return l2learn_node_inline (vm, node, frame, 0 /* do_trace */ );
440 }
441
442 VLIB_REGISTER_NODE (l2learn_node) = {
443   .name = "l2-learn",
444   .vector_size = sizeof (u32),
445   .format_trace = format_l2learn_trace,
446   .type = VLIB_NODE_TYPE_INTERNAL,
447
448   .n_errors = ARRAY_LEN(l2learn_error_strings),
449   .error_strings = l2learn_error_strings,
450
451   .n_next_nodes = L2LEARN_N_NEXT,
452
453   /* edit / add dispositions here */
454   .next_nodes = {
455         [L2LEARN_NEXT_DROP] = "error-drop",
456         [L2LEARN_NEXT_L2FWD] = "l2-fwd",
457   },
458 };
459
460 #ifndef CLIB_MARCH_VARIANT
461 clib_error_t *
462 l2learn_init (vlib_main_t * vm)
463 {
464   l2learn_main_t *mp = &l2learn_main;
465
466   mp->vlib_main = vm;
467   mp->vnet_main = vnet_get_main ();
468
469   /* Initialize the feature next-node indexes */
470   feat_bitmap_init_next_nodes (vm,
471                                l2learn_node.index,
472                                L2INPUT_N_FEAT,
473                                l2input_get_feat_names (),
474                                mp->feat_next_node_index);
475
476   /* init the hash table ptr */
477   mp->mac_table = get_mac_table ();
478
479   /*
480    * Set the default number of dynamically learned macs to the number
481    * of buckets.
482    */
483   mp->global_learn_limit = L2LEARN_DEFAULT_LIMIT;
484
485   /*
486    * Set the default number of dynamically learned macs to the number
487    * of buckets.
488    */
489   mp->bd_default_learn_limit = L2LEARN_DEFAULT_LIMIT;
490   return 0;
491 }
492
493 VLIB_INIT_FUNCTION (l2learn_init);
494
495
496 /**
497  * Set subinterface learn enable/disable.
498  * The CLI format is:
499  *    set interface l2 learn <interface> [disable]
500  */
501 static clib_error_t *
502 int_learn (vlib_main_t * vm,
503            unformat_input_t * input, vlib_cli_command_t * cmd)
504 {
505   vnet_main_t *vnm = vnet_get_main ();
506   clib_error_t *error = 0;
507   u32 sw_if_index;
508   u32 enable;
509
510   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
511     {
512       error = clib_error_return (0, "unknown interface `%U'",
513                                  format_unformat_error, input);
514       goto done;
515     }
516
517   enable = 1;
518   if (unformat (input, "disable"))
519     {
520       enable = 0;
521     }
522
523   /* set the interface flag */
524   l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_LEARN, enable);
525
526 done:
527   return error;
528 }
529
530 /*?
531  * Layer 2 learning can be enabled and disabled on each
532  * interface and on each bridge-domain. Use this command to
533  * manage interfaces. It is enabled by default.
534  *
535  * @cliexpar
536  * Example of how to enable learning:
537  * @cliexcmd{set interface l2 learn GigabitEthernet0/8/0}
538  * Example of how to disable learning:
539  * @cliexcmd{set interface l2 learn GigabitEthernet0/8/0 disable}
540 ?*/
541 VLIB_CLI_COMMAND (int_learn_cli, static) = {
542   .path = "set interface l2 learn",
543   .short_help = "set interface l2 learn <interface> [disable]",
544   .function = int_learn,
545 };
546
547
548 static clib_error_t *
549 l2learn_config (vlib_main_t * vm, unformat_input_t * input)
550 {
551   l2learn_main_t *mp = &l2learn_main;
552
553   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
554     {
555       if (unformat (input, "limit %d", &mp->global_learn_limit))
556         ;
557
558       else
559         return clib_error_return (0, "unknown input `%U'",
560                                   format_unformat_error, input);
561     }
562
563   return 0;
564 }
565
566 VLIB_CONFIG_FUNCTION (l2learn_config, "l2learn");
567
568 #endif
569
570
571 /*
572  * fd.io coding-style-patch-verification: ON
573  *
574  * Local Variables:
575  * eval: (c-set-style "gnu")
576  * End:
577  */