MPLS Mcast
[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, "%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 uword
125 unformat_mpls_label_net_byte_order (unformat_input_t * input,
126                                         va_list * args)
127 {
128   u32 * result = va_arg (*args, u32 *);
129   u32 label;
130
131   if (!unformat (input, "MPLS: label %d", &label))
132     return 0;
133
134   label = (label<<12) | (1<<8) /* s-bit set */ | 0xFF /* ttl */;
135
136   *result = clib_host_to_net_u32 (label);
137   return 1;
138 }
139
140 u8 * format_mpls_unicast_header_host_byte_order (u8 * s, va_list * args)
141 {
142   mpls_unicast_header_t *h = va_arg(*args, mpls_unicast_header_t *);
143   u32 label = h->label_exp_s_ttl;
144   
145   s = format (s, "label %d exp %d, s %d, ttl %d",
146               vnet_mpls_uc_get_label (label),
147               vnet_mpls_uc_get_exp (label),
148               vnet_mpls_uc_get_s (label),
149               vnet_mpls_uc_get_ttl (label));
150   return s;
151 }
152
153 u8 * format_mpls_unicast_header_net_byte_order (u8 * s, va_list * args)
154 {
155   mpls_unicast_header_t *h = va_arg(*args, mpls_unicast_header_t *);
156   mpls_unicast_header_t h_host;
157
158   h_host.label_exp_s_ttl = clib_net_to_host_u32 (h->label_exp_s_ttl);
159
160   return format (s, "%U", format_mpls_unicast_header_host_byte_order,
161                  &h_host);
162 }
163
164 typedef struct {
165   u32 fib_index;
166   u32 entry_index;
167   u32 dest;
168   u32 s_bit;
169   u32 label;
170 } show_mpls_fib_t;
171
172 int
173 mpls_dest_cmp(void * a1, void * a2)
174 {
175   show_mpls_fib_t * r1 = a1;
176   show_mpls_fib_t * r2 = a2;
177
178   return clib_net_to_host_u32(r1->dest) - clib_net_to_host_u32(r2->dest);
179 }
180
181 int
182 mpls_fib_index_cmp(void * a1, void * a2)
183 {
184   show_mpls_fib_t * r1 = a1;
185   show_mpls_fib_t * r2 = a2;
186
187   return r1->fib_index - r2->fib_index;
188 }
189
190 int
191 mpls_label_cmp(void * a1, void * a2)
192 {
193   show_mpls_fib_t * r1 = a1;
194   show_mpls_fib_t * r2 = a2;
195
196   return r1->label - r2->label;
197 }
198
199 static clib_error_t *
200 vnet_mpls_local_label (vlib_main_t * vm,
201                        unformat_input_t * input,
202                        vlib_cli_command_t * cmd)
203 {
204   unformat_input_t _line_input, * line_input = &_line_input;
205   fib_route_path_t *rpaths = NULL, rpath;
206   u32 table_id, is_del, is_ip;
207   mpls_label_t local_label;
208   mpls_label_t out_label;
209   clib_error_t * error;
210   mpls_eos_bit_t eos;
211   vnet_main_t * vnm;
212   fib_prefix_t pfx;
213
214   vnm = vnet_get_main();
215   error = NULL;
216   is_ip = 0;
217   table_id = 0;
218   eos = MPLS_EOS;
219   is_del = 0;
220   local_label = MPLS_LABEL_INVALID;
221   memset(&pfx, 0, sizeof(pfx));
222
223    /* Get a line of input. */
224   if (! unformat_user (input, unformat_line_input, line_input))
225     return 0;
226
227   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
228     {
229       memset(&rpath, 0, sizeof(rpath));
230
231       if (unformat (line_input, "table %d", &table_id))
232         ;
233       else if (unformat (line_input, "del"))
234         is_del = 1;
235       else if (unformat (line_input, "add"))
236         is_del = 0;
237       else if (unformat (line_input, "eos"))
238         pfx.fp_eos = MPLS_EOS;
239       else if (unformat (line_input, "non-eos"))
240         pfx.fp_eos = MPLS_NON_EOS;
241       else if (unformat (line_input, "%U/%d",
242                          unformat_ip4_address,
243                          &pfx.fp_addr.ip4,
244                          &pfx.fp_len))
245       {
246           pfx.fp_proto = FIB_PROTOCOL_IP4;
247           is_ip = 1;
248       }
249       else if (unformat (line_input, "%U/%d",
250                          unformat_ip6_address,
251                          &pfx.fp_addr.ip6,
252                          &pfx.fp_len))
253       {
254           pfx.fp_proto = FIB_PROTOCOL_IP6;
255           is_ip = 1;
256       }
257       else if (unformat (line_input, "via %U %U weight %u",
258                          unformat_ip4_address,
259                          &rpath.frp_addr.ip4,
260                          unformat_vnet_sw_interface, vnm,
261                          &rpath.frp_sw_if_index,
262                          &rpath.frp_weight))
263       {
264           rpath.frp_proto = FIB_PROTOCOL_IP4;
265           vec_add1(rpaths, rpath);
266       }
267
268       else if (unformat (line_input, "via %U %U weight %u",
269                          unformat_ip6_address,
270                          &rpath.frp_addr.ip6,
271                          unformat_vnet_sw_interface, vnm,
272                          &rpath.frp_sw_if_index,
273                          &rpath.frp_weight))
274       {
275           rpath.frp_proto = FIB_PROTOCOL_IP6;
276           vec_add1(rpaths, rpath);
277       }
278
279       else if (unformat (line_input, "via %U %U",
280                          unformat_ip4_address,
281                          &rpath.frp_addr.ip4,
282                          unformat_vnet_sw_interface, vnm,
283                          &rpath.frp_sw_if_index))
284       {
285           rpath.frp_weight = 1;
286           rpath.frp_proto = FIB_PROTOCOL_IP4;
287           vec_add1(rpaths, rpath);
288       }
289       else if (unformat (line_input, "rx-ip4 %U",
290                          unformat_vnet_sw_interface, vnm,
291                          &rpath.frp_sw_if_index))
292       {
293           rpath.frp_weight = 1;
294           rpath.frp_proto = FIB_PROTOCOL_IP4;
295           rpath.frp_flags = FIB_ROUTE_PATH_INTF_RX;
296           vec_add1(rpaths, rpath);
297       }
298       else if (unformat (line_input, "via %U %U",
299                          unformat_ip6_address,
300                          &rpath.frp_addr.ip6,
301                          unformat_vnet_sw_interface, vnm,
302                          &rpath.frp_sw_if_index))
303       {
304           rpath.frp_weight = 1;
305           rpath.frp_proto = FIB_PROTOCOL_IP6;
306           vec_add1(rpaths, rpath);
307       }
308       else if (unformat (line_input, "via %U next-hop-table %d",
309                          unformat_ip4_address,
310                          &rpath.frp_addr.ip4,
311                          &rpath.frp_fib_index))
312       {
313           rpath.frp_weight = 1;
314           rpath.frp_sw_if_index = ~0;
315           rpath.frp_proto = FIB_PROTOCOL_IP4;
316           vec_add1(rpaths, rpath);
317       }
318       else if (unformat (line_input, "via %U next-hop-table %d",
319                          unformat_ip6_address,
320                          &rpath.frp_addr.ip6,
321                          &rpath.frp_fib_index))
322       {
323           rpath.frp_weight = 1;
324           rpath.frp_sw_if_index = ~0;
325           rpath.frp_proto = FIB_PROTOCOL_IP6;
326           vec_add1(rpaths, rpath);
327       }
328       else if (unformat (line_input, "via %U",
329                          unformat_ip4_address,
330                          &rpath.frp_addr.ip4))
331       {
332           /*
333            * the recursive next-hops are by default in the same table
334            * as the prefix
335            */
336           rpath.frp_fib_index = table_id;
337           rpath.frp_weight = 1;
338           rpath.frp_sw_if_index = ~0;
339           rpath.frp_proto = FIB_PROTOCOL_IP4;
340           vec_add1(rpaths, rpath);
341       }
342       else if (unformat (line_input, "via %U",
343                          unformat_ip6_address,
344                          &rpath.frp_addr.ip6))
345       {
346           rpath.frp_fib_index = table_id;
347           rpath.frp_weight = 1;
348           rpath.frp_sw_if_index = ~0;
349           rpath.frp_proto = FIB_PROTOCOL_IP6;
350           vec_add1(rpaths, rpath);
351       }
352       else if (unformat (line_input, "%d", &local_label))
353         ;
354       else if (unformat (line_input,
355                          "ip4-lookup-in-table %d",
356                          &rpath.frp_fib_index))
357       {
358           rpath.frp_proto = FIB_PROTOCOL_IP4;
359           rpath.frp_sw_if_index = FIB_NODE_INDEX_INVALID;
360           pfx.fp_payload_proto = DPO_PROTO_IP4;
361           vec_add1(rpaths, rpath);
362       }
363       else if (unformat (line_input,
364                          "ip6-lookup-in-table %d",
365                          &rpath.frp_fib_index))
366       {
367           rpath.frp_proto = FIB_PROTOCOL_IP6;
368           rpath.frp_sw_if_index = FIB_NODE_INDEX_INVALID;
369           vec_add1(rpaths, rpath);
370           pfx.fp_payload_proto = DPO_PROTO_IP6;
371       }
372       else if (unformat (line_input,
373                          "mpls-lookup-in-table %d",
374                          &rpath.frp_fib_index))
375       {
376           rpath.frp_proto = FIB_PROTOCOL_MPLS;
377           rpath.frp_sw_if_index = FIB_NODE_INDEX_INVALID;
378           pfx.fp_payload_proto = DPO_PROTO_MPLS;
379           vec_add1(rpaths, rpath);
380       }
381       else if (unformat (line_input, "out-label %U",
382                          unformat_mpls_unicast_label,
383                          &out_label))
384       {
385           if (vec_len(rpaths) == 0)
386           {
387               error = clib_error_return(0 , "Paths then labels");
388               goto done;
389           }
390           vec_add1(rpaths[vec_len(rpaths)-1].frp_label_stack, out_label);
391       }
392       else
393       {
394           error = clib_error_return (0, "unkown input: %U",
395                                      format_unformat_error, line_input);
396           goto done;
397       }
398
399     }
400
401   if (MPLS_LABEL_INVALID == local_label)
402   {
403       error = clib_error_return (0, "local-label required: %U",
404                                  format_unformat_error, input);
405       goto done;
406   }
407
408
409   if (is_ip)
410   {
411       u32 fib_index = fib_table_find(pfx.fp_proto, table_id);
412
413       if (FIB_NODE_INDEX_INVALID == fib_index)
414       {
415           error = clib_error_return (0, "%U table-id %d does not exist",
416                                      format_fib_protocol, pfx.fp_proto, table_id);
417           goto done;
418       }
419
420       if (is_del)
421       {
422           fib_table_entry_local_label_remove(fib_index, &pfx, local_label);
423       }
424       else
425       {
426           fib_table_entry_local_label_add(fib_index, &pfx, local_label);
427       }
428   }
429   else
430   {
431       fib_node_index_t lfe, fib_index;
432       u32 fi;
433
434       if (NULL == rpaths)
435       {
436           error = clib_error_return(0 , "no paths");
437           goto done;
438       }
439
440       pfx.fp_proto = FIB_PROTOCOL_MPLS;
441       pfx.fp_len = 21;
442       pfx.fp_label = local_label;
443       pfx.fp_payload_proto = fib_proto_to_dpo(rpaths[0].frp_proto);
444
445       /*
446        * the CLI parsing stored table Ids, swap to FIB indicies
447        */
448       if (FIB_NODE_INDEX_INVALID == rpath.frp_sw_if_index)
449       {
450           fi = fib_table_id_find_fib_index(dpo_proto_to_fib(pfx.fp_payload_proto),
451                                            rpaths[0].frp_fib_index);
452
453           if (~0 == fi)
454           {
455               error = clib_error_return(0 , "%U Via table %d does not exist",
456                                         format_dpo_proto, pfx.fp_payload_proto,
457                                         rpaths[0].frp_fib_index);
458               goto done;
459           }
460           rpaths[0].frp_fib_index = fi;
461       }
462
463       fib_index = mpls_fib_index_from_table_id(table_id);
464
465       if (FIB_NODE_INDEX_INVALID == fib_index)
466       {
467           error = clib_error_return (0, "MPLS table-id %d does not exist",
468                                      table_id);
469           goto done;
470       }
471
472       lfe = fib_table_entry_path_add2(fib_index,
473                                       &pfx,
474                                       FIB_SOURCE_CLI,
475                                       FIB_ENTRY_FLAG_NONE,
476                                       rpaths);
477
478       if (FIB_NODE_INDEX_INVALID == lfe)
479       {
480           error = clib_error_return (0, "Failed to create %U-%U in MPLS table-id %d",
481                                      format_mpls_unicast_label, local_label,
482                                      format_mpls_eos_bit, eos,
483                                      table_id);
484           goto done;
485       }
486   }
487
488 done:
489   unformat_free (line_input);
490
491   return error;
492 }
493
494 VLIB_CLI_COMMAND (mpls_local_label_command, static) = {
495   .path = "mpls local-label",
496   .function = vnet_mpls_local_label,
497   .short_help = "Create/Delete MPL local labels",
498 };
499
500 int
501 mpls_fib_reset_labels (u32 fib_id)
502 {
503   // FIXME
504   return 0;
505 }
506
507 static clib_error_t *
508 mpls_init (vlib_main_t * vm)
509 {
510   mpls_main_t * mm = &mpls_main;
511   clib_error_t * error;
512
513   mm->vlib_main = vm;
514   mm->vnet_main = vnet_get_main();
515
516   if ((error = vlib_call_init_function (vm, ip_main_init)))
517     return error;
518
519   return vlib_call_init_function (vm, mpls_input_init);
520 }
521
522 VLIB_INIT_FUNCTION (mpls_init);