L2 BD: introduce a BD interface on which to send UU packets
[vpp.git] / src / vnet / l2 / l2_xcrw.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vnet/l2/l2_xcrw.h>
16
17 /**
18  * @file
19  * General L2 / L3 cross-connect, used to set up
20  * "L2 interface <--> your-favorite-tunnel-encap" tunnels.
21  *
22  * We set up a typical L2 cross-connect or (future) bridge
23  * to hook L2 interface(s) up to the L3 stack in arbitrary ways.
24  *
25  * Each l2_xcrw adjacency specifies 3 things:
26  *
27  * 1. The next graph node (presumably in the L3 stack) to
28  *    process the (L2 -> L3) packet
29  *
30  * 2. A new value for vnet_buffer(b)->sw_if_index[VLIB_TX]
31  *    (i.e. a lookup FIB index),
32  *
33  * 3. A rewrite string to apply.
34  *
35  * Example: to cross-connect an L2 interface or (future) bridge
36  * to an mpls-o-gre tunnel, set up the L2 rewrite string as shown in
37  * mpls_gre_rewrite, and use "mpls-post-rewrite" to fix the
38  * GRE IP header checksum and length fields.
39  */
40
41 typedef struct
42 {
43   u32 next_index;
44   u32 tx_fib_index;
45 } l2_xcrw_trace_t;
46
47 /* packet trace format function */
48 static u8 *
49 format_l2_xcrw_trace (u8 * s, va_list * args)
50 {
51   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
52   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
53   l2_xcrw_trace_t *t = va_arg (*args, l2_xcrw_trace_t *);
54
55   s = format (s, "L2_XCRW: next index %d tx_fib_index %d",
56               t->next_index, t->tx_fib_index);
57   return s;
58 }
59
60 l2_xcrw_main_t l2_xcrw_main;
61
62 static vlib_node_registration_t l2_xcrw_node;
63
64 static char *l2_xcrw_error_strings[] = {
65 #define _(sym,string) string,
66   foreach_l2_xcrw_error
67 #undef _
68 };
69
70 static uword
71 l2_xcrw_node_fn (vlib_main_t * vm,
72                  vlib_node_runtime_t * node, vlib_frame_t * frame)
73 {
74   u32 n_left_from, *from, *to_next;
75   l2_xcrw_next_t next_index;
76   l2_xcrw_main_t *xcm = &l2_xcrw_main;
77   vlib_node_t *n = vlib_get_node (vm, l2_xcrw_node.index);
78   u32 node_counter_base_index = n->error_heap_index;
79   vlib_error_main_t *em = &vm->error_main;
80
81   from = vlib_frame_vector_args (frame);
82   n_left_from = frame->n_vectors;
83   next_index = node->cached_next_index;
84
85   while (n_left_from > 0)
86     {
87       u32 n_left_to_next;
88
89       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
90
91       while (n_left_from >= 4 && n_left_to_next >= 2)
92         {
93           u32 bi0, bi1;
94           vlib_buffer_t *b0, *b1;
95           u32 next0, next1;
96           u32 sw_if_index0, sw_if_index1;
97           l2_xcrw_adjacency_t *adj0, *adj1;
98
99           /* Prefetch next iteration. */
100           {
101             vlib_buffer_t *p2, *p3;
102
103             p2 = vlib_get_buffer (vm, from[2]);
104             p3 = vlib_get_buffer (vm, from[3]);
105
106             vlib_prefetch_buffer_header (p2, LOAD);
107             vlib_prefetch_buffer_header (p3, LOAD);
108
109             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
110             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
111           }
112
113           /* speculatively enqueue b0 and b1 to the current next frame */
114           to_next[0] = bi0 = from[0];
115           to_next[1] = bi1 = from[1];
116           from += 2;
117           to_next += 2;
118           n_left_from -= 2;
119           n_left_to_next -= 2;
120
121           b0 = vlib_get_buffer (vm, bi0);
122           b1 = vlib_get_buffer (vm, bi1);
123
124           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
125           sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
126
127           adj0 = vec_elt_at_index (xcm->adj_by_sw_if_index, sw_if_index0);
128           adj1 = vec_elt_at_index (xcm->adj_by_sw_if_index, sw_if_index1);
129
130           next0 = adj0->rewrite_header.next_index;
131           vnet_buffer (b0)->sw_if_index[VLIB_TX] =
132             adj0->rewrite_header.sw_if_index;
133
134           next1 = adj1->rewrite_header.next_index;
135           vnet_buffer (b1)->sw_if_index[VLIB_TX] =
136             adj1->rewrite_header.sw_if_index;
137
138           em->counters[node_counter_base_index + next1]++;
139
140           if (PREDICT_TRUE (next0 > 0))
141             {
142               u8 *h0 = vlib_buffer_get_current (b0);
143               vnet_rewrite_one_header (adj0[0], h0,
144                                        adj0->rewrite_header.data_bytes);
145               vlib_buffer_advance (b0, -adj0->rewrite_header.data_bytes);
146               em->counters[node_counter_base_index + L2_XCRW_ERROR_FWD]++;
147             }
148
149           if (PREDICT_TRUE (next1 > 0))
150             {
151               u8 *h1 = vlib_buffer_get_current (b1);
152               vnet_rewrite_one_header (adj1[0], h1,
153                                        adj1->rewrite_header.data_bytes);
154               vlib_buffer_advance (b1, -adj1->rewrite_header.data_bytes);
155               em->counters[node_counter_base_index + L2_XCRW_ERROR_FWD]++;
156             }
157
158
159           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
160             {
161               if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
162                                  && (b0->flags & VLIB_BUFFER_IS_TRACED)))
163                 {
164                   l2_xcrw_trace_t *t =
165                     vlib_add_trace (vm, node, b0, sizeof (*t));
166                   t->next_index = next0;
167                   t->tx_fib_index = adj0->rewrite_header.sw_if_index;
168                 }
169               if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
170                                  && (b1->flags & VLIB_BUFFER_IS_TRACED)))
171                 {
172                   l2_xcrw_trace_t *t =
173                     vlib_add_trace (vm, node, b1, sizeof (*t));
174                   t->next_index = next1;
175                   t->tx_fib_index = adj1->rewrite_header.sw_if_index;
176                 }
177             }
178
179           /* verify speculative enqueues, maybe switch current next frame */
180           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
181                                            to_next, n_left_to_next,
182                                            bi0, bi1, next0, next1);
183         }
184
185       while (n_left_from > 0 && n_left_to_next > 0)
186         {
187           u32 bi0;
188           vlib_buffer_t *b0;
189           u32 next0;
190           u32 sw_if_index0;
191           l2_xcrw_adjacency_t *adj0;
192
193           /* speculatively enqueue b0 to the current next frame */
194           bi0 = from[0];
195           to_next[0] = bi0;
196           from += 1;
197           to_next += 1;
198           n_left_from -= 1;
199           n_left_to_next -= 1;
200
201           b0 = vlib_get_buffer (vm, bi0);
202
203           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
204
205           adj0 = vec_elt_at_index (xcm->adj_by_sw_if_index, sw_if_index0);
206
207           next0 = adj0->rewrite_header.next_index;
208           vnet_buffer (b0)->sw_if_index[VLIB_TX] =
209             adj0->rewrite_header.sw_if_index;
210
211           if (PREDICT_TRUE (next0 > 0))
212             {
213               u8 *h0 = vlib_buffer_get_current (b0);
214               vnet_rewrite_one_header (adj0[0], h0,
215                                        adj0->rewrite_header.data_bytes);
216               vlib_buffer_advance (b0, -adj0->rewrite_header.data_bytes);
217               em->counters[node_counter_base_index + L2_XCRW_ERROR_FWD]++;
218             }
219
220           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
221                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
222             {
223               l2_xcrw_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
224               t->next_index = next0;
225               t->tx_fib_index = adj0->rewrite_header.sw_if_index;
226             }
227
228           /* verify speculative enqueue, maybe switch current next frame */
229           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
230                                            to_next, n_left_to_next,
231                                            bi0, next0);
232         }
233
234       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
235     }
236
237   return frame->n_vectors;
238 }
239
240 /* *INDENT-OFF* */
241 VLIB_REGISTER_NODE (l2_xcrw_node, static) = {
242   .function = l2_xcrw_node_fn,
243   .name = "l2-xcrw",
244   .vector_size = sizeof (u32),
245   .format_trace = format_l2_xcrw_trace,
246   .type = VLIB_NODE_TYPE_INTERNAL,
247
248   .n_errors = ARRAY_LEN(l2_xcrw_error_strings),
249   .error_strings = l2_xcrw_error_strings,
250
251   .n_next_nodes = L2_XCRW_N_NEXT,
252
253   /* edit / add dispositions here */
254   .next_nodes = {
255         [L2_XCRW_NEXT_DROP] = "error-drop",
256   },
257 };
258 /* *INDENT-ON* */
259
260 VLIB_NODE_FUNCTION_MULTIARCH (l2_xcrw_node, l2_xcrw_node_fn)
261      clib_error_t *l2_xcrw_init (vlib_main_t * vm)
262 {
263   l2_xcrw_main_t *mp = &l2_xcrw_main;
264
265   mp->vlib_main = vm;
266   mp->vnet_main = &vnet_main;
267   mp->tunnel_index_by_l2_sw_if_index = hash_create (0, sizeof (uword));
268
269   return 0;
270 }
271
272 VLIB_INIT_FUNCTION (l2_xcrw_init);
273
274 static uword
275 dummy_interface_tx (vlib_main_t * vm,
276                     vlib_node_runtime_t * node, vlib_frame_t * frame)
277 {
278   clib_warning ("you shouldn't be here, leaking buffers...");
279   return frame->n_vectors;
280 }
281
282 static u8 *
283 format_xcrw_name (u8 * s, va_list * args)
284 {
285   u32 dev_instance = va_arg (*args, u32);
286   return format (s, "xcrw%d", dev_instance);
287 }
288
289 /* *INDENT-OFF* */
290 VNET_DEVICE_CLASS (xcrw_device_class,static) = {
291   .name = "Xcrw",
292   .format_device_name = format_xcrw_name,
293   .tx_function = dummy_interface_tx,
294 };
295 /* *INDENT-ON* */
296
297 /* Create a sham tunnel interface and return its sw_if_index */
298 static u32
299 create_xcrw_interface (vlib_main_t * vm)
300 {
301   vnet_main_t *vnm = vnet_get_main ();
302   static u32 instance;
303   u8 address[6];
304   u32 hw_if_index;
305   vnet_hw_interface_t *hi;
306   u32 sw_if_index;
307
308   /* mac address doesn't really matter */
309   memset (address, 0, sizeof (address));
310   address[2] = 0x12;
311
312   /* can returns error iff phy != 0 */
313   (void) ethernet_register_interface
314     (vnm, xcrw_device_class.index, instance++, address, &hw_if_index,
315      /* flag change */ 0);
316
317   hi = vnet_get_hw_interface (vnm, hw_if_index);
318   sw_if_index = hi->sw_if_index;
319   vnet_sw_interface_set_flags (vnm, sw_if_index,
320                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);
321
322   /* Output to the sham tunnel invokes the encap node */
323   hi->output_node_index = l2_xcrw_node.index;
324
325   return sw_if_index;
326 }
327
328 int
329 vnet_configure_l2_xcrw (vlib_main_t * vm, vnet_main_t * vnm,
330                         u32 l2_sw_if_index, u32 tx_fib_index,
331                         u8 * rewrite, u32 next_node_index, int is_add)
332 {
333   l2_xcrw_main_t *xcm = &l2_xcrw_main;
334   l2_xcrw_adjacency_t *a;
335   l2_xcrw_tunnel_t *t;
336   uword *p;
337
338   if (is_add)
339     {
340
341       pool_get (xcm->tunnels, t);
342
343       /* No interface allocated? Do it. Otherwise, set admin up */
344       if (t->tunnel_sw_if_index == 0)
345         t->tunnel_sw_if_index = create_xcrw_interface (vm);
346       else
347         vnet_sw_interface_set_flags (vnm, t->tunnel_sw_if_index,
348                                      VNET_SW_INTERFACE_FLAG_ADMIN_UP);
349
350       t->l2_sw_if_index = l2_sw_if_index;
351
352       vec_validate (xcm->adj_by_sw_if_index, t->l2_sw_if_index);
353
354       a = vec_elt_at_index (xcm->adj_by_sw_if_index, t->l2_sw_if_index);
355       memset (a, 0, sizeof (*a));
356
357       a->rewrite_header.sw_if_index = tx_fib_index;
358
359       /*
360        * Add or find a dynamic disposition for the successor node,
361        * e.g. so we can ship pkts to mpls_post_rewrite...
362        */
363       a->rewrite_header.next_index =
364         vlib_node_add_next (vm, l2_xcrw_node.index, next_node_index);
365
366       if (vec_len (rewrite))
367         vnet_rewrite_set_data (a[0], rewrite, vec_len (rewrite));
368
369       set_int_l2_mode (vm, vnm, MODE_L2_XC, t->l2_sw_if_index, 0,
370                        L2_BD_PORT_TYPE_NORMAL, 0, t->tunnel_sw_if_index);
371       hash_set (xcm->tunnel_index_by_l2_sw_if_index,
372                 t->l2_sw_if_index, t - xcm->tunnels);
373       return 0;
374     }
375   else
376     {
377       p = hash_get (xcm->tunnel_index_by_l2_sw_if_index, l2_sw_if_index);
378       if (p == 0)
379         return VNET_API_ERROR_INVALID_SW_IF_INDEX;
380
381       t = pool_elt_at_index (xcm->tunnels, p[0]);
382
383       a = vec_elt_at_index (xcm->adj_by_sw_if_index, t->l2_sw_if_index);
384       /* Reset adj to drop traffic */
385       memset (a, 0, sizeof (*a));
386
387       set_int_l2_mode (vm, vnm, MODE_L3, t->l2_sw_if_index, 0,
388                        L2_BD_PORT_TYPE_NORMAL, 0, 0);
389
390       vnet_sw_interface_set_flags (vnm, t->tunnel_sw_if_index, 0 /* down */ );
391
392       hash_unset (xcm->tunnel_index_by_l2_sw_if_index, l2_sw_if_index);
393       pool_put (xcm->tunnels, t);
394     }
395   return 0;
396 }
397
398
399 static clib_error_t *
400 set_l2_xcrw_command_fn (vlib_main_t * vm,
401                         unformat_input_t * input, vlib_cli_command_t * cmd)
402 {
403   unformat_input_t _line_input, *line_input = &_line_input;
404   int is_add = 1;
405   int is_ipv6 = 0;              /* for fib id -> fib index mapping */
406   u32 tx_fib_id = ~0;
407   u32 tx_fib_index = ~0;
408   u32 next_node_index = ~0;
409   u32 l2_sw_if_index;
410   u8 *rw = 0;
411   vnet_main_t *vnm = vnet_get_main ();
412   int rv;
413   clib_error_t *error = NULL;
414
415
416   if (!unformat_user (input, unformat_line_input, line_input))
417     return 0;
418
419   if (!unformat (line_input, "%U",
420                  unformat_vnet_sw_interface, vnm, &l2_sw_if_index))
421     {
422       error = clib_error_return (0, "unknown input '%U'",
423                                  format_unformat_error, line_input);
424       goto done;
425     }
426
427   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
428     {
429       if (unformat (line_input, "next %U",
430                     unformat_vlib_node, vm, &next_node_index))
431         ;
432       else if (unformat (line_input, "tx-fib-id %d", &tx_fib_id))
433         ;
434       else if (unformat (line_input, "del"))
435         is_add = 0;
436       else if (unformat (line_input, "ipv6"))
437         is_ipv6 = 1;
438       else if (unformat (line_input, "rw %U", unformat_hex_string, &rw));
439       else
440         break;
441     }
442
443   if (next_node_index == ~0)
444     {
445       error = clib_error_return (0, "next node not specified");
446       goto done;
447     }
448
449   if (tx_fib_id != ~0)
450     {
451       uword *p;
452
453       if (is_ipv6)
454         p = hash_get (ip6_main.fib_index_by_table_id, tx_fib_id);
455       else
456         p = hash_get (ip4_main.fib_index_by_table_id, tx_fib_id);
457
458       if (p == 0)
459         {
460           error =
461             clib_error_return (0, "nonexistent tx_fib_id %d", tx_fib_id);
462           goto done;
463         }
464
465       tx_fib_index = p[0];
466     }
467
468   rv = vnet_configure_l2_xcrw (vm, vnm, l2_sw_if_index, tx_fib_index,
469                                rw, next_node_index, is_add);
470
471   switch (rv)
472     {
473
474     case 0:
475       break;
476
477     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
478       error = clib_error_return (0, "%U not cross-connected",
479                                  format_vnet_sw_if_index_name,
480                                  vnm, l2_sw_if_index);
481       goto done;
482
483     default:
484       error = clib_error_return (0, "vnet_configure_l2_xcrw returned %d", rv);
485       goto done;
486     }
487
488 done:
489   vec_free (rw);
490   unformat_free (line_input);
491
492   return error;
493 }
494
495 /*?
496  * Add or delete a Layer 2 to Layer 3 rewrite cross-connect. This is
497  * used to hook Layer 2 interface(s) up to the Layer 3 stack in
498  * arbitrary ways. For example, cross-connect an L2 interface or
499  * (future) bridge to an mpls-o-gre tunnel. Set up the L2 rewrite
500  * string as shown in mpls_gre_rewrite, and use \"mpls-post-rewrite\"
501  * to fix the GRE IP header checksum and length fields.
502  *
503  * @cliexpar
504  * @todo This is incomplete. This needs a detailed description and a
505  * practical example.
506 ?*/
507 /* *INDENT-OFF* */
508 VLIB_CLI_COMMAND (set_l2_xcrw_command, static) = {
509   .path = "set interface l2 xcrw",
510   .short_help =
511   "set interface l2 xcrw <interface> next <node-name>\n"
512   "    [del] [tx-fib-id <id>] [ipv6] rw <hex-bytes>",
513   .function = set_l2_xcrw_command_fn,
514 };
515 /* *INDENT-ON* */
516
517 static u8 *
518 format_l2xcrw (u8 * s, va_list * args)
519 {
520   vnet_main_t *vnm = va_arg (*args, vnet_main_t *);
521   l2_xcrw_tunnel_t *t = va_arg (*args, l2_xcrw_tunnel_t *);
522   l2_xcrw_main_t *xcm = &l2_xcrw_main;
523   vlib_main_t *vm = vlib_get_main ();
524   l2_xcrw_adjacency_t *a;
525   u8 *rewrite_string;
526
527   if (t == 0)
528     {
529       s = format (s, "%-25s%s", "L2 interface", "Tunnel Details");
530       return s;
531     }
532
533   s = format (s, "%-25U %U ",
534               format_vnet_sw_if_index_name, vnm, t->l2_sw_if_index,
535               format_vnet_sw_if_index_name, vnm, t->tunnel_sw_if_index);
536
537   a = vec_elt_at_index (xcm->adj_by_sw_if_index, t->l2_sw_if_index);
538
539   s = format (s, "next %U ",
540               format_vlib_next_node_name, vm, l2_xcrw_node.index,
541               a->rewrite_header.next_index);
542
543   if (a->rewrite_header.sw_if_index != ~0)
544     s = format (s, "tx fib index %d ", a->rewrite_header.sw_if_index);
545
546   if (a->rewrite_header.data_bytes)
547     {
548       rewrite_string = (u8 *) (a + 1);
549       rewrite_string -= a->rewrite_header.data_bytes;
550       s = format (s, "rewrite data: %U ",
551                   format_hex_bytes, rewrite_string,
552                   a->rewrite_header.data_bytes);
553     }
554
555   s = format (s, "\n");
556
557   return s;
558 }
559
560
561 static clib_error_t *
562 show_l2xcrw_command_fn (vlib_main_t * vm,
563                         unformat_input_t * input, vlib_cli_command_t * cmd)
564 {
565   vnet_main_t *vnm = vnet_get_main ();
566   l2_xcrw_main_t *xcm = &l2_xcrw_main;
567   l2_xcrw_tunnel_t *t;
568
569   if (pool_elts (xcm->tunnels) == 0)
570     {
571       vlib_cli_output (vm, "No L2 / L3 rewrite cross-connects configured");
572       return 0;
573     }
574
575   vlib_cli_output (vm, "%U", format_l2xcrw, 0, 0);
576
577   /* *INDENT-OFF* */
578   pool_foreach (t, xcm->tunnels,
579   ({
580     vlib_cli_output (vm, "%U", format_l2xcrw, vnm, t);
581   }));
582   /* *INDENT-ON* */
583
584   return 0;
585 }
586
587 /*?
588  * Display a Layer 2 to Layer 3 rewrite cross-connect. This is used to
589  * hook Layer 2 interface(s) up to the Layer 3 stack in arbitrary ways.
590  *
591  * @todo This is incomplete. This needs a detailed description and a
592  * practical example.
593 ?*/
594 /* *INDENT-OFF* */
595 VLIB_CLI_COMMAND (show_l2xcrw_command, static) = {
596   .path = "show l2xcrw",
597   .short_help = "show l2xcrw",
598   .function = show_l2xcrw_command_fn,
599 };
600 /* *INDENT-ON* */
601
602 /*
603  * fd.io coding-style-patch-verification: ON
604  *
605  * Local Variables:
606  * eval: (c-set-style "gnu")
607  * End:
608  */