c5ddab73ce467a8ee163ef2d9b7e382fb1f7e3e0
[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       fib_mpls_label_t fml = {
305         .fml_value = steer_pl->vpn_label,
306       };
307       vec_add1 (path.frp_label_stack, fml);
308       path.frp_eos = MPLS_NON_EOS;
309     }
310
311   u32 label_i;
312   vec_foreach_index (label_i, internal_labels)
313   {
314     path.frp_local_label = internal_labels[label_i];
315     path.frp_preference = label_i;
316     vec_add1 (paths, path);
317   }
318
319   /* Finally we must add to FIB IGP to N */
320   clib_memcpy (&path.frp_addr, &steer_pl->next_hop,
321                sizeof (steer_pl->next_hop));
322   path.frp_preference = vec_len (internal_labels);
323   path.frp_label_stack = NULL;
324
325   if (steer_pl->nh_type == SR_STEER_IPV6)
326     {
327       path.frp_proto = DPO_PROTO_IP6;
328       path.frp_fib_index =
329         fib_table_find (FIB_PROTOCOL_IP6,
330                         (steer_pl->classify.fib_table !=
331                          (u32) ~ 0 ? steer_pl->classify.fib_table : 0));
332     }
333   else if (steer_pl->nh_type == SR_STEER_IPV4)
334     {
335       path.frp_proto = DPO_PROTO_IP4;
336       path.frp_fib_index =
337         fib_table_find (FIB_PROTOCOL_IP4,
338                         (steer_pl->classify.fib_table !=
339                          (u32) ~ 0 ? steer_pl->classify.fib_table : 0));
340     }
341
342   vec_add1 (paths, path);
343   if (steer_pl->classify.traffic_type == SR_STEER_IPV6)
344     fib_table_entry_update (fib_table_find
345                             (FIB_PROTOCOL_IP6,
346                              (steer_pl->classify.fib_table !=
347                               (u32) ~ 0 ? steer_pl->classify.fib_table : 0)),
348                             &pfx, FIB_SOURCE_SR,
349                             FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT, paths);
350   else if (steer_pl->classify.traffic_type == SR_STEER_IPV4)
351     fib_table_entry_update (fib_table_find
352                             (FIB_PROTOCOL_IP4,
353                              (steer_pl->classify.fib_table !=
354                               (u32) ~ 0 ? steer_pl->classify.fib_table : 0)),
355                             &pfx, FIB_SOURCE_SR,
356                             FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT, paths);
357
358   vec_free (paths);
359   paths = NULL;
360 }
361
362 /**
363  * @brief Steer traffic L3 traffic through a given SR-MPLS policy
364  *
365  * @param is_del
366  * @param bsid is the bindingSID of the SR Policy (alt to sr_policy_index)
367  * @param sr_policy is the index of the SR Policy (alt to bsid)
368  * @param table_id is the VRF where to install the FIB entry for the BSID
369  * @param prefix is the IPv4/v6 address for L3 traffic type
370  * @param mask_width is the mask for L3 traffic type
371  * @param traffic_type describes the type of traffic
372  * @param next_hop SR TE Next-Hop
373  * @param nh_type is the AF of Next-Hop
374  * @param color SR TE color
375  * @param co_bits SR TE color-only bits
376  *
377  * @return 0 if correct, else error
378  */
379 int
380 sr_mpls_steering_policy_add (mpls_label_t bsid, u32 table_id,
381                              ip46_address_t * prefix, u32 mask_width,
382                              u8 traffic_type, ip46_address_t * next_hop,
383                              u8 nh_type, u32 color, char co_bits,
384                              mpls_label_t vpn_label)
385 {
386   mpls_sr_main_t *sm = &sr_mpls_main;
387   sr_mpls_steering_key_t key;
388   mpls_sr_steering_policy_t *steer_pl;
389   fib_prefix_t pfx = { 0 };
390
391   mpls_sr_policy_t *sr_policy = 0;
392   uword *p = 0;
393
394   memset (&key, 0, sizeof (sr_mpls_steering_key_t));
395
396   if (traffic_type != SR_STEER_IPV4 && traffic_type != SR_STEER_IPV6)
397     return -1;
398
399   /* Compute the steer policy key */
400   key.prefix.as_u64[0] = prefix->as_u64[0];
401   key.prefix.as_u64[1] = prefix->as_u64[1];
402   key.mask_width = mask_width;
403   key.fib_table = (table_id != (u32) ~ 0 ? table_id : 0);
404   key.traffic_type = traffic_type;
405
406   /*
407    * Search for steering policy. If already exists we are adding a new
408    * color.
409    */
410   if (!sm->sr_steer_policies_hash.hash)
411     mhash_init (&sm->sr_steer_policies_hash, sizeof (uword),
412                 sizeof (sr_mpls_steering_key_t));
413
414   p = mhash_get (&sm->sr_steer_policies_hash, &key);
415   if (p)
416     {
417       steer_pl = pool_elt_at_index (sm->steer_policies, p[0]);
418       if (steer_pl->bsid != (u32) ~ 0)
419         return -1;              //Means we are rewritting the steering. Not allowed.
420
421       /* Means we are adding a color. Check that NH match. */
422       if (ip46_address_cmp (&steer_pl->next_hop, next_hop))
423         return -2;
424       if (vec_search (steer_pl->color, color) != ~0)
425         return -3;
426       if (steer_pl->co_bits != co_bits)
427         return -4;              /* CO colors should be the same */
428       if (steer_pl->vpn_label != vpn_label)
429         return -5;              /* VPN label should be the same */
430
431       /* Remove the steering and ReDo it */
432       vec_add1 (steer_pl->color, color);
433       vec_sort_with_function (steer_pl->color, sort_color_descent);
434       compute_sr_te_automated_steering_fib_entry (steer_pl);
435       internal_label_lock_co (steer_pl->next_hop, color, steer_pl->co_bits);
436       return 0;
437     }
438
439   /* Create a new steering policy */
440   pool_get (sm->steer_policies, steer_pl);
441   memset (steer_pl, 0, sizeof (*steer_pl));
442   clib_memcpy (&steer_pl->classify.prefix, prefix, sizeof (ip46_address_t));
443   clib_memcpy (&steer_pl->next_hop, next_hop, sizeof (ip46_address_t));
444   steer_pl->nh_type = nh_type;
445   steer_pl->co_bits = co_bits;
446   steer_pl->classify.mask_width = mask_width;
447   steer_pl->classify.fib_table = (table_id != (u32) ~ 0 ? table_id : 0);
448   steer_pl->classify.traffic_type = traffic_type;
449   steer_pl->color = NULL;
450   steer_pl->vpn_label = vpn_label;
451
452   /* Create and store key */
453   mhash_set (&sm->sr_steer_policies_hash, &key, steer_pl - sm->steer_policies,
454              NULL);
455
456   /* Local steering */
457   if (bsid != (u32) ~ 0)
458     {
459       if (!sm->sr_policies_index_hash)
460         sm->sr_policies_index_hash = hash_create (0, sizeof (mpls_label_t));
461       steer_pl->bsid = bsid;
462       p = hash_get (sm->sr_policies_index_hash, bsid);
463       if (!p)
464         return -1;
465       sr_policy = pool_elt_at_index (sm->sr_policies, p[0]);
466
467       fib_route_path_t path = {
468         .frp_proto = DPO_PROTO_MPLS,
469         .frp_local_label = sr_policy->bsid,
470         .frp_eos = MPLS_EOS,
471         .frp_sw_if_index = ~0,
472         .frp_fib_index = 0,
473         .frp_weight = 1,
474         .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
475         .frp_label_stack = 0
476       };
477       fib_route_path_t *paths = NULL;
478
479       if (steer_pl->vpn_label != (u32) ~ 0)
480         {
481           fib_mpls_label_t fml = {
482             .fml_value = steer_pl->vpn_label,
483           };
484           vec_add1 (path.frp_label_stack, fml);
485         }
486
487       /* FIB API calls - Recursive route through the BindingSID */
488       if (traffic_type == SR_STEER_IPV6)
489         {
490           pfx.fp_proto = FIB_PROTOCOL_IP6;
491           pfx.fp_len = steer_pl->classify.mask_width;
492           pfx.fp_addr.ip6 = steer_pl->classify.prefix.ip6;
493           path.frp_fib_index = 0;
494           path.frp_preference = 0;
495           vec_add1 (paths, path);
496           fib_table_entry_path_add2 (fib_table_find
497                                      (FIB_PROTOCOL_IP6,
498                                       (table_id != (u32) ~ 0 ? table_id : 0)),
499                                      &pfx, FIB_SOURCE_SR,
500                                      FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT, paths);
501           vec_free (paths);
502         }
503       else if (traffic_type == SR_STEER_IPV4)
504         {
505           pfx.fp_proto = FIB_PROTOCOL_IP4;
506           pfx.fp_len = steer_pl->classify.mask_width;
507           pfx.fp_addr.ip4 = steer_pl->classify.prefix.ip4;
508           path.frp_fib_index = 0;
509           path.frp_preference = 0;
510           vec_add1 (paths, path);
511           fib_table_entry_path_add2 (fib_table_find
512                                      (FIB_PROTOCOL_IP4,
513                                       (table_id != (u32) ~ 0 ? table_id : 0)),
514                                      &pfx, FIB_SOURCE_SR,
515                                      FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT, paths);
516           vec_free (paths);
517         }
518     }
519   /* Automated steering */
520   else
521     {
522       steer_pl->bsid = (u32) ~ 0;
523       vec_add1 (steer_pl->color, color);
524       compute_sr_te_automated_steering_fib_entry (steer_pl);
525       internal_label_lock_co (steer_pl->next_hop, color, steer_pl->co_bits);
526     }
527   return 0;
528 }
529
530 /**
531  * @brief Delete steering rule for an SR-MPLS policy
532  *
533  * @param is_del
534  * @param bsid is the bindingSID of the SR Policy (alt to sr_policy_index)
535  * @param sr_policy is the index of the SR Policy (alt to bsid)
536  * @param table_id is the VRF where to install the FIB entry for the BSID
537  * @param prefix is the IPv4/v6 address for L3 traffic type
538  * @param mask_width is the mask for L3 traffic type
539  * @param traffic_type describes the type of traffic
540  * @param next_hop SR TE Next-HOP
541  * @param nh_type is the AF of Next-Hop
542  * @param color SR TE color
543  *
544  * @return 0 if correct, else error
545  */
546 int
547 sr_mpls_steering_policy_del (ip46_address_t * prefix, u32 mask_width,
548                              u8 traffic_type, u32 table_id, u32 color)
549 {
550   mpls_sr_main_t *sm = &sr_mpls_main;
551   sr_mpls_steering_key_t key;
552   mpls_sr_steering_policy_t *steer_pl;
553   fib_prefix_t pfx = { 0 };
554   uword *p = 0;
555
556   memset (&key, 0, sizeof (sr_mpls_steering_key_t));
557
558   /* Compute the steer policy key */
559   if (traffic_type != SR_STEER_IPV4 && traffic_type != SR_STEER_IPV6)
560     return -1;
561
562   key.prefix.as_u64[0] = prefix->as_u64[0];
563   key.prefix.as_u64[1] = prefix->as_u64[1];
564   key.mask_width = mask_width;
565   key.fib_table = (table_id != (u32) ~ 0 ? table_id : 0);
566   key.traffic_type = traffic_type;
567
568   if (!sm->sr_steer_policies_hash.hash)
569     mhash_init (&sm->sr_steer_policies_hash, sizeof (uword),
570                 sizeof (sr_mpls_steering_key_t));
571
572   /* Search for the item */
573   p = mhash_get (&sm->sr_steer_policies_hash, &key);
574
575   if (!p)
576     return -1;
577
578   /* Retrieve Steer Policy function */
579   steer_pl = pool_elt_at_index (sm->steer_policies, p[0]);
580
581   if (steer_pl->bsid == (u32) ~ 0)
582     {
583       /* Remove the color from the color vector */
584       vec_del1 (steer_pl->color, vec_search (steer_pl->color, color));
585
586       if (vec_len (steer_pl->color))
587         {
588           /* Reorder Colors */
589           vec_sort_with_function (steer_pl->color, sort_color_descent);
590           compute_sr_te_automated_steering_fib_entry (steer_pl);
591           /* Remove all the locks for this ones... */
592           internal_label_unlock_co (steer_pl->next_hop, color,
593                                     steer_pl->co_bits);
594           return 0;
595         }
596       else
597         {
598           vec_free (steer_pl->color);
599           /* Remove FIB entry */
600           if (steer_pl->classify.traffic_type == SR_STEER_IPV6)
601             {
602               pfx.fp_proto = FIB_PROTOCOL_IP6;
603               pfx.fp_len = steer_pl->classify.mask_width;
604               pfx.fp_addr.ip6 = steer_pl->classify.prefix.ip6;
605               fib_table_entry_delete (fib_table_find
606                                       (FIB_PROTOCOL_IP6,
607                                        steer_pl->classify.fib_table), &pfx,
608                                       FIB_SOURCE_SR);
609             }
610           else if (steer_pl->classify.traffic_type == SR_STEER_IPV4)
611             {
612               pfx.fp_proto = FIB_PROTOCOL_IP4;
613               pfx.fp_len = steer_pl->classify.mask_width;
614               pfx.fp_addr.ip4 = steer_pl->classify.prefix.ip4;
615               fib_table_entry_delete (fib_table_find
616                                       (FIB_PROTOCOL_IP4,
617                                        steer_pl->classify.fib_table), &pfx,
618                                       FIB_SOURCE_SR);
619             }
620           /* Remove all the locks for this ones... */
621           internal_label_unlock_co (steer_pl->next_hop, color,
622                                     steer_pl->co_bits);
623         }
624     }
625   else                          //Remove by BSID
626     {
627       if (steer_pl->classify.traffic_type == SR_STEER_IPV6)
628         {
629           pfx.fp_proto = FIB_PROTOCOL_IP6;
630           pfx.fp_len = steer_pl->classify.mask_width;
631           pfx.fp_addr.ip6 = steer_pl->classify.prefix.ip6;
632           fib_table_entry_delete (fib_table_find
633                                   (FIB_PROTOCOL_IP6,
634                                    steer_pl->classify.fib_table), &pfx,
635                                   FIB_SOURCE_SR);
636         }
637       else if (steer_pl->classify.traffic_type == SR_STEER_IPV4)
638         {
639           pfx.fp_proto = FIB_PROTOCOL_IP4;
640           pfx.fp_len = steer_pl->classify.mask_width;
641           pfx.fp_addr.ip4 = steer_pl->classify.prefix.ip4;
642           fib_table_entry_delete (fib_table_find
643                                   (FIB_PROTOCOL_IP4,
644                                    steer_pl->classify.fib_table), &pfx,
645                                   FIB_SOURCE_SR);
646         }
647     }
648   /* Delete SR steering policy entry */
649   pool_put (sm->steer_policies, steer_pl);
650   mhash_unset (&sm->sr_steer_policies_hash, &key, NULL);
651   if (mhash_elts (&sm->sr_steer_policies_hash) == 0)
652     {
653       mhash_free (&sm->sr_steer_policies_hash);
654       sm->sr_steer_policies_hash.hash = NULL;
655     }
656   return 0;
657 }
658
659 static clib_error_t *
660 sr_mpls_steer_policy_command_fn (vlib_main_t * vm, unformat_input_t * input,
661                                  vlib_cli_command_t * cmd)
662 {
663   int is_del = 0;
664
665   ip46_address_t prefix, nh;
666   u32 dst_mask_width = 0;
667   u8 traffic_type = 0;
668   u8 nh_type = 0;
669   u32 fib_table = (u32) ~ 0, color = (u32) ~ 0;
670   u32 co_bits = 0;
671
672   mpls_label_t bsid, vpn_label = (u32) ~ 0;
673
674   u8 sr_policy_set = 0;
675
676   memset (&prefix, 0, sizeof (ip46_address_t));
677   memset (&nh, 0, sizeof (ip46_address_t));
678
679   int rv;
680   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
681     {
682       if (unformat (input, "del"))
683         is_del = 1;
684       else if (!traffic_type
685                && unformat (input, "l3 %U/%d", unformat_ip6_address,
686                             &prefix.ip6, &dst_mask_width))
687         traffic_type = SR_STEER_IPV6;
688       else if (!traffic_type
689                && unformat (input, "l3 %U/%d", unformat_ip4_address,
690                             &prefix.ip4, &dst_mask_width))
691         traffic_type = SR_STEER_IPV4;
692       else if (!sr_policy_set
693                && unformat (input, "via sr policy bsid %U",
694                             unformat_mpls_unicast_label, &bsid))
695         sr_policy_set = 1;
696       else if (!sr_policy_set
697                && unformat (input, "via next-hop %U color %d co %d",
698                             unformat_ip4_address, &nh.ip4, &color, &co_bits))
699         {
700           sr_policy_set = 1;
701           nh_type = SR_STEER_IPV4;
702         }
703       else if (!sr_policy_set
704                && unformat (input, "via next-hop %U color %d co %d",
705                             unformat_ip6_address, &nh.ip6, &color, &co_bits))
706         {
707           sr_policy_set = 1;
708           nh_type = SR_STEER_IPV6;
709         }
710       else if (fib_table == (u32) ~ 0
711                && unformat (input, "fib-table %d", &fib_table));
712       else if (unformat (input, "vpn-label %U",
713                          unformat_mpls_unicast_label, &vpn_label));
714       else
715         break;
716     }
717
718   if (!traffic_type)
719     return clib_error_return (0, "No L3 traffic specified");
720   if (!sr_policy_set)
721     return clib_error_return (0, "No SR policy specified");
722
723   /* Make sure that the prefixes are clean */
724   if (traffic_type == SR_STEER_IPV4)
725     {
726       u32 mask =
727         (dst_mask_width ? (0xFFFFFFFFu >> (32 - dst_mask_width)) : 0);
728       prefix.ip4.as_u32 &= mask;
729     }
730   else if (traffic_type == SR_STEER_IPV6)
731     {
732       ip6_address_t mask;
733       ip6_address_mask_from_width (&mask, dst_mask_width);
734       ip6_address_mask (&prefix.ip6, &mask);
735     }
736
737   if (nh_type)
738     bsid = (u32) ~ 0;
739
740   if (is_del)
741     rv =
742       sr_mpls_steering_policy_del (&prefix, dst_mask_width,
743                                    traffic_type, fib_table, color);
744
745   else
746     rv =
747       sr_mpls_steering_policy_add (bsid, fib_table, &prefix, dst_mask_width,
748                                    traffic_type, &nh, nh_type, color, co_bits,
749                                    vpn_label);
750
751   switch (rv)
752     {
753     case 0:
754       break;
755     case 1:
756       return 0;
757     case -1:
758       return clib_error_return (0, "Incorrect API usage.");
759     case -2:
760       return clib_error_return (0, "The Next-Hop does not match.");
761     case -3:
762       return clib_error_return (0, "The color already exists.");
763     case -4:
764       return clib_error_return (0, "The co-bits do not match.");
765     case -5:
766       return clib_error_return (0, "The VPN-labels do not match.");
767     default:
768       return clib_error_return (0, "BUG: sr steer policy returns %d", rv);
769     }
770   return 0;
771 }
772
773 /* *INDENT-OFF* */
774 VLIB_CLI_COMMAND(sr_mpls_steer_policy_command, static)=
775 {
776   .path = "sr mpls steer",
777     .short_help = "sr mpls steer (del) l3 <ip_addr/mask> "
778     "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)",
779     .long_help =
780     "\tSteer L3 traffic through an existing SR policy.\n"
781     "\tExamples:\n"
782     "\t\tsr steer l3 2001::/64 via sr_policy bsid 29999\n"
783     "\t\tsr steer del l3 2001::/64 via sr_policy bsid 29999\n"
784     "\t\tsr steer l3 2001::/64 via next-hop 1.1.1.1 color 1234 co 0\n"
785     "\t\tsr steer l3 2001::/64 via next-hop 2001::1 color 1234 co 2 vpn-label 500\n",
786     .function = sr_mpls_steer_policy_command_fn,
787 };
788 /* *INDENT-ON* */
789
790 static clib_error_t *
791 show_sr_mpls_steering_policies_command_fn (vlib_main_t * vm,
792                                            unformat_input_t * input,
793                                            vlib_cli_command_t * cmd)
794 {
795   mpls_sr_main_t *sm = &sr_mpls_main;
796   mpls_sr_steering_policy_t **steer_policies = 0;
797   mpls_sr_steering_policy_t *steer_pl;
798
799   int i;
800
801   vlib_cli_output (vm, "SR MPLS steering policies:");
802   /* *INDENT-OFF* */
803   pool_foreach(steer_pl, sm->steer_policies, ({
804     vec_add1(steer_policies, steer_pl);
805   }));
806   /* *INDENT-ON* */
807   for (i = 0; i < vec_len (steer_policies); i++)
808     {
809       vlib_cli_output (vm, "==========================");
810       steer_pl = steer_policies[i];
811       if (steer_pl->classify.traffic_type == SR_STEER_IPV4)
812         {
813           vlib_cli_output (vm, "Prefix: %U/%d via:",
814                            format_ip4_address,
815                            &steer_pl->classify.prefix.ip4,
816                            steer_pl->classify.mask_width);
817         }
818       else if (steer_pl->classify.traffic_type == SR_STEER_IPV6)
819         {
820           vlib_cli_output (vm, "Prefix: %U/%d via:",
821                            format_ip6_address,
822                            &steer_pl->classify.prefix.ip6,
823                            steer_pl->classify.mask_width);
824         }
825
826       if (steer_pl->bsid != (u32) ~ 0)
827         {
828           vlib_cli_output (vm, "· BSID %U",
829                            format_mpls_unicast_label, steer_pl->bsid);
830         }
831       else
832         {
833           if (steer_pl->nh_type == SR_STEER_IPV4)
834             {
835               vlib_cli_output (vm, "· Next-hop %U",
836                                format_ip4_address, &steer_pl->next_hop.ip4);
837             }
838           else if (steer_pl->nh_type == SR_STEER_IPV6)
839             {
840               vlib_cli_output (vm, "· Next-hop %U",
841                                format_ip6_address, &steer_pl->next_hop.ip6);
842             }
843
844           u32 *color_i = 0;
845           u8 *s = NULL;
846           s = format (s, "[ ");
847           vec_foreach (color_i, steer_pl->color)
848           {
849             s = format (s, "%d, ", *color_i);
850           }
851           s = format (s, "\b\b ]");
852           vlib_cli_output (vm, "· Color %s", s);
853
854           switch (steer_pl->co_bits)
855             {
856             case SR_TE_CO_BITS_00:
857               vlib_cli_output (vm, "· CO-bits: 00");
858               break;
859             case SR_TE_CO_BITS_01:
860               vlib_cli_output (vm, "· CO-bits: 01");
861               break;
862             case SR_TE_CO_BITS_10:
863               vlib_cli_output (vm, "· CO-bits: 10");
864               break;
865             case SR_TE_CO_BITS_11:
866               vlib_cli_output (vm, "· CO-bits: 11");
867               break;
868             }
869         }
870     }
871   return 0;
872 }
873
874 /* *INDENT-OFF* */
875 VLIB_CLI_COMMAND(show_sr_mpls_steering_policies_command, static)=
876 {
877   .path = "show sr mpls steering policies",
878     .short_help = "show sr mpls steering policies",
879     .function = show_sr_mpls_steering_policies_command_fn,
880 };
881 /* *INDENT-ON* */
882
883 clib_error_t *
884 sr_mpls_steering_init (vlib_main_t * vm)
885 {
886   mpls_sr_main_t *sm = &sr_mpls_main;
887
888   /* Init memory for function keys */
889   sm->sr_steer_policies_hash.hash = NULL;
890
891   sm->fib_table_EC = (u32) ~ 0;
892   sm->ec_labels = 0;
893
894   return 0;
895 }
896
897 /* *INDENT-OFF* */
898 VLIB_INIT_FUNCTION(sr_mpls_steering_init);
899 /* *INDENT-ON* */
900
901 /*
902  * fd.io coding-style-patch-verification: ON
903  *
904  * Local Variables: eval: (c-set-style "gnu") End:
905  */