SNAT: user's dump and session dump of a certain snat user. 34/5534/4
authormagalik <magalik@cisco.com>
Thu, 9 Feb 2017 07:25:45 +0000 (23:25 -0800)
committerOle Trøan <otroan@employees.org>
Thu, 2 Mar 2017 18:20:38 +0000 (18:20 +0000)
Change-Id: If75a35dbdcb43c1ce0128b8649f2ca3970d3fff5
Signed-off-by: Martin <magalik@cisco.com>
src/plugins/snat/in2out.c
src/plugins/snat/out2in.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 b4b7793..c6dc7ca 100644 (file)
@@ -247,6 +247,7 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
       pool_get (sm->per_thread_data[cpu_index].users, u);
       memset (u, 0, sizeof (*u));
       u->addr = ip0->src_address;
+      u->fib_index = rx_fib_index0;
 
       pool_get (sm->per_thread_data[cpu_index].list_pool, per_user_list_head_elt);
 
index 3bfc0aa..7905436 100644 (file)
@@ -147,6 +147,7 @@ create_session_for_static_mapping (snat_main_t *sm,
       pool_get (sm->per_thread_data[cpu_index].users, u);
       memset (u, 0, sizeof (*u));
       u->addr = in2out.addr;
+      u->fib_index = in2out.fib_index;
 
       pool_get (sm->per_thread_data[cpu_index].list_pool,
                 per_user_list_head_elt);
index c429f05..3462a8d 100644 (file)
@@ -351,3 +351,69 @@ define snat_ipfix_enable_disable_reply {
   u32 context;
   i32 retval;
 };
+
+/** \brief Dump S-NAT users
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define snat_user_dump {
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief S-NAT users response
+    @param context - sender context, to match reply w/ request
+    @vrf_id - VRF ID
+    @param is_ip4 - 1 if address type is IPv4
+    @param ip_adress - IP address
+    @param nsessions - number of dynamic sessions
+    @param nstaticsessions - number of static sessions
+*/
+define snat_user_details {
+  u32 context;
+  u32 vrf_id;
+  u8 is_ip4;
+  u8 ip_address[16];
+  u32 nsessions;
+  u32 nstaticsessions;
+};
+
+/** \brief S-NAT user's sessions
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param user_ip - IP address of the user to dump
+    @param vrf_id - VRF_ID
+*/
+define snat_user_session_dump {
+  u32 client_index;
+  u32 context;
+  u8 ip_address[16];
+  u32 vrf_id;
+};
+
+/** \brief S-NAT user's sessions response
+    @param context - sender context, to match reply w/ request
+    @param is_ip4 - 1 if address type is IPv4
+    @param outside_ip_address - outside IP address
+    @param outside_port - outside port
+    @param inside_ip_address - inside IP address
+    @param inside_port - inside port
+    @param protocol - protocol
+    @param is_static - 1 if session is static
+    @param last_heard - last heard timer
+    @param total_bytes - count of bytes sent through session
+    @param total_pkts - count of pakets sent through session
+*/
+define snat_user_session_details {
+  u32 context;
+  u8 is_ip4;
+  u8 outside_ip_address[16];
+  u16 outside_port;
+  u8 inside_ip_address[16];
+  u16 inside_port;
+  u16 protocol;
+  u8 is_static;
+  f64 last_heard;
+  u64 total_bytes;
+  u32 total_pkts;
+};
index 8c2bacd..0ec20ef 100644 (file)
@@ -1412,6 +1412,144 @@ static void *vl_api_snat_ipfix_enable_disable_t_print
   FINISH;
 }
 
