Line data Source code
1 : /* Copyright (c) 2021-2022 Cisco and/or its affiliates.
2 : * Licensed under the Apache License, Version 2.0 (the "License");
3 : * you may not use this file except in compliance with the License.
4 : * You may obtain a copy of the License at:
5 : *
6 : * http://www.apache.org/licenses/LICENSE-2.0
7 : *
8 : * Unless required by applicable law or agreed to in writing, software
9 : * distributed under the License is distributed on an "AS IS" BASIS,
10 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 : * See the License for the specific language governing permissions and
12 : * limitations under the License. */
13 : #include <vlib/vlib.h>
14 : #include <vnet/fib/fib_path_list.h>
15 : #include <vnet/classify/vnet_classify.h>
16 : #include <vnet/classify/in_out_acl.h>
17 : #include <vnet/plugin/plugin.h>
18 : #include <vpp/app/version.h>
19 : #include "ip_session_redirect.h"
20 :
21 : typedef struct
22 : {
23 : u8 *match_and_table_index;
24 : dpo_id_t dpo; /* forwarding dpo */
25 : fib_node_t node; /* linkage into the FIB graph */
26 : fib_node_index_t pl;
27 : u32 sibling;
28 : u32 parent_node_index;
29 : u32 opaque_index;
30 : u32 table_index;
31 : fib_forward_chain_type_t payload_type;
32 : u8 is_punt : 1;
33 : u8 is_ip6 : 1;
34 : } ip_session_redirect_t;
35 :
36 : typedef struct
37 : {
38 : ip_session_redirect_t *pool;
39 : u32 *session_by_match_and_table_index;
40 : fib_node_type_t fib_node_type;
41 : } ip_session_redirect_main_t;
42 :
43 : static ip_session_redirect_main_t ip_session_redirect_main;
44 :
45 : static int
46 12 : ip_session_redirect_stack (ip_session_redirect_t *ipr)
47 : {
48 12 : dpo_id_t dpo = DPO_INVALID;
49 :
50 12 : fib_path_list_contribute_forwarding (ipr->pl, ipr->payload_type,
51 12 : fib_path_list_is_popular (ipr->pl) ?
52 : FIB_PATH_LIST_FWD_FLAG_NONE :
53 : FIB_PATH_LIST_FWD_FLAG_COLLAPSE,
54 : &dpo);
55 12 : dpo_stack_from_node (ipr->parent_node_index, &ipr->dpo, &dpo);
56 12 : dpo_reset (&dpo);
57 :
58 : /* update session with new next_index */
59 24 : return vnet_classify_add_del_session (
60 12 : &vnet_classify_main, ipr->table_index, ipr->match_and_table_index,
61 12 : ipr->dpo.dpoi_next_node /* hit_next_index */, ipr->opaque_index,
62 : 0 /* advance */, CLASSIFY_ACTION_SET_METADATA,
63 : ipr->dpo.dpoi_index /* metadata */, 1 /* is_add */);
64 : }
65 :
66 : static ip_session_redirect_t *
67 20 : ip_session_redirect_find (ip_session_redirect_main_t *im, u32 table_index,
68 : const u8 *match)
69 : {
70 : /* we are adding the table index at the end of the match string so we
71 : * can disambiguiate identical matches in different tables in
72 : * im->session_by_match_and_table_index */
73 20 : u8 *match_and_table_index = vec_dup (match);
74 20 : vec_add (match_and_table_index, (void *) &table_index, 4);
75 : uword *p =
76 40 : hash_get_mem (im->session_by_match_and_table_index, match_and_table_index);
77 20 : vec_free (match_and_table_index);
78 20 : if (!p)
79 8 : return 0;
80 12 : return pool_elt_at_index (im->pool, p[0]);
81 : }
82 :
83 : int
84 12 : ip_session_redirect_add (vlib_main_t *vm, u32 table_index, u32 opaque_index,
85 : dpo_proto_t proto, int is_punt, const u8 *match,
86 : const fib_route_path_t *rpaths)
87 : {
88 12 : ip_session_redirect_main_t *im = &ip_session_redirect_main;
89 : fib_forward_chain_type_t payload_type;
90 : ip_session_redirect_t *ipr;
91 : const char *pname;
92 :
93 12 : payload_type = fib_forw_chain_type_from_dpo_proto (proto);
94 12 : switch (payload_type)
95 : {
96 6 : case FIB_FORW_CHAIN_TYPE_UNICAST_IP4:
97 6 : pname = is_punt ? "ip4-punt-acl" : "ip4-inacl";
98 6 : break;
99 6 : case FIB_FORW_CHAIN_TYPE_UNICAST_IP6:
100 6 : pname = is_punt ? "ip6-punt-acl" : "ip6-inacl";
101 6 : break;
102 0 : default:
103 0 : return VNET_API_ERROR_INVALID_ADDRESS_FAMILY;
104 : }
105 :
106 12 : ipr = ip_session_redirect_find (im, table_index, match);
107 12 : if (ipr)
108 : {
109 : /* update to an existing session */
110 4 : fib_path_list_child_remove (ipr->pl, ipr->sibling);
111 4 : dpo_reset (&ipr->dpo);
112 : }
113 : else
114 : {
115 : /* allocate a new entry */
116 8 : pool_get (im->pool, ipr);
117 8 : fib_node_init (&ipr->node, im->fib_node_type);
118 8 : ipr->match_and_table_index = vec_dup ((u8 *) match);
119 : /* we are adding the table index at the end of the match string so we
120 : * can disambiguiate identical matches in different tables in
121 : * im->session_by_match_and_table_index */
122 8 : vec_add (ipr->match_and_table_index, (void *) &table_index, 4);
123 8 : ipr->table_index = table_index;
124 16 : hash_set_mem (im->session_by_match_and_table_index,
125 : ipr->match_and_table_index, ipr - im->pool);
126 : }
127 :
128 12 : ipr->payload_type = payload_type;
129 12 : ipr->pl = fib_path_list_create (
130 : FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF, rpaths);
131 24 : ipr->sibling =
132 12 : fib_path_list_child_add (ipr->pl, im->fib_node_type, ipr - im->pool);
133 12 : ipr->parent_node_index = vlib_get_node_by_name (vm, (u8 *) pname)->index;
134 12 : ipr->opaque_index = opaque_index;
135 12 : ipr->is_punt = is_punt;
136 12 : ipr->is_ip6 = payload_type == FIB_FORW_CHAIN_TYPE_UNICAST_IP6;
137 :
138 12 : return ip_session_redirect_stack (ipr);
139 : }
140 :
141 : int
142 8 : ip_session_redirect_del (vlib_main_t *vm, u32 table_index, const u8 *match)
143 : {
144 8 : ip_session_redirect_main_t *im = &ip_session_redirect_main;
145 8 : vnet_classify_main_t *cm = &vnet_classify_main;
146 : ip_session_redirect_t *ipr;
147 : int rv;
148 :
149 8 : ipr = ip_session_redirect_find (im, table_index, match);
150 8 : if (!ipr)
151 0 : return VNET_API_ERROR_NO_SUCH_ENTRY;
152 :
153 8 : rv = vnet_classify_add_del_session (
154 8 : cm, ipr->table_index, ipr->match_and_table_index, 0 /* hit_next_index */,
155 : 0 /* opaque_index */, 0 /* advance */, 0 /* action */, 0 /* metadata */,
156 : 0 /* is_add */);
157 8 : if (rv)
158 0 : return rv;
159 :
160 16 : hash_unset_mem (im->session_by_match_and_table_index,
161 : ipr->match_and_table_index);
162 8 : vec_free (ipr->match_and_table_index);
163 8 : fib_path_list_child_remove (ipr->pl, ipr->sibling);
164 8 : dpo_reset (&ipr->dpo);
165 8 : pool_put (im->pool, ipr);
166 8 : return 0;
167 : }
168 :
169 : static int
170 0 : ip_session_redirect_show_yield (vlib_main_t *vm, f64 *start)
171 : {
172 : /* yields for 2 clock ticks every 1 tick to avoid blocking the main thread
173 : * when dumping huge data structures */
174 0 : f64 now = vlib_time_now (vm);
175 0 : if (now - *start > 11e-6)
176 : {
177 0 : vlib_process_suspend (vm, 21e-6);
178 0 : *start = vlib_time_now (vm);
179 0 : return 1;
180 : }
181 :
182 0 : return 0;
183 : }
184 :
185 : static u8 *
186 0 : format_ip_session_redirect (u8 *s, va_list *args)
187 : {
188 0 : const ip_session_redirect_main_t *im = &ip_session_redirect_main;
189 0 : const ip_session_redirect_t *ipr =
190 : va_arg (*args, const ip_session_redirect_t *);
191 0 : index_t ipri = ipr - im->pool;
192 0 : const char *type = ipr->is_punt ? "[punt]" : "[acl]";
193 0 : const char *ip = ipr->is_ip6 ? "[ip6]" : "[ip4]";
194 : s =
195 0 : format (s, "[%u] %s %s table %d key %U opaque_index 0x%x\n", ipri, type,
196 : ip, ipr->table_index, format_hex_bytes, ipr->match_and_table_index,
197 0 : vec_len (ipr->match_and_table_index) - 4, ipr->opaque_index);
198 0 : s = format (s, " via:\n");
199 0 : s = format (s, " %U", format_fib_path_list, ipr->pl, 2);
200 0 : s = format (s, " forwarding\n");
201 0 : s = format (s, " %U", format_dpo_id, &ipr->dpo, 0);
202 0 : return s;
203 : }
204 :
205 : static clib_error_t *
206 0 : ip_session_redirect_show_cmd (vlib_main_t *vm, unformat_input_t *main_input,
207 : vlib_cli_command_t *cmd)
208 : {
209 0 : ip_session_redirect_main_t *im = &ip_session_redirect_main;
210 0 : unformat_input_t _line_input, *line_input = &_line_input;
211 0 : vnet_classify_main_t *cm = &vnet_classify_main;
212 : ip_session_redirect_t *ipr;
213 0 : clib_error_t *error = 0;
214 0 : u32 table_index = ~0;
215 0 : int is_punt = -1;
216 0 : int is_ip6 = -1;
217 0 : u8 *match = 0;
218 0 : int max = 50;
219 0 : u8 *s = 0;
220 :
221 0 : if (unformat_is_eof (main_input))
222 0 : unformat_init (line_input, 0,
223 : 0); /* support straight "sh ip session redirect" */
224 0 : else if (!unformat_user (main_input, unformat_line_input, line_input))
225 0 : return 0;
226 :
227 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
228 : {
229 0 : if (unformat (line_input, "all"))
230 : ;
231 0 : else if (unformat (line_input, "punt"))
232 0 : is_punt = 1;
233 0 : else if (unformat (line_input, "acl"))
234 0 : is_punt = 0;
235 0 : else if (unformat (line_input, "ip4"))
236 0 : is_ip6 = 0;
237 0 : else if (unformat (line_input, "ip6"))
238 0 : is_ip6 = 1;
239 0 : else if (unformat (line_input, "table %u", &table_index))
240 : ;
241 0 : else if (unformat (line_input, "match %U", unformat_classify_match, cm,
242 : &match, table_index))
243 : ;
244 0 : else if (unformat (line_input, "max %d", &max))
245 : ;
246 : else
247 : {
248 0 : error = unformat_parse_error (line_input);
249 0 : goto out;
250 : }
251 : }
252 :
253 0 : if (match)
254 : {
255 0 : ipr = ip_session_redirect_find (im, table_index, match);
256 0 : if (!ipr)
257 0 : vlib_cli_output (vm, "none");
258 : else
259 0 : vlib_cli_output (vm, "%U", format_ip_session_redirect, ipr);
260 : }
261 : else
262 : {
263 0 : f64 start = vlib_time_now (vm);
264 0 : ip_session_redirect_t *iprs = im->pool;
265 0 : int n = 0;
266 0 : pool_foreach (ipr, iprs)
267 : {
268 0 : if (n >= max)
269 : {
270 0 : n = -1; /* signal overflow */
271 0 : break;
272 : }
273 0 : if ((~0 == table_index || ipr->table_index == table_index) &&
274 0 : (-1 == is_punt || ipr->is_punt == is_punt) &&
275 0 : (-1 == is_ip6 || ipr->is_ip6 == is_ip6))
276 : {
277 0 : s = format (s, "%U\n", format_ip_session_redirect, ipr);
278 0 : n++;
279 : }
280 0 : if (ip_session_redirect_show_yield (vm, &start))
281 : {
282 : /* we must reload the pool as it might have moved */
283 0 : u32 ii = ipr - iprs;
284 0 : iprs = im->pool;
285 0 : ipr = iprs + ii;
286 : }
287 : }
288 0 : vec_add1 (s, 0);
289 0 : vlib_cli_output (vm, (char *) s);
290 0 : vec_free (s);
291 0 : if (-1 == n)
292 : {
293 0 : vlib_cli_output (
294 : vm,
295 : "\nPlease note: only the first %d entries displayed. "
296 : "To display more, specify max.",
297 : max);
298 : }
299 : }
300 :
301 0 : out:
302 0 : vec_free (match);
303 0 : unformat_free (line_input);
304 0 : return error;
305 : }
306 :
307 175447 : VLIB_CLI_COMMAND (ip_session_redirect_show_command, static) = {
308 : .path = "show ip session redirect",
309 : .function = ip_session_redirect_show_cmd,
310 : .short_help = "show ip session redirect [all|[table <table-index>] "
311 : "[punt|acl] [ip4|ip6] [match]]",
312 : };
313 :
314 : static clib_error_t *
315 0 : ip_session_redirect_cmd (vlib_main_t *vm, unformat_input_t *main_input,
316 : vlib_cli_command_t *cmd)
317 : {
318 0 : unformat_input_t _line_input, *line_input = &_line_input;
319 0 : vnet_classify_main_t *cm = &vnet_classify_main;
320 0 : dpo_proto_t proto = DPO_PROTO_IP4;
321 0 : fib_route_path_t *rpaths = 0, rpath;
322 0 : clib_error_t *error = 0;
323 0 : u32 opaque_index = ~0;
324 0 : u32 table_index = ~0;
325 0 : int is_punt = 0;
326 0 : int is_add = 1;
327 0 : u8 *match = 0;
328 : int rv;
329 :
330 0 : if (!unformat_user (main_input, unformat_line_input, line_input))
331 0 : return 0;
332 :
333 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
334 : {
335 0 : if (unformat (line_input, "del"))
336 0 : is_add = 0;
337 0 : else if (unformat (line_input, "add"))
338 0 : is_add = 1;
339 0 : else if (unformat (line_input, "punt"))
340 0 : is_punt = 1;
341 0 : else if (unformat (line_input, "table %u", &table_index))
342 : ;
343 0 : else if (unformat (line_input, "opaque-index %u", &opaque_index))
344 : ;
345 0 : else if (unformat (line_input, "match %U", unformat_classify_match, cm,
346 : &match, table_index))
347 : ;
348 0 : else if (unformat (line_input, "via %U", unformat_fib_route_path, &rpath,
349 : &proto))
350 0 : vec_add1 (rpaths, rpath);
351 : else
352 : {
353 0 : error = unformat_parse_error (line_input);
354 0 : goto out;
355 : }
356 : }
357 :
358 0 : if (~0 == table_index || 0 == match)
359 : {
360 0 : error = clib_error_create ("missing table index or match");
361 0 : goto out;
362 : }
363 :
364 0 : if (is_add)
365 : {
366 0 : if (0 == rpaths)
367 : {
368 0 : error = clib_error_create ("missing path");
369 0 : goto out;
370 : }
371 0 : rv = ip_session_redirect_add (vm, table_index, opaque_index, proto,
372 : is_punt, match, rpaths);
373 : }
374 : else
375 : {
376 0 : rv = ip_session_redirect_del (vm, table_index, match);
377 : }
378 :
379 0 : if (rv)
380 0 : error = clib_error_create ("failed with error %d", rv);
381 :
382 0 : out:
383 0 : vec_free (rpaths);
384 0 : vec_free (match);
385 0 : unformat_free (line_input);
386 0 : return error;
387 : }
388 :
389 175447 : VLIB_CLI_COMMAND (ip_session_redirect_command, static) = {
390 : .path = "ip session redirect",
391 : .function = ip_session_redirect_cmd,
392 : .short_help = "ip session redirect [add] [punt] table <index> match <match> "
393 : "via <path> | del table <index> match <match>"
394 : };
395 :
396 : static fib_node_t *
397 0 : ip_session_redirect_get_node (fib_node_index_t index)
398 : {
399 0 : ip_session_redirect_main_t *im = &ip_session_redirect_main;
400 0 : ip_session_redirect_t *ipr = pool_elt_at_index (im->pool, index);
401 0 : return &ipr->node;
402 : }
403 :
404 : static ip_session_redirect_t *
405 0 : ip_session_redirect_get_from_node (fib_node_t *node)
406 : {
407 0 : return (
408 : ip_session_redirect_t *) (((char *) node) -
409 : STRUCT_OFFSET_OF (ip_session_redirect_t, node));
410 : }
411 :
412 : static void
413 0 : ip_session_redirect_last_lock_gone (fib_node_t *node)
414 : {
415 : /* the lifetime of the entry is managed by the table. */
416 0 : ASSERT (0);
417 0 : }
418 :
419 : /* A back walk has reached this entry */
420 : static fib_node_back_walk_rc_t
421 0 : ip_session_redirect_back_walk_notify (fib_node_t *node,
422 : fib_node_back_walk_ctx_t *ctx)
423 : {
424 : int rv;
425 0 : ip_session_redirect_t *ipr = ip_session_redirect_get_from_node (node);
426 0 : rv = ip_session_redirect_stack (ipr);
427 0 : ASSERT (0 == rv);
428 0 : if (rv)
429 0 : clib_warning ("ip_session_redirect_stack() error %d", rv);
430 0 : return FIB_NODE_BACK_WALK_CONTINUE;
431 : }
432 :
433 : static const fib_node_vft_t ip_session_redirect_vft = {
434 : .fnv_get = ip_session_redirect_get_node,
435 : .fnv_last_lock = ip_session_redirect_last_lock_gone,
436 : .fnv_back_walk = ip_session_redirect_back_walk_notify,
437 : };
438 :
439 : static clib_error_t *
440 559 : ip_session_redirect_init (vlib_main_t *vm)
441 : {
442 559 : ip_session_redirect_main_t *im = &ip_session_redirect_main;
443 559 : im->session_by_match_and_table_index =
444 559 : hash_create_vec (0, sizeof (u8), sizeof (u32));
445 559 : im->fib_node_type = fib_node_register_new_type ("ip-session-redirect",
446 : &ip_session_redirect_vft);
447 559 : return 0;
448 : }
449 :
450 1119 : VLIB_INIT_FUNCTION (ip_session_redirect_init);
451 :
452 : VLIB_PLUGIN_REGISTER () = {
453 : .version = VPP_BUILD_VER,
454 : .description = "IP session redirect",
455 : };
456 :
457 : /*
458 : * fd.io coding-style-patch-verification: ON
459 : *
460 : * Local Variables:
461 : * eval: (c-set-style "gnu")
462 : * End:
463 : */
|