25957fb3fea8f2248648ba76b6f0703066f6c921
[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   u32 table_id, is_del, is_ip, payload_proto;
216   fib_route_path_t *rpaths = NULL, rpath;
217   mpls_label_t local_label;
218   clib_error_t * error;
219   mpls_eos_bit_t eos;
220   fib_prefix_t pfx;
221
222   error = NULL;
223   is_ip = 0;
224   table_id = 0;
225   eos = MPLS_EOS;
226   is_del = 0;
227   local_label = MPLS_LABEL_INVALID;
228   memset(&pfx, 0, sizeof(pfx));
229   payload_proto = DPO_PROTO_MPLS;
230
231    /* Get a line of input. */
232   if (! unformat_user (input, unformat_line_input, line_input))
233     return 0;
234
235   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
236     {
237       if (unformat (line_input, "table %d", &table_id))
238         ;
239       else if (unformat (line_input, "del"))
240         is_del = 1;
241       else if (unformat (line_input, "add"))
242         is_del = 0;
243       else if (unformat (line_input, "eos"))
244         pfx.fp_eos = MPLS_EOS;
245       else if (unformat (line_input, "non-eos"))
246         pfx.fp_eos = MPLS_NON_EOS;
247       else if (unformat (line_input, "%U/%d",
248                          unformat_ip4_address,
249                          &pfx.fp_addr.ip4,
250                          &pfx.fp_len))
251       {
252           pfx.fp_proto = FIB_PROTOCOL_IP4;
253           is_ip = 1;
254       }
255       else if (unformat (line_input, "%U/%d",
256                          unformat_ip6_address,
257                          &pfx.fp_addr.ip6,
258                          &pfx.fp_len))
259       {
260           pfx.fp_proto = FIB_PROTOCOL_IP6;
261           is_ip = 1;
262       }
263       else if (unformat (line_input, "via %U",
264                          unformat_fib_route_path,
265                          &rpath, &payload_proto))
266       {
267           pfx.fp_payload_proto = payload_proto;
268           vec_add1(rpaths, rpath);
269       }
270       else if (unformat (line_input, "%d", &local_label))
271         ;
272       else
273       {
274           error = clib_error_return (0, "unkown input: %U",
275                                      format_unformat_error, line_input);
276           goto done;
277       }
278
279     }
280
281   if (MPLS_LABEL_INVALID == local_label)
282   {
283       error = clib_error_return (0, "local-label required: %U",
284                                  format_unformat_error, input);
285       goto done;
286   }
287
288
289   if (is_ip)
290   {
291       u32 fib_index = fib_table_find(pfx.fp_proto, table_id);
292
293       if (FIB_NODE_INDEX_INVALID == fib_index)
294       {
295           error = clib_error_return (0, "%U table-id %d does not exist",
296                                      format_fib_protocol, pfx.fp_proto, table_id);
297           goto done;
298       }
299
300       if (is_del)
301       {
302           fib_table_entry_local_label_remove(fib_index, &pfx, local_label);
303       }
304       else
305       {
306           fib_table_entry_local_label_add(fib_index, &pfx, local_label);
307       }
308   }
309   else
310   {
311       fib_node_index_t fib_index;
312       u32 fi;
313
314       if (NULL == rpaths)
315       {
316           error = clib_error_return(0 , "no paths");
317           goto done;
318       }
319
320       pfx.fp_proto = FIB_PROTOCOL_MPLS;
321       pfx.fp_len = 21;
322       pfx.fp_label = local_label;
323       pfx.fp_payload_proto = rpaths[0].frp_proto;
324
325       /*
326        * the CLI parsing stored table Ids, swap to FIB indicies
327        */
328       if (FIB_NODE_INDEX_INVALID == rpath.frp_sw_if_index)
329       {
330           fi = fib_table_find(dpo_proto_to_fib(pfx.fp_payload_proto),
331                               rpaths[0].frp_fib_index);
332
333           if (~0 == fi)
334           {
335               error = clib_error_return(0 , "%U Via table %d does not exist",
336                                         format_dpo_proto, pfx.fp_payload_proto,
337                                         rpaths[0].frp_fib_index);
338               goto done;
339           }
340           rpaths[0].frp_fib_index = fi;
341       }
342
343       fib_index = mpls_fib_index_from_table_id(table_id);
344
345       if (FIB_NODE_INDEX_INVALID == fib_index)
346       {
347           error = clib_error_return (0, "MPLS table-id %d does not exist",
348                                      table_id);
349           goto done;
350       }
351
352       if (is_del)
353       {
354           fib_table_entry_path_remove2(fib_index,
355                                        &pfx,
356                                        FIB_SOURCE_CLI,
357                                        rpaths);
358       }
359       else
360       {
361           fib_node_index_t lfe;
362
363           lfe = fib_table_entry_path_add2(fib_index,
364                                           &pfx,
365                                           FIB_SOURCE_CLI,
366                                           FIB_ENTRY_FLAG_NONE,
367                                           rpaths);
368
369           if (FIB_NODE_INDEX_INVALID == lfe)
370           {
371               error = clib_error_return (0, "Failed to create %U-%U in MPLS table-id %d",
372                                          format_mpls_unicast_label, local_label,
373                                          format_mpls_eos_bit, eos,
374                                          table_id);
375               goto done;
376           }
377       }
378   }
379
380 done:
381   unformat_free (line_input);
382
383   return error;
384 }
385
386 VLIB_CLI_COMMAND (mpls_local_label_command, static) = {
387   .path = "mpls local-label",
388   .function = vnet_mpls_local_label,
389   .short_help = "mpls local-label [add|del] <label-value> [eos|non-eos] 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-attached] [rx-ip4 <interface>] [out-labels <value value value>]",
390 };
391
392 clib_error_t *
393 vnet_mpls_table_cmd (vlib_main_t * vm,
394                      unformat_input_t * main_input,
395                      vlib_cli_command_t * cmdo)
396 {
397   unformat_input_t _line_input, *line_input = &_line_input;
398   clib_error_t *error = NULL;
399   u32 table_id, is_add;
400   u8 *name = NULL;
401
402   is_add = 1;
403   table_id = ~0;
404
405   /* Get a line of input. */
406   if (!unformat_user (main_input, unformat_line_input, line_input))
407     return 0;
408
409   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
410     {
411       if (unformat (line_input, "%d", &table_id))
412         ;
413       else if (unformat (line_input, "del"))
414         is_add = 0;
415       else if (unformat (line_input, "add"))
416         is_add = 1;
417       else if (unformat (line_input, "name %s", &name))
418         ;
419       else
420         {
421           error = unformat_parse_error (line_input);
422           goto done;
423         }
424     }
425
426   if (~0 == table_id)
427     {
428       error = clib_error_return (0, "No table id");
429       goto done;
430     }
431   else
432     {
433       if (is_add)
434         {
435             mpls_table_create (table_id, 0, name);
436         }
437       else
438         {
439           mpls_table_delete (table_id, 0);
440         }
441     }
442
443  done:
444   unformat_free (line_input);
445   return error;
446 }
447
448 /* *INDENT-ON* */
449 /*?
450  * This command is used to add or delete MPLS Tables. All
451  * Tables must be explicitly added before that can be used,
452  * Including the default table.
453  ?*/
454 /* *INDENT-OFF* */
455 VLIB_CLI_COMMAND (mpls_table_command, static) = {
456   .path = "mpls table",
457   .short_help = "mpls table [add|del] <table-id>",
458   .function = vnet_mpls_table_cmd,
459   .is_mp_safe = 1,
460 };
461
462 int
463 mpls_fib_reset_labels (u32 fib_id)
464 {
465   // FIXME
466   return 0;
467 }
468
469 static clib_error_t *
470 mpls_init (vlib_main_t * vm)
471 {
472   clib_error_t * error;
473
474   if ((error = vlib_call_init_function (vm, ip_main_init)))
475     return error;
476
477   return vlib_call_init_function (vm, mpls_input_init);
478 }
479
480 VLIB_INIT_FUNCTION (mpls_init);