FIB table add/delete API
[vpp.git] / src / 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, MPLS_IETF_IPV4_EXPLICIT_NULL_BRIEF_STRING))
70       *label = MPLS_IETF_IPV4_EXPLICIT_NULL_LABEL;
71   else if (unformat (input, MPLS_IETF_IPV6_EXPLICIT_NULL_BRIEF_STRING))
72       *label = MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL;
73   else if (unformat (input, MPLS_IETF_ROUTER_ALERT_BRIEF_STRING))
74       *label = MPLS_IETF_ROUTER_ALERT_LABEL;
75   else if (unformat (input, MPLS_IETF_IMPLICIT_NULL_BRIEF_STRING))
76       *label = MPLS_IETF_IMPLICIT_NULL_LABEL;
77   else if (unformat (input, "%d", label))
78       ;
79   else
80     return (0);
81
82   return (1);
83 }
84
85 u8 * format_mpls_eos_bit (u8 * s, va_list * args)
86 {
87   mpls_eos_bit_t eb = va_arg (*args, mpls_eos_bit_t);
88
89   ASSERT(eb <= MPLS_EOS);
90
91   s = format(s, "%s", mpls_eos_bit_names[eb]);
92
93   return (s);
94 }
95
96 u8 * format_mpls_header (u8 * s, va_list * args)
97 {
98   mpls_unicast_header_t hdr = va_arg (*args, mpls_unicast_header_t);
99
100   return (format(s, "[%U:%d:%d:%U]",
101                  format_mpls_unicast_label, 
102                  vnet_mpls_uc_get_label(hdr.label_exp_s_ttl),
103                  vnet_mpls_uc_get_ttl(hdr.label_exp_s_ttl),
104                  vnet_mpls_uc_get_exp(hdr.label_exp_s_ttl),
105                  format_mpls_eos_bit,
106                  vnet_mpls_uc_get_s(hdr.label_exp_s_ttl)));
107 }
108
109 uword
110 unformat_mpls_header (unformat_input_t * input, va_list * args)
111 {
112   u8 ** result = va_arg (*args, u8 **);
113   mpls_unicast_header_t _h, * h = &_h;
114   u32 label, label_exp_s_ttl;
115
116   if (! unformat (input, "MPLS %d", &label))
117     return 0;
118
119   label_exp_s_ttl = (label<<12) | (1<<8) /* s-bit */ | 0xFF;
120   h->label_exp_s_ttl = clib_host_to_net_u32 (label_exp_s_ttl);
121
122   /* Add gre, mpls headers to result. */
123   {
124     void * p;
125     u32 h_n_bytes = sizeof (h[0]);
126
127     vec_add2 (*result, p, h_n_bytes);
128     clib_memcpy (p, h, h_n_bytes);
129   }
130
131   return 1;
132 }
133
134 uword
135 unformat_mpls_label_net_byte_order (unformat_input_t * input,
136                                         va_list * args)
137 {
138   u32 * result = va_arg (*args, u32 *);
139   u32 label;
140
141   if (!unformat (input, "MPLS: label %d", &label))
142     return 0;
143
144   label = (label<<12) | (1<<8) /* s-bit set */ | 0xFF /* ttl */;
145
146   *result = clib_host_to_net_u32 (label);
147   return 1;
148 }
149
150 u8 * format_mpls_unicast_header_host_byte_order (u8 * s, va_list * args)
151 {
152   mpls_unicast_header_t *h = va_arg(*args, mpls_unicast_header_t *);
153   u32 label = h->label_exp_s_ttl;
154   
155   s = format (s, "label %d exp %d, s %d, ttl %d",
156               vnet_mpls_uc_get_label (label),
157               vnet_mpls_uc_get_exp (label),
158               vnet_mpls_uc_get_s (label),
159               vnet_mpls_uc_get_ttl (label));
160   return s;
161 }
162
163 u8 * format_mpls_unicast_header_net_byte_order (u8 * s, va_list * args)
164 {
165   mpls_unicast_header_t *h = va_arg(*args, mpls_unicast_header_t *);
166   mpls_unicast_header_t h_host;
167
168   h_host.label_exp_s_ttl = clib_net_to_host_u32 (h->label_exp_s_ttl);
169
170   return format (s, "%U", format_mpls_unicast_header_host_byte_order,
171                  &h_host);
172 }
173
174 typedef struct {
175   u32 fib_index;
176   u32 entry_index;
177   u32 dest;
178   u32 s_bit;
179   u32 label;
180 } show_mpls_fib_t;
181
182 int
183 mpls_dest_cmp(void * a1, void * a2)
184 {
185   show_mpls_fib_t * r1 = a1;
186   show_mpls_fib_t * r2 = a2;
187
188   return clib_net_to_host_u32(r1->dest) - clib_net_to_host_u32(r2->dest);
189 }
190
191 int
192 mpls_fib_index_cmp(void * a1, void * a2)
193 {
194   show_mpls_fib_t * r1 = a1;
195   show_mpls_fib_t * r2 = a2;
196
197   return r1->fib_index - r2->fib_index;
198 }
199
200 int
201 mpls_label_cmp(void * a1, void * a2)
202 {
203   show_mpls_fib_t * r1 = a1;
204   show_mpls_fib_t * r2 = a2;
205
206   return r1->label - r2->label;
207 }
208
209 static clib_error_t *
210 vnet_mpls_local_label (vlib_main_t * vm,
211                        unformat_input_t * input,
212                        vlib_cli_command_t * cmd)
213 {
214   unformat_input_t _line_input, * line_input = &_line_input;
215   fib_route_path_t *rpaths = NULL, rpath;
216   u32 table_id, is_del, is_ip;
217   mpls_label_t local_label;
218   mpls_label_t out_label;
219   clib_error_t * error;
220   mpls_eos_bit_t eos;
221   vnet_main_t * vnm;
222   fib_prefix_t pfx;
223
224   vnm = vnet_get_main();
225   error = NULL;
226   is_ip = 0;
227   table_id = 0;
228   eos = MPLS_EOS;
229   is_del = 0;
230   local_label = MPLS_LABEL_INVALID;
231   memset(&pfx, 0, sizeof(pfx));
232
233    /* Get a line of input. */
234   if (! unformat_user (input, unformat_line_input, line_input))
235     return 0;
236
237   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
238     {
239       memset(&rpath, 0, sizeof(rpath));
240
241       if (unformat (line_input, "table %d", &table_id))
242         ;
243       else if (unformat (line_input, "del"))
244         is_del = 1;
245       else if (unformat (line_input, "add"))
246         is_del = 0;
247       else if (unformat (line_input, "eos"))
248         pfx.fp_eos = MPLS_EOS;
249       else if (unformat (line_input, "non-eos"))
250         pfx.fp_eos = MPLS_NON_EOS;
251       else if (unformat (line_input, "%U/%d",
252                          unformat_ip4_address,
253                          &pfx.fp_addr.ip4,
254                          &pfx.fp_len))
255       {
256           pfx.fp_proto = FIB_PROTOCOL_IP4;
257           is_ip = 1;
258       }
259       else if (unformat (line_input, "%U/%d",
260                          unformat_ip6_address,
261                          &pfx.fp_addr.ip6,
262                          &pfx.fp_len))
263       {
264           pfx.fp_proto = FIB_PROTOCOL_IP6;
265           is_ip = 1;
266       }
267       else if (unformat (line_input, "via %U %U weight %u",
268                          unformat_ip4_address,
269                          &rpath.frp_addr.ip4,
270                          unformat_vnet_sw_interface, vnm,
271                          &rpath.frp_sw_if_index,
272                          &rpath.frp_weight))
273       {
274           rpath.frp_proto = DPO_PROTO_IP4;
275           vec_add1(rpaths, rpath);
276       }
277
278       else if (unformat (line_input, "via %U %U weight %u",
279                          unformat_ip6_address,
280                          &rpath.frp_addr.ip6,
281                          unformat_vnet_sw_interface, vnm,
282                          &rpath.frp_sw_if_index,
283                          &rpath.frp_weight))
284       {
285           rpath.frp_proto = DPO_PROTO_IP6;
286           vec_add1(rpaths, rpath);
287       }
288
289       else if (unformat (line_input, "via %U %U",
290                          unformat_ip4_address,
291                          &rpath.frp_addr.ip4,
292                          unformat_vnet_sw_interface, vnm,
293                          &rpath.frp_sw_if_index))
294       {
295           rpath.frp_weight = 1;
296           rpath.frp_proto = DPO_PROTO_IP4;
297           vec_add1(rpaths, rpath);
298       }
299       else if (unformat (line_input, "rx-ip4 %U",
300                          unformat_vnet_sw_interface, vnm,
301                          &rpath.frp_sw_if_index))
302       {
303           rpath.frp_weight = 1;
304           rpath.frp_proto = DPO_PROTO_IP4;
305           rpath.frp_flags = FIB_ROUTE_PATH_INTF_RX;
306           vec_add1(rpaths, rpath);
307       }
308       else if (unformat (line_input, "via %U %U",
309                          unformat_ip6_address,
310                          &rpath.frp_addr.ip6,
311                          unformat_vnet_sw_interface, vnm,
312                          &rpath.frp_sw_if_index))
313       {
314           rpath.frp_weight = 1;
315           rpath.frp_proto = DPO_PROTO_IP6;
316           vec_add1(rpaths, rpath);
317       }
318       else if (unformat (line_input, "via %U next-hop-table %d",
319                          unformat_ip4_address,
320                          &rpath.frp_addr.ip4,
321                          &rpath.frp_fib_index))
322       {
323           rpath.frp_weight = 1;
324           rpath.frp_sw_if_index = ~0;
325           rpath.frp_proto = DPO_PROTO_IP4;
326           vec_add1(rpaths, rpath);
327       }
328       else if (unformat (line_input, "via %U next-hop-table %d",
329                          unformat_ip6_address,
330                          &rpath.frp_addr.ip6,
331                          &rpath.frp_fib_index))
332       {
333           rpath.frp_weight = 1;
334           rpath.frp_sw_if_index = ~0;
335           rpath.frp_proto = DPO_PROTO_IP6;
336           vec_add1(rpaths, rpath);
337       }
338       else if (unformat (line_input, "via %U",
339                          unformat_ip4_address,
340                          &rpath.frp_addr.ip4))
341       {
342           /*
343            * the recursive next-hops are by default in the same table
344            * as the prefix
345            */
346           rpath.frp_fib_index = table_id;
347           rpath.frp_weight = 1;
348           rpath.frp_sw_if_index = ~0;
349           rpath.frp_proto = DPO_PROTO_IP4;
350           vec_add1(rpaths, rpath);
351       }
352       else if (unformat (line_input, "via %U",
353                          unformat_ip6_address,
354                          &rpath.frp_addr.ip6))
355       {
356           rpath.frp_fib_index = table_id;
357           rpath.frp_weight = 1;
358           rpath.frp_sw_if_index = ~0;
359           rpath.frp_proto = DPO_PROTO_IP6;
360           vec_add1(rpaths, rpath);
361       }
362       else if (unformat (line_input, "%d", &local_label))
363         ;
364       else if (unformat (line_input,
365                          "ip4-lookup-in-table %d",
366                          &rpath.frp_fib_index))
367       {
368           rpath.frp_proto = DPO_PROTO_IP4;
369           rpath.frp_sw_if_index = FIB_NODE_INDEX_INVALID;
370           pfx.fp_payload_proto = DPO_PROTO_IP4;
371           vec_add1(rpaths, rpath);
372       }
373       else if (unformat (line_input,
374                          "ip6-lookup-in-table %d",
375                          &rpath.frp_fib_index))
376       {
377           rpath.frp_proto = DPO_PROTO_IP6;
378           rpath.frp_sw_if_index = FIB_NODE_INDEX_INVALID;
379           vec_add1(rpaths, rpath);
380           pfx.fp_payload_proto = DPO_PROTO_IP6;
381       }
382       else if (unformat (line_input,
383                          "mpls-lookup-in-table %d",
384                          &rpath.frp_fib_index))
385       {
386           rpath.frp_proto = DPO_PROTO_MPLS;
387           rpath.frp_sw_if_index = FIB_NODE_INDEX_INVALID;
388           pfx.fp_payload_proto = DPO_PROTO_MPLS;
389           vec_add1(rpaths, rpath);
390       }
391       else if (unformat (line_input,
392                          "l2-input-on %U",
393                          unformat_vnet_sw_interface, vnm,
394                          &rpath.frp_sw_if_index))
395       {
396           rpath.frp_proto = DPO_PROTO_ETHERNET;
397           pfx.fp_payload_proto = DPO_PROTO_ETHERNET;
398           rpath.frp_flags = FIB_ROUTE_PATH_INTF_RX;
399           vec_add1(rpaths, rpath);
400       }
401       else if (unformat (line_input, "out-labels"))
402       {
403           if (vec_len (rpaths) == 0)
404           {
405               error = clib_error_return (0, "Paths then labels");
406               goto done;
407           }
408           else
409           {
410               while (unformat (line_input, "%U",
411                                unformat_mpls_unicast_label,
412                                &out_label))
413               {
414                   vec_add1 (rpaths[vec_len (rpaths) - 1].frp_label_stack,
415                             out_label);
416               }
417           }
418       }
419       else
420       {
421           error = clib_error_return (0, "unkown input: %U",
422                                      format_unformat_error, line_input);
423           goto done;
424       }
425
426     }
427
428   if (MPLS_LABEL_INVALID == local_label)
429   {
430       error = clib_error_return (0, "local-label required: %U",
431                                  format_unformat_error, input);
432       goto done;
433   }
434
435
436   if (is_ip)
437   {
438       u32 fib_index = fib_table_find(pfx.fp_proto, table_id);
439
440       if (FIB_NODE_INDEX_INVALID == fib_index)
441       {
442           error = clib_error_return (0, "%U table-id %d does not exist",
443                                      format_fib_protocol, pfx.fp_proto, table_id);
444           goto done;
445       }
446
447       if (is_del)
448       {
449           fib_table_entry_local_label_remove(fib_index, &pfx, local_label);
450       }
451       else
452       {
453           fib_table_entry_local_label_add(fib_index, &pfx, local_label);
454       }
455   }
456   else
457   {
458       fib_node_index_t fib_index;
459       u32 fi;
460
461       if (NULL == rpaths)
462       {
463           error = clib_error_return(0 , "no paths");
464           goto done;
465       }
466
467       pfx.fp_proto = FIB_PROTOCOL_MPLS;
468       pfx.fp_len = 21;
469       pfx.fp_label = local_label;
470       pfx.fp_payload_proto = rpaths[0].frp_proto;
471
472       /*
473        * the CLI parsing stored table Ids, swap to FIB indicies
474        */
475       if (FIB_NODE_INDEX_INVALID == rpath.frp_sw_if_index)
476       {
477           fi = fib_table_find(dpo_proto_to_fib(pfx.fp_payload_proto),
478                               rpaths[0].frp_fib_index);
479
480           if (~0 == fi)
481           {
482               error = clib_error_return(0 , "%U Via table %d does not exist",
483                                         format_dpo_proto, pfx.fp_payload_proto,
484                                         rpaths[0].frp_fib_index);
485               goto done;
486           }
487           rpaths[0].frp_fib_index = fi;
488       }
489
490       fib_index = mpls_fib_index_from_table_id(table_id);
491
492       if (FIB_NODE_INDEX_INVALID == fib_index)
493       {
494           error = clib_error_return (0, "MPLS table-id %d does not exist",
495                                      table_id);
496           goto done;
497       }
498
499       if (is_del)
500       {
501           fib_table_entry_path_remove2(fib_index,
502                                        &pfx,
503                                        FIB_SOURCE_CLI,
504                                        rpaths);
505       }
506       else
507       {
508           fib_node_index_t lfe;
509
510           lfe = fib_table_entry_path_add2(fib_index,
511                                           &pfx,
512                                           FIB_SOURCE_CLI,
513                                           FIB_ENTRY_FLAG_NONE,
514                                           rpaths);
515
516           if (FIB_NODE_INDEX_INVALID == lfe)
517           {
518               error = clib_error_return (0, "Failed to create %U-%U in MPLS table-id %d",
519                                          format_mpls_unicast_label, local_label,
520                                          format_mpls_eos_bit, eos,
521                                          table_id);
522               goto done;
523           }
524       }
525   }
526
527 done:
528   unformat_free (line_input);
529
530   return error;
531 }
532
533 VLIB_CLI_COMMAND (mpls_local_label_command, static) = {
534   .path = "mpls local-label",
535   .function = vnet_mpls_local_label,
536   .short_help = "Create/Delete MPL local labels",
537 };
538
539 clib_error_t *
540 vnet_mpls_table_cmd (vlib_main_t * vm,
541                      unformat_input_t * main_input,
542                      vlib_cli_command_t * cmdo)
543 {
544   unformat_input_t _line_input, *line_input = &_line_input;
545   clib_error_t *error = NULL;
546   u32 table_id, is_add;
547
548   is_add = 1;
549   table_id = ~0;
550
551   /* Get a line of input. */
552   if (!unformat_user (main_input, unformat_line_input, line_input))
553     return 0;
554
555   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
556     {
557       if (unformat (line_input, "%d", &table_id))
558         ;
559       else if (unformat (line_input, "del"))
560         is_add = 0;
561       else if (unformat (line_input, "add"))
562         is_add = 1;
563       else
564         {
565           error = unformat_parse_error (line_input);
566           goto done;
567         }
568     }
569
570   if (~0 == table_id)
571     {
572       error = clib_error_return (0, "No table id");
573       goto done;
574     }
575   else if (0 == table_id)
576     {
577       error = clib_error_return (0, "Can't change the default table");
578       goto done;
579     }
580   else
581     {
582       if (is_add)
583         {
584           mpls_table_create (table_id, 0);
585         }
586       else
587         {
588           mpls_table_delete (table_id, 0);
589         }
590     }
591
592  done:
593   unformat_free (line_input);
594   return error;
595 }
596
597 /* *INDENT-ON* */
598 /*?
599  * This command is used to add or delete MPLS Tables. All
600  * Tables must be explicitly added before that can be used,
601  * Including the default table.
602  ?*/
603 /* *INDENT-OFF* */
604 VLIB_CLI_COMMAND (ip6_table_command, static) = {
605   .path = "mpla table",
606   .short_help = "mpls table [add|del] <table-id>",
607   .function = vnet_mpls_table_cmd,
608   .is_mp_safe = 1,
609 };
610
611 int
612 mpls_fib_reset_labels (u32 fib_id)
613 {
614   // FIXME
615   return 0;
616 }
617
618 static clib_error_t *
619 mpls_init (vlib_main_t * vm)
620 {
621   clib_error_t * error;
622
623   if ((error = vlib_call_init_function (vm, ip_main_init)))
624     return error;
625
626   return vlib_call_init_function (vm, mpls_input_init);
627 }
628
629 VLIB_INIT_FUNCTION (mpls_init);