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:
6 * http://www.apache.org/licenses/LICENSE-2.0
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"
23 u8 *match_and_table_index;
24 dpo_id_t dpo; /* forwarding dpo */
25 fib_node_t node; /* linkage into the FIB graph */
28 u32 parent_node_index;
31 fib_forward_chain_type_t payload_type;
34 } ip_session_redirect_t;
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;
43 static ip_session_redirect_main_t ip_session_redirect_main;
46 ip_session_redirect_stack (ip_session_redirect_t *ipr)
48 dpo_id_t dpo = DPO_INVALID;
50 fib_path_list_contribute_forwarding (ipr->pl, ipr->payload_type,
51 fib_path_list_is_popular (ipr->pl) ?
52 FIB_PATH_LIST_FWD_FLAG_NONE :
53 FIB_PATH_LIST_FWD_FLAG_COLLAPSE,
55 dpo_stack_from_node (ipr->parent_node_index, &ipr->dpo, &dpo);
58 /* update session with new next_index */
59 return vnet_classify_add_del_session (
60 &vnet_classify_main, ipr->table_index, ipr->match_and_table_index,
61 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 */);
66 static ip_session_redirect_t *
67 ip_session_redirect_find (ip_session_redirect_main_t *im, u32 table_index,
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 u8 *match_and_table_index = vec_dup (match);
74 vec_add (match_and_table_index, (void *) &table_index, 4);
76 hash_get_mem (im->session_by_match_and_table_index, match_and_table_index);
77 vec_free (match_and_table_index);
80 return pool_elt_at_index (im->pool, p[0]);
84 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)
88 ip_session_redirect_main_t *im = &ip_session_redirect_main;
89 fib_forward_chain_type_t payload_type;
90 ip_session_redirect_t *ipr;
93 payload_type = fib_forw_chain_type_from_dpo_proto (proto);
96 case FIB_FORW_CHAIN_TYPE_UNICAST_IP4:
97 pname = is_punt ? "ip4-punt-acl" : "ip4-inacl";
99 case FIB_FORW_CHAIN_TYPE_UNICAST_IP6:
100 pname = is_punt ? "ip6-punt-acl" : "ip6-inacl";
103 return VNET_API_ERROR_INVALID_ADDRESS_FAMILY;
106 ipr = ip_session_redirect_find (im, table_index, match);
109 /* update to an existing session */
110 fib_path_list_child_remove (ipr->pl, ipr->sibling);
111 dpo_reset (&ipr->dpo);
115 /* allocate a new entry */
116 pool_get (im->pool, ipr);
117 fib_node_init (&ipr->node, im->fib_node_type);
118 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 vec_add (ipr->match_and_table_index, (void *) &table_index, 4);
123 ipr->table_index = table_index;
124 hash_set_mem (im->session_by_match_and_table_index,
125 ipr->match_and_table_index, ipr - im->pool);
128 ipr->payload_type = payload_type;
129 ipr->pl = fib_path_list_create (
130 FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF, rpaths);
132 fib_path_list_child_add (ipr->pl, im->fib_node_type, ipr - im->pool);
133 ipr->parent_node_index = vlib_get_node_by_name (vm, (u8 *) pname)->index;
134 ipr->opaque_index = opaque_index;
135 ipr->is_punt = is_punt;
136 ipr->is_ip6 = payload_type == FIB_FORW_CHAIN_TYPE_UNICAST_IP6;
138 return ip_session_redirect_stack (ipr);
142 ip_session_redirect_del (vlib_main_t *vm, u32 table_index, const u8 *match)
144 ip_session_redirect_main_t *im = &ip_session_redirect_main;
145 vnet_classify_main_t *cm = &vnet_classify_main;
146 ip_session_redirect_t *ipr;
149 ipr = ip_session_redirect_find (im, table_index, match);
151 return VNET_API_ERROR_NO_SUCH_ENTRY;
153 rv = vnet_classify_add_del_session (
154 cm, ipr->table_index, ipr->match_and_table_index, 0 /* hit_next_index */,
155 0 /* opaque_index */, 0 /* advance */, 0 /* action */, 0 /* metadata */,
160 hash_unset_mem (im->session_by_match_and_table_index,
161 ipr->match_and_table_index);
162 vec_free (ipr->match_and_table_index);
163 fib_path_list_child_remove (ipr->pl, ipr->sibling);
164 dpo_reset (&ipr->dpo);
165 pool_put (im->pool, ipr);
170 ip_session_redirect_show_yield (vlib_main_t *vm, f64 *start)
172 /* yields for 2 clock ticks every 1 tick to avoid blocking the main thread
173 * when dumping huge data structures */
174 f64 now = vlib_time_now (vm);
175 if (now - *start > 11e-6)
177 vlib_process_suspend (vm, 21e-6);
178 *start = vlib_time_now (vm);
186 format_ip_session_redirect (u8 *s, va_list *args)
188 const ip_session_redirect_main_t *im = &ip_session_redirect_main;
189 const ip_session_redirect_t *ipr =
190 va_arg (*args, const ip_session_redirect_t *);
191 index_t ipri = ipr - im->pool;
192 const char *type = ipr->is_punt ? "[punt]" : "[acl]";
193 const char *ip = ipr->is_ip6 ? "[ip6]" : "[ip4]";
195 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 vec_len (ipr->match_and_table_index) - 4, ipr->opaque_index);
198 s = format (s, " via:\n");
199 s = format (s, " %U", format_fib_path_list, ipr->pl, 2);
200 s = format (s, " forwarding\n");
201 s = format (s, " %U", format_dpo_id, &ipr->dpo, 0);
205 static clib_error_t *
206 ip_session_redirect_show_cmd (vlib_main_t *vm, unformat_input_t *main_input,
207 vlib_cli_command_t *cmd)
209 ip_session_redirect_main_t *im = &ip_session_redirect_main;
210 unformat_input_t _line_input, *line_input = &_line_input;
211 vnet_classify_main_t *cm = &vnet_classify_main;
212 ip_session_redirect_t *ipr;
213 clib_error_t *error = 0;
214 u32 table_index = ~0;
221 if (unformat_is_eof (main_input))
222 unformat_init (line_input, 0,
223 0); /* support straight "sh ip session redirect" */
224 else if (!unformat_user (main_input, unformat_line_input, line_input))
227 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
229 if (unformat (line_input, "all"))
231 else if (unformat (line_input, "punt"))
233 else if (unformat (line_input, "acl"))
235 else if (unformat (line_input, "ip4"))
237 else if (unformat (line_input, "ip6"))
239 else if (unformat (line_input, "table %u", &table_index))
241 else if (unformat (line_input, "match %U", unformat_classify_match, cm,
242 &match, table_index))
244 else if (unformat (line_input, "max %d", &max))
248 error = unformat_parse_error (line_input);
255 ipr = ip_session_redirect_find (im, table_index, match);
257 vlib_cli_output (vm, "none");
259 vlib_cli_output (vm, "%U", format_ip_session_redirect, ipr);
263 f64 start = vlib_time_now (vm);
264 ip_session_redirect_t *iprs = im->pool;
266 pool_foreach (ipr, iprs)
270 n = -1; /* signal overflow */
273 if ((~0 == table_index || ipr->table_index == table_index) &&
274 (-1 == is_punt || ipr->is_punt == is_punt) &&
275 (-1 == is_ip6 || ipr->is_ip6 == is_ip6))
277 s = format (s, "%U\n", format_ip_session_redirect, ipr);
280 if (ip_session_redirect_show_yield (vm, &start))
282 /* we must reload the pool as it might have moved */
289 vlib_cli_output (vm, (char *) s);
295 "\nPlease note: only the first %d entries displayed. "
296 "To display more, specify max.",
303 unformat_free (line_input);
307 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]]",
314 static clib_error_t *
315 ip_session_redirect_cmd (vlib_main_t *vm, unformat_input_t *main_input,
316 vlib_cli_command_t *cmd)
318 unformat_input_t _line_input, *line_input = &_line_input;
319 vnet_classify_main_t *cm = &vnet_classify_main;
320 dpo_proto_t proto = DPO_PROTO_IP4;
321 fib_route_path_t *rpaths = 0, rpath;
322 clib_error_t *error = 0;
323 u32 opaque_index = ~0;
324 u32 table_index = ~0;
330 if (!unformat_user (main_input, unformat_line_input, line_input))
333 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
335 if (unformat (line_input, "del"))
337 else if (unformat (line_input, "add"))
339 else if (unformat (line_input, "punt"))
341 else if (unformat (line_input, "table %u", &table_index))
343 else if (unformat (line_input, "opaque-index %u", &opaque_index))
345 else if (unformat (line_input, "match %U", unformat_classify_match, cm,
346 &match, table_index))
348 else if (unformat (line_input, "via %U", unformat_fib_route_path, &rpath,
350 vec_add1 (rpaths, rpath);
353 error = unformat_parse_error (line_input);
358 if (~0 == table_index || 0 == match)
360 error = clib_error_create ("missing table index or match");
368 error = clib_error_create ("missing path");
371 rv = ip_session_redirect_add (vm, table_index, opaque_index, proto,
372 is_punt, match, rpaths);
376 rv = ip_session_redirect_del (vm, table_index, match);
380 error = clib_error_create ("failed with error %d", rv);
385 unformat_free (line_input);
389 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>"
397 ip_session_redirect_get_node (fib_node_index_t index)
399 ip_session_redirect_main_t *im = &ip_session_redirect_main;
400 ip_session_redirect_t *ipr = pool_elt_at_index (im->pool, index);
404 static ip_session_redirect_t *
405 ip_session_redirect_get_from_node (fib_node_t *node)
408 ip_session_redirect_t *) (((char *) node) -
409 STRUCT_OFFSET_OF (ip_session_redirect_t, node));
413 ip_session_redirect_last_lock_gone (fib_node_t *node)
415 /* the lifetime of the entry is managed by the table. */
419 /* A back walk has reached this entry */
420 static fib_node_back_walk_rc_t
421 ip_session_redirect_back_walk_notify (fib_node_t *node,
422 fib_node_back_walk_ctx_t *ctx)
425 ip_session_redirect_t *ipr = ip_session_redirect_get_from_node (node);
426 rv = ip_session_redirect_stack (ipr);
429 clib_warning ("ip_session_redirect_stack() error %d", rv);
430 return FIB_NODE_BACK_WALK_CONTINUE;
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,
439 static clib_error_t *
440 ip_session_redirect_init (vlib_main_t *vm)
442 ip_session_redirect_main_t *im = &ip_session_redirect_main;
443 im->session_by_match_and_table_index =
444 hash_create_vec (0, sizeof (u8), sizeof (u32));
445 im->fib_node_type = fib_node_register_new_type ("ip-session-redirect",
446 &ip_session_redirect_vft);
450 VLIB_INIT_FUNCTION (ip_session_redirect_init);
452 VLIB_PLUGIN_REGISTER () = {
453 .version = VPP_BUILD_VER,
454 .description = "IP session redirect",
458 * fd.io coding-style-patch-verification: ON
461 * eval: (c-set-style "gnu")