2 * interface.c: mpls interfaces
4 * Copyright (c) 2012 Cisco and/or its affiliates.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #include <vnet/vnet.h>
19 #include <vnet/pg/pg.h>
20 #include <vnet/gre/gre.h>
21 #include <vnet/mpls-gre/mpls.h>
23 static uword mpls_gre_set_rewrite (vnet_main_t * vnm,
28 uword max_rewrite_bytes)
31 * Conundrum: packets from tun/tap destined for the tunnel
32 * actually have this rewrite applied. Transit packets do not.
33 * To make the two cases equivalent, don't generate a
34 * rewrite here, build the entire header in the fast path.
39 /* manually added to the interface output node */
40 #define MPLS_GRE_OUTPUT_NEXT_POST_REWRITE 1
43 mpls_gre_interface_tx (vlib_main_t * vm,
44 vlib_node_runtime_t * node,
47 mpls_main_t * gm = &mpls_main;
48 vnet_main_t * vnm = gm->vnet_main;
50 u32 * from, * to_next, n_left_from, n_left_to_next;
52 /* Vector of buffer / pkt indices we're supposed to process */
53 from = vlib_frame_vector_args (frame);
55 /* Number of buffers / pkts */
56 n_left_from = frame->n_vectors;
58 /* Speculatively send the first buffer to the last disposition we used */
59 next_index = node->cached_next_index;
61 while (n_left_from > 0)
63 /* set up to enqueue to our disposition with index = next_index */
64 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
67 * As long as we have enough pkts left to process two pkts
68 * and prefetch two pkts...
70 while (n_left_from >= 4 && n_left_to_next >= 2)
72 vlib_buffer_t * b0, * b1;
73 u32 bi0, next0, bi1, next1;
74 mpls_gre_tunnel_t * t0, * t1;
75 u32 sw_if_index0, sw_if_index1;
76 vnet_hw_interface_t * hi0, * hi1;
79 /* Prefetch the next iteration */
81 vlib_buffer_t * p2, * p3;
83 p2 = vlib_get_buffer (vm, from[2]);
84 p3 = vlib_get_buffer (vm, from[3]);
86 vlib_prefetch_buffer_header (p2, LOAD);
87 vlib_prefetch_buffer_header (p3, LOAD);
90 * Prefetch packet data. We expect to overwrite
91 * the inbound L2 header with an ip header and a
92 * gre header. Might want to prefetch the last line
93 * of rewrite space as well; need profile data
95 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
96 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
99 /* Pick up the next two buffer indices */
103 /* Speculatively enqueue them where we sent the last buffer */
111 b0 = vlib_get_buffer (vm, bi0);
112 b1 = vlib_get_buffer (vm, bi1);
114 sw_if_index0 = vnet_buffer(b0)->sw_if_index [VLIB_TX];
115 sw_if_index1 = vnet_buffer(b1)->sw_if_index [VLIB_TX];
118 hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
119 hi1 = vnet_get_sup_hw_interface (vnm, sw_if_index1);
121 /* hw_instance = tunnel pool index */
122 t0 = pool_elt_at_index (gm->gre_tunnels, hi0->hw_instance);
123 t1 = pool_elt_at_index (gm->gre_tunnels, hi1->hw_instance);
125 /* Apply rewrite - $$$$$ fixme don't use memcpy */
126 vlib_buffer_advance (b0, -(word)vec_len(t0->rewrite_data));
127 vlib_buffer_advance (b1, -(word)vec_len(t1->rewrite_data));
129 dst0 = vlib_buffer_get_current (b0);
130 dst1 = vlib_buffer_get_current (b1);
132 clib_memcpy (dst0, t0->rewrite_data, vec_len(t0->rewrite_data));
133 clib_memcpy (dst1, t1->rewrite_data, vec_len(t1->rewrite_data));
135 /* Fix TX fib indices */
136 vnet_buffer(b0)->sw_if_index [VLIB_TX] = t0->outer_fib_index;
137 vnet_buffer(b1)->sw_if_index [VLIB_TX] = t1->outer_fib_index;
139 /* mpls-post-rewrite takes it from here... */
140 next0 = MPLS_GRE_OUTPUT_NEXT_POST_REWRITE;
141 next1 = MPLS_GRE_OUTPUT_NEXT_POST_REWRITE;
143 if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
145 mpls_gre_tx_trace_t *tr = vlib_add_trace (vm, node,
147 tr->tunnel_id = t0 - gm->gre_tunnels;
148 tr->length = b0->current_length;
149 tr->src.as_u32 = t0->tunnel_src.as_u32;
150 tr->dst.as_u32 = t0->tunnel_dst.as_u32;
152 tr->mpls_encap_index = t0->encap_index;
154 if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
156 mpls_gre_tx_trace_t *tr = vlib_add_trace (vm, node,
158 tr->tunnel_id = t1 - gm->gre_tunnels;
159 tr->length = b1->current_length;
160 tr->src.as_u32 = t1->tunnel_src.as_u32;
161 tr->dst.as_u32 = t1->tunnel_dst.as_u32;
163 tr->mpls_encap_index = t1->encap_index;
166 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
167 to_next, n_left_to_next,
168 bi0, bi1, next0, next1);
171 while (n_left_from > 0 && n_left_to_next > 0)
175 mpls_gre_tunnel_t * t0;
177 vnet_hw_interface_t * hi0;
187 b0 = vlib_get_buffer (vm, bi0);
189 sw_if_index0 = vnet_buffer(b0)->sw_if_index [VLIB_TX];
191 hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
193 t0 = pool_elt_at_index (gm->gre_tunnels, hi0->hw_instance);
195 /* Apply rewrite - $$$$$ fixme don't use memcpy */
196 vlib_buffer_advance (b0, -(word)vec_len(t0->rewrite_data));
198 dst0 = vlib_buffer_get_current (b0);
200 clib_memcpy (dst0, t0->rewrite_data, vec_len(t0->rewrite_data));
202 /* Fix the TX fib index */
203 vnet_buffer(b0)->sw_if_index [VLIB_TX] = t0->outer_fib_index;
205 /* mpls-post-rewrite takes it from here... */
206 next0 = MPLS_GRE_OUTPUT_NEXT_POST_REWRITE;
208 if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
210 mpls_gre_tx_trace_t *tr = vlib_add_trace (vm, node,
212 tr->tunnel_id = t0 - gm->gre_tunnels;
213 tr->length = b0->current_length;
214 tr->src.as_u32 = t0->tunnel_src.as_u32;
215 tr->dst.as_u32 = t0->tunnel_dst.as_u32;
217 tr->mpls_encap_index = t0->encap_index;
220 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
221 to_next, n_left_to_next,
225 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
228 vlib_node_increment_counter (vm, gre_input_node.index,
229 GRE_ERROR_PKTS_ENCAP, frame->n_vectors);
231 return frame->n_vectors;
234 static u8 * format_mpls_gre_tunnel_name (u8 * s, va_list * args)
236 u32 dev_instance = va_arg (*args, u32);
237 return format (s, "mpls-gre%d", dev_instance);
240 static u8 * format_mpls_gre_device (u8 * s, va_list * args)
242 u32 dev_instance = va_arg (*args, u32);
243 CLIB_UNUSED (int verbose) = va_arg (*args, int);
245 s = format (s, "MPLS-GRE tunnel: id %d\n", dev_instance);
249 VNET_DEVICE_CLASS (mpls_gre_device_class) = {
250 .name = "MPLS-GRE tunnel device",
251 .format_device_name = format_mpls_gre_tunnel_name,
252 .format_device = format_mpls_gre_device,
253 .format_tx_trace = format_mpls_gre_tx_trace,
254 .tx_function = mpls_gre_interface_tx,
255 .no_flatten_output_chains = 1,
258 .admin_up_down_function = 0;
262 VLIB_DEVICE_TX_FUNCTION_MULTIARCH (mpls_gre_device_class,
263 mpls_gre_interface_tx)
265 VNET_HW_INTERFACE_CLASS (mpls_gre_hw_interface_class) = {
267 .format_header = format_mpls_gre_header_with_length,
269 .unformat_header = unformat_mpls_gre_header,
271 .set_rewrite = mpls_gre_set_rewrite,
275 static uword mpls_eth_set_rewrite (vnet_main_t * vnm,
280 uword max_rewrite_bytes)
283 * Conundrum: packets from tun/tap destined for the tunnel
284 * actually have this rewrite applied. Transit packets do not.
285 * To make the two cases equivalent, don't generate a
286 * rewrite here, build the entire header in the fast path.
291 /* manually added to the interface output node */
292 #define MPLS_ETH_OUTPUT_NEXT_OUTPUT 1
295 mpls_eth_interface_tx (vlib_main_t * vm,
296 vlib_node_runtime_t * node,
297 vlib_frame_t * frame)
299 mpls_main_t * gm = &mpls_main;
300 vnet_main_t * vnm = gm->vnet_main;
302 u32 * from, * to_next, n_left_from, n_left_to_next;
304 /* Vector of buffer / pkt indices we're supposed to process */
305 from = vlib_frame_vector_args (frame);
307 /* Number of buffers / pkts */
308 n_left_from = frame->n_vectors;
310 /* Speculatively send the first buffer to the last disposition we used */
311 next_index = node->cached_next_index;
313 while (n_left_from > 0)
315 /* set up to enqueue to our disposition with index = next_index */
316 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
319 * As long as we have enough pkts left to process two pkts
320 * and prefetch two pkts...
322 while (n_left_from >= 4 && n_left_to_next >= 2)
324 vlib_buffer_t * b0, * b1;
325 u32 bi0, next0, bi1, next1;
326 mpls_eth_tunnel_t * t0, * t1;
327 u32 sw_if_index0, sw_if_index1;
328 vnet_hw_interface_t * hi0, * hi1;
331 /* Prefetch the next iteration */
333 vlib_buffer_t * p2, * p3;
335 p2 = vlib_get_buffer (vm, from[2]);
336 p3 = vlib_get_buffer (vm, from[3]);
338 vlib_prefetch_buffer_header (p2, LOAD);
339 vlib_prefetch_buffer_header (p3, LOAD);
342 * Prefetch packet data. We expect to overwrite
343 * the inbound L2 header with an ip header and a
344 * gre header. Might want to prefetch the last line
345 * of rewrite space as well; need profile data
347 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
348 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
351 /* Pick up the next two buffer indices */
355 /* Speculatively enqueue them where we sent the last buffer */
363 b0 = vlib_get_buffer (vm, bi0);
364 b1 = vlib_get_buffer (vm, bi1);
366 sw_if_index0 = vnet_buffer(b0)->sw_if_index [VLIB_TX];
367 sw_if_index1 = vnet_buffer(b1)->sw_if_index [VLIB_TX];
370 hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
371 hi1 = vnet_get_sup_hw_interface (vnm, sw_if_index1);
373 /* hw_instance = tunnel pool index */
374 t0 = pool_elt_at_index (gm->eth_tunnels, hi0->hw_instance);
375 t1 = pool_elt_at_index (gm->eth_tunnels, hi1->hw_instance);
377 /* Apply rewrite - $$$$$ fixme don't use memcpy */
378 vlib_buffer_advance (b0, -(word)vec_len(t0->rewrite_data));
379 vlib_buffer_advance (b1, -(word)vec_len(t1->rewrite_data));
381 dst0 = vlib_buffer_get_current (b0);
382 dst1 = vlib_buffer_get_current (b1);
384 clib_memcpy (dst0, t0->rewrite_data, vec_len(t0->rewrite_data));
385 clib_memcpy (dst1, t1->rewrite_data, vec_len(t1->rewrite_data));
387 /* Fix TX fib indices */
388 vnet_buffer(b0)->sw_if_index [VLIB_TX] = t0->tx_sw_if_index;
389 vnet_buffer(b1)->sw_if_index [VLIB_TX] = t1->tx_sw_if_index;
391 /* mpls-post-rewrite takes it from here... */
392 next0 = MPLS_ETH_OUTPUT_NEXT_OUTPUT;
393 next1 = MPLS_ETH_OUTPUT_NEXT_OUTPUT;
395 if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
397 mpls_eth_tx_trace_t *tr = vlib_add_trace (vm, node,
400 tr->tunnel_id = t0 - gm->eth_tunnels;
401 tr->tx_sw_if_index = t0->tx_sw_if_index;
402 tr->mpls_encap_index = t0->encap_index;
403 tr->length = b0->current_length;
404 hi0 = vnet_get_sup_hw_interface (vnm, t0->tx_sw_if_index);
405 clib_memcpy (tr->dst, hi0->hw_address, sizeof (tr->dst));
407 if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
409 mpls_eth_tx_trace_t *tr = vlib_add_trace (vm, node,
412 tr->tunnel_id = t1 - gm->eth_tunnels;
413 tr->tx_sw_if_index = t1->tx_sw_if_index;
414 tr->mpls_encap_index = t1->encap_index;
415 tr->length = b0->current_length;
416 hi1 = vnet_get_sup_hw_interface (vnm, t1->tx_sw_if_index);
417 clib_memcpy (tr->dst, hi1->hw_address, sizeof (tr->dst));
420 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
421 to_next, n_left_to_next,
422 bi0, bi1, next0, next1);
424 while (n_left_from > 0 && n_left_to_next > 0)
428 mpls_eth_tunnel_t * t0;
430 vnet_hw_interface_t * hi0;
440 b0 = vlib_get_buffer (vm, bi0);
442 sw_if_index0 = vnet_buffer(b0)->sw_if_index [VLIB_TX];
444 hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
446 t0 = pool_elt_at_index (gm->eth_tunnels, hi0->hw_instance);
448 /* Apply rewrite - $$$$$ fixme don't use memcpy */
449 vlib_buffer_advance (b0, -(word)vec_len(t0->rewrite_data));
451 dst0 = vlib_buffer_get_current (b0);
453 clib_memcpy (dst0, t0->rewrite_data, vec_len(t0->rewrite_data));
455 /* Fix the TX interface */
456 vnet_buffer(b0)->sw_if_index [VLIB_TX] = t0->tx_sw_if_index;
458 /* Send the packet */
459 next0 = MPLS_ETH_OUTPUT_NEXT_OUTPUT;
461 if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
463 mpls_eth_tx_trace_t *tr = vlib_add_trace (vm, node,
466 tr->tunnel_id = t0 - gm->eth_tunnels;
467 tr->tx_sw_if_index = t0->tx_sw_if_index;
468 tr->mpls_encap_index = t0->encap_index;
469 tr->length = b0->current_length;
470 hi0 = vnet_get_sup_hw_interface (vnm, t0->tx_sw_if_index);
471 clib_memcpy (tr->dst, hi0->hw_address, sizeof (tr->dst));
474 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
475 to_next, n_left_to_next,
479 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
482 vlib_node_increment_counter (vm, mpls_input_node.index,
483 MPLS_ERROR_PKTS_ENCAP, frame->n_vectors);
485 return frame->n_vectors;
488 static u8 * format_mpls_eth_tunnel_name (u8 * s, va_list * args)
490 u32 dev_instance = va_arg (*args, u32);
491 return format (s, "mpls-eth%d", dev_instance);
494 static u8 * format_mpls_eth_device (u8 * s, va_list * args)
496 u32 dev_instance = va_arg (*args, u32);
497 CLIB_UNUSED (int verbose) = va_arg (*args, int);
499 s = format (s, "MPLS-ETH tunnel: id %d\n", dev_instance);
503 VNET_DEVICE_CLASS (mpls_eth_device_class) = {
504 .name = "MPLS-ETH tunnel device",
505 .format_device_name = format_mpls_eth_tunnel_name,
506 .format_device = format_mpls_eth_device,
507 .format_tx_trace = format_mpls_eth_tx_trace,
508 .tx_function = mpls_eth_interface_tx,
509 .no_flatten_output_chains = 1,
512 .admin_up_down_function = 0;
516 VLIB_DEVICE_TX_FUNCTION_MULTIARCH (mpls_eth_device_class,
517 mpls_eth_interface_tx)
519 VNET_HW_INTERFACE_CLASS (mpls_eth_hw_interface_class) = {
521 .format_header = format_mpls_eth_header_with_length,
523 .unformat_header = unformat_mpls_eth_header,
525 .set_rewrite = mpls_eth_set_rewrite,
528 #define foreach_mpls_post_rewrite_next \
529 _ (IP4_LOOKUP, "ip4-lookup")
532 #define _(s,n) MPLS_POST_REWRITE_NEXT_##s,
533 foreach_mpls_post_rewrite_next
535 MPLS_POST_REWRITE_N_NEXT,
536 } mpls_post_rewrite_next_t;
540 mpls_post_rewrite (vlib_main_t * vm,
541 vlib_node_runtime_t * node,
542 vlib_frame_t * from_frame)
544 u32 n_left_from, next_index, * from, * to_next;
545 u16 old_l0 = 0, old_l1 = 0;
547 from = vlib_frame_vector_args (from_frame);
548 n_left_from = from_frame->n_vectors;
550 next_index = node->cached_next_index;
552 while (n_left_from > 0)
556 vlib_get_next_frame (vm, node, next_index,
557 to_next, n_left_to_next);
559 while (n_left_from >= 4 && n_left_to_next >= 2)
562 vlib_buffer_t * b0, * b1;
563 ip4_header_t * ip0, * ip1;
564 u32 next0 = MPLS_POST_REWRITE_NEXT_IP4_LOOKUP;
565 u32 next1 = MPLS_POST_REWRITE_NEXT_IP4_LOOKUP;
567 ip_csum_t sum0, sum1;
569 /* Prefetch next iteration. */
571 vlib_buffer_t * p2, * p3;
573 p2 = vlib_get_buffer (vm, from[2]);
574 p3 = vlib_get_buffer (vm, from[3]);
576 vlib_prefetch_buffer_header (p2, LOAD);
577 vlib_prefetch_buffer_header (p3, LOAD);
579 CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
580 CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
593 b0 = vlib_get_buffer (vm, bi0);
594 b1 = vlib_get_buffer (vm, bi1);
595 ip0 = vlib_buffer_get_current (b0);
596 ip1 = vlib_buffer_get_current (b1);
598 /* Note: the tunnel rewrite sets up sw_if_index[VLIB_TX] */
600 /* set the GRE (outer) ip packet length, fix the bloody checksum */
601 sum0 = ip0->checksum;
602 sum1 = ip1->checksum;
604 /* old_l0, old_l1 always 0, see the rewrite setup */
606 clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
608 clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1));
610 sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
611 length /* changed member */);
612 sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t,
613 length /* changed member */);
614 ip0->checksum = ip_csum_fold (sum0);
615 ip1->checksum = ip_csum_fold (sum1);
616 ip0->length = new_l0;
617 ip1->length = new_l1;
619 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
620 to_next, n_left_to_next,
621 bi0, bi1, next0, next1);
624 while (n_left_from > 0 && n_left_to_next > 0)
629 u32 next0 = MPLS_POST_REWRITE_NEXT_IP4_LOOKUP;
640 b0 = vlib_get_buffer (vm, bi0);
641 ip0 = vlib_buffer_get_current (b0);
643 /* Note: the tunnel rewrite sets up sw_if_index[VLIB_TX] */
645 /* set the GRE (outer) ip packet length, fix the bloody checksum */
646 sum0 = ip0->checksum;
647 /* old_l0 always 0, see the rewrite setup */
649 clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
651 sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
652 length /* changed member */);
653 ip0->checksum = ip_csum_fold (sum0);
654 ip0->length = new_l0;
656 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
657 to_next, n_left_to_next,
661 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
663 vlib_node_increment_counter (vm, mpls_input_node.index,
664 MPLS_ERROR_PKTS_ENCAP, from_frame->n_vectors);
665 return from_frame->n_vectors;
668 VLIB_REGISTER_NODE (mpls_post_rewrite_node) = {
669 .function = mpls_post_rewrite,
670 .name = "mpls-post-rewrite",
671 /* Takes a vector of packets. */
672 .vector_size = sizeof (u32),
674 .runtime_data_bytes = 0,
676 .n_next_nodes = MPLS_POST_REWRITE_N_NEXT,
678 #define _(s,n) [MPLS_POST_REWRITE_NEXT_##s] = n,
679 foreach_mpls_post_rewrite_next
684 VLIB_NODE_FUNCTION_MULTIARCH (mpls_post_rewrite_node, mpls_post_rewrite)
686 static u8 * mpls_gre_rewrite (mpls_main_t *mm, mpls_gre_tunnel_t * t)
689 ip4_gre_and_mpls_header_t * h0;
690 u8 * rewrite_data = 0;
692 mpls_unicast_header_t *lp0;
695 /* look up the encap label stack using the RX FIB */
696 e = mpls_encap_by_fib_and_dest (mm, t->inner_fib_index, t->tunnel_dst.as_u32);
700 clib_warning ("no label for inner fib index %d, dst %U",
701 t->inner_fib_index, format_ip4_address,
706 vec_validate (rewrite_data, sizeof (*h0)
707 + sizeof (mpls_unicast_header_t) * vec_len(e->labels) -1);
708 memset (rewrite_data, 0, sizeof (*h0));
710 h0 = (ip4_gre_and_mpls_header_t *) rewrite_data;
711 /* Copy the encap label stack */
713 for (i = 0; i < vec_len(e->labels); i++)
714 lp0[i] = e->labels[i];
716 h0->gre.protocol = clib_host_to_net_u16(GRE_PROTOCOL_mpls_unicast);
717 ip0->ip_version_and_header_length = 0x45;
719 ip0->protocol = IP_PROTOCOL_GRE;
720 /* $$$ fixup ip4 header length and checksum after-the-fact */
721 ip0->src_address.as_u32 = t->tunnel_src.as_u32;
722 ip0->dst_address.as_u32 = t->tunnel_dst.as_u32;
723 ip0->checksum = ip4_header_checksum (ip0);
725 return (rewrite_data);
728 int vnet_mpls_gre_add_del_tunnel (ip4_address_t *src,
730 ip4_address_t *intfc,
732 u32 inner_fib_id, u32 outer_fib_id,
733 u32 * tunnel_sw_if_index,
737 ip4_main_t * im = &ip4_main;
738 ip_lookup_main_t * lm = &im->lookup_main;
739 mpls_main_t * mm = &mpls_main;
740 vnet_main_t * vnm = vnet_get_main();
742 mpls_gre_tunnel_t *tp;
743 int need_route_add_del = 1;
744 u32 inner_fib_index = 0;
745 u32 outer_fib_index = 0;
749 int found_tunnel = 0;
750 mpls_encap_t * e = 0;
751 u32 hw_if_index = ~0;
752 vnet_hw_interface_t * hi;
758 /* No questions, no answers */
759 if (tunnel_sw_if_index == 0)
760 tunnel_sw_if_index = &dummy;
762 *tunnel_sw_if_index = ~0;
764 if (inner_fib_id != (u32)~0)
768 p = hash_get (im->fib_index_by_table_id, inner_fib_id);
770 return VNET_API_ERROR_NO_SUCH_INNER_FIB;
771 inner_fib_index = p[0];
774 if (outer_fib_id != 0)
778 p = hash_get (im->fib_index_by_table_id, outer_fib_id);
780 return VNET_API_ERROR_NO_SUCH_FIB;
781 outer_fib_index = p[0];
784 /* suppress duplicate mpls interface generation. */
785 pool_foreach (tp, mm->gre_tunnels,
788 * If we have a tunnel which matches (src, dst, intfc/mask)
789 * AND the expected route is in the FIB, it's a dup
791 if (!memcmp (&tp->tunnel_src, src, sizeof (*src))
792 && !memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
793 && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
794 && tp->inner_fib_index == inner_fib_index)
796 ip4_fib_t * fib = vec_elt_at_index (im->fibs, inner_fib_index);
797 uword * hash = fib->adj_index_by_dst_address[mask_width];
798 uword key = intfc->as_u32 & im->fib_masks[mask_width];
799 uword *p = hash_get (hash, key);
805 /* A dup, and the route is in the fib. Done */
810 /* Reinstall the route (and other stuff) */
811 e = mpls_encap_by_fib_and_dest (mm, inner_fib_index,
814 return VNET_API_ERROR_NO_SUCH_LABEL;
820 /* Delete, the route is already gone? */
822 need_route_add_del = 0;
829 /* Delete, and we can't find the tunnel */
830 if (is_add == 0 && found_tunnel == 0)
831 return VNET_API_ERROR_NO_SUCH_ENTRY;
833 e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, dst->as_u32);
835 return VNET_API_ERROR_NO_SUCH_LABEL;
837 pool_get(mm->gre_tunnels, tp);
838 memset (tp, 0, sizeof (*tp));
840 if (vec_len (mm->free_gre_sw_if_indices) > 0)
843 mm->free_gre_sw_if_indices[vec_len(mm->free_gre_sw_if_indices)-1];
844 _vec_len (mm->free_gre_sw_if_indices) -= 1;
845 hi = vnet_get_hw_interface (vnm, hw_if_index);
846 hi->dev_instance = tp - mm->gre_tunnels;
847 hi->hw_instance = tp - mm->gre_tunnels;
851 hw_if_index = vnet_register_interface
852 (vnm, mpls_gre_device_class.index, tp - mm->gre_tunnels,
853 mpls_gre_hw_interface_class.index,
854 tp - mm->gre_tunnels);
855 hi = vnet_get_hw_interface (vnm, hw_if_index);
857 /* ... to make the IP and L2 x-connect cases identical */
858 slot = vlib_node_add_named_next_with_slot
859 (vnm->vlib_main, hi->tx_node_index,
860 "mpls-post-rewrite", MPLS_GRE_OUTPUT_NEXT_POST_REWRITE);
862 ASSERT (slot == MPLS_GRE_OUTPUT_NEXT_POST_REWRITE);
865 *tunnel_sw_if_index = hi->sw_if_index;
866 vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
867 VNET_SW_INTERFACE_FLAG_ADMIN_UP);
869 tp->hw_if_index = hw_if_index;
872 tp->tunnel_src.as_u32 = src->as_u32;
873 tp->tunnel_dst.as_u32 = dst->as_u32;
874 tp->intfc_address.as_u32 = intfc->as_u32;
875 tp->mask_width = mask_width;
876 tp->inner_fib_index = inner_fib_index;
877 tp->outer_fib_index = outer_fib_index;
878 tp->encap_index = e - mm->encaps;
879 tp->l2_only = l2_only;
881 /* Create the adjacency and add to v4 fib */
882 memset(&adj, 0, sizeof (adj));
883 adj.explicit_fib_index = ~0;
884 adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
886 rewrite_data = mpls_gre_rewrite (mm, tp);
887 if (rewrite_data == 0)
889 if (*tunnel_sw_if_index != ~0)
891 hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
892 vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
894 vec_add1 (mm->free_gre_sw_if_indices, tp->hw_if_index);
896 pool_put (mm->gre_tunnels, tp);
897 return VNET_API_ERROR_NO_SUCH_LABEL;
900 /* Save a copy of the rewrite data for L2 x-connect */
901 vec_free (tp->rewrite_data);
903 tp->rewrite_data = rewrite_data;
905 vnet_rewrite_for_tunnel
907 outer_fib_index /* tx_sw_if_index, aka outer fib ID */,
908 ip4_rewrite_node.index,
909 mpls_post_rewrite_node.index,
911 rewrite_data, vec_len(rewrite_data));
914 ip_add_adjacency (lm, &adj, 1 /* one adj */,
919 if (need_route_add_del && !l2_only)
922 ip4_add_del_route_next_hop (im,
926 &zero /* no next hop */,
927 (u32)~0 /* next_hop_sw_if_index */,
930 tp->inner_fib_index);
933 ip4_add_del_route_args_t a;
934 memset (&a, 0, sizeof (a));
936 a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL;
937 a.table_index_or_table_id = tp->inner_fib_index;
938 a.dst_address = tp->intfc_address;
939 a.dst_address_length = tp->mask_width;
942 ip4_add_del_route (im, &a);
943 ip4_maybe_remap_adjacencies (im, tp->inner_fib_index,
944 IP4_ROUTE_FLAG_FIB_INDEX);
948 if (is_add == 0 && found_tunnel)
950 hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
951 vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
953 vec_add1 (mm->free_gre_sw_if_indices, tp->hw_if_index);
954 vec_free (tp->rewrite_data);
955 pool_put (mm->gre_tunnels, tp);
962 * Remove all mpls tunnels in the specified fib
964 int vnet_mpls_gre_delete_fib_tunnels (u32 fib_id)
966 ip4_main_t * im = &ip4_main;
967 mpls_main_t * mm = &mpls_main;
968 vnet_main_t * vnm = mm->vnet_main;
969 mpls_gre_tunnel_t *tp;
972 u32 * tunnels_to_delete = 0;
973 vnet_hw_interface_t * hi;
977 p = hash_get (im->fib_index_by_table_id, fib_id);
979 return VNET_API_ERROR_NO_SUCH_INNER_FIB;
982 pool_foreach (tp, mm->gre_tunnels,
984 if (tp->inner_fib_index == fib_index)
985 vec_add1 (tunnels_to_delete, tp - mm->gre_tunnels);
988 fib = vec_elt_at_index (im->fibs, fib_index);
990 for (i = 0; i < vec_len(tunnels_to_delete); i++) {
991 tp = pool_elt_at_index (mm->gre_tunnels, tunnels_to_delete[i]);
992 uword * hash = fib->adj_index_by_dst_address[tp->mask_width];
993 uword key = tp->intfc_address.as_u32 & im->fib_masks[tp->mask_width];
994 uword *p = hash_get (hash, key);
995 ip4_add_del_route_args_t a;
997 /* Delete, the route if not already gone */
998 if (p && !tp->l2_only)
1000 memset (&a, 0, sizeof (a));
1001 a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL;
1002 a.table_index_or_table_id = tp->inner_fib_index;
1003 a.dst_address = tp->intfc_address;
1004 a.dst_address_length = tp->mask_width;
1006 ip4_add_del_route (im, &a);
1007 ip4_maybe_remap_adjacencies (im, tp->inner_fib_index,
1008 IP4_ROUTE_FLAG_FIB_INDEX);
1011 hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1012 vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
1013 0 /* admin down */);
1014 vec_add1 (mm->free_gre_sw_if_indices, tp->hw_if_index);
1015 vec_free (tp->rewrite_data);
1016 pool_put (mm->gre_tunnels, tp);
1019 vec_free(tunnels_to_delete);
1024 static clib_error_t *
1025 create_mpls_gre_tunnel_command_fn (vlib_main_t * vm,
1026 unformat_input_t * input,
1027 vlib_cli_command_t * cmd)
1029 unformat_input_t _line_input, * line_input = &_line_input;
1030 ip4_address_t src, dst, intfc;
1031 int src_set = 0, dst_set = 0, intfc_set = 0;
1033 u32 inner_fib_id = (u32)~0;
1034 u32 outer_fib_id = 0;
1038 u32 tunnel_intfc_sw_if_index = ~0;
1040 /* Get a line of input. */
1041 if (! unformat_user (input, unformat_line_input, line_input))
1044 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1046 if (unformat (line_input, "src %U",
1047 unformat_ip4_address, &src))
1049 else if (unformat (line_input, "dst %U",
1050 unformat_ip4_address, &dst))
1052 else if (unformat (line_input, "intfc %U/%d",
1053 unformat_ip4_address, &intfc, &mask_width))
1055 else if (unformat (line_input, "inner-fib-id %d", &inner_fib_id))
1057 else if (unformat (line_input, "outer-fib-id %d", &outer_fib_id))
1059 else if (unformat (line_input, "del"))
1061 else if (unformat (line_input, "l2-only"))
1064 return clib_error_return (0, "unknown input '%U'",
1065 format_unformat_error, line_input);
1069 return clib_error_return (0, "missing: src <ip-address>");
1072 return clib_error_return (0, "missing: dst <ip-address>");
1075 return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
1078 rv = vnet_mpls_gre_add_del_tunnel (&src, &dst, &intfc, mask_width,
1079 inner_fib_id, outer_fib_id,
1080 &tunnel_intfc_sw_if_index,
1088 case VNET_API_ERROR_NO_SUCH_INNER_FIB:
1089 return clib_error_return (0, "inner fib ID %d doesn't exist\n",
1091 case VNET_API_ERROR_NO_SUCH_FIB:
1092 return clib_error_return (0, "outer fib ID %d doesn't exist\n",
1095 case VNET_API_ERROR_NO_SUCH_ENTRY:
1096 return clib_error_return (0, "tunnel not found\n");
1098 case VNET_API_ERROR_NO_SUCH_LABEL:
1100 * This happens when there's no MPLS label for the dst address
1101 * no need for two error messages.
1106 return clib_error_return (0, "vnet_mpls_gre_add_del_tunnel returned %d",
1112 VLIB_CLI_COMMAND (create_mpls_tunnel_command, static) = {
1113 .path = "create mpls gre tunnel",
1115 "create mpls gre tunnel [del] src <addr> dst <addr> intfc <addr>/<mw>",
1116 .function = create_mpls_gre_tunnel_command_fn,
1119 u8 * format_mpls_encap_index (u8 * s, va_list * args)
1121 mpls_main_t * mm = va_arg (*args, mpls_main_t *);
1122 u32 entry_index = va_arg (*args, u32);
1126 e = pool_elt_at_index (mm->encaps, entry_index);
1128 for (i = 0; i < vec_len (e->labels); i++)
1130 (s, "%d ", vnet_mpls_uc_get_label(clib_net_to_host_u32
1131 (e->labels[i].label_exp_s_ttl)));
1136 u8 * format_mpls_gre_tunnel (u8 * s, va_list * args)
1138 mpls_gre_tunnel_t * t = va_arg (*args, mpls_gre_tunnel_t *);
1139 mpls_main_t * mm = &mpls_main;
1141 if (t->l2_only == 0)
1143 s = format (s, "[%d]: src %U, dst %U, adj %U/%d, labels %U\n",
1144 t - mm->gre_tunnels,
1145 format_ip4_address, &t->tunnel_src,
1146 format_ip4_address, &t->tunnel_dst,
1147 format_ip4_address, &t->intfc_address,
1149 format_mpls_encap_index, mm, t->encap_index);
1151 s = format (s, " inner fib index %d, outer fib index %d",
1152 t->inner_fib_index, t->outer_fib_index);
1156 s = format (s, "[%d]: src %U, dst %U, key %U, labels %U\n",
1157 t - mm->gre_tunnels,
1158 format_ip4_address, &t->tunnel_src,
1159 format_ip4_address, &t->tunnel_dst,
1160 format_ip4_address, &t->intfc_address,
1161 format_mpls_encap_index, mm, t->encap_index);
1163 s = format (s, " l2 interface %d, outer fib index %d",
1164 t->hw_if_index, t->outer_fib_index);
1170 u8 * format_mpls_ethernet_tunnel (u8 * s, va_list * args)
1172 mpls_eth_tunnel_t * t = va_arg (*args, mpls_eth_tunnel_t *);
1173 mpls_main_t * mm = &mpls_main;
1175 s = format (s, "[%d]: dst %U, adj %U/%d, labels %U\n",
1176 t - mm->eth_tunnels,
1177 format_ethernet_address, &t->tunnel_dst,
1178 format_ip4_address, &t->intfc_address,
1180 format_mpls_encap_index, mm, t->encap_index);
1183 s = format (s, " tx on %U, rx fib index %d",
1184 format_vnet_sw_if_index_name, mm->vnet_main, t->tx_sw_if_index,
1185 t->inner_fib_index);
1190 static clib_error_t *
1191 show_mpls_tunnel_command_fn (vlib_main_t * vm,
1192 unformat_input_t * input,
1193 vlib_cli_command_t * cmd)
1195 mpls_main_t * mm = &mpls_main;
1196 mpls_gre_tunnel_t * gt;
1197 mpls_eth_tunnel_t * et;
1199 if (pool_elts (mm->gre_tunnels))
1201 vlib_cli_output (vm, "MPLS-GRE tunnels");
1202 pool_foreach (gt, mm->gre_tunnels,
1204 vlib_cli_output (vm, "%U", format_mpls_gre_tunnel, gt);
1208 vlib_cli_output (vm, "No MPLS-GRE tunnels");
1210 if (pool_elts (mm->eth_tunnels))
1212 vlib_cli_output (vm, "MPLS-Ethernet tunnels");
1213 pool_foreach (et, mm->eth_tunnels,
1215 vlib_cli_output (vm, "%U", format_mpls_ethernet_tunnel, et);
1219 vlib_cli_output (vm, "No MPLS-Ethernet tunnels");
1224 VLIB_CLI_COMMAND (show_mpls_tunnel_command, static) = {
1225 .path = "show mpls tunnel",
1226 .short_help = "show mpls tunnel",
1227 .function = show_mpls_tunnel_command_fn,
1230 /* force inclusion from application's main.c */
1231 clib_error_t *mpls_interface_init (vlib_main_t *vm)
1233 clib_error_t * error;
1235 if ((error = vlib_call_init_function (vm, mpls_policy_encap_init)))
1240 VLIB_INIT_FUNCTION(mpls_interface_init);
1243 static u8 * mpls_ethernet_rewrite (mpls_main_t *mm, mpls_eth_tunnel_t * t)
1245 u8 * rewrite_data = 0;
1247 mpls_unicast_header_t *lp0;
1250 /* look up the encap label stack using the RX FIB and adjacency address*/
1251 e = mpls_encap_by_fib_and_dest (mm, t->inner_fib_index,
1252 t->intfc_address.as_u32);
1256 clib_warning ("no label for inner fib index %d, dst %U",
1257 t->inner_fib_index, format_ip4_address,
1262 vec_validate (rewrite_data,
1263 sizeof (mpls_unicast_header_t) * vec_len(e->labels) -1);
1265 /* Copy the encap label stack */
1266 lp0 = (mpls_unicast_header_t *) rewrite_data;
1268 for (i = 0; i < vec_len(e->labels); i++)
1269 lp0[i] = e->labels[i];
1271 return (rewrite_data);
1274 int vnet_mpls_ethernet_add_del_tunnel (u8 *dst,
1275 ip4_address_t *intfc,
1279 u32 * tunnel_sw_if_index,
1283 ip4_main_t * im = &ip4_main;
1284 ip_lookup_main_t * lm = &im->lookup_main;
1285 mpls_main_t * mm = &mpls_main;
1286 vnet_main_t * vnm = vnet_get_main();
1288 mpls_eth_tunnel_t *tp;
1289 int need_route_add_del = 1;
1290 u32 inner_fib_index = 0;
1294 int found_tunnel = 0;
1295 mpls_encap_t * e = 0;
1296 u32 hw_if_index = ~0;
1297 vnet_hw_interface_t * hi;
1303 if (tunnel_sw_if_index == 0)
1304 tunnel_sw_if_index = &dummy;
1306 *tunnel_sw_if_index = ~0;
1308 if (inner_fib_id != (u32)~0)
1312 p = hash_get (im->fib_index_by_table_id, inner_fib_id);
1314 return VNET_API_ERROR_NO_SUCH_FIB;
1315 inner_fib_index = p[0];
1318 /* suppress duplicate mpls interface generation. */
1319 pool_foreach (tp, mm->eth_tunnels,
1322 * If we have a tunnel which matches (src, dst, intfc/mask)
1323 * AND the expected route is in the FIB, it's a dup
1325 if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
1326 && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
1327 && tp->inner_fib_index == inner_fib_index)
1329 ip4_fib_t * fib = vec_elt_at_index (im->fibs, inner_fib_index);
1330 uword * hash = fib->adj_index_by_dst_address[mask_width];
1331 uword key = intfc->as_u32 & im->fib_masks[mask_width];
1332 uword *p = hash_get (hash, key);
1342 e = mpls_encap_by_fib_and_dest (mm, inner_fib_index,
1345 return VNET_API_ERROR_NO_SUCH_LABEL;
1352 /* Delete, the route is already gone? */
1354 need_route_add_del = 0;
1361 /* Delete, and we can't find the tunnel */
1362 if (is_add == 0 && found_tunnel == 0)
1363 return VNET_API_ERROR_NO_SUCH_ENTRY;
1365 e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, intfc->as_u32);
1367 return VNET_API_ERROR_NO_SUCH_LABEL;
1369 pool_get(mm->eth_tunnels, tp);
1370 memset (tp, 0, sizeof (*tp));
1372 if (vec_len (mm->free_eth_sw_if_indices) > 0)
1375 mm->free_eth_sw_if_indices[vec_len(mm->free_eth_sw_if_indices)-1];
1376 _vec_len (mm->free_eth_sw_if_indices) -= 1;
1377 hi = vnet_get_hw_interface (vnm, hw_if_index);
1378 hi->dev_instance = tp - mm->eth_tunnels;
1379 hi->hw_instance = tp - mm->eth_tunnels;
1383 hw_if_index = vnet_register_interface
1384 (vnm, mpls_eth_device_class.index, tp - mm->eth_tunnels,
1385 mpls_eth_hw_interface_class.index,
1386 tp - mm->eth_tunnels);
1387 hi = vnet_get_hw_interface (vnm, hw_if_index);
1389 /* ... to make the IP and L2 x-connect cases identical */
1390 slot = vlib_node_add_named_next_with_slot
1391 (vnm->vlib_main, hi->tx_node_index,
1392 "interface-output", MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1394 ASSERT (slot == MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1397 *tunnel_sw_if_index = hi->sw_if_index;
1398 vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
1399 VNET_SW_INTERFACE_FLAG_ADMIN_UP);
1401 tp->hw_if_index = hw_if_index;
1404 clib_memcpy(tp->tunnel_dst, dst, sizeof (tp->tunnel_dst));
1405 tp->intfc_address.as_u32 = intfc->as_u32;
1406 tp->mask_width = mask_width;
1407 tp->inner_fib_index = inner_fib_index;
1408 tp->encap_index = e - mm->encaps;
1409 tp->tx_sw_if_index = tx_sw_if_index;
1410 tp->l2_only = l2_only;
1412 /* Create the adjacency and add to v4 fib */
1413 memset(&adj, 0, sizeof (adj));
1414 adj.explicit_fib_index = ~0;
1415 adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
1417 rewrite_data = mpls_ethernet_rewrite (mm, tp);
1418 if (rewrite_data == 0)
1420 if (*tunnel_sw_if_index != ~0)
1422 hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1423 vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
1424 0 /* admin down */);
1425 vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
1428 pool_put (mm->eth_tunnels, tp);
1429 return VNET_API_ERROR_NO_SUCH_LABEL;
1432 vnet_rewrite_for_sw_interface
1434 VNET_L3_PACKET_TYPE_MPLS_UNICAST,
1436 ip4_rewrite_node.index,
1438 &adj.rewrite_header,
1439 sizeof (adj.rewrite_data));
1442 * Prepend the (0,1,2) VLAN tag ethernet header
1443 * we just built to the mpls header stack
1445 vec_insert (rewrite_data, adj.rewrite_header.data_bytes, 0);
1446 clib_memcpy(rewrite_data,
1447 vnet_rewrite_get_data_internal(&adj.rewrite_header,
1448 sizeof (adj.rewrite_data)),
1449 adj.rewrite_header.data_bytes);
1451 vnet_rewrite_set_data_internal (&adj.rewrite_header,
1452 sizeof(adj.rewrite_data),
1454 vec_len(rewrite_data));
1456 vec_free (tp->rewrite_data);
1458 tp->rewrite_data = rewrite_data;
1461 ip_add_adjacency (lm, &adj, 1 /* one adj */,
1466 if (need_route_add_del && !l2_only)
1469 ip4_add_del_route_next_hop (im,
1473 &zero /* no next hop */,
1474 (u32)~0 /* next_hop_sw_if_index */,
1477 tp->inner_fib_index);
1480 ip4_add_del_route_args_t a;
1481 memset (&a, 0, sizeof (a));
1483 a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL;
1484 a.table_index_or_table_id = tp->inner_fib_index;
1485 a.dst_address = tp->intfc_address;
1486 a.dst_address_length = tp->mask_width;
1489 ip4_add_del_route (im, &a);
1490 ip4_maybe_remap_adjacencies (im, tp->inner_fib_index,
1491 IP4_ROUTE_FLAG_FIB_INDEX);
1494 if (is_add == 0 && found_tunnel)
1496 hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1497 vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
1498 0 /* admin down */);
1499 vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
1500 vec_free (tp->rewrite_data);
1501 pool_put (mm->eth_tunnels, tp);
1507 static clib_error_t *
1508 create_mpls_ethernet_tunnel_command_fn (vlib_main_t * vm,
1509 unformat_input_t * input,
1510 vlib_cli_command_t * cmd)
1512 unformat_input_t _line_input, * line_input = &_line_input;
1513 vnet_main_t * vnm = vnet_get_main();
1514 ip4_address_t intfc;
1517 int dst_set = 0, intfc_set = 0;
1519 u32 inner_fib_id = (u32)~0;
1525 /* Get a line of input. */
1526 if (! unformat_user (input, unformat_line_input, line_input))
1529 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1531 if (unformat (line_input, "dst %U",
1532 unformat_ethernet_address, &dst))
1534 else if (unformat (line_input, "adj %U/%d",
1535 unformat_ip4_address, &intfc, &mask_width))
1537 else if (unformat (line_input, "tx-intfc %U",
1538 unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
1540 else if (unformat (line_input, "fib-id %d", &inner_fib_id))
1542 else if (unformat (line_input, "l2-only"))
1544 else if (unformat (line_input, "del"))
1547 return clib_error_return (0, "unknown input '%U'",
1548 format_unformat_error, line_input);
1552 return clib_error_return (0, "missing tx-intfc");
1555 return clib_error_return (0, "missing: dst <ethernet-address>");
1558 return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
1561 rv = vnet_mpls_ethernet_add_del_tunnel (dst, &intfc, mask_width,
1562 inner_fib_id, tx_sw_if_index,
1563 0 /* tunnel sw_if_index */,
1568 case VNET_API_ERROR_NO_SUCH_FIB:
1569 return clib_error_return (0, "rx fib ID %d doesn't exist\n",
1572 case VNET_API_ERROR_NO_SUCH_ENTRY:
1573 return clib_error_return (0, "tunnel not found\n");
1575 case VNET_API_ERROR_NO_SUCH_LABEL:
1577 * This happens when there's no MPLS label for the dst address
1578 * no need for two error messages.
1580 return clib_error_return (0, "no label for %U in fib %d",
1581 format_ip4_address, &intfc, inner_fib_id);
1591 VLIB_CLI_COMMAND (create_mpls_ethernet_tunnel_command, static) = {
1592 .path = "create mpls ethernet tunnel",
1594 "create mpls ethernet tunnel [del] dst <mac-addr> intfc <addr>/<mw>",
1595 .function = create_mpls_ethernet_tunnel_command_fn,
1599 int vnet_mpls_policy_tunnel_add_rewrite (mpls_main_t * mm,
1601 u32 policy_tunnel_index)
1603 mpls_eth_tunnel_t * t;
1605 u8 * rewrite_data = 0;
1607 mpls_unicast_header_t *lp;
1610 if (pool_is_free_index (mm->eth_tunnels, policy_tunnel_index))
1611 return VNET_API_ERROR_NO_SUCH_ENTRY;
1613 t = pool_elt_at_index (mm->eth_tunnels, policy_tunnel_index);
1615 memset (&adj, 0, sizeof (adj));
1617 /* Build L2 encap */
1618 vnet_rewrite_for_sw_interface
1620 VNET_L3_PACKET_TYPE_MPLS_UNICAST,
1622 mpls_policy_encap_node.index,
1624 &adj.rewrite_header,
1625 sizeof (adj.rewrite_data));
1627 vec_validate (rewrite_data, adj.rewrite_header.data_bytes -1);
1629 clib_memcpy(rewrite_data,
1630 vnet_rewrite_get_data_internal(&adj.rewrite_header,
1631 sizeof (adj.rewrite_data)),
1632 adj.rewrite_header.data_bytes);
1634 /* Append the label stack */
1636 vec_add2 (rewrite_data, label_start, vec_len(e->labels) * sizeof (u32));
1638 lp = (mpls_unicast_header_t *) label_start;
1640 for (i = 0; i < vec_len(e->labels); i++)
1641 lp[i] = e->labels[i];
1643 /* Remember the rewrite data */
1644 e->rewrite = rewrite_data;
1645 e->output_next_index = adj.rewrite_header.next_index;
1650 int vnet_mpls_ethernet_add_del_policy_tunnel (u8 *dst,
1651 ip4_address_t *intfc,
1655 u32 * tunnel_sw_if_index,
1656 u32 classify_table_index,
1657 u32 * new_tunnel_index,
1661 ip4_main_t * im = &ip4_main;
1662 ip_lookup_main_t * lm = &im->lookup_main;
1663 mpls_main_t * mm = &mpls_main;
1664 vnet_main_t * vnm = vnet_get_main();
1666 mpls_eth_tunnel_t *tp;
1667 int need_route_add_del = 1;
1668 u32 inner_fib_index = 0;
1671 int found_tunnel = 0;
1672 mpls_encap_t * e = 0;
1673 u32 hw_if_index = ~0;
1674 vnet_hw_interface_t * hi;
1680 if (tunnel_sw_if_index == 0)
1681 tunnel_sw_if_index = &dummy;
1683 *tunnel_sw_if_index = ~0;
1685 if (inner_fib_id != (u32)~0)
1689 p = hash_get (im->fib_index_by_table_id, inner_fib_id);
1691 return VNET_API_ERROR_NO_SUCH_FIB;
1692 inner_fib_index = p[0];
1695 /* suppress duplicate mpls interface generation. */
1696 pool_foreach (tp, mm->eth_tunnels,
1699 * If we have a tunnel which matches (src, dst, intfc/mask)
1700 * AND the expected route is in the FIB, it's a dup
1702 if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
1703 && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
1704 && tp->inner_fib_index == inner_fib_index)
1706 ip4_fib_t * fib = vec_elt_at_index (im->fibs, inner_fib_index);
1707 uword * hash = fib->adj_index_by_dst_address[mask_width];
1708 uword key = intfc->as_u32 & im->fib_masks[mask_width];
1709 uword *p = hash_get (hash, key);
1724 /* Delete, the route is already gone? */
1726 need_route_add_del = 0;
1733 /* Delete, and we can't find the tunnel */
1734 if (is_add == 0 && found_tunnel == 0)
1735 return VNET_API_ERROR_NO_SUCH_ENTRY;
1737 pool_get(mm->eth_tunnels, tp);
1738 memset (tp, 0, sizeof (*tp));
1740 if (vec_len (mm->free_eth_sw_if_indices) > 0)
1743 mm->free_eth_sw_if_indices[vec_len(mm->free_eth_sw_if_indices)-1];
1744 _vec_len (mm->free_eth_sw_if_indices) -= 1;
1745 hi = vnet_get_hw_interface (vnm, hw_if_index);
1746 hi->dev_instance = tp - mm->eth_tunnels;
1747 hi->hw_instance = tp - mm->eth_tunnels;
1751 hw_if_index = vnet_register_interface
1752 (vnm, mpls_eth_device_class.index, tp - mm->eth_tunnels,
1753 mpls_eth_hw_interface_class.index,
1754 tp - mm->eth_tunnels);
1755 hi = vnet_get_hw_interface (vnm, hw_if_index);
1757 /* ... to make the IP and L2 x-connect cases identical */
1758 slot = vlib_node_add_named_next_with_slot
1759 (vnm->vlib_main, hi->tx_node_index,
1760 "interface-output", MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1762 ASSERT (slot == MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1765 *tunnel_sw_if_index = hi->sw_if_index;
1766 vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
1767 VNET_SW_INTERFACE_FLAG_ADMIN_UP);
1769 tp->hw_if_index = hw_if_index;
1772 clib_memcpy(tp->tunnel_dst, dst, sizeof (tp->tunnel_dst));
1773 tp->intfc_address.as_u32 = intfc->as_u32;
1774 tp->mask_width = mask_width;
1775 tp->inner_fib_index = inner_fib_index;
1776 tp->encap_index = e - mm->encaps;
1777 tp->tx_sw_if_index = tx_sw_if_index;
1778 tp->l2_only = l2_only;
1780 if (new_tunnel_index)
1781 *new_tunnel_index = tp - mm->eth_tunnels;
1783 /* Create the classify adjacency and add to v4 fib */
1784 memset(&adj, 0, sizeof (adj));
1785 adj.explicit_fib_index = ~0;
1786 adj.lookup_next_index = IP_LOOKUP_NEXT_CLASSIFY;
1787 adj.classify.table_index = classify_table_index;
1790 ip_add_adjacency (lm, &adj, 1 /* one adj */,
1795 if (need_route_add_del && !l2_only)
1798 ip4_add_del_route_next_hop (im,
1802 &zero /* no next hop */,
1803 (u32)~0 /* next_hop_sw_if_index */,
1806 tp->inner_fib_index);
1809 ip4_add_del_route_args_t a;
1810 memset (&a, 0, sizeof (a));
1812 a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL;
1813 a.table_index_or_table_id = tp->inner_fib_index;
1814 a.dst_address = tp->intfc_address;
1815 a.dst_address_length = tp->mask_width;
1818 ip4_add_del_route (im, &a);
1819 ip4_maybe_remap_adjacencies (im, tp->inner_fib_index,
1820 IP4_ROUTE_FLAG_FIB_INDEX);
1823 if (is_add == 0 && found_tunnel)
1825 hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1826 vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
1827 0 /* admin down */);
1828 vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
1829 pool_put (mm->eth_tunnels, tp);
1835 static clib_error_t *
1836 create_mpls_ethernet_policy_tunnel_command_fn (vlib_main_t * vm,
1837 unformat_input_t * input,
1838 vlib_cli_command_t * cmd)
1840 unformat_input_t _line_input, * line_input = &_line_input;
1841 vnet_main_t * vnm = vnet_get_main();
1842 ip4_address_t intfc;
1845 int dst_set = 0, intfc_set = 0;
1847 u32 inner_fib_id = (u32)~0;
1848 u32 classify_table_index = (u32)~0;
1849 u32 new_tunnel_index;
1855 /* Get a line of input. */
1856 if (! unformat_user (input, unformat_line_input, line_input))
1859 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1861 if (unformat (line_input, "dst %U",
1862 unformat_ethernet_address, &dst))
1864 else if (unformat (line_input, "adj %U/%d",
1865 unformat_ip4_address, &intfc, &mask_width))
1867 else if (unformat (line_input, "tx-intfc %U",
1868 unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
1870 else if (unformat (line_input, "classify-table-index %d",
1871 &classify_table_index))
1873 else if (unformat (line_input, "fib-id %d", &inner_fib_id))
1875 else if (unformat (line_input, "l2-only"))
1877 else if (unformat (line_input, "del"))
1880 return clib_error_return (0, "unknown input '%U'",
1881 format_unformat_error, line_input);
1884 if (classify_table_index == ~0)
1885 return clib_error_return (0, "missing classify_table_index");
1888 return clib_error_return (0, "missing tx-intfc");
1891 return clib_error_return (0, "missing: dst <ethernet-address>");
1894 return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
1897 rv = vnet_mpls_ethernet_add_del_policy_tunnel (dst, &intfc, mask_width,
1898 inner_fib_id, tx_sw_if_index,
1899 0 /* tunnel sw_if_index */,
1900 classify_table_index,
1905 case VNET_API_ERROR_NO_SUCH_FIB:
1906 return clib_error_return (0, "rx fib ID %d doesn't exist\n",
1909 case VNET_API_ERROR_NO_SUCH_ENTRY:
1910 return clib_error_return (0, "tunnel not found\n");
1912 case VNET_API_ERROR_NO_SUCH_LABEL:
1914 * This happens when there's no MPLS label for the dst address
1915 * no need for two error messages.
1917 return clib_error_return (0, "no label for %U in fib %d",
1918 format_ip4_address, &intfc, inner_fib_id);
1926 vlib_cli_output (vm, "tunnel index %d", new_tunnel_index);
1931 VLIB_CLI_COMMAND (create_mpls_ethernet_policy_tunnel_command, static) = {
1932 .path = "create mpls ethernet policy tunnel",
1934 "create mpls ethernet policy tunnel [del] dst <mac-addr> intfc <addr>/<mw>\n"
1935 " classify-table-index <nn>",
1936 .function = create_mpls_ethernet_policy_tunnel_command_fn,