First commit SR MPLS
[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.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 /**
19  * @file
20  * @brief Packet steering into SR-MPLS Policies
21  *
22  * This file is in charge of handling the FIB appropiatly to steer packets
23  * through SR Policies as defined in 'sr_mpls_policy.c'. Notice that here
24  * we are only doing steering. SR policy application is done in
25  * sr_policy_rewrite.c
26  *
27  * Supports:
28  *  - Steering of IPv6 traffic Destination Address based
29  *  - Steering of IPv4 traffic Destination Address based
30  */
31
32 #include <vlib/vlib.h>
33 #include <vnet/vnet.h>
34 #include <vnet/srmpls/sr.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 /**
43  * @brief Steer traffic L3 traffic through a given SR-MPLS policy
44  *
45  * @param is_del
46  * @param bsid is the bindingSID of the SR Policy (alt to sr_policy_index)
47  * @param sr_policy is the index of the SR Policy (alt to bsid)
48  * @param table_id is the VRF where to install the FIB entry for the BSID
49  * @param prefix is the IPv4/v6 address for L3 traffic type
50  * @param mask_width is the mask for L3 traffic type
51  * @param traffic_type describes the type of traffic
52  *
53  * @return 0 if correct, else error
54  */
55 int
56 sr_mpls_steering_policy (int is_del, mpls_label_t bsid, u32 sr_policy_index,
57                          u32 table_id, ip46_address_t * prefix,
58                          u32 mask_width, u8 traffic_type)
59 {
60   mpls_sr_main_t *sm = &sr_mpls_main;
61   sr_mpls_steering_key_t key;
62   mpls_sr_steering_policy_t *steer_pl;
63   fib_prefix_t pfx = { 0 };
64
65   mpls_sr_policy_t *sr_policy = 0;
66   uword *p = 0;
67
68   memset (&key, 0, sizeof (sr_mpls_steering_key_t));
69
70   /* Compute the steer policy key */
71   if (traffic_type == SR_STEER_IPV4 || traffic_type == SR_STEER_IPV6)
72     {
73       key.prefix.as_u64[0] = prefix->as_u64[0];
74       key.prefix.as_u64[1] = prefix->as_u64[1];
75       key.mask_width = mask_width;
76       key.fib_table = (table_id != (u32) ~ 0 ? table_id : 0);
77     }
78   else
79     return -1;
80
81   key.traffic_type = traffic_type;
82
83   /* Search for the item */
84   p = mhash_get (&sm->sr_steer_policies_hash, &key);
85
86   if (p)
87     {
88       /* Retrieve Steer Policy function */
89       steer_pl = pool_elt_at_index (sm->steer_policies, p[0]);
90
91       if (is_del)
92         {
93           if (steer_pl->classify.traffic_type == SR_STEER_IPV6)
94             {
95               /* Remove FIB entry */
96               pfx.fp_proto = FIB_PROTOCOL_IP6;
97               pfx.fp_len = steer_pl->classify.mask_width;
98               pfx.fp_addr.ip6 = steer_pl->classify.prefix.ip6;
99
100               fib_table_entry_delete (fib_table_find
101                                       (FIB_PROTOCOL_MPLS,
102                                        steer_pl->classify.fib_table), &pfx,
103                                       FIB_SOURCE_SR);
104             }
105           else if (steer_pl->classify.traffic_type == SR_STEER_IPV4)
106             {
107               /* Remove FIB entry */
108               pfx.fp_proto = FIB_PROTOCOL_IP4;
109               pfx.fp_len = steer_pl->classify.mask_width;
110               pfx.fp_addr.ip4 = steer_pl->classify.prefix.ip4;
111
112               fib_table_entry_delete (fib_table_find
113                                       (FIB_PROTOCOL_MPLS,
114                                        steer_pl->classify.fib_table), &pfx,
115                                       FIB_SOURCE_SR);
116             }
117
118           /* Delete SR steering policy entry */
119           pool_put (sm->steer_policies, steer_pl);
120           mhash_unset (&sm->sr_steer_policies_hash, &key, NULL);
121
122           return 1;
123         }
124       else                      /* It means user requested to update an existing SR steering policy */
125         {
126           /* Retrieve SR steering policy */
127           if (bsid)             //TODO FIXME
128             {
129               p = hash_get (sm->sr_policies_index_hash, bsid);
130               if (p)
131                 sr_policy = pool_elt_at_index (sm->sr_policies, p[0]);
132               else
133                 return -2;
134             }
135           else
136             sr_policy = pool_elt_at_index (sm->sr_policies, sr_policy_index);
137
138           if (!sr_policy)
139             return -2;
140
141           steer_pl->sr_policy = sr_policy - sm->sr_policies;
142
143           /* Remove old FIB/hw redirection and create a new one */
144           if (steer_pl->classify.traffic_type == SR_STEER_IPV6)
145             {
146               /* Remove FIB entry */
147               pfx.fp_proto = FIB_PROTOCOL_IP6;
148               pfx.fp_len = steer_pl->classify.mask_width;
149               pfx.fp_addr.ip6 = steer_pl->classify.prefix.ip6;
150
151               fib_table_entry_delete (fib_table_find
152                                       (FIB_PROTOCOL_IP6,
153                                        steer_pl->classify.fib_table), &pfx,
154                                       FIB_SOURCE_SR);
155
156               /* Create a new one */
157               goto update_fib;
158             }
159           else if (steer_pl->classify.traffic_type == SR_STEER_IPV4)
160             {
161               /* Remove FIB entry */
162               pfx.fp_proto = FIB_PROTOCOL_IP4;
163               pfx.fp_len = steer_pl->classify.mask_width;
164               pfx.fp_addr.ip4 = steer_pl->classify.prefix.ip4;
165
166               fib_table_entry_delete (fib_table_find
167                                       (FIB_PROTOCOL_IP4,
168                                        steer_pl->classify.fib_table), &pfx,
169                                       FIB_SOURCE_SR);
170
171               /* Create a new one */
172               goto update_fib;
173             }
174         }
175     }
176   else
177     /* delete; steering policy does not exist; complain */
178   if (is_del)
179     return -4;
180
181   /* Retrieve SR policy */
182   if (bsid)                     //FIX
183     {
184       p = hash_get (sm->sr_policies_index_hash, bsid);
185       if (p)
186         sr_policy = pool_elt_at_index (sm->sr_policies, p[0]);
187       else
188         return -2;
189     }
190   else
191     sr_policy = pool_elt_at_index (sm->sr_policies, sr_policy_index);
192
193   /* Create a new steering policy */
194   pool_get (sm->steer_policies, steer_pl);
195   memset (steer_pl, 0, sizeof (*steer_pl));
196
197   if (traffic_type == SR_STEER_IPV4 || traffic_type == SR_STEER_IPV6)
198     {
199       clib_memcpy (&steer_pl->classify.prefix, prefix,
200                    sizeof (ip46_address_t));
201       steer_pl->classify.mask_width = mask_width;
202       steer_pl->classify.fib_table = (table_id != (u32) ~ 0 ? table_id : 0);
203       steer_pl->classify.traffic_type = traffic_type;
204     }
205   else
206     {
207       /* Incorrect API usage. Should never get here */
208       pool_put (sm->steer_policies, steer_pl);
209       mhash_unset (&sm->sr_steer_policies_hash, &key, NULL);
210       return -1;
211     }
212   steer_pl->sr_policy = sr_policy - sm->sr_policies;
213
214   /* Create and store key */
215   mhash_set (&sm->sr_steer_policies_hash, &key, steer_pl - sm->steer_policies,
216              NULL);
217
218 update_fib:;
219
220   fib_route_path_t path = {
221     .frp_proto = FIB_PROTOCOL_MPLS,
222     .frp_local_label = sr_policy->bsid,
223     .frp_eos = MPLS_EOS,
224     .frp_sw_if_index = ~0,
225     .frp_fib_index = 0,
226     .frp_weight = 1,
227     .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
228     .frp_label_stack = NULL
229   };
230
231   fib_route_path_t *paths = NULL;
232
233   /* FIB API calls - Recursive route through the BindingSID */
234   if (traffic_type == SR_STEER_IPV6)
235     {
236       pfx.fp_proto = FIB_PROTOCOL_IP6;
237       pfx.fp_len = steer_pl->classify.mask_width;
238       pfx.fp_addr.ip6 = steer_pl->classify.prefix.ip6;
239       path.frp_fib_index = 0;
240
241       vec_add1 (paths, path);
242
243       fib_table_entry_path_add2 (fib_table_find
244                                  (FIB_PROTOCOL_IP6,
245                                   (table_id != (u32) ~ 0 ? table_id : 0)),
246                                  &pfx, FIB_SOURCE_SR,
247                                  FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT, paths);
248
249       vec_free (paths);
250     }
251   else if (traffic_type == SR_STEER_IPV4)
252     {
253       pfx.fp_proto = FIB_PROTOCOL_IP4;
254       pfx.fp_len = steer_pl->classify.mask_width;
255       pfx.fp_addr.ip4 = steer_pl->classify.prefix.ip4;
256       path.frp_fib_index = 0;
257
258       vec_add1 (paths, path);
259
260       fib_table_entry_path_add2 (fib_table_find
261                                  (FIB_PROTOCOL_IP4,
262                                   (table_id != (u32) ~ 0 ? table_id : 0)),
263                                  &pfx, FIB_SOURCE_SR,
264                                  FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT, paths);
265
266       vec_free (paths);
267     }
268
269   return 0;
270 }
271
272 static clib_error_t *
273 sr_mpls_steer_policy_command_fn (vlib_main_t * vm, unformat_input_t * input,
274                                  vlib_cli_command_t * cmd)
275 {
276   int is_del = 0;
277
278   ip46_address_t prefix;
279   u32 dst_mask_width = 0;
280   u8 traffic_type = 0;
281   u32 fib_table = (u32) ~ 0;
282
283   mpls_label_t bsid;
284   u32 sr_policy_index = (u32) ~ 0;
285
286   u8 sr_policy_set = 0;
287
288   memset (&prefix, 0, sizeof (ip46_address_t));
289
290   int rv;
291   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
292     {
293       if (unformat (input, "del"))
294         is_del = 1;
295       else if (!traffic_type
296                && unformat (input, "l3 %U/%d", unformat_ip6_address,
297                             &prefix.ip6, &dst_mask_width))
298         traffic_type = SR_STEER_IPV6;
299       else if (!traffic_type
300                && unformat (input, "l3 %U/%d", unformat_ip4_address,
301                             &prefix.ip4, &dst_mask_width))
302         traffic_type = SR_STEER_IPV4;
303       else if (!sr_policy_set
304                && unformat (input, "via sr policy index %d",
305                             &sr_policy_index))
306         sr_policy_set = 1;
307       else if (!sr_policy_set
308                && unformat (input, "via sr policy bsid %U",
309                             unformat_mpls_unicast_label, &bsid))
310         sr_policy_set = 1;
311       else if (fib_table == (u32) ~ 0
312                && unformat (input, "fib-table %d", &fib_table));
313       else
314         break;
315     }
316
317   if (!traffic_type)
318     return clib_error_return (0, "No L3 traffic specified");
319   if (!sr_policy_set)
320     return clib_error_return (0, "No SR policy specified");
321
322   /* Make sure that the prefixes are clean */
323   if (traffic_type == SR_STEER_IPV4)
324     {
325       u32 mask =
326         (dst_mask_width ? (0xFFFFFFFFu >> (32 - dst_mask_width)) : 0);
327       prefix.ip4.as_u32 &= mask;
328     }
329   else if (traffic_type == SR_STEER_IPV6)
330     {
331       ip6_address_t mask;
332       ip6_address_mask_from_width (&mask, dst_mask_width);
333       ip6_address_mask (&prefix.ip6, &mask);
334     }
335
336   rv =
337     sr_mpls_steering_policy (is_del, bsid,
338                              sr_policy_index, fib_table, &prefix,
339                              dst_mask_width, traffic_type);
340
341   switch (rv)
342     {
343     case 0:
344       break;
345     case 1:
346       return 0;
347     case -1:
348       return clib_error_return (0, "Incorrect API usage.");
349     case -2:
350       return clib_error_return (0,
351                                 "The requested SR policy could not be located. Review the BSID/index.");
352     case -3:
353       return clib_error_return (0,
354                                 "Unable to do SW redirect. Incorrect interface.");
355     case -4:
356       return clib_error_return (0,
357                                 "The requested SR steering policy could not be deleted.");
358     case -5:
359       return clib_error_return (0,
360                                 "The SR policy is not an encapsulation one.");
361     default:
362       return clib_error_return (0, "BUG: sr steer policy returns %d", rv);
363     }
364   return 0;
365 }
366
367 /* *INDENT-OFF* */
368 VLIB_CLI_COMMAND (sr_mpls_steer_policy_command, static) = {
369   .path = "sr mpls steer",
370   .short_help = "sr mpls steer (del) l3 <ip_addr/mask>"
371     "via sr policy bsid <mpls_label> (fib-table <fib_table_index>)",
372   .long_help =
373     "\tSteer L3 traffic through an existing SR policy.\n"
374     "\tExamples:\n"
375     "\t\tsr steer l3 2001::/64 via sr_policy index 5\n"
376     "\t\tsr steer l3 2001::/64 via sr_policy bsid 29999\n"
377     "\t\tsr steer del l3 2001::/64 via sr_policy index 5\n",
378   .function = sr_mpls_steer_policy_command_fn,
379 };
380 /* *INDENT-ON* */
381
382 static clib_error_t *
383 show_sr_mpls_steering_policies_command_fn (vlib_main_t * vm,
384                                            unformat_input_t * input,
385                                            vlib_cli_command_t * cmd)
386 {
387   mpls_sr_main_t *sm = &sr_mpls_main;
388   mpls_sr_steering_policy_t **steer_policies = 0;
389   mpls_sr_steering_policy_t *steer_pl;
390
391   mpls_sr_policy_t *pl = 0;
392   int i;
393
394   vlib_cli_output (vm, "SR MPLS steering policies:");
395   /* *INDENT-OFF* */
396   pool_foreach (steer_pl, sm->steer_policies, ({vec_add1(steer_policies, steer_pl);}));
397   /* *INDENT-ON* */
398   vlib_cli_output (vm, "Traffic\t\tSR policy BSID");
399   for (i = 0; i < vec_len (steer_policies); i++)
400     {
401       steer_pl = steer_policies[i];
402       pl = pool_elt_at_index (sm->sr_policies, steer_pl->sr_policy);
403       if (steer_pl->classify.traffic_type == SR_STEER_IPV4)
404         {
405           vlib_cli_output (vm, "L3 %U/%d\t%U",
406                            format_ip4_address,
407                            &steer_pl->classify.prefix.ip4,
408                            steer_pl->classify.mask_width,
409                            format_mpls_unicast_label, pl->bsid);
410         }
411       else if (steer_pl->classify.traffic_type == SR_STEER_IPV6)
412         {
413           vlib_cli_output (vm, "L3 %U/%d\t%U",
414                            format_ip6_address,
415                            &steer_pl->classify.prefix.ip6,
416                            steer_pl->classify.mask_width,
417                            format_mpls_unicast_label, pl->bsid);
418         }
419     }
420   return 0;
421 }
422
423 /* *INDENT-OFF* */
424 VLIB_CLI_COMMAND (show_sr_mpls_steering_policies_command, static) = {
425   .path = "show sr mpls steering policies",
426   .short_help = "show sr mpls steering policies",
427   .function = show_sr_mpls_steering_policies_command_fn,
428 };
429 /* *INDENT-ON* */
430
431 clib_error_t *
432 sr_mpls_steering_init (vlib_main_t * vm)
433 {
434   mpls_sr_main_t *sm = &sr_mpls_main;
435
436   /* Init memory for function keys */
437   mhash_init (&sm->sr_steer_policies_hash, sizeof (uword),
438               sizeof (sr_mpls_steering_key_t));
439
440   return 0;
441 }
442
443 /* *INDENT-OFF* */
444 VLIB_INIT_FUNCTION (sr_mpls_steering_init);
445 /* *INDENT-ON* */
446
447 /*
448 * fd.io coding-style-patch-verification: ON
449 *
450 * Local Variables:
451 * eval: (c-set-style "gnu")
452 * End:
453 */