abf_policy.c revision 5ca2a8ae
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 */
26fib_node_type_t abf_policy_fib_node_type;
27
28/**
29 * Pool of ABF objects
30 */
31static abf_policy_t *abf_policy_pool;
32
33/**
34 * DB of ABF policy objects
35 *  - policy ID to index conversion.
36 */
37static uword *abf_policy_db;
38
39
40abf_policy_t *
41abf_policy_get (u32 index)
42{
43  return (pool_elt_at_index (abf_policy_pool, index));
44}
45
46static u32
47abf_policy_get_index (const abf_policy_t * abf)
48{
49  return (abf - abf_policy_pool);
50}
51
52static abf_policy_t *
53abf_policy_find_i (u32 policy_id)
54{
55  u32 api;
56
57  api = abf_policy_find (policy_id);
58
59  if (INDEX_INVALID != api)
60    return (abf_policy_get (api));
61
62  return (NULL);
63}
64
65u32
66abf_policy_find (u32 policy_id)
67{
68  uword *p;
69
70  p = hash_get (abf_policy_db, policy_id);
71
72  if (NULL != p)
73    return (p[0]);
74
75  return (INDEX_INVALID);
76}
77
78
79int
80abf_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  api = abf_policy_find (policy_id);
87
88  if (INDEX_INVALID == api)
89    {
90      /*
91       * create a new policy
92       */
93      pool_get (abf_policy_pool, ap);
94
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);
101
102      /*
103       * become a child of the path list so we get poked when
104       * the forwarding changes.
105       */
106      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      hash_set (abf_policy_db, policy_id, api);
114
115      /*
116       * take a lock on behalf of the CLI/API creation
117       */
118      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      ap = abf_policy_get (api);
130      old_pl = ap->ap_pl;
131      if (ap->ap_acl != acl_index)
132	{
133	  /* Should change this error code to something more descriptive */
134	  return (VNET_API_ERROR_INVALID_VALUE);
135	}
136
137      if (FIB_NODE_INDEX_INVALID != old_pl)
138	{
139	  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	  fib_path_list_child_remove (old_pl, ap->ap_sibling);
145	}
146      else
147	{
148	  ap->ap_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
149					     FIB_PATH_LIST_FLAG_NO_URPF),
150					    rpaths);
151	}
152
153      ap->ap_sibling = fib_path_list_child_add (ap->ap_pl,
154						abf_policy_fib_node_type,
155						api);
156
157      fib_node_back_walk_ctx_t ctx = {
158	.fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
159      };
160
161      fib_walk_sync (abf_policy_fib_node_type, api, &ctx);
162    }
163  return (0);
164}
165
166static void
167abf_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  ASSERT (ap->ap_sibling == ~0);
174  ASSERT (ap->ap_pl == FIB_NODE_INDEX_INVALID);
175
176  hash_unset (abf_policy_db, ap->ap_id);
177  pool_put (abf_policy_pool, ap);
178}
179
180int
181abf_policy_delete (u32 policy_id, const fib_route_path_t * rpaths)
182{
183  abf_policy_t *ap;
184  u32 api;
185
186  api = abf_policy_find (policy_id);
187
188  if (INDEX_INVALID == api)
189    {
190      /*
191       * no such policy
192       */
193      return (VNET_API_ERROR_INVALID_VALUE);
194    }
195  else
196    {
197      /*
198       * update an existing policy.
199       * - add the path to the path-list and swap our ancestry
200       * - backwalk to poke all attachments to update
201       */
202      fib_node_index_t old_pl;
203
204      ap = abf_policy_get (api);
205      old_pl = ap->ap_pl;
206
207      fib_path_list_lock (old_pl);
208      ap->ap_pl =
209	fib_path_list_copy_and_path_remove (ap->ap_pl,
210					    (FIB_PATH_LIST_FLAG_SHARED |
211					     FIB_PATH_LIST_FLAG_NO_URPF),
212					    rpaths);
213
214      fib_path_list_child_remove (old_pl, ap->ap_sibling);
215      ap->ap_sibling = ~0;
216
217      if (FIB_NODE_INDEX_INVALID == ap->ap_pl)
218	{
219	  /*
220	   * no more paths on this policy. It's toast
221	   * remove the CLI/API's lock
222	   */
223	  fib_node_unlock (&ap->ap_node);
224	}
225      else
226	{
227	  ap->ap_sibling = fib_path_list_child_add (ap->ap_pl,
228						    abf_policy_fib_node_type,
229						    api);
230
231	  fib_node_back_walk_ctx_t ctx = {
232	    .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
233	  };
234
235	  fib_walk_sync (abf_policy_fib_node_type, api, &ctx);
236	}
237      fib_path_list_unlock (old_pl);
238    }
239
240  return (0);
241}
242
243static clib_error_t *
244abf_policy_cmd (vlib_main_t * vm,
245		unformat_input_t * main_input, vlib_cli_command_t * cmd)
246{
247  unformat_input_t _line_input, *line_input = &_line_input;
248  fib_route_path_t *rpaths = NULL, rpath;
249  u32 acl_index, policy_id, is_del;
250  dpo_proto_t payload_proto;
251  int rv = 0;
252
253  is_del = 0;
254  acl_index = INDEX_INVALID;
255  policy_id = INDEX_INVALID;
256
257  /* Get a line of input. */
258  if (!unformat_user (main_input, unformat_line_input, line_input))
259    return 0;
260
261  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
262    {
263      if (unformat (line_input, "acl %d", &acl_index))
264	;
265      else if (unformat (line_input, "id %d", &policy_id))
266	;
267      else if (unformat (line_input, "del"))
268	is_del = 1;
269      else if (unformat (line_input, "add"))
270	is_del = 0;
271      else if (unformat (line_input, "via %U",
272			 unformat_fib_route_path, &rpath, &payload_proto))
273	vec_add1 (rpaths, rpath);
274      else
275	return (clib_error_return (0, "unknown input '%U'",
276				   format_unformat_error, line_input));
277    }
278
279  if (INDEX_INVALID == policy_id)
280    {
281      vlib_cli_output (vm, "Specify a Policy ID");
282      return 0;
283    }
284
285  if (!is_del)
286    {
287      if (INDEX_INVALID == acl_index)
288	{
289	  vlib_cli_output (vm, "ACL index must be set");
290	  return 0;
291	}
292
293      rv = abf_policy_update (policy_id, acl_index, rpaths);
294      /* Should change this error code to something more descriptive */
295      if (rv == VNET_API_ERROR_INVALID_VALUE)
296	{
297	  vlib_cli_output (vm,
298			   "ACL index must match existing ACL index in policy");
299	  return 0;
300	}
301    }
302  else
303    {
304      abf_policy_delete (policy_id, rpaths);
305    }
306
307  unformat_free (line_input);
308  return (NULL);
309}
310
311/* *INDENT-OFF* */
312/**
313 * Create an ABF policy.
314 */
315VLIB_CLI_COMMAND (abf_policy_cmd_node, static) = {
316  .path = "abf policy",
317  .function = abf_policy_cmd,
318  .short_help = "abf policy [add|del] id <index> acl <index> via ...",
319  .is_mp_safe = 1,
320};
321/* *INDENT-ON* */
322
323static u8 *
324format_abf (u8 * s, va_list * args)
325{
326  abf_policy_t *ap = va_arg (*args, abf_policy_t *);
327
328  s = format (s, "abf:[%d]: policy:%d acl:%d",
329	      ap - abf_policy_pool, ap->ap_id, ap->ap_acl);
330  s = format (s, "\n ");
331  if (FIB_NODE_INDEX_INVALID == ap->ap_pl)
332    {
333      s = format (s, "no forwarding");
334    }
335  else
336    {
337      s = fib_path_list_format (ap->ap_pl, s);
338    }
339
340  return (s);
341}
342
343void
344abf_policy_walk (abf_policy_walk_cb_t cb, void *ctx)
345{
346  u32 api;
347
348  /* *INDENT-OFF* */
349  pool_foreach_index(api, abf_policy_pool,
350  ({
351    if (!cb(api, ctx))
352      break;
353  }));
354  /* *INDENT-ON* */
355}
356
357static clib_error_t *
358abf_show_policy_cmd (vlib_main_t * vm,
359		     unformat_input_t * input, vlib_cli_command_t * cmd)
360{
361  u32 policy_id;
362  abf_policy_t *ap;
363
364  policy_id = INDEX_INVALID;
365
366  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
367    {
368      if (unformat (input, "%d", &policy_id))
369	;
370      else
371	return (clib_error_return (0, "unknown input '%U'",
372				   format_unformat_error, input));
373    }
374
375  if (INDEX_INVALID == policy_id)
376    {
377      /* *INDENT-OFF* */
378      pool_foreach(ap, abf_policy_pool,
379      ({
380        vlib_cli_output(vm, "%U", format_abf, ap);
381      }));
382      /* *INDENT-ON* */
383    }
384  else
385    {
386      ap = abf_policy_find_i (policy_id);
387
388      if (NULL != ap)
389	vlib_cli_output (vm, "%U", format_abf, ap);
390      else
391	vlib_cli_output (vm, "Invalid policy ID:%d", policy_id);
392    }
393
394  return (NULL);
395}
396
397/* *INDENT-OFF* */
398VLIB_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>",
402  .is_mp_safe = 1,
403};
404/* *INDENT-ON* */
405
406static fib_node_t *
407abf_policy_get_node (fib_node_index_t index)
408{
409  abf_policy_t *ap = abf_policy_get (index);
410  return (&(ap->ap_node));
411}
412
413static abf_policy_t *
414abf_policy_get_from_node (fib_node_t * node)
415{
416  return ((abf_policy_t *) (((char *) node) -
417			    STRUCT_OFFSET_OF (abf_policy_t, ap_node)));
418}
419
420static void
421abf_policy_last_lock_gone (fib_node_t * node)
422{
423  abf_policy_destroy (abf_policy_get_from_node (node));
424}
425
426/*
427 * A back walk has reached this ABF policy
428 */
429static fib_node_back_walk_rc_t
430abf_policy_back_walk_notify (fib_node_t * node,
431			     fib_node_back_walk_ctx_t * ctx)
432{
433  /*
434   * re-stack the fmask on the n-eos of the via
435   */
436  abf_policy_t *abf = abf_policy_get_from_node (node);
437
438  /*
439   * propagate further up the graph.
440   * we can do this synchronously since the fan out is small.
441   */
442  fib_walk_sync (abf_policy_fib_node_type, abf_policy_get_index (abf), ctx);
443
444  return (FIB_NODE_BACK_WALK_CONTINUE);
445}
446
447/*
448 * The BIER fmask's graph node virtual function table
449 */
450static const fib_node_vft_t abf_policy_vft = {
451  .fnv_get = abf_policy_get_node,
452  .fnv_last_lock = abf_policy_last_lock_gone,
453  .fnv_back_walk = abf_policy_back_walk_notify,
454};
455
456static clib_error_t *
457abf_policy_init (vlib_main_t * vm)
458{
459  abf_policy_fib_node_type = fib_node_register_new_type (&abf_policy_vft);
460
461  return (NULL);
462}
463
464VLIB_INIT_FUNCTION (abf_policy_init);
465
466/*
467 * fd.io coding-style-patch-verification: ON
468 *
469 * Local Variables:
470 * eval: (c-set-style "gnu")
471 * End:
472 */
473