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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
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 #include <vnet/l2/l2_input.h>
22 #ifndef CLIB_MARCH_VARIANT
23 dvr_dpo_t *dvr_dpo_pool;
26 * The 'DB' of DVR DPOs.
27 * There is one per-interface per-L3 proto, so this is a per-interface vector
29 static index_t *dvr_dpo_db[DPO_PROTO_NUM];
36 pool_get(dvr_dpo_pool, dd);
41 static inline dvr_dpo_t *
42 dvr_dpo_get_from_dpo (const dpo_id_t *dpo)
44 ASSERT(DPO_DVR == dpo->dpoi_type);
46 return (dvr_dpo_get(dpo->dpoi_index));
50 dvr_dpo_get_index (dvr_dpo_t *dd)
52 return (dd - dvr_dpo_pool);
56 dvr_dpo_lock (dpo_id_t *dpo)
60 dd = dvr_dpo_get_from_dpo(dpo);
65 dvr_dpo_unlock (dpo_id_t *dpo)
69 dd = dvr_dpo_get_from_dpo(dpo);
72 if (0 == dd->dd_locks)
74 if (DPO_PROTO_IP4 == dd->dd_proto)
76 vnet_feature_enable_disable ("ip4-output", "ip4-dvr-reinject",
77 dd->dd_sw_if_index, 0, 0, 0);
81 vnet_feature_enable_disable ("ip6-output", "ip6-dvr-reinject",
82 dd->dd_sw_if_index, 0, 0, 0);
85 dvr_dpo_db[dd->dd_proto][dd->dd_sw_if_index] = INDEX_INVALID;
86 pool_put(dvr_dpo_pool, dd);
91 dvr_dpo_add_or_lock (u32 sw_if_index,
95 l2_input_config_t *config;
98 vec_validate_init_empty(dvr_dpo_db[dproto],
102 if (INDEX_INVALID == dvr_dpo_db[dproto][sw_if_index])
104 dd = dvr_dpo_alloc();
106 dd->dd_sw_if_index = sw_if_index;
107 dd->dd_proto = dproto;
109 dvr_dpo_db[dproto][sw_if_index] = dvr_dpo_get_index(dd);
111 config = l2input_intf_config (sw_if_index);
113 if (l2_input_is_bridge(config) ||
114 l2_input_is_xconnect(config))
116 dd->dd_reinject = DVR_REINJECT_L2;
120 dd->dd_reinject = DVR_REINJECT_L3;
124 * enable the reinject into L2 path feature on the interface
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);
137 dd = dvr_dpo_get(dvr_dpo_db[dproto][sw_if_index]);
140 dpo_set(dpo, DPO_DVR, dproto, dvr_dpo_get_index(dd));
142 #endif /* CLIB_MARCH_VARIANT */
145 static clib_error_t *
146 dvr_dpo_interface_state_change (vnet_main_t * vnm,
155 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(
156 dvr_dpo_interface_state_change);
159 * @brief Registered callback for HW interface state changes
161 static clib_error_t *
162 dvr_dpo_hw_interface_state_change (vnet_main_t * vnm,
169 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION(
170 dvr_dpo_hw_interface_state_change);
172 static clib_error_t *
173 dvr_dpo_interface_delete (vnet_main_t * vnm,
180 VNET_SW_INTERFACE_ADD_DEL_FUNCTION(
181 dvr_dpo_interface_delete);
183 #ifndef CLIB_MARCH_VARIANT
185 format_dvr_reinject (u8* s, va_list *ap)
187 dvr_dpo_reinject_t ddr = va_arg(*ap, int);
191 case DVR_REINJECT_L2:
192 s = format (s, "l2");
194 case DVR_REINJECT_L3:
195 s = format (s, "l3");
202 format_dvr_dpo (u8* s, va_list *ap)
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);
209 return format (s, "%U-dvr-%U-dpo %U", format_dpo_proto, dd->dd_proto,
210 format_vnet_sw_if_index_name, vnm, dd->dd_sw_if_index,
211 format_dvr_reinject, dd->dd_reinject);
215 dvr_dpo_mem_show (void)
217 fib_show_memory_usage("DVR",
218 pool_elts(dvr_dpo_pool),
219 pool_len(dvr_dpo_pool),
224 const static dpo_vft_t dvr_dpo_vft = {
225 .dv_lock = dvr_dpo_lock,
226 .dv_unlock = dvr_dpo_unlock,
227 .dv_format = format_dvr_dpo,
228 .dv_mem_show = dvr_dpo_mem_show,
232 * @brief The per-protocol VLIB graph nodes that are assigned to a glean
235 * this means that these graph nodes are ones from which a glean is the
236 * parent object in the DPO-graph.
238 const static char* const dvr_dpo_ip4_nodes[] =
243 const static char* const dvr_dpo_ip6_nodes[] =
249 const static char* const * const dvr_dpo_nodes[DPO_PROTO_NUM] =
251 [DPO_PROTO_IP4] = dvr_dpo_ip4_nodes,
252 [DPO_PROTO_IP6] = dvr_dpo_ip6_nodes,
256 dvr_dpo_module_init (void)
258 dpo_register(DPO_DVR,
262 #endif /* CLIB_MARCH_VARIANT */
265 * @brief Interface DPO trace data
267 typedef struct dvr_dpo_trace_t_
273 dvr_dpo_inline (vlib_main_t * vm,
274 vlib_node_runtime_t * node,
275 vlib_frame_t * from_frame,
278 u32 n_left_from, next_index, * from, * to_next;
279 ip_lookup_main_t *lm = (is_ip6?
280 &ip6_main.lookup_main:
281 &ip4_main.lookup_main);
283 from = vlib_frame_vector_args (from_frame);
284 n_left_from = from_frame->n_vectors;
286 next_index = node->cached_next_index;
288 while (n_left_from > 0)
292 vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
294 while (n_left_from >= 4 && n_left_to_next > 2)
296 const dvr_dpo_t *dd0, *dd1;
297 u32 bi0, ddi0, bi1, ddi1;
298 vlib_buffer_t *b0, *b1;
312 b0 = vlib_get_buffer (vm, bi0);
313 b1 = vlib_get_buffer (vm, bi1);
315 ddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
316 ddi1 = vnet_buffer(b1)->ip.adj_index[VLIB_TX];
317 dd0 = dvr_dpo_get(ddi0);
318 dd1 = dvr_dpo_get(ddi1);
320 vnet_buffer(b0)->sw_if_index[VLIB_TX] = dd0->dd_sw_if_index;
321 vnet_buffer(b1)->sw_if_index[VLIB_TX] = dd1->dd_sw_if_index;
323 len0 = ((u8*)vlib_buffer_get_current(b0) -
324 (u8*)ethernet_buffer_get_header(b0));
325 len1 = ((u8*)vlib_buffer_get_current(b1) -
326 (u8*)ethernet_buffer_get_header(b1));
327 vnet_buffer(b0)->l2.l2_len =
328 vnet_buffer(b0)->ip.save_rewrite_length =
330 vnet_buffer(b1)->l2.l2_len =
331 vnet_buffer(b1)->ip.save_rewrite_length =
334 b0->flags |= VNET_BUFFER_F_IS_DVR;
335 b1->flags |= VNET_BUFFER_F_IS_DVR;
337 vlib_buffer_advance(b0, -len0);
338 vlib_buffer_advance(b1, -len1);
340 vnet_feature_arc_start (lm->output_feature_arc_index,
341 dd0->dd_sw_if_index, &next0, b0);
342 vnet_feature_arc_start (lm->output_feature_arc_index,
343 dd1->dd_sw_if_index, &next1, b1);
345 if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
347 dvr_dpo_trace_t *tr0;
349 tr0 = vlib_add_trace (vm, node, b0, sizeof (*tr0));
350 tr0->sw_if_index = dd0->dd_sw_if_index;
352 if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
354 dvr_dpo_trace_t *tr1;
356 tr1 = vlib_add_trace (vm, node, b1, sizeof (*tr1));
357 tr1->sw_if_index = dd1->dd_sw_if_index;
360 vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
361 n_left_to_next, bi0, bi1,
365 while (n_left_from > 0 && n_left_to_next > 0)
367 const dvr_dpo_t * dd0;
381 b0 = vlib_get_buffer (vm, bi0);
383 ddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
384 dd0 = dvr_dpo_get(ddi0);
386 vnet_buffer(b0)->sw_if_index[VLIB_TX] = dd0->dd_sw_if_index;
389 * take that, rewind it back...
391 len0 = ((u8*)vlib_buffer_get_current(b0) -
392 (u8*)ethernet_buffer_get_header(b0));
393 vnet_buffer(b0)->l2.l2_len =
394 vnet_buffer(b0)->ip.save_rewrite_length =
396 b0->flags |= VNET_BUFFER_F_IS_DVR;
397 vlib_buffer_advance(b0, -len0);
400 * start processing the ipX output features
402 vnet_feature_arc_start(lm->output_feature_arc_index,
403 dd0->dd_sw_if_index, &next0, b0);
405 if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
409 tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
410 tr->sw_if_index = dd0->dd_sw_if_index;
413 vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
417 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
419 return from_frame->n_vectors;
423 format_dvr_dpo_trace (u8 * s, va_list * args)
425 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
426 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
427 dvr_dpo_trace_t * t = va_arg (*args, dvr_dpo_trace_t *);
428 u32 indent = format_get_indent (s);
429 s = format (s, "%U sw_if_index:%d",
430 format_white_space, indent,
435 VLIB_NODE_FN (ip4_dvr_dpo_node) (vlib_main_t * vm,
436 vlib_node_runtime_t * node,
437 vlib_frame_t * from_frame)
439 return (dvr_dpo_inline(vm, node, from_frame, 0));
442 VLIB_NODE_FN (ip6_dvr_dpo_node) (vlib_main_t * vm,
443 vlib_node_runtime_t * node,
444 vlib_frame_t * from_frame)
446 return (dvr_dpo_inline(vm, node, from_frame, 1));
449 VLIB_REGISTER_NODE (ip4_dvr_dpo_node) = {
450 .name = "ip4-dvr-dpo",
451 .vector_size = sizeof (u32),
452 .format_trace = format_dvr_dpo_trace,
453 .sibling_of = "ip4-rewrite",
455 VLIB_REGISTER_NODE (ip6_dvr_dpo_node) = {
456 .name = "ip6-dvr-dpo",
457 .vector_size = sizeof (u32),
458 .format_trace = format_dvr_dpo_trace,
459 .sibling_of = "ip6-rewrite",
462 typedef enum dvr_reinject_next_t_
464 DVR_REINJECT_NEXT_L2,
465 DVR_REINJECT_NEXT_L3,
466 } dvr_reinject_next_t;
469 dvr_reinject_inline (vlib_main_t * vm,
470 vlib_node_runtime_t * node,
471 vlib_frame_t * from_frame)
473 u32 n_left_from, next_index, * from, * to_next;
475 from = vlib_frame_vector_args (from_frame);
476 n_left_from = from_frame->n_vectors;
478 next_index = node->cached_next_index;
480 while (n_left_from > 0)
484 vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
486 while (n_left_from >= 4 && n_left_to_next > 2)
488 dvr_reinject_next_t next0, next1;
489 const dvr_dpo_t *dd0, *dd1;
490 u32 bi0, bi1, ddi0, ddi1;
491 vlib_buffer_t *b0, *b1;
502 b0 = vlib_get_buffer (vm, bi0);
503 b1 = vlib_get_buffer (vm, bi1);
505 if (b0->flags & VNET_BUFFER_F_IS_DVR)
507 ddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
508 dd0 = dvr_dpo_get(ddi0);
509 next0 = (dd0->dd_reinject == DVR_REINJECT_L2 ?
510 DVR_REINJECT_NEXT_L2 :
511 DVR_REINJECT_NEXT_L3);
514 vnet_feature_next( &next0, b0);
516 if (b1->flags & VNET_BUFFER_F_IS_DVR)
518 ddi1 = vnet_buffer(b1)->ip.adj_index[VLIB_TX];
519 dd1 = dvr_dpo_get(ddi1);
520 next1 = (dd1->dd_reinject == DVR_REINJECT_L2 ?
521 DVR_REINJECT_NEXT_L2 :
522 DVR_REINJECT_NEXT_L3);
525 vnet_feature_next( &next1, b1);
527 if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
529 dvr_dpo_trace_t *tr0;
531 tr0 = vlib_add_trace (vm, node, b0, sizeof (*tr0));
532 tr0->sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_TX];
534 if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
536 dvr_dpo_trace_t *tr1;
538 tr1 = vlib_add_trace (vm, node, b1, sizeof (*tr1));
539 tr1->sw_if_index = vnet_buffer(b1)->sw_if_index[VLIB_TX];
542 vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
543 n_left_to_next, bi0, bi1,
547 while (n_left_from > 0 && n_left_to_next > 0)
549 dvr_reinject_next_t next0;
550 const dvr_dpo_t *dd0;
561 b0 = vlib_get_buffer (vm, bi0);
563 if (b0->flags & VNET_BUFFER_F_IS_DVR)
565 ddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
566 dd0 = dvr_dpo_get(ddi0);
567 next0 = (dd0->dd_reinject == DVR_REINJECT_L2 ?
568 DVR_REINJECT_NEXT_L2 :
569 DVR_REINJECT_NEXT_L3);
572 vnet_feature_next( &next0, b0);
574 if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
578 tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
579 tr->sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_TX];
582 vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
583 n_left_to_next, bi0, next0);
585 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
587 return from_frame->n_vectors;
590 VLIB_NODE_FN (ip4_dvr_reinject_node) (vlib_main_t * vm,
591 vlib_node_runtime_t * node,
592 vlib_frame_t * from_frame)
594 return (dvr_reinject_inline(vm, node, from_frame));
597 VLIB_NODE_FN (ip6_dvr_reinject_node) (vlib_main_t * vm,
598 vlib_node_runtime_t * node,
599 vlib_frame_t * from_frame)
601 return (dvr_reinject_inline(vm, node, from_frame));
604 VLIB_REGISTER_NODE (ip4_dvr_reinject_node) = {
605 .name = "ip4-dvr-reinject",
606 .vector_size = sizeof (u32),
607 .format_trace = format_dvr_dpo_trace,
611 [DVR_REINJECT_NEXT_L2] = "l2-output",
612 [DVR_REINJECT_NEXT_L3] = "interface-output",
616 VLIB_REGISTER_NODE (ip6_dvr_reinject_node) = {
617 .name = "ip6-dvr-reinject",
618 .vector_size = sizeof (u32),
619 .format_trace = format_dvr_dpo_trace,
623 [DVR_REINJECT_NEXT_L2] = "l2-output",
624 [DVR_REINJECT_NEXT_L3] = "interface-output",
628 VNET_FEATURE_INIT (ip4_dvr_reinject_feat_node, static) =
630 .arc_name = "ip4-output",
631 .node_name = "ip4-dvr-reinject",
633 VNET_FEATURE_INIT (ip6_dvr_reinject_feat_node, static) =
635 .arc_name = "ip6-output",
636 .node_name = "ip6-dvr-reinject",