tcp: add option to punt traffic
Until now, if the stack didn't find a connection for a packet, it sent
back a reset. With the punt option enabled, packets are now enqueued to
error-punt where they can be handed off to the host os.
Change-Id: I12dea8694b8bd24c92b0d601412928aa7b8046cb
Signed-off-by: Florin Coras <fcoras@cisco.com>
Signed-off-by: Pierre Pfister <ppfister@cisco.com>
diff --git a/src/vnet/ip/punt.c b/src/vnet/ip/punt.c
index 1ea32fa..0869954 100644
--- a/src/vnet/ip/punt.c
+++ b/src/vnet/ip/punt.c
@@ -26,6 +26,7 @@
#include <vlib/vlib.h>
#include <vnet/pg/pg.h>
#include <vnet/udp/udp.h>
+#include <vnet/tcp/tcp.h>
#include <vnet/ip/punt.h>
#include <vppinfra/sparse_vec.h>
#include <vlib/unix/unix.h>
@@ -613,16 +614,15 @@
* @brief Request IP traffic punt to the local TCP/IP stack.
*
* @em Note
- * - UDP is the only protocol supported in the current implementation
- * - When requesting UDP punt port number(s) must be specified
- * - All TCP traffic is currently punted to the host by default
+ * - UDP and TCP are the only protocols supported in the current implementation
*
* @param vm vlib_main_t corresponding to the current thread
* @param ipv IP protcol version.
* 4 - IPv4, 6 - IPv6, ~0 for both IPv6 and IPv4
* @param protocol 8-bits L4 protocol value
- * Only value of 17 (UDP) is currently supported
- * @param port 16-bits L4 (TCP/IP) port number when applicable
+ * UDP is 17
+ * TCP is 1
+ * @param port 16-bits L4 (TCP/IP) port number when applicable (UDP only)
*
* @returns 0 on success, non-zero value otherwise
*/
@@ -630,28 +630,42 @@
vnet_punt_add_del (vlib_main_t * vm, u8 ipv, u8 protocol, u16 port,
bool is_add)
{
+
/* For now we only support UDP punt */
- if (protocol != IP_PROTOCOL_UDP)
+ if (protocol != IP_PROTOCOL_UDP && protocol != IP_PROTOCOL_TCP)
return clib_error_return (0,
- "only UDP protocol (%d) is supported, got %d",
- IP_PROTOCOL_UDP, protocol);
+ "only UDP (%d) and TCP (%d) protocols are supported, got %d",
+ IP_PROTOCOL_UDP, IP_PROTOCOL_TCP, protocol);
if (ipv != (u8) ~ 0 && ipv != 4 && ipv != 6)
return clib_error_return (0, "IP version must be 4 or 6, got %d", ipv);
if (port == (u16) ~ 0)
{
- if (ipv == 4 || ipv == (u8) ~ 0)
- udp_punt_unknown (vm, 1, is_add);
+ if ((ipv == 4) || (ipv == (u8) ~ 0))
+ {
+ if (protocol == IP_PROTOCOL_UDP)
+ udp_punt_unknown (vm, 1, is_add);
+ else if (protocol == IP_PROTOCOL_TCP)
+ tcp_punt_unknown (vm, 1, is_add);
+ }
- if (ipv == 6 || ipv == (u8) ~ 0)
- udp_punt_unknown (vm, 0, is_add);
+ if ((ipv == 6) || (ipv == (u8) ~ 0))
+ {
+ if (protocol == IP_PROTOCOL_UDP)
+ udp_punt_unknown (vm, 0, is_add);
+ else if (protocol == IP_PROTOCOL_TCP)
+ tcp_punt_unknown (vm, 0, is_add);
+ }
return 0;
}
else if (is_add)
{
+ if (protocol == IP_PROTOCOL_TCP)
+ return clib_error_return (0, "punt TCP ports is not supported yet");
+
if (ipv == 4 || ipv == (u8) ~ 0)
udp_register_dst_port (vm, port, udp4_punt_node.index, 1);
@@ -665,32 +679,36 @@
}
static clib_error_t *
-udp_punt_cli (vlib_main_t * vm,
- unformat_input_t * input, vlib_cli_command_t * cmd)
+punt_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
{
- u32 udp_port;
+ u32 port;
bool is_add = true;
+ u32 protocol = ~0;
clib_error_t *error;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "del"))
is_add = false;
- if (unformat (input, "all"))
+ else if (unformat (input, "all"))
{
/* punt both IPv6 and IPv4 when used in CLI */
- error = vnet_punt_add_del (vm, ~0, IP_PROTOCOL_UDP, ~0, is_add);
+ error = vnet_punt_add_del (vm, ~0, protocol, ~0, is_add);
if (error)
clib_error_report (error);
}
- else if (unformat (input, "%d", &udp_port))
+ else if (unformat (input, "%d", &port))
{
/* punt both IPv6 and IPv4 when used in CLI */
- error =
- vnet_punt_add_del (vm, ~0, IP_PROTOCOL_UDP, udp_port, is_add);
+ error = vnet_punt_add_del (vm, ~0, protocol, port, is_add);
if (error)
clib_error_report (error);
}
+ else if (unformat (input, "udp"))
+ protocol = IP_PROTOCOL_UDP;
+ else if (unformat (input, "tcp"))
+ protocol = IP_PROTOCOL_TCP;
}
return 0;
@@ -717,10 +735,10 @@
* @endparblock
?*/
/* *INDENT-OFF* */
-VLIB_CLI_COMMAND (punt_udp_command, static) = {
- .path = "set punt udp",
- .short_help = "set punt udp [del] <all | port-num1 [port-num2 ...]>",
- .function = udp_punt_cli,
+VLIB_CLI_COMMAND (punt_command, static) = {
+ .path = "set punt",
+ .short_help = "set punt [udp|tcp] [del] <all | port-num1 [port-num2 ...]>",
+ .function = punt_cli,
};
/* *INDENT-ON* */
diff --git a/src/vnet/tcp/tcp.c b/src/vnet/tcp/tcp.c
index f779428..f457ef7 100644
--- a/src/vnet/tcp/tcp.c
+++ b/src/vnet/tcp/tcp.c
@@ -1398,6 +1398,16 @@
return 0;
}
+void
+tcp_punt_unknown (vlib_main_t * vm, u8 is_ip4, u8 is_add)
+{
+ tcp_main_t *tm = &tcp_main;
+ if (is_ip4)
+ tm->punt_unknown4 = is_add;
+ else
+ tm->punt_unknown6 = is_add;
+}
+
clib_error_t *
tcp_init (vlib_main_t * vm)
{
@@ -1893,6 +1903,29 @@
};
/* *INDENT-ON* */
+static clib_error_t *
+show_tcp_punt_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd_arg)
+{
+ tcp_main_t *tm = vnet_get_tcp_main ();
+ if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ return clib_error_return (0, "unknown input `%U'", format_unformat_error,
+ input);
+ vlib_cli_output (vm, "IPv4 TCP punt: %s",
+ tm->punt_unknown4 ? "enabled" : "disabled");
+ vlib_cli_output (vm, "IPv6 TCP punt: %s",
+ tm->punt_unknown6 ? "enabled" : "disabled");
+ return 0;
+}
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_tcp_punt_command, static) =
+{
+ .path = "show tcp punt",
+ .short_help = "show tcp punt",
+ .function = show_tcp_punt_fn,
+};
+/* *INDENT-ON* */
+
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/vnet/tcp/tcp.h b/src/vnet/tcp/tcp.h
index bb8091a..259dbca 100644
--- a/src/vnet/tcp/tcp.h
+++ b/src/vnet/tcp/tcp.h
@@ -417,6 +417,9 @@
/** vlib buffer size */
u32 bytes_per_buffer;
+
+ u8 punt_unknown4;
+ u8 punt_unknown6;
} tcp_main_t;
extern tcp_main_t tcp_main;
@@ -441,6 +444,8 @@
clib_error_t *vnet_tcp_enable_disable (vlib_main_t * vm, u8 is_en);
+void tcp_punt_unknown (vlib_main_t * vm, u8 is_ip4, u8 is_add);
+
always_inline tcp_connection_t *
tcp_connection_get (u32 conn_index, u32 thread_index)
{
diff --git a/src/vnet/tcp/tcp_error.def b/src/vnet/tcp/tcp_error.def
index 0892231..a179717 100644
--- a/src/vnet/tcp/tcp_error.def
+++ b/src/vnet/tcp/tcp_error.def
@@ -39,4 +39,5 @@
tcp_error (INVALID_CONNECTION, "Invalid connection")
tcp_error (NO_WND, "No window")
tcp_error (CONNECTION_CLOSED, "Connection closed")
-tcp_error (CREATE_EXISTS, "Connection already exists")
\ No newline at end of file
+tcp_error (CREATE_EXISTS, "Connection already exists")
+tcp_error (PUNT, "Packets punted")
\ No newline at end of file
diff --git a/src/vnet/tcp/tcp_input.c b/src/vnet/tcp/tcp_input.c
index 64a0707..bd57eca 100644
--- a/src/vnet/tcp/tcp_input.c
+++ b/src/vnet/tcp/tcp_input.c
@@ -2869,6 +2869,7 @@
TCP_INPUT_NEXT_SYN_SENT,
TCP_INPUT_NEXT_ESTABLISHED,
TCP_INPUT_NEXT_RESET,
+ TCP_INPUT_NEXT_PUNT,
TCP_INPUT_N_NEXT
} tcp_input_next_t;
@@ -2878,7 +2879,8 @@
_ (RCV_PROCESS, "tcp4-rcv-process") \
_ (SYN_SENT, "tcp4-syn-sent") \
_ (ESTABLISHED, "tcp4-established") \
- _ (RESET, "tcp4-reset")
+ _ (RESET, "tcp4-reset") \
+ _ (PUNT, "error-punt")
#define foreach_tcp6_input_next \
_ (DROP, "error-drop") \
@@ -2886,7 +2888,8 @@
_ (RCV_PROCESS, "tcp6-rcv-process") \
_ (SYN_SENT, "tcp6-syn-sent") \
_ (ESTABLISHED, "tcp6-established") \
- _ (RESET, "tcp6-reset")
+ _ (RESET, "tcp6-reset") \
+ _ (PUNT, "error-punt")
#define filter_flags (TCP_FLAG_SYN|TCP_FLAG_ACK|TCP_FLAG_RST|TCP_FLAG_FIN)
@@ -3010,9 +3013,18 @@
}
else
{
- /* Send reset */
- next0 = TCP_INPUT_NEXT_RESET;
- error0 = TCP_ERROR_NO_LISTENER;
+ if ((is_ip4 && tm->punt_unknown4) ||
+ (!is_ip4 && tm->punt_unknown6))
+ {
+ next0 = TCP_INPUT_NEXT_PUNT;
+ error0 = TCP_ERROR_PUNT;
+ }
+ else
+ {
+ /* Send reset */
+ next0 = TCP_INPUT_NEXT_RESET;
+ error0 = TCP_ERROR_NO_LISTENER;
+ }
}
done: