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>
This commit is contained in:

committed by
Dave Wallace

parent
5b55526da4
commit
83e73709c3
@ -771,6 +771,11 @@ I: geneve
|
||||
M: community vpp-dev@lists.fd.io
|
||||
F: src/plugins/geneve/
|
||||
|
||||
Plugin - IP session redirect
|
||||
I: ip_session_redirect
|
||||
M: Benoît Ganne <bganne@cisco.com>
|
||||
F: src/plugins/ip_session_redirect/
|
||||
|
||||
Plugin - linux-cp
|
||||
I: linux-cp
|
||||
M: Neale Ranns <neale@graphiant.com>
|
||||
|
@ -40,3 +40,4 @@ For more on plugins please refer to :ref:`add_plugin`.
|
||||
acl_hash_lookup
|
||||
acl_lookup_context
|
||||
bufmon_doc
|
||||
ip_session_redirect_doc
|
||||
|
1
docs/developer/plugins/ip_session_redirect_doc.rst
Symbolic link
1
docs/developer/plugins/ip_session_redirect_doc.rst
Symbolic link
@ -0,0 +1 @@
|
||||
../../../src/plugins/ip_session_redirect/ip_session_redirect_doc.rst
|
24
src/plugins/ip_session_redirect/CMakeLists.txt
Normal file
24
src/plugins/ip_session_redirect/CMakeLists.txt
Normal file
@ -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
|
||||
)
|
9
src/plugins/ip_session_redirect/FEATURE.yaml
Normal file
9
src/plugins/ip_session_redirect/FEATURE.yaml
Normal file
@ -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]
|
124
src/plugins/ip_session_redirect/api.c
Normal file
124
src/plugins/ip_session_redirect/api.c
Normal file
@ -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:
|
||||
*/
|
106
src/plugins/ip_session_redirect/ip_session_redirect.api
Normal file
106
src/plugins/ip_session_redirect/ip_session_redirect.api
Normal file
@ -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:
|
||||
*/
|
33
src/plugins/ip_session_redirect/ip_session_redirect.h
Normal file
33
src/plugins/ip_session_redirect/ip_session_redirect.h
Normal file
@ -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:
|
||||
*/
|
42
src/plugins/ip_session_redirect/ip_session_redirect_doc.rst
Normal file
42
src/plugins/ip_session_redirect/ip_session_redirect_doc.rst
Normal file
@ -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
|
48
src/plugins/ip_session_redirect/punt_redirect.vpp
Normal file
48
src/plugins/ip_session_redirect/punt_redirect.vpp
Normal file
@ -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
|
463
src/plugins/ip_session_redirect/redirect.c
Normal file
463
src/plugins/ip_session_redirect/redirect.c
Normal file
File diff suppressed because it is too large
Load Diff
195
src/plugins/ip_session_redirect/test_api.c
Normal file
195
src/plugins/ip_session_redirect/test_api.c
Normal file
@ -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:
|
||||
*/
|
@ -69,7 +69,7 @@ fib_api_next_hop_decode (const vl_api_fib_path_t *in,
|
||||
*out = to_ip46 (FIB_API_PATH_NH_PROTO_IP6 == in->proto, (void *)&in->nh.address);
|
||||
}
|
||||
|
||||
static vl_api_fib_path_nh_proto_t
|
||||
vl_api_fib_path_nh_proto_t
|
||||
fib_api_path_dpo_proto_to_nh (dpo_proto_t dproto)
|
||||
{
|
||||
switch (dproto)
|
||||
@ -108,7 +108,7 @@ fib_api_next_hop_encode (const fib_route_path_t *rpath,
|
||||
sizeof (rpath->frp_addr.ip6));
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
fib_api_path_nh_proto_to_dpo (vl_api_fib_path_nh_proto_t pp,
|
||||
dpo_proto_t *dproto)
|
||||
{
|
||||
|
@ -29,6 +29,8 @@ struct _vl_api_fib_prefix;
|
||||
/**
|
||||
* Encode and decode functions from the API types to internal types
|
||||
*/
|
||||
extern vl_api_fib_path_nh_proto_t fib_api_path_dpo_proto_to_nh (dpo_proto_t dproto);
|
||||
extern int fib_api_path_nh_proto_to_dpo (vl_api_fib_path_nh_proto_t pp, dpo_proto_t *dproto);
|
||||
extern void fib_api_path_encode(const fib_route_path_t * api_rpath,
|
||||
vl_api_fib_path_t *out);
|
||||
extern int fib_api_path_decode(vl_api_fib_path_t *in,
|
||||
|
229
test/test_ip_session_redirect.py
Normal file
229
test/test_ip_session_redirect.py
Normal file
@ -0,0 +1,229 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import unittest
|
||||
|
||||
import socket
|
||||
|
||||
from scapy.packet import Raw
|
||||
from scapy.layers.l2 import Ether
|
||||
from scapy.layers.inet import IP, UDP
|
||||
from scapy.layers.inet6 import IPv6
|
||||
|
||||
from vpp_papi import VppEnum
|
||||
from vpp_ip_route import VppRoutePath
|
||||
|
||||
from framework import VppTestCase
|
||||
|
||||
|
||||
class TestIpSessionRedirect(VppTestCase):
|
||||
"""IP session redirect Test Case"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestIpSessionRedirect, cls).setUpClass()
|
||||
itfs = cls.create_pg_interfaces(range(3))
|
||||
for itf in itfs:
|
||||
itf.admin_up()
|
||||
itf.config_ip4()
|
||||
itf.resolve_arp()
|
||||
itf.config_ip6()
|
||||
itf.resolve_ndp()
|
||||
|
||||
def __build_mask(self, ip, src_port, match_n_vectors):
|
||||
# UDP: udp src port (2 bytes)
|
||||
udp = src_port.to_bytes(2, byteorder="big")
|
||||
match = ip + udp
|
||||
# skip the remainer
|
||||
match += b"\x00" * (match_n_vectors * 16 - len(match))
|
||||
return match
|
||||
|
||||
def build_mask4(self, proto, src_ip, src_port):
|
||||
proto = proto.to_bytes(1, byteorder="big")
|
||||
# IP: skip 9 bytes | proto (1 byte) | skip checksum (2 bytes) | src IP
|
||||
# (4 bytes) | skip dst IP (4 bytes)
|
||||
ip = b"\x00" * 9 + proto + b"\x00" * 2 + src_ip + b"\x00" * 4
|
||||
return self.__build_mask(ip, src_port, 2)
|
||||
|
||||
def build_mask6(self, proto, src_ip, src_port):
|
||||
nh = proto.to_bytes(1, byteorder="big")
|
||||
# IPv6: skip 6 bytes | nh (1 byte) | skip hl (1 byte) | src IP (16
|
||||
# bytes) | skip dst IP (16 bytes)
|
||||
ip = b"\x00" * 6 + nh + b"\x00" + src_ip + b"\x00" * 16
|
||||
return self.__build_mask(ip, src_port, 4)
|
||||
|
||||
def build_match(self, src_ip, src_port, is_ip6):
|
||||
if is_ip6:
|
||||
return self.build_mask6(
|
||||
0x11, socket.inet_pton(socket.AF_INET6, src_ip), src_port
|
||||
)
|
||||
else:
|
||||
return self.build_mask4(
|
||||
0x11, socket.inet_pton(socket.AF_INET, src_ip), src_port
|
||||
)
|
||||
|
||||
def create_table(self, is_ip6):
|
||||
if is_ip6:
|
||||
mask = self.build_mask6(0xFF, b"\xff" * 16, 0xFFFF)
|
||||
match_n_vectors = 4
|
||||
else:
|
||||
mask = self.build_mask4(0xFF, b"\xff" * 4, 0xFFFF)
|
||||
match_n_vectors = 2
|
||||
r = self.vapi.classify_add_del_table(
|
||||
is_add=True,
|
||||
match_n_vectors=match_n_vectors,
|
||||
miss_next_index=0, # drop
|
||||
current_data_flag=1, # match on current header (ip)
|
||||
mask_len=len(mask),
|
||||
mask=mask,
|
||||
)
|
||||
return r.new_table_index
|
||||
|
||||
def __test_redirect(self, sport, dport, is_punt, is_ip6):
|
||||
if is_ip6:
|
||||
af = VppEnum.vl_api_address_family_t.ADDRESS_IP6
|
||||
nh1 = self.pg1.remote_ip6
|
||||
nh2 = self.pg2.remote_ip6
|
||||
# note: nh3 is using a v4 adj to forward ipv6 packets
|
||||
nh3 = self.pg2.remote_ip4
|
||||
src = self.pg0.remote_ip6
|
||||
dst = self.pg0.local_ip6
|
||||
IP46 = IPv6
|
||||
proto = VppEnum.vl_api_fib_path_nh_proto_t.FIB_API_PATH_NH_PROTO_IP6
|
||||
else:
|
||||
af = VppEnum.vl_api_address_family_t.ADDRESS_IP4
|
||||
nh1 = self.pg1.remote_ip4
|
||||
nh2 = self.pg2.remote_ip4
|
||||
# note: nh3 is using a v6 adj to forward ipv4 packets
|
||||
nh3 = self.pg2.remote_ip6
|
||||
src = self.pg0.remote_ip4
|
||||
dst = self.pg0.local_ip4
|
||||
IP46 = IP
|
||||
proto = VppEnum.vl_api_fib_path_nh_proto_t.FIB_API_PATH_NH_PROTO_IP4
|
||||
|
||||
if is_punt:
|
||||
# punt udp packets to dport
|
||||
self.vapi.set_punt(
|
||||
is_add=1,
|
||||
punt={
|
||||
"type": VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4,
|
||||
"punt": {
|
||||
"l4": {
|
||||
"af": af,
|
||||
"protocol": VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP,
|
||||
"port": dport,
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
pkts = [
|
||||
(
|
||||
Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
|
||||
/ IP46(src=src, dst=dst)
|
||||
/ UDP(sport=sport, dport=dport)
|
||||
/ Raw("\x17" * 100)
|
||||
)
|
||||
] * 2
|
||||
|
||||
# create table and configure ACL
|
||||
table_index = self.create_table(is_ip6)
|
||||
ip4_tid, ip6_tid = (
|
||||
(0xFFFFFFFF, table_index) if is_ip6 else (table_index, 0xFFFFFFFF)
|
||||
)
|
||||
|
||||
if is_punt:
|
||||
self.vapi.punt_acl_add_del(
|
||||
is_add=1, ip4_table_index=ip4_tid, ip6_table_index=ip6_tid
|
||||
)
|
||||
else:
|
||||
self.vapi.input_acl_set_interface(
|
||||
is_add=1,
|
||||
ip4_table_index=ip4_tid,
|
||||
ip6_table_index=ip6_tid,
|
||||
l2_table_index=0xFFFFFFFF,
|
||||
sw_if_index=self.pg0.sw_if_index,
|
||||
)
|
||||
|
||||
# add a session redirect rule but not matching the stream: expect to
|
||||
# drop
|
||||
paths = [VppRoutePath(nh1, 0xFFFFFFFF).encode()]
|
||||
match1 = self.build_match(src, sport + 10, is_ip6)
|
||||
r = self.vapi.ip_session_redirect_add_v2(
|
||||
table_index=table_index,
|
||||
match_len=len(match1),
|
||||
match=match1,
|
||||
is_punt=is_punt,
|
||||
n_paths=1,
|
||||
paths=paths,
|
||||
)
|
||||
self.send_and_assert_no_replies(self.pg0, pkts)
|
||||
|
||||
# redirect a session matching the stream: expect to pass
|
||||
match2 = self.build_match(src, sport, is_ip6)
|
||||
self.vapi.ip_session_redirect_add_v2(
|
||||
table_index=table_index,
|
||||
match_len=len(match2),
|
||||
match=match2,
|
||||
is_punt=is_punt,
|
||||
n_paths=1,
|
||||
paths=paths,
|
||||
)
|
||||
self.send_and_expect_only(self.pg0, pkts, self.pg1)
|
||||
|
||||
# update the matching entry so it redirects to pg2
|
||||
# nh3 is using a v4 adj for v6 and vice-versa, hence we must specify
|
||||
# the payload proto with v2 api
|
||||
paths = [VppRoutePath(nh3, 0xFFFFFFFF).encode()]
|
||||
self.vapi.ip_session_redirect_add_v2(
|
||||
table_index=table_index,
|
||||
match_len=len(match2),
|
||||
match=match2,
|
||||
is_punt=is_punt,
|
||||
n_paths=1,
|
||||
paths=paths,
|
||||
proto=proto,
|
||||
)
|
||||
self.send_and_expect_only(self.pg0, pkts, self.pg2)
|
||||
|
||||
# we still have only 2 sessions, not 3
|
||||
t = self.vapi.classify_table_info(table_id=table_index)
|
||||
self.assertEqual(t.active_sessions, 2)
|
||||
|
||||
# cleanup
|
||||
self.vapi.ip_session_redirect_del(table_index, len(match2), match2)
|
||||
self.vapi.ip_session_redirect_del(table_index, len(match1), match1)
|
||||
t = self.vapi.classify_table_info(table_id=table_index)
|
||||
self.assertEqual(t.active_sessions, 0)
|
||||
|
||||
if is_punt:
|
||||
self.vapi.punt_acl_add_del(
|
||||
is_add=0, ip4_table_index=ip4_tid, ip6_table_index=ip6_tid
|
||||
)
|
||||
else:
|
||||
self.vapi.input_acl_set_interface(
|
||||
is_add=0,
|
||||
ip4_table_index=ip4_tid,
|
||||
ip6_table_index=ip6_tid,
|
||||
l2_table_index=0xFFFFFFFF,
|
||||
sw_if_index=self.pg0.sw_if_index,
|
||||
)
|
||||
|
||||
def test_punt_redirect_ipv4(self):
|
||||
"""IPv4 punt session redirect test"""
|
||||
return self.__test_redirect(sport=6754, dport=17923, is_punt=True, is_ip6=False)
|
||||
|
||||
def test_punt_redirect_ipv6(self):
|
||||
"""IPv6 punt session redirect test"""
|
||||
return self.__test_redirect(sport=28447, dport=4035, is_punt=True, is_ip6=True)
|
||||
|
||||
def test_redirect_ipv4(self):
|
||||
"""IPv4 session redirect test"""
|
||||
return self.__test_redirect(sport=834, dport=1267, is_punt=False, is_ip6=False)
|
||||
|
||||
def test_redirect_ipv6(self):
|
||||
"""IPv6 session redirect test"""
|
||||
return self.__test_redirect(sport=9999, dport=32768, is_punt=False, is_ip6=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(testRunner=VppTestRunner)
|
Reference in New Issue
Block a user