DVR: run L3 output features
[vpp.git] / src / vnet / dpo / dvr_dpo.c
1 /*
2  * Copyright (c) 2016 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
16 #include <vnet/dpo/dvr_dpo.h>
17 #include <vnet/fib/fib_node.h>
18 #include <vnet/ip/ip.h>
19 #include <vnet/ethernet/ethernet.h>
20
21 /**
22  * The 'DB' of DVR DPOs.
23  * There is one per-interface per-L3 proto, so this is a per-interface vector
24  */
25 static index_t *dvr_dpo_db[DPO_PROTO_NUM];
26
27 static dvr_dpo_t *
28 dvr_dpo_alloc (void)
29 {
30     dvr_dpo_t *dd;
31
32     pool_get(dvr_dpo_pool, dd);
33
34     return (dd);
35 }
36
37 static inline dvr_dpo_t *
38 dvr_dpo_get_from_dpo (const dpo_id_t *dpo)
39 {
40     ASSERT(DPO_DVR == dpo->dpoi_type);
41
42     return (dvr_dpo_get(dpo->dpoi_index));
43 }
44
45 static inline index_t
46 dvr_dpo_get_index (dvr_dpo_t *dd)
47 {
48     return (dd - dvr_dpo_pool);
49 }
50
51 static void
52 dvr_dpo_lock (dpo_id_t *dpo)
53 {
54     dvr_dpo_t *dd;
55
56     dd = dvr_dpo_get_from_dpo(dpo);
57     dd->dd_locks++;
58 }
59
60 static void
61 dvr_dpo_unlock (dpo_id_t *dpo)
62 {
63     dvr_dpo_t *dd;
64
65     dd = dvr_dpo_get_from_dpo(dpo);
66     dd->dd_locks--;
67
68     if (0 == dd->dd_locks)
69     {
70         if (DPO_PROTO_IP4 == dd->dd_proto)
71         {
72             vnet_feature_enable_disable ("ip4-output", "ip4-dvr-reinject",
73                                          dd->dd_sw_if_index, 0, 0, 0);
74         }
75         else
76         {
77             vnet_feature_enable_disable ("ip6-output", "ip6-dvr-reinject",
78                                          dd->dd_sw_if_index, 0, 0, 0);
79         }
80
81         dvr_dpo_db[dd->dd_proto][dd->dd_sw_if_index] = INDEX_INVALID;
82         pool_put(dvr_dpo_pool, dd);
83     }
84 }
85
86 void
87 dvr_dpo_add_or_lock (u32 sw_if_index,
88                      dpo_proto_t dproto,
89                      dpo_id_t *dpo)
90 {
91     dvr_dpo_t *dd;
92
93     vec_validate_init_empty(dvr_dpo_db[dproto],
94                             sw_if_index,
95                             INDEX_INVALID);
96
97     if (INDEX_INVALID == dvr_dpo_db[dproto][sw_if_index])
98     {
99         dd = dvr_dpo_alloc();
100
101         dd->dd_sw_if_index = sw_if_index;
102         dd->dd_proto = dproto;
103
104         dvr_dpo_db[dproto][sw_if_index] = dvr_dpo_get_index(dd);
105
106         /*
107          * enable the reinject into L2 path feature on the interface
108          */
109         if (DPO_PROTO_IP4 == dproto)
110             vnet_feature_enable_disable ("ip4-output", "ip4-dvr-reinject",
111                                          dd->dd_sw_if_index, 1, 0, 0);
112         else if (DPO_PROTO_IP6 == dproto)
113             vnet_feature_enable_disable ("ip6-output", "ip6-dvr-reinject",
114                                          dd->dd_sw_if_index, 1, 0, 0);
115         else
116             ASSERT(0);
117     }
118     else
119     {
120         dd = dvr_dpo_get(dvr_dpo_db[dproto][sw_if_index]);
121     }
122
123     dpo_set(dpo, DPO_DVR, dproto, dvr_dpo_get_index(dd));
124 }
125
126
127 static clib_error_t *
128 dvr_dpo_interface_state_change (vnet_main_t * vnm,
129                                       u32 sw_if_index,
130                                       u32 flags)
131 {
132     /*
133      */
134     return (NULL);
135 }
136
137 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(
138     dvr_dpo_interface_state_change);
139
140 /**
141  * @brief Registered callback for HW interface state changes
142  */
143 static clib_error_t *
144 dvr_dpo_hw_interface_state_change (vnet_main_t * vnm,
145                                          u32 hw_if_index,
146                                          u32 flags)
147 {
148     return (NULL);
149 }
150
151 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION(
152     dvr_dpo_hw_interface_state_change);
153
154 static clib_error_t *
155 dvr_dpo_interface_delete (vnet_main_t * vnm,
156                                 u32 sw_if_index,
157                                 u32 is_add)
158 {
159     return (NULL);
160 }
161
162 VNET_SW_INTERFACE_ADD_DEL_FUNCTION(
163     dvr_dpo_interface_delete);
164
165 u8*
166 format_dvr_dpo (u8* s, va_list *ap)
167 {
168     index_t index = va_arg(*ap, index_t);
169     CLIB_UNUSED(u32 indent) = va_arg(*ap, u32);
170     vnet_main_t * vnm = vnet_get_main();
171     dvr_dpo_t *dd = dvr_dpo_get(index);
172
173     return (format(s, "dvr-%U-dpo",
174                    format_vnet_sw_interface_name,
175                    vnm,
176                    vnet_get_sw_interface(vnm, dd->dd_sw_if_index)));
177 }
178
179 static void
180 dvr_dpo_mem_show (void)
181 {
182     fib_show_memory_usage("DVR",
183                           pool_elts(dvr_dpo_pool),
184                           pool_len(dvr_dpo_pool),
185                           sizeof(dvr_dpo_t));
186 }
187
188
189 const static dpo_vft_t dvr_dpo_vft = {
190     .dv_lock = dvr_dpo_lock,
191     .dv_unlock = dvr_dpo_unlock,
192     .dv_format = format_dvr_dpo,
193     .dv_mem_show = dvr_dpo_mem_show,
194 };
195
196 /**
197  * @brief The per-protocol VLIB graph nodes that are assigned to a glean
198  *        object.
199  *
200  * this means that these graph nodes are ones from which a glean is the
201  * parent object in the DPO-graph.
202  */
203 const static char* const dvr_dpo_ip4_nodes[] =
204 {
205     "ip4-dvr-dpo",
206     NULL,
207 };
208 const static char* const dvr_dpo_ip6_nodes[] =
209 {
210     "ip6-dvr-dpo",
211     NULL,
212 };
213
214 const static char* const * const dvr_dpo_nodes[DPO_PROTO_NUM] =
215 {
216     [DPO_PROTO_IP4]  = dvr_dpo_ip4_nodes,
217     [DPO_PROTO_IP6]  = dvr_dpo_ip6_nodes,
218 };
219
220 void
221 dvr_dpo_module_init (void)
222 {
223     dpo_register(DPO_DVR,
224                  &dvr_dpo_vft,
225                  dvr_dpo_nodes);
226 }
227
228 /**
229  * @brief Interface DPO trace data
230  */
231 typedef struct dvr_dpo_trace_t_
232 {
233     u32 sw_if_index;
234 } dvr_dpo_trace_t;
235
236 always_inline uword
237 dvr_dpo_inline (vlib_main_t * vm,
238                 vlib_node_runtime_t * node,
239                 vlib_frame_t * from_frame,
240                 u8 is_ip6)
241 {
242     u32 n_left_from, next_index, * from, * to_next;
243     ip_lookup_main_t *lm = (is_ip6?
244                             &ip6_main.lookup_main:
245                             &ip4_main.lookup_main);
246
247     from = vlib_frame_vector_args (from_frame);
248     n_left_from = from_frame->n_vectors;
249
250     next_index = node->cached_next_index;
251
252     while (n_left_from > 0)
253     {
254         u32 n_left_to_next;
255
256         vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
257
258         while (n_left_from >= 4 && n_left_to_next > 2)
259         {
260             const dvr_dpo_t *dd0, *dd1;
261             u32 bi0, ddi0, bi1, ddi1;
262             vlib_buffer_t *b0, *b1;
263             u32 next0, next1;
264             u8 len0, len1;
265
266             bi0 = from[0];
267             to_next[0] = bi0;
268             bi1 = from[1];
269             to_next[1] = bi1;
270             from += 2;
271             to_next += 2;
272             n_left_from -= 2;
273             n_left_to_next -= 2;
274             next0 = next1 = 0;
275
276             b0 = vlib_get_buffer (vm, bi0);
277             b1 = vlib_get_buffer (vm, bi1);
278
279             ddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
280             ddi1 = vnet_buffer(b1)->ip.adj_index[VLIB_TX];
281             dd0 = dvr_dpo_get(ddi0);
282             dd1 = dvr_dpo_get(ddi1);
283
284             vnet_buffer(b0)->sw_if_index[VLIB_TX] = dd0->dd_sw_if_index;
285             vnet_buffer(b1)->sw_if_index[VLIB_TX] = dd1->dd_sw_if_index;
286
287             len0 = ((u8*)vlib_buffer_get_current(b0) -
288                     (u8*)ethernet_buffer_get_header(b0));
289             len1 = ((u8*)vlib_buffer_get_current(b1) -
290                     (u8*)ethernet_buffer_get_header(b1));
291             vnet_buffer(b0)->l2.l2_len = len0;
292             vnet_buffer(b1)->l2.l2_len = len1;
293             vnet_buffer(b0)->flags |= VNET_OPAQUE_F_IS_DVR;
294             vnet_buffer(b1)->flags |= VNET_OPAQUE_F_IS_DVR;
295
296             vlib_buffer_advance(b0, -len0);
297             vlib_buffer_advance(b1, -len1);
298
299             vnet_feature_arc_start (lm->output_feature_arc_index,
300                                     dd0->dd_sw_if_index, &next0, b0);
301             vnet_feature_arc_start (lm->output_feature_arc_index,
302                                     dd1->dd_sw_if_index, &next1, b1);
303
304             if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
305             {
306                 dvr_dpo_trace_t *tr0;
307
308                 tr0 = vlib_add_trace (vm, node, b0, sizeof (*tr0));
309                 tr0->sw_if_index = dd0->dd_sw_if_index;
310             }
311             if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
312             {
313                 dvr_dpo_trace_t *tr1;
314
315                 tr1 = vlib_add_trace (vm, node, b1, sizeof (*tr1));
316                 tr1->sw_if_index = dd1->dd_sw_if_index;
317             }
318
319             vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
320                                             n_left_to_next, bi0, bi1,
321                                             next0, next1);
322         }
323
324         while (n_left_from > 0 && n_left_to_next > 0)
325         {
326             const dvr_dpo_t * dd0;
327             vlib_buffer_t * b0;
328             u32 bi0, ddi0;
329             u32 next0;
330             u8 len0;
331
332             bi0 = from[0];
333             to_next[0] = bi0;
334             from += 1;
335             to_next += 1;
336             n_left_from -= 1;
337             n_left_to_next -= 1;
338             next0 = 0;
339
340             b0 = vlib_get_buffer (vm, bi0);
341
342             ddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
343             dd0 = dvr_dpo_get(ddi0);
344
345             vnet_buffer(b0)->sw_if_index[VLIB_TX] = dd0->dd_sw_if_index;
346
347             /*
348              * take that, rewind it back...
349              */
350             len0 = ((u8*)vlib_buffer_get_current(b0) -
351                     (u8*)ethernet_buffer_get_header(b0));
352             vnet_buffer(b0)->l2.l2_len = len0;
353             vnet_buffer(b0)->flags |= VNET_OPAQUE_F_IS_DVR;
354             vlib_buffer_advance(b0, -len0);
355
356             /*
357              * start processing the ipX output features
358              */
359             vnet_feature_arc_start(lm->output_feature_arc_index,
360                                    dd0->dd_sw_if_index, &next0, b0);
361
362             if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
363             {
364                 dvr_dpo_trace_t *tr;
365
366                 tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
367                 tr->sw_if_index = dd0->dd_sw_if_index;
368             }
369
370             vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
371                                             n_left_to_next, bi0,
372                                             next0);
373         }
374         vlib_put_next_frame (vm, node, next_index, n_left_to_next);
375     }
376     return from_frame->n_vectors;
377 }
378
379 static u8 *
380 format_dvr_dpo_trace (u8 * s, va_list * args)
381 {
382     CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
383     CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
384     dvr_dpo_trace_t * t = va_arg (*args, dvr_dpo_trace_t *);
385     u32 indent = format_get_indent (s);
386     s = format (s, "%U sw_if_index:%d",
387                 format_white_space, indent,
388                 t->sw_if_index);
389     return s;
390 }
391
392 static uword
393 ip4_dvr_dpo (vlib_main_t * vm,
394              vlib_node_runtime_t * node,
395              vlib_frame_t * from_frame)
396 {
397     return (dvr_dpo_inline(vm, node, from_frame, 0));
398 }
399
400 static uword
401 ip6_dvr_dpo (vlib_main_t * vm,
402              vlib_node_runtime_t * node,
403              vlib_frame_t * from_frame)
404 {
405     return (dvr_dpo_inline(vm, node, from_frame, 1));
406 }
407
408 VLIB_REGISTER_NODE (ip4_dvr_dpo_node) = {
409     .function = ip4_dvr_dpo,
410     .name = "ip4-dvr-dpo",
411     .vector_size = sizeof (u32),
412     .format_trace = format_dvr_dpo_trace,
413     .sibling_of = "ip4-rewrite",
414 };
415 VLIB_REGISTER_NODE (ip6_dvr_dpo_node) = {
416     .function = ip6_dvr_dpo,
417     .name = "ip6-dvr-dpo",
418     .vector_size = sizeof (u32),
419     .format_trace = format_dvr_dpo_trace,
420     .sibling_of = "ip6-rewrite",
421 };
422
423 VLIB_NODE_FUNCTION_MULTIARCH (ip4_dvr_dpo_node, ip4_dvr_dpo)
424 VLIB_NODE_FUNCTION_MULTIARCH (ip6_dvr_dpo_node, ip6_dvr_dpo)
425
426 typedef enum dvr_reinject_next_t_
427 {
428     DVR_REINJECT_OUTPUT = 0,
429 } dvr_reinject_next_t;
430
431 always_inline uword
432 dvr_reinject_inline (vlib_main_t * vm,
433                      vlib_node_runtime_t * node,
434                      vlib_frame_t * from_frame)
435 {
436     u32 n_left_from, next_index, * from, * to_next;
437
438     from = vlib_frame_vector_args (from_frame);
439     n_left_from = from_frame->n_vectors;
440
441     next_index = node->cached_next_index;
442
443     while (n_left_from > 0)
444     {
445         u32 n_left_to_next;
446
447         vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
448
449         while (n_left_from >= 4 && n_left_to_next > 2)
450         {
451             dvr_reinject_next_t next0, next1;
452             vlib_buffer_t *b0, *b1;
453             u32 bi0, bi1;
454
455             bi0 = from[0];
456             to_next[0] = bi0;
457             bi1 = from[1];
458             to_next[1] = bi1;
459             from += 2;
460             to_next += 2;
461             n_left_from -= 2;
462             n_left_to_next -= 2;
463
464             b0 = vlib_get_buffer (vm, bi0);
465             b1 = vlib_get_buffer (vm, bi1);
466
467             if (vnet_buffer(b0)->flags & VNET_OPAQUE_F_IS_DVR)
468                 next0 = DVR_REINJECT_OUTPUT;
469             else
470                 vnet_feature_next(vnet_buffer(b0)->sw_if_index[VLIB_TX],
471                                   &next0, b0);
472
473             if (vnet_buffer(b1)->flags & VNET_OPAQUE_F_IS_DVR)
474                 next1 = DVR_REINJECT_OUTPUT;
475             else
476                 vnet_feature_next(vnet_buffer(b1)->sw_if_index[VLIB_TX],
477                                   &next1, b1);
478
479             if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
480             {
481                 dvr_dpo_trace_t *tr0;
482
483                 tr0 = vlib_add_trace (vm, node, b0, sizeof (*tr0));
484                 tr0->sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_TX];
485             }
486             if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
487             {
488                 dvr_dpo_trace_t *tr1;
489
490                 tr1 = vlib_add_trace (vm, node, b1, sizeof (*tr1));
491                 tr1->sw_if_index = vnet_buffer(b1)->sw_if_index[VLIB_TX];
492             }
493
494             vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
495                                             n_left_to_next, bi0, bi1,
496                                             next0, next1);
497         }
498
499         while (n_left_from > 0 && n_left_to_next > 0)
500         {
501             dvr_reinject_next_t next0;
502             vlib_buffer_t * b0;
503             u32 bi0;
504
505             bi0 = from[0];
506             to_next[0] = bi0;
507             from += 1;
508             to_next += 1;
509             n_left_from -= 1;
510             n_left_to_next -= 1;
511
512             b0 = vlib_get_buffer (vm, bi0);
513
514             if (vnet_buffer(b0)->flags & VNET_OPAQUE_F_IS_DVR)
515                 next0 = DVR_REINJECT_OUTPUT;
516             else
517                 vnet_feature_next(vnet_buffer(b0)->sw_if_index[VLIB_TX],
518                                   &next0, b0);
519
520             if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
521             {
522                 dvr_dpo_trace_t *tr;
523
524                 tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
525                 tr->sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_TX];
526             }
527
528             vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
529                                             n_left_to_next, bi0, next0);
530         }
531         vlib_put_next_frame (vm, node, next_index, n_left_to_next);
532     }
533     return from_frame->n_vectors;
534 }
535
536 static uword
537 ip4_dvr_reinject (vlib_main_t * vm,
538                   vlib_node_runtime_t * node,
539                   vlib_frame_t * from_frame)
540 {
541     return (dvr_reinject_inline(vm, node, from_frame));
542 }
543
544 static uword
545 ip6_dvr_reinject (vlib_main_t * vm,
546                   vlib_node_runtime_t * node,
547                   vlib_frame_t * from_frame)
548 {
549     return (dvr_reinject_inline(vm, node, from_frame));
550 }
551
552 VLIB_REGISTER_NODE (ip4_dvr_reinject_node) = {
553     .function = ip4_dvr_reinject,
554     .name = "ip4-dvr-reinject",
555     .vector_size = sizeof (u32),
556     .format_trace = format_dvr_dpo_trace,
557
558     .n_next_nodes = 1,
559     .next_nodes = {
560         [DVR_REINJECT_OUTPUT] = "l2-output",
561     },
562 };
563
564 VLIB_REGISTER_NODE (ip6_dvr_reinject_node) = {
565     .function = ip6_dvr_reinject,
566     .name = "ip6-dvr-reinject",
567     .vector_size = sizeof (u32),
568     .format_trace = format_dvr_dpo_trace,
569
570     .n_next_nodes = 1,
571     .next_nodes = {
572         [DVR_REINJECT_OUTPUT] = "l2-output",
573     },
574 };
575
576 VNET_FEATURE_INIT (ip4_dvr_reinject_feat_node, static) =
577 {
578   .arc_name = "ip4-output",
579   .node_name = "ip4-dvr-reinject",
580   .runs_after = VNET_FEATURES ("nat44-in2out-output",
581                                "acl-plugin-out-ip4-fa"),
582 };
583 VNET_FEATURE_INIT (ip6_dvr_reinject_feat_node, static) =
584 {
585   .arc_name = "ip6-output",
586   .node_name = "ip6-dvr-reinject",
587   .runs_after = VNET_FEATURES ("acl-plugin-out-ip6-fa"),
588 };
589
590 VLIB_NODE_FUNCTION_MULTIARCH (ip4_dvr_reinject_node, ip4_dvr_reinject)
591 VLIB_NODE_FUNCTION_MULTIARCH (ip6_dvr_reinject_node, ip6_dvr_reinject)