CGN: configurable timeouts 58/6158/2
authorMatus Fabian <matfabia@cisco.com>
Wed, 12 Apr 2017 10:36:13 +0000 (03:36 -0700)
committerOle Trøan <otroan@employees.org>
Wed, 12 Apr 2017 13:09:34 +0000 (13:09 +0000)
add API and CLI configuration of deterministic NAT session timeout for TCP, UDP
and ICMP protocol

Change-Id: I577440452e7eaedcb5d80501a7fd4b76e31e8c9c
Signed-off-by: Matus Fabian <matfabia@cisco.com>
src/plugins/snat/in2out.c
src/plugins/snat/snat.api
src/plugins/snat/snat.c
src/plugins/snat/snat.h
src/plugins/snat/snat_test.c
test/test_snat.py
test/vpp_papi_provider.py

index 2dbaeb4..1e8e144 100644 (file)
@@ -1687,16 +1687,16 @@ snat_det_in2out_node_fn (vlib_main_t * vm,
           switch(ses0->state)
             {
             case SNAT_SESSION_UDP_ACTIVE:
-                ses0->expire = now + SNAT_UDP_TIMEOUT;
+                ses0->expire = now + sm->udp_timeout;
                 break;
             case SNAT_SESSION_TCP_SYN_SENT:
             case SNAT_SESSION_TCP_FIN_WAIT:
             case SNAT_SESSION_TCP_CLOSE_WAIT:
             case SNAT_SESSION_TCP_LAST_ACK:
-                ses0->expire = now + SNAT_TCP_TRANSITORY_TIMEOUT;
+                ses0->expire = now + sm->tcp_transitory_timeout;
                 break;
             case SNAT_SESSION_TCP_ESTABLISHED:
-                ses0->expire = now + SNAT_TCP_ESTABLISHED_TIMEOUT;
+                ses0->expire = now + sm->tcp_established_timeout;
                 break;
             }
 
@@ -1831,16 +1831,16 @@ snat_det_in2out_node_fn (vlib_main_t * vm,
           switch(ses1->state)
             {
             case SNAT_SESSION_UDP_ACTIVE:
-                ses1->expire = now + SNAT_UDP_TIMEOUT;
+                ses1->expire = now + sm->udp_timeout;
                 break;
             case SNAT_SESSION_TCP_SYN_SENT:
             case SNAT_SESSION_TCP_FIN_WAIT:
             case SNAT_SESSION_TCP_CLOSE_WAIT:
             case SNAT_SESSION_TCP_LAST_ACK:
-                ses1->expire = now + SNAT_TCP_TRANSITORY_TIMEOUT;
+                ses1->expire = now + sm->tcp_transitory_timeout;
                 break;
             case SNAT_SESSION_TCP_ESTABLISHED:
-                ses1->expire = now + SNAT_TCP_ESTABLISHED_TIMEOUT;
+                ses1->expire = now + sm->tcp_established_timeout;
                 break;
             }
 
@@ -2011,16 +2011,16 @@ snat_det_in2out_node_fn (vlib_main_t * vm,
           switch(ses0->state)
             {
             case SNAT_SESSION_UDP_ACTIVE:
-                ses0->expire = now + SNAT_UDP_TIMEOUT;
+                ses0->expire = now + sm->udp_timeout;
                 break;
             case SNAT_SESSION_TCP_SYN_SENT:
             case SNAT_SESSION_TCP_FIN_WAIT:
             case SNAT_SESSION_TCP_CLOSE_WAIT:
             case SNAT_SESSION_TCP_LAST_ACK:
-                ses0->expire = now + SNAT_TCP_TRANSITORY_TIMEOUT;
+                ses0->expire = now + sm->tcp_transitory_timeout;
                 break;
             case SNAT_SESSION_TCP_ESTABLISHED:
-                ses0->expire = now + SNAT_TCP_ESTABLISHED_TIMEOUT;
+                ses0->expire = now + sm->tcp_established_timeout;
                 break;
             }
 
@@ -2218,7 +2218,7 @@ u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node,
   u32 now = (u32) vlib_time_now (sm->vlib_main);
 
   ses0->state = SNAT_SESSION_ICMP_ACTIVE;
-  ses0->expire = now + SNAT_ICMP_TIMEOUT;
+  ses0->expire = now + sm->icmp_timeout;
 
 out:
   *p_proto = protocol;
index 92aa32b..9689f5f 100644 (file)
@@ -126,7 +126,8 @@ define snat_interface_details {
     @param local_port - local port number
     @param external_port - external port number
     @param external_sw_if_index - external interface (if set
-                                  external_ip_address is ignored)
+                                  external_ip_address is ignored, ~0 means not
+                                  used)
     @param vfr_id - VRF ID
 */
 define snat_add_static_mapping {
@@ -546,3 +547,55 @@ define snat_det_map_details {
   u16 ports_per_host;
   u32 ses_num;
 };
+
+/** \brief Set values of timeouts for deterministic NAT (seconds, 0 = default)
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param udp - UDP timeout (default 300sec)
+    @param tcp_established - TCP established timeout (default 7440sec)
+    @param tcp_transitory - TCP transitory timeout (default 240sec)
+    @param icmp - ICMP timeout (default 60sec)
+*/
+define snat_det_set_timeouts {
+  u32 client_index;
+  u32 context;
+  u32 udp;
+  u32 tcp_established;
+  u32 tcp_transitory;
+  u32 icmp;
+};
+
+/** \brief Set values of timeouts for deterministic NAT reply
+    @param context - sender context, to match reply w/ request
+    @param retval - return code
+*/
+define snat_det_set_timeouts_reply {
+  u32 context;
+  i32 retval;
+};
+
+/** \brief Get values of timeouts for deterministic NAT (seconds)
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define snat_det_get_timeouts {
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief Get values of timeouts for deterministic NAT reply
+    @param context - sender context, to match reply w/ request
+    @param retval - return code
+    @param udp - UDP timeout (default 300sec)
+    @param tcp_established - TCP established timeout (default 7440sec)
+    @param tcp_transitory - TCP transitory timeout (default 240sec)
+    @param icmp - ICMP timeout (default 60sec)
+*/
+define snat_det_get_timeouts_reply {
+  u32 context;
+  i32 retval;
+  u32 udp;
+  u32 tcp_established;
+  u32 tcp_transitory;
+  u32 icmp;
+};
index 65d26b3..24ef5df 100644 (file)
@@ -1654,6 +1654,64 @@ static void * vl_api_snat_det_map_dump_t_print
   FINISH;
 }
 
+static void
+vl_api_snat_det_set_timeouts_t_handler
+(vl_api_snat_det_set_timeouts_t * mp)
+{
+  snat_main_t * sm = &snat_main;
+  vl_api_snat_det_set_timeouts_reply_t * rmp;
+  int rv = 0;
+
+  sm->udp_timeout = ntohl(mp->udp);
+  sm->tcp_established_timeout = ntohl(mp->tcp_established);
+  sm->tcp_transitory_timeout = ntohl(mp->tcp_transitory);
+  sm->icmp_timeout = ntohl(mp->icmp);
+
+  REPLY_MACRO (VL_API_SNAT_DET_SET_TIMEOUTS_REPLY);
+}
+
+static void *vl_api_snat_det_set_timeouts_t_print
+(vl_api_snat_det_set_timeouts_t *mp, void * handle)
+{
+  u8 * s;
+
+  s = format (0, "SCRIPT: snat_det_set_timeouts ");
+  s = format (s, "udp %d tcp_established %d tcp_transitory %d icmp %d\n",
+              ntohl(mp->udp),
+              ntohl(mp->tcp_established),
+              ntohl(mp->tcp_transitory),
+              ntohl(mp->icmp));
+
+  FINISH;
+}
+
+static void
+vl_api_snat_det_get_timeouts_t_handler
+(vl_api_snat_det_get_timeouts_t * mp)
+{
+  snat_main_t * sm = &snat_main;
+  vl_api_snat_det_get_timeouts_reply_t * rmp;
+  int rv = 0;
+
+  REPLY_MACRO2(VL_API_SNAT_DET_GET_TIMEOUTS_REPLY,
+  ({
+    rmp->udp = htonl(sm->udp_timeout);
+    rmp->tcp_established = htonl(sm->tcp_established_timeout);
+    rmp->tcp_transitory = htonl(sm->tcp_transitory_timeout);
+    rmp->icmp = htonl(sm->icmp_timeout);
+  }))
+}
+
+static void *vl_api_snat_det_get_timeouts_t_print
+(vl_api_snat_det_get_timeouts_t * mp, void * handle)
+{
+  u8 * s;
+
+  s = format(0, "SCRIPT: snat_det_get_timeouts");
+
+  FINISH;
+}
+
 /* List of message types that this plugin understands */
 #define foreach_snat_plugin_api_msg                                     \
 _(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range)                       \
@@ -1674,7 +1732,10 @@ _(SNAT_USER_SESSION_DUMP, snat_user_session_dump)                       \
 _(SNAT_ADD_DET_MAP, snat_add_det_map)                                   \
 _(SNAT_DET_FORWARD, snat_det_forward)                                   \
 _(SNAT_DET_REVERSE, snat_det_reverse)                                   \
-_(SNAT_DET_MAP_DUMP, snat_det_map_dump)
+_(SNAT_DET_MAP_DUMP, snat_det_map_dump)                                 \
+_(SNAT_DET_SET_TIMEOUTS, snat_det_set_timeouts)                         \
+_(SNAT_DET_GET_TIMEOUTS, snat_det_get_timeouts)
+
 
 /* Set up the API message handling tables */
 static clib_error_t *
@@ -1758,6 +1819,10 @@ static clib_error_t * snat_init (vlib_main_t * vm)
   sm->workers = 0;
   sm->fq_in2out_index = ~0;
   sm->fq_out2in_index = ~0;
+  sm->udp_timeout = SNAT_UDP_TIMEOUT;
+  sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
+  sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
+  sm->icmp_timeout = SNAT_ICMP_TIMEOUT;
 
   p = hash_get_mem (tm->thread_registrations_by_name, "workers");
   if (p)
@@ -2855,6 +2920,12 @@ show_snat_command_fn (vlib_main_t * vm,
 
   if (sm->deterministic)
     {
+      vlib_cli_output (vm, "udp timeout: %dsec", sm->udp_timeout);
+      vlib_cli_output (vm, "tcp-established timeout: %dsec",
+                       sm->tcp_established_timeout);
+      vlib_cli_output (vm, "tcp-transitory timeout: %dsec",
+                       sm->tcp_transitory_timeout);
+      vlib_cli_output (vm, "icmp timeout: %dsec", sm->icmp_timeout);
       vlib_cli_output (vm, "%d deterministic mappings",
                        pool_elts (sm->det_maps));
       if (verbose > 0)
@@ -3346,3 +3417,69 @@ VLIB_CLI_COMMAND (snat_det_reverse_command, static) = {
     .short_help = "snat deterministic reverse <addr>:<port>",
     .function = snat_det_reverse_command_fn,
 };
+
+static clib_error_t *
+set_timeout_command_fn (vlib_main_t * vm,
+                        unformat_input_t * input,
+                        vlib_cli_command_t * cmd)
+{
+  snat_main_t *sm = &snat_main;
+  unformat_input_t _line_input, *line_input = &_line_input;
+  clib_error_t *error = 0;
+
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "udp %u", &sm->udp_timeout))
+        ;
+      else if (unformat (line_input, "tcp-established %u",
+               &sm->tcp_established_timeout))
+        ;
+      else if (unformat (line_input, "tcp-transitory %u",
+               &sm->tcp_transitory_timeout))
+        ;
+      else if (unformat (line_input, "icmp %u", &sm->icmp_timeout))
+        ;
+      else if (unformat (line_input, "reset"))
+        {
+          sm->udp_timeout = SNAT_UDP_TIMEOUT;
+          sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
+          sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
+          sm->icmp_timeout = SNAT_ICMP_TIMEOUT;
+        }
+      else
+        {
+          error = clib_error_return (0, "unknown input '%U'",
+                                     format_unformat_error, line_input);
+          goto done;
+        }
+    }
+
+  unformat_free (line_input);
+
+done:
+  unformat_free (line_input);
+
+  return error;
+}
+
+/*?
+ * @cliexpar
+ * @cliexstart{set snat deterministic timeout}
+ * Set values of timeouts for deterministic NAT (in seconds), use:
+ *  vpp# set snat deterministic timeout udp 120 tcp-established 7500
+ *  tcp-transitory 250 icmp 90
+ * To reset default values use:
+ *  vpp# set snat deterministic timeout reset
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (set_timeout_command, static) = {
+  .path = "set snat deterministic timeout",
+  .function = set_timeout_command_fn,
+  .short_help =
+    "set snat deterministic timeout [udp <sec> | tcp-established <sec> "
+    "tcp-transitory <sec> | icmp <sec> | reset]",
+};
index d0ffea7..92bc8d1 100644 (file)
@@ -312,6 +312,12 @@ typedef struct snat_main_s {
   /* tenant VRF aware address pool activation flag */
   u8 vrf_mode;
 
+  /* values of various timeouts */
+  u32 udp_timeout;
+  u32 tcp_established_timeout;
+  u32 tcp_transitory_timeout;
+  u32 icmp_timeout;
+
   /* API message ID base */
   u16 msg_id_base;
 
index e26a395..4117d94 100644 (file)
@@ -21,6 +21,7 @@
 #include <vlibsocket/api.h>
 #include <vppinfra/error.h>
 #include <vnet/ip/ip.h>
+#include <snat/snat.h>
 
 #define __plugin_msg_base snat_test_main.msg_id_base
 #include <vlibapi/vat_helper_macros.h>
@@ -67,7 +68,8 @@ _(snat_add_static_mapping_reply)                \
 _(snat_set_workers_reply)                       \
 _(snat_add_del_interface_addr_reply)            \
 _(snat_ipfix_enable_disable_reply)              \
-_(snat_add_det_map_reply)
+_(snat_add_det_map_reply)                       \
+_(snat_det_set_timeouts_reply)
 
 #define _(n)                                            \
     static void vl_api_##n##_t_handler                  \
@@ -111,7 +113,9 @@ _(SNAT_USER_SESSION_DETAILS, snat_user_session_details)         \
 _(SNAT_ADD_DET_MAP_REPLY, snat_add_det_map_reply)               \
 _(SNAT_DET_FORWARD_REPLY, snat_det_forward_reply)               \
 _(SNAT_DET_REVERSE_REPLY, snat_det_reverse_reply)               \
-_(SNAT_DET_MAP_DETAILS, snat_det_map_details)
+_(SNAT_DET_MAP_DETAILS, snat_det_map_details)                   \
+_(SNAT_DET_SET_TIMEOUTS_REPLY, snat_det_set_timeouts_reply)     \
+_(SNAT_DET_GET_TIMEOUTS_REPLY, snat_det_get_timeouts_reply)
 
 static int api_snat_add_address_range (vat_main_t * vam)
 {
@@ -893,6 +897,78 @@ static int api_snat_det_map_dump(vat_main_t * vam)
   return ret;
 }
 
+static int api_snat_det_set_timeouts (vat_main_t * vam)
+{
+  unformat_input_t * i = vam->input;
+  vl_api_snat_det_set_timeouts_t * mp;
+  u32 udp = SNAT_UDP_TIMEOUT;
+  u32 tcp_established = SNAT_TCP_ESTABLISHED_TIMEOUT;
+  u32 tcp_transitory = SNAT_TCP_TRANSITORY_TIMEOUT;
+  u32 icmp = SNAT_ICMP_TIMEOUT;
+  int ret;
+
+  if (unformat (i, "udp %d", &udp))
+    ;
+  else if (unformat (i, "tcp_established %d", &tcp_established))
+    ;
+  else if (unformat (i, "tcp_transitory %d", &tcp_transitory))
+    ;
+  else if (unformat (i, "icmp %d", &icmp))
+    ;
+  else
+    {
+      clib_warning("unknown input '%U'", format_unformat_error, i);
+      return -99;
+    }
+
+  M(SNAT_DET_SET_TIMEOUTS, mp);
+  mp->udp = htonl(udp);
+  mp->tcp_established = htonl(tcp_established);
+  mp->tcp_transitory = htonl(tcp_transitory);
+  mp->icmp = htonl(icmp);
+
+  S(mp);
+  W (ret);
+  return ret;
+}
+
+static void vl_api_snat_det_get_timeouts_reply_t_handler
+  (vl_api_snat_det_get_timeouts_reply_t *mp)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  vat_main_t *vam = sm->vat_main;
+  i32 retval = ntohl (mp->retval);
+
+  if (retval >= 0)
+    {
+      fformat (vam->ofp, "udp timeout: %dsec\n", ntohl (mp->udp));
+      fformat (vam->ofp, "tcp-established timeout: %dsec",
+               ntohl (mp->tcp_established));
+      fformat (vam->ofp, "tcp-transitory timeout: %dsec",
+               ntohl (mp->tcp_transitory));
+      fformat (vam->ofp, "icmp timeout: %dsec", ntohl (mp->icmp));
+    }
+  vam->retval = retval;
+  vam->result_ready = 1;
+}
+
+static int api_snat_det_get_timeouts(vat_main_t * vam)
+{
+  vl_api_snat_det_get_timeouts_t * mp;
+  int ret;
+
+  if (vam->json_output)
+    {
+      clib_warning ("JSON output not supported for snat_show_config");
+      return -99;
+    }
+
+  M(SNAT_DET_GET_TIMEOUTS, mp);
+  S(mp);
+  W (ret);
+  return ret;
+}
+
 /* 
  * List of messages that the api test plugin sends,
  * and that the data plane plugin processes
@@ -922,7 +998,10 @@ _(snat_add_det_map, "in <in_addr>/<in_plen> out "                \
   "<out_addr>/<out_plen> [del]")                                 \
 _(snat_det_forward, "<in_addr>")                                 \
 _(snat_det_reverse, "<out_addr> <out_port>")                     \
-_(snat_det_map_dump, "")
+_(snat_det_map_dump, "")                                         \
+_(snat_det_set_timeouts, "[udp <sec> | tcp_established <sec> | " \
+  "tcp_transitory <sec> | icmp <sec>]")                          \
+_(snat_det_get_timeouts, "")
 
 static void 
 snat_vat_api_hookup (vat_main_t *vam)
index 0708d44..4739a7c 100644 (file)
@@ -1345,10 +1345,29 @@ class TestDeterministicNAT(MethodHolder):
         deterministic_mappings = self.vapi.snat_det_map_dump()
         self.assertEqual(len(deterministic_mappings), 0)
 
+    def test_set_timeouts(self):
+        """ Set deterministic NAT timeouts """
+        timeouts_before = self.vapi.snat_det_get_timeouts()
+
+        self.vapi.snat_det_set_timeouts(timeouts_before.udp + 10,
+                                        timeouts_before.tcp_established + 10,
+                                        timeouts_before.tcp_transitory + 10,
+                                        timeouts_before.icmp + 10)
+
+        timeouts_after = self.vapi.snat_det_get_timeouts()
+
+        self.assertNotEqual(timeouts_before.udp, timeouts_after.udp)
+        self.assertNotEqual(timeouts_before.icmp, timeouts_after.icmp)
+        self.assertNotEqual(timeouts_before.tcp_established,
+                            timeouts_after.tcp_established)
+        self.assertNotEqual(timeouts_before.tcp_transitory,
+                            timeouts_after.tcp_transitory)
+
     def clear_snat(self):
         """
         Clear SNAT configuration.
         """
+        self.vapi.snat_det_set_timeouts()
         deterministic_mappings = self.vapi.snat_det_map_dump()
         for dsm in deterministic_mappings:
             self.vapi.snat_add_det_map(dsm.in_addr,
index ceb684b..4541f01 100644 (file)
@@ -1199,6 +1199,33 @@ class VppPapiProvider(object):
         """
         return self.api(self.papi.snat_det_map_dump, {})
 
+    def snat_det_set_timeouts(
+            self,
+            udp=300,
+            tcp_established=7440,
+            tcp_transitory=240,
+            icmp=60):
+        """Set values of timeouts for deterministic NAT (in seconds)
+
+        :param udp - UDP timeout (Default value = 300)
+        :param tcp_established - TCP established timeout (Default value = 7440)
+        :param tcp_transitory - TCP transitory timeout (Default value = 240)
+        :param icmp - ICMP timeout (Default value = 60)
+        """
+        return self.api(
+            self.papi.snat_det_set_timeouts,
+            {'udp': udp,
+             'tcp_established': tcp_established,
+             'tcp_transitory': tcp_transitory,
+             'icmp': icmp})
+
+    def snat_det_get_timeouts(self):
+        """Get values of timeouts for deterministic NAT
+
+        :return: Timeouts for deterministic NAT (in seconds)
+        """
+        return self.api(self.papi.snat_det_get_timeouts, {})
+
     def control_ping(self):
         self.api(self.papi.control_ping)