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