fib: Table Replace
[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   case MPLS_LABEL_POP:
51       s = format (s, "pop");
52       break;
53   default:
54       s = format (s, "%d", label);
55       break;
56   }
57   return s;
58 }
59
60 uword unformat_mpls_unicast_label (unformat_input_t * input, va_list * args)
61 {
62   mpls_label_t *label = va_arg (*args, mpls_label_t*);
63   
64   if (unformat (input, MPLS_IETF_IPV4_EXPLICIT_NULL_STRING))
65       *label = MPLS_IETF_IPV4_EXPLICIT_NULL_LABEL;
66   else if (unformat (input, MPLS_IETF_IPV6_EXPLICIT_NULL_STRING))
67       *label = MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL;
68   else if (unformat (input, MPLS_IETF_ROUTER_ALERT_STRING))
69       *label = MPLS_IETF_ROUTER_ALERT_LABEL;
70   else if (unformat (input, MPLS_IETF_IMPLICIT_NULL_STRING))
71       *label = MPLS_IETF_IMPLICIT_NULL_LABEL;
72   else if (unformat (input, MPLS_IETF_IPV4_EXPLICIT_NULL_BRIEF_STRING))
73       *label = MPLS_IETF_IPV4_EXPLICIT_NULL_LABEL;
74   else if (unformat (input, MPLS_IETF_IPV6_EXPLICIT_NULL_BRIEF_STRING))
75       *label = MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL;
76   else if (unformat (input, MPLS_IETF_ROUTER_ALERT_BRIEF_STRING))
77       *label = MPLS_IETF_ROUTER_ALERT_LABEL;
78   else if (unformat (input, MPLS_IETF_IMPLICIT_NULL_BRIEF_STRING))
79       *label = MPLS_IETF_IMPLICIT_NULL_LABEL;
80   else if (unformat (input, "%d", label))
81       ;
82   else
83     return (0);
84
85   return (1);
86 }
87
88 u8 * format_mpls_eos_bit (u8 * s, va_list * args)
89 {
90   mpls_eos_bit_t eb = va_arg (*args, mpls_eos_bit_t);
91
92   ASSERT(eb <= MPLS_EOS);
93
94   s = format(s, "%s", mpls_eos_bit_names[eb]);
95
96   return (s);
97 }
98
99 u8 * format_mpls_header (u8 * s, va_list * args)
100 {
101   mpls_unicast_header_t hdr = va_arg (*args, mpls_unicast_header_t);
102
103   return (format(s, "[%U:%d:%d:%U]",
104                  format_mpls_unicast_label, 
105                  vnet_mpls_uc_get_label(hdr.label_exp_s_ttl),
106                  vnet_mpls_uc_get_ttl(hdr.label_exp_s_ttl),
107                  vnet_mpls_uc_get_exp(hdr.label_exp_s_ttl),
108                  format_mpls_eos_bit,
109                  vnet_mpls_uc_get_s(hdr.label_exp_s_ttl)));
110 }
111
112 uword
113 unformat_mpls_header (unformat_input_t * input, va_list * args)
114 {
115   u8 ** result = va_arg (*args, u8 **);
116   mpls_unicast_header_t _h, * h = &_h;
117   u32 label, label_exp_s_ttl;
118
119   if (! unformat (input, "MPLS %d", &label))
120     return 0;
121
122   label_exp_s_ttl = (label<<12) | (1<<8) /* s-bit */ | 0xFF;
123   h->label_exp_s_ttl = clib_host_to_net_u32 (label_exp_s_ttl);
124
125   /* Add gre, mpls headers to result. */
126   {
127     void * p;
128     u32 h_n_bytes = sizeof (h[0]);
129
130     vec_add2 (*result, p, h_n_bytes);
131     clib_memcpy (p, h, h_n_bytes);
132   }
133
134   return 1;
135 }
136
137 uword
138 unformat_mpls_label_net_byte_order (unformat_input_t * input,
139                                         va_list * args)
140 {
141   u32 * result = va_arg (*args, u32 *);
142   u32 label;
143
144   if (!unformat (input, "MPLS: label %d", &label))
145     return 0;
146
147   label = (label<<12) | (1<<8) /* s-bit set */ | 0xFF /* ttl */;
148
149   *result = clib_host_to_net_u32 (label);
150   return 1;
151 }
152
153 u8 * format_mpls_unicast_header_host_byte_order (u8 * s, va_list * args)
154 {
155   mpls_unicast_header_t *h = va_arg(*args, mpls_unicast_header_t *);
156   u32 label = h->label_exp_s_ttl;
157   
158   s = format (s, "label %d exp %d, s %d, ttl %d",
159               vnet_mpls_uc_get_label (label),
160               vnet_mpls_uc_get_exp (label),
161               vnet_mpls_uc_get_s (label),
162               vnet_mpls_uc_get_ttl (label));
163   return s;
164 }
165
166 u8 * format_mpls_unicast_header_net_byte_order (u8 * s, va_list * args)
167 {
168   mpls_unicast_header_t *h = va_arg(*args, mpls_unicast_header_t *);
169   mpls_unicast_header_t h_host;
170
171   h_host.label_exp_s_ttl = clib_net_to_host_u32 (h->label_exp_s_ttl);
172
173   return format (s, "%U", format_mpls_unicast_header_host_byte_order,
174                  &h_host);
175 }
176
177 typedef struct {
178   u32 fib_index;
179   u32 entry_index;
180   u32 dest;
181   u32 s_bit;
182   u32 label;
183 } show_mpls_fib_t;
184
185 int
186 mpls_dest_cmp(void * a1, void * a2)
187 {
188   show_mpls_fib_t * r1 = a1;
189   show_mpls_fib_t * r2 = a2;
190
191   return clib_net_to_host_u32(r1->dest) - clib_net_to_host_u32(r2->dest);
192 }
193
194 int
195 mpls_fib_index_cmp(void * a1, void * a2)
196 {
197   show_mpls_fib_t * r1 = a1;
198   show_mpls_fib_t * r2 = a2;
199
200   return r1->fib_index - r2->fib_index;
201 }
202
203 int
204 mpls_label_cmp(void * a1, void * a2)
205 {
206   show_mpls_fib_t * r1 = a1;
207   show_mpls_fib_t * r2 = a2;
208
209   return r1->label - r2->label;
210 }
211
212 static clib_error_t *
213 vnet_mpls_local_label (vlib_main_t * vm,
214                        unformat_input_t * input,
215                        vlib_cli_command_t * cmd)
216 {
217   unformat_input_t _line_input, * line_input = &_line_input;
218   u32 table_id, is_del, is_ip, payload_proto;
219   fib_route_path_t *rpaths = NULL, rpath;
220   mpls_label_t local_label;
221   clib_error_t * error;
222   mpls_eos_bit_t eos;
223   fib_prefix_t pfx;
224
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   clib_memset(&pfx, 0, sizeof(pfx));
232   payload_proto = DPO_PROTO_MPLS;
233
234    /* Get a line of input. */
235   if (! unformat_user (input, unformat_line_input, line_input))
236     return 0;
237
238   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
239     {
240       if (unformat (line_input, "table %d", &table_id))
241         ;
242       else if (unformat (line_input, "del"))
243         is_del = 1;
244       else if (unformat (line_input, "add"))
245         is_del = 0;
246       else if (unformat (line_input, "eos"))
247         pfx.fp_eos = MPLS_EOS;
248       else if (unformat (line_input, "non-eos"))
249         pfx.fp_eos = MPLS_NON_EOS;
250       else if (unformat (line_input, "%U/%d",
251                          unformat_ip4_address,
252                          &pfx.fp_addr.ip4,
253                          &pfx.fp_len))
254       {
255           pfx.fp_proto = FIB_PROTOCOL_IP4;
256           is_ip = 1;
257       }
258       else if (unformat (line_input, "%U/%d",
259                          unformat_ip6_address,
260                          &pfx.fp_addr.ip6,
261                          &pfx.fp_len))
262       {
263           pfx.fp_proto = FIB_PROTOCOL_IP6;
264           is_ip = 1;
265       }
266       else if (unformat (line_input, "via %U",
267                          unformat_fib_route_path,
268                          &rpath, &payload_proto))
269       {
270           pfx.fp_payload_proto = payload_proto;
271           vec_add1(rpaths, rpath);
272       }
273       else if (unformat (line_input, "%d", &local_label))
274         ;
275       else
276       {
277           error = clib_error_return (0, "unknown input: %U",
278                                      format_unformat_error, line_input);
279           goto done;
280       }
281
282     }
283
284   if (MPLS_LABEL_INVALID == local_label)
285   {
286       error = clib_error_return (0, "local-label required: %U",
287                                  format_unformat_error, input);
288       goto done;
289   }
290
291
292   if (is_ip)
293   {
294       u32 fib_index = fib_table_find(pfx.fp_proto, table_id);
295
296       if (FIB_NODE_INDEX_INVALID == fib_index)
297       {
298           error = clib_error_return (0, "%U table-id %d does not exist",
299                                      format_fib_protocol, pfx.fp_proto, table_id);
300           goto done;
301       }
302
303       if (is_del)
304       {
305           fib_table_entry_local_label_remove(fib_index, &pfx, local_label);
306       }
307       else
308       {
309           fib_table_entry_local_label_add(fib_index, &pfx, local_label);
310       }
311   }
312   else
313   {
314       fib_node_index_t fib_index;
315
316       if (NULL == rpaths)
317       {
318           error = clib_error_return(0 , "no paths");
319           goto done;
320       }
321
322       pfx.fp_proto = FIB_PROTOCOL_MPLS;
323       pfx.fp_len = 21;
324       pfx.fp_label = local_label;
325       pfx.fp_payload_proto = rpaths[0].frp_proto;
326
327       fib_index = mpls_fib_index_from_table_id(table_id);
328
329       if (FIB_NODE_INDEX_INVALID == fib_index)
330       {
331           error = clib_error_return (0, "MPLS table-id %d does not exist",
332                                      table_id);
333           goto done;
334       }
335
336       if (is_del)
337       {
338           fib_table_entry_path_remove2(fib_index,
339                                        &pfx,
340                                        FIB_SOURCE_CLI,
341                                        rpaths);
342       }
343       else
344       {
345           fib_node_index_t lfe;
346
347           lfe = fib_table_entry_path_add2(fib_index,
348                                           &pfx,
349                                           FIB_SOURCE_CLI,
350                                           FIB_ENTRY_FLAG_NONE,
351                                           rpaths);
352
353           if (FIB_NODE_INDEX_INVALID == lfe)
354           {
355               error = clib_error_return (0, "Failed to create %U-%U in MPLS table-id %d",
356                                          format_mpls_unicast_label, local_label,
357                                          format_mpls_eos_bit, eos,
358                                          table_id);
359               goto done;
360           }
361       }
362   }
363
364 done:
365   unformat_free (line_input);
366
367   return error;
368 }
369
370 VLIB_CLI_COMMAND (mpls_local_label_command, static) = {
371   .path = "mpls local-label",
372   .function = vnet_mpls_local_label,
373   .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>]",
374 };
375
376 clib_error_t *
377 vnet_mpls_table_cmd (vlib_main_t * vm,
378                      unformat_input_t * main_input,
379                      vlib_cli_command_t * cmdo)
380 {
381   unformat_input_t _line_input, *line_input = &_line_input;
382   clib_error_t *error = NULL;
383   u32 table_id, is_add;
384   u8 *name = NULL;
385
386   is_add = 1;
387   table_id = ~0;
388
389   /* Get a line of input. */
390   if (!unformat_user (main_input, unformat_line_input, line_input))
391     return 0;
392
393   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
394     {
395       if (unformat (line_input, "%d", &table_id))
396         ;
397       else if (unformat (line_input, "del"))
398         is_add = 0;
399       else if (unformat (line_input, "add"))
400         is_add = 1;
401       else if (unformat (line_input, "name %s", &name))
402         ;
403       else
404         {
405           error = unformat_parse_error (line_input);
406           goto done;
407         }
408     }
409
410   if (~0 == table_id)
411     {
412       error = clib_error_return (0, "No table id");
413       goto done;
414     }
415   else
416     {
417       if (is_add)
418         {
419             mpls_table_create (table_id, 0, name);
420         }
421       else
422         {
423           mpls_table_delete (table_id, 0);
424         }
425     }
426
427  done:
428   unformat_free (line_input);
429   return error;
430 }
431
432 /* *INDENT-ON* */
433 /*?
434  * This command is used to add or delete MPLS Tables. All
435  * Tables must be explicitly added before that can be used,
436  * Including the default table.
437  ?*/
438 /* *INDENT-OFF* */
439 VLIB_CLI_COMMAND (mpls_table_command, static) = {
440   .path = "mpls table",
441   .short_help = "mpls table [add|del] <table-id>",
442   .function = vnet_mpls_table_cmd,
443   .is_mp_safe = 1,
444 };
445
446 static clib_error_t *
447 mpls_init (vlib_main_t * vm)
448 {
449   clib_error_t * error;
450
451   if ((error = vlib_call_init_function (vm, ip_main_init)))
452     return error;
453
454   return vlib_call_init_function (vm, mpls_input_init);
455 }
456
457 VLIB_INIT_FUNCTION (mpls_init);