Line data Source code
1 : /*
2 : * Copyright (c) 2017 Cisco and/or its affiliates.
3 : * Licensed under the Apache License, Version 2.0 (the "License");
4 : * you may not use this file except in compliance with the License.
5 : * You may obtain a copy of the License at:
6 : *
7 : * http://www.apache.org/licenses/LICENSE-2.0
8 : *
9 : * Unless required by applicable law or agreed to in writing, software
10 : * distributed under the License is distributed on an "AS IS" BASIS,
11 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : * See the License for the specific language governing permissions and
13 : * limitations under the License.
14 : */
15 :
16 : #include <plugins/abf/abf_policy.h>
17 :
18 : #include <vlib/vlib.h>
19 : #include <vnet/plugin/plugin.h>
20 : #include <vnet/fib/fib_path_list.h>
21 : #include <vnet/fib/fib_walk.h>
22 :
23 : /**
24 : * FIB node type the attachment is registered
25 : */
26 : fib_node_type_t abf_policy_fib_node_type;
27 :
28 : /**
29 : * Pool of ABF objects
30 : */
31 : static abf_policy_t *abf_policy_pool;
32 :
33 : /**
34 : * DB of ABF policy objects
35 : * - policy ID to index conversion.
36 : */
37 : static uword *abf_policy_db;
38 :
39 :
40 : abf_policy_t *
41 179 : abf_policy_get (u32 index)
42 : {
43 179 : return (pool_elt_at_index (abf_policy_pool, index));
44 : }
45 :
46 : static u32
47 23 : abf_policy_get_index (const abf_policy_t * abf)
48 : {
49 23 : return (abf - abf_policy_pool);
50 : }
51 :
52 : static abf_policy_t *
53 0 : abf_policy_find_i (u32 policy_id)
54 : {
55 : u32 api;
56 :
57 0 : api = abf_policy_find (policy_id);
58 :
59 0 : if (INDEX_INVALID != api)
60 0 : return (abf_policy_get (api));
61 :
62 0 : return (NULL);
63 : }
64 :
65 : u32
66 22 : abf_policy_find (u32 policy_id)
67 : {
68 : uword *p;
69 :
70 22 : p = hash_get (abf_policy_db, policy_id);
71 :
72 22 : if (NULL != p)
73 15 : return (p[0]);
74 :
75 7 : return (INDEX_INVALID);
76 : }
77 :
78 :
79 : int
80 7 : abf_policy_update (u32 policy_id,
81 : u32 acl_index, const fib_route_path_t * rpaths)
82 : {
83 : abf_policy_t *ap;
84 : u32 api;
85 :
86 7 : api = abf_policy_find (policy_id);
87 :
88 7 : if (INDEX_INVALID == api)
89 : {
90 : /*
91 : * create a new policy
92 : */
93 7 : pool_get (abf_policy_pool, ap);
94 :
95 7 : api = ap - abf_policy_pool;
96 7 : fib_node_init (&ap->ap_node, abf_policy_fib_node_type);
97 7 : ap->ap_acl = acl_index;
98 7 : ap->ap_id = policy_id;
99 7 : ap->ap_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
100 : FIB_PATH_LIST_FLAG_NO_URPF), rpaths);
101 :
102 : /*
103 : * become a child of the path list so we get poked when
104 : * the forwarding changes.
105 : */
106 7 : ap->ap_sibling = fib_path_list_child_add (ap->ap_pl,
107 : abf_policy_fib_node_type,
108 : api);
109 :
110 : /*
111 : * add this new policy to the DB
112 : */
113 7 : hash_set (abf_policy_db, policy_id, api);
114 :
115 : /*
116 : * take a lock on behalf of the CLI/API creation
117 : */
118 7 : fib_node_lock (&ap->ap_node);
119 : }
120 : else
121 : {
122 : /*
123 : * update an existing policy.
124 : * - add the path to the path-list and swap our ancestry
125 : * - backwalk to poke all attachments to update
126 : */
127 : fib_node_index_t old_pl;
128 :
129 0 : ap = abf_policy_get (api);
130 0 : old_pl = ap->ap_pl;
131 0 : if (ap->ap_acl != acl_index)
132 : {
133 : /* Should change this error code to something more descriptive */
134 0 : return (VNET_API_ERROR_INVALID_VALUE);
135 : }
136 :
137 0 : if (FIB_NODE_INDEX_INVALID != old_pl)
138 : {
139 0 : ap->ap_pl = fib_path_list_copy_and_path_add (old_pl,
140 : (FIB_PATH_LIST_FLAG_SHARED
141 : |
142 : FIB_PATH_LIST_FLAG_NO_URPF),
143 : rpaths);
144 0 : fib_path_list_child_remove (old_pl, ap->ap_sibling);
145 : }
146 : else
147 : {
148 0 : ap->ap_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
149 : FIB_PATH_LIST_FLAG_NO_URPF),
150 : rpaths);
151 : }
152 :
153 0 : ap->ap_sibling = fib_path_list_child_add (ap->ap_pl,
154 : abf_policy_fib_node_type,
155 : api);
156 :
157 0 : fib_node_back_walk_ctx_t ctx = {
158 : .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
159 : };
160 :
161 0 : fib_walk_sync (abf_policy_fib_node_type, api, &ctx);
162 : }
163 7 : return (0);
164 : }
165 :
166 : static void
167 7 : abf_policy_destroy (abf_policy_t * ap)
168 : {
169 : /*
170 : * this ABF should not be a sibling on the path list, since
171 : * that was removed when the API config went
172 : */
173 7 : ASSERT (ap->ap_sibling == ~0);
174 7 : ASSERT (ap->ap_pl == FIB_NODE_INDEX_INVALID);
175 :
176 7 : hash_unset (abf_policy_db, ap->ap_id);
177 7 : pool_put (abf_policy_pool, ap);
178 7 : }
179 :
180 : int
181 7 : abf_policy_delete (u32 policy_id, const fib_route_path_t * rpaths)
182 : {
183 : abf_policy_t *ap;
184 : u32 api;
185 :
186 7 : api = abf_policy_find (policy_id);
187 :
188 7 : if (INDEX_INVALID == api)
189 : {
190 : /*
191 : * no such policy
192 : */
193 0 : return (VNET_API_ERROR_INVALID_VALUE);
194 : }
195 :
196 : /*
197 : * update an existing policy.
198 : * - add the path to the path-list and swap our ancestry
199 : * - backwalk to poke all attachments to update
200 : */
201 : fib_node_index_t old_pl;
202 :
203 7 : ap = abf_policy_get (api);
204 7 : old_pl = ap->ap_pl;
205 :
206 7 : fib_path_list_lock (old_pl);
207 7 : ap->ap_pl = fib_path_list_copy_and_path_remove (
208 : ap->ap_pl, (FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF),
209 : rpaths);
210 :
211 7 : fib_path_list_child_remove (old_pl, ap->ap_sibling);
212 7 : ap->ap_sibling = ~0;
213 :
214 7 : if (FIB_NODE_INDEX_INVALID == ap->ap_pl)
215 : {
216 : /*
217 : * no more paths on this policy. It's toast
218 : * remove the CLI/API's lock
219 : */
220 7 : fib_node_unlock (&ap->ap_node);
221 : }
222 : else
223 : {
224 0 : ap->ap_sibling =
225 0 : fib_path_list_child_add (ap->ap_pl, abf_policy_fib_node_type, api);
226 :
227 0 : fib_node_back_walk_ctx_t ctx = {
228 : .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
229 : };
230 :
231 0 : fib_walk_sync (abf_policy_fib_node_type, api, &ctx);
232 : }
233 7 : fib_path_list_unlock (old_pl);
234 :
235 7 : return (0);
236 : }
237 :
238 : static clib_error_t *
239 0 : abf_policy_cmd (vlib_main_t * vm,
240 : unformat_input_t * main_input, vlib_cli_command_t * cmd)
241 : {
242 0 : unformat_input_t _line_input, *line_input = &_line_input;
243 0 : fib_route_path_t *rpaths = NULL, rpath;
244 : u32 acl_index, policy_id, is_del;
245 : dpo_proto_t payload_proto;
246 0 : int rv = 0;
247 :
248 0 : is_del = 0;
249 0 : acl_index = INDEX_INVALID;
250 0 : policy_id = INDEX_INVALID;
251 :
252 : /* Get a line of input. */
253 0 : if (!unformat_user (main_input, unformat_line_input, line_input))
254 0 : return 0;
255 :
256 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
257 : {
258 0 : if (unformat (line_input, "acl %d", &acl_index))
259 : ;
260 0 : else if (unformat (line_input, "id %d", &policy_id))
261 : ;
262 0 : else if (unformat (line_input, "del"))
263 0 : is_del = 1;
264 0 : else if (unformat (line_input, "add"))
265 0 : is_del = 0;
266 0 : else if (unformat (line_input, "via %U",
267 : unformat_fib_route_path, &rpath, &payload_proto))
268 0 : vec_add1 (rpaths, rpath);
269 : else
270 : {
271 : clib_error_t *err;
272 0 : err = clib_error_return (0, "unknown input '%U'",
273 : format_unformat_error, line_input);
274 0 : unformat_free (line_input);
275 0 : return err;
276 : }
277 : }
278 :
279 0 : if (INDEX_INVALID == policy_id)
280 : {
281 0 : vlib_cli_output (vm, "Specify a Policy ID");
282 0 : goto out;
283 : }
284 :
285 0 : if (vec_len (rpaths) == 0)
286 : {
287 0 : vlib_cli_output (vm, "Hop path must not be empty");
288 0 : goto out;
289 : }
290 :
291 0 : if (!is_del)
292 : {
293 0 : if (INDEX_INVALID == acl_index)
294 : {
295 0 : vlib_cli_output (vm, "ACL index must be set");
296 0 : goto out;
297 : }
298 :
299 0 : rv = abf_policy_update (policy_id, acl_index, rpaths);
300 : /* Should change this error code to something more descriptive */
301 0 : if (rv == VNET_API_ERROR_INVALID_VALUE)
302 : {
303 0 : vlib_cli_output (vm,
304 : "ACL index must match existing ACL index in policy");
305 0 : goto out;
306 : }
307 : }
308 : else
309 : {
310 0 : abf_policy_delete (policy_id, rpaths);
311 : }
312 :
313 0 : out:
314 0 : unformat_free (line_input);
315 0 : return (NULL);
316 : }
317 :
318 : /* *INDENT-OFF* */
319 : /**
320 : * Create an ABF policy.
321 : */
322 282985 : VLIB_CLI_COMMAND (abf_policy_cmd_node, static) = {
323 : .path = "abf policy",
324 : .function = abf_policy_cmd,
325 : .short_help = "abf policy [add|del] id <index> acl <index> via ...",
326 : .is_mp_safe = 1,
327 : };
328 : /* *INDENT-ON* */
329 :
330 : static u8 *
331 0 : format_abf (u8 * s, va_list * args)
332 : {
333 0 : abf_policy_t *ap = va_arg (*args, abf_policy_t *);
334 :
335 0 : s = format (s, "abf:[%d]: policy:%d acl:%d",
336 0 : ap - abf_policy_pool, ap->ap_id, ap->ap_acl);
337 0 : s = format (s, "\n ");
338 0 : if (FIB_NODE_INDEX_INVALID == ap->ap_pl)
339 : {
340 0 : s = format (s, "no forwarding");
341 : }
342 : else
343 : {
344 0 : s = fib_path_list_format (ap->ap_pl, s);
345 : }
346 :
347 0 : return (s);
348 : }
349 :
350 : void
351 14 : abf_policy_walk (abf_policy_walk_cb_t cb, void *ctx)
352 : {
353 : u32 api;
354 :
355 : /* *INDENT-OFF* */
356 33 : pool_foreach_index (api, abf_policy_pool)
357 : {
358 19 : if (!cb(api, ctx))
359 0 : break;
360 : }
361 : /* *INDENT-ON* */
362 14 : }
363 :
364 : static clib_error_t *
365 0 : abf_show_policy_cmd (vlib_main_t * vm,
366 : unformat_input_t * input, vlib_cli_command_t * cmd)
367 : {
368 : u32 policy_id;
369 : abf_policy_t *ap;
370 :
371 0 : policy_id = INDEX_INVALID;
372 :
373 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
374 : {
375 0 : if (unformat (input, "%d", &policy_id))
376 : ;
377 : else
378 0 : return (clib_error_return (0, "unknown input '%U'",
379 : format_unformat_error, input));
380 : }
381 :
382 0 : if (INDEX_INVALID == policy_id)
383 : {
384 : /* *INDENT-OFF* */
385 0 : pool_foreach (ap, abf_policy_pool)
386 : {
387 0 : vlib_cli_output(vm, "%U", format_abf, ap);
388 : }
389 : /* *INDENT-ON* */
390 : }
391 : else
392 : {
393 0 : ap = abf_policy_find_i (policy_id);
394 :
395 0 : if (NULL != ap)
396 0 : vlib_cli_output (vm, "%U", format_abf, ap);
397 : else
398 0 : vlib_cli_output (vm, "Invalid policy ID:%d", policy_id);
399 : }
400 :
401 0 : return (NULL);
402 : }
403 :
404 : /* *INDENT-OFF* */
405 282985 : VLIB_CLI_COMMAND (abf_policy_show_policy_cmd_node, static) = {
406 : .path = "show abf policy",
407 : .function = abf_show_policy_cmd,
408 : .short_help = "show abf policy <value>",
409 : .is_mp_safe = 1,
410 : };
411 : /* *INDENT-ON* */
412 :
413 : static fib_node_t *
414 102 : abf_policy_get_node (fib_node_index_t index)
415 : {
416 102 : abf_policy_t *ap = abf_policy_get (index);
417 102 : return (&(ap->ap_node));
418 : }
419 :
420 : static abf_policy_t *
421 30 : abf_policy_get_from_node (fib_node_t * node)
422 : {
423 30 : return ((abf_policy_t *) (((char *) node) -
424 : STRUCT_OFFSET_OF (abf_policy_t, ap_node)));
425 : }
426 :
427 : static void
428 7 : abf_policy_last_lock_gone (fib_node_t * node)
429 : {
430 7 : abf_policy_destroy (abf_policy_get_from_node (node));
431 7 : }
432 :
433 : /*
434 : * A back walk has reached this ABF policy
435 : */
436 : static fib_node_back_walk_rc_t
437 23 : abf_policy_back_walk_notify (fib_node_t * node,
438 : fib_node_back_walk_ctx_t * ctx)
439 : {
440 : /*
441 : * re-stack the fmask on the n-eos of the via
442 : */
443 23 : abf_policy_t *abf = abf_policy_get_from_node (node);
444 :
445 : /*
446 : * propagate further up the graph.
447 : * we can do this synchronously since the fan out is small.
448 : */
449 23 : fib_walk_sync (abf_policy_fib_node_type, abf_policy_get_index (abf), ctx);
450 :
451 23 : return (FIB_NODE_BACK_WALK_CONTINUE);
452 : }
453 :
454 : /*
455 : * The BIER fmask's graph node virtual function table
456 : */
457 : static const fib_node_vft_t abf_policy_vft = {
458 : .fnv_get = abf_policy_get_node,
459 : .fnv_last_lock = abf_policy_last_lock_gone,
460 : .fnv_back_walk = abf_policy_back_walk_notify,
461 : };
462 :
463 : static clib_error_t *
464 575 : abf_policy_init (vlib_main_t * vm)
465 : {
466 575 : abf_policy_fib_node_type =
467 575 : fib_node_register_new_type ("abf-policy", &abf_policy_vft);
468 :
469 575 : return (NULL);
470 : }
471 :
472 2303 : VLIB_INIT_FUNCTION (abf_policy_init);
473 :
474 : /*
475 : * fd.io coding-style-patch-verification: ON
476 : *
477 : * Local Variables:
478 : * eval: (c-set-style "gnu")
479 : * End:
480 : */
|