reassembly: feature/concurrency

This change makes ip reassembly an interface feature, while adding
concurrency support. Due to this, punt is no longer needed to test
reassembly.

Change-Id: I467669514ec33283ce935be0f1dd08f07684f0c7
Signed-off-by: Klement Sekera <ksekera@cisco.com>
This commit is contained in:
Klement Sekera
2018-02-22 11:41:12 +01:00
committed by Damjan Marion
parent 2303cb181b
commit 4c53313cd7
18 changed files with 998 additions and 698 deletions

View File

@ -161,15 +161,24 @@ typedef struct
} icmp;
/* reassembly */
struct
union
{
u16 fragment_first;
u16 fragment_last;
u16 range_first;
u16 range_last;
u32 next_range_bi;
u16 ip6_frag_hdr_offset;
u16 estimated_mtu;
/* in/out variables */
struct
{
u32 next_index; /* index of next node - ignored if "feature" node */
u16 estimated_mtu; /* estimated MTU calculated during reassembly */
};
/* internal variables used during reassembly */
struct
{
u16 fragment_first;
u16 fragment_last;
u16 range_first;
u16 range_last;
u32 next_range_bi;
u16 ip6_frag_hdr_offset;
};
} reass;
};

View File

@ -910,6 +910,22 @@ define ip_reassembly_get_reply
u8 is_ip6;
};
/** \brief Enable/disable reassembly feature
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param sw_if_index - interface to enable/disable feature on
@param enable_ip4 - enable ip4 reassembly if non-zero, disable if 0
@param enable_ip6 - enable ip6 reassembly if non-zero, disable if 0
*/
autoreply define ip_reassembly_enable_disable
{
u32 client_index;
u32 context;
u32 sw_if_index;
u8 enable_ip4;
u8 enable_ip6;
};
/*
* Local Variables:
* eval: (c-set-style "gnu")

View File

@ -38,6 +38,8 @@
*/
#include <vnet/ip/ip.h>
#include <vnet/ip/ip4_reassembly.h>
#include <vnet/ip/ip6_reassembly.h>
/**
* @file
@ -218,6 +220,87 @@ VLIB_CLI_COMMAND (set_interface_ip_address_command, static) = {
};
/* *INDENT-ON* */
static clib_error_t *
set_reassembly_command_fn (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
vnet_main_t *vnm = vnet_get_main ();
unformat_input_t _line_input, *line_input = &_line_input;
u32 sw_if_index = ~0;
u8 ip4_on = 0;
u8 ip6_on = 0;
/* Get a line of input. */
if (!unformat_user (input, unformat_line_input, line_input))
{
return NULL;
}
if (!unformat (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
{
return clib_error_return (0, "Invalid interface name");
}
if (unformat (input, "on"))
{
ip4_on = 1;
ip6_on = 1;
}
else if (unformat (input, "off"))
{
ip4_on = 0;
ip6_on = 0;
}
else if (unformat (input, "ip4"))
{
ip4_on = 1;
ip6_on = 0;
}
else if (unformat (input, "ip6"))
{
ip4_on = 0;
ip6_on = 1;
}
else
{
return clib_error_return (0, "Unknown input `%U'",
format_unformat_error, line_input);
}
vnet_api_error_t rv4 = ip4_reass_enable_disable (sw_if_index, ip4_on);
vnet_api_error_t rv6 = ip6_reass_enable_disable (sw_if_index, ip6_on);
if (rv4 && rv6)
{
return clib_error_return (0,
"`ip4_reass_enable_disable' API call failed, rv=%d:%U, "
"`ip6_reass_enable_disable' API call failed, rv=%d:%U",
(int) rv4, format_vnet_api_errno, rv4,
(int) rv6, format_vnet_api_errno, rv6);
}
else if (rv4)
{
return clib_error_return (0,
"`ip4_reass_enable_disable' API call failed, rv=%d:%U",
(int) rv4, format_vnet_api_errno, rv4);
}
else if (rv6)
{
return clib_error_return (0,
"`ip6_reass_enable_disable' API call failed, rv=%d:%U",
(int) rv6, format_vnet_api_errno, rv6);
}
return NULL;
}
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (set_reassembly_command, static) = {
.path = "set interface reassembly",
.short_help = "set interface reassembly <interface-name> [on|off|ip4|ip6]",
.function = set_reassembly_command_fn,
};
/* *INDENT-ON* */
/* Dummy init function to get us linked in. */
static clib_error_t *
ip4_cli_init (vlib_main_t * vm)

View File

@ -1217,14 +1217,8 @@ ip4_local_inline (vlib_main_t * vm,
sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
sw_if_index1 = vnet_buffer (p1)->sw_if_index[VLIB_RX];
/* Treat IP frag packets as "experimental" protocol for now
until support of IP frag reassembly is implemented */
proto0 =
ip4_is_fragment (ip0) ? IP_PROTOCOL_VPP_FRAGMENTATION :
ip0->protocol;
proto1 =
ip4_is_fragment (ip1) ? IP_PROTOCOL_VPP_FRAGMENTATION :
ip1->protocol;
proto0 = ip0->protocol;
proto1 = ip1->protocol;
if (head_of_feature_arc == 0)
goto skip_checks;
@ -1387,11 +1381,7 @@ ip4_local_inline (vlib_main_t * vm,
vnet_buffer (p0)->l3_hdr_offset = p0->current_data;
sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
/* Treat IP frag packets as "experimental" protocol for now
until support of IP frag reassembly is implemented */
proto0 =
ip4_is_fragment (ip0) ? IP_PROTOCOL_VPP_FRAGMENTATION :
ip0->protocol;
proto0 = ip0->protocol;
if (head_of_feature_arc == 0 || p0->flags & VNET_BUFFER_F_IS_NATED)
goto skip_check;
@ -1482,7 +1472,6 @@ VLIB_REGISTER_NODE (ip4_local_node) =
[IP_LOCAL_NEXT_PUNT] = "ip4-punt",
[IP_LOCAL_NEXT_UDP_LOOKUP] = "ip4-udp-lookup",
[IP_LOCAL_NEXT_ICMP] = "ip4-icmp-input",
[IP_LOCAL_NEXT_REASSEMBLY] = "ip4-reassembly",
},
};
/* *INDENT-ON* */

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,9 @@ vnet_api_error_t ip4_reass_set (u32 timeout_ms, u32 max_reassemblies,
vnet_api_error_t ip4_reass_get (u32 * timeout_ms, u32 * max_reassemblies,
u32 * expire_walk_interval_ms);
vnet_api_error_t ip4_reass_enable_disable (u32 sw_if_index,
u8 enable_disable);
#endif /* __included_ip4_reassembly_h */
/*

View File

@ -1352,7 +1352,6 @@ VLIB_REGISTER_NODE (ip6_local_node, static) =
[IP_LOCAL_NEXT_PUNT] = "ip6-punt",
[IP_LOCAL_NEXT_UDP_LOOKUP] = "ip6-udp-lookup",
[IP_LOCAL_NEXT_ICMP] = "ip6-icmp-input",
[IP_LOCAL_NEXT_REASSEMBLY] = "ip6-reassembly",
},
};
/* *INDENT-ON* */

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,9 @@ vnet_api_error_t ip6_reass_set (u32 timeout_ms, u32 max_reassemblies,
vnet_api_error_t ip6_reass_get (u32 * timeout_ms, u32 * max_reassemblies,
u32 * expire_walk_interval_ms);
vnet_api_error_t ip6_reass_enable_disable (u32 sw_if_index,
u8 enable_disable);
#endif /* __included_ip6_reassembly_h */
/*

View File

@ -103,7 +103,8 @@ _(IP_SOURCE_AND_PORT_RANGE_CHECK_ADD_DEL, \
_(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL, \
ip_source_and_port_range_check_interface_add_del) \
_(IP_REASSEMBLY_SET, ip_reassembly_set) \
_(IP_REASSEMBLY_GET, ip_reassembly_get)
_(IP_REASSEMBLY_GET, ip_reassembly_get) \
_(IP_REASSEMBLY_ENABLE_DISABLE, ip_reassembly_enable_disable)
extern void stats_dslock_with_hint (int hint, int tag);
extern void stats_dsunlock (void);
@ -2904,6 +2905,23 @@ vl_api_ip_reassembly_get_t_handler (vl_api_ip_reassembly_get_t * mp)
vl_msg_api_send_shmem (q, (u8 *) & rmp);
}
void
vl_api_ip_reassembly_enable_disable_t_handler
(vl_api_ip_reassembly_enable_disable_t * mp)
{
vl_api_ip_reassembly_enable_disable_reply_t *rmp;
int rv = 0;
rv = ip4_reass_enable_disable (clib_net_to_host_u32 (mp->sw_if_index),
mp->enable_ip4);
if (0 == rv)
{
rv = ip6_reass_enable_disable (clib_net_to_host_u32 (mp->sw_if_index),
mp->enable_ip6);
}
REPLY_MACRO (VL_API_IP_REASSEMBLY_SET_REPLY);
}
#define vl_msg_name_crc_list
#include <vnet/ip/ip.api.h>
#undef vl_msg_name_crc_list

View File

@ -230,8 +230,6 @@ ip_lookup_init (ip_lookup_main_t * lm, u32 is_ip6)
}
lm->local_next_by_ip_protocol[IP_PROTOCOL_UDP] = IP_LOCAL_NEXT_UDP_LOOKUP;
lm->local_next_by_ip_protocol[IP_PROTOCOL_VPP_FRAGMENTATION] =
IP_LOCAL_NEXT_REASSEMBLY;
lm->local_next_by_ip_protocol[is_ip6 ? IP_PROTOCOL_ICMP6 :
IP_PROTOCOL_ICMP] = IP_LOCAL_NEXT_ICMP;
lm->builtin_protocol_by_ip_protocol[IP_PROTOCOL_UDP] =

View File

@ -111,7 +111,6 @@ typedef enum
IP_LOCAL_NEXT_PUNT,
IP_LOCAL_NEXT_UDP_LOOKUP,
IP_LOCAL_NEXT_ICMP,
IP_LOCAL_NEXT_REASSEMBLY,
IP_LOCAL_N_NEXT,
} ip_local_next_t;

View File

@ -377,6 +377,10 @@ class VppTestCase(unittest.TestCase):
try:
cls.vapi.connect()
except:
try:
cls.vapi.disconnect()
except:
pass
if cls.debug_gdbserver:
print(colorize("You're running VPP inside gdbserver but "
"VPP-API connection failed, did you forget "

View File

@ -1,68 +0,0 @@
#!/bin/bash
file="$1"
usage(){
echo "Usage: $0 <requirements file>"
}
if [ "$file" == "" ]
then
echo "Invalid parameters specified."
usage
exit 1
fi
if [ ! -f $file ]
then
echo "File '$file' does not exist."
usage
exit 1
fi
if test "$DOCKER_TEST" = "True"
then
echo "=============================================================================="
echo "DOCKER_TEST is set to '$DOCKER_TEST'."
echo "Skipping verification of some system parameters."
echo "Make sure these are set properly, otherwise tests might fail."
echo "Required values/criteria are in '`readlink -e $file`'."
echo "=============================================================================="
exit 0
fi
cat $file | grep -v -e '^#.*$' | grep -v -e '^ *$' | while read line
do
value_file=`echo $line | awk '{print $1}'`
operator=`echo $line | awk '{print $2}'`
value=`echo $line | awk '{print $3}'`
set_value=`echo $line | awk '{print $4}'`
if [[ "$value_file" == "" || "$operator" == "" || "$value" == "" || "$set_value" == "" ]]
then
echo "Syntax error in requirements file."
exit 1
fi
current_value=`cat $value_file`
if test "$current_value" $operator "$value"
then
if test "$V" = "2"
then
echo "Requirement '$value_file $operator $value' satisfied."
fi
else
echo "Requirement '$value_file $operator $value' not satisfied."
echo "Writing '$set_value' to '$value_file'."
echo "$set_value" | tee "$value_file" > /dev/null
if ! test "`cat $value_file`" = "$set_value"
then
echo "Repeating the write using sudo..."
echo "$set_value" | sudo -n tee "$value_file" > /dev/null
if ! test "`cat $value_file`" = "$set_value"
then
echo "Couldn't set the required value. Is that value allowed? Is sudo working?"
exit 1
fi
fi
echo "Succesfully wrote '$set_value' to '$value_file'."
fi
done

View File

@ -1,18 +0,0 @@
# test framework system requirements
# format of this file is
# <path> <operator> <comparison-value> <set-value>
#
# path - path to value e.g. in /proc which needs to be checked
# operator - test operator (e.g. -gt)
# comparison-value - value, against which the value read from <path> is compared
# set-value - value, to which the path is set if the test fails
#
# the comparison is done using `test' command
# test_reassembly.py
# needed by test_reassembly which uses udp punt via unix domain sockets
# to ensure that all data which vpp might produce in a burst fits into
# the socket send buffer
/proc/sys/net/core/wmem_max -ge 4636252 4636252
/proc/sys/net/core/wmem_default -ge 4636252 4636252

File diff suppressed because it is too large Load Diff

View File

@ -3203,6 +3203,15 @@ class VppPapiProvider(object):
""" Get IP reassembly parameters """
return self.api(self.papi.ip_reassembly_get, {'is_ip6': is_ip6})
def ip_reassembly_enable_disable(self, sw_if_index, enable_ip4=False,
enable_ip6=False):
""" Enable/disable IP reassembly """
return self.api(self.papi.ip_reassembly_enable_disable,
{'sw_if_index': sw_if_index,
'enable_ip4': 1 if enable_ip4 else 0,
'enable_ip6': 1 if enable_ip6 else 0,
})
def gbp_endpoint_add_del(self, is_add, sw_if_index, addr, is_ip6, epg):
""" GBP endpoint Add/Del """
return self.api(self.papi.gbp_endpoint_add_del,

View File

@ -1,75 +0,0 @@
from socket import socket, AF_UNIX, SOCK_DGRAM
from select import select
from time import time
from struct import unpack, calcsize
from util import ppc
from scapy.layers.l2 import Ether
client_uds_socket_name = "client-uds-socket"
vpp_uds_socket_name = "vpp-uds-socket"
VPP_PUNT_HEADER_FMT = '=Ii'
VPP_PUNT_HEADER_SIZE = calcsize(VPP_PUNT_HEADER_FMT)
class VppPuntAction:
PUNT_L2 = 0
PUNT_IP4_ROUTED = 1
PUNT_IP6_ROUTED = 2
class VppUDSPuntSocket(object):
def __init__(self, testcase, port, is_ip4=1, l4_protocol=0x11):
client_path = '%s/%s-%s-%s' % (testcase.tempdir,
client_uds_socket_name,
"4" if is_ip4 else "6", port)
testcase.vapi.punt_socket_register(
port, client_path, is_ip4=is_ip4, l4_protocol=l4_protocol)
self.testcase = testcase
self.uds = socket(AF_UNIX, SOCK_DGRAM)
self.uds.bind(client_path)
self.uds.connect(testcase.punt_socket_path)
def wait_for_packets(self, count, timeout=1):
packets = []
now = time()
deadline = now + timeout
while len(packets) < count and now < deadline:
r, w, e = select([self.uds], [], [self.uds], deadline - now)
if self.uds in r:
x = self.uds.recv(1024 * 1024)
sw_if_index, punt_action = unpack(
VPP_PUNT_HEADER_FMT, x[:VPP_PUNT_HEADER_SIZE])
packets.append({'sw_if_index': sw_if_index,
'punt_action': punt_action,
'packet': x[VPP_PUNT_HEADER_SIZE:]})
if self.uds in e:
raise Exception("select() indicates error on UDS socket")
now = time()
if len(packets) != count:
raise Exception("Unexpected packet count received, got %s packets,"
" expected %s packets" % (len(packets), count))
self.testcase.logger.debug(
"Got %s packets via punt socket" % len(packets))
return packets
def assert_nothing_captured(self, timeout=.25):
packets = []
now = time()
deadline = now + timeout
while now < deadline:
r, w, e = select([self.uds], [], [self.uds], deadline - now)
if self.uds in r:
x = self.uds.recv(1024 * 1024)
packets.append(Ether(x[VPP_PUNT_HEADER_SIZE:]))
if self.uds in e:
raise Exception("select() indicates error on UDS socket")
now = time()
if len(packets) > 0:
self.testcase.logger.error(
ppc("Unexpected packets captured:", packets))
raise Exception("Unexpected packet count received, got %s packets,"
" expected no packets" % len(packets))