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