blob: 3a9aea2d263c2abe02e6c7b020ceec71d5babae6 [file] [log] [blame]
Pablo Camarillo5d73eec2017-04-24 17:51:56 +02001/*
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 */
55int
56sr_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
218update_fib:;
219
220 fib_route_path_t path = {
Neale Rannsda78f952017-05-24 09:15:43 -0700221 .frp_proto = DPO_PROTO_MPLS,
Pablo Camarillo5d73eec2017-04-24 17:51:56 +0200222 .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
272static clib_error_t *
273sr_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* */
368VLIB_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
382static clib_error_t *
383show_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* */
424VLIB_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
431clib_error_t *
432sr_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* */
444VLIB_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*/