First commit SR MPLS
[vpp.git] / src / vnet / srmpls / sr_mpls_policy.c
1 /*
2  * sr_mpls_policy.c: SR-MPLS policies
3  *
4  * Copyright (c) 2016 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 /**
19  * @file
20  * @brief SR MPLS policy creation and application
21  *
22  * Create an SR policy.
23  * An SR policy can be either of 'default' type or 'spray' type
24  * An SR policy has attached a list of SID lists.
25  * In case the SR policy is a default one it will load balance among them.
26  * An SR policy has associated a BindingSID.
27  * In case any packet arrives with MPLS_label == BindingSID then the SR policy
28  * associated to such bindingSID will be applied to such packet.
29  *
30  */
31
32 #include <vlib/vlib.h>
33 #include <vnet/vnet.h>
34 #include <vnet/srmpls/sr.h>
35 #include <vnet/fib/mpls_fib.h>
36 #include <vnet/dpo/dpo.h>
37 #include <vnet/dpo/replicate_dpo.h>
38 #include <vnet/dpo/mpls_label_dpo.h>
39 #include <vnet/dpo/lookup_dpo.h>
40
41 #include <vppinfra/error.h>
42 #include <vppinfra/elog.h>
43
44 mpls_sr_main_t sr_mpls_main;
45
46 /***************************  SR LB helper functions **************************/
47 /**
48  * @brief Creates a Segment List and adds it to an SR policy
49  *
50  * Creates a Segment List and adds it to the SR policy. Notice that the SL are
51  * not necessarily unique. Hence there might be two Segment List within the
52  * same SR Policy with exactly the same segments and same weight.
53  *
54  * @param sr_policy is the SR policy where the SL will be added
55  * @param sl is a vector of IPv6 addresses composing the Segment List
56  * @param weight is the weight of the SegmentList (for load-balancing purposes)
57  * @param is_encap represents the mode (SRH insertion vs Encapsulation)
58  *
59  * @return pointer to the just created segment list
60  */
61 static inline mpls_sr_sl_t *
62 create_sl (mpls_sr_policy_t * sr_policy, mpls_label_t * sl, u32 weight)
63 {
64   mpls_sr_main_t *sm = &sr_mpls_main;
65   mpls_sr_sl_t *segment_list;
66
67   pool_get (sm->sid_lists, segment_list);
68   memset (segment_list, 0, sizeof (*segment_list));
69
70   vec_add1 (sr_policy->segments_lists, segment_list - sm->sid_lists);
71
72   /* Fill in segment list */
73   segment_list->weight =
74     (weight != (u32) ~ 0 ? weight : SR_SEGMENT_LIST_WEIGHT_DEFAULT);
75   segment_list->segments = vec_dup (sl);
76
77   fib_route_path_t path = {
78     .frp_proto = FIB_PROTOCOL_MPLS,
79     .frp_sw_if_index = ~0,
80     .frp_fib_index = 0,
81     .frp_weight = segment_list->weight,
82     .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
83     .frp_label_stack = NULL,
84     .frp_local_label = sl[0],
85   };
86
87   vec_add (path.frp_label_stack, sl + 1, vec_len (sl) - 1);
88
89   fib_route_path_t *paths = NULL;
90   vec_add1 (paths, path);
91
92   mpls_eos_bit_t eos;
93   FOR_EACH_MPLS_EOS_BIT (eos)
94   {
95     /* *INDENT-OFF* */
96     fib_prefix_t pfx = {
97       .fp_len = 21,
98       .fp_proto = FIB_PROTOCOL_MPLS,
99       .fp_label = sr_policy->bsid,
100       .fp_eos = eos,
101       .fp_payload_proto = DPO_PROTO_MPLS,
102     };
103     /* *INDENT-ON* */
104
105     fib_table_entry_path_add2 (0,
106                                &pfx,
107                                FIB_SOURCE_SR,
108                                (sr_policy->type == SR_POLICY_TYPE_DEFAULT ?
109                                 FIB_ENTRY_FLAG_NONE :
110                                 FIB_ENTRY_FLAG_MULTICAST), paths);
111   }
112
113   vec_free (paths);
114
115   return segment_list;
116 }
117
118 /******************************* SR rewrite API *******************************/
119 /* Three functions for handling sr policies:
120  *   -> sr_mpls_policy_add
121  *   -> sr_mpls_policy_del
122  *   -> sr_mpls_policy_mod
123  * All of them are API. CLI function on sr_policy_command_fn                  */
124
125 /**
126  * @brief Create a new SR policy
127  *
128  * @param bsid is the bindingSID of the SR Policy
129  * @param segments is a vector of MPLS labels composing the segment list
130  * @param behavior is the behavior of the SR policy. (default//spray)
131  * @param fib_table is the VRF where to install the FIB entry for the BSID
132  * @param weight is the weight of this specific SID list
133  *
134  * @return 0 if correct, else error
135  */
136 int
137 sr_mpls_policy_add (mpls_label_t bsid, mpls_label_t * segments,
138                     u8 behavior, u32 weight)
139 {
140   mpls_sr_main_t *sm = &sr_mpls_main;
141   mpls_sr_policy_t *sr_policy = 0;
142   uword *p;
143
144   /* Search for existing keys (BSID) */
145   p = hash_get (sm->sr_policies_index_hash, bsid);
146   if (p)
147     {
148       /* Add SR policy that already exists; complain */
149       return -12;
150     }
151
152   /* Add an SR policy object */
153   pool_get (sm->sr_policies, sr_policy);
154   memset (sr_policy, 0, sizeof (*sr_policy));
155   sr_policy->bsid = bsid;
156   sr_policy->type = behavior;
157
158   /* Copy the key */
159   hash_set (sm->sr_policies_index_hash, bsid, sr_policy - sm->sr_policies);
160
161   /* Create a segment list and add the index to the SR policy */
162   create_sl (sr_policy, segments, weight);
163
164   return 0;
165 }
166
167 /**
168  * @brief Delete a SR policy
169  *
170  * @param bsid is the bindingSID of the SR Policy
171  * @param index is the index of the SR policy
172  *
173  * @return 0 if correct, else error
174  */
175 int
176 sr_mpls_policy_del (mpls_label_t bsid, u32 index)
177 {
178   mpls_sr_main_t *sm = &sr_mpls_main;
179   mpls_sr_policy_t *sr_policy = 0;
180   mpls_sr_sl_t *segment_list;
181   mpls_eos_bit_t eos;
182   u32 *sl_index;
183   uword *p;
184
185   if (bsid)
186     {
187       p = hash_get (sm->sr_policies_index_hash, bsid);
188       if (p)
189         sr_policy = pool_elt_at_index (sm->sr_policies, p[0]);
190       else
191         return -1;
192     }
193   else
194     {
195       sr_policy = pool_elt_at_index (sm->sr_policies, index);
196       if (!sr_policy)
197         return -1;
198     }
199
200   /* Clean SID Lists */
201   vec_foreach (sl_index, sr_policy->segments_lists)
202   {
203     segment_list = pool_elt_at_index (sm->sid_lists, *sl_index);
204
205     fib_route_path_t path = {
206       .frp_proto = FIB_PROTOCOL_MPLS,
207       .frp_sw_if_index = ~0,
208       .frp_fib_index = 0,
209       .frp_weight = segment_list->weight,
210       .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
211       .frp_local_label = segment_list->segments[0],
212     };
213
214     fib_route_path_t *paths = NULL;
215     vec_add1 (paths, path);
216
217     /* remove each of the MPLS routes */
218     FOR_EACH_MPLS_EOS_BIT (eos)
219     {
220       /* *INDENT-OFF* */
221       fib_prefix_t pfx = {
222         .fp_len = 21,
223         .fp_proto = FIB_PROTOCOL_MPLS,
224         .fp_label = sr_policy->bsid,
225         .fp_eos = eos,
226         .fp_payload_proto = DPO_PROTO_MPLS,
227       };
228       /* *INDENT-ON* */
229
230       fib_table_entry_path_remove2 (0, &pfx, FIB_SOURCE_SR, paths);
231     }
232     vec_free (paths);
233     vec_free (segment_list->segments);
234     pool_put_index (sm->sid_lists, *sl_index);
235   }
236
237   /* Remove SR policy entry */
238   hash_unset (sm->sr_policies_index_hash, sr_policy->bsid);
239   pool_put (sm->sr_policies, sr_policy);
240
241   return 0;
242 }
243
244 /**
245  * @brief Modify an existing SR policy
246  *
247  * The possible modifications are adding a new Segment List, modifying an
248  * existing Segment List (modify the weight only) and delete a given
249  * Segment List from the SR Policy.
250  *
251  * @param bsid is the bindingSID of the SR Policy
252  * @param index is the index of the SR policy
253  * @param fib_table is the VRF where to install the FIB entry for the BSID
254  * @param operation is the operation to perform (among the top ones)
255  * @param segments is a vector of IPv6 address composing the segment list
256  * @param sl_index is the index of the Segment List to modify/delete
257  * @param weight is the weight of the sid list. optional.
258  *
259  * @return 0 if correct, else error
260  */
261 int
262 sr_mpls_policy_mod (mpls_label_t bsid, u32 index, u8 operation,
263                     mpls_label_t * segments, u32 sl_index, u32 weight)
264 {
265   mpls_sr_main_t *sm = &sr_mpls_main;
266   mpls_sr_policy_t *sr_policy = 0;
267   mpls_sr_sl_t *segment_list;
268   u32 *sl_index_iterate;
269   uword *p;
270
271   if (bsid)
272     {
273       p = hash_get (sm->sr_policies_index_hash, bsid);
274       if (p)
275         sr_policy = pool_elt_at_index (sm->sr_policies, p[0]);
276       else
277         return -1;
278     }
279   else
280     {
281       sr_policy = pool_elt_at_index (sm->sr_policies, index);
282       if (!sr_policy)
283         return -1;
284     }
285
286   if (operation == 1)           /* Add SR List to an existing SR policy */
287     {
288       /* Create the new SL */
289       segment_list = create_sl (sr_policy, segments, weight);
290
291     }
292   else if (operation == 2)      /* Delete SR List from an existing SR policy */
293     {
294       /* Check that currently there are more than one SID list */
295       if (vec_len (sr_policy->segments_lists) == 1)
296         return -21;
297
298       /* Check that the SR list does exist and is assigned to the sr policy */
299       vec_foreach (sl_index_iterate, sr_policy->segments_lists)
300         if (*sl_index_iterate == sl_index)
301         break;
302
303       if (*sl_index_iterate != sl_index)
304         return -22;
305
306       /* Remove the lucky SR list that is being kicked out */
307       segment_list = pool_elt_at_index (sm->sid_lists, sl_index);
308
309       mpls_eos_bit_t eos;
310       fib_route_path_t path = {
311         .frp_proto = FIB_PROTOCOL_MPLS,
312         .frp_sw_if_index = ~0,
313         .frp_fib_index = 0,
314         .frp_weight = segment_list->weight,
315         .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
316         .frp_local_label = segment_list->segments[0],
317       };
318
319       fib_route_path_t *paths = NULL;
320       vec_add1 (paths, path);
321
322       FOR_EACH_MPLS_EOS_BIT (eos)
323       {
324         /* *INDENT-OFF* */
325         fib_prefix_t pfx = {
326           .fp_len = 21,
327           .fp_proto = FIB_PROTOCOL_MPLS,
328           .fp_label = sr_policy->bsid,
329           .fp_eos = eos,
330           .fp_payload_proto = DPO_PROTO_MPLS,
331         };
332         /* *INDENT-ON* */
333
334         fib_table_entry_path_remove2 (0, &pfx, FIB_SOURCE_SR, paths);
335       }
336
337       vec_free (paths);
338       vec_free (segment_list->segments);
339       pool_put_index (sm->sid_lists, sl_index);
340       vec_del1 (sr_policy->segments_lists,
341                 sl_index_iterate - sr_policy->segments_lists);
342     }
343   else if (operation == 3)      /* Modify the weight of an existing SR List */
344     {
345       /* Find the corresponding SL */
346       vec_foreach (sl_index_iterate, sr_policy->segments_lists)
347         if (*sl_index_iterate == sl_index)
348         break;
349
350       if (*sl_index_iterate != sl_index)
351         return -32;
352
353       /* Change the weight */
354       segment_list = pool_elt_at_index (sm->sid_lists, sl_index);
355       segment_list->weight = weight;
356
357       /* Update LB */
358       //FIXME
359     }
360   return 0;
361 }
362
363 /**
364  * @brief CLI for 'sr mpls policies' command family
365  */
366 static clib_error_t *
367 sr_mpls_policy_command_fn (vlib_main_t * vm, unformat_input_t * input,
368                            vlib_cli_command_t * cmd)
369 {
370   int rv = -1;
371   char is_del = 0, is_add = 0, is_mod = 0;
372   char policy_set = 0;
373   mpls_label_t bsid, next_label;
374   u32 sr_policy_index = (u32) ~ 0, sl_index = (u32) ~ 0;
375   u32 weight = (u32) ~ 0;
376   mpls_label_t *segments = 0;
377   u8 operation = 0;
378   u8 is_spray = 0;
379
380   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
381     {
382       if (!is_add && !is_mod && !is_del && unformat (input, "add"))
383         is_add = 1;
384       else if (!is_add && !is_mod && !is_del && unformat (input, "del"))
385         is_del = 1;
386       else if (!is_add && !is_mod && !is_del && unformat (input, "mod"))
387         is_mod = 1;
388       else if (!policy_set
389                && unformat (input, "bsid %U", unformat_mpls_unicast_label,
390                             &bsid))
391         policy_set = 1;
392       else if (!is_add && !policy_set
393                && unformat (input, "index %d", &sr_policy_index))
394         policy_set = 1;
395       else if (unformat (input, "weight %d", &weight));
396       else
397         if (unformat
398             (input, "next %U", unformat_mpls_unicast_label, &next_label))
399         {
400           vec_add (segments, &next_label, 1);
401         }
402       else if (unformat (input, "add sl"))
403         operation = 1;
404       else if (unformat (input, "del sl index %d", &sl_index))
405         operation = 2;
406       else if (unformat (input, "mod sl index %d", &sl_index))
407         operation = 3;
408       else if (unformat (input, "spray"))
409         is_spray = 1;
410       else
411         break;
412     }
413
414   if (!is_add && !is_mod && !is_del)
415     return clib_error_return (0, "Incorrect CLI");
416
417   if (!policy_set)
418     return clib_error_return (0, "No SR policy BSID or index specified");
419
420   if (is_add)
421     {
422       if (vec_len (segments) == 0)
423         return clib_error_return (0, "No Segment List specified");
424
425       rv = sr_mpls_policy_add (bsid, segments,
426                                (is_spray ? SR_POLICY_TYPE_SPRAY :
427                                 SR_POLICY_TYPE_DEFAULT), weight);
428     }
429   else if (is_del)
430     rv =
431       sr_mpls_policy_del ((sr_policy_index != (u32) ~ 0 ? (u32) ~ 0 : bsid),
432                           sr_policy_index);
433   else if (is_mod)
434     {
435       if (!operation)
436         return clib_error_return (0, "No SL modification specified");
437       if (operation != 1 && sl_index == (u32) ~ 0)
438         return clib_error_return (0, "No Segment List index specified");
439       if (operation == 1 && vec_len (segments) == 0)
440         return clib_error_return (0, "No Segment List specified");
441       if (operation == 3 && weight == (u32) ~ 0)
442         return clib_error_return (0, "No new weight for the SL specified");
443       rv =
444         sr_mpls_policy_mod ((sr_policy_index != (u32) ~ 0 ? (u32) ~ 0 : bsid),
445                             sr_policy_index, operation, segments,
446                             sl_index, weight);
447     }
448
449   switch (rv)
450     {
451     case 0:
452       break;
453     case 1:
454       return 0;
455     case -12:
456       return clib_error_return (0,
457                                 "There is already a FIB entry for the BindingSID address.\n"
458                                 "The SR policy could not be created.");
459     case -21:
460       return clib_error_return (0,
461                                 "The selected SR policy only contains ONE segment list. "
462                                 "Please remove the SR policy instead");
463     case -22:
464       return clib_error_return (0,
465                                 "Could not delete the segment list. "
466                                 "It is not associated with that SR policy.");
467     case -32:
468       return clib_error_return (0,
469                                 "Could not modify the segment list. "
470                                 "The given SL is not associated with such SR policy.");
471     default:
472       return clib_error_return (0, "BUG: sr policy returns %d", rv);
473     }
474   return 0;
475 }
476
477 /* *INDENT-OFF* */
478 VLIB_CLI_COMMAND (sr_mpls_policy_command, static) = {
479   .path = "sr mpls policy",
480   .short_help = "sr mpls policy [add||del||mod] bsid 2999 "
481   "next 10 next 20 next 30 (weight 1) (spray)",
482   .long_help = "TBD.\n",
483   .function = sr_mpls_policy_command_fn,
484 };
485 /* *INDENT-ON* */
486
487 /**
488  * @brief CLI to display onscreen all the SR MPLS policies
489  */
490 static clib_error_t *
491 show_sr_mpls_policies_command_fn (vlib_main_t * vm, unformat_input_t * input,
492                                   vlib_cli_command_t * cmd)
493 {
494   mpls_sr_main_t *sm = &sr_mpls_main;
495   mpls_sr_sl_t *segment_list = 0;
496   mpls_sr_policy_t *sr_policy = 0;
497   mpls_sr_policy_t **vec_policies = 0;
498   mpls_label_t *label;
499   u32 *sl_index;
500   u8 *s;
501   int i = 0;
502
503   vlib_cli_output (vm, "SR MPLS policies:");
504
505   /* *INDENT-OFF* */
506                 pool_foreach  (sr_policy, sm->sr_policies, {vec_add1 (vec_policies, sr_policy); } );
507   /* *INDENT-ON* */
508
509   vec_foreach_index (i, vec_policies)
510   {
511     sr_policy = vec_policies[i];
512     vlib_cli_output (vm, "[%u].-\tBSID: %U",
513                      (u32) (sr_policy - sm->sr_policies),
514                      format_mpls_unicast_label, sr_policy->bsid);
515     vlib_cli_output (vm, "\tType: %s",
516                      (sr_policy->type ==
517                       SR_POLICY_TYPE_DEFAULT ? "Default" : "Spray"));
518     vlib_cli_output (vm, "\tSegment Lists:");
519     vec_foreach (sl_index, sr_policy->segments_lists)
520     {
521       s = NULL;
522       segment_list = pool_elt_at_index (sm->sid_lists, *sl_index);
523       s = format (s, "\t[%u].- ", *sl_index);
524       s = format (s, "< ");
525       vec_foreach (label, segment_list->segments)
526       {
527         s = format (s, "%U, ", format_mpls_unicast_label, *label);
528       }
529       s = format (s, "\b\b > ");
530       vlib_cli_output (vm, "  %s", s);
531     }
532     vlib_cli_output (vm, "-----------");
533   }
534   vec_free (vec_policies);
535   return 0;
536 }
537
538 /* *INDENT-OFF* */
539 VLIB_CLI_COMMAND (show_sr_mpls_policies_command, static) = {
540   .path = "show sr mpls policies",
541   .short_help = "show sr mpls policies",
542   .function = show_sr_mpls_policies_command_fn,
543 };
544 /* *INDENT-ON* */
545
546 /********************* SR MPLS Policy initialization ***********************/
547 /**
548  * @brief SR MPLS Policy  initialization
549  */
550 clib_error_t *
551 sr_mpls_policy_rewrite_init (vlib_main_t * vm)
552 {
553   mpls_sr_main_t *sm = &sr_mpls_main;
554
555   /* Init memory for sr policy keys (bsid <-> ip6_address_t) */
556   sm->sr_policies_index_hash = hash_create (0, sizeof (mpls_label_t));
557
558   return 0;
559 }
560
561 VLIB_INIT_FUNCTION (sr_mpls_policy_rewrite_init);
562
563 /*
564 * fd.io coding-style-patch-verification: ON
565 *
566 * Local Variables:
567 * eval: (c-set-style "gnu")
568 * End:
569 */