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