+static void
+send_snat_user_details
+(snat_user_t * u, unix_shared_memory_queue_t * q, u32 context)
+{
+  vl_api_snat_user_details_t * rmp;
+  snat_main_t * sm = &snat_main;
+  ip4_fib_t * fib_table;
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+  rmp->_vl_msg_id = ntohs (VL_API_SNAT_USER_DETAILS+sm->msg_id_base);
+
+  fib_table = ip4_fib_get(u->fib_index);
+  rmp->vrf_id = ntohl (fib_table->table_id);
+
+  rmp->is_ip4 = 1;
+  clib_memcpy(rmp->ip_address, &(u->addr), 4);
+  rmp->nsessions = ntohl (u->nsessions);
+  rmp->nstaticsessions = ntohl (u->nstaticsessions);
+  rmp->context = context;
+
+  vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+vl_api_snat_user_dump_t_handler
+(vl_api_snat_user_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+  snat_main_t * sm = &snat_main;
+  snat_main_per_thread_data_t * tsm;
+  snat_user_t * u;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (q == 0)
+    return;
+
+  vec_foreach (tsm, sm->per_thread_data)
+    vec_foreach (u, tsm->users)
+      send_snat_user_details (u, q, mp->context);
+}
+
+static void *vl_api_snat_user_dump_t_print
+(vl_api_snat_user_dump_t *mp, void * handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: snat_user_dump ");
+
+  FINISH;
+}
+
+static void
+send_snat_user_session_details
+(snat_session_t * s, unix_shared_memory_queue_t * q, u32 context)
+{
+  vl_api_snat_user_session_details_t * rmp;
+  snat_main_t * sm = &snat_main;
+
+  rmp = vl_msg_api_alloc (sizeof(*rmp));
+  memset (rmp, 0, sizeof (*rmp));
+  rmp->_vl_msg_id = ntohs (VL_API_SNAT_USER_SESSION_DETAILS+sm->msg_id_base);
+  rmp->is_ip4 = 1;
+  clib_memcpy(rmp->outside_ip_address, (&s->out2in.addr), 4);
+  rmp->outside_port = s->out2in.port;
+  clib_memcpy(rmp->inside_ip_address, (&s->in2out.addr), 4);
+  rmp->inside_port = s->in2out.port;
+  rmp->protocol = ntohs(snat_proto_to_ip_proto(s->in2out.protocol));
+  rmp->is_static = s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING ? 1 : 0;
+  rmp->last_heard = ntohl(s->last_heard);
+  rmp->total_bytes = ntohl(s->total_bytes);
+  rmp->total_pkts = ntohl(s->total_pkts);
+  rmp->context = context;
+
+  vl_msg_api_send_shmem (q, (u8 *) & rmp);
+}
+
+static void
+vl_api_snat_user_session_dump_t_handler
+(vl_api_snat_user_session_dump_t * mp)
+{
+  unix_shared_memory_queue_t *q;
+  snat_main_t * sm = &snat_main;
+  snat_main_per_thread_data_t *tsm;
+  snat_session_t * s;
+  clib_bihash_kv_8_8_t key, value;
+  snat_user_key_t ukey;
+  snat_user_t * u;
+  u32 session_index, head_index, elt_index;
+  dlist_elt_t * head, * elt;
+
+  q = vl_api_client_index_to_input_queue (mp->client_index);
+  if (q == 0)
+    return;
+
+  clib_memcpy (&ukey.addr, mp->ip_address, 4);
+  ukey.fib_index = ip4_fib_index_from_table_id (ntohl(mp->vrf_id));
+  key.key = ukey.as_u64;
+  if (!clib_bihash_search_8_8 (&sm->worker_by_in, &key, &value))
+    tsm = vec_elt_at_index (sm->per_thread_data, value.value);
+  else
+    tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
+  if (clib_bihash_search_8_8 (&sm->user_hash, &key, &value))
+    return;
+  u = pool_elt_at_index (tsm->users, value.value);
+  if (!u->nsessions && !u->nstaticsessions)
+    return;
+
+  head_index = u->sessions_per_user_list_head_index;
+  head = pool_elt_at_index (tsm->list_pool, head_index);
+  elt_index = head->next;
+  elt = pool_elt_at_index (tsm->list_pool, elt_index);
+  session_index = elt->value;
+  while (session_index != ~0)
+    {
+      s = pool_elt_at_index (tsm->sessions, session_index);
+
+      send_snat_user_session_details (s, q, mp->context);
+
+      elt_index = elt->next;
+      elt = pool_elt_at_index (tsm->list_pool, elt_index);
+      session_index = elt->value;
+    }
+}
+
+static void *vl_api_snat_user_session_dump_t_print
+(vl_api_snat_user_session_dump_t *mp, void * handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: snat_user_session_dump ");
+  s = format (s, "ip_address %U vrf_id %d\n",
+              format_ip4_address, mp->ip_address,
+              clib_net_to_host_u32 (mp->vrf_id));
+
+  FINISH;
+}
+
 /* List of message types that this plugin understands */
 #define foreach_snat_plugin_api_msg                                     \
 _(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range)                       \
@@ -1426,7 +1564,9 @@ _(SNAT_SET_WORKERS, snat_set_workers)                                   \
 _(SNAT_WORKER_DUMP, snat_worker_dump)                                   \
 _(SNAT_ADD_DEL_INTERFACE_ADDR, snat_add_del_interface_addr)             \
 _(SNAT_INTERFACE_ADDR_DUMP, snat_interface_addr_dump)                   \
-_(SNAT_IPFIX_ENABLE_DISABLE, snat_ipfix_enable_disable)
+_(SNAT_IPFIX_ENABLE_DISABLE, snat_ipfix_enable_disable)                 \
+_(SNAT_USER_DUMP, snat_user_dump)                                       \
+_(SNAT_USER_SESSION_DUMP, snat_user_session_dump)
 
 /* Set up the API message handling tables */
 static clib_error_t *
index 39cbd3f..47f2e6e 100644 (file)
@@ -110,6 +110,7 @@ typedef CLIB_PACKED(struct {
 
 typedef struct {
   ip4_address_t addr;
+  u32 fib_index;
   u32 sessions_per_user_list_head_index;
   u32 nsessions;
   u32 nstaticsessions;
index a299e0a..dae63a2 100644 (file)
@@ -104,7 +104,9 @@ _(SNAT_ADD_DEL_INTERFACE_ADDR_REPLY,                            \
   snat_add_del_interface_addr_reply)                            \
 _(SNAT_INTERFACE_ADDR_DETAILS, snat_interface_addr_details)     \
 _(SNAT_IPFIX_ENABLE_DISABLE_REPLY,                              \
-  snat_ipfix_enable_disable_reply)
+  snat_ipfix_enable_disable_reply)                              \
+_(SNAT_USER_DETAILS, snat_user_details)                         \
+_(SNAT_USER_SESSION_DETAILS, snat_user_session_details)
 
 static int api_snat_add_address_range (vat_main_t * vam)
 {
@@ -645,6 +647,78 @@ static int api_snat_ipfix_enable_disable (vat_main_t * vam)
   return ret;
 }
 
+static void vl_api_snat_user_session_details_t_handler
+  (vl_api_snat_user_session_details_t *mp)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  vat_main_t *vam = sm->vat_main;
+
+  fformat(vam->ofp, "%s session %U:%d to %U:%d protocol id %d "
+                    "total packets %d total bytes %d\n",
+          mp->is_static ? "static" : "dynamic",
+          format_ip4_address, mp->inside_ip_address, ntohl(mp->inside_port),
+          format_ip4_address, mp->outside_ip_address, ntohl(mp->outside_port),
+          ntohl(mp->protocol), ntohl(mp->total_pkts), ntohl(mp->total_bytes));
+}
+
+static int api_snat_user_session_dump(vat_main_t * vam)
+{
+  vl_api_snat_user_session_dump_t * mp;
+  vl_api_snat_control_ping_t *mp_ping;
+  int ret;
+
+  if (vam->json_output)
+    {
+      clib_warning ("JSON output not supported for snat_address_dump");
+      return -99;
+    }
+
+  M(SNAT_USER_SESSION_DUMP, mp);
+  S(mp);
+
+  /* Use a control ping for synchronization */
+  M(SNAT_CONTROL_PING, mp_ping);
+  S(mp_ping);
+
+  W (ret);
+  return ret;
+}
+
+static void vl_api_snat_user_details_t_handler
+  (vl_api_snat_user_details_t *mp)
+{
+  snat_test_main_t * sm = &snat_test_main;
+  vat_main_t *vam = sm->vat_main;
+
+  fformat(vam->ofp, "user with ip %U with vrf_id %d "
+                    "with %d sessions and %d static sessions\n",
+          format_ip4_address, mp->ip_address, ntohl(mp->vrf_id),
+          ntohl(mp->nsessions), ntohl(mp->nstaticsessions));
+}
+
+static int api_snat_user_dump(vat_main_t * vam)
+{
+  vl_api_snat_user_dump_t * mp;
+  vl_api_snat_control_ping_t *mp_ping;
+  int ret;
+
+  if (vam->json_output)
+    {
+      clib_warning ("JSON output not supported for snat_address_dump");
+      return -99;
+    }
+
+  M(SNAT_USER_DUMP, mp);
+  S(mp);
+
+  /* Use a control ping for synchronization */
+  M(SNAT_CONTROL_PING, mp_ping);
+  S(mp_ping);
+
+  W (ret);
+  return ret;
+}
+
 /* 
  * List of messages that the api test plugin sends,
  * and that the data plane plugin processes
@@ -667,7 +741,9 @@ _(snat_add_del_interface_addr,                                   \
   "<intfc> | sw_if_index <id> [del]")                            \
 _(snat_interface_addr_dump, "")                                  \
 _(snat_ipfix_enable_disable, "[domain <id>] [src_port <n>] "     \
-  "[disable]")
+  "[disable]")                                                   \
+_(snat_user_dump, "")                                            \
+_(snat_user_session_dump, "ip_address <ip> vrf_id <table-id>")
 
 static void 
 snat_vat_api_hookup (vat_main_t *vam)
index 01bbe10..4cb5116 100644 (file)
@@ -870,6 +870,27 @@ class TestSNAT(VppTestCase):
         capture = self.pg5.get_capture(len(pkts))
         self.verify_capture_in(capture, self.pg5)
 
+        # pg5 session dump
+        addresses = self.vapi.snat_address_dump()
+        self.assertEqual(len(addresses), 1)
+        sessions = self.vapi.snat_user_session_dump(self.pg5.remote_ip4n, 10)
+        self.assertEqual(len(sessions), 3)
+        for session in sessions:
+            self.assertFalse(session.is_static)
+            self.assertEqual(session.inside_ip_address[0:4],
+                             self.pg5.remote_ip4n)
+            self.assertEqual(session.outside_ip_address,
+                             addresses[0].ip_address)
+        self.assertEqual(sessions[0].protocol, IP_PROTOS.tcp)
+        self.assertEqual(sessions[1].protocol, IP_PROTOS.udp)
+        self.assertEqual(sessions[2].protocol, IP_PROTOS.icmp)
+        self.assertEqual(sessions[0].inside_port, self.tcp_port_in)
+        self.assertEqual(sessions[1].inside_port, self.udp_port_in)
+        self.assertEqual(sessions[2].inside_port, self.icmp_id_in)
+        self.assertEqual(sessions[0].outside_port, self.tcp_port_out)
+        self.assertEqual(sessions[1].outside_port, self.udp_port_out)
+        self.assertEqual(sessions[2].outside_port, self.icmp_id_out)
+
         # in2out 3rd interface
         pkts = self.create_stream_in(self.pg6, self.pg3)
         self.pg6.add_stream(pkts)
@@ -886,6 +907,44 @@ class TestSNAT(VppTestCase):
         capture = self.pg6.get_capture(len(pkts))
         self.verify_capture_in(capture, self.pg6)
 
+        # general user and session dump verifications
+        users = self.vapi.snat_user_dump()
+        self.assertTrue(len(users) >= 3)
+        addresses = self.vapi.snat_address_dump()
+        self.assertEqual(len(addresses), 1)
+        for user in users:
+            sessions = self.vapi.snat_user_session_dump(user.ip_address,
+                                                        user.vrf_id)
+            for session in sessions:
+                self.assertEqual(user.ip_address, session.inside_ip_address)
+                self.assertTrue(session.total_bytes > session.total_pkts > 0)
+                self.assertTrue(session.protocol in
+                                [IP_PROTOS.tcp, IP_PROTOS.udp,
+                                 IP_PROTOS.icmp])
+
+        # pg4 session dump
+        sessions = self.vapi.snat_user_session_dump(self.pg4.remote_ip4n, 10)
+        self.assertTrue(len(sessions) >= 4)
+        for session in sessions:
+            self.assertFalse(session.is_static)
+            self.assertEqual(session.inside_ip_address[0:4],
+                             self.pg4.remote_ip4n)
+            self.assertEqual(session.outside_ip_address,
+                             addresses[0].ip_address)
+
+        # pg6 session dump
+        sessions = self.vapi.snat_user_session_dump(self.pg6.remote_ip4n, 20)
+        self.assertTrue(len(sessions) >= 3)
+        for session in sessions:
+            self.assertTrue(session.is_static)
+            self.assertEqual(session.inside_ip_address[0:4],
+                             self.pg6.remote_ip4n)
+            self.assertEqual(map(ord, session.outside_ip_address[0:4]),
+                             map(int, static_nat_ip.split('.')))
+            self.assertTrue(session.inside_port in
+                            [self.tcp_port_in, self.udp_port_in,
+                             self.icmp_id_in])
+
     def test_hairpinning(self):
         """ SNAT hairpinning """
 
index ea574b3..de189c3 100644 (file)
@@ -1072,6 +1072,29 @@ class VppPapiProvider(object):
              'src_port': src_port,
              'enable': enable})
 
+    def snat_user_session_dump(
+            self,
+            ip_address,
+            vrf_id):
+        """Dump S-NAT user's sessions
+
+        :param ip_address: ip adress of the user to be dumped
+        :param cpu_index: cpu_index on which the user is
+        :param vrf_id: VRF ID
+        :return: Dictionary of S-NAT sessions
+        """
+        return self.api(
+            self.papi.snat_user_session_dump,
+            {'ip_address': ip_address,
+             'vrf_id': vrf_id})
+
+    def snat_user_dump(self):
+        """Dump S-NAT users
+
+        :return: Dictionary of S-NAT users
+        """
+        return self.api(self.papi.snat_user_dump, {})
+
     def control_ping(self):
         self.api(self.papi.control_ping)