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