summaryrefslogtreecommitdiffstats
path: root/src/plugins/mss_clamp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/mss_clamp')
-rw-r--r--src/plugins/mss_clamp/CMakeLists.txt30
-rw-r--r--src/plugins/mss_clamp/mss_clamp.api112
-rw-r--r--src/plugins/mss_clamp/mss_clamp.c292
-rw-r--r--src/plugins/mss_clamp/mss_clamp.h55
-rw-r--r--src/plugins/mss_clamp/mss_clamp_api.c136
-rw-r--r--src/plugins/mss_clamp/mss_clamp_node.c411
-rw-r--r--src/plugins/mss_clamp/test/test_mss_clamp.py295
7 files changed, 1331 insertions, 0 deletions
diff --git a/src/plugins/mss_clamp/CMakeLists.txt b/src/plugins/mss_clamp/CMakeLists.txt
new file mode 100644
index 00000000000..d650d7fc528
--- /dev/null
+++ b/src/plugins/mss_clamp/CMakeLists.txt
@@ -0,0 +1,30 @@
+
+# Copyright (c) <current-year> <your-organization>
+# 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(mss_clamp
+ SOURCES
+ mss_clamp.c
+ mss_clamp_api.c
+ mss_clamp_node.c
+
+
+ MULTIARCH_SOURCES
+ mss_clamp_node.c
+
+ API_FILES
+ mss_clamp.api
+
+ INSTALL_HEADERS
+ mss_clamp.h
+)
diff --git a/src/plugins/mss_clamp/mss_clamp.api b/src/plugins/mss_clamp/mss_clamp.api
new file mode 100644
index 00000000000..e1b9fdea6cb
--- /dev/null
+++ b/src/plugins/mss_clamp/mss_clamp.api
@@ -0,0 +1,112 @@
+/* Hey Emacs use -*- mode: C -*- */
+/*
+ * Copyright (c) 2016 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 = "1.0.0";
+import "vnet/interface_types.api";
+
+/** \brief TCP MSS Clamping direction flag
+ */
+enumflag mss_clamp_dir : u8 {
+ MSS_CLAMP_DIR_NONE = 0x0,
+ MSS_CLAMP_DIR_RX = 0x1,
+ MSS_CLAMP_DIR_TX = 0x2,
+};
+
+/** \brief Enable/Disable TCP MSS Clamping feature on an interface
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param sw_if_index - interface index on which clamping will be applied
+ @param ipv4_mss - Maximum Segment Size for IPv4/TCP
+ @param ipv6_mss - Maximum Segment Size for IPv6/TCP
+ @param ipv4_direction - Direction clamping is enabled on (IPv4/TCP)
+ @param ipv6_direction - Direction clamping is enabled on (IPv6/TCP)
+ */
+autoreply define mss_clamp_enable_disable {
+ u32 client_index;
+ u32 context;
+ vl_api_interface_index_t sw_if_index;
+ u16 ipv4_mss;
+ u16 ipv6_mss;
+ vl_api_mss_clamp_dir_t ipv4_direction;
+ vl_api_mss_clamp_dir_t ipv6_direction;
+};
+
+
+/** \brief Get the list of configured mss values
+ @param client_index - opaque cookie to identify the sender
+*/
+service {
+ rpc mss_clamp_get returns mss_clamp_get_reply
+ stream mss_clamp_details;
+};
+
+/** \brief Get the TCP MSS Clamping feature settings
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param cursor - cursor to continue when there is more to read
+ @param sw_if_index - interface index to filter the result,
+ ~0 means no filter
+ */
+define mss_clamp_get {
+ u32 client_index;
+ u32 context;
+ u32 cursor;
+ vl_api_interface_index_t sw_if_index;
+};
+
+/** \brief Reply for get TCP MSS Clamping feature settings request
+ @param context - returned sender context, to match reply w/ request
+ @param retval - return code
+ @param cursor - cursor to continue when there is more to read
+ */
+define mss_clamp_get_reply {
+ u32 context;
+ i32 retval;
+ u32 cursor;
+};
+
+/** \brief Configured MSS values on an interface
+ @param context - returned sender context, to match reply w/ request
+ @param sw_if_index - interface index on which clamping is applied
+ @param ipv4_mss - Maximum Segment Size for IPv4/TCP
+ @param ipv6_mss - Maximum Segment Size for IPv6/TCP
+ @param ipv4_direction - Direction clamping is enabled on (IPv4/TCP)
+ @param ipv6_direction - Direction clamping is enabled on (IPv6/TCP)
+ */
+define mss_clamp_details {
+ u32 context;
+ vl_api_interface_index_t sw_if_index;
+ u16 ipv4_mss;
+ u16 ipv6_mss;
+ vl_api_mss_clamp_dir_t ipv4_direction;
+ vl_api_mss_clamp_dir_t ipv6_direction;
+};
+
+counters mss_clamp {
+ clamped {
+ severity info;
+ type counter64;
+ units "packets";
+ description "packets clamped";
+ };
+};
+paths {
+ "/err/tcp-mss-clamping-ip4-in" "mss-clamp";
+ "/err/tcp-mss-clamping-ip4-out" "mss-clamp";
+ "/err/tcp-mss-clamping-ip6-in" "mss-clamp";
+ "/err/tcp-mss-clamping-ip6-out" "mss-clamp";
+};
+
diff --git a/src/plugins/mss_clamp/mss_clamp.c b/src/plugins/mss_clamp/mss_clamp.c
new file mode 100644
index 00000000000..cdac5456641
--- /dev/null
+++ b/src/plugins/mss_clamp/mss_clamp.c
@@ -0,0 +1,292 @@
+/*
+ * mss_clamp.c - TCP MSS clamping plug-in
+ *
+ * Copyright (c) 2018 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 <vnet/vnet.h>
+#include <vnet/plugin/plugin.h>
+#include <mss_clamp/mss_clamp.h>
+#include <mss_clamp/mss_clamp.api_types.h>
+
+mssc_main_t mssc_main;
+
+/* Action function shared between message handler and debug CLI */
+
+static void
+mssc_enable_disable_feat (u32 sw_if_index, u8 dir4, u8 dir6, int enable)
+{
+ if (dir4 == MSS_CLAMP_DIR_NONE && dir6 == MSS_CLAMP_DIR_NONE)
+ return;
+
+ // ip4
+ if ((dir4 & MSS_CLAMP_DIR_RX) != MSS_CLAMP_DIR_NONE)
+ vnet_feature_enable_disable ("ip4-unicast", "tcp-mss-clamping-ip4-in",
+ sw_if_index, enable, 0, 0);
+ if ((dir4 & MSS_CLAMP_DIR_TX) != MSS_CLAMP_DIR_NONE)
+ vnet_feature_enable_disable ("ip4-output", "tcp-mss-clamping-ip4-out",
+ sw_if_index, enable, 0, 0);
+ // ip6
+ if ((dir6 & MSS_CLAMP_DIR_RX) != MSS_CLAMP_DIR_NONE)
+ vnet_feature_enable_disable ("ip6-unicast", "tcp-mss-clamping-ip6-in",
+ sw_if_index, enable, 0, 0);
+ if ((dir6 & MSS_CLAMP_DIR_TX) != MSS_CLAMP_DIR_NONE)
+ vnet_feature_enable_disable ("ip6-output", "tcp-mss-clamping-ip6-out",
+ sw_if_index, enable, 0, 0);
+}
+
+int
+mssc_enable_disable (u32 sw_if_index, u8 dir4, u8 dir6, u16 mss4, u16 mss6)
+{
+ mssc_main_t *cm = &mssc_main;
+ u8 *dir_enabled4, *dir_enabled6;
+ int rv = 0;
+
+ if (dir4 == MSS_CLAMP_DIR_NONE)
+ mss4 = MSS_CLAMP_UNSET;
+ if (dir6 == MSS_CLAMP_DIR_NONE)
+ mss6 = MSS_CLAMP_UNSET;
+
+ vec_validate_init_empty (cm->dir_enabled4, sw_if_index, MSS_CLAMP_DIR_NONE);
+ vec_validate_init_empty (cm->dir_enabled6, sw_if_index, MSS_CLAMP_DIR_NONE);
+ vec_validate_init_empty (cm->max_mss4, sw_if_index, MSS_CLAMP_UNSET);
+ vec_validate_init_empty (cm->max_mss6, sw_if_index, MSS_CLAMP_UNSET);
+
+ cm->max_mss4[sw_if_index] = mss4;
+ cm->max_mss6[sw_if_index] = mss6;
+ dir_enabled4 = &cm->dir_enabled4[sw_if_index];
+ dir_enabled6 = &cm->dir_enabled6[sw_if_index];
+
+ // Disable the directions that are no longer needed
+ mssc_enable_disable_feat (sw_if_index, (*dir_enabled4) & ~dir4,
+ (*dir_enabled6) & ~dir6, 0);
+ // Enable the new directions
+ mssc_enable_disable_feat (sw_if_index, ~(*dir_enabled4) & dir4,
+ ~(*dir_enabled6) & dir6, 1);
+
+ *dir_enabled4 = dir4;
+ *dir_enabled6 = dir6;
+
+ return rv;
+}
+
+int
+mssc_get_mss (u32 sw_if_index, u8 *dir4, u8 *dir6, u16 *mss4, u16 *mss6)
+{
+ mssc_main_t *cm = &mssc_main;
+ int rv = VNET_API_ERROR_FEATURE_DISABLED;
+
+ if (vec_len (cm->dir_enabled4) > sw_if_index &&
+ MSS_CLAMP_DIR_NONE != cm->dir_enabled4[sw_if_index])
+ {
+ *mss4 = cm->max_mss4[sw_if_index];
+ *dir4 = cm->dir_enabled4[sw_if_index];
+ rv = 0;
+ }
+ else
+ {
+ *mss4 = MSS_CLAMP_DIR_NONE;
+ *dir4 = 0;
+ }
+
+ if (vec_len (cm->dir_enabled6) > sw_if_index &&
+ MSS_CLAMP_DIR_NONE != cm->dir_enabled6[sw_if_index])
+ {
+ *mss6 = cm->max_mss6[sw_if_index];
+ *dir6 = cm->dir_enabled6[sw_if_index];
+ rv = 0;
+ }
+ else
+ {
+ *mss6 = MSS_CLAMP_DIR_NONE;
+ *dir6 = 0;
+ }
+ return rv;
+}
+
+static uword
+unformat_mssc_dir (unformat_input_t *input, va_list *args)
+{
+ u8 *result = va_arg (*args, u8 *);
+ u8 dir = MSS_CLAMP_DIR_RX | MSS_CLAMP_DIR_TX;
+
+ if (unformat (input, "disable"))
+ dir = MSS_CLAMP_DIR_NONE;
+ else if (unformat (input, "enable"))
+ dir = MSS_CLAMP_DIR_RX | MSS_CLAMP_DIR_TX;
+ else if (unformat (input, "rx"))
+ dir = MSS_CLAMP_DIR_RX;
+ else if (unformat (input, "tx"))
+ dir = MSS_CLAMP_DIR_TX;
+ else
+ return 0;
+
+ *result = dir;
+ return 1;
+}
+
+static clib_error_t *
+mssc_enable_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ u32 sw_if_index = ~0;
+ u8 dir4 = ~0, dir6 = ~0;
+ u32 mss4 = ~0, mss6 = ~0;
+ int rv;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "ip4 %U", unformat_mssc_dir, &dir4))
+ ;
+ else if (unformat (input, "ip6 %U", unformat_mssc_dir, &dir6))
+ ;
+ else if (unformat (input, "ip4-mss %d", &mss4))
+ ;
+ else if (unformat (input, "ip6-mss %d", &mss6))
+ ;
+ else if (unformat (input, "%U", unformat_vnet_sw_interface,
+ vnet_get_main (), &sw_if_index))
+ ;
+ else
+ break;
+ }
+
+ if (sw_if_index == ~0)
+ return clib_error_return (0, "Please specify an interface");
+
+ if (dir4 == (u8) ~0 || dir6 == (u8) ~0)
+ return clib_error_return (
+ 0, "Please specify the MSS clamping direction for ip4 and ip6");
+
+ if (dir4 != MSS_CLAMP_DIR_NONE)
+ {
+ if (mss4 == ~0)
+ return clib_error_return (
+ 0, "Please specify the Max Segment Size for ip4");
+ if (mss4 >= MSS_CLAMP_UNSET)
+ return clib_error_return (0, "Invalid Max Segment Size");
+ }
+ if (dir6 != MSS_CLAMP_DIR_NONE)
+ {
+ if (mss6 == ~0)
+ return clib_error_return (
+ 0, "Please specify the Max Segment Size for ip6");
+ if (mss6 >= MSS_CLAMP_UNSET)
+ return clib_error_return (0, "Invalid Max Segment Size");
+ }
+
+ rv = mssc_enable_disable (sw_if_index, dir4, dir6, mss4, mss6);
+
+ if (rv)
+ return clib_error_return (0, "Failed: %d = %U", rv, format_vnet_api_errno,
+ rv);
+
+ return (NULL);
+}
+
+VLIB_CLI_COMMAND (mssc_enable_disable_command, static) = {
+ .path = "set interface tcp-mss-clamp",
+ .short_help = "set interface tcp-mss-clamp <interface-name> "
+ "ip4 [enable|disable|rx|tx] ip4-mss <size> "
+ "ip6 [enable|disable|rx|tx] ip6-mss <size>",
+ .function = mssc_enable_command_fn,
+};
+
+static u8 *
+format_mssc_clamping (u8 *s, va_list *args)
+{
+ u8 dir = va_arg (*args, u32);
+ u16 mss = va_arg (*args, u32);
+#define DIR2S(d) \
+ (((d) == (MSS_CLAMP_DIR_RX | MSS_CLAMP_DIR_TX)) ? \
+ "" : \
+ (((d) == MSS_CLAMP_DIR_RX) ? " [RX]" : " [TX]"))
+
+ if (MSS_CLAMP_DIR_NONE == dir)
+ {
+ return format (s, "disabled");
+ }
+ u32 mss_u32 = mss;
+ return format (s, "%d%s", mss_u32, DIR2S (dir));
+}
+
+static clib_error_t *
+mssc_show_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ mssc_main_t *cm = &mssc_main;
+ u32 sw_if_index = ~0;
+ u32 ii;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "%U", unformat_vnet_sw_interface, vnet_get_main (),
+ &sw_if_index))
+ ;
+ else
+ break;
+ }
+
+ if (sw_if_index == ~0)
+ {
+ vec_foreach_index (ii, cm->dir_enabled4)
+ {
+ u8 dir4 = cm->dir_enabled4[ii];
+ u8 dir6 = cm->dir_enabled6[ii];
+ if (MSS_CLAMP_DIR_NONE != dir4 || MSS_CLAMP_DIR_NONE != dir6)
+ {
+ u16 mss4 = cm->max_mss4[ii];
+ u16 mss6 = cm->max_mss6[ii];
+ vlib_cli_output (vm, "%U: ip4: %U ip6: %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ ii, format_mssc_clamping, dir4, mss4,
+ format_mssc_clamping, dir6, mss6);
+ }
+ }
+ }
+ else
+ {
+ u16 mss4, mss6;
+ u8 dir4, dir6;
+ mssc_get_mss (sw_if_index, &dir4, &dir6, &mss4, &mss6);
+ vlib_cli_output (vm, "%U: ip4: %U ip6: %U", format_vnet_sw_if_index_name,
+ vnet_get_main (), sw_if_index, format_mssc_clamping,
+ dir4, mss4, format_mssc_clamping, dir6, mss6);
+ }
+
+ return (NULL);
+}
+
+VLIB_CLI_COMMAND (mssc_show_command, static) = {
+ .path = "show interface tcp-mss-clamp",
+ .short_help = "show interface tcp-mss-clamp [interface-name]",
+ .long_help = "show TCP MSS clamping configurations",
+ .function = mssc_show_command_fn,
+};
+
+static clib_error_t *
+mssc_init (vlib_main_t *vm)
+{
+ return NULL;
+}
+
+VLIB_INIT_FUNCTION (mssc_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/mss_clamp/mss_clamp.h b/src/plugins/mss_clamp/mss_clamp.h
new file mode 100644
index 00000000000..46aa12aa81f
--- /dev/null
+++ b/src/plugins/mss_clamp/mss_clamp.h
@@ -0,0 +1,55 @@
+/*
+ * mss_clamp.h - TCP MSS clamping plug-in header file
+ *
+ * Copyright (c) 2018 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 __included_mss_clamp_h__
+#define __included_mss_clamp_h__
+
+#include <stdbool.h> /* for bool in .api */
+#include <vnet/vnet.h>
+
+extern int mssc_enable_disable (u32 sw_if_index, u8 dir4, u8 dir6, u16 mss4,
+ u16 mss6);
+extern int mssc_get_mss (u32 sw_if_index, u8 *dir4, u8 *dir6, u16 *mss4,
+ u16 *mss6);
+
+typedef struct
+{
+ /* Maximum segment size per interface for IPv4/IPv6 */
+ u16 *max_mss4;
+ u16 *max_mss6;
+
+ /* Direction the feature is enabled for IPv4/IPv6 (rx, tx, both) */
+ u8 *dir_enabled4;
+ u8 *dir_enabled6;
+
+ /* API message ID base */
+ u16 msg_id_base;
+} mssc_main_t;
+
+extern mssc_main_t mssc_main;
+
+#define MSS_CLAMP_UNSET 0xffff
+
+#endif /* __included_mss_clamp_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/mss_clamp/mss_clamp_api.c b/src/plugins/mss_clamp/mss_clamp_api.c
new file mode 100644
index 00000000000..cb47cd6ce06
--- /dev/null
+++ b/src/plugins/mss_clamp/mss_clamp_api.c
@@ -0,0 +1,136 @@
+/*
+ * mss_clamp_api.c - TCP MSS clamping plug-in
+ *
+ * Copyright (c) <current-year> <your-organization>
+ * 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 <vnet/vnet.h>
+#include <vnet/plugin/plugin.h> /* VLIB_PLUGIN_REGISTER */
+#include <mss_clamp/mss_clamp.h>
+#include <mss_clamp/mss_clamp.api_enum.h>
+#include <mss_clamp/mss_clamp.api_types.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vpp/app/version.h> /* VPP_BUILD_VER */
+
+#define REPLY_MSG_ID_BASE cm->msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+/* API message handler */
+static void
+vl_api_mss_clamp_enable_disable_t_handler (
+ vl_api_mss_clamp_enable_disable_t *mp)
+{
+ mssc_main_t *cm = &mssc_main;
+ vl_api_mss_clamp_enable_disable_reply_t *rmp;
+ int rv;
+
+ VALIDATE_SW_IF_INDEX (mp);
+
+ rv = mssc_enable_disable (ntohl (mp->sw_if_index), mp->ipv4_direction,
+ mp->ipv6_direction, ntohs (mp->ipv4_mss),
+ ntohs (mp->ipv6_mss));
+
+ BAD_SW_IF_INDEX_LABEL;
+ REPLY_MACRO (VL_API_MSS_CLAMP_ENABLE_DISABLE_REPLY);
+}
+
+static void
+send_mss_clamp_details (u32 sw_if_index, vl_api_registration_t *rp,
+ u32 context)
+{
+ mssc_main_t *cm = &mssc_main;
+ vl_api_mss_clamp_details_t *rmp;
+ u16 mss4, mss6;
+ u8 dir4, dir6;
+ int rv;
+
+ mss4 = mss6 = 0;
+ dir4 = dir6 = MSS_CLAMP_DIR_NONE;
+ rv = mssc_get_mss (sw_if_index, &dir4, &dir6, &mss4, &mss6);
+ if (rv == VNET_API_ERROR_FEATURE_DISABLED)
+ return;
+
+ REPLY_MACRO_DETAILS4 (VL_API_MSS_CLAMP_DETAILS, rp, context, ({
+ rmp->sw_if_index = htonl (sw_if_index);
+ rmp->ipv4_mss = htons (mss4);
+ rmp->ipv6_mss = htons (mss6);
+ rmp->ipv4_direction = dir4;
+ rmp->ipv6_direction = dir6;
+ }));
+}
+
+static void
+vl_api_mss_clamp_get_t_handler (vl_api_mss_clamp_get_t *mp)
+{
+ mssc_main_t *cm = &mssc_main;
+ vl_api_mss_clamp_get_reply_t *rmp;
+ int rv = 0;
+ u32 sw_if_index = ntohl (mp->sw_if_index);
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ if (sw_if_index == ~0)
+ {
+ if (vec_len (cm->dir_enabled4) == 0)
+ {
+ REPLY_MACRO2 (VL_API_MSS_CLAMP_GET_REPLY, ({ rmp->cursor = ~0; }));
+ return;
+ }
+
+ REPLY_AND_DETAILS_MACRO (
+ VL_API_MSS_CLAMP_GET_REPLY, cm->dir_enabled4,
+ ({ send_mss_clamp_details (cursor, reg, mp->context); }));
+ }
+ else
+ {
+ VALIDATE_SW_IF_INDEX (mp);
+ send_mss_clamp_details (sw_if_index, reg, mp->context);
+
+ BAD_SW_IF_INDEX_LABEL;
+ REPLY_MACRO2 (VL_API_MSS_CLAMP_GET_REPLY, ({ rmp->cursor = ~0; }));
+ }
+}
+
+/* API definitions */
+#include <vnet/format_fns.h>
+#include <mss_clamp/mss_clamp.api.c>
+
+/* Set up the API message handling tables */
+static clib_error_t *
+mssc_api_hookup (vlib_main_t *vm)
+{
+ mssc_main_t *cm = &mssc_main;
+
+ cm->msg_id_base = setup_message_id_table ();
+ return 0;
+}
+
+VLIB_API_INIT_FUNCTION (mssc_api_hookup);
+
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "TCP MSS clamping plugin",
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/mss_clamp/mss_clamp_node.c b/src/plugins/mss_clamp/mss_clamp_node.c
new file mode 100644
index 00000000000..4a40b2329e1
--- /dev/null
+++ b/src/plugins/mss_clamp/mss_clamp_node.c
@@ -0,0 +1,411 @@
+/*
+ * mss_clamp_node.c - Node implementing TCP MSS clamping
+ *
+ * Copyright (c) 2018 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/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+#include <mss_clamp/mss_clamp.h>
+#include <mss_clamp/mss_clamp.api_enum.h>
+#include <vnet/fib/fib_types.h>
+#include <vnet/feature/feature.h>
+#include <vnet/ip/ip4.h>
+#include <vnet/ip/ip6.h>
+
+extern vlib_node_registration_t mssc_ip4_in_node, mssc_ip4_out_node;
+extern vlib_node_registration_t mssc_ip6_in_node, mssc_ip6_out_node;
+
+typedef struct mssc_trace_t_
+{
+ u32 max_mss;
+ u32 clamped;
+} mssc_trace_t;
+
+/* packet trace format function */
+static u8 *
+format_mssc_trace (u8 *s, va_list *args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ mssc_trace_t *t = va_arg (*args, mssc_trace_t *);
+
+ s = format (s, "max mss: %d clamped: %d", t->max_mss, t->clamped);
+ return s;
+}
+
+typedef enum
+{
+ MSSC_NEXT_DROP,
+ MSSC_N_NEXT,
+} mssc_next_t;
+
+/*
+ * fixup the maximum segment size if it's a syn packet
+ * return 1 if the mss was changed otherwise 0
+ */
+always_inline u32
+mssc_mss_fixup (vlib_buffer_t *b0, tcp_header_t *tcp0, u16 max_mss0)
+{
+ ip_csum_t sum0;
+
+ if (PREDICT_FALSE (tcp_syn (tcp0)))
+ {
+ u8 opt_len, opts_len, kind;
+ const u8 *data;
+ u16 mss0, new_mss0;
+
+ opts_len = (tcp_doff (tcp0) << 2) - sizeof (tcp_header_t);
+ data = (const u8 *) (tcp0 + 1);
+
+ for (; opts_len > 0; opts_len -= opt_len, data += opt_len)
+ {
+ kind = data[0];
+
+ /* Get options length */
+ if (kind == TCP_OPTION_EOL)
+ break;
+ else if (kind == TCP_OPTION_NOOP)
+ {
+ opt_len = 1;
+ continue;
+ }
+ else
+ {
+ /* broken options */
+ if (opts_len < 2)
+ return 0;
+ opt_len = data[1];
+
+ /* weird option length */
+ if (opt_len < 2 || opt_len > opts_len)
+ return 0;
+ }
+
+ if (kind == TCP_OPTION_MSS)
+ {
+ mss0 = *(u16 *) (data + 2);
+ if (clib_net_to_host_u16 (mss0) > max_mss0)
+ {
+ new_mss0 = clib_host_to_net_u16 (max_mss0);
+ *((u16 *) (data + 2)) = new_mss0;
+ sum0 = tcp0->checksum;
+ sum0 = ip_csum_update (sum0, mss0, new_mss0, tcp_header_t,
+ checksum);
+ tcp0->checksum = ip_csum_fold (sum0);
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+always_inline uword
+mssc_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame,
+ vlib_dir_t dir, fib_protocol_t fproto)
+{
+ mssc_main_t *cm = &mssc_main;
+ vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
+ u16 nexts[VLIB_FRAME_SIZE], *next;
+ u32 n_left, *from;
+ u32 pkts_clamped = 0;
+
+ from = vlib_frame_vector_args (frame);
+ n_left = frame->n_vectors;
+ b = bufs;
+ next = nexts;
+
+ vlib_get_buffers (vm, from, bufs, n_left);
+
+ while (n_left >= 4)
+ {
+ u32 sw_if_index0, sw_if_index1;
+ const u8 *h0, *h1;
+ u32 clamped0, clamped1;
+
+ /* Prefetch next iteration. */
+ {
+ vlib_prefetch_buffer_header (b[2], LOAD);
+ vlib_prefetch_buffer_header (b[3], LOAD);
+ vlib_prefetch_buffer_data (b[2], LOAD);
+ vlib_prefetch_buffer_data (b[3], LOAD);
+ }
+
+ sw_if_index0 = vnet_buffer (b[0])->sw_if_index[dir];
+ sw_if_index1 = vnet_buffer (b[1])->sw_if_index[dir];
+ clamped0 = clamped1 = 0;
+
+ /* speculatively enqueue b0 to the current next frame */
+ vnet_feature_next_u16 (&next[0], b[0]);
+ vnet_feature_next_u16 (&next[1], b[1]);
+
+ h0 = (u8 *) vlib_buffer_get_current (b[0]);
+ h1 = (u8 *) vlib_buffer_get_current (b[1]);
+ if (VLIB_TX == dir)
+ {
+ h0 += vnet_buffer (b[0])->ip.save_rewrite_length;
+ h1 += vnet_buffer (b[1])->ip.save_rewrite_length;
+ }
+
+ if (FIB_PROTOCOL_IP4 == fproto)
+ {
+ ip4_header_t *ip0 = (ip4_header_t *) h0;
+ ip4_header_t *ip1 = (ip4_header_t *) h1;
+
+ if (IP_PROTOCOL_TCP == ip0->protocol)
+ {
+ clamped0 = mssc_mss_fixup (b[0], ip4_next_header (ip0),
+ cm->max_mss4[sw_if_index0]);
+ }
+ if (IP_PROTOCOL_TCP == ip1->protocol)
+ {
+ clamped1 = mssc_mss_fixup (b[1], ip4_next_header (ip1),
+ cm->max_mss4[sw_if_index1]);
+ }
+ }
+ else if (FIB_PROTOCOL_IP6 == fproto)
+ {
+ ip6_header_t *ip0 = (ip6_header_t *) h0;
+ ip6_header_t *ip1 = (ip6_header_t *) h1;
+
+ if (IP_PROTOCOL_TCP == ip0->protocol)
+ {
+ clamped0 = mssc_mss_fixup (b[0], ip6_next_header (ip0),
+ cm->max_mss6[sw_if_index0]);
+ }
+ if (IP_PROTOCOL_TCP == ip1->protocol)
+ {
+ clamped1 = mssc_mss_fixup (b[1], ip6_next_header (ip1),
+ cm->max_mss6[sw_if_index1]);
+ }
+ }
+
+ pkts_clamped += clamped0 + clamped1;
+
+ if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
+ {
+ if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
+ {
+ mssc_trace_t *t;
+
+ t = vlib_add_trace (vm, node, b[0], sizeof (*t));
+ t->max_mss = (FIB_PROTOCOL_IP4 == fproto) ?
+ cm->max_mss4[sw_if_index0] :
+ cm->max_mss6[sw_if_index0];
+ t->clamped = clamped0;
+ }
+ if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
+ {
+ mssc_trace_t *t;
+
+ t = vlib_add_trace (vm, node, b[1], sizeof (*t));
+ t->max_mss = (FIB_PROTOCOL_IP4 == fproto) ?
+ cm->max_mss4[sw_if_index1] :
+ cm->max_mss6[sw_if_index1];
+ t->clamped = clamped1;
+ }
+ }
+
+ b += 2;
+ next += 2;
+ n_left -= 2;
+ }
+
+ while (n_left > 0)
+ {
+ u32 sw_if_index0;
+ const u8 *h0;
+ u32 clamped0;
+
+ sw_if_index0 = vnet_buffer (b[0])->sw_if_index[dir];
+ clamped0 = 0;
+
+ /* speculatively enqueue b0 to the current next frame */
+ vnet_feature_next_u16 (&next[0], b[0]);
+
+ h0 = (u8 *) vlib_buffer_get_current (b[0]);
+ if (VLIB_TX == dir)
+ h0 += vnet_buffer (b[0])->ip.save_rewrite_length;
+
+ if (FIB_PROTOCOL_IP4 == fproto)
+ {
+ ip4_header_t *ip0 = (ip4_header_t *) h0;
+
+ if (IP_PROTOCOL_TCP == ip0->protocol)
+ {
+ clamped0 = mssc_mss_fixup (b[0], ip4_next_header (ip0),
+ cm->max_mss4[sw_if_index0]);
+ }
+ }
+ else if (FIB_PROTOCOL_IP6 == fproto)
+ {
+ ip6_header_t *ip0 = (ip6_header_t *) h0;
+
+ if (IP_PROTOCOL_TCP == ip0->protocol)
+ {
+ clamped0 = mssc_mss_fixup (b[0], ip6_next_header (ip0),
+ cm->max_mss6[sw_if_index0]);
+ }
+ }
+
+ pkts_clamped += clamped0;
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+ (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ mssc_trace_t *t;
+
+ t = vlib_add_trace (vm, node, b[0], sizeof (*t));
+ t->max_mss = (FIB_PROTOCOL_IP4 == fproto) ?
+ cm->max_mss4[sw_if_index0] :
+ cm->max_mss6[sw_if_index0];
+ t->clamped = clamped0;
+ }
+
+ b++;
+ next++;
+ n_left--;
+ }
+
+ vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
+ vlib_node_increment_counter (vm, node->node_index, MSS_CLAMP_ERROR_CLAMPED,
+ pkts_clamped);
+
+ return frame->n_vectors;
+}
+
+static uword
+mssc_ip4_in (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return (mssc_inline (vm, node, frame, VLIB_RX, FIB_PROTOCOL_IP4));
+}
+
+static uword
+mssc_ip4_out (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return (mssc_inline (vm, node, frame, VLIB_TX, FIB_PROTOCOL_IP4));
+}
+
+static uword
+mssc_ip6_in (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return (mssc_inline (vm, node, frame, VLIB_RX, FIB_PROTOCOL_IP6));
+}
+
+static uword
+mssc_ip6_out (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+ return (mssc_inline (vm, node, frame, VLIB_TX, FIB_PROTOCOL_IP6));
+}
+
+VLIB_REGISTER_NODE (mssc_ip4_in_node) =
+{
+ .function = mssc_ip4_in,
+ .name = "tcp-mss-clamping-ip4-in",
+ .vector_size = sizeof (u32),
+ .format_trace = format_mssc_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = MSS_CLAMP_N_ERROR,
+ .error_counters = mss_clamp_error_counters,
+
+ .n_next_nodes = MSSC_N_NEXT,
+ .next_nodes = {
+ [MSSC_NEXT_DROP] = "error-drop",
+ },
+};
+
+VLIB_REGISTER_NODE (mssc_ip4_out_node) =
+{
+ .function = mssc_ip4_out,
+ .name = "tcp-mss-clamping-ip4-out",
+ .vector_size = sizeof (u32),
+ .format_trace = format_mssc_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = MSS_CLAMP_N_ERROR,
+ .error_counters = mss_clamp_error_counters,
+
+ .n_next_nodes = MSSC_N_NEXT,
+ .next_nodes = {
+ [MSSC_NEXT_DROP] = "error-drop",
+ },
+};
+
+VLIB_REGISTER_NODE (mssc_ip6_in_node) =
+{
+ .function = mssc_ip6_in,
+ .name = "tcp-mss-clamping-ip6-in",
+ .vector_size = sizeof (u32),
+ .format_trace = format_mssc_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = MSS_CLAMP_N_ERROR,
+ .error_counters = mss_clamp_error_counters,
+
+ .n_next_nodes = MSSC_N_NEXT,
+ .next_nodes = {
+ [MSSC_NEXT_DROP] = "error-drop",
+ },
+};
+
+VLIB_REGISTER_NODE (mssc_ip6_out_node) =
+{
+ .function = mssc_ip6_out,
+ .name = "tcp-mss-clamping-ip6-out",
+ .vector_size = sizeof (u32),
+ .format_trace = format_mssc_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = MSS_CLAMP_N_ERROR,
+ .error_counters = mss_clamp_error_counters,
+
+ .n_next_nodes = MSSC_N_NEXT,
+ .next_nodes = {
+ [MSSC_NEXT_DROP] = "error-drop",
+ },
+};
+
+VNET_FEATURE_INIT (mssc_ip4_in_feat, static) = {
+ .arc_name = "ip4-unicast",
+ .node_name = "tcp-mss-clamping-ip4-in",
+ .runs_after = VNET_FEATURES ("ip4-policer-classify"),
+};
+
+VNET_FEATURE_INIT (mssc_ip4_out_feat, static) = {
+ .arc_name = "ip4-output",
+ .node_name = "tcp-mss-clamping-ip4-out",
+};
+
+VNET_FEATURE_INIT (mssc_ip6_in_feat, static) = {
+ .arc_name = "ip6-unicast",
+ .node_name = "tcp-mss-clamping-ip6-in",
+ .runs_after = VNET_FEATURES ("ip6-policer-classify"),
+};
+
+VNET_FEATURE_INIT (mssc_ip6_out_feat, static) = {
+ .arc_name = "ip6-output",
+ .node_name = "tcp-mss-clamping-ip6-out",
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/mss_clamp/test/test_mss_clamp.py b/src/plugins/mss_clamp/test/test_mss_clamp.py
new file mode 100644
index 00000000000..23495b6050b
--- /dev/null
+++ b/src/plugins/mss_clamp/test/test_mss_clamp.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python3
+
+import unittest
+
+from framework import VppTestCase, VppTestRunner
+
+from scapy.layers.inet import IP, TCP
+from scapy.layers.inet6 import IPv6
+from scapy.layers.l2 import Ether
+from scapy.packet import Raw
+
+
+class TestMSSClamp(VppTestCase):
+ """ TCP MSS Clamping Test Case """
+
+ def setUp(self):
+ super(TestMSSClamp, self).setUp()
+
+ # create 2 pg interfaces
+ self.create_pg_interfaces(range(2))
+
+ for i in self.pg_interfaces:
+ i.admin_up()
+ i.config_ip4()
+ i.resolve_arp()
+ i.config_ip6()
+ i.resolve_ndp()
+
+ def tearDown(self):
+ for i in self.pg_interfaces:
+ i.unconfig_ip4()
+ i.unconfig_ip6()
+ i.admin_down()
+ super(TestMSSClamp, self).tearDown()
+
+ def verify_pkt(self, rx, expected_mss):
+ # check that the MSS size equals the expected value
+ # and the IP and TCP checksums are correct
+ tcp = rx[TCP]
+ tcp_csum = tcp.chksum
+ del tcp.chksum
+ ip_csum = 0
+ if (rx.haslayer(IP)):
+ ip_csum = rx[IP].chksum
+ del rx[IP].chksum
+
+ opt = tcp.options
+ self.assertEqual(opt[0][0], 'MSS')
+ self.assertEqual(opt[0][1], expected_mss)
+ # recalculate checksums
+ rx = rx.__class__(bytes(rx))
+ tcp = rx[TCP]
+ self.assertEqual(tcp_csum, tcp.chksum)
+ if (rx.haslayer(IP)):
+ self.assertEqual(ip_csum, rx[IP].chksum)
+
+ def send_and_verify_ip4(self, src_pg, dst_pg, mss, expected_mss):
+ # IPv4 TCP packet with the requested MSS option.
+ # from a host on src_pg to a host on dst_pg.
+ p = (Ether(dst=src_pg.local_mac,
+ src=src_pg.remote_mac) /
+ IP(src=src_pg.remote_ip4,
+ dst=dst_pg.remote_ip4) /
+ TCP(sport=1234, dport=1234,
+ flags="S",
+ options=[('MSS', (mss)), ('EOL', None)]) /
+ Raw('\xa5' * 100))
+
+ rxs = self.send_and_expect(src_pg, p * 65, dst_pg)
+
+ for rx in rxs:
+ self.verify_pkt(rx, expected_mss)
+
+ def send_and_verify_ip6(self, src_pg, dst_pg, mss, expected_mss):
+ #
+ # IPv6 TCP packet with the requested MSS option.
+ # from a host on src_pg to a host on dst_pg.
+ #
+ p = (Ether(dst=src_pg.local_mac,
+ src=src_pg.remote_mac) /
+ IPv6(src=src_pg.remote_ip6,
+ dst=dst_pg.remote_ip6) /
+ TCP(sport=1234, dport=1234,
+ flags="S",
+ options=[('MSS', (mss)), ('EOL', None)]) /
+ Raw('\xa5' * 100))
+
+ rxs = self.send_and_expect(src_pg, p * 65, dst_pg)
+
+ for rx in rxs:
+ self.verify_pkt(rx, expected_mss)
+
+ def test_tcp_mss_clamping_ip4_tx(self):
+ """ IP4 TCP MSS Clamping TX """
+
+ # enable the TCP MSS clamping feature to lower the MSS to 1424.
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=1424, ipv6_mss=0,
+ ipv4_direction=3, ipv6_direction=0)
+
+ # Verify that the feature is enabled.
+ rv, reply = self.vapi.mss_clamp_get(sw_if_index=self.pg1.sw_if_index)
+ self.assertEqual(reply[0].ipv4_mss, 1424)
+ self.assertEqual(reply[0].ipv4_direction, 3)
+
+ # Send syn packets and verify that the MSS value is lowered.
+ self.send_and_verify_ip4(self.pg0, self.pg1, 1460, 1424)
+
+ # check the stats
+ stats = self.statistics.get_counter(
+ '/err/tcp-mss-clamping-ip4-out/clamped')
+ self.assertEqual(sum(stats), 65)
+
+ # Send syn packets with small enough MSS values and verify they are
+ # unchanged.
+ self.send_and_verify_ip4(self.pg0, self.pg1, 1400, 1400)
+
+ # enable the the feature only in TX direction
+ # and change the max MSS value
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=1420, ipv6_mss=0,
+ ipv4_direction=2, ipv6_direction=0)
+
+ # Send syn packets and verify that the MSS value is lowered.
+ self.send_and_verify_ip4(self.pg0, self.pg1, 1460, 1420)
+
+ # enable the the feature only in RX direction
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=1424, ipv6_mss=0,
+ ipv4_direction=1, ipv6_direction=0)
+
+ # Send the packets again and ensure they are unchanged.
+ self.send_and_verify_ip4(self.pg0, self.pg1, 1460, 1460)
+
+ # disable the feature
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=0, ipv6_mss=0,
+ ipv4_direction=0, ipv6_direction=0)
+
+ # Send the packets again and ensure they are unchanged.
+ self.send_and_verify_ip4(self.pg0, self.pg1, 1460, 1460)
+
+ def test_tcp_mss_clamping_ip4_rx(self):
+ """ IP4 TCP MSS Clamping RX """
+
+ # enable the TCP MSS clamping feature to lower the MSS to 1424.
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=1424, ipv6_mss=0,
+ ipv4_direction=3, ipv6_direction=0)
+
+ # Verify that the feature is enabled.
+ rv, reply = self.vapi.mss_clamp_get(sw_if_index=self.pg1.sw_if_index)
+ self.assertEqual(reply[0].ipv4_mss, 1424)
+ self.assertEqual(reply[0].ipv4_direction, 3)
+
+ # Send syn packets and verify that the MSS value is lowered.
+ self.send_and_verify_ip4(self.pg1, self.pg0, 1460, 1424)
+
+ # check the stats
+ stats = self.statistics.get_counter(
+ '/err/tcp-mss-clamping-ip4-in/clamped')
+ self.assertEqual(sum(stats), 65)
+
+ # Send syn packets with small enough MSS values and verify they are
+ # unchanged.
+ self.send_and_verify_ip4(self.pg1, self.pg0, 1400, 1400)
+
+ # enable the the feature only in RX direction
+ # and change the max MSS value
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=1420, ipv6_mss=0,
+ ipv4_direction=1, ipv6_direction=0)
+
+ # Send syn packets and verify that the MSS value is lowered.
+ self.send_and_verify_ip4(self.pg1, self.pg0, 1460, 1420)
+
+ # enable the the feature only in TX direction
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=1424, ipv6_mss=0,
+ ipv4_direction=2, ipv6_direction=0)
+
+ # Send the packets again and ensure they are unchanged.
+ self.send_and_verify_ip4(self.pg1, self.pg0, 1460, 1460)
+
+ # disable the feature
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=0, ipv6_mss=0,
+ ipv4_direction=0, ipv6_direction=0)
+
+ # Send the packets again and ensure they are unchanged.
+ self.send_and_verify_ip4(self.pg1, self.pg0, 1460, 1460)
+
+ def test_tcp_mss_clamping_ip6_tx(self):
+ """ IP6 TCP MSS Clamping TX """
+
+ # enable the TCP MSS clamping feature to lower the MSS to 1424.
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=0, ipv6_mss=1424,
+ ipv4_direction=0, ipv6_direction=3)
+
+ # Verify that the feature is enabled.
+ rv, reply = self.vapi.mss_clamp_get(sw_if_index=self.pg1.sw_if_index)
+ self.assertEqual(reply[0].ipv6_mss, 1424)
+ self.assertEqual(reply[0].ipv6_direction, 3)
+
+ # Send syn packets and verify that the MSS value is lowered.
+ self.send_and_verify_ip6(self.pg0, self.pg1, 1460, 1424)
+
+ # check the stats
+ stats = self.statistics.get_counter(
+ '/err/tcp-mss-clamping-ip6-out/clamped')
+ self.assertEqual(sum(stats), 65)
+
+ # Send syn packets with small enough MSS values and verify they are
+ # unchanged.
+ self.send_and_verify_ip6(self.pg0, self.pg1, 1400, 1400)
+
+ # enable the the feature only in TX direction
+ # and change the max MSS value
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=0, ipv6_mss=1420,
+ ipv4_direction=0, ipv6_direction=2)
+
+ # Send syn packets and verify that the MSS value is lowered.
+ self.send_and_verify_ip6(self.pg0, self.pg1, 1460, 1420)
+
+ # enable the the feature only in RX direction
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=0, ipv6_mss=1424,
+ ipv4_direction=0, ipv6_direction=1)
+
+ # Send the packets again and ensure they are unchanged.
+ self.send_and_verify_ip6(self.pg0, self.pg1, 1460, 1460)
+
+ # disable the feature
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=0, ipv6_mss=0,
+ ipv4_direction=0, ipv6_direction=0)
+
+ # Send the packets again and ensure they are unchanged.
+ self.send_and_verify_ip6(self.pg0, self.pg1, 1460, 1460)
+
+ def test_tcp_mss_clamping_ip6_rx(self):
+ """ IP6 TCP MSS Clamping RX """
+
+ # enable the TCP MSS clamping feature to lower the MSS to 1424.
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=0, ipv6_mss=1424,
+ ipv4_direction=0, ipv6_direction=3)
+
+ # Verify that the feature is enabled.
+ rv, reply = self.vapi.mss_clamp_get(sw_if_index=self.pg1.sw_if_index)
+ self.assertEqual(reply[0].ipv6_mss, 1424)
+ self.assertEqual(reply[0].ipv6_direction, 3)
+
+ # Send syn packets and verify that the MSS value is lowered.
+ self.send_and_verify_ip6(self.pg1, self.pg0, 1460, 1424)
+
+ # check the stats
+ stats = self.statistics.get_counter(
+ '/err/tcp-mss-clamping-ip6-in/clamped')
+ self.assertEqual(sum(stats), 65)
+
+ # Send syn packets with small enough MSS values and verify they are
+ # unchanged.
+ self.send_and_verify_ip6(self.pg1, self.pg0, 1400, 1400)
+
+ # enable the the feature only in RX direction
+ # and change the max MSS value
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=0, ipv6_mss=1420,
+ ipv4_direction=0, ipv6_direction=1)
+
+ # Send syn packets and verify that the MSS value is lowered.
+ self.send_and_verify_ip6(self.pg1, self.pg0, 1460, 1420)
+
+ # enable the the feature only in TX direction
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=0, ipv6_mss=1424,
+ ipv4_direction=0, ipv6_direction=2)
+
+ # Send the packets again and ensure they are unchanged.
+ self.send_and_verify_ip6(self.pg1, self.pg0, 1460, 1460)
+
+ # disable the feature
+ self.vapi.mss_clamp_enable_disable(self.pg1.sw_if_index,
+ ipv4_mss=0, ipv6_mss=0,
+ ipv4_direction=0, ipv6_direction=0)
+
+ # Send the packets again and ensure they are unchanged.
+ self.send_and_verify_ip6(self.pg1, self.pg0, 1460, 1460)
+
+
+if __name__ == '__main__':
+ unittest.main(testRunner=VppTestRunner)