misc: Purge unused pg includes
[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   /* Set up the default next node (typically L2FWD) */
123   *next0 = vnet_l2_feature_next (b0, msm->feat_next_node_index,
124                                  L2INPUT_FEAT_LEARN);
125
126   /* Check mac table lookup result */
127   if (PREDICT_TRUE (result0->fields.sw_if_index == sw_if_index0))
128     {
129       /* Entry in L2FIB with matching sw_if_index matched - normal fast path */
130       u32 dtime = timestamp - result0->fields.timestamp;
131       u32 dsn = result0->fields.sn.as_u16 - vnet_buffer (b0)->l2.l2fib_sn;
132       u32 check = (dtime && vnet_buffer (b0)->l2.bd_age) || dsn;
133
134       if (PREDICT_TRUE (check == 0))
135         return;                 /* MAC entry up to date */
136       if (l2fib_entry_result_is_set_AGE_NOT (result0))
137         return;                 /* Static MAC always age_not */
138       if (msm->global_learn_count > msm->global_learn_limit)
139         return;                 /* Above learn limit - do not update */
140
141       /* Limit updates per l2-learn node call to avoid prolonged update burst
142        * as dtime advance over 1 minute mark, unless more than 1 min behind
143        * or SN obsolete */
144       if ((*count > 2) && (dtime == 1) && (dsn == 0))
145         return;
146
147       counter_base[L2LEARN_ERROR_HIT_UPDATE] += 1;
148       *count += 1;
149     }
150   else if (result0->raw == ~0)
151     {
152       /* Entry not in L2FIB - add it  */
153       counter_base[L2LEARN_ERROR_MISS] += 1;
154
155       if (msm->global_learn_count >= msm->global_learn_limit)
156         {
157           /*
158            * Global limit reached. Do not learn the mac but forward the packet.
159            * In the future, limits could also be per-interface or bridge-domain.
160            */
161           counter_base[L2LEARN_ERROR_LIMIT] += 1;
162           return;
163         }
164
165       /* Do not learn if mac is 0 */
166       l2fib_entry_key_t key = *key0;
167       key.fields.bd_index = 0;
168       if (key.raw == 0)
169         return;
170
171       /* It is ok to learn */
172       msm->global_learn_count++;
173       result0->raw = 0;         /* clear all fields */
174       result0->fields.sw_if_index = sw_if_index0;
175       if (msm->client_pid != 0)
176         l2fib_entry_result_set_LRN_EVT (result0);
177       else
178         l2fib_entry_result_clear_LRN_EVT (result0);
179     }
180   else
181     {
182       /* Entry in L2FIB with different sw_if_index - mac move or filter */
183       if (l2fib_entry_result_is_set_FILTER (result0))
184         {
185           ASSERT (result0->fields.sw_if_index == ~0);
186           /* drop packet because lookup matched a filter mac entry */
187           b0->error = node->errors[L2LEARN_ERROR_FILTER_DROP];
188           *next0 = L2LEARN_NEXT_DROP;
189           return;
190         }
191
192       if (l2fib_entry_result_is_set_STATIC (result0))
193         {
194           /*
195            * Don't overwrite a static mac
196            * TODO: Check violation policy. For now drop the packet
197            */
198           b0->error = node->errors[L2LEARN_ERROR_MAC_MOVE_VIOLATE];
199           *next0 = L2LEARN_NEXT_DROP;
200           return;
201         }
202
203       /*
204        * TODO: may want to rate limit mac moves
205        * TODO: check global/bridge domain/interface learn limits
206        */
207       result0->fields.sw_if_index = sw_if_index0;
208       if (l2fib_entry_result_is_set_AGE_NOT (result0))
209         {
210           /* The mac was provisioned */
211           msm->global_learn_count++;
212           l2fib_entry_result_clear_AGE_NOT (result0);
213         }
214       if (msm->client_pid != 0)
215         l2fib_entry_result_set_bits (result0,
216                                      (L2FIB_ENTRY_RESULT_FLAG_LRN_EVT |
217                                       L2FIB_ENTRY_RESULT_FLAG_LRN_MOV));
218       else
219         l2fib_entry_result_clear_bits (result0,
220                                        (L2FIB_ENTRY_RESULT_FLAG_LRN_EVT |
221                                         L2FIB_ENTRY_RESULT_FLAG_LRN_MOV));
222       counter_base[L2LEARN_ERROR_MAC_MOVE] += 1;
223     }
224
225   /* Update the entry */
226   result0->fields.timestamp = timestamp;
227   result0->fields.sn.as_u16 = vnet_buffer (b0)->l2.l2fib_sn;
228
229   BVT (clib_bihash_kv) kv;
230   kv.key = key0->raw;
231   kv.value = result0->raw;
232   BV (clib_bihash_add_del) (msm->mac_table, &kv, 1 /* is_add */ );
233
234   /* Invalidate the cache */
235   cached_key->raw = ~0;
236 }
237
238
239 static_always_inline uword
240 l2learn_node_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
241                      vlib_frame_t * frame, int do_trace)
242 {
243   u32 n_left, *from;
244   l2learn_main_t *msm = &l2learn_main;
245   vlib_node_t *n = vlib_get_node (vm, l2learn_node.index);
246   u32 node_counter_base_index = n->error_heap_index;
247   vlib_error_main_t *em = &vm->error_main;
248   l2fib_entry_key_t cached_key;
249   l2fib_entry_result_t cached_result;
250   u8 timestamp = (u8) (vlib_time_now (vm) / 60);
251   u32 count = 0;
252   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
253   u16 nexts[VLIB_FRAME_SIZE], *next;
254
255   from = vlib_frame_vector_args (frame);
256   n_left = frame->n_vectors;    /* number of packets to process */
257   vlib_get_buffers (vm, from, bufs, n_left);
258   next = nexts;
259   b = bufs;
260
261   /* Clear the one-entry cache in case mac table was updated */
262   cached_key.raw = ~0;
263   cached_result.raw = ~0;       /* warning be gone */
264
265   while (n_left > 8)
266     {
267       u32 sw_if_index0, sw_if_index1, sw_if_index2, sw_if_index3;
268       const ethernet_header_t *h0, *h1, *h2, *h3;
269       l2fib_entry_key_t key0, key1, key2, key3;
270       l2fib_entry_result_t result0, result1, result2, result3;
271
272       /* Prefetch next iteration. */
273       {
274         /* buffer header is read and written, so use LOAD
275          * prefetch */
276         vlib_prefetch_buffer_header (b[4], LOAD);
277         vlib_prefetch_buffer_header (b[5], LOAD);
278         vlib_prefetch_buffer_header (b[6], LOAD);
279         vlib_prefetch_buffer_header (b[7], LOAD);
280
281         CLIB_PREFETCH (b[4]->data, CLIB_CACHE_LINE_BYTES, LOAD);
282         CLIB_PREFETCH (b[5]->data, CLIB_CACHE_LINE_BYTES, LOAD);
283         CLIB_PREFETCH (b[6]->data, CLIB_CACHE_LINE_BYTES, LOAD);
284         CLIB_PREFETCH (b[7]->data, CLIB_CACHE_LINE_BYTES, LOAD);
285       }
286
287       /* RX interface handles */
288       sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
289       sw_if_index1 = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
290       sw_if_index2 = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
291       sw_if_index3 = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
292
293       /* Process 4 x pkts */
294
295       h0 = vlib_buffer_get_current (b[0]);
296       h1 = vlib_buffer_get_current (b[1]);
297       h2 = vlib_buffer_get_current (b[2]);
298       h3 = vlib_buffer_get_current (b[3]);
299
300       if (do_trace)
301         {
302           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
303             {
304               l2learn_trace_t *t =
305                 vlib_add_trace (vm, node, b[0], sizeof (*t));
306               t->sw_if_index = sw_if_index0;
307               t->bd_index = vnet_buffer (b[0])->l2.bd_index;
308               clib_memcpy_fast (t->src, h0->src_address, 6);
309               clib_memcpy_fast (t->dst, h0->dst_address, 6);
310             }
311           if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
312             {
313               l2learn_trace_t *t =
314                 vlib_add_trace (vm, node, b[1], sizeof (*t));
315               t->sw_if_index = sw_if_index1;
316               t->bd_index = vnet_buffer (b[1])->l2.bd_index;
317               clib_memcpy_fast (t->src, h1->src_address, 6);
318               clib_memcpy_fast (t->dst, h1->dst_address, 6);
319             }
320           if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
321             {
322               l2learn_trace_t *t =
323                 vlib_add_trace (vm, node, b[2], sizeof (*t));
324               t->sw_if_index = sw_if_index2;
325               t->bd_index = vnet_buffer (b[2])->l2.bd_index;
326               clib_memcpy_fast (t->src, h2->src_address, 6);
327               clib_memcpy_fast (t->dst, h2->dst_address, 6);
328             }
329           if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
330             {
331               l2learn_trace_t *t =
332                 vlib_add_trace (vm, node, b[3], sizeof (*t));
333               t->sw_if_index = sw_if_index3;
334               t->bd_index = vnet_buffer (b[3])->l2.bd_index;
335               clib_memcpy_fast (t->src, h3->src_address, 6);
336               clib_memcpy_fast (t->dst, h3->dst_address, 6);
337             }
338         }
339
340       /* process 4 pkts */
341       vlib_node_increment_counter (vm, l2learn_node.index,
342                                    L2LEARN_ERROR_L2LEARN, 4);
343
344       l2fib_lookup_4 (msm->mac_table, &cached_key, &cached_result,
345                       h0->src_address,
346                       h1->src_address,
347                       h2->src_address,
348                       h3->src_address,
349                       vnet_buffer (b[0])->l2.bd_index,
350                       vnet_buffer (b[1])->l2.bd_index,
351                       vnet_buffer (b[2])->l2.bd_index,
352                       vnet_buffer (b[3])->l2.bd_index,
353                       &key0, &key1, &key2, &key3,
354                       &result0, &result1, &result2, &result3);
355
356       l2learn_process (node, msm, &em->counters[node_counter_base_index],
357                        b[0], sw_if_index0, &key0, &cached_key,
358                        &count, &result0, next, timestamp);
359
360       l2learn_process (node, msm, &em->counters[node_counter_base_index],
361                        b[1], sw_if_index1, &key1, &cached_key,
362                        &count, &result1, next + 1, timestamp);
363
364       l2learn_process (node, msm, &em->counters[node_counter_base_index],
365                        b[2], sw_if_index2, &key2, &cached_key,
366                        &count, &result2, next + 2, timestamp);
367
368       l2learn_process (node, msm, &em->counters[node_counter_base_index],
369                        b[3], sw_if_index3, &key3, &cached_key,
370                        &count, &result3, next + 3, timestamp);
371
372       next += 4;
373       b += 4;
374       n_left -= 4;
375     }
376
377   while (n_left > 0)
378     {
379       u32 sw_if_index0;
380       ethernet_header_t *h0;
381       l2fib_entry_key_t key0;
382       l2fib_entry_result_t result0;
383
384       sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
385
386       h0 = vlib_buffer_get_current (b[0]);
387
388       if (do_trace && PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
389         {
390           l2learn_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
391           t->sw_if_index = sw_if_index0;
392           t->bd_index = vnet_buffer (b[0])->l2.bd_index;
393           clib_memcpy_fast (t->src, h0->src_address, 6);
394           clib_memcpy_fast (t->dst, h0->dst_address, 6);
395         }
396
397       /* process 1 pkt */
398       vlib_node_increment_counter (vm, l2learn_node.index,
399                                    L2LEARN_ERROR_L2LEARN, 1);
400
401
402       l2fib_lookup_1 (msm->mac_table, &cached_key, &cached_result,
403                       h0->src_address, vnet_buffer (b[0])->l2.bd_index,
404                       &key0, &result0);
405
406       l2learn_process (node, msm, &em->counters[node_counter_base_index],
407                        b[0], sw_if_index0, &key0, &cached_key,
408                        &count, &result0, next, timestamp);
409
410       next += 1;
411       b += 1;
412       n_left -= 1;
413     }
414
415   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
416
417   return frame->n_vectors;
418 }
419
420 VLIB_NODE_FN (l2learn_node) (vlib_main_t * vm,
421                              vlib_node_runtime_t * node, vlib_frame_t * frame)
422 {
423   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
424     return l2learn_node_inline (vm, node, frame, 1 /* do_trace */ );
425   return l2learn_node_inline (vm, node, frame, 0 /* do_trace */ );
426 }
427
428 /* *INDENT-OFF* */
429 VLIB_REGISTER_NODE (l2learn_node) = {
430   .name = "l2-learn",
431   .vector_size = sizeof (u32),
432   .format_trace = format_l2learn_trace,
433   .type = VLIB_NODE_TYPE_INTERNAL,
434
435   .n_errors = ARRAY_LEN(l2learn_error_strings),
436   .error_strings = l2learn_error_strings,
437
438   .n_next_nodes = L2LEARN_N_NEXT,
439
440   /* edit / add dispositions here */
441   .next_nodes = {
442         [L2LEARN_NEXT_DROP] = "error-drop",
443         [L2LEARN_NEXT_L2FWD] = "l2-fwd",
444   },
445 };
446 /* *INDENT-ON* */
447
448 #ifndef CLIB_MARCH_VARIANT
449 clib_error_t *
450 l2learn_init (vlib_main_t * vm)
451 {
452   l2learn_main_t *mp = &l2learn_main;
453
454   mp->vlib_main = vm;
455   mp->vnet_main = vnet_get_main ();
456
457   /* Initialize the feature next-node indexes */
458   feat_bitmap_init_next_nodes (vm,
459                                l2learn_node.index,
460                                L2INPUT_N_FEAT,
461                                l2input_get_feat_names (),
462                                mp->feat_next_node_index);
463
464   /* init the hash table ptr */
465   mp->mac_table = get_mac_table ();
466
467   /*
468    * Set the default number of dynamically learned macs to the number
469    * of buckets.
470    */
471   mp->global_learn_limit = L2LEARN_DEFAULT_LIMIT;
472
473   return 0;
474 }
475
476 VLIB_INIT_FUNCTION (l2learn_init);
477
478
479 /**
480  * Set subinterface learn enable/disable.
481  * The CLI format is:
482  *    set interface l2 learn <interface> [disable]
483  */
484 static clib_error_t *
485 int_learn (vlib_main_t * vm,
486            unformat_input_t * input, vlib_cli_command_t * cmd)
487 {
488   vnet_main_t *vnm = vnet_get_main ();
489   clib_error_t *error = 0;
490   u32 sw_if_index;
491   u32 enable;
492
493   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
494     {
495       error = clib_error_return (0, "unknown interface `%U'",
496                                  format_unformat_error, input);
497       goto done;
498     }
499
500   enable = 1;
501   if (unformat (input, "disable"))
502     {
503       enable = 0;
504     }
505
506   /* set the interface flag */
507   l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_LEARN, enable);
508
509 done:
510   return error;
511 }
512
513 /*?
514  * Layer 2 learning can be enabled and disabled on each
515  * interface and on each bridge-domain. Use this command to
516  * manage interfaces. It is enabled by default.
517  *
518  * @cliexpar
519  * Example of how to enable learning:
520  * @cliexcmd{set interface l2 learn GigabitEthernet0/8/0}
521  * Example of how to disable learning:
522  * @cliexcmd{set interface l2 learn GigabitEthernet0/8/0 disable}
523 ?*/
524 /* *INDENT-OFF* */
525 VLIB_CLI_COMMAND (int_learn_cli, static) = {
526   .path = "set interface l2 learn",
527   .short_help = "set interface l2 learn <interface> [disable]",
528   .function = int_learn,
529 };
530 /* *INDENT-ON* */
531
532
533 static clib_error_t *
534 l2learn_config (vlib_main_t * vm, unformat_input_t * input)
535 {
536   l2learn_main_t *mp = &l2learn_main;
537
538   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
539     {
540       if (unformat (input, "limit %d", &mp->global_learn_limit))
541         ;
542
543       else
544         return clib_error_return (0, "unknown input `%U'",
545                                   format_unformat_error, input);
546     }
547
548   return 0;
549 }
550
551 VLIB_CONFIG_FUNCTION (l2learn_config, "l2learn");
552
553 #endif
554
555
556 /*
557  * fd.io coding-style-patch-verification: ON
558  *
559  * Local Variables:
560  * eval: (c-set-style "gnu")
561  * End:
562  */