blob: 533ce4b39f39d3727899ea5a15c60974abdbe5e0 [file] [log] [blame]
Neale Rannsf068c3e2018-01-03 04:18:48 -08001/*
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>
Neale Rannsa342da22019-06-06 10:35:07 +000020#include <vnet/l2/l2_input.h>
Neale Rannsf068c3e2018-01-03 04:18:48 -080021
Filip Tehlareb9a27f2019-03-07 01:42:11 -080022#ifndef CLIB_MARCH_VARIANT
BenoƮt Ganne47727c02019-02-12 13:35:08 +010023dvr_dpo_t *dvr_dpo_pool;
24
Neale Rannsf068c3e2018-01-03 04:18:48 -080025/**
26 * The 'DB' of DVR DPOs.
27 * There is one per-interface per-L3 proto, so this is a per-interface vector
28 */
29static index_t *dvr_dpo_db[DPO_PROTO_NUM];
30
31static dvr_dpo_t *
32dvr_dpo_alloc (void)
33{
34 dvr_dpo_t *dd;
35
36 pool_get(dvr_dpo_pool, dd);
37
38 return (dd);
39}
40
41static inline dvr_dpo_t *
42dvr_dpo_get_from_dpo (const dpo_id_t *dpo)
43{
44 ASSERT(DPO_DVR == dpo->dpoi_type);
45
46 return (dvr_dpo_get(dpo->dpoi_index));
47}
48
49static inline index_t
50dvr_dpo_get_index (dvr_dpo_t *dd)
51{
52 return (dd - dvr_dpo_pool);
53}
54
55static void
56dvr_dpo_lock (dpo_id_t *dpo)
57{
58 dvr_dpo_t *dd;
59
60 dd = dvr_dpo_get_from_dpo(dpo);
61 dd->dd_locks++;
62}
63
64static void
65dvr_dpo_unlock (dpo_id_t *dpo)
66{
67 dvr_dpo_t *dd;
68
69 dd = dvr_dpo_get_from_dpo(dpo);
70 dd->dd_locks--;
71
72 if (0 == dd->dd_locks)
73 {
74 if (DPO_PROTO_IP4 == dd->dd_proto)
75 {
76 vnet_feature_enable_disable ("ip4-output", "ip4-dvr-reinject",
77 dd->dd_sw_if_index, 0, 0, 0);
78 }
79 else
80 {
81 vnet_feature_enable_disable ("ip6-output", "ip6-dvr-reinject",
82 dd->dd_sw_if_index, 0, 0, 0);
83 }
84
85 dvr_dpo_db[dd->dd_proto][dd->dd_sw_if_index] = INDEX_INVALID;
86 pool_put(dvr_dpo_pool, dd);
87 }
88}
89
90void
91dvr_dpo_add_or_lock (u32 sw_if_index,
92 dpo_proto_t dproto,
93 dpo_id_t *dpo)
94{
Neale Rannsa342da22019-06-06 10:35:07 +000095 l2_input_config_t *config;
Neale Rannsf068c3e2018-01-03 04:18:48 -080096 dvr_dpo_t *dd;
97
98 vec_validate_init_empty(dvr_dpo_db[dproto],
99 sw_if_index,
100 INDEX_INVALID);
101
102 if (INDEX_INVALID == dvr_dpo_db[dproto][sw_if_index])
103 {
104 dd = dvr_dpo_alloc();
105
106 dd->dd_sw_if_index = sw_if_index;
107 dd->dd_proto = dproto;
108
109 dvr_dpo_db[dproto][sw_if_index] = dvr_dpo_get_index(dd);
110
Neale Rannsa342da22019-06-06 10:35:07 +0000111 config = l2input_intf_config (sw_if_index);
112
Neale Ranns47a3d992020-09-29 15:38:51 +0000113 if (l2_input_is_bridge(config) ||
114 l2_input_is_xconnect(config))
Neale Rannsa342da22019-06-06 10:35:07 +0000115 {
116 dd->dd_reinject = DVR_REINJECT_L2;
117 }
118 else
119 {
120 dd->dd_reinject = DVR_REINJECT_L3;
121 }
122
Neale Rannsf068c3e2018-01-03 04:18:48 -0800123 /*
124 * enable the reinject into L2 path feature on the interface
125 */
126 if (DPO_PROTO_IP4 == dproto)
127 vnet_feature_enable_disable ("ip4-output", "ip4-dvr-reinject",
128 dd->dd_sw_if_index, 1, 0, 0);
129 else if (DPO_PROTO_IP6 == dproto)
130 vnet_feature_enable_disable ("ip6-output", "ip6-dvr-reinject",
131 dd->dd_sw_if_index, 1, 0, 0);
132 else
133 ASSERT(0);
134 }
135 else
136 {
137 dd = dvr_dpo_get(dvr_dpo_db[dproto][sw_if_index]);
138 }
139
140 dpo_set(dpo, DPO_DVR, dproto, dvr_dpo_get_index(dd));
141}
Filip Tehlareb9a27f2019-03-07 01:42:11 -0800142#endif /* CLIB_MARCH_VARIANT */
Neale Rannsf068c3e2018-01-03 04:18:48 -0800143
144
145static clib_error_t *
146dvr_dpo_interface_state_change (vnet_main_t * vnm,
147 u32 sw_if_index,
148 u32 flags)
149{
150 /*
151 */
152 return (NULL);
153}
154
155VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(
156 dvr_dpo_interface_state_change);
157
158/**
159 * @brief Registered callback for HW interface state changes
160 */
161static clib_error_t *
162dvr_dpo_hw_interface_state_change (vnet_main_t * vnm,
163 u32 hw_if_index,
164 u32 flags)
165{
166 return (NULL);
167}
168
169VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION(
170 dvr_dpo_hw_interface_state_change);
171
172static clib_error_t *
173dvr_dpo_interface_delete (vnet_main_t * vnm,
174 u32 sw_if_index,
175 u32 is_add)
176{
177 return (NULL);
178}
179
180VNET_SW_INTERFACE_ADD_DEL_FUNCTION(
181 dvr_dpo_interface_delete);
182
Filip Tehlareb9a27f2019-03-07 01:42:11 -0800183#ifndef CLIB_MARCH_VARIANT
184static u8*
Neale Rannsa342da22019-06-06 10:35:07 +0000185format_dvr_reinject (u8* s, va_list *ap)
186{
187 dvr_dpo_reinject_t ddr = va_arg(*ap, int);
188
189 switch (ddr)
190 {
191 case DVR_REINJECT_L2:
192 s = format (s, "l2");
193 break;
194 case DVR_REINJECT_L3:
195 s = format (s, "l3");
196 break;
197 }
198 return (s);
199}
200
201static u8*
Neale Rannsf068c3e2018-01-03 04:18:48 -0800202format_dvr_dpo (u8* s, va_list *ap)
203{
204 index_t index = va_arg(*ap, index_t);
205 CLIB_UNUSED(u32 indent) = va_arg(*ap, u32);
206 vnet_main_t * vnm = vnet_get_main();
207 dvr_dpo_t *dd = dvr_dpo_get(index);
208
Neale Rannsa342da22019-06-06 10:35:07 +0000209 return (format(s, "%U-dvr-%U-dpo %U",
210 format_dpo_proto, dd->dd_proto,
Neale Rannsf068c3e2018-01-03 04:18:48 -0800211 format_vnet_sw_interface_name,
212 vnm,
Neale Rannsa342da22019-06-06 10:35:07 +0000213 vnet_get_sw_interface(vnm, dd->dd_sw_if_index),
214 format_dvr_reinject, dd->dd_reinject));
Neale Rannsf068c3e2018-01-03 04:18:48 -0800215}
216
217static void
218dvr_dpo_mem_show (void)
219{
220 fib_show_memory_usage("DVR",
221 pool_elts(dvr_dpo_pool),
222 pool_len(dvr_dpo_pool),
223 sizeof(dvr_dpo_t));
224}
225
226
227const static dpo_vft_t dvr_dpo_vft = {
228 .dv_lock = dvr_dpo_lock,
229 .dv_unlock = dvr_dpo_unlock,
230 .dv_format = format_dvr_dpo,
231 .dv_mem_show = dvr_dpo_mem_show,
232};
233
234/**
235 * @brief The per-protocol VLIB graph nodes that are assigned to a glean
236 * object.
237 *
238 * this means that these graph nodes are ones from which a glean is the
239 * parent object in the DPO-graph.
240 */
241const static char* const dvr_dpo_ip4_nodes[] =
242{
243 "ip4-dvr-dpo",
244 NULL,
245};
246const static char* const dvr_dpo_ip6_nodes[] =
247{
248 "ip6-dvr-dpo",
249 NULL,
250};
251
252const static char* const * const dvr_dpo_nodes[DPO_PROTO_NUM] =
253{
254 [DPO_PROTO_IP4] = dvr_dpo_ip4_nodes,
255 [DPO_PROTO_IP6] = dvr_dpo_ip6_nodes,
256};
257
258void
259dvr_dpo_module_init (void)
260{
261 dpo_register(DPO_DVR,
262 &dvr_dpo_vft,
263 dvr_dpo_nodes);
264}
Filip Tehlareb9a27f2019-03-07 01:42:11 -0800265#endif /* CLIB_MARCH_VARIANT */
Neale Rannsf068c3e2018-01-03 04:18:48 -0800266
267/**
268 * @brief Interface DPO trace data
269 */
270typedef struct dvr_dpo_trace_t_
271{
272 u32 sw_if_index;
273} dvr_dpo_trace_t;
274
275always_inline uword
276dvr_dpo_inline (vlib_main_t * vm,
277 vlib_node_runtime_t * node,
278 vlib_frame_t * from_frame,
279 u8 is_ip6)
280{
281 u32 n_left_from, next_index, * from, * to_next;
282 ip_lookup_main_t *lm = (is_ip6?
283 &ip6_main.lookup_main:
284 &ip4_main.lookup_main);
285
286 from = vlib_frame_vector_args (from_frame);
287 n_left_from = from_frame->n_vectors;
288
289 next_index = node->cached_next_index;
290
291 while (n_left_from > 0)
292 {
293 u32 n_left_to_next;
294
295 vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
296
297 while (n_left_from >= 4 && n_left_to_next > 2)
298 {
299 const dvr_dpo_t *dd0, *dd1;
300 u32 bi0, ddi0, bi1, ddi1;
301 vlib_buffer_t *b0, *b1;
302 u32 next0, next1;
303 u8 len0, len1;
304
305 bi0 = from[0];
306 to_next[0] = bi0;
307 bi1 = from[1];
308 to_next[1] = bi1;
309 from += 2;
310 to_next += 2;
311 n_left_from -= 2;
312 n_left_to_next -= 2;
313 next0 = next1 = 0;
314
315 b0 = vlib_get_buffer (vm, bi0);
316 b1 = vlib_get_buffer (vm, bi1);
317
318 ddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
319 ddi1 = vnet_buffer(b1)->ip.adj_index[VLIB_TX];
320 dd0 = dvr_dpo_get(ddi0);
321 dd1 = dvr_dpo_get(ddi1);
322
323 vnet_buffer(b0)->sw_if_index[VLIB_TX] = dd0->dd_sw_if_index;
324 vnet_buffer(b1)->sw_if_index[VLIB_TX] = dd1->dd_sw_if_index;
325
326 len0 = ((u8*)vlib_buffer_get_current(b0) -
327 (u8*)ethernet_buffer_get_header(b0));
328 len1 = ((u8*)vlib_buffer_get_current(b1) -
329 (u8*)ethernet_buffer_get_header(b1));
Neale Ranns7bf3f9f2018-04-09 02:25:27 -0700330 vnet_buffer(b0)->l2.l2_len =
331 vnet_buffer(b0)->ip.save_rewrite_length =
332 len0;
333 vnet_buffer(b1)->l2.l2_len =
334 vnet_buffer(b1)->ip.save_rewrite_length =
335 len1;
Neale Rannsa342da22019-06-06 10:35:07 +0000336
Damjan Mariondac03522018-02-01 15:30:13 +0100337 b0->flags |= VNET_BUFFER_F_IS_DVR;
338 b1->flags |= VNET_BUFFER_F_IS_DVR;
Neale Rannsf068c3e2018-01-03 04:18:48 -0800339
340 vlib_buffer_advance(b0, -len0);
341 vlib_buffer_advance(b1, -len1);
342
343 vnet_feature_arc_start (lm->output_feature_arc_index,
344 dd0->dd_sw_if_index, &next0, b0);
345 vnet_feature_arc_start (lm->output_feature_arc_index,
346 dd1->dd_sw_if_index, &next1, b1);
347
348 if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
349 {
350 dvr_dpo_trace_t *tr0;
351
352 tr0 = vlib_add_trace (vm, node, b0, sizeof (*tr0));
353 tr0->sw_if_index = dd0->dd_sw_if_index;
354 }
355 if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
356 {
357 dvr_dpo_trace_t *tr1;
358
359 tr1 = vlib_add_trace (vm, node, b1, sizeof (*tr1));
360 tr1->sw_if_index = dd1->dd_sw_if_index;
361 }
362
363 vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
364 n_left_to_next, bi0, bi1,
365 next0, next1);
366 }
367
368 while (n_left_from > 0 && n_left_to_next > 0)
369 {
370 const dvr_dpo_t * dd0;
371 vlib_buffer_t * b0;
372 u32 bi0, ddi0;
373 u32 next0;
374 u8 len0;
375
376 bi0 = from[0];
377 to_next[0] = bi0;
378 from += 1;
379 to_next += 1;
380 n_left_from -= 1;
381 n_left_to_next -= 1;
382 next0 = 0;
383
384 b0 = vlib_get_buffer (vm, bi0);
385
386 ddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
387 dd0 = dvr_dpo_get(ddi0);
388
389 vnet_buffer(b0)->sw_if_index[VLIB_TX] = dd0->dd_sw_if_index;
390
391 /*
392 * take that, rewind it back...
393 */
394 len0 = ((u8*)vlib_buffer_get_current(b0) -
395 (u8*)ethernet_buffer_get_header(b0));
Neale Ranns7bf3f9f2018-04-09 02:25:27 -0700396 vnet_buffer(b0)->l2.l2_len =
397 vnet_buffer(b0)->ip.save_rewrite_length =
398 len0;
Damjan Mariondac03522018-02-01 15:30:13 +0100399 b0->flags |= VNET_BUFFER_F_IS_DVR;
Neale Rannsf068c3e2018-01-03 04:18:48 -0800400 vlib_buffer_advance(b0, -len0);
401
402 /*
403 * start processing the ipX output features
404 */
405 vnet_feature_arc_start(lm->output_feature_arc_index,
406 dd0->dd_sw_if_index, &next0, b0);
407
408 if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
409 {
410 dvr_dpo_trace_t *tr;
411
412 tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
413 tr->sw_if_index = dd0->dd_sw_if_index;
414 }
415
416 vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
417 n_left_to_next, bi0,
418 next0);
419 }
420 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
421 }
422 return from_frame->n_vectors;
423}
424
425static u8 *
426format_dvr_dpo_trace (u8 * s, va_list * args)
427{
428 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
429 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
430 dvr_dpo_trace_t * t = va_arg (*args, dvr_dpo_trace_t *);
431 u32 indent = format_get_indent (s);
432 s = format (s, "%U sw_if_index:%d",
433 format_white_space, indent,
434 t->sw_if_index);
435 return s;
436}
437
Filip Tehlareb9a27f2019-03-07 01:42:11 -0800438VLIB_NODE_FN (ip4_dvr_dpo_node) (vlib_main_t * vm,
Neale Rannsf068c3e2018-01-03 04:18:48 -0800439 vlib_node_runtime_t * node,
440 vlib_frame_t * from_frame)
441{
442 return (dvr_dpo_inline(vm, node, from_frame, 0));
443}
444
Filip Tehlareb9a27f2019-03-07 01:42:11 -0800445VLIB_NODE_FN (ip6_dvr_dpo_node) (vlib_main_t * vm,
Neale Rannsf068c3e2018-01-03 04:18:48 -0800446 vlib_node_runtime_t * node,
447 vlib_frame_t * from_frame)
448{
449 return (dvr_dpo_inline(vm, node, from_frame, 1));
450}
451
452VLIB_REGISTER_NODE (ip4_dvr_dpo_node) = {
Neale Rannsf068c3e2018-01-03 04:18:48 -0800453 .name = "ip4-dvr-dpo",
454 .vector_size = sizeof (u32),
455 .format_trace = format_dvr_dpo_trace,
456 .sibling_of = "ip4-rewrite",
457};
458VLIB_REGISTER_NODE (ip6_dvr_dpo_node) = {
Neale Rannsf068c3e2018-01-03 04:18:48 -0800459 .name = "ip6-dvr-dpo",
460 .vector_size = sizeof (u32),
461 .format_trace = format_dvr_dpo_trace,
462 .sibling_of = "ip6-rewrite",
463};
464
Neale Rannsf068c3e2018-01-03 04:18:48 -0800465typedef enum dvr_reinject_next_t_
466{
Neale Rannsa342da22019-06-06 10:35:07 +0000467 DVR_REINJECT_NEXT_L2,
468 DVR_REINJECT_NEXT_L3,
Neale Rannsf068c3e2018-01-03 04:18:48 -0800469} dvr_reinject_next_t;
470
471always_inline uword
472dvr_reinject_inline (vlib_main_t * vm,
473 vlib_node_runtime_t * node,
474 vlib_frame_t * from_frame)
475{
476 u32 n_left_from, next_index, * from, * to_next;
477
478 from = vlib_frame_vector_args (from_frame);
479 n_left_from = from_frame->n_vectors;
480
481 next_index = node->cached_next_index;
482
483 while (n_left_from > 0)
484 {
485 u32 n_left_to_next;
486
487 vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
488
489 while (n_left_from >= 4 && n_left_to_next > 2)
490 {
491 dvr_reinject_next_t next0, next1;
Neale Rannsa342da22019-06-06 10:35:07 +0000492 const dvr_dpo_t *dd0, *dd1;
493 u32 bi0, bi1, ddi0, ddi1;
Neale Rannsf068c3e2018-01-03 04:18:48 -0800494 vlib_buffer_t *b0, *b1;
Neale Rannsf068c3e2018-01-03 04:18:48 -0800495
496 bi0 = from[0];
497 to_next[0] = bi0;
498 bi1 = from[1];
499 to_next[1] = bi1;
500 from += 2;
501 to_next += 2;
502 n_left_from -= 2;
503 n_left_to_next -= 2;
504
505 b0 = vlib_get_buffer (vm, bi0);
506 b1 = vlib_get_buffer (vm, bi1);
507
Damjan Mariondac03522018-02-01 15:30:13 +0100508 if (b0->flags & VNET_BUFFER_F_IS_DVR)
Neale Rannsa342da22019-06-06 10:35:07 +0000509 {
510 ddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
511 dd0 = dvr_dpo_get(ddi0);
512 next0 = (dd0->dd_reinject == DVR_REINJECT_L2 ?
513 DVR_REINJECT_NEXT_L2 :
514 DVR_REINJECT_NEXT_L3);
515 }
Neale Rannsf068c3e2018-01-03 04:18:48 -0800516 else
Damjan Marion7d98a122018-07-19 20:42:08 +0200517 vnet_feature_next( &next0, b0);
Neale Rannsf068c3e2018-01-03 04:18:48 -0800518
Damjan Mariondac03522018-02-01 15:30:13 +0100519 if (b1->flags & VNET_BUFFER_F_IS_DVR)
Neale Rannsa342da22019-06-06 10:35:07 +0000520 {
521 ddi1 = vnet_buffer(b1)->ip.adj_index[VLIB_TX];
522 dd1 = dvr_dpo_get(ddi1);
523 next1 = (dd1->dd_reinject == DVR_REINJECT_L2 ?
524 DVR_REINJECT_NEXT_L2 :
525 DVR_REINJECT_NEXT_L3);
526 }
Neale Rannsf068c3e2018-01-03 04:18:48 -0800527 else
Damjan Marion7d98a122018-07-19 20:42:08 +0200528 vnet_feature_next( &next1, b1);
Neale Rannsf068c3e2018-01-03 04:18:48 -0800529
530 if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
531 {
532 dvr_dpo_trace_t *tr0;
533
534 tr0 = vlib_add_trace (vm, node, b0, sizeof (*tr0));
535 tr0->sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_TX];
536 }
537 if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
538 {
539 dvr_dpo_trace_t *tr1;
540
541 tr1 = vlib_add_trace (vm, node, b1, sizeof (*tr1));
542 tr1->sw_if_index = vnet_buffer(b1)->sw_if_index[VLIB_TX];
543 }
544
545 vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
546 n_left_to_next, bi0, bi1,
547 next0, next1);
548 }
549
550 while (n_left_from > 0 && n_left_to_next > 0)
551 {
552 dvr_reinject_next_t next0;
Neale Rannsa342da22019-06-06 10:35:07 +0000553 const dvr_dpo_t *dd0;
Neale Rannsf068c3e2018-01-03 04:18:48 -0800554 vlib_buffer_t * b0;
Neale Rannsa342da22019-06-06 10:35:07 +0000555 u32 bi0, ddi0;
Neale Rannsf068c3e2018-01-03 04:18:48 -0800556
557 bi0 = from[0];
558 to_next[0] = bi0;
559 from += 1;
560 to_next += 1;
561 n_left_from -= 1;
562 n_left_to_next -= 1;
563
564 b0 = vlib_get_buffer (vm, bi0);
565
Damjan Mariondac03522018-02-01 15:30:13 +0100566 if (b0->flags & VNET_BUFFER_F_IS_DVR)
Neale Rannsa342da22019-06-06 10:35:07 +0000567 {
568 ddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
569 dd0 = dvr_dpo_get(ddi0);
570 next0 = (dd0->dd_reinject == DVR_REINJECT_L2 ?
571 DVR_REINJECT_NEXT_L2 :
572 DVR_REINJECT_NEXT_L3);
573 }
Neale Rannsf068c3e2018-01-03 04:18:48 -0800574 else
Damjan Marion7d98a122018-07-19 20:42:08 +0200575 vnet_feature_next( &next0, b0);
Neale Rannsf068c3e2018-01-03 04:18:48 -0800576
577 if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
578 {
579 dvr_dpo_trace_t *tr;
580
581 tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
582 tr->sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_TX];
583 }
584
585 vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
586 n_left_to_next, bi0, next0);
587 }
588 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
589 }
590 return from_frame->n_vectors;
591}
592
Filip Tehlareb9a27f2019-03-07 01:42:11 -0800593VLIB_NODE_FN (ip4_dvr_reinject_node) (vlib_main_t * vm,
Neale Rannsf068c3e2018-01-03 04:18:48 -0800594 vlib_node_runtime_t * node,
595 vlib_frame_t * from_frame)
596{
597 return (dvr_reinject_inline(vm, node, from_frame));
598}
599
Filip Tehlareb9a27f2019-03-07 01:42:11 -0800600VLIB_NODE_FN (ip6_dvr_reinject_node) (vlib_main_t * vm,
Neale Rannsf068c3e2018-01-03 04:18:48 -0800601 vlib_node_runtime_t * node,
602 vlib_frame_t * from_frame)
603{
604 return (dvr_reinject_inline(vm, node, from_frame));
605}
606
607VLIB_REGISTER_NODE (ip4_dvr_reinject_node) = {
Neale Rannsf068c3e2018-01-03 04:18:48 -0800608 .name = "ip4-dvr-reinject",
609 .vector_size = sizeof (u32),
610 .format_trace = format_dvr_dpo_trace,
611
612 .n_next_nodes = 1,
613 .next_nodes = {
Neale Rannsa342da22019-06-06 10:35:07 +0000614 [DVR_REINJECT_NEXT_L2] = "l2-output",
615 [DVR_REINJECT_NEXT_L3] = "interface-output",
Neale Rannsf068c3e2018-01-03 04:18:48 -0800616 },
617};
618
619VLIB_REGISTER_NODE (ip6_dvr_reinject_node) = {
Neale Rannsf068c3e2018-01-03 04:18:48 -0800620 .name = "ip6-dvr-reinject",
621 .vector_size = sizeof (u32),
622 .format_trace = format_dvr_dpo_trace,
623
624 .n_next_nodes = 1,
625 .next_nodes = {
Neale Rannsa342da22019-06-06 10:35:07 +0000626 [DVR_REINJECT_NEXT_L2] = "l2-output",
627 [DVR_REINJECT_NEXT_L3] = "interface-output",
Neale Rannsf068c3e2018-01-03 04:18:48 -0800628 },
629};
630
631VNET_FEATURE_INIT (ip4_dvr_reinject_feat_node, static) =
632{
633 .arc_name = "ip4-output",
634 .node_name = "ip4-dvr-reinject",
635 .runs_after = VNET_FEATURES ("nat44-in2out-output",
636 "acl-plugin-out-ip4-fa"),
637};
638VNET_FEATURE_INIT (ip6_dvr_reinject_feat_node, static) =
639{
640 .arc_name = "ip6-output",
641 .node_name = "ip6-dvr-reinject",
642 .runs_after = VNET_FEATURES ("acl-plugin-out-ip6-fa"),
643};
644