L2 BD: introduce a BD interface on which to send UU packets
[vpp.git] / src / vnet / l2 / l2_fwd.c
1 /*
2  * l2_fwd.c : layer 2 forwarding 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/pg/pg.h>
21 #include <vnet/ethernet/ethernet.h>
22 #include <vlib/cli.h>
23
24 #include <vnet/l2/l2_input.h>
25 #include <vnet/l2/l2_bvi.h>
26 #include <vnet/l2/l2_fwd.h>
27 #include <vnet/l2/l2_fib.h>
28 #include <vnet/l2/feat_bitmap.h>
29
30 #include <vppinfra/error.h>
31 #include <vppinfra/hash.h>
32 #include <vppinfra/sparse_vec.h>
33
34
35 /**
36  * @file
37  * @brief Ethernet Forwarding.
38  *
39  * Code in this file handles forwarding Layer 2 packets. This file calls
40  * the FIB lookup, packet learning and the packet flooding as necessary.
41  * Packet is then sent to the next graph node.
42  */
43
44 typedef struct
45 {
46
47   /* Hash table */
48   BVT (clib_bihash) * mac_table;
49
50   /* next node index for the L3 input node of each ethertype */
51   next_by_ethertype_t l3_next;
52
53   /* Next nodes for each feature */
54   u32 feat_next_node_index[32];
55
56   /* convenience variables */
57   vlib_main_t *vlib_main;
58   vnet_main_t *vnet_main;
59 } l2fwd_main_t;
60
61 typedef struct
62 {
63   /* per-pkt trace data */
64   u8 src[6];
65   u8 dst[6];
66   u32 sw_if_index;
67   u16 bd_index;
68 } l2fwd_trace_t;
69
70 /* packet trace format function */
71 static u8 *
72 format_l2fwd_trace (u8 * s, va_list * args)
73 {
74   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
75   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
76   l2fwd_trace_t *t = va_arg (*args, l2fwd_trace_t *);
77
78   s = format (s, "l2-fwd:   sw_if_index %d dst %U src %U bd_index %d",
79               t->sw_if_index,
80               format_ethernet_address, t->dst,
81               format_ethernet_address, t->src, t->bd_index);
82   return s;
83 }
84
85 #ifndef CLIB_MARCH_VARIANT
86 l2fwd_main_t l2fwd_main;
87 #else
88 extern l2fwd_main_t l2fwd_main;
89 #endif
90
91 static vlib_node_registration_t l2fwd_node;
92
93 #define foreach_l2fwd_error                             \
94 _(L2FWD,         "L2 forward packets")                  \
95 _(FLOOD,         "L2 forward misses")                   \
96 _(HIT,           "L2 forward hits")                     \
97 _(BVI_BAD_MAC,   "BVI L3 MAC mismatch")                 \
98 _(BVI_ETHERTYPE, "BVI packet with unhandled ethertype") \
99 _(FILTER_DROP,   "Filter Mac Drop")                     \
100 _(REFLECT_DROP,  "Reflection Drop")                     \
101 _(STALE_DROP,    "Stale entry Drop")
102
103 typedef enum
104 {
105 #define _(sym,str) L2FWD_ERROR_##sym,
106   foreach_l2fwd_error
107 #undef _
108     L2FWD_N_ERROR,
109 } l2fwd_error_t;
110
111 static char *l2fwd_error_strings[] = {
112 #define _(sym,string) string,
113   foreach_l2fwd_error
114 #undef _
115 };
116
117 typedef enum
118 {
119   L2FWD_NEXT_L2_OUTPUT,
120   L2FWD_NEXT_DROP,
121   L2FWD_N_NEXT,
122 } l2fwd_next_t;
123
124 /** Forward one packet based on the mac table lookup result. */
125
126 static_always_inline void
127 l2fwd_process (vlib_main_t * vm,
128                vlib_node_runtime_t * node,
129                l2fwd_main_t * msm,
130                vlib_error_main_t * em,
131                vlib_buffer_t * b0,
132                u32 sw_if_index0, l2fib_entry_result_t * result0, u16 * next0)
133 {
134   int try_flood = result0->raw == ~0;
135   int flood_error;
136
137   if (PREDICT_FALSE (try_flood))
138     {
139       flood_error = L2FWD_ERROR_FLOOD;
140     }
141   else
142     {
143       /* lookup hit, forward packet  */
144 #ifdef COUNTERS
145       em->counters[node_counter_base_index + L2FWD_ERROR_HIT] += 1;
146 #endif
147
148       vnet_buffer (b0)->sw_if_index[VLIB_TX] = result0->fields.sw_if_index;
149       *next0 = L2FWD_NEXT_L2_OUTPUT;
150       int l2fib_seq_num_valid = 1;
151
152       /* check l2fib seq num for stale entries */
153       if (!l2fib_entry_result_is_set_AGE_NOT (result0))
154         {
155           l2fib_seq_num_t in_sn = {.as_u16 = vnet_buffer (b0)->l2.l2fib_sn };
156           l2fib_seq_num_t expected_sn = {
157             .bd = in_sn.bd,
158             .swif = *l2fib_swif_seq_num (result0->fields.sw_if_index),
159           };
160           l2fib_seq_num_valid =
161             expected_sn.as_u16 == result0->fields.sn.as_u16;
162         }
163
164       if (PREDICT_FALSE (!l2fib_seq_num_valid))
165         {
166           flood_error = L2FWD_ERROR_STALE_DROP;
167           try_flood = 1;
168         }
169       /* perform reflection check */
170       else if (PREDICT_FALSE (sw_if_index0 == result0->fields.sw_if_index))
171         {
172           b0->error = node->errors[L2FWD_ERROR_REFLECT_DROP];
173           *next0 = L2FWD_NEXT_DROP;
174         }
175       /* perform filter check */
176       else if (PREDICT_FALSE (l2fib_entry_result_is_set_FILTER (result0)))
177         {
178           b0->error = node->errors[L2FWD_ERROR_FILTER_DROP];
179           *next0 = L2FWD_NEXT_DROP;
180         }
181       /* perform BVI check */
182       else if (PREDICT_FALSE (l2fib_entry_result_is_set_BVI (result0)))
183         {
184           u32 rc;
185           rc = l2_to_bvi (vm,
186                           msm->vnet_main,
187                           b0,
188                           vnet_buffer (b0)->sw_if_index[VLIB_TX],
189                           &msm->l3_next, next0);
190
191           if (PREDICT_FALSE (rc))
192             {
193               if (rc == TO_BVI_ERR_BAD_MAC)
194                 {
195                   b0->error = node->errors[L2FWD_ERROR_BVI_BAD_MAC];
196                   *next0 = L2FWD_NEXT_DROP;
197                 }
198               else if (rc == TO_BVI_ERR_ETHERTYPE)
199                 {
200                   b0->error = node->errors[L2FWD_ERROR_BVI_ETHERTYPE];
201                   *next0 = L2FWD_NEXT_DROP;
202                 }
203             }
204         }
205     }
206
207   /* flood */
208   if (PREDICT_FALSE (try_flood))
209     {
210       /*
211        * lookup miss, so flood which is typically the next feature
212        * unless some other feature is inserted before uu_flood
213        */
214       if (vnet_buffer (b0)->l2.feature_bitmap &
215           (L2INPUT_FEAT_UU_FLOOD | L2INPUT_FEAT_UU_FWD))
216         {
217           *next0 = vnet_l2_feature_next (b0, msm->feat_next_node_index,
218                                          L2INPUT_FEAT_FWD);
219         }
220       else
221         {
222           /* Flooding is disabled */
223           b0->error = node->errors[flood_error];
224           *next0 = L2FWD_NEXT_DROP;
225         }
226     }
227 }
228
229
230 static_always_inline uword
231 l2fwd_node_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
232                    vlib_frame_t * frame, int do_trace)
233 {
234   u32 n_left, *from;
235   l2fwd_main_t *msm = &l2fwd_main;
236   vlib_node_t *n = vlib_get_node (vm, l2fwd_node.index);
237   CLIB_UNUSED (u32 node_counter_base_index) = n->error_heap_index;
238   vlib_error_main_t *em = &vm->error_main;
239   l2fib_entry_key_t cached_key;
240   l2fib_entry_result_t cached_result;
241   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
242   u16 nexts[VLIB_FRAME_SIZE], *next;
243
244   /* Clear the one-entry cache in case mac table was updated */
245   cached_key.raw = ~0;
246   cached_result.raw = ~0;
247
248   from = vlib_frame_vector_args (frame);
249   n_left = frame->n_vectors;    /* number of packets to process */
250   vlib_get_buffers (vm, from, bufs, n_left);
251   next = nexts;
252   b = bufs;
253
254   while (n_left >= 8)
255     {
256       u32 sw_if_index0, sw_if_index1, sw_if_index2, sw_if_index3;
257       const ethernet_header_t *h0, *h1, *h2, *h3;
258       l2fib_entry_key_t key0, key1, key2, key3;
259       l2fib_entry_result_t result0, result1, result2, result3;
260       u32 bucket0, bucket1, bucket2, bucket3;
261
262       /* Prefetch next iteration. */
263       {
264         vlib_prefetch_buffer_header (b[4], LOAD);
265         vlib_prefetch_buffer_header (b[5], LOAD);
266         vlib_prefetch_buffer_header (b[6], LOAD);
267         vlib_prefetch_buffer_header (b[7], LOAD);
268
269         CLIB_PREFETCH (b[4]->data, CLIB_CACHE_LINE_BYTES, LOAD);
270         CLIB_PREFETCH (b[5]->data, CLIB_CACHE_LINE_BYTES, LOAD);
271         CLIB_PREFETCH (b[6]->data, CLIB_CACHE_LINE_BYTES, LOAD);
272         CLIB_PREFETCH (b[7]->data, CLIB_CACHE_LINE_BYTES, LOAD);
273       }
274
275       /* RX interface handles */
276       sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
277       sw_if_index1 = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
278       sw_if_index2 = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
279       sw_if_index3 = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
280
281       h0 = vlib_buffer_get_current (b[0]);
282       h1 = vlib_buffer_get_current (b[1]);
283       h2 = vlib_buffer_get_current (b[2]);
284       h3 = vlib_buffer_get_current (b[3]);
285
286       if (do_trace)
287         {
288           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
289             {
290               l2fwd_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
291               t->sw_if_index = sw_if_index0;
292               t->bd_index = vnet_buffer (b[0])->l2.bd_index;
293               clib_memcpy (t->src, h0->src_address, 6);
294               clib_memcpy (t->dst, h0->dst_address, 6);
295             }
296           if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
297             {
298               l2fwd_trace_t *t = vlib_add_trace (vm, node, b[1], sizeof (*t));
299               t->sw_if_index = sw_if_index1;
300               t->bd_index = vnet_buffer (b[1])->l2.bd_index;
301               clib_memcpy (t->src, h1->src_address, 6);
302               clib_memcpy (t->dst, h1->dst_address, 6);
303             }
304           if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
305             {
306               l2fwd_trace_t *t = vlib_add_trace (vm, node, b[2], sizeof (*t));
307               t->sw_if_index = sw_if_index2;
308               t->bd_index = vnet_buffer (b[2])->l2.bd_index;
309               clib_memcpy (t->src, h2->src_address, 6);
310               clib_memcpy (t->dst, h2->dst_address, 6);
311             }
312           if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
313             {
314               l2fwd_trace_t *t = vlib_add_trace (vm, node, b[3], sizeof (*t));
315               t->sw_if_index = sw_if_index3;
316               t->bd_index = vnet_buffer (b[3])->l2.bd_index;
317               clib_memcpy (t->src, h3->src_address, 6);
318               clib_memcpy (t->dst, h3->dst_address, 6);
319             }
320         }
321
322 #ifdef COUNTERS
323       em->counters[node_counter_base_index + L2FWD_ERROR_L2FWD] += 4;
324 #endif
325       /* *INDENT-OFF* */
326       l2fib_lookup_4 (msm->mac_table, &cached_key, &cached_result,
327                       h0->dst_address, h1->dst_address,
328                       h2->dst_address, h3->dst_address,
329                       vnet_buffer (b[0])->l2.bd_index,
330                       vnet_buffer (b[1])->l2.bd_index,
331                       vnet_buffer (b[2])->l2.bd_index,
332                       vnet_buffer (b[3])->l2.bd_index,
333                       &key0,    /* not used */
334                       &key1,    /* not used */
335                       &key2,    /* not used */
336                       &key3,    /* not used */
337                       &bucket0, /* not used */
338                       &bucket1, /* not used */
339                       &bucket2, /* not used */
340                       &bucket3, /* not used */
341                       &result0,
342                       &result1,
343                       &result2,
344                       &result3);
345       /* *INDENT-ON* */
346       l2fwd_process (vm, node, msm, em, b[0], sw_if_index0, &result0, next);
347       l2fwd_process (vm, node, msm, em, b[1], sw_if_index1, &result1,
348                      next + 1);
349       l2fwd_process (vm, node, msm, em, b[2], sw_if_index2, &result2,
350                      next + 2);
351       l2fwd_process (vm, node, msm, em, b[3], sw_if_index3, &result3,
352                      next + 3);
353
354       /* verify speculative enqueues, maybe switch current next frame */
355       /* if next0==next1==next_index then nothing special needs to be done */
356       next += 4;
357       b += 4;
358       n_left -= 4;
359     }
360
361   while (n_left > 0)
362     {
363       u32 sw_if_index0;
364       ethernet_header_t *h0;
365       l2fib_entry_key_t key0;
366       l2fib_entry_result_t result0;
367       u32 bucket0;
368
369       sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
370
371       h0 = vlib_buffer_get_current (b[0]);
372
373       if (do_trace && PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
374         {
375           l2fwd_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
376           t->sw_if_index = sw_if_index0;
377           t->bd_index = vnet_buffer (b[0])->l2.bd_index;
378           clib_memcpy (t->src, h0->src_address, 6);
379           clib_memcpy (t->dst, h0->dst_address, 6);
380         }
381
382       /* process 1 pkt */
383 #ifdef COUNTERS
384       em->counters[node_counter_base_index + L2FWD_ERROR_L2FWD] += 1;
385 #endif
386       l2fib_lookup_1 (msm->mac_table, &cached_key, &cached_result, h0->dst_address, vnet_buffer (b[0])->l2.bd_index, &key0,     /* not used */
387                       &bucket0, /* not used */
388                       &result0);
389       l2fwd_process (vm, node, msm, em, b[0], sw_if_index0, &result0, next);
390
391       /* verify speculative enqueue, maybe switch current next frame */
392       next += 1;
393       b += 1;
394       n_left -= 1;
395     }
396
397   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
398
399   return frame->n_vectors;
400 }
401
402 VLIB_NODE_FN (l2fwd_node) (vlib_main_t * vm,
403                            vlib_node_runtime_t * node, vlib_frame_t * frame)
404 {
405   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
406     return l2fwd_node_inline (vm, node, frame, 1 /* do_trace */ );
407   return l2fwd_node_inline (vm, node, frame, 0 /* do_trace */ );
408 }
409
410 /* *INDENT-OFF* */
411 VLIB_REGISTER_NODE (l2fwd_node,static) = {
412   .name = "l2-fwd",
413   .vector_size = sizeof (u32),
414   .format_trace = format_l2fwd_trace,
415   .type = VLIB_NODE_TYPE_INTERNAL,
416
417   .n_errors = ARRAY_LEN(l2fwd_error_strings),
418   .error_strings = l2fwd_error_strings,
419
420   .n_next_nodes = L2FWD_N_NEXT,
421
422   /* edit / add dispositions here */
423   .next_nodes = {
424     [L2FWD_NEXT_L2_OUTPUT] = "l2-output",
425     [L2FWD_NEXT_DROP] = "error-drop",
426   },
427 };
428 /* *INDENT-ON* */
429
430 #ifndef CLIB_MARCH_VARIANT
431 clib_error_t *
432 l2fwd_init (vlib_main_t * vm)
433 {
434   l2fwd_main_t *mp = &l2fwd_main;
435
436   mp->vlib_main = vm;
437   mp->vnet_main = vnet_get_main ();
438
439   /* Initialize the feature next-node indexes */
440   feat_bitmap_init_next_nodes (vm,
441                                l2fwd_node.index,
442                                L2INPUT_N_FEAT,
443                                l2input_get_feat_names (),
444                                mp->feat_next_node_index);
445
446   /* init the hash table ptr */
447   mp->mac_table = get_mac_table ();
448
449   /* Initialize the next nodes for each ethertype */
450   next_by_ethertype_init (&mp->l3_next);
451
452   return 0;
453 }
454
455 VLIB_INIT_FUNCTION (l2fwd_init);
456
457
458 /** Add the L3 input node for this ethertype to the next nodes structure. */
459 void
460 l2fwd_register_input_type (vlib_main_t * vm,
461                            ethernet_type_t type, u32 node_index)
462 {
463   l2fwd_main_t *mp = &l2fwd_main;
464   u32 next_index;
465
466   next_index = vlib_node_add_next (vm, l2fwd_node.index, node_index);
467
468   next_by_ethertype_register (&mp->l3_next, type, next_index);
469 }
470
471
472 /**
473  * Set subinterface forward enable/disable.
474  * The CLI format is:
475  *   set interface l2 forward <interface> [disable]
476  */
477 static clib_error_t *
478 int_fwd (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
479 {
480   vnet_main_t *vnm = vnet_get_main ();
481   clib_error_t *error = 0;
482   u32 sw_if_index;
483   u32 enable;
484
485   if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
486     {
487       error = clib_error_return (0, "unknown interface `%U'",
488                                  format_unformat_error, input);
489       goto done;
490     }
491
492   enable = 1;
493   if (unformat (input, "disable"))
494     {
495       enable = 0;
496     }
497
498   /* set the interface flag */
499   if (l2input_intf_config (sw_if_index)->xconnect)
500     {
501       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_XCONNECT, enable);
502     }
503   else
504     {
505       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_FWD, enable);
506     }
507
508 done:
509   return error;
510 }
511
512 /*?
513  * Layer 2 unicast forwarding can be enabled and disabled on each
514  * interface and on each bridge-domain. Use this command to
515  * manage interfaces. It is enabled by default.
516  *
517  * @cliexpar
518  * Example of how to enable forwarding:
519  * @cliexcmd{set interface l2 forward GigabitEthernet0/8/0}
520  * Example of how to disable forwarding:
521  * @cliexcmd{set interface l2 forward GigabitEthernet0/8/0 disable}
522 ?*/
523 /* *INDENT-OFF* */
524 VLIB_CLI_COMMAND (int_fwd_cli, static) = {
525   .path = "set interface l2 forward",
526   .short_help = "set interface l2 forward <interface> [disable]",
527   .function = int_fwd,
528 };
529 /* *INDENT-ON* */
530
531 #endif
532
533 /*
534  * fd.io coding-style-patch-verification: ON
535  *
536  * Local Variables:
537  * eval: (c-set-style "gnu")
538  * End:
539  */