IP directed broadcast
[vpp.git] / src / vnet / mpls / mpls_tunnel.c
1 /*
2  * mpls_tunnel.c: MPLS tunnel interfaces (i.e. for RSVP-TE)
3  *
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:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 #include <vnet/vnet.h>
19 #include <vnet/pg/pg.h>
20 #include <vnet/mpls/mpls_tunnel.h>
21 #include <vnet/mpls/mpls_types.h>
22 #include <vnet/ip/ip.h>
23 #include <vnet/fib/fib_path_list.h>
24 #include <vnet/adj/adj_midchain.h>
25 #include <vnet/adj/adj_mcast.h>
26 #include <vnet/dpo/replicate_dpo.h>
27 #include <vnet/fib/mpls_fib.h>
28
29 /**
30  * @brief pool of tunnel instances
31  */
32 static mpls_tunnel_t *mpls_tunnel_pool;
33
34 /**
35  * @brief Pool of free tunnel SW indices - i.e. recycled indices
36  */
37 static u32 * mpls_tunnel_free_hw_if_indices;
38
39 /**
40  * @brief DB of SW index to tunnel index
41  */
42 static u32 *mpls_tunnel_db;
43
44 /**
45  * @brief MPLS tunnel flags strings
46  */
47 static const char *mpls_tunnel_attribute_names[] = MPLS_TUNNEL_ATTRIBUTES;
48
49 /**
50  * @brief Get a tunnel object from a SW interface index
51  */
52 static mpls_tunnel_t*
53 mpls_tunnel_get_from_sw_if_index (u32 sw_if_index)
54 {
55     if ((vec_len(mpls_tunnel_db) < sw_if_index) ||
56         (~0 == mpls_tunnel_db[sw_if_index]))
57         return (NULL);
58
59     return (pool_elt_at_index(mpls_tunnel_pool,
60                               mpls_tunnel_db[sw_if_index]));
61 }
62
63 /**
64  * @brief Build a rewrite string for the MPLS tunnel.
65  */
66 static u8*
67 mpls_tunnel_build_rewrite_i (void)
68 {
69     /*
70      * passing the adj code a NULL rewirte means 'i don't have one cos
71      * t'other end is unresolved'. That's not the case here. For the mpls
72      * tunnel there are just no bytes of encap to apply in the adj. We'll impose
73      * the label stack once we choose a path. So return a zero length rewrite.
74      */
75     u8 *rewrite = NULL;
76
77     vec_validate(rewrite, 0);
78     vec_reset_length(rewrite);
79
80     return (rewrite);
81 }
82
83 /**
84  * @brief Build a rewrite string for the MPLS tunnel.
85  */
86 static u8*
87 mpls_tunnel_build_rewrite (vnet_main_t * vnm,
88                            u32 sw_if_index,
89                            vnet_link_t link_type,
90                            const void *dst_address)
91 {
92     return (mpls_tunnel_build_rewrite_i());
93 }
94
95 typedef struct mpls_tunnel_collect_forwarding_ctx_t_
96 {
97     load_balance_path_t * next_hops;
98     const mpls_tunnel_t *mt;
99     fib_forward_chain_type_t fct;
100 } mpls_tunnel_collect_forwarding_ctx_t;
101
102 static fib_path_list_walk_rc_t
103 mpls_tunnel_collect_forwarding (fib_node_index_t pl_index,
104                                 fib_node_index_t path_index,
105                                 void *arg)
106 {
107     mpls_tunnel_collect_forwarding_ctx_t *ctx;
108     fib_path_ext_t *path_ext;
109
110     ctx = arg;
111
112     /*
113      * if the path is not resolved, don't include it.
114      */
115     if (!fib_path_is_resolved(path_index))
116     {
117         return (FIB_PATH_LIST_WALK_CONTINUE);
118     }
119
120     /*
121      * get the matching path-extension for the path being visited.
122      */
123     path_ext = fib_path_ext_list_find_by_path_index(&ctx->mt->mt_path_exts,
124                                                     path_index);
125
126     /*
127      * we don't want IP TTL decrements for packets hitting the MPLS labels
128      * we stack on, since the IP TTL decrement is done by the adj
129      */
130     path_ext->fpe_mpls_flags |= FIB_PATH_EXT_MPLS_FLAG_NO_IP_TTL_DECR;
131
132     /*
133      * found a matching extension. stack it to obtain the forwarding
134      * info for this path.
135      */
136     ctx->next_hops = fib_path_ext_stack(path_ext,
137                                         ctx->fct,
138                                         ctx->fct,
139                                         ctx->next_hops);
140
141     return (FIB_PATH_LIST_WALK_CONTINUE);
142 }
143
144 static void
145 mpls_tunnel_mk_lb (mpls_tunnel_t *mt,
146                    vnet_link_t linkt,
147                    fib_forward_chain_type_t fct,
148                    dpo_id_t *dpo_lb)
149 {
150     dpo_proto_t lb_proto;
151
152     /*
153      * If the entry has path extensions then we construct a load-balance
154      * by stacking the extensions on the forwarding chains of the paths.
155      * Otherwise we use the load-balance of the path-list
156      */
157     mpls_tunnel_collect_forwarding_ctx_t ctx = {
158         .mt = mt,
159         .next_hops = NULL,
160         .fct = fct,
161     };
162
163     /*
164      * As an optimisation we allocate the vector of next-hops to be sized
165      * equal to the maximum nuber of paths we will need, which is also the
166      * most likely number we will need, since in most cases the paths are 'up'.
167      */
168     vec_validate(ctx.next_hops, fib_path_list_get_n_paths(mt->mt_path_list));
169     vec_reset_length(ctx.next_hops);
170
171     lb_proto = fib_forw_chain_type_to_dpo_proto(fct);
172
173     fib_path_list_walk(mt->mt_path_list,
174                        mpls_tunnel_collect_forwarding,
175                        &ctx);
176
177     if (!dpo_id_is_valid(dpo_lb))
178     {
179         /*
180          * first time create
181          */
182         if (mt->mt_flags & MPLS_TUNNEL_FLAG_MCAST)
183         {
184             dpo_set(dpo_lb,
185                     DPO_REPLICATE,
186                     lb_proto,
187                     replicate_create(0, lb_proto));
188         }
189         else
190         {
191             flow_hash_config_t fhc;
192
193             switch (linkt)
194             {
195             case VNET_LINK_MPLS:
196                 fhc = MPLS_FLOW_HASH_DEFAULT;
197                 break;
198             case VNET_LINK_IP4:
199             case VNET_LINK_IP6:
200                 fhc = IP_FLOW_HASH_DEFAULT;
201                 break;
202             default:
203                 fhc = 0;
204                 break;
205             }
206
207             dpo_set(dpo_lb,
208                     DPO_LOAD_BALANCE,
209                     lb_proto,
210                     load_balance_create(0, lb_proto, fhc));
211         }
212     }
213
214     if (mt->mt_flags & MPLS_TUNNEL_FLAG_MCAST)
215     {
216         /*
217          * MPLS multicast
218          */
219         replicate_multipath_update(dpo_lb, ctx.next_hops);
220     }
221     else
222     {
223         load_balance_multipath_update(dpo_lb,
224                                       ctx.next_hops,
225                                       LOAD_BALANCE_FLAG_NONE);
226         vec_free(ctx.next_hops);
227     }
228 }
229
230 /**
231  * mpls_tunnel_stack
232  *
233  * 'stack' (resolve the recursion for) the tunnel's midchain adjacency
234  */
235 static void
236 mpls_tunnel_stack (adj_index_t ai)
237 {
238     ip_adjacency_t *adj;
239     mpls_tunnel_t *mt;
240     u32 sw_if_index;
241
242     adj = adj_get(ai);
243     sw_if_index = adj->rewrite_header.sw_if_index;
244
245     mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
246
247     if (NULL == mt)
248         return;
249
250     /*
251      * while we're stacking the adj, remove the tunnel from the child list
252      * of the path list. this breaks a circular dependency of walk updates
253      * where the create of adjacencies in the children can lead to walks
254      * that get back here.
255      */
256     fib_path_list_lock(mt->mt_path_list);
257
258     fib_path_list_child_remove(mt->mt_path_list,
259                                mt->mt_sibling_index);
260
261     /*
262      * Construct the DPO (load-balance or replicate) that we can stack
263      * the tunnel's midchain on
264      */
265     if (vnet_hw_interface_get_flags(vnet_get_main(),
266                                     mt->mt_hw_if_index) &
267         VNET_HW_INTERFACE_FLAG_LINK_UP)
268     {
269         dpo_id_t dpo = DPO_INVALID;
270
271         mpls_tunnel_mk_lb(mt,
272                           adj->ia_link,
273                           fib_forw_chain_type_from_link_type(
274                               adj_get_link_type(ai)),
275                           &dpo);
276
277         adj_nbr_midchain_stack(ai, &dpo);
278         dpo_reset(&dpo);
279     }
280     else
281     {
282         adj_nbr_midchain_unstack(ai);
283     }
284
285     mt->mt_sibling_index = fib_path_list_child_add(mt->mt_path_list,
286                                                    FIB_NODE_TYPE_MPLS_TUNNEL,
287                                                    mt - mpls_tunnel_pool);
288
289     fib_path_list_unlock(mt->mt_path_list);
290 }
291
292 /**
293  * @brief Call back when restacking all adjacencies on a MPLS interface
294  */
295 static adj_walk_rc_t
296 mpls_adj_walk_cb (adj_index_t ai,
297                  void *ctx)
298 {
299     mpls_tunnel_stack(ai);
300
301     return (ADJ_WALK_RC_CONTINUE);
302 }
303
304 static void
305 mpls_tunnel_restack (mpls_tunnel_t *mt)
306 {
307     fib_protocol_t proto;
308
309     /*
310      * walk all the adjacencies on the MPLS interface and restack them
311      */
312     if (mt->mt_flags & MPLS_TUNNEL_FLAG_L2)
313     {
314         /*
315          * Stack a load-balance that drops, whilst we have no paths
316          */
317         vnet_hw_interface_t * hi;
318         dpo_id_t dpo = DPO_INVALID;
319
320         mpls_tunnel_mk_lb(mt,
321                           VNET_LINK_MPLS,
322                           FIB_FORW_CHAIN_TYPE_ETHERNET,
323                           &dpo);
324
325         hi = vnet_get_hw_interface(vnet_get_main(), mt->mt_hw_if_index);
326         dpo_stack_from_node(hi->tx_node_index,
327                             &mt->mt_l2_lb,
328                             &dpo);
329         dpo_reset(&dpo);
330     }
331     else
332     {
333         FOR_EACH_FIB_PROTOCOL(proto)
334         {
335             adj_nbr_walk(mt->mt_sw_if_index,
336                          proto,
337                          mpls_adj_walk_cb,
338                          NULL);
339         }
340     }
341 }
342
343 static clib_error_t *
344 mpls_tunnel_admin_up_down (vnet_main_t * vnm,
345                            u32 hw_if_index,
346                            u32 flags)
347 {
348     vnet_hw_interface_t * hi;
349     mpls_tunnel_t *mt;
350
351     hi = vnet_get_hw_interface (vnm, hw_if_index);
352
353     mt = mpls_tunnel_get_from_sw_if_index(hi->sw_if_index);
354
355     if (NULL == mt)
356         return (NULL);
357
358     if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
359         vnet_hw_interface_set_flags (vnm, hw_if_index,
360                                      VNET_HW_INTERFACE_FLAG_LINK_UP);
361     else
362         vnet_hw_interface_set_flags (vnm, hw_if_index, 0 /* down */);
363
364     mpls_tunnel_restack(mt);
365
366     return (NULL);
367 }
368
369 /**
370  * @brief Fixup the adj rewrite post encap. This is a no-op since the
371  * rewrite is a stack of labels.
372  */
373 static void
374 mpls_tunnel_fixup (vlib_main_t *vm,
375                    ip_adjacency_t *adj,
376                    vlib_buffer_t *b0,
377                    const void*data)
378 {
379     /*
380      * A no-op w.r.t. the header. but reset the 'have we pushed any
381      * MPLS labels onto the packet' flag. That way when we enter the
382      * tunnel we'll get a TTL set to 255
383      */
384     vnet_buffer(b0)->mpls.first = 0;
385 }
386
387 static void
388 mpls_tunnel_update_adj (vnet_main_t * vnm,
389                         u32 sw_if_index,
390                         adj_index_t ai)
391 {
392     ip_adjacency_t *adj;
393
394     ASSERT(ADJ_INDEX_INVALID != ai);
395
396     adj = adj_get(ai);
397
398     switch (adj->lookup_next_index)
399     {
400     case IP_LOOKUP_NEXT_ARP:
401     case IP_LOOKUP_NEXT_GLEAN:
402     case IP_LOOKUP_NEXT_BCAST:
403         adj_nbr_midchain_update_rewrite(ai, mpls_tunnel_fixup,
404                                         NULL,
405                                         ADJ_FLAG_NONE,
406                                         mpls_tunnel_build_rewrite_i());
407         break;
408     case IP_LOOKUP_NEXT_MCAST:
409         /*
410          * Construct a partial rewrite from the known ethernet mcast dest MAC
411          * There's no MAC fixup, so the last 2 parameters are 0
412          */
413         adj_mcast_midchain_update_rewrite(ai, mpls_tunnel_fixup,
414                                           NULL,
415                                           ADJ_FLAG_NONE,
416                                           mpls_tunnel_build_rewrite_i(),
417                                           0, 0);
418         break;
419
420     case IP_LOOKUP_NEXT_DROP:
421     case IP_LOOKUP_NEXT_PUNT:
422     case IP_LOOKUP_NEXT_LOCAL:
423     case IP_LOOKUP_NEXT_REWRITE:
424     case IP_LOOKUP_NEXT_MIDCHAIN:
425     case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
426     case IP_LOOKUP_NEXT_ICMP_ERROR:
427     case IP_LOOKUP_N_NEXT:
428       ASSERT (0);
429       break;
430     }
431
432     mpls_tunnel_stack(ai);
433 }
434
435 static u8 *
436 format_mpls_tunnel_name (u8 * s, va_list * args)
437 {
438   u32 dev_instance = va_arg (*args, u32);
439   return format (s, "mpls-tunnel%d", dev_instance);
440 }
441
442 static u8 *
443 format_mpls_tunnel_device (u8 * s, va_list * args)
444 {
445   u32 dev_instance = va_arg (*args, u32);
446   CLIB_UNUSED (int verbose) = va_arg (*args, int);
447
448   return (format (s, "MPLS-tunnel: id %d\n", dev_instance));
449 }
450
451 /**
452  * @brief Packet trace structure
453  */
454 typedef struct mpls_tunnel_trace_t_
455 {
456     /**
457    * Tunnel-id / index in tunnel vector
458    */
459   u32 tunnel_id;
460 } mpls_tunnel_trace_t;
461
462 static u8 *
463 format_mpls_tunnel_tx_trace (u8 * s,
464                              va_list * args)
465 {
466   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
467   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
468   mpls_tunnel_trace_t * t = va_arg (*args, mpls_tunnel_trace_t *);
469
470   s = format (s, "MPLS: tunnel %d", t->tunnel_id);
471   return s;
472 }
473
474 /**
475  * @brief TX function. Only called L2. L3 traffic uses the adj-midchains
476  */
477 static uword
478 mpls_tunnel_tx (vlib_main_t * vm,
479                 vlib_node_runtime_t * node,
480                 vlib_frame_t * frame)
481 {
482   u32 next_index;
483   u32 * from, * to_next, n_left_from, n_left_to_next;
484   vnet_interface_output_runtime_t * rd = (void *) node->runtime_data;
485   const mpls_tunnel_t *mt;
486
487   mt = pool_elt_at_index(mpls_tunnel_pool, rd->dev_instance);
488
489   /* Vector of buffer / pkt indices we're supposed to process */
490   from = vlib_frame_vector_args (frame);
491
492   /* Number of buffers / pkts */
493   n_left_from = frame->n_vectors;
494
495   /* Speculatively send the first buffer to the last disposition we used */
496   next_index = node->cached_next_index;
497
498   while (n_left_from > 0)
499     {
500       /* set up to enqueue to our disposition with index = next_index */
501       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
502
503       /*
504        * FIXME DUAL LOOP
505        */
506       while (n_left_from > 0 && n_left_to_next > 0)
507         {
508           vlib_buffer_t * b0;
509           u32 bi0;
510
511           bi0 = from[0];
512           to_next[0] = bi0;
513           from += 1;
514           to_next += 1;
515           n_left_from -= 1;
516           n_left_to_next -= 1;
517
518           b0 = vlib_get_buffer(vm, bi0);
519
520           vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mt->mt_l2_lb.dpoi_index;
521           /* since we are coming out of the L2 world, where the vlib_buffer
522            * union is used for other things, make sure it is clean for
523            * MPLS from now on.
524            */
525           vnet_buffer(b0)->mpls.first = 0;
526
527           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
528             {
529               mpls_tunnel_trace_t *tr = vlib_add_trace (vm, node,
530                                                    b0, sizeof (*tr));
531               tr->tunnel_id = rd->dev_instance;
532             }
533
534           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
535                                            to_next, n_left_to_next,
536                                            bi0, mt->mt_l2_lb.dpoi_next_node);
537         }
538
539       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
540     }
541
542   return frame->n_vectors;
543 }
544
545 VNET_DEVICE_CLASS (mpls_tunnel_class) = {
546     .name = "MPLS tunnel device",
547     .format_device_name = format_mpls_tunnel_name,
548     .format_device = format_mpls_tunnel_device,
549     .format_tx_trace = format_mpls_tunnel_tx_trace,
550     .tx_function = mpls_tunnel_tx,
551     .admin_up_down_function = mpls_tunnel_admin_up_down,
552 };
553
554 VNET_HW_INTERFACE_CLASS (mpls_tunnel_hw_interface_class) = {
555   .name = "MPLS-Tunnel",
556   .update_adjacency = mpls_tunnel_update_adj,
557   .build_rewrite = mpls_tunnel_build_rewrite,
558   .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
559 };
560
561 const mpls_tunnel_t *
562 mpls_tunnel_get (u32 mti)
563 {
564     return (pool_elt_at_index(mpls_tunnel_pool, mti));
565 }
566
567 /**
568  * @brief Walk all the MPLS tunnels
569  */
570 void
571 mpls_tunnel_walk (mpls_tunnel_walk_cb_t cb,
572                   void *ctx)
573 {
574     u32 mti;
575
576     pool_foreach_index(mti, mpls_tunnel_pool,
577     ({
578         cb(mti, ctx);
579     }));
580 }
581
582 void
583 vnet_mpls_tunnel_del (u32 sw_if_index)
584 {
585     mpls_tunnel_t *mt;
586
587     mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
588
589     if (NULL == mt)
590         return;
591
592     if (FIB_NODE_INDEX_INVALID != mt->mt_path_list)
593         fib_path_list_child_remove(mt->mt_path_list,
594                                    mt->mt_sibling_index);
595     dpo_reset(&mt->mt_l2_lb);
596
597     vec_add1 (mpls_tunnel_free_hw_if_indices, mt->mt_hw_if_index);
598     pool_put(mpls_tunnel_pool, mt);
599     mpls_tunnel_db[sw_if_index] = ~0;
600 }
601
602 u32
603 vnet_mpls_tunnel_create (u8 l2_only,
604                          u8 is_multicast)
605 {
606     vnet_hw_interface_t * hi;
607     mpls_tunnel_t *mt;
608     vnet_main_t * vnm;
609     u32 mti;
610
611     vnm = vnet_get_main();
612     pool_get(mpls_tunnel_pool, mt);
613     memset (mt, 0, sizeof (*mt));
614     mti = mt - mpls_tunnel_pool;
615     fib_node_init(&mt->mt_node, FIB_NODE_TYPE_MPLS_TUNNEL);
616     mt->mt_path_list = FIB_NODE_INDEX_INVALID;
617     mt->mt_sibling_index = FIB_NODE_INDEX_INVALID;
618
619     if (is_multicast)
620         mt->mt_flags |= MPLS_TUNNEL_FLAG_MCAST;
621     if (l2_only)
622         mt->mt_flags |= MPLS_TUNNEL_FLAG_L2;
623
624     /*
625      * Create a new, or re=use and old, tunnel HW interface
626      */
627     if (vec_len (mpls_tunnel_free_hw_if_indices) > 0)
628     {
629         mt->mt_hw_if_index =
630             mpls_tunnel_free_hw_if_indices[vec_len(mpls_tunnel_free_hw_if_indices)-1];
631         _vec_len (mpls_tunnel_free_hw_if_indices) -= 1;
632         hi = vnet_get_hw_interface (vnm, mt->mt_hw_if_index);
633         hi->hw_instance = mti;
634         hi->dev_instance = mti;
635     }
636     else
637     {
638         mt->mt_hw_if_index = vnet_register_interface(
639                                  vnm,
640                                  mpls_tunnel_class.index,
641                                  mti,
642                                  mpls_tunnel_hw_interface_class.index,
643                                  mti);
644         hi = vnet_get_hw_interface (vnm, mt->mt_hw_if_index);
645     }
646
647     /* Standard default MPLS tunnel MTU. */
648     vnet_sw_interface_set_mtu (vnm, hi->sw_if_index, 9000);
649
650     /*
651      * Add the new tunnel to the tunnel DB - key:SW if index
652      */
653     mt->mt_sw_if_index = hi->sw_if_index;
654     vec_validate_init_empty(mpls_tunnel_db, mt->mt_sw_if_index, ~0);
655     mpls_tunnel_db[mt->mt_sw_if_index] = mti;
656
657     return (mt->mt_sw_if_index);
658 }
659
660 void
661 vnet_mpls_tunnel_path_add (u32 sw_if_index,
662                            fib_route_path_t *rpaths)
663 {
664     mpls_tunnel_t *mt;
665     u32 mti;
666
667     mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
668
669     if (NULL == mt)
670         return;
671
672     mti = mt - mpls_tunnel_pool;
673
674     /*
675      * construct a path-list from the path provided
676      */
677     if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
678     {
679         mt->mt_path_list = fib_path_list_create(FIB_PATH_LIST_FLAG_SHARED, rpaths);
680         mt->mt_sibling_index = fib_path_list_child_add(mt->mt_path_list,
681                                                        FIB_NODE_TYPE_MPLS_TUNNEL,
682                                                        mti);
683     }
684     else
685     {
686         fib_node_index_t old_pl_index;
687
688         old_pl_index = mt->mt_path_list;
689
690         mt->mt_path_list =
691             fib_path_list_copy_and_path_add(old_pl_index,
692                                             FIB_PATH_LIST_FLAG_SHARED,
693                                             rpaths);
694
695         fib_path_list_child_remove(old_pl_index,
696                                    mt->mt_sibling_index);
697         mt->mt_sibling_index = fib_path_list_child_add(mt->mt_path_list,
698                                                        FIB_NODE_TYPE_MPLS_TUNNEL,
699                                                        mti);
700         /*
701          * re-resolve all the path-extensions with the new path-list
702          */
703         fib_path_ext_list_resolve(&mt->mt_path_exts, mt->mt_path_list);
704     }
705     fib_path_ext_list_insert(&mt->mt_path_exts,
706                              mt->mt_path_list,
707                              FIB_PATH_EXT_MPLS,
708                              rpaths);
709     mpls_tunnel_restack(mt);
710 }
711
712 int
713 vnet_mpls_tunnel_path_remove (u32 sw_if_index,
714                               fib_route_path_t *rpaths)
715 {
716     mpls_tunnel_t *mt;
717     u32 mti;
718
719     mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
720
721     if (NULL == mt)
722         return (0);
723
724     mti = mt - mpls_tunnel_pool;
725
726     /*
727      * construct a path-list from the path provided
728      */
729     if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
730     {
731         /* can't remove a path if we have onoe */
732         return (0);
733     }
734     else
735     {
736         fib_node_index_t old_pl_index;
737
738         old_pl_index = mt->mt_path_list;
739
740         mt->mt_path_list =
741             fib_path_list_copy_and_path_remove(old_pl_index,
742                                                FIB_PATH_LIST_FLAG_SHARED,
743                                                rpaths);
744
745         fib_path_list_child_remove(old_pl_index,
746                                    mt->mt_sibling_index);
747
748         if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
749         {
750             /* no paths left */
751             return (0);
752         }
753         else
754         {
755             mt->mt_sibling_index =
756                 fib_path_list_child_add(mt->mt_path_list,
757                                         FIB_NODE_TYPE_MPLS_TUNNEL,
758                                         mti);
759         }
760         /*
761          * find the matching path extension and remove it
762          */
763         fib_path_ext_list_remove(&mt->mt_path_exts,
764                                   FIB_PATH_EXT_MPLS,
765                                   rpaths);
766
767         /*
768          * re-resolve all the path-extensions with the new path-list
769          */
770         fib_path_ext_list_resolve(&mt->mt_path_exts,
771                                   mt->mt_path_list);
772
773         mpls_tunnel_restack(mt);
774    }
775
776     return (fib_path_list_get_n_paths(mt->mt_path_list));
777 }
778
779
780 static clib_error_t *
781 vnet_create_mpls_tunnel_command_fn (vlib_main_t * vm,
782                                     unformat_input_t * input,
783                                     vlib_cli_command_t * cmd)
784 {
785     unformat_input_t _line_input, * line_input = &_line_input;
786     vnet_main_t * vnm = vnet_get_main();
787     u8 is_del = 0, l2_only = 0, is_multicast =0;
788     fib_route_path_t rpath, *rpaths = NULL;
789     u32 sw_if_index = ~0, payload_proto;
790     clib_error_t *error = NULL;
791
792     memset(&rpath, 0, sizeof(rpath));
793     payload_proto = DPO_PROTO_MPLS;
794
795     /* Get a line of input. */
796     if (! unformat_user (input, unformat_line_input, line_input))
797         return 0;
798
799     while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
800     {
801         if (unformat (line_input, "del %U",
802                       unformat_vnet_sw_interface, vnm,
803                       &sw_if_index))
804             is_del = 1;
805         else if (unformat (line_input, "add %U",
806                            unformat_vnet_sw_interface, vnm,
807                            &sw_if_index))
808             is_del = 0;
809         else if (unformat (line_input, "add"))
810             is_del = 0;
811         else if (unformat (line_input, "l2-only"))
812             l2_only = 1;
813         else if (unformat (line_input, "multicast"))
814             is_multicast = 1;
815         else if (unformat (line_input, "via %U",
816                            unformat_fib_route_path,
817                            &rpath, &payload_proto))
818             vec_add1(rpaths, rpath);
819         else
820         {
821             error = clib_error_return (0, "unknown input '%U'",
822                                        format_unformat_error, line_input);
823             goto done;
824         }
825     }
826
827     if (is_del)
828     {
829         if (!vnet_mpls_tunnel_path_remove(sw_if_index, rpaths))
830         {
831             vnet_mpls_tunnel_del(sw_if_index);
832         }
833     }
834     else
835     {
836         if (0 == vec_len(rpath.frp_label_stack))
837         {
838             error = clib_error_return (0, "No Output Labels '%U'",
839                                        format_unformat_error, line_input);
840             goto done;
841         }
842
843         if (~0 == sw_if_index)
844         {
845             sw_if_index = vnet_mpls_tunnel_create(l2_only, is_multicast);
846         }
847         vnet_mpls_tunnel_path_add(sw_if_index, rpaths);
848     }
849
850 done:
851     vec_free(rpaths);
852     unformat_free (line_input);
853
854     return error;
855 }
856
857 /*?
858  * This command create a uni-directional MPLS tunnel
859  *
860  * @cliexpar
861  * @cliexstart{create mpls tunnel}
862  *  create mpls tunnel via 10.0.0.1 GigEthernet0/8/0 out-label 33 out-label 34
863  * @cliexend
864  ?*/
865 VLIB_CLI_COMMAND (create_mpls_tunnel_command, static) = {
866   .path = "mpls tunnel",
867   .short_help =
868   "mpls tunnel [multicast] [l2-only] via [next-hop-address] [next-hop-interface] [next-hop-table <value>] [weight <value>] [preference <value>] [udp-encap-id <value>] [ip4-lookup-in-table <value>] [ip6-lookup-in-table <value>] [mpls-lookup-in-table <value>] [resolve-via-host] [resolve-via-connected] [rx-ip4 <interface>] [out-labels <value value value>]",
869   .function = vnet_create_mpls_tunnel_command_fn,
870 };
871
872 static u8 *
873 format_mpls_tunnel (u8 * s, va_list * args)
874 {
875     mpls_tunnel_t *mt = va_arg (*args, mpls_tunnel_t *);
876     mpls_tunnel_attribute_t attr;
877
878     s = format(s, "mpls_tunnel%d: sw_if_index:%d hw_if_index:%d",
879                mt - mpls_tunnel_pool,
880                mt->mt_sw_if_index,
881                mt->mt_hw_if_index);
882     if (MPLS_TUNNEL_FLAG_NONE != mt->mt_flags) {
883         s = format(s, " \n flags:");
884         FOR_EACH_MPLS_TUNNEL_ATTRIBUTE(attr) {
885             if ((1<<attr) & mt->mt_flags) {
886                 s = format (s, "%s,", mpls_tunnel_attribute_names[attr]);
887             }
888         }
889     }
890     s = format(s, "\n via:\n");
891     s = fib_path_list_format(mt->mt_path_list, s);
892     s = format(s, "%U", format_fib_path_ext_list, &mt->mt_path_exts);
893     s = format(s, "\n");
894
895     if (mt->mt_flags & MPLS_TUNNEL_FLAG_L2)
896     {
897         s = format(s, " forwarding: %U\n",
898                    format_fib_forw_chain_type,
899                    FIB_FORW_CHAIN_TYPE_ETHERNET);
900         s = format(s, " %U\n", format_dpo_id, &mt->mt_l2_lb, 2);
901     }
902
903     return (s);
904 }
905
906 static clib_error_t *
907 show_mpls_tunnel_command_fn (vlib_main_t * vm,
908                              unformat_input_t * input,
909                              vlib_cli_command_t * cmd)
910 {
911     mpls_tunnel_t * mt;
912     u32 mti = ~0;
913
914     if (pool_elts (mpls_tunnel_pool) == 0)
915         vlib_cli_output (vm, "No MPLS tunnels configured...");
916
917     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
918     {
919         if (unformat (input, "%d", &mti))
920             ;
921         else
922             break;
923     }
924
925     if (~0 == mti)
926     {
927         pool_foreach (mt, mpls_tunnel_pool,
928         ({
929             vlib_cli_output (vm, "[@%d] %U",
930                              mt - mpls_tunnel_pool,
931                              format_mpls_tunnel, mt);
932         }));
933     }
934     else
935     {
936         if (pool_is_free_index(mpls_tunnel_pool, mti))
937             return clib_error_return (0, "Not atunnel index %d", mti);
938
939         mt = pool_elt_at_index(mpls_tunnel_pool, mti);
940
941         vlib_cli_output (vm, "[@%d] %U",
942                          mt - mpls_tunnel_pool,
943                          format_mpls_tunnel, mt);
944     }
945
946     return 0;
947 }
948
949 /*?
950  * This command to show MPLS tunnels
951  *
952  * @cliexpar
953  * @cliexstart{sh mpls tunnel 2}
954  * [@2] mpls_tunnel2: sw_if_index:5 hw_if_index:5
955  *  label-stack:
956  *    3,
957  *  via:
958  *   index:26 locks:1 proto:ipv4 uPRF-list:26 len:1 itfs:[2, ]
959  *     index:26 pl-index:26 ipv4 weight=1 attached-nexthop:  oper-flags:resolved,
960  *      10.0.0.2 loop0
961  *         [@0]: ipv4 via 10.0.0.2 loop0: IP4: de:ad:00:00:00:00 -> 00:00:11:aa:bb:cc
962  * @cliexend
963  ?*/
964 VLIB_CLI_COMMAND (show_mpls_tunnel_command, static) = {
965     .path = "show mpls tunnel",
966     .function = show_mpls_tunnel_command_fn,
967 };
968
969 static mpls_tunnel_t *
970 mpls_tunnel_from_fib_node (fib_node_t *node)
971 {
972     ASSERT(FIB_NODE_TYPE_MPLS_TUNNEL == node->fn_type);
973     return ((mpls_tunnel_t*) (((char*)node) -
974                              STRUCT_OFFSET_OF(mpls_tunnel_t, mt_node)));
975 }
976
977 /**
978  * Function definition to backwalk a FIB node
979  */
980 static fib_node_back_walk_rc_t
981 mpls_tunnel_back_walk (fib_node_t *node,
982                       fib_node_back_walk_ctx_t *ctx)
983 {
984     mpls_tunnel_restack(mpls_tunnel_from_fib_node(node));
985
986     return (FIB_NODE_BACK_WALK_CONTINUE);
987 }
988
989 /**
990  * Function definition to get a FIB node from its index
991  */
992 static fib_node_t*
993 mpls_tunnel_fib_node_get (fib_node_index_t index)
994 {
995     mpls_tunnel_t * mt;
996
997     mt = pool_elt_at_index(mpls_tunnel_pool, index);
998
999     return (&mt->mt_node);
1000 }
1001
1002 /**
1003  * Function definition to inform the FIB node that its last lock has gone.
1004  */
1005 static void
1006 mpls_tunnel_last_lock_gone (fib_node_t *node)
1007 {
1008     /*
1009      * The MPLS MPLS tunnel is a root of the graph. As such
1010      * it never has children and thus is never locked.
1011      */
1012     ASSERT(0);
1013 }
1014
1015 /*
1016  * Virtual function table registered by MPLS MPLS tunnels
1017  * for participation in the FIB object graph.
1018  */
1019 const static fib_node_vft_t mpls_vft = {
1020     .fnv_get = mpls_tunnel_fib_node_get,
1021     .fnv_last_lock = mpls_tunnel_last_lock_gone,
1022     .fnv_back_walk = mpls_tunnel_back_walk,
1023 };
1024
1025 static clib_error_t *
1026 mpls_tunnel_init (vlib_main_t *vm)
1027 {
1028   fib_node_register_type(FIB_NODE_TYPE_MPLS_TUNNEL, &mpls_vft);
1029
1030   return 0;
1031 }
1032 VLIB_INIT_FUNCTION(mpls_tunnel_init);