Deprecate MPLSoGRE tunnels (VPP-502)
[vpp.git] / vnet / vnet / mpls / mpls.c
1 /*
2  * mpls.c: mpls
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.h>
20 #include <vnet/fib/ip4_fib.h>
21 #include <vnet/fib/mpls_fib.h>
22
23 const static char* mpls_eos_bit_names[] = MPLS_EOS_BITS;
24
25 mpls_main_t mpls_main;
26
27 u8 * format_mpls_unicast_label (u8 * s, va_list * args)
28 {
29   mpls_label_t label = va_arg (*args, mpls_label_t);
30
31   switch (label) {
32   case MPLS_IETF_IPV4_EXPLICIT_NULL_LABEL:
33       s = format (s, "%s", MPLS_IETF_IPV4_EXPLICIT_NULL_STRING);
34       break;
35   case MPLS_IETF_ROUTER_ALERT_LABEL:
36       s = format (s, "%s", MPLS_IETF_ROUTER_ALERT_STRING);
37       break;
38   case MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL:
39       s = format (s, "%s", MPLS_IETF_IPV6_EXPLICIT_NULL_STRING);
40       break;
41   case MPLS_IETF_IMPLICIT_NULL_LABEL:
42       s = format (s, "%s", MPLS_IETF_IMPLICIT_NULL_STRING);
43       break;
44   case MPLS_IETF_ELI_LABEL:
45       s = format (s, "%s", MPLS_IETF_ELI_STRING);
46       break;
47   case MPLS_IETF_GAL_LABEL:
48       s = format (s, "%s", MPLS_IETF_GAL_STRING);
49       break;
50   default:
51       s = format (s, "%d", label);
52       break;
53   }
54   return s;
55 }
56
57 uword unformat_mpls_unicast_label (unformat_input_t * input, va_list * args)
58 {
59   mpls_label_t *label = va_arg (*args, mpls_label_t*);
60   
61   if (unformat (input, MPLS_IETF_IPV4_EXPLICIT_NULL_STRING))
62       *label = MPLS_IETF_IPV4_EXPLICIT_NULL_LABEL;
63   else if (unformat (input, MPLS_IETF_IPV6_EXPLICIT_NULL_STRING))
64       *label = MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL;
65   else if (unformat (input, MPLS_IETF_ROUTER_ALERT_STRING))
66       *label = MPLS_IETF_ROUTER_ALERT_LABEL;
67   else if (unformat (input, MPLS_IETF_IMPLICIT_NULL_STRING))
68       *label = MPLS_IETF_IMPLICIT_NULL_LABEL;
69   else if (unformat (input, "%d", label))
70       ;
71
72   return (1);
73 }
74
75 u8 * format_mpls_eos_bit (u8 * s, va_list * args)
76 {
77   mpls_eos_bit_t eb = va_arg (*args, mpls_eos_bit_t);
78
79   ASSERT(eb <= MPLS_EOS);
80
81   s = format(s, "%s", mpls_eos_bit_names[eb]);
82
83   return (s);
84 }
85
86 u8 * format_mpls_header (u8 * s, va_list * args)
87 {
88   mpls_unicast_header_t hdr = va_arg (*args, mpls_unicast_header_t);
89
90   return (format(s, "[%U:%d:%d:%U]",
91                  format_mpls_unicast_label, 
92                  vnet_mpls_uc_get_label(hdr.label_exp_s_ttl),
93                  vnet_mpls_uc_get_ttl(hdr.label_exp_s_ttl),
94                  vnet_mpls_uc_get_exp(hdr.label_exp_s_ttl),
95                  format_mpls_eos_bit,
96                  vnet_mpls_uc_get_s(hdr.label_exp_s_ttl)));
97 }
98
99 uword
100 unformat_mpls_header (unformat_input_t * input, va_list * args)
101 {
102   u8 ** result = va_arg (*args, u8 **);
103   mpls_unicast_header_t _h, * h = &_h;
104   u32 label, label_exp_s_ttl;
105
106   if (! unformat (input, "MPLS %d", &label))
107     return 0;
108
109   label_exp_s_ttl = (label<<12) | (1<<8) /* s-bit */ | 0xFF;
110   h->label_exp_s_ttl = clib_host_to_net_u32 (label_exp_s_ttl);
111
112   /* Add gre, mpls headers to result. */
113   {
114     void * p;
115     u32 h_n_bytes = sizeof (h[0]);
116
117     vec_add2 (*result, p, h_n_bytes);
118     clib_memcpy (p, h, h_n_bytes);
119   }
120
121   return 1;
122 }
123
124 u8 * format_mpls_eth_tx_trace (u8 * s, va_list * args)
125 {
126   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
127   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
128   mpls_eth_tx_trace_t * t = va_arg (*args, mpls_eth_tx_trace_t *);
129   mpls_main_t * mm = &mpls_main;
130     
131   if (t->lookup_miss)
132     s = format (s, "MPLS: lookup miss");
133   else
134     {
135       s = format (s, "MPLS: tunnel %d labels %U len %d tx_sw_index %d dst %U",
136                   t->tunnel_id,
137                   format_mpls_encap_index, mm, t->mpls_encap_index, 
138                   clib_net_to_host_u16 (t->length),
139                   t->tx_sw_if_index, 
140                   format_ethernet_address, t->dst);
141     }
142   return s;
143 }
144
145 u8 * format_mpls_eth_header_with_length (u8 * s, va_list * args)
146 {
147   ethernet_header_t * h = va_arg (*args, ethernet_header_t *);
148   mpls_unicast_header_t * m = (mpls_unicast_header_t *)(h+1);
149   u32 max_header_bytes = va_arg (*args, u32);
150   uword header_bytes;
151
152   header_bytes = sizeof (h[0]);
153   if (max_header_bytes != 0 && header_bytes > max_header_bytes)
154     return format (s, "ethernet header truncated");
155
156   s = format 
157     (s, "ETHERNET-MPLS label %d", 
158      vnet_mpls_uc_get_label (clib_net_to_host_u32 (m->label_exp_s_ttl)));
159
160   return s;
161 }
162
163 uword
164 unformat_mpls_label_net_byte_order (unformat_input_t * input,
165                                         va_list * args)
166 {
167   u32 * result = va_arg (*args, u32 *);
168   u32 label;
169
170   if (!unformat (input, "MPLS: label %d", &label))
171     return 0;
172
173   label = (label<<12) | (1<<8) /* s-bit set */ | 0xFF /* ttl */;
174
175   *result = clib_host_to_net_u32 (label);
176   return 1;
177 }
178
179 mpls_encap_t * 
180 mpls_encap_by_fib_and_dest (mpls_main_t * mm, u32 rx_fib, u32 dst_address)
181 {
182   uword * p;
183   mpls_encap_t * e;
184   u64 key;
185
186   key = ((u64)rx_fib<<32) | ((u64) dst_address);
187   p = hash_get (mm->mpls_encap_by_fib_and_dest, key);
188
189   if (!p)
190     return 0;
191
192   e = pool_elt_at_index (mm->encaps, p[0]);
193   return e;
194 }
195
196 int vnet_mpls_add_del_encap (ip4_address_t *dest, u32 fib_id, 
197                              u32 *labels_host_byte_order,
198                              u32 policy_tunnel_index,
199                              int no_dst_hash, u32 * indexp, int is_add)
200 {
201   mpls_main_t * mm = &mpls_main;
202   ip4_main_t * im = &ip4_main;
203   mpls_encap_t * e;
204   u32 label_net_byte_order, label_host_byte_order;
205   u32 fib_index;
206   u64 key;
207   uword *p;
208   int i;
209   
210   p = hash_get (im->fib_index_by_table_id, fib_id);
211   if (! p)
212     return VNET_API_ERROR_NO_SUCH_FIB;
213   
214   fib_index = p[0];
215   
216   key = ((u64)fib_index<<32) | ((u64) dest->as_u32);
217   
218   if (is_add)
219     {
220       pool_get (mm->encaps, e);
221       memset (e, 0, sizeof (*e));
222       
223       for (i = 0; i < vec_len (labels_host_byte_order); i++)
224         {
225           mpls_unicast_header_t h;
226           label_host_byte_order = labels_host_byte_order[i];
227           
228           /* Reformat label into mpls_unicast_header_t */
229           label_host_byte_order <<= 12;
230           // FIXME NEOS AND EOS
231           //if (i == vec_len(labels_host_byte_order) - 1)
232           //  label_host_byte_order |= 1<<8;            /* S=1 */
233           label_host_byte_order |= 0xff;            /* TTL=FF */
234           label_net_byte_order = clib_host_to_net_u32 (label_host_byte_order);
235           h.label_exp_s_ttl = label_net_byte_order;
236           vec_add1 (e->labels, h);
237         }
238       if (no_dst_hash == 0)
239         hash_set (mm->mpls_encap_by_fib_and_dest, key, e - mm->encaps);
240       if (indexp)
241         *indexp = e - mm->encaps;
242       if (policy_tunnel_index != ~0)
243         return vnet_mpls_policy_tunnel_add_rewrite (mm, e, policy_tunnel_index);
244     }
245   else
246     {
247       p = hash_get (mm->mpls_encap_by_fib_and_dest, key);
248       if (!p)
249         return VNET_API_ERROR_NO_SUCH_LABEL;
250       
251       e = pool_elt_at_index (mm->encaps, p[0]);
252       
253       vec_free (e->labels);
254       vec_free (e->rewrite);
255       pool_put(mm->encaps, e);
256       
257       if (no_dst_hash == 0)
258         hash_unset (mm->mpls_encap_by_fib_and_dest, key);    
259     }
260   return 0;
261 }
262
263 static clib_error_t *
264 mpls_add_encap_command_fn (vlib_main_t * vm,
265                            unformat_input_t * input,
266                            vlib_cli_command_t * cmd)
267 {
268   u32 fib_id;
269   u32 *labels = 0;
270   u32 this_label;
271   ip4_address_t dest;
272   u32 policy_tunnel_index = ~0;
273   int no_dst_hash = 0;
274   int rv;
275   int fib_set = 0;
276   int dest_set = 0;
277
278   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
279     {
280       if (unformat (input, "fib %d", &fib_id))
281         fib_set = 1;
282       else if (unformat (input, "dest %U", unformat_ip4_address, &dest))
283         dest_set = 1;
284       else if (unformat (input, "no-dst-hash"))
285         no_dst_hash = 1;
286       else if (unformat (input, "label %d", &this_label))
287         vec_add1 (labels, this_label);
288       else if (unformat (input, "policy-tunnel %d", &policy_tunnel_index))
289         ;
290       else
291         break;
292     }
293
294   if (fib_set == 0)
295     return clib_error_return (0, "fib-id missing");
296   if (dest_set == 0)
297     return clib_error_return (0, "destination IP address missing");
298   if (vec_len (labels) == 0)
299     return clib_error_return (0, "label stack missing");
300   
301   rv = vnet_mpls_add_del_encap (&dest, fib_id, labels, 
302                                 policy_tunnel_index, 
303                                 no_dst_hash, 0 /* indexp */,
304                                 1 /* is_add */);
305   vec_free (labels);
306
307   switch (rv)
308     {
309     case 0:
310       break;
311
312     case VNET_API_ERROR_NO_SUCH_FIB:
313       return clib_error_return (0, "fib id %d unknown", fib_id);
314       
315     default:
316       return clib_error_return (0, "vnet_mpls_add_del_encap returned %d", 
317                                 rv);
318     }
319
320   return 0;
321 }
322
323 VLIB_CLI_COMMAND (mpls_add_encap_command, static) = {
324   .path = "mpls encap add",
325   .short_help = 
326   "mpls encap add label <label> ... fib <id> dest <ip4-address>",
327   .function = mpls_add_encap_command_fn,
328 };
329
330 u8 * format_mpls_unicast_header_host_byte_order (u8 * s, va_list * args)
331 {
332   mpls_unicast_header_t *h = va_arg(*args, mpls_unicast_header_t *);
333   u32 label = h->label_exp_s_ttl;
334   
335   s = format (s, "label %d exp %d, s %d, ttl %d",
336               vnet_mpls_uc_get_label (label),
337               vnet_mpls_uc_get_exp (label),
338               vnet_mpls_uc_get_s (label),
339               vnet_mpls_uc_get_ttl (label));
340   return s;
341 }
342
343 u8 * format_mpls_unicast_header_net_byte_order (u8 * s, va_list * args)
344 {
345   mpls_unicast_header_t *h = va_arg(*args, mpls_unicast_header_t *);
346   mpls_unicast_header_t h_host;
347
348   h_host.label_exp_s_ttl = clib_net_to_host_u32 (h->label_exp_s_ttl);
349
350   return format (s, "%U", format_mpls_unicast_header_host_byte_order,
351                  &h_host);
352 }
353
354 static clib_error_t *
355 mpls_del_encap_command_fn (vlib_main_t * vm,
356                  unformat_input_t * input,
357                  vlib_cli_command_t * cmd)
358 {
359   u32 fib_id;
360   ip4_address_t dest;
361   int rv;
362   
363   if (unformat (input, "fib %d dest %U", &fib_id, 
364                 unformat_ip4_address, &dest)) 
365     {
366       rv = vnet_mpls_add_del_encap (&dest, fib_id, 0 /* labels */, 
367                                     ~0 /* policy_tunnel_index */,
368                                     0 /* no_dst_hash */,
369                                     0 /* indexp */,
370                                     0 /* is_add */);
371       switch (rv)
372         {
373         case VNET_API_ERROR_NO_SUCH_FIB:
374           return clib_error_return (0, "fib id %d unknown", fib_id);
375         case VNET_API_ERROR_NO_SUCH_ENTRY:
376           return clib_error_return (0, "dest %U not in fib %d",
377                                     format_ip4_address, &dest, fib_id);
378         default:
379           break;
380         }
381       return 0;
382     }
383   else
384     return clib_error_return (0, "unknown input `%U'",
385                               format_unformat_error, input);
386 }
387
388 VLIB_CLI_COMMAND (mpls_del_encap_command, static) = {
389   .path = "mpls encap delete",
390   .short_help = "mpls encap delete fib <id> dest <ip4-address>",
391   .function = mpls_del_encap_command_fn,
392 };
393
394 int
395 mpls_dest_cmp(void * a1, void * a2)
396 {
397   show_mpls_fib_t * r1 = a1;
398   show_mpls_fib_t * r2 = a2;
399
400   return clib_net_to_host_u32(r1->dest) - clib_net_to_host_u32(r2->dest);
401 }
402
403 int
404 mpls_fib_index_cmp(void * a1, void * a2)
405 {
406   show_mpls_fib_t * r1 = a1;
407   show_mpls_fib_t * r2 = a2;
408
409   return r1->fib_index - r2->fib_index;
410 }
411
412 int
413 mpls_label_cmp(void * a1, void * a2)
414 {
415   show_mpls_fib_t * r1 = a1;
416   show_mpls_fib_t * r2 = a2;
417
418   return r1->label - r2->label;
419 }
420
421 static clib_error_t *
422 show_mpls_fib_command_fn (vlib_main_t * vm,
423                  unformat_input_t * input,
424                  vlib_cli_command_t * cmd)
425 {
426   u64 key; 
427   u32 value;
428   show_mpls_fib_t *records = 0;
429   show_mpls_fib_t *s;
430   mpls_main_t * mm = &mpls_main;
431   ip4_fib_t * rx_fib;
432
433   hash_foreach (key, value, mm->mpls_encap_by_fib_and_dest, 
434   ({
435     vec_add2 (records, s, 1);
436     s->fib_index = (u32)(key>>32);
437     s->dest = (u32)(key & 0xFFFFFFFF);
438     s->entry_index = (u32) value;
439   }));
440
441   if (!vec_len(records))
442     {
443       vlib_cli_output (vm, "MPLS encap table empty");
444     }
445   /* sort output by dst address within fib */
446   vec_sort_with_function (records, mpls_dest_cmp);
447   vec_sort_with_function (records, mpls_fib_index_cmp);
448   vlib_cli_output (vm, "MPLS encap table");
449   vlib_cli_output (vm, "%=6s%=16s%=16s", "Table", "Dest address", "Labels");
450   vec_foreach (s, records)
451     {
452       rx_fib = ip4_fib_get (s->fib_index);
453       vlib_cli_output (vm, "%=6d%=16U%=16U", rx_fib->table_id, 
454                        format_ip4_address, &s->dest,
455                        format_mpls_encap_index, mm, s->entry_index);
456     }
457
458   vec_free(records);
459   return 0;
460 }
461
462 VLIB_CLI_COMMAND (show_mpls_fib_command, static) = {
463     .path = "show mpls encap",
464     .short_help = "show mpls encap",
465     .function = show_mpls_fib_command_fn,
466 };
467
468 static clib_error_t *
469 vnet_mpls_local_label (vlib_main_t * vm,
470                        unformat_input_t * input,
471                        vlib_cli_command_t * cmd)
472 {
473   unformat_input_t _line_input, * line_input = &_line_input;
474   fib_route_path_t *rpaths = NULL, rpath;
475   clib_error_t * error = 0;
476   u32 table_id, is_del, is_ip;
477   fib_prefix_t pfx;
478   mpls_label_t local_label;
479   mpls_eos_bit_t eos;
480
481   is_ip = 0;
482   table_id = 0;
483   eos = MPLS_EOS;
484   is_del = 0;
485   local_label = MPLS_LABEL_INVALID;
486   memset(&pfx, 0, sizeof(pfx));
487
488    /* Get a line of input. */
489   if (! unformat_user (input, unformat_line_input, line_input))
490     return 0;
491
492   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
493     {
494       memset(&rpath, 0, sizeof(rpath));
495
496       if (unformat (line_input, "table %d", &table_id))
497         ;
498       else if (unformat (line_input, "del"))
499         is_del = 1;
500       else if (unformat (line_input, "add"))
501         is_del = 0;
502       else if (unformat (line_input, "eos"))
503         pfx.fp_eos = MPLS_EOS;
504       else if (unformat (line_input, "non-eos"))
505         pfx.fp_eos = MPLS_NON_EOS;
506       else if (unformat (line_input, "%U/%d",
507                          unformat_ip4_address,
508                          &pfx.fp_addr.ip4,
509                          &pfx.fp_len))
510       {
511           pfx.fp_proto = FIB_PROTOCOL_IP4;
512           is_ip = 1;
513       }
514       else if (unformat (line_input, "%U/%d",
515                          unformat_ip6_address,
516                          &pfx.fp_addr.ip6,
517                          &pfx.fp_len))
518       {
519           pfx.fp_proto = FIB_PROTOCOL_IP6;
520           is_ip = 1;
521       }
522       else if (unformat (line_input, "%d", &local_label))
523         ;
524       else if (unformat (line_input,
525                          "ip4-lookup-in-table %d",
526                          &rpath.frp_fib_index))
527       {
528           rpath.frp_label = MPLS_LABEL_INVALID;
529           rpath.frp_proto = FIB_PROTOCOL_IP4;
530           rpath.frp_sw_if_index = FIB_NODE_INDEX_INVALID;
531           pfx.fp_payload_proto = FIB_PROTOCOL_IP4;
532           vec_add1(rpaths, rpath);
533       }
534       else if (unformat (line_input,
535                          "ip6-lookup-in-table %d",
536                          &rpath.frp_fib_index))
537       {
538           rpath.frp_label = MPLS_LABEL_INVALID;
539           rpath.frp_proto = FIB_PROTOCOL_IP6;
540           rpath.frp_sw_if_index = FIB_NODE_INDEX_INVALID;
541           vec_add1(rpaths, rpath);
542           pfx.fp_payload_proto = FIB_PROTOCOL_IP6;
543       }
544       else if (unformat (line_input,
545                          "mpls-lookup-in-table %d",
546                          &rpath.frp_fib_index))
547       {
548           rpath.frp_label = MPLS_LABEL_INVALID;
549           rpath.frp_proto = FIB_PROTOCOL_MPLS;
550           rpath.frp_sw_if_index = FIB_NODE_INDEX_INVALID;
551           pfx.fp_payload_proto = FIB_PROTOCOL_MPLS;
552           vec_add1(rpaths, rpath);
553       }
554       else
555       {
556           error = clib_error_return (0, "unkown input: %U",
557                                      format_unformat_error, input);
558           goto done;
559       }
560
561     }
562
563   if (MPLS_LABEL_INVALID == local_label)
564   {
565       error = clib_error_return (0, "local-label required: %U",
566                                  format_unformat_error, input);
567       goto done;
568   }
569
570
571   if (is_ip)
572   {
573       u32 fib_index = fib_table_find(pfx.fp_proto, table_id);
574
575       if (FIB_NODE_INDEX_INVALID == fib_index)
576       {
577           error = clib_error_return (0, "%U table-id %d does not exist",
578                                      format_fib_protocol, pfx.fp_proto, table_id);
579           goto done;
580       }
581
582       if (is_del)
583       {
584           fib_table_entry_local_label_remove(fib_index, &pfx, local_label);
585       }
586       else
587       {
588           fib_table_entry_local_label_add(fib_index, &pfx, local_label);
589       }
590   }
591   else
592   {
593       fib_node_index_t lfe, fib_index;
594       u32 fi;
595
596       pfx.fp_proto = FIB_PROTOCOL_MPLS;
597       pfx.fp_len = 21;
598       pfx.fp_label = local_label;
599
600       /*
601        * the CLI parsing stored table Ids, swap to FIB indicies
602        */
603       fi = fib_table_id_find_fib_index(pfx.fp_payload_proto,
604                                        rpaths[0].frp_fib_index);
605
606       if (~0 == fi)
607       {
608           error = clib_error_return(0 , "%U Via table %d does not exist",
609                                     format_fib_protocol, pfx.fp_payload_proto,
610                                     rpaths[0].frp_fib_index);
611           goto done;
612       }
613       rpaths[0].frp_fib_index = fi;
614
615       fib_index = mpls_fib_index_from_table_id(table_id);
616
617       if (FIB_NODE_INDEX_INVALID == fib_index)
618       {
619           error = clib_error_return (0, "MPLS table-id %d does not exist",
620                                      table_id);
621           goto done;
622       }
623
624       lfe = fib_table_entry_path_add2(fib_index,
625                                       &pfx,
626                                       FIB_SOURCE_CLI,
627                                       FIB_ENTRY_FLAG_NONE,
628                                       rpaths);
629
630       if (FIB_NODE_INDEX_INVALID == lfe)
631       {
632           error = clib_error_return (0, "Failed to create %U-%U in MPLS table-id %d",
633                                      format_mpls_unicast_label, local_label,
634                                      format_mpls_eos_bit, eos,
635                                      table_id);
636           goto done;
637       }
638   }
639
640 done:
641   return error;
642 }
643
644 VLIB_CLI_COMMAND (mpls_local_label_command, static) = {
645   .path = "mpls local-label",
646   .function = vnet_mpls_local_label,
647   .short_help = "Create/Delete MPL local labels",
648 };
649
650 int mpls_fib_reset_labels (u32 fib_id)
651 {
652   u64 key; 
653   u32 value;
654   show_mpls_fib_t *records = 0;
655   show_mpls_fib_t *s;
656   mpls_main_t * mm = &mpls_main;
657   ip4_main_t * im = &ip4_main;
658   u32 fib_index;
659   uword *p;
660
661   p = hash_get (im->fib_index_by_table_id, fib_id);
662   if (! p)
663     return VNET_API_ERROR_NO_SUCH_FIB;
664
665   fib_index = p[0];
666
667   hash_foreach (key, value, mm->mpls_encap_by_fib_and_dest, 
668   ({
669     if (fib_index == (u32)(key>>32)) {
670         vec_add2 (records, s, 1);
671         s->dest = (u32)(key & 0xFFFFFFFF);
672         s->entry_index = (u32) value;
673     }
674   }));
675
676   vec_foreach (s, records)
677     {
678       key = ((u64)fib_index<<32) | ((u64) s->dest);
679       hash_unset (mm->mpls_encap_by_fib_and_dest, key);
680       pool_put_index (mm->encaps, s->entry_index);
681     }
682                 
683   vec_free(records);
684   return 0;
685 }
686
687 static clib_error_t * mpls_init (vlib_main_t * vm)
688 {
689   mpls_main_t * mm = &mpls_main;
690   clib_error_t * error;
691
692   mm->vlib_main = vm;
693   mm->vnet_main = vnet_get_main();
694
695   if ((error = vlib_call_init_function (vm, ip_main_init)))
696     return error;
697
698   mm->mpls_encap_by_fib_and_dest = hash_create (0, sizeof (uword));
699
700   return vlib_call_init_function (vm, mpls_input_init);
701 }
702
703 VLIB_INIT_FUNCTION (mpls_init);
704
705 mpls_main_t * mpls_get_main (vlib_main_t * vm)
706 {
707   vlib_call_init_function (vm, mpls_init);
708   return &mpls_main;
709 }
710