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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <plugins/abf/abf_policy.h>
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>
24 * FIB node type the attachment is registered
26 fib_node_type_t abf_policy_fib_node_type;
31 static abf_policy_t *abf_policy_pool;
34 * DB of ABF policy objects
35 * - policy ID to index conversion.
37 static uword *abf_policy_db;
41 abf_policy_get (u32 index)
43 return (pool_elt_at_index (abf_policy_pool, index));
47 abf_policy_get_index (const abf_policy_t * abf)
49 return (abf - abf_policy_pool);
53 abf_policy_find_i (u32 policy_id)
57 api = abf_policy_find (policy_id);
59 if (INDEX_INVALID != api)
60 return (abf_policy_get (api));
66 abf_policy_find (u32 policy_id)
70 p = hash_get (abf_policy_db, policy_id);
75 return (INDEX_INVALID);
80 abf_policy_update (u32 policy_id,
81 u32 acl_index, const fib_route_path_t * rpaths)
86 api = abf_policy_find (policy_id);
88 if (INDEX_INVALID == api)
93 pool_get (abf_policy_pool, ap);
95 api = ap - abf_policy_pool;
96 fib_node_init (&ap->ap_node, abf_policy_fib_node_type);
97 ap->ap_acl = acl_index;
98 ap->ap_id = policy_id;
99 ap->ap_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
100 FIB_PATH_LIST_FLAG_NO_URPF), rpaths);
103 * become a child of the path list so we get poked when
104 * the forwarding changes.
106 ap->ap_sibling = fib_path_list_child_add (ap->ap_pl,
107 abf_policy_fib_node_type,
111 * add this new policy to the DB
113 hash_set (abf_policy_db, policy_id, api);
116 * take a lock on behalf of the CLI/API creation
118 fib_node_lock (&ap->ap_node);
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
127 fib_node_index_t old_pl;
129 ap = abf_policy_get (api);
131 if (ap->ap_acl != acl_index)
133 /* Should change this error code to something more descriptive */
134 return (VNET_API_ERROR_INVALID_VALUE);
137 if (FIB_NODE_INDEX_INVALID != old_pl)
139 ap->ap_pl = fib_path_list_copy_and_path_add (old_pl,
140 (FIB_PATH_LIST_FLAG_SHARED
142 FIB_PATH_LIST_FLAG_NO_URPF),
144 fib_path_list_child_remove (old_pl, ap->ap_sibling);
148 ap->ap_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
149 FIB_PATH_LIST_FLAG_NO_URPF),
153 ap->ap_sibling = fib_path_list_child_add (ap->ap_pl,
154 abf_policy_fib_node_type,
157 fib_node_back_walk_ctx_t ctx = {
158 .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
161 fib_walk_sync (abf_policy_fib_node_type, api, &ctx);
167 abf_policy_destroy (abf_policy_t * ap)
170 * this ABF should not be a sibling on the path list, since
171 * that was removed when the API config went
173 ASSERT (ap->ap_sibling == ~0);
174 ASSERT (ap->ap_pl == FIB_NODE_INDEX_INVALID);
176 hash_unset (abf_policy_db, ap->ap_id);
177 pool_put (abf_policy_pool, ap);
181 abf_policy_delete (u32 policy_id, const fib_route_path_t * rpaths)
186 api = abf_policy_find (policy_id);
188 if (INDEX_INVALID == api)
193 return (VNET_API_ERROR_INVALID_VALUE);
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
201 fib_node_index_t old_pl;
203 ap = abf_policy_get (api);
206 fib_path_list_lock (old_pl);
207 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),
211 fib_path_list_child_remove (old_pl, ap->ap_sibling);
214 if (FIB_NODE_INDEX_INVALID == ap->ap_pl)
217 * no more paths on this policy. It's toast
218 * remove the CLI/API's lock
220 fib_node_unlock (&ap->ap_node);
225 fib_path_list_child_add (ap->ap_pl, abf_policy_fib_node_type, api);
227 fib_node_back_walk_ctx_t ctx = {
228 .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
231 fib_walk_sync (abf_policy_fib_node_type, api, &ctx);
233 fib_path_list_unlock (old_pl);
238 static clib_error_t *
239 abf_policy_cmd (vlib_main_t * vm,
240 unformat_input_t * main_input, vlib_cli_command_t * cmd)
242 unformat_input_t _line_input, *line_input = &_line_input;
243 fib_route_path_t *rpaths = NULL, rpath;
244 u32 acl_index, policy_id, is_del;
245 dpo_proto_t payload_proto;
249 acl_index = INDEX_INVALID;
250 policy_id = INDEX_INVALID;
252 /* Get a line of input. */
253 if (!unformat_user (main_input, unformat_line_input, line_input))
256 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
258 if (unformat (line_input, "acl %d", &acl_index))
260 else if (unformat (line_input, "id %d", &policy_id))
262 else if (unformat (line_input, "del"))
264 else if (unformat (line_input, "add"))
266 else if (unformat (line_input, "via %U",
267 unformat_fib_route_path, &rpath, &payload_proto))
268 vec_add1 (rpaths, rpath);
272 err = clib_error_return (0, "unknown input '%U'",
273 format_unformat_error, line_input);
274 unformat_free (line_input);
279 if (INDEX_INVALID == policy_id)
281 vlib_cli_output (vm, "Specify a Policy ID");
285 if (vec_len (rpaths) == 0)
287 vlib_cli_output (vm, "Hop path must not be empty");
293 if (INDEX_INVALID == acl_index)
295 vlib_cli_output (vm, "ACL index must be set");
299 rv = abf_policy_update (policy_id, acl_index, rpaths);
300 /* Should change this error code to something more descriptive */
301 if (rv == VNET_API_ERROR_INVALID_VALUE)
304 "ACL index must match existing ACL index in policy");
310 abf_policy_delete (policy_id, rpaths);
314 unformat_free (line_input);
319 * Create an ABF policy.
321 VLIB_CLI_COMMAND (abf_policy_cmd_node, static) = {
322 .path = "abf policy",
323 .function = abf_policy_cmd,
324 .short_help = "abf policy [add|del] id <index> acl <index> via ...",
329 format_abf (u8 * s, va_list * args)
331 abf_policy_t *ap = va_arg (*args, abf_policy_t *);
333 s = format (s, "abf:[%d]: policy:%d acl:%d",
334 ap - abf_policy_pool, ap->ap_id, ap->ap_acl);
335 s = format (s, "\n ");
336 if (FIB_NODE_INDEX_INVALID == ap->ap_pl)
338 s = format (s, "no forwarding");
342 s = fib_path_list_format (ap->ap_pl, s);
349 abf_policy_walk (abf_policy_walk_cb_t cb, void *ctx)
353 pool_foreach_index (api, abf_policy_pool)
360 static clib_error_t *
361 abf_show_policy_cmd (vlib_main_t * vm,
362 unformat_input_t * input, vlib_cli_command_t * cmd)
367 policy_id = INDEX_INVALID;
369 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
371 if (unformat (input, "%d", &policy_id))
374 return (clib_error_return (0, "unknown input '%U'",
375 format_unformat_error, input));
378 if (INDEX_INVALID == policy_id)
380 pool_foreach (ap, abf_policy_pool)
382 vlib_cli_output(vm, "%U", format_abf, ap);
387 ap = abf_policy_find_i (policy_id);
390 vlib_cli_output (vm, "%U", format_abf, ap);
392 vlib_cli_output (vm, "Invalid policy ID:%d", policy_id);
398 VLIB_CLI_COMMAND (abf_policy_show_policy_cmd_node, static) = {
399 .path = "show abf policy",
400 .function = abf_show_policy_cmd,
401 .short_help = "show abf policy <value>",
406 abf_policy_get_node (fib_node_index_t index)
408 abf_policy_t *ap = abf_policy_get (index);
409 return (&(ap->ap_node));
412 static abf_policy_t *
413 abf_policy_get_from_node (fib_node_t * node)
415 return ((abf_policy_t *) (((char *) node) -
416 STRUCT_OFFSET_OF (abf_policy_t, ap_node)));
420 abf_policy_last_lock_gone (fib_node_t * node)
422 abf_policy_destroy (abf_policy_get_from_node (node));
426 * A back walk has reached this ABF policy
428 static fib_node_back_walk_rc_t
429 abf_policy_back_walk_notify (fib_node_t * node,
430 fib_node_back_walk_ctx_t * ctx)
433 * re-stack the fmask on the n-eos of the via
435 abf_policy_t *abf = abf_policy_get_from_node (node);
438 * propagate further up the graph.
439 * we can do this synchronously since the fan out is small.
441 fib_walk_sync (abf_policy_fib_node_type, abf_policy_get_index (abf), ctx);
443 return (FIB_NODE_BACK_WALK_CONTINUE);
447 * The BIER fmask's graph node virtual function table
449 static const fib_node_vft_t abf_policy_vft = {
450 .fnv_get = abf_policy_get_node,
451 .fnv_last_lock = abf_policy_last_lock_gone,
452 .fnv_back_walk = abf_policy_back_walk_notify,
455 static clib_error_t *
456 abf_policy_init (vlib_main_t * vm)
458 abf_policy_fib_node_type =
459 fib_node_register_new_type ("abf-policy", &abf_policy_vft);
464 VLIB_INIT_FUNCTION (abf_policy_init);
467 * fd.io coding-style-patch-verification: ON
470 * eval: (c-set-style "gnu")