MTU: Software interface / Per-protocol MTU support
[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         adj_nbr_midchain_update_rewrite(ai, mpls_tunnel_fixup,
403                                         NULL,
404                                         ADJ_FLAG_NONE,
405                                         mpls_tunnel_build_rewrite_i());
406         break;
407     case IP_LOOKUP_NEXT_MCAST:
408         /*
409          * Construct a partial rewrite from the known ethernet mcast dest MAC
410          * There's no MAC fixup, so the last 2 parameters are 0
411          */
412         adj_mcast_midchain_update_rewrite(ai, mpls_tunnel_fixup,
413                                           NULL,
414                                           ADJ_FLAG_NONE,
415                                           mpls_tunnel_build_rewrite_i(),
416                                           0, 0);
417         break;
418
419     case IP_LOOKUP_NEXT_DROP:
420     case IP_LOOKUP_NEXT_PUNT:
421     case IP_LOOKUP_NEXT_LOCAL:
422     case IP_LOOKUP_NEXT_REWRITE:
423     case IP_LOOKUP_NEXT_MIDCHAIN:
424     case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
425     case IP_LOOKUP_NEXT_ICMP_ERROR:
426     case IP_LOOKUP_N_NEXT:
427       ASSERT (0);
428       break;
429     }
430
431     mpls_tunnel_stack(ai);
432 }
433
434 static u8 *
435 format_mpls_tunnel_name (u8 * s, va_list * args)
436 {
437   u32 dev_instance = va_arg (*args, u32);
438   return format (s, "mpls-tunnel%d", dev_instance);
439 }
440
441 static u8 *
442 format_mpls_tunnel_device (u8 * s, va_list * args)
443 {
444   u32 dev_instance = va_arg (*args, u32);
445   CLIB_UNUSED (int verbose) = va_arg (*args, int);
446
447   return (format (s, "MPLS-tunnel: id %d\n", dev_instance));
448 }
449
450 /**
451  * @brief Packet trace structure
452  */
453 typedef struct mpls_tunnel_trace_t_
454 {
455     /**
456    * Tunnel-id / index in tunnel vector
457    */
458   u32 tunnel_id;
459 } mpls_tunnel_trace_t;
460
461 static u8 *
462 format_mpls_tunnel_tx_trace (u8 * s,
463                              va_list * args)
464 {
465   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
466   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
467   mpls_tunnel_trace_t * t = va_arg (*args, mpls_tunnel_trace_t *);
468
469   s = format (s, "MPLS: tunnel %d", t->tunnel_id);
470   return s;
471 }
472
473 /**
474  * @brief TX function. Only called L2. L3 traffic uses the adj-midchains
475  */
476 static uword
477 mpls_tunnel_tx (vlib_main_t * vm,
478                 vlib_node_runtime_t * node,
479                 vlib_frame_t * frame)
480 {
481   u32 next_index;
482   u32 * from, * to_next, n_left_from, n_left_to_next;
483   vnet_interface_output_runtime_t * rd = (void *) node->runtime_data;
484   const mpls_tunnel_t *mt;
485
486   mt = pool_elt_at_index(mpls_tunnel_pool, rd->dev_instance);
487
488   /* Vector of buffer / pkt indices we're supposed to process */
489   from = vlib_frame_vector_args (frame);
490
491   /* Number of buffers / pkts */
492   n_left_from = frame->n_vectors;
493
494   /* Speculatively send the first buffer to the last disposition we used */
495   next_index = node->cached_next_index;
496
497   while (n_left_from > 0)
498     {
499       /* set up to enqueue to our disposition with index = next_index */
500       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
501
502       /*
503        * FIXME DUAL LOOP
504        */
505       while (n_left_from > 0 && n_left_to_next > 0)
506         {
507           vlib_buffer_t * b0;
508           u32 bi0;
509
510           bi0 = from[0];
511           to_next[0] = bi0;
512           from += 1;
513           to_next += 1;
514           n_left_from -= 1;
515           n_left_to_next -= 1;
516
517           b0 = vlib_get_buffer(vm, bi0);
518
519           vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mt->mt_l2_lb.dpoi_index;
520           /* since we are coming out of the L2 world, where the vlib_buffer
521            * union is used for other things, make sure it is clean for
522            * MPLS from now on.
523            */
524           vnet_buffer(b0)->mpls.first = 0;
525
526           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
527             {
528               mpls_tunnel_trace_t *tr = vlib_add_trace (vm, node,
529                                                    b0, sizeof (*tr));
530               tr->tunnel_id = rd->dev_instance;
531             }
532
533           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
534                                            to_next, n_left_to_next,
535                                            bi0, mt->mt_l2_lb.dpoi_next_node);
536         }
537
538       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
539     }
540
541   return frame->n_vectors;
542 }
543
544 VNET_DEVICE_CLASS (mpls_tunnel_class) = {
545     .name = "MPLS tunnel device",
546     .format_device_name = format_mpls_tunnel_name,
547     .format_device = format_mpls_tunnel_device,
548     .format_tx_trace = format_mpls_tunnel_tx_trace,
549     .tx_function = mpls_tunnel_tx,
550     .admin_up_down_function = mpls_tunnel_admin_up_down,
551 };
552
553 VNET_HW_INTERFACE_CLASS (mpls_tunnel_hw_interface_class) = {
554   .name = "MPLS-Tunnel",
555   .update_adjacency = mpls_tunnel_update_adj,
556   .build_rewrite = mpls_tunnel_build_rewrite,
557   .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
558 };
559
560 const mpls_tunnel_t *
561 mpls_tunnel_get (u32 mti)
562 {
563     return (pool_elt_at_index(mpls_tunnel_pool, mti));
564 }
565
566 /**
567  * @brief Walk all the MPLS tunnels
568  */
569 void
570 mpls_tunnel_walk (mpls_tunnel_walk_cb_t cb,
571                   void *ctx)
572 {
573     u32 mti;
574
575     pool_foreach_index(mti, mpls_tunnel_pool,
576     ({
577         cb(mti, ctx);
578     }));
579 }
580
581 void
582 vnet_mpls_tunnel_del (u32 sw_if_index)
583 {
584     mpls_tunnel_t *mt;
585
586     mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
587
588     if (NULL == mt)
589         return;
590
591     if (FIB_NODE_INDEX_INVALID != mt->mt_path_list)
592         fib_path_list_child_remove(mt->mt_path_list,
593                                    mt->mt_sibling_index);
594     dpo_reset(&mt->mt_l2_lb);
595
596     vec_add1 (mpls_tunnel_free_hw_if_indices, mt->mt_hw_if_index);
597     pool_put(mpls_tunnel_pool, mt);
598     mpls_tunnel_db[sw_if_index] = ~0;
599 }
600
601 u32
602 vnet_mpls_tunnel_create (u8 l2_only,
603                          u8 is_multicast)
604 {
605     vnet_hw_interface_t * hi;
606     mpls_tunnel_t *mt;
607     vnet_main_t * vnm;
608     u32 mti;
609
610     vnm = vnet_get_main();
611     pool_get(mpls_tunnel_pool, mt);
612     memset (mt, 0, sizeof (*mt));
613     mti = mt - mpls_tunnel_pool;
614     fib_node_init(&mt->mt_node, FIB_NODE_TYPE_MPLS_TUNNEL);
615     mt->mt_path_list = FIB_NODE_INDEX_INVALID;
616     mt->mt_sibling_index = FIB_NODE_INDEX_INVALID;
617
618     if (is_multicast)
619         mt->mt_flags |= MPLS_TUNNEL_FLAG_MCAST;
620     if (l2_only)
621         mt->mt_flags |= MPLS_TUNNEL_FLAG_L2;
622
623     /*
624      * Create a new, or re=use and old, tunnel HW interface
625      */
626     if (vec_len (mpls_tunnel_free_hw_if_indices) > 0)
627     {
628         mt->mt_hw_if_index =
629             mpls_tunnel_free_hw_if_indices[vec_len(mpls_tunnel_free_hw_if_indices)-1];
630         _vec_len (mpls_tunnel_free_hw_if_indices) -= 1;
631         hi = vnet_get_hw_interface (vnm, mt->mt_hw_if_index);
632         hi->hw_instance = mti;
633         hi->dev_instance = mti;
634     }
635     else
636     {
637         mt->mt_hw_if_index = vnet_register_interface(
638                                  vnm,
639                                  mpls_tunnel_class.index,
640                                  mti,
641                                  mpls_tunnel_hw_interface_class.index,
642                                  mti);
643         hi = vnet_get_hw_interface (vnm, mt->mt_hw_if_index);
644     }
645
646     /* Standard default MPLS tunnel MTU. */
647     vnet_sw_interface_set_mtu (vnm, hi->sw_if_index, 9000);
648
649     /*
650      * Add the new tunnel to the tunnel DB - key:SW if index
651      */
652     mt->mt_sw_if_index = hi->sw_if_index;
653     vec_validate_init_empty(mpls_tunnel_db, mt->mt_sw_if_index, ~0);
654     mpls_tunnel_db[mt->mt_sw_if_index] = mti;
655
656     return (mt->mt_sw_if_index);
657 }
658
659 void
660 vnet_mpls_tunnel_path_add (u32 sw_if_index,
661                            fib_route_path_t *rpaths)
662 {
663     mpls_tunnel_t *mt;
664     u32 mti;
665
666     mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
667
668     if (NULL == mt)
669         return;
670
671     mti = mt - mpls_tunnel_pool;
672
673     /*
674      * construct a path-list from the path provided
675      */
676     if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
677     {
678         mt->mt_path_list = fib_path_list_create(FIB_PATH_LIST_FLAG_SHARED, rpaths);
679         mt->mt_sibling_index = fib_path_list_child_add(mt->mt_path_list,
680                                                        FIB_NODE_TYPE_MPLS_TUNNEL,
681                                                        mti);
682     }
683     else
684     {
685         fib_node_index_t old_pl_index;
686
687         old_pl_index = mt->mt_path_list;
688
689         mt->mt_path_list =
690             fib_path_list_copy_and_path_add(old_pl_index,
691                                             FIB_PATH_LIST_FLAG_SHARED,
692                                             rpaths);
693
694         fib_path_list_child_remove(old_pl_index,
695                                    mt->mt_sibling_index);
696         mt->mt_sibling_index = fib_path_list_child_add(mt->mt_path_list,
697                                                        FIB_NODE_TYPE_MPLS_TUNNEL,
698                                                        mti);
699         /*
700          * re-resolve all the path-extensions with the new path-list
701          */
702         fib_path_ext_list_resolve(&mt->mt_path_exts, mt->mt_path_list);
703     }
704     fib_path_ext_list_insert(&mt->mt_path_exts,
705                              mt->mt_path_list,
706                              FIB_PATH_EXT_MPLS,
707                              rpaths);
708     mpls_tunnel_restack(mt);
709 }
710
711 int
712 vnet_mpls_tunnel_path_remove (u32 sw_if_index,
713                               fib_route_path_t *rpaths)
714 {
715     mpls_tunnel_t *mt;
716     u32 mti;
717
718     mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
719
720     if (NULL == mt)
721         return (0);
722
723     mti = mt - mpls_tunnel_pool;
724
725     /*
726      * construct a path-list from the path provided
727      */
728     if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
729     {
730         /* can't remove a path if we have onoe */
731         return (0);
732     }
733     else
734     {
735         fib_node_index_t old_pl_index;
736
737         old_pl_index = mt->mt_path_list;
738
739         mt->mt_path_list =
740             fib_path_list_copy_and_path_remove(old_pl_index,
741                                                FIB_PATH_LIST_FLAG_SHARED,
742                                                rpaths);
743
744         fib_path_list_child_remove(old_pl_index,
745                                    mt->mt_sibling_index);
746
747         if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
748         {
749             /* no paths left */
750             return (0);
751         }
752         else
753         {
754             mt->mt_sibling_index =
755                 fib_path_list_child_add(mt->mt_path_list,
756                                         FIB_NODE_TYPE_MPLS_TUNNEL,
757                                         mti);
758         }
759         /*
760          * find the matching path extension and remove it
761          */
762         fib_path_ext_list_remove(&mt->mt_path_exts,
763                                   FIB_PATH_EXT_MPLS,
764                                   rpaths);
765
766         /*
767          * re-resolve all the path-extensions with the new path-list
768          */
769         fib_path_ext_list_resolve(&mt->mt_path_exts,
770                                   mt->mt_path_list);
771
772         mpls_tunnel_restack(mt);
773    }
774
775     return (fib_path_list_get_n_paths(mt->mt_path_list));
776 }
777
778
779 static clib_error_t *
780 vnet_create_mpls_tunnel_command_fn (vlib_main_t * vm,
781                                     unformat_input_t * input,
782                                     vlib_cli_command_t * cmd)
783 {
784     unformat_input_t _line_input, * line_input = &_line_input;
785     vnet_main_t * vnm = vnet_get_main();
786     u8 is_del = 0, l2_only = 0, is_multicast =0;
787     fib_route_path_t rpath, *rpaths = NULL;
788     u32 sw_if_index = ~0, payload_proto;
789     clib_error_t *error = NULL;
790
791     memset(&rpath, 0, sizeof(rpath));
792     payload_proto = DPO_PROTO_MPLS;
793
794     /* Get a line of input. */
795     if (! unformat_user (input, unformat_line_input, line_input))
796         return 0;
797
798     while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
799     {
800         if (unformat (line_input, "del %U",
801                       unformat_vnet_sw_interface, vnm,
802                       &sw_if_index))
803             is_del = 1;
804         else if (unformat (line_input, "add %U",
805                            unformat_vnet_sw_interface, vnm,
806                            &sw_if_index))
807             is_del = 0;
808         else if (unformat (line_input, "add"))
809             is_del = 0;
810         else if (unformat (line_input, "l2-only"))
811             l2_only = 1;
812         else if (unformat (line_input, "multicast"))
813             is_multicast = 1;
814         else if (unformat (line_input, "via %U",
815                            unformat_fib_route_path,
816                            &rpath, &payload_proto))
817             vec_add1(rpaths, rpath);
818         else
819         {
820             error = clib_error_return (0, "unknown input '%U'",
821                                        format_unformat_error, line_input);
822             goto done;
823         }
824     }
825
826     if (is_del)
827     {
828         if (!vnet_mpls_tunnel_path_remove(sw_if_index, rpaths))
829         {
830             vnet_mpls_tunnel_del(sw_if_index);
831         }
832     }
833     else
834     {
835         if (0 == vec_len(rpath.frp_label_stack))
836         {
837             error = clib_error_return (0, "No Output Labels '%U'",
838                                        format_unformat_error, line_input);
839             goto done;
840         }
841
842         if (~0 == sw_if_index)
843         {
844             sw_if_index = vnet_mpls_tunnel_create(l2_only, is_multicast);
845         }
846         vnet_mpls_tunnel_path_add(sw_if_index, rpaths);
847     }
848
849 done:
850     vec_free(rpaths);
851     unformat_free (line_input);
852
853     return error;
854 }
855
856 /*?
857  * This command create a uni-directional MPLS tunnel
858  *
859  * @cliexpar
860  * @cliexstart{create mpls tunnel}
861  *  create mpls tunnel via 10.0.0.1 GigEthernet0/8/0 out-label 33 out-label 34
862  * @cliexend
863  ?*/
864 VLIB_CLI_COMMAND (create_mpls_tunnel_command, static) = {
865   .path = "mpls tunnel",
866   .short_help =
867   "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>]",
868   .function = vnet_create_mpls_tunnel_command_fn,
869 };
870
871 static u8 *
872 format_mpls_tunnel (u8 * s, va_list * args)
873 {
874     mpls_tunnel_t *mt = va_arg (*args, mpls_tunnel_t *);
875     mpls_tunnel_attribute_t attr;
876
877     s = format(s, "mpls_tunnel%d: sw_if_index:%d hw_if_index:%d",
878                mt - mpls_tunnel_pool,
879                mt->mt_sw_if_index,
880                mt->mt_hw_if_index);
881     if (MPLS_TUNNEL_FLAG_NONE != mt->mt_flags) {
882         s = format(s, " \n flags:");
883         FOR_EACH_MPLS_TUNNEL_ATTRIBUTE(attr) {
884             if ((1<<attr) & mt->mt_flags) {
885                 s = format (s, "%s,", mpls_tunnel_attribute_names[attr]);
886             }
887         }
888     }
889     s = format(s, "\n via:\n");
890     s = fib_path_list_format(mt->mt_path_list, s);
891     s = format(s, "%U", format_fib_path_ext_list, &mt->mt_path_exts);
892     s = format(s, "\n");
893
894     if (mt->mt_flags & MPLS_TUNNEL_FLAG_L2)
895     {
896         s = format(s, " forwarding: %U\n",
897                    format_fib_forw_chain_type,
898                    FIB_FORW_CHAIN_TYPE_ETHERNET);
899         s = format(s, " %U\n", format_dpo_id, &mt->mt_l2_lb, 2);
900     }
901
902     return (s);
903 }
904
905 static clib_error_t *
906 show_mpls_tunnel_command_fn (vlib_main_t * vm,
907                              unformat_input_t * input,
908                              vlib_cli_command_t * cmd)
909 {
910     mpls_tunnel_t * mt;
911     u32 mti = ~0;
912
913     if (pool_elts (mpls_tunnel_pool) == 0)
914         vlib_cli_output (vm, "No MPLS tunnels configured...");
915
916     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
917     {
918         if (unformat (input, "%d", &mti))
919             ;
920         else
921             break;
922     }
923
924     if (~0 == mti)
925     {
926         pool_foreach (mt, mpls_tunnel_pool,
927         ({
928             vlib_cli_output (vm, "[@%d] %U",
929                              mt - mpls_tunnel_pool,
930                              format_mpls_tunnel, mt);
931         }));
932     }
933     else
934     {
935         if (pool_is_free_index(mpls_tunnel_pool, mti))
936             return clib_error_return (0, "Not atunnel index %d", mti);
937
938         mt = pool_elt_at_index(mpls_tunnel_pool, mti);
939
940         vlib_cli_output (vm, "[@%d] %U",
941                          mt - mpls_tunnel_pool,
942                          format_mpls_tunnel, mt);
943     }
944
945     return 0;
946 }
947
948 /*?
949  * This command to show MPLS tunnels
950  *
951  * @cliexpar
952  * @cliexstart{sh mpls tunnel 2}
953  * [@2] mpls_tunnel2: sw_if_index:5 hw_if_index:5
954  *  label-stack:
955  *    3,
956  *  via:
957  *   index:26 locks:1 proto:ipv4 uPRF-list:26 len:1 itfs:[2, ]
958  *     index:26 pl-index:26 ipv4 weight=1 attached-nexthop:  oper-flags:resolved,
959  *      10.0.0.2 loop0
960  *         [@0]: ipv4 via 10.0.0.2 loop0: IP4: de:ad:00:00:00:00 -> 00:00:11:aa:bb:cc
961  * @cliexend
962  ?*/
963 VLIB_CLI_COMMAND (show_mpls_tunnel_command, static) = {
964     .path = "show mpls tunnel",
965     .function = show_mpls_tunnel_command_fn,
966 };
967
968 static mpls_tunnel_t *
969 mpls_tunnel_from_fib_node (fib_node_t *node)
970 {
971     ASSERT(FIB_NODE_TYPE_MPLS_TUNNEL == node->fn_type);
972     return ((mpls_tunnel_t*) (((char*)node) -
973                              STRUCT_OFFSET_OF(mpls_tunnel_t, mt_node)));
974 }
975
976 /**
977  * Function definition to backwalk a FIB node
978  */
979 static fib_node_back_walk_rc_t
980 mpls_tunnel_back_walk (fib_node_t *node,
981                       fib_node_back_walk_ctx_t *ctx)
982 {
983     mpls_tunnel_restack(mpls_tunnel_from_fib_node(node));
984
985     return (FIB_NODE_BACK_WALK_CONTINUE);
986 }
987
988 /**
989  * Function definition to get a FIB node from its index
990  */
991 static fib_node_t*
992 mpls_tunnel_fib_node_get (fib_node_index_t index)
993 {
994     mpls_tunnel_t * mt;
995
996     mt = pool_elt_at_index(mpls_tunnel_pool, index);
997
998     return (&mt->mt_node);
999 }
1000
1001 /**
1002  * Function definition to inform the FIB node that its last lock has gone.
1003  */
1004 static void
1005 mpls_tunnel_last_lock_gone (fib_node_t *node)
1006 {
1007     /*
1008      * The MPLS MPLS tunnel is a root of the graph. As such
1009      * it never has children and thus is never locked.
1010      */
1011     ASSERT(0);
1012 }
1013
1014 /*
1015  * Virtual function table registered by MPLS MPLS tunnels
1016  * for participation in the FIB object graph.
1017  */
1018 const static fib_node_vft_t mpls_vft = {
1019     .fnv_get = mpls_tunnel_fib_node_get,
1020     .fnv_last_lock = mpls_tunnel_last_lock_gone,
1021     .fnv_back_walk = mpls_tunnel_back_walk,
1022 };
1023
1024 static clib_error_t *
1025 mpls_tunnel_init (vlib_main_t *vm)
1026 {
1027   fib_node_register_type(FIB_NODE_TYPE_MPLS_TUNNEL, &mpls_vft);
1028
1029   return 0;
1030 }
1031 VLIB_INIT_FUNCTION(mpls_tunnel_init);