ACL based forwarding
[vpp.git] / src / plugins / abf / abf_policy.c
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  */
27 fib_node_type_t abf_policy_fib_node_type;
28
29 /**
30  * Pool of ABF objects
31  */
32 static abf_policy_t *abf_policy_pool;
33
34 /**
35  * DB of ABF policy objects
36  *  - policy ID to index conversion.
37  */
38 static uword *abf_policy_db;
39
40
41 abf_policy_t *
42 abf_policy_get (u32 index)
43 {
44   return (pool_elt_at_index (abf_policy_pool, index));
45 }
46
47 static u32
48 abf_policy_get_index (const abf_policy_t * abf)
49 {
50   return (abf - abf_policy_pool);
51 }
52
53 static abf_policy_t *
54 abf_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
66 u32
67 abf_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
80 void
81 abf_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
161 static void
162 abf_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
175 int
176 abf_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
236 static clib_error_t *
237 abf_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  */
300 VLIB_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
308 static u8 *
309 format_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
328 void
329 abf_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
342 static clib_error_t *
343 abf_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* */
383 VLIB_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
391 static fib_node_t *
392 abf_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
398 static abf_policy_t *
399 abf_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
405 static void
406 abf_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  */
414 static fib_node_back_walk_rc_t
415 abf_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  */
435 static 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
441 static clib_error_t *
442 abf_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
449 VLIB_INIT_FUNCTION (abf_policy_init);
450
451 /* *INDENT-OFF* */
452 VLIB_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  */