summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorBenoît Ganne <bganne@cisco.com>2021-08-10 16:23:36 +0200
committerDave Wallace <dwallacelf@gmail.com>2023-05-16 13:29:04 +0000
commit83e73709c33ca4188a04f355ccb13bde13d63271 (patch)
treebc57ceed6079975631f6054c83914e4289d8c823 /src/plugins
parent5b55526da489def814a239a3ae6599fcb30dbb6e (diff)
ip_session_redirect: add session redirect plugin
This feature enables the use of the classifier and ip-in-out-acl nodes to redirect matching sessions via arbitrary fib paths instead of relying on additional VRFs. Type: feature Change-Id: Ia59d35481c2555aec96c806b62bf29671abb295a Signed-off-by: Benoît Ganne <bganne@cisco.com>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/ip_session_redirect/CMakeLists.txt24
-rw-r--r--src/plugins/ip_session_redirect/FEATURE.yaml9
-rw-r--r--src/plugins/ip_session_redirect/api.c124
-rw-r--r--src/plugins/ip_session_redirect/ip_session_redirect.api106
-rw-r--r--src/plugins/ip_session_redirect/ip_session_redirect.h33
-rw-r--r--src/plugins/ip_session_redirect/ip_session_redirect_doc.rst42
-rw-r--r--src/plugins/ip_session_redirect/punt_redirect.vpp48
-rw-r--r--src/plugins/ip_session_redirect/redirect.c463
-rw-r--r--src/plugins/ip_session_redirect/test_api.c195
9 files changed, 1044 insertions, 0 deletions
diff --git a/src/plugins/ip_session_redirect/CMakeLists.txt b/src/plugins/ip_session_redirect/CMakeLists.txt
new file mode 100644
index 00000000000..f77500fc9fc
--- /dev/null
+++ b/src/plugins/ip_session_redirect/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright (c) 2021-2022 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_vpp_plugin(ip_session_redirect
+ SOURCES
+ api.c
+ redirect.c
+
+ API_FILES
+ ip_session_redirect.api
+
+ API_TEST_SOURCES
+ test_api.c
+)
diff --git a/src/plugins/ip_session_redirect/FEATURE.yaml b/src/plugins/ip_session_redirect/FEATURE.yaml
new file mode 100644
index 00000000000..d5cca4673d6
--- /dev/null
+++ b/src/plugins/ip_session_redirect/FEATURE.yaml
@@ -0,0 +1,9 @@
+---
+name: IP session redirect
+maintainer: Benoît Ganne <bganne@cisco.com>
+features:
+ - use the classifier ACL infrastructure to redirect sessions via arbitrary
+ fib paths
+description: "IP session redirect plugin"
+state: experimental
+properties: [CLI, STATS, MULTITHREAD, API]
diff --git a/src/plugins/ip_session_redirect/api.c b/src/plugins/ip_session_redirect/api.c
new file mode 100644
index 00000000000..1d17d55b5b4
--- /dev/null
+++ b/src/plugins/ip_session_redirect/api.c
@@ -0,0 +1,124 @@
+/* Copyright (c) 2021-2022 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#include <vlib/vlib.h>
+#include <vnet/fib/fib_api.h>
+#include <vnet/ip/ip_format_fns.h>
+#include <vlibmemory/api.h>
+#include <vlibapi/api.h>
+
+#define REPLY_MSG_ID_BASE vl_api_ip_sesion_redirect_msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+#include "ip_session_redirect.api_enum.h"
+#include "ip_session_redirect.api_types.h"
+
+#include "ip_session_redirect.h"
+
+static u16 vl_api_ip_sesion_redirect_msg_id_base;
+
+static int
+vl_api_ip_session_redirect_add (u32 table_index, u32 opaque_index,
+ vl_api_fib_path_nh_proto_t proto, int is_punt,
+ u8 *match, int match_len,
+ vl_api_fib_path_t *paths, int n_paths)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ fib_route_path_t *paths_ = 0;
+ dpo_proto_t proto_;
+ u8 *match_ = 0;
+ int rv = 0;
+
+ if (n_paths <= 0)
+ {
+ rv = VNET_API_ERROR_NO_PATHS_IN_ROUTE;
+ goto err0;
+ }
+
+ for (int i = 0; i < n_paths; i++)
+ {
+ fib_route_path_t path;
+ if ((rv = fib_api_path_decode (&paths[i], &path)))
+ goto err1;
+ vec_add1 (paths_, path);
+ }
+
+ if (~0 == proto)
+ proto_ = paths_[0].frp_proto;
+ else
+ fib_api_path_nh_proto_to_dpo (ntohl (proto), &proto_);
+
+ vec_add (match_, match, match_len);
+ rv = ip_session_redirect_add (vm, ntohl (table_index), ntohl (opaque_index),
+ proto_, is_punt, match_, paths_);
+ vec_free (match_);
+
+err1:
+ vec_free (paths_);
+err0:
+ return rv;
+}
+
+static void
+vl_api_ip_session_redirect_add_t_handler (vl_api_ip_session_redirect_add_t *mp)
+{
+ vl_api_ip_session_redirect_add_reply_t *rmp;
+ int rv = vl_api_ip_session_redirect_add (
+ mp->table_index, mp->opaque_index, ~0 /* proto */, mp->is_punt, mp->match,
+ mp->match_len, mp->paths, mp->n_paths);
+ REPLY_MACRO (VL_API_IP_SESSION_REDIRECT_ADD_REPLY)
+}
+
+static void
+vl_api_ip_session_redirect_add_v2_t_handler (
+ vl_api_ip_session_redirect_add_v2_t *mp)
+{
+ vl_api_ip_session_redirect_add_v2_reply_t *rmp;
+ int rv = vl_api_ip_session_redirect_add (
+ mp->table_index, mp->opaque_index, mp->proto, mp->is_punt, mp->match,
+ mp->match_len, mp->paths, mp->n_paths);
+ REPLY_MACRO (VL_API_IP_SESSION_REDIRECT_ADD_V2_REPLY)
+}
+
+static void
+vl_api_ip_session_redirect_del_t_handler (vl_api_ip_session_redirect_del_t *mp)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ vl_api_ip_session_redirect_del_reply_t *rmp;
+ u8 *match = 0;
+ int rv;
+
+ vec_add (match, mp->match, mp->match_len);
+ rv = ip_session_redirect_del (vm, ntohl (mp->table_index), match);
+ vec_free (match);
+
+ REPLY_MACRO (VL_API_IP_SESSION_REDIRECT_DEL_REPLY);
+}
+
+#include "ip_session_redirect.api.c"
+static clib_error_t *
+ip_session_redirect_plugin_api_hookup (vlib_main_t *vm)
+{
+ vl_api_ip_sesion_redirect_msg_id_base = setup_message_id_table ();
+ return 0;
+}
+
+VLIB_API_INIT_FUNCTION (ip_session_redirect_plugin_api_hookup);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ip_session_redirect/ip_session_redirect.api b/src/plugins/ip_session_redirect/ip_session_redirect.api
new file mode 100644
index 00000000000..2bf2373dbd2
--- /dev/null
+++ b/src/plugins/ip_session_redirect/ip_session_redirect.api
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2021-2022 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+option version = "0.3.0";
+import "vnet/interface_types.api";
+import "vnet/fib/fib_types.api";
+
+/** \brief Add or update a session redirection
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param table_index - classifier table index
+ @param opaque_index - classifier session opaque index
+ @param match_len - classifier session match length in bytes (max is 80-bytes)
+ @param match - classifier session match
+ @param is_punt - true = punted traffic, false = forwarded traffic
+ @param n_paths - number of paths
+ @param paths - the paths of the redirect
+*/
+
+autoreply define ip_session_redirect_add
+{
+ option deprecated;
+ u32 client_index;
+ u32 context;
+
+ u32 table_index;
+ u8 match_len;
+ u8 match[80];
+ u32 opaque_index [default=0xffffffff];
+ bool is_punt;
+ u8 n_paths;
+ vl_api_fib_path_t paths[n_paths];
+
+ option vat_help = "table <index> match <match> via <path>";
+ option status="in_progress";
+};
+
+/** \brief Add or update a session redirection - version 2
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param table_index - classifier table index
+ @param opaque_index - classifier session opaque index
+ @param proto - protocol of forwarded packets (default autodetect from path nh)
+ @param is_punt - true = punted traffic, false = forwarded traffic
+ @param match_len - classifier session match length in bytes (max is 80-bytes)
+ @param match - classifier session match
+ @param n_paths - number of paths
+ @param paths - the paths of the redirect
+*/
+
+autoreply define ip_session_redirect_add_v2
+{
+ u32 client_index;
+ u32 context;
+
+ u32 table_index;
+ u32 opaque_index [default=0xffffffff];
+ vl_api_fib_path_nh_proto_t proto [default=0xffffffff];
+ bool is_punt;
+ u8 match_len;
+ u8 match[80];
+ u8 n_paths;
+ vl_api_fib_path_t paths[n_paths];
+
+ option vat_help = "table <index> match <match> via <path>";
+ option status="in_progress";
+};
+
+/** \brief Delete a session redirection
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param table_index - classifier table index
+ @param match_len - classifier session match length in bytes (max is 80-bytes)
+ @param match - classifier session match
+*/
+
+autoreply define ip_session_redirect_del
+{
+ u32 client_index;
+ u32 context;
+
+ u32 table_index;
+ u8 match_len;
+ u8 match[match_len];
+
+ option vat_help = "session-index <index> table <index> match <match>";
+ option status="in_progress";
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ip_session_redirect/ip_session_redirect.h b/src/plugins/ip_session_redirect/ip_session_redirect.h
new file mode 100644
index 00000000000..45f64eebba1
--- /dev/null
+++ b/src/plugins/ip_session_redirect/ip_session_redirect.h
@@ -0,0 +1,33 @@
+/* Copyright (c) 2021-2022 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#ifndef IP_SESSION_REDIRECT_H_
+#define IP_SESSION_REDIRECT_H_
+
+#include <vnet/fib/fib_node.h>
+
+int ip_session_redirect_add (vlib_main_t *vm, u32 table_index,
+ u32 opaque_index, dpo_proto_t proto, int is_punt,
+ const u8 *match, const fib_route_path_t *rpaths);
+int ip_session_redirect_del (vlib_main_t *vm, u32 table_index,
+ const u8 *match);
+
+#endif /* IP_SESSION_REDIRECT_H_ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ip_session_redirect/ip_session_redirect_doc.rst b/src/plugins/ip_session_redirect/ip_session_redirect_doc.rst
new file mode 100644
index 00000000000..aad87166f8f
--- /dev/null
+++ b/src/plugins/ip_session_redirect/ip_session_redirect_doc.rst
@@ -0,0 +1,42 @@
+IP session redirect
+===================
+
+This plugin allows to steer packet via different paths based on the
+classifier.
+It leverages the VPP classifier ACL infrastructure (classifier, in_out_acl
+etc), extending its capabilities to redirect traffic without having to
+resort on additional VRFs.
+It also allows to steer punted packets using the same mechanism.
+
+Maturity level
+--------------
+
+Under development: it should work, but has not been thoroughly tested.
+
+Features
+--------
+
+- steer regular or/and punt traffic using the classifier
+- API
+
+Quickstart
+----------
+
+1. configure punting
+
+::
+
+ ~# vppctl set punt ipv4 udp all
+
+2. create the classifier table and uses it for punt ACL
+
+::
+
+ ~# vppctl classify table miss-next drop mask l3 ip4 src l4 udp src_port buckets 100000
+ ~# vppctl set interface input acl intfc local0 ip4-punt-table 0
+
+3. add session to steer punted packets
+
+::
+
+ ~# vppctl ip session redirect table 0 match l3 ip4 src 10.10.10.10 l4 src_port 1234 via 10.10.0.10 pg1
diff --git a/src/plugins/ip_session_redirect/punt_redirect.vpp b/src/plugins/ip_session_redirect/punt_redirect.vpp
new file mode 100644
index 00000000000..e3594cd71d9
--- /dev/null
+++ b/src/plugins/ip_session_redirect/punt_redirect.vpp
@@ -0,0 +1,48 @@
+create packet-generator interface pg0
+set int ip addr pg0 10.10.10.1/24
+
+create packet-generator interface pg1
+set int ip addr pg1 10.10.0.1/24
+set ip neighbor pg1 10.10.0.10 4.5.6
+
+set punt ipv4 udp all
+
+classify table miss-next drop mask l3 ip4 src l4 udp src_port buckets 100000
+set interface input acl intfc local0 ip4-punt-table 0
+ip session redirect punt table 0 match l3 ip4 src 10.10.10.10 l4 src_port 1234 via 10.10.0.10 pg1
+
+set int st pg0 up
+set int st pg1 up
+
+comment { punt because of no udp listener for 53667, redirected }
+packet-generator new { \
+ name ok \
+ limit 1 \
+ node ethernet-input \
+ source pg0 \
+ size 100-100 \
+ data { \
+ IP4: 5.6.7 -> 2.3.4 \
+ UDP: 10.10.10.10 -> 10.10.10.1 \
+ UDP: 1234 -> 53667 \
+ incrementing 1 \
+ } \
+}
+
+comment { punt because of no udp listener for 53668, dropped }
+packet-generator new { \
+ name nok \
+ limit 1 \
+ node ethernet-input \
+ source pg0 \
+ size 100-100 \
+ data { \
+ IP4: 5.6.7 -> 2.3.4 \
+ UDP: 10.10.10.10 -> 10.10.10.1 \
+ UDP: 1235 -> 53668 \
+ incrementing 1 \
+ } \
+}
+
+trace add pg-input 10
+pa en
diff --git a/src/plugins/ip_session_redirect/redirect.c b/src/plugins/ip_session_redirect/redirect.c
new file mode 100644
index 00000000000..d925d12cde7
--- /dev/null
+++ b/src/plugins/ip_session_redirect/redirect.c
@@ -0,0 +1,463 @@
+/* Copyright (c) 2021-2022 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+#include <vlib/vlib.h>
+#include <vnet/fib/fib_path_list.h>
+#include <vnet/classify/vnet_classify.h>
+#include <vnet/classify/in_out_acl.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+#include "ip_session_redirect.h"
+
+typedef struct
+{
+ u8 *match_and_table_index;
+ dpo_id_t dpo; /* forwarding dpo */
+ fib_node_t node; /* linkage into the FIB graph */
+ fib_node_index_t pl;
+ u32 sibling;
+ u32 parent_node_index;
+ u32 opaque_index;
+ u32 table_index;
+ fib_forward_chain_type_t payload_type;
+ u8 is_punt : 1;
+ u8 is_ip6 : 1;
+} ip_session_redirect_t;
+
+typedef struct
+{
+ ip_session_redirect_t *pool;
+ u32 *session_by_match_and_table_index;
+ fib_node_type_t fib_node_type;
+} ip_session_redirect_main_t;
+
+static ip_session_redirect_main_t ip_session_redirect_main;
+
+static int
+ip_session_redirect_stack (ip_session_redirect_t *ipr)
+{
+ dpo_id_t dpo = DPO_INVALID;
+
+ fib_path_list_contribute_forwarding (ipr->pl, ipr->payload_type,
+ fib_path_list_is_popular (ipr->pl) ?
+ FIB_PATH_LIST_FWD_FLAG_NONE :
+ FIB_PATH_LIST_FWD_FLAG_COLLAPSE,
+ &dpo);
+ dpo_stack_from_node (ipr->parent_node_index, &ipr->dpo, &dpo);
+ dpo_reset (&dpo);
+
+ /* update session with new next_index */
+ return vnet_classify_add_del_session (
+ &vnet_classify_main, ipr->table_index, ipr->match_and_table_index,
+ ipr->dpo.dpoi_next_node /* hit_next_index */, ipr->opaque_index,
+ 0 /* advance */, CLASSIFY_ACTION_SET_METADATA,
+ ipr->dpo.dpoi_index /* metadata */, 1 /* is_add */);
+}
+
+static ip_session_redirect_t *
+ip_session_redirect_find (ip_session_redirect_main_t *im, u32 table_index,
+ const u8 *match)
+{
+ /* we are adding the table index at the end of the match string so we
+ * can disambiguiate identical matches in different tables in
+ * im->session_by_match_and_table_index */
+ u8 *match_and_table_index = vec_dup (match);
+ vec_add (match_and_table_index, (void *) &table_index, 4);
+ uword *p =
+ hash_get_mem (im->session_by_match_and_table_index, match_and_table_index);
+ vec_free (match_and_table_index);
+ if (!p)
+ return 0;
+ return pool_elt_at_index (im->pool, p[0]);
+}
+
+int
+ip_session_redirect_add (vlib_main_t *vm, u32 table_index, u32 opaque_index,
+ dpo_proto_t proto, int is_punt, const u8 *match,
+ const fib_route_path_t *rpaths)
+{
+ ip_session_redirect_main_t *im = &ip_session_redirect_main;
+ fib_forward_chain_type_t payload_type;
+ ip_session_redirect_t *ipr;
+ const char *pname;
+
+ payload_type = fib_forw_chain_type_from_dpo_proto (proto);
+ switch (payload_type)
+ {
+ case FIB_FORW_CHAIN_TYPE_UNICAST_IP4:
+ pname = is_punt ? "ip4-punt-acl" : "ip4-inacl";
+ break;
+ case FIB_FORW_CHAIN_TYPE_UNICAST_IP6:
+ pname = is_punt ? "ip6-punt-acl" : "ip6-inacl";
+ break;
+ default:
+ return VNET_API_ERROR_INVALID_ADDRESS_FAMILY;
+ }
+
+ ipr = ip_session_redirect_find (im, table_index, match);
+ if (ipr)
+ {
+ /* update to an existing session */
+ fib_path_list_child_remove (ipr->pl, ipr->sibling);
+ dpo_reset (&ipr->dpo);
+ }
+ else
+ {
+ /* allocate a new entry */
+ pool_get (im->pool, ipr);
+ fib_node_init (&ipr->node, im->fib_node_type);
+ ipr->match_and_table_index = vec_dup ((u8 *) match);
+ /* we are adding the table index at the end of the match string so we
+ * can disambiguiate identical matches in different tables in
+ * im->session_by_match_and_table_index */
+ vec_add (ipr->match_and_table_index, (void *) &table_index, 4);
+ ipr->table_index = table_index;
+ hash_set_mem (im->session_by_match_and_table_index,
+ ipr->match_and_table_index, ipr - im->pool);
+ }
+
+ ipr->payload_type = payload_type;
+ ipr->pl = fib_path_list_create (
+ FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF, rpaths);
+ ipr->sibling =
+ fib_path_list_child_add (ipr->pl, im->fib_node_type, ipr - im->pool);
+ ipr->parent_node_index = vlib_get_node_by_name (vm, (u8 *) pname)->index;
+ ipr->opaque_index = opaque_index;
+ ipr->is_punt = is_punt;
+ ipr->is_ip6 = payload_type == FIB_FORW_CHAIN_TYPE_UNICAST_IP6;
+
+ return ip_session_redirect_stack (ipr);
+}
+
+int
+ip_session_redirect_del (vlib_main_t *vm, u32 table_index, const u8 *match)
+{
+ ip_session_redirect_main_t *im = &ip_session_redirect_main;
+ vnet_classify_main_t *cm = &vnet_classify_main;
+ ip_session_redirect_t *ipr;
+ int rv;
+
+ ipr = ip_session_redirect_find (im, table_index, match);
+ if (!ipr)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ rv = vnet_classify_add_del_session (
+ cm, ipr->table_index, ipr->match_and_table_index, 0 /* hit_next_index */,
+ 0 /* opaque_index */, 0 /* advance */, 0 /* action */, 0 /* metadata */,
+ 0 /* is_add */);
+ if (rv)
+ return rv;
+
+ hash_unset_mem (im->session_by_match_and_table_index,
+ ipr->match_and_table_index);
+ vec_free (ipr->match_and_table_index);
+ fib_path_list_child_remove (ipr->pl, ipr->sibling);
+ dpo_reset (&ipr->dpo);
+ pool_put (im->pool, ipr);
+ return 0;
+}
+
+static int
+ip_session_redirect_show_yield (vlib_main_t *vm, f64 *start)
+{
+ /* yields for 2 clock ticks every 1 tick to avoid blocking the main thread
+ * when dumping huge data structures */
+ f64 now = vlib_time_now (vm);
+ if (now - *start > 11e-6)
+ {
+ vlib_process_suspend (vm, 21e-6);
+ *start = vlib_time_now (vm);
+ return 1;
+ }
+
+ return 0;
+}
+
+static u8 *
+format_ip_session_redirect (u8 *s, va_list *args)
+{
+ const ip_session_redirect_main_t *im = &ip_session_redirect_main;
+ const ip_session_redirect_t *ipr =
+ va_arg (*args, const ip_session_redirect_t *);
+ index_t ipri = ipr - im->pool;
+ const char *type = ipr->is_punt ? "[punt]" : "[acl]";
+ const char *ip = ipr->is_ip6 ? "[ip6]" : "[ip4]";
+ s =
+ format (s, "[%u] %s %s table %d key %U opaque_index 0x%x\n", ipri, type,
+ ip, ipr->table_index, format_hex_bytes, ipr->match_and_table_index,
+ vec_len (ipr->match_and_table_index) - 4, ipr->opaque_index);
+ s = format (s, " via:\n");
+ s = format (s, " %U", format_fib_path_list, ipr->pl, 2);
+ s = format (s, " forwarding\n");
+ s = format (s, " %U", format_dpo_id, &ipr->dpo, 0);
+ return s;
+}
+
+static clib_error_t *
+ip_session_redirect_show_cmd (vlib_main_t *vm, unformat_input_t *main_input,
+ vlib_cli_command_t *cmd)
+{
+ ip_session_redirect_main_t *im = &ip_session_redirect_main;
+ unformat_input_t _line_input, *line_input = &_line_input;
+ vnet_classify_main_t *cm = &vnet_classify_main;
+ ip_session_redirect_t *ipr;
+ clib_error_t *error = 0;
+ u32 table_index = ~0;
+ int is_punt = -1;
+ int is_ip6 = -1;
+ u8 *match = 0;
+ int max = 50;
+ u8 *s = 0;
+
+ if (unformat_is_eof (main_input))
+ unformat_init (line_input, 0,
+ 0); /* support straight "sh ip session redirect" */
+ else if (!unformat_user (main_input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "all"))
+ ;
+ else if (unformat (line_input, "punt"))
+ is_punt = 1;
+ else if (unformat (line_input, "acl"))
+ is_punt = 0;
+ else if (unformat (line_input, "ip4"))
+ is_ip6 = 0;
+ else if (unformat (line_input, "ip6"))
+ is_ip6 = 1;
+ else if (unformat (line_input, "table %u", &table_index))
+ ;
+ else if (unformat (line_input, "match %U", unformat_classify_match, cm,
+ &match, table_index))
+ ;
+ else if (unformat (line_input, "max %d", &max))
+ ;
+ else
+ {
+ error = unformat_parse_error (line_input);
+ goto out;
+ }
+ }
+
+ if (match)
+ {
+ ipr = ip_session_redirect_find (im, table_index, match);
+ if (!ipr)
+ vlib_cli_output (vm, "none");
+ else
+ vlib_cli_output (vm, "%U", format_ip_session_redirect, ipr);
+ }
+ else
+ {
+ f64 start = vlib_time_now (vm);
+ ip_session_redirect_t *iprs = im->pool;
+ int n = 0;
+ pool_foreach (ipr, iprs)
+ {
+ if (n >= max)
+ {
+ n = -1; /* signal overflow */
+ break;
+ }
+ if ((~0 == table_index || ipr->table_index == table_index) &&
+ (-1 == is_punt || ipr->is_punt == is_punt) &&
+ (-1 == is_ip6 || ipr->is_ip6 == is_ip6))
+ {
+ s = format (s, "%U\n", format_ip_session_redirect, ipr);
+ n++;
+ }
+ if (ip_session_redirect_show_yield (vm, &start))
+ {
+ /* we must reload the pool as it might have moved */
+ u32 ii = ipr - iprs;
+ iprs = im->pool;
+ ipr = iprs + ii;
+ }
+ }
+ vec_add1 (s, 0);
+ vlib_cli_output (vm, (char *) s);
+ vec_free (s);
+ if (-1 == n)
+ {
+ vlib_cli_output (
+ vm,
+ "\nPlease note: only the first %d entries displayed. "
+ "To display more, specify max.",
+ max);
+ }
+ }
+
+out:
+ vec_free (match);
+ unformat_free (line_input);
+ return error;
+}
+
+VLIB_CLI_COMMAND (ip_session_redirect_show_command, static) = {
+ .path = "show ip session redirect",
+ .function = ip_session_redirect_show_cmd,
+ .short_help = "show ip session redirect [all|[table <table-index>] "
+ "[punt|acl] [ip4|ip6] [match]]",
+};
+
+static clib_error_t *
+ip_session_redirect_cmd (vlib_main_t *vm, unformat_input_t *main_input,
+ vlib_cli_command_t *cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ vnet_classify_main_t *cm = &vnet_classify_main;
+ dpo_proto_t proto = DPO_PROTO_IP4;
+ fib_route_path_t *rpaths = 0, rpath;
+ clib_error_t *error = 0;
+ u32 opaque_index = ~0;
+ u32 table_index = ~0;
+ int is_punt = 0;
+ int is_add = 1;
+ u8 *match = 0;
+ int rv;
+
+ if (!unformat_user (main_input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "del"))
+ is_add = 0;
+ else if (unformat (line_input, "add"))
+ is_add = 1;
+ else if (unformat (line_input, "punt"))
+ is_punt = 1;
+ else if (unformat (line_input, "table %u", &table_index))
+ ;
+ else if (unformat (line_input, "opaque-index %u", &opaque_index))
+ ;
+ else if (unformat (line_input, "match %U", unformat_classify_match, cm,
+ &match, table_index))
+ ;
+ else if (unformat (line_input, "via %U", unformat_fib_route_path, &rpath,
+ &proto))
+ vec_add1 (rpaths, rpath);
+ else
+ {
+ error = unformat_parse_error (line_input);
+ goto out;
+ }
+ }
+
+ if (~0 == table_index || 0 == match)
+ {
+ error = clib_error_create ("missing table index or match");
+ goto out;
+ }
+
+ if (is_add)
+ {
+ if (0 == rpaths)
+ {
+ error = clib_error_create ("missing path");
+ goto out;
+ }
+ rv = ip_session_redirect_add (vm, table_index, opaque_index, proto,
+ is_punt, match, rpaths);
+ }
+ else
+ {
+ rv = ip_session_redirect_del (vm, table_index, match);
+ }
+
+ if (rv)
+ error = clib_error_create ("failed with error %d", rv);
+
+out:
+ vec_free (rpaths);
+ vec_free (match);
+ unformat_free (line_input);
+ return error;
+}
+
+VLIB_CLI_COMMAND (ip_session_redirect_command, static) = {
+ .path = "ip session redirect",
+ .function = ip_session_redirect_cmd,
+ .short_help = "ip session redirect [add] [punt] table <index> match <match> "
+ "via <path> | del table <index> match <match>"
+};
+
+static fib_node_t *
+ip_session_redirect_get_node (fib_node_index_t index)
+{
+ ip_session_redirect_main_t *im = &ip_session_redirect_main;
+ ip_session_redirect_t *ipr = pool_elt_at_index (im->pool, index);
+ return &ipr->node;
+}
+
+static ip_session_redirect_t *
+ip_session_redirect_get_from_node (fib_node_t *node)
+{
+ return (
+ ip_session_redirect_t *) (((char *) node) -
+ STRUCT_OFFSET_OF (ip_session_redirect_t, node));
+}
+
+static void
+ip_session_redirect_last_lock_gone (fib_node_t *node)
+{
+ /* the lifetime of the entry is managed by the table. */
+ ASSERT (0);
+}
+
+/* A back walk has reached this entry */
+static fib_node_back_walk_rc_t
+ip_session_redirect_back_walk_notify (fib_node_t *node,
+ fib_node_back_walk_ctx_t *ctx)
+{
+ int rv;
+ ip_session_redirect_t *ipr = ip_session_redirect_get_from_node (node);
+ rv = ip_session_redirect_stack (ipr);
+ ASSERT (0 == rv);
+ if (rv)
+ clib_warning ("ip_session_redirect_stack() error %d", rv);
+ return FIB_NODE_BACK_WALK_CONTINUE;
+}
+
+static const fib_node_vft_t ip_session_redirect_vft = {
+ .fnv_get = ip_session_redirect_get_node,
+ .fnv_last_lock = ip_session_redirect_last_lock_gone,
+ .fnv_back_walk = ip_session_redirect_back_walk_notify,
+};
+
+static clib_error_t *
+ip_session_redirect_init (vlib_main_t *vm)
+{
+ ip_session_redirect_main_t *im = &ip_session_redirect_main;
+ im->session_by_match_and_table_index =
+ hash_create_vec (0, sizeof (u8), sizeof (u32));
+ im->fib_node_type = fib_node_register_new_type ("ip-session-redirect",
+ &ip_session_redirect_vft);
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (ip_session_redirect_init);
+
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "IP session redirect",
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ip_session_redirect/test_api.c b/src/plugins/ip_session_redirect/test_api.c
new file mode 100644
index 00000000000..e4026a673ff
--- /dev/null
+++ b/src/plugins/ip_session_redirect/test_api.c
@@ -0,0 +1,195 @@
+/* Copyright (c) 2021-2022 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#include <vlib/vlib.h>
+#include <vnet/fib/fib_api.h>
+#include <vnet/ip/ip_format_fns.h>
+#include <vnet/classify/vnet_classify.h>
+#include <vat/vat.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#define __plugin_msg_base ip_session_redirect_test_main.msg_id_base
+#include <vlibapi/vat_helper_macros.h>
+/* declare message IDs */
+#include "ip_session_redirect.api_enum.h"
+#include "ip_session_redirect.api_types.h"
+#include "ip_session_redirect.h"
+
+typedef struct
+{
+ /* API message ID base */
+ u16 msg_id_base;
+ vat_main_t *vat_main;
+} ip_session_redirect_test_main_t;
+
+ip_session_redirect_test_main_t ip_session_redirect_test_main;
+
+static int
+api_ip_session_redirect_add_parse (vat_main_t *vam, u32 *table_index,
+ u32 *opaque_index, dpo_proto_t *proto,
+ int *is_punt, u8 **match,
+ fib_route_path_t **paths)
+{
+ vnet_classify_main_t *cm = &vnet_classify_main;
+ fib_route_path_t path;
+
+ *table_index = ~0;
+ *opaque_index = ~0;
+ *proto = DPO_PROTO_IP4;
+ *is_punt = 0;
+ *match = 0;
+ *paths = 0;
+
+ while (unformat_check_input (vam->input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (vam->input, "punt"))
+ *is_punt = 1;
+ else if (unformat (vam->input, "table %u", table_index))
+ ;
+ else if (unformat (vam->input, "opaque-index %u", opaque_index))
+ ;
+ else if (unformat (vam->input, "match %U", unformat_classify_match, cm,
+ match, *table_index))
+ ;
+ else if (unformat (vam->input, "via %U", unformat_fib_route_path, &path,
+ proto))
+ vec_add1 (*paths, path);
+ else
+ {
+ clib_warning ("unknown input `%U'", format_unformat_error,
+ vam->input);
+ return -99;
+ }
+ }
+
+ return 0;
+}
+
+static int
+api_ip_session_redirect_add (vat_main_t *vam)
+{
+ vl_api_ip_session_redirect_add_t *mp;
+ fib_route_path_t *paths;
+ dpo_proto_t proto;
+ u32 opaque_index;
+ u32 table_index;
+ int is_punt;
+ int ret, i;
+ u8 *match;
+
+ ret = api_ip_session_redirect_add_parse (vam, &table_index, &opaque_index,
+ &proto, &is_punt, &match, &paths);
+ if (ret)
+ goto err;
+
+ M2 (IP_SESSION_REDIRECT_ADD, mp, vec_len (paths) * sizeof (mp->paths[0]));
+
+ mp->table_index = htonl (table_index);
+ mp->opaque_index = htonl (opaque_index);
+ mp->is_punt = is_punt;
+ memcpy_s (mp->match, sizeof (mp->match), match, vec_len (match));
+ mp->n_paths = vec_len (paths);
+ vec_foreach_index (i, paths)
+ fib_api_path_encode (&paths[i], &mp->paths[i]);
+
+ S (mp);
+ W (ret);
+
+err:
+ vec_free (match);
+ vec_free (paths);
+ return ret;
+}
+
+static int
+api_ip_session_redirect_add_v2 (vat_main_t *vam)
+{
+ vl_api_ip_session_redirect_add_v2_t *mp;
+ fib_route_path_t *paths;
+ dpo_proto_t proto;
+ u32 opaque_index;
+ u32 table_index;
+ int is_punt;
+ int ret, i;
+ u8 *match;
+
+ ret = api_ip_session_redirect_add_parse (vam, &table_index, &opaque_index,
+ &proto, &is_punt, &match, &paths);
+ if (ret)
+ goto err;
+
+ M2 (IP_SESSION_REDIRECT_ADD_V2, mp, vec_len (paths) * sizeof (mp->paths[0]));
+
+ mp->table_index = htonl (table_index);
+ mp->opaque_index = htonl (opaque_index);
+ mp->proto = fib_api_path_dpo_proto_to_nh (proto);
+ mp->is_punt = is_punt;
+ memcpy_s (mp->match, sizeof (mp->match), match, vec_len (match));
+ mp->n_paths = vec_len (paths);
+ vec_foreach_index (i, paths)
+ fib_api_path_encode (&paths[i], &mp->paths[i]);
+
+ S (mp);
+ W (ret);
+
+err:
+ vec_free (match);
+ vec_free (paths);
+ return ret;
+}
+
+static int
+api_ip_session_redirect_del (vat_main_t *vam)
+{
+ vnet_classify_main_t *cm = &vnet_classify_main;
+ vl_api_ip_session_redirect_del_t *mp;
+ u32 table_index = ~0;
+ u8 *match = 0;
+ int ret;
+
+ while (unformat_check_input (vam->input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (vam->input, "table %u", &table_index))
+ ;
+ else if (unformat (vam->input, "match %U", unformat_classify_match, cm,
+ &match, table_index))
+ ;
+ else
+ {
+ clib_warning ("unknown input '%U'", format_unformat_error,
+ vam->input);
+ return -99;
+ }
+ }
+
+ M2 (IP_SESSION_REDIRECT_DEL, mp, vec_len (match));
+
+ mp->table_index = htonl (table_index);
+ mp->match_len = htonl (vec_len (match));
+ clib_memcpy (mp->match, match, vec_len (match));
+
+ S (mp);
+ W (ret);
+
+ return ret;
+}
+
+#include "ip_session_redirect.api_test.c"
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */