0bd34665c3d10753fcc8607ce34a0a1c48d13661
[vpp.git] / src / vnet / srmpls / sr_mpls_steering.c
1 /*
2  * sr_steering.c: ipv6 segment routing steering into SR policy
3  *
4  * Copyright (c) 2016 Cisco and/or its affiliates. Licensed under the Apache
5  * License, Version 2.0 (the "License"); you may not use this file except in
6  * compliance with the License. You may obtain a copy of the License at:
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16
17 /**
18  * @file
19  * @brief Packet steering into SR-MPLS Policies
20  *
21  * This file is in charge of handling the FIB appropiatly to steer packets
22  * through SR Policies as defined in 'sr_mpls_policy.c'. Notice that here
23  * we are only doing steering. SR policy application is done in
24  * sr_policy_rewrite.c
25  *
26  * Supports:
27  *  - Steering of IPv6 traffic Destination Address based through BSID
28  *  - Steering of IPv4 traffic Destination Address based through BSID
29  *  - Steering of IPv4 and IPv6 traffic through N,C (SR CP)
30  */
31
32 #include <vlib/vlib.h>
33 #include <vnet/vnet.h>
34 #include <vnet/srmpls/sr_mpls.h>
35 #include <vnet/ip/ip4_packet.h>
36 #include <vnet/ip/ip6_packet.h>
37 #include <vnet/fib/mpls_fib.h>
38
39 #include <vppinfra/error.h>
40 #include <vppinfra/elog.h>
41
42 #define SRMPLS_TE_OFFSET 50
43
44 /**
45  * @brief function to sort the colors in descending order
46  */
47 int
48 sort_color_descent (const u32 * x, u32 * y)
49 {
50   return *y - *x;
51 }
52
53 /********************* Internal (NH, C) labels *******************************/
54 /**
55  * @brief find the corresponding label for (endpoint, color) and lock it
56  * endpoint might be NULL or ANY
57  * NULL = 0, ANY=~0
58  */
59 u32
60 find_or_create_internal_label (ip46_address_t endpoint, u32 color)
61 {
62   mpls_sr_main_t *sm = &sr_mpls_main;
63   uword *color_table, *result_label;
64
65   if (!sm->sr_policies_c2e2eclabel_hash.hash)
66     mhash_init (&sm->sr_policies_c2e2eclabel_hash, sizeof (mhash_t),
67                 sizeof (u32));
68
69   color_table = mhash_get (&sm->sr_policies_c2e2eclabel_hash, &color);
70   if (!color_table)
71     {
72       mhash_t color_t;
73       memset (&color_t, 0, sizeof (mhash_t));
74       mhash_init (&color_t, sizeof (u32), sizeof (ip46_address_t));
75       mhash_set_mem (&sm->sr_policies_c2e2eclabel_hash, &color,
76                      (uword *) & color_t, NULL);
77       color_table = mhash_get (&sm->sr_policies_c2e2eclabel_hash, &color);
78     }
79
80   result_label = mhash_get ((mhash_t *) color_table, &endpoint);
81
82   if (result_label)
83     return (u32) * result_label;
84
85   /* Create and set a new internal label */
86   u32 *new_internal_label = 0;
87   pool_get (sm->ec_labels, new_internal_label);
88   *new_internal_label = 0;
89   mhash_set ((mhash_t *) color_table, &endpoint,
90              (new_internal_label - sm->ec_labels) + SRMPLS_TE_OFFSET, NULL);
91
92   return (new_internal_label - sm->ec_labels) + SRMPLS_TE_OFFSET;
93 }
94
95 always_inline void
96 internal_label_lock_co (ip46_address_t endpoint, u32 color, char co_bits)
97 {
98   ip46_address_t zero, any;
99   ip46_address_reset (&zero);
100   any.as_u64[0] = any.as_u64[1] = (u64) ~ 0;
101   switch (co_bits)
102     {
103     case SR_TE_CO_BITS_10:
104       internal_label_lock (endpoint, color);
105       internal_label_lock (zero, color);
106       internal_label_lock (any, color);
107       break;
108     case SR_TE_CO_BITS_01:
109       internal_label_lock (endpoint, color);
110       internal_label_lock (zero, color);
111       break;
112     case SR_TE_CO_BITS_00:
113     case SR_TE_CO_BITS_11:
114       internal_label_lock (endpoint, color);
115       break;
116     }
117 }
118
119 /**
120  * @brief lock the label for (NH, C)
121  * endpoint might be NULL or ANY
122  * NULL = 0, ANY=~0
123  */
124 void
125 internal_label_lock (ip46_address_t endpoint, u32 color)
126 {
127   mpls_sr_main_t *sm = &sr_mpls_main;
128   uword *color_table, *result_label;
129
130   if (!sm->sr_policies_c2e2eclabel_hash.hash)
131     return;
132
133   color_table = mhash_get (&sm->sr_policies_c2e2eclabel_hash, &color);
134   if (!color_table)
135     return;
136
137   result_label = mhash_get ((mhash_t *) color_table, &endpoint);
138
139   if (!result_label)
140     return;
141
142   /* Lock it */
143   u32 *label_lock =
144     pool_elt_at_index (sm->ec_labels, *result_label - SRMPLS_TE_OFFSET);
145   (*label_lock)++;
146 }
147
148
149 always_inline void
150 internal_label_unlock_co (ip46_address_t endpoint, u32 color, char co_bits)
151 {
152   ip46_address_t zero, any;
153   ip46_address_reset (&zero);
154   any.as_u64[0] = any.as_u64[1] = (u64) ~ 0;
155   switch (co_bits)
156     {
157     case SR_TE_CO_BITS_10:
158       internal_label_unlock (endpoint, color);
159       internal_label_unlock (zero, color);
160       internal_label_unlock (any, color);
161       break;
162     case SR_TE_CO_BITS_01:
163       internal_label_unlock (endpoint, color);
164       internal_label_unlock (zero, color);
165       break;
166     case SR_TE_CO_BITS_00:
167     case SR_TE_CO_BITS_11:
168       internal_label_unlock (endpoint, color);
169       break;
170     }
171 }
172
173 /**
174  * @brief Release lock on label for (endpoint, color)
175  * endpoint might be NULL or ANY
176  * NULL = 0, ANY=~0
177  */
178 void
179 internal_label_unlock (ip46_address_t endpoint, u32 color)
180 {
181   mpls_sr_main_t *sm = &sr_mpls_main;
182   uword *color_table, *result_label;
183
184   if (!sm->sr_policies_c2e2eclabel_hash.hash)
185     return;
186
187   color_table = mhash_get (&sm->sr_policies_c2e2eclabel_hash, &color);
188   if (!color_table)
189     return;
190
191   result_label = mhash_get ((mhash_t *) color_table, &endpoint);
192
193   if (!result_label)
194     return;
195
196   u32 *label_lock =
197     pool_elt_at_index (sm->ec_labels, *result_label - SRMPLS_TE_OFFSET);
198   (*label_lock)--;
199
200   if (*label_lock == 0)
201     {
202       pool_put (sm->ec_labels, label_lock);
203       mhash_unset ((mhash_t *) color_table, &endpoint, NULL);
204       if (mhash_elts ((mhash_t *) color_table) == 0)
205         {
206           mhash_free ((mhash_t *) color_table);
207           mhash_unset (&sm->sr_policies_c2e2eclabel_hash, &color, NULL);
208           if (mhash_elts (&sm->sr_policies_c2e2eclabel_hash) == 0)
209             {
210               mhash_free (&sm->sr_policies_c2e2eclabel_hash);
211               sm->sr_policies_c2e2eclabel_hash.hash = NULL;
212               fib_table_unlock (sm->fib_table_EC, FIB_PROTOCOL_MPLS,
213                                 FIB_SOURCE_SR);
214               sm->fib_table_EC = (u32) ~ 0;
215             }
216         }
217     }
218 }
219
220 /********************* steering computation  *********************************/
221 /**
222  * @brief function to update the FIB
223  */
224 void
225 compute_sr_te_automated_steering_fib_entry (mpls_sr_steering_policy_t *
226                                             steer_pl)
227 {
228   mpls_sr_main_t *sm = &sr_mpls_main;
229   fib_prefix_t pfx = { 0 };
230
231   u32 *internal_labels = 0;
232   ip46_address_t zero, any;
233   ip46_address_reset (&zero);
234   any.as_u64[0] = any.as_u64[1] = (u64) ~ 0;
235
236   u32 *color_i = NULL;
237   vec_foreach (color_i, steer_pl->color)
238   {
239     switch (steer_pl->co_bits)
240       {
241       case SR_TE_CO_BITS_10:
242         vec_add1 (internal_labels,
243                   find_or_create_internal_label (steer_pl->next_hop,
244                                                  *color_i));
245         vec_add1 (internal_labels,
246                   find_or_create_internal_label (zero, *color_i));
247         vec_add1 (internal_labels,
248                   find_or_create_internal_label (any, *color_i));
249         break;
250       case SR_TE_CO_BITS_01:
251         vec_add1 (internal_labels,
252                   find_or_create_internal_label (steer_pl->next_hop,
253                                                  *color_i));
254         vec_add1 (internal_labels,
255                   find_or_create_internal_label (zero, *color_i));
256         break;
257       case SR_TE_CO_BITS_00:
258       case SR_TE_CO_BITS_11:
259         vec_add1 (internal_labels,
260                   find_or_create_internal_label (steer_pl->next_hop,
261                                                  *color_i));
262         break;
263       }
264   }
265
266   /* Does hidden FIB already exist? */
267   if (sm->fib_table_EC == (u32) ~ 0)
268     {
269       sm->fib_table_EC = fib_table_create_and_lock (FIB_PROTOCOL_MPLS,
270                                                     FIB_SOURCE_SR,
271                                                     "SR-MPLS Traffic Engineering (NextHop,Color)");
272
273       fib_table_flush (sm->fib_table_EC, FIB_PROTOCOL_MPLS,
274                        FIB_SOURCE_SPECIAL);
275     }
276
277   /* Add the corresponding FIB entries */
278   fib_route_path_t path = {
279     .frp_proto = DPO_PROTO_MPLS,
280     .frp_eos = MPLS_EOS,
281     .frp_sw_if_index = ~0,
282     .frp_fib_index = sm->fib_table_EC,
283     .frp_weight = 1,
284     .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
285     .frp_label_stack = 0
286   };
287   fib_route_path_t *paths = NULL;
288
289   if (steer_pl->classify.traffic_type == SR_STEER_IPV6)
290     {
291       pfx.fp_proto = FIB_PROTOCOL_IP6;
292       pfx.fp_len = steer_pl->classify.mask_width;
293       pfx.fp_addr.ip6 = steer_pl->classify.prefix.ip6;
294     }
295   else if (steer_pl->classify.traffic_type == SR_STEER_IPV4)
296     {
297       pfx.fp_proto = FIB_PROTOCOL_IP4;
298       pfx.fp_len = steer_pl->classify.mask_width;
299       pfx.fp_addr.ip4 = steer_pl->classify.prefix.ip4;
300     }
301
302   if (steer_pl->vpn_label != (u32) ~ 0)
303     {
304       vec_add1 (path.frp_label_stack, steer_pl->vpn_label);
305       path.frp_eos = MPLS_NON_EOS;
306     }
307
308   u32 label_i;
309   vec_foreach_index (label_i, internal_labels)
310   {
311     path.frp_local_label = internal_labels[label_i];
312     path.frp_preference = label_i;
313     vec_add1 (paths, path);
314   }
315
316   /* Finally we must add to FIB IGP to N */
317   clib_memcpy (&path.frp_addr, &steer_pl->next_hop,
318                sizeof (steer_pl->next_hop));
319   path.frp_preference = vec_len (internal_labels);
320   path.frp_label_stack = NULL;
321
322   if (steer_pl->nh_type == SR_STEER_IPV6)
323     {
324       path.frp_proto = DPO_PROTO_IP6;
325       path.frp_fib_index =
326         fib_table_find (FIB_PROTOCOL_IP6,
327                         (steer_pl->classify.fib_table !=
328                          (u32) ~ 0 ? steer_pl->classify.fib_table : 0));
329     }
330   else if (steer_pl->nh_type == SR_STEER_IPV4)
331     {
332       path.frp_proto = DPO_PROTO_IP4;
333       path.frp_fib_index =
334         fib_table_find (FIB_PROTOCOL_IP4,
335                         (steer_pl->classify.fib_table !=
336                          (u32) ~ 0 ? steer_pl->classify.fib_table : 0));
337     }
338
339   vec_add1 (paths, path);
340   if (steer_pl->classify.traffic_type == SR_STEER_IPV6)
341     fib_table_entry_update (fib_table_find
342                             (FIB_PROTOCOL_IP6,
343                              (steer_pl->classify.fib_table !=
344                               (u32) ~ 0 ? steer_pl->classify.fib_table : 0)),
345                             &pfx, FIB_SOURCE_SR,
346                             FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT, paths);
347   else if (steer_pl->classify.traffic_type == SR_STEER_IPV4)
348     fib_table_entry_update (fib_table_find
349                             (FIB_PROTOCOL_IP4,
350                              (steer_pl->classify.fib_table !=
351                               (u32) ~ 0 ? steer_pl->classify.fib_table : 0)),
352                             &pfx, FIB_SOURCE_SR,
353                             FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT, paths);
354
355   vec_free (paths);
356   paths = NULL;
357 }
358
359 /**
360  * @brief Steer traffic L3 traffic through a given SR-MPLS policy
361  *
362  * @param is_del
363  * @param bsid is the bindingSID of the SR Policy (alt to sr_policy_index)
364  * @param sr_policy is the index of the SR Policy (alt to bsid)
365  * @param table_id is the VRF where to install the FIB entry for the BSID
366  * @param prefix is the IPv4/v6 address for L3 traffic type
367  * @param mask_width is the mask for L3 traffic type
368  * @param traffic_type describes the type of traffic
369  * @param next_hop SR TE Next-Hop
370  * @param nh_type is the AF of Next-Hop
371  * @param color SR TE color
372  * @param co_bits SR TE color-only bits
373  *
374  * @return 0 if correct, else error
375  */
376 int
377 sr_mpls_steering_policy_add (mpls_label_t bsid, u32 table_id,
378                              ip46_address_t * prefix, u32 mask_width,
379                              u8 traffic_type, ip46_address_t * next_hop,
380                              u8 nh_type, u32 color, char co_bits,
381                              mpls_label_t vpn_label)
382 {
383   mpls_sr_main_t *sm = &sr_mpls_main;
384   sr_mpls_steering_key_t key;
385   mpls_sr_steering_policy_t *steer_pl;
386   fib_prefix_t pfx = { 0 };
387
388   mpls_sr_policy_t *sr_policy = 0;
389   uword *p = 0;
390
391   memset (&key, 0, sizeof (sr_mpls_steering_key_t));
392
393   /* Compute the steer policy key */
394   if (traffic_type == SR_STEER_IPV4 || traffic_type == SR_STEER_IPV6)
395     {
396       key.prefix.as_u64[0] = prefix->as_u64[0];
397       key.prefix.as_u64[1] = prefix->as_u64[1];
398       key.mask_width = mask_width;
399       key.fib_table = (table_id != (u32) ~ 0 ? table_id : 0);
400     }
401   else
402     return -1;
403
404   key.traffic_type = traffic_type;
405
406   if (traffic_type != SR_STEER_IPV4 && traffic_type != SR_STEER_IPV6)
407     return -1;
408
409   /*
410    * Search for steering policy. If already exists we are adding a new
411    * color.
412    */
413   if (!sm->sr_steer_policies_hash.hash)
414     mhash_init (&sm->sr_steer_policies_hash, sizeof (uword),
415                 sizeof (sr_mpls_steering_key_t));
416
417   p = mhash_get (&sm->sr_steer_policies_hash, &key);
418   if (p)
419     {
420       steer_pl = pool_elt_at_index (sm->steer_policies, p[0]);
421       if (steer_pl->bsid != (u32) ~ 0)
422         return -1;              //Means we are rewritting the steering. Not allowed.
423
424       /* Means we are adding a color. Check that NH match. */
425       if (ip46_address_cmp (&steer_pl->next_hop, next_hop))
426         return -2;
427       if (vec_search (steer_pl->color, color) != ~0)
428         return -3;
429       if (steer_pl->co_bits != co_bits)
430         return -4;              /* CO colors should be the same */
431       if (steer_pl->vpn_label != vpn_label)
432         return -5;              /* VPN label should be the same */
433
434       /* Remove the steering and ReDo it */
435       vec_add1 (steer_pl->color, color);
436       vec_sort_with_function (steer_pl->color, sort_color_descent);
437       compute_sr_te_automated_steering_fib_entry (steer_pl);
438       internal_label_lock_co (steer_pl->next_hop, color, steer_pl->co_bits);
439       return 0;
440     }
441
442   /* Create a new steering policy */
443   pool_get (sm->steer_policies, steer_pl);
444   memset (steer_pl, 0, sizeof (*steer_pl));
445   clib_memcpy (&steer_pl->classify.prefix, prefix, sizeof (ip46_address_t));
446   clib_memcpy (&steer_pl->next_hop, next_hop, sizeof (ip46_address_t));
447   steer_pl->nh_type = nh_type;
448   steer_pl->co_bits = co_bits;
449   steer_pl->classify.mask_width = mask_width;
450   steer_pl->classify.fib_table = (table_id != (u32) ~ 0 ? table_id : 0);
451   steer_pl->classify.traffic_type = traffic_type;
452   steer_pl->color = NULL;
453   steer_pl->vpn_label = vpn_label;
454
455   /* Create and store key */
456   mhash_set (&sm->sr_steer_policies_hash, &key, steer_pl - sm->steer_policies,
457              NULL);
458
459   /* Local steering */
460   if (bsid != (u32) ~ 0)
461     {
462       if (!sm->sr_policies_index_hash)
463         sm->sr_policies_index_hash = hash_create (0, sizeof (mpls_label_t));
464       steer_pl->bsid = bsid;
465       p = hash_get (sm->sr_policies_index_hash, bsid);
466       if (!p)
467         return -1;
468       sr_policy = pool_elt_at_index (sm->sr_policies, p[0]);
469
470       fib_route_path_t path = {
471         .frp_proto = DPO_PROTO_MPLS,
472         .frp_local_label = sr_policy->bsid,
473         .frp_eos = MPLS_EOS,
474         .frp_sw_if_index = ~0,
475         .frp_fib_index = 0,
476         .frp_weight = 1,
477         .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
478         .frp_label_stack = 0
479       };
480       fib_route_path_t *paths = NULL;
481
482       if (steer_pl->vpn_label != (u32) ~ 0)
483         vec_add1 (path.frp_label_stack, steer_pl->vpn_label);
484
485       /* FIB API calls - Recursive route through the BindingSID */
486       if (traffic_type == SR_STEER_IPV6)
487         {
488           pfx.fp_proto = FIB_PROTOCOL_IP6;
489           pfx.fp_len = steer_pl->classify.mask_width;
490           pfx.fp_addr.ip6 = steer_pl->classify.prefix.ip6;
491           path.frp_fib_index = 0;
492           path.frp_preference = 0;
493           vec_add1 (paths, path);
494           fib_table_entry_path_add2 (fib_table_find
495                                      (FIB_PROTOCOL_IP6,
496                                       (table_id != (u32) ~ 0 ? table_id : 0)),
497                                      &pfx, FIB_SOURCE_SR,
498                                      FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT, paths);
499           vec_free (paths);
500         }
501       else if (traffic_type == SR_STEER_IPV4)
502         {
503           pfx.fp_proto = FIB_PROTOCOL_IP4;
504           pfx.fp_len = steer_pl->classify.mask_width;
505           pfx.fp_addr.ip4 = steer_pl->classify.prefix.ip4;
506           path.frp_fib_index = 0;
507           path.frp_preference = 0;
508           vec_add1 (paths, path);
509           fib_table_entry_path_add2 (fib_table_find
510                                      (FIB_PROTOCOL_IP4,
511                                       (table_id != (u32) ~ 0 ? table_id : 0)),
512                                      &pfx, FIB_SOURCE_SR,
513                                      FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT, paths);
514           vec_free (paths);
515         }
516     }
517   /* Automated steering */
518   else
519     {
520       steer_pl->bsid = (u32) ~ 0;
521       vec_add1 (steer_pl->color, color);
522       compute_sr_te_automated_steering_fib_entry (steer_pl);
523       internal_label_lock_co (steer_pl->next_hop, color, steer_pl->co_bits);
524     }
525   return 0;
526 }
527
528 /**
529  * @brief Delete steering rule for an SR-MPLS policy
530  *
531  * @param is_del
532  * @param bsid is the bindingSID of the SR Policy (alt to sr_policy_index)
533  * @param sr_policy is the index of the SR Policy (alt to bsid)
534  * @param table_id is the VRF where to install the FIB entry for the BSID
535  * @param prefix is the IPv4/v6 address for L3 traffic type
536  * @param mask_width is the mask for L3 traffic type
537  * @param traffic_type describes the type of traffic
538  * @param next_hop SR TE Next-HOP
539  * @param nh_type is the AF of Next-Hop
540  * @param color SR TE color
541  *
542  * @return 0 if correct, else error
543  */
544 int
545 sr_mpls_steering_policy_del (ip46_address_t * prefix, u32 mask_width,
546                              u8 traffic_type, u32 table_id, u32 color)
547 {
548   mpls_sr_main_t *sm = &sr_mpls_main;
549   sr_mpls_steering_key_t key;
550   mpls_sr_steering_policy_t *steer_pl;
551   fib_prefix_t pfx = { 0 };
552   uword *p = 0;
553
554   memset (&key, 0, sizeof (sr_mpls_steering_key_t));
555
556   /* Compute the steer policy key */
557   if (traffic_type != SR_STEER_IPV4 && traffic_type != SR_STEER_IPV6)
558     return -1;
559
560   key.prefix.as_u64[0] = prefix->as_u64[0];
561   key.prefix.as_u64[1] = prefix->as_u64[1];
562   key.mask_width = mask_width;
563   key.fib_table = (table_id != (u32) ~ 0 ? table_id : 0);
564   key.traffic_type = traffic_type;
565
566   if (!sm->sr_steer_policies_hash.hash)
567     mhash_init (&sm->sr_steer_policies_hash, sizeof (uword),
568                 sizeof (sr_mpls_steering_key_t));
569
570   /* Search for the item */
571   p = mhash_get (&sm->sr_steer_policies_hash, &key);
572
573   if (!p)
574     return -1;
575
576   /* Retrieve Steer Policy function */
577   steer_pl = pool_elt_at_index (sm->steer_policies, p[0]);
578
579   if (steer_pl->bsid == (u32) ~ 0)
580     {
581       /* Remove the color from the color vector */
582       vec_del1 (steer_pl->color, vec_search (steer_pl->color, color));
583
584       if (vec_len (steer_pl->color))
585         {
586           /* Reorder Colors */
587           vec_sort_with_function (steer_pl->color, sort_color_descent);
588           compute_sr_te_automated_steering_fib_entry (steer_pl);
589           /* Remove all the locks for this ones... */
590           internal_label_unlock_co (steer_pl->next_hop, color,
591                                     steer_pl->co_bits);
592           return 0;
593         }
594       else
595         {
596           vec_free (steer_pl->color);
597           /* Remove FIB entry */
598           if (steer_pl->classify.traffic_type == SR_STEER_IPV6)
599             {
600               pfx.fp_proto = FIB_PROTOCOL_IP6;
601               pfx.fp_len = steer_pl->classify.mask_width;
602               pfx.fp_addr.ip6 = steer_pl->classify.prefix.ip6;
603               fib_table_entry_delete (fib_table_find
604                                       (FIB_PROTOCOL_IP6,
605                                        steer_pl->classify.fib_table), &pfx,
606                                       FIB_SOURCE_SR);
607             }
608           else if (steer_pl->classify.traffic_type == SR_STEER_IPV4)
609             {
610               pfx.fp_proto = FIB_PROTOCOL_IP4;
611               pfx.fp_len = steer_pl->classify.mask_width;
612               pfx.fp_addr.ip4 = steer_pl->classify.prefix.ip4;
613               fib_table_entry_delete (fib_table_find
614                                       (FIB_PROTOCOL_IP4,
615                                        steer_pl->classify.fib_table), &pfx,
616                                       FIB_SOURCE_SR);
617             }
618           /* Remove all the locks for this ones... */
619           internal_label_unlock_co (steer_pl->next_hop, color,
620                                     steer_pl->co_bits);
621         }
622     }
623   else                          //Remove by BSID
624     {
625       if (steer_pl->classify.traffic_type == SR_STEER_IPV6)
626         {
627           pfx.fp_proto = FIB_PROTOCOL_IP6;
628           pfx.fp_len = steer_pl->classify.mask_width;
629           pfx.fp_addr.ip6 = steer_pl->classify.prefix.ip6;
630           fib_table_entry_delete (fib_table_find
631                                   (FIB_PROTOCOL_IP6,
632                                    steer_pl->classify.fib_table), &pfx,
633                                   FIB_SOURCE_SR);
634         }
635       else if (steer_pl->classify.traffic_type == SR_STEER_IPV4)
636         {
637           pfx.fp_proto = FIB_PROTOCOL_IP4;
638           pfx.fp_len = steer_pl->classify.mask_width;
639           pfx.fp_addr.ip4 = steer_pl->classify.prefix.ip4;
640           fib_table_entry_delete (fib_table_find
641                                   (FIB_PROTOCOL_IP4,
642                                    steer_pl->classify.fib_table), &pfx,
643                                   FIB_SOURCE_SR);
644         }
645     }
646   /* Delete SR steering policy entry */
647   pool_put (sm->steer_policies, steer_pl);
648   mhash_unset (&sm->sr_steer_policies_hash, &key, NULL);
649   if (mhash_elts (&sm->sr_steer_policies_hash) == 0)
650     {
651       mhash_free (&sm->sr_steer_policies_hash);
652       sm->sr_steer_policies_hash.hash = NULL;
653     }
654   return 0;
655 }
656
657 static clib_error_t *
658 sr_mpls_steer_policy_command_fn (vlib_main_t * vm, unformat_input_t * input,
659                                  vlib_cli_command_t * cmd)
660 {
661   int is_del = 0;
662
663   ip46_address_t prefix, nh;
664   u32 dst_mask_width = 0;
665   u8 traffic_type = 0;
666   u8 nh_type = 0;
667   u32 fib_table = (u32) ~ 0, color = (u32) ~ 0;
668   u32 co_bits = 0;
669
670   mpls_label_t bsid, vpn_label = (u32) ~ 0;
671
672   u8 sr_policy_set = 0;
673
674   memset (&prefix, 0, sizeof (ip46_address_t));
675   memset (&nh, 0, sizeof (ip46_address_t));
676
677   int rv;
678   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
679     {
680       if (unformat (input, "del"))
681         is_del = 1;
682       else if (!traffic_type
683                && unformat (input, "l3 %U/%d", unformat_ip6_address,
684                             &prefix.ip6, &dst_mask_width))
685         traffic_type = SR_STEER_IPV6;
686       else if (!traffic_type
687                && unformat (input, "l3 %U/%d", unformat_ip4_address,
688                             &prefix.ip4, &dst_mask_width))
689         traffic_type = SR_STEER_IPV4;
690       else if (!sr_policy_set
691                && unformat (input, "via sr policy bsid %U",
692                             unformat_mpls_unicast_label, &bsid))
693         sr_policy_set = 1;
694       else if (!sr_policy_set
695                && unformat (input, "via next-hop %U color %d co %d",
696                             unformat_ip4_address, &nh.ip4, &color, &co_bits))
697         {
698           sr_policy_set = 1;
699           nh_type = SR_STEER_IPV4;
700         }
701       else if (!sr_policy_set
702                && unformat (input, "via next-hop %U color %d co %d",
703                             unformat_ip6_address, &nh.ip6, &color, &co_bits))
704         {
705           sr_policy_set = 1;
706           nh_type = SR_STEER_IPV6;
707         }
708       else if (fib_table == (u32) ~ 0
709                && unformat (input, "fib-table %d", &fib_table));
710       else if (unformat (input, "vpn-label %U",
711                          unformat_mpls_unicast_label, &vpn_label));
712       else
713         break;
714     }
715
716   if (!traffic_type)
717     return clib_error_return (0, "No L3 traffic specified");
718   if (!sr_policy_set)
719     return clib_error_return (0, "No SR policy specified");
720
721   /* Make sure that the prefixes are clean */
722   if (traffic_type == SR_STEER_IPV4)
723     {
724       u32 mask =
725         (dst_mask_width ? (0xFFFFFFFFu >> (32 - dst_mask_width)) : 0);
726       prefix.ip4.as_u32 &= mask;
727     }
728   else if (traffic_type == SR_STEER_IPV6)
729     {
730       ip6_address_t mask;
731       ip6_address_mask_from_width (&mask, dst_mask_width);
732       ip6_address_mask (&prefix.ip6, &mask);
733     }
734
735   if (nh_type)
736     bsid = (u32) ~ 0;
737
738   if (is_del)
739     rv =
740       sr_mpls_steering_policy_del (&prefix, dst_mask_width,
741                                    traffic_type, fib_table, color);
742
743   else
744     rv =
745       sr_mpls_steering_policy_add (bsid, fib_table, &prefix, dst_mask_width,
746                                    traffic_type, &nh, nh_type, color, co_bits,
747                                    vpn_label);
748
749   switch (rv)
750     {
751     case 0:
752       break;
753     case 1:
754       return 0;
755     case -1:
756       return clib_error_return (0, "Incorrect API usage.");
757     case -2:
758       return clib_error_return (0, "The Next-Hop does not match.");
759     case -3:
760       return clib_error_return (0, "The color already exists.");
761     case -4:
762       return clib_error_return (0, "The co-bits do not match.");
763     case -5:
764       return clib_error_return (0, "The VPN-labels do not match.");
765     default:
766       return clib_error_return (0, "BUG: sr steer policy returns %d", rv);
767     }
768   return 0;
769 }
770
771 /* *INDENT-OFF* */
772 VLIB_CLI_COMMAND(sr_mpls_steer_policy_command, static)=
773 {
774   .path = "sr mpls steer",
775     .short_help = "sr mpls steer (del) l3 <ip_addr/mask> "
776     "via [sr policy bsid <mpls_label> || next-hop <ip46_addr> color <u32> co <0|1|2|3> ](fib-table <fib_table_index>)(vpn-label 500)",
777     .long_help =
778     "\tSteer L3 traffic through an existing SR policy.\n"
779     "\tExamples:\n"
780     "\t\tsr steer l3 2001::/64 via sr_policy bsid 29999\n"
781     "\t\tsr steer del l3 2001::/64 via sr_policy bsid 29999\n"
782     "\t\tsr steer l3 2001::/64 via next-hop 1.1.1.1 color 1234 co 0\n"
783     "\t\tsr steer l3 2001::/64 via next-hop 2001::1 color 1234 co 2 vpn-label 500\n",
784     .function = sr_mpls_steer_policy_command_fn,
785 };
786 /* *INDENT-ON* */
787
788 static clib_error_t *
789 show_sr_mpls_steering_policies_command_fn (vlib_main_t * vm,
790                                            unformat_input_t * input,
791                                            vlib_cli_command_t * cmd)
792 {
793   mpls_sr_main_t *sm = &sr_mpls_main;
794   mpls_sr_steering_policy_t **steer_policies = 0;
795   mpls_sr_steering_policy_t *steer_pl;
796
797   int i;
798
799   vlib_cli_output (vm, "SR MPLS steering policies:");
800   /* *INDENT-OFF* */
801   pool_foreach(steer_pl, sm->steer_policies, ({
802     vec_add1(steer_policies, steer_pl);
803   }));
804   /* *INDENT-ON* */
805   for (i = 0; i < vec_len (steer_policies); i++)
806     {
807       vlib_cli_output (vm, "==========================");
808       steer_pl = steer_policies[i];
809       if (steer_pl->classify.traffic_type == SR_STEER_IPV4)
810         {
811           vlib_cli_output (vm, "Prefix: %U/%d via:",
812                            format_ip4_address,
813                            &steer_pl->classify.prefix.ip4,
814                            steer_pl->classify.mask_width);
815         }
816       else if (steer_pl->classify.traffic_type == SR_STEER_IPV6)
817         {
818           vlib_cli_output (vm, "Prefix: %U/%d via:",
819                            format_ip6_address,
820                            &steer_pl->classify.prefix.ip6,
821                            steer_pl->classify.mask_width);
822         }
823
824       if (steer_pl->bsid != (u32) ~ 0)
825         {
826           vlib_cli_output (vm, "· BSID %U",
827                            format_mpls_unicast_label, steer_pl->bsid);
828         }
829       else
830         {
831           if (steer_pl->nh_type == SR_STEER_IPV4)
832             {
833               vlib_cli_output (vm, "· Next-hop %U",
834                                format_ip4_address, &steer_pl->next_hop.ip4);
835             }
836           else if (steer_pl->nh_type == SR_STEER_IPV6)
837             {
838               vlib_cli_output (vm, "· Next-hop %U",
839                                format_ip6_address, &steer_pl->next_hop.ip6);
840             }
841
842           u32 *color_i = 0;
843           u8 *s = NULL;
844           s = format (s, "[ ");
845           vec_foreach (color_i, steer_pl->color)
846           {
847             s = format (s, "%d, ", *color_i);
848           }
849           s = format (s, "\b\b ]");
850           vlib_cli_output (vm, "· Color %s", s);
851
852           switch (steer_pl->co_bits)
853             {
854             case SR_TE_CO_BITS_00:
855               vlib_cli_output (vm, "· CO-bits: 00");
856               break;
857             case SR_TE_CO_BITS_01:
858               vlib_cli_output (vm, "· CO-bits: 01");
859               break;
860             case SR_TE_CO_BITS_10:
861               vlib_cli_output (vm, "· CO-bits: 10");
862               break;
863             case SR_TE_CO_BITS_11:
864               vlib_cli_output (vm, "· CO-bits: 11");
865               break;
866             }
867         }
868     }
869   return 0;
870 }
871
872 /* *INDENT-OFF* */
873 VLIB_CLI_COMMAND(show_sr_mpls_steering_policies_command, static)=
874 {
875   .path = "show sr mpls steering policies",
876     .short_help = "show sr mpls steering policies",
877     .function = show_sr_mpls_steering_policies_command_fn,
878 };
879 /* *INDENT-ON* */
880
881 clib_error_t *
882 sr_mpls_steering_init (vlib_main_t * vm)
883 {
884   mpls_sr_main_t *sm = &sr_mpls_main;
885
886   /* Init memory for function keys */
887   sm->sr_steer_policies_hash.hash = NULL;
888
889   sm->fib_table_EC = (u32) ~ 0;
890   sm->ec_labels = 0;
891
892   return 0;
893 }
894
895 /* *INDENT-OFF* */
896 VLIB_INIT_FUNCTION(sr_mpls_steering_init);
897 /* *INDENT-ON* */
898
899 /*
900  * fd.io coding-style-patch-verification: ON
901  *
902  * Local Variables: eval: (c-set-style "gnu") End:
903  */