ikev2: add support for custom ipsec-over-udp port
[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 <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 abf_policy_get (u32 index)
42 {
43   return (pool_elt_at_index (abf_policy_pool, index));
44 }
45
46 static u32
47 abf_policy_get_index (const abf_policy_t * abf)
48 {
49   return (abf - abf_policy_pool);
50 }
51
52 static abf_policy_t *
53 abf_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
65 u32
66 abf_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
79 int
80 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   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
166 static void
167 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   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
180 int
181 abf_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
243 static clib_error_t *
244 abf_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  */
315 VLIB_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
323 static u8 *
324 format_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
343 void
344 abf_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
357 static clib_error_t *
358 abf_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* */
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>",
402   .is_mp_safe = 1,
403 };
404 /* *INDENT-ON* */
405
406 static fib_node_t *
407 abf_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
413 static abf_policy_t *
414 abf_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
420 static void
421 abf_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  */
429 static fib_node_back_walk_rc_t
430 abf_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  */
450 static 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
456 static clib_error_t *
457 abf_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
464 VLIB_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  */