From 23caa885afad3501ea95b52cdbc64c0d48072a83 Mon Sep 17 00:00:00 2001 From: magalik Date: Wed, 8 Feb 2017 23:25:45 -0800 Subject: SNAT: user's dump and session dump of a certain snat user. Change-Id: If75a35dbdcb43c1ce0128b8649f2ca3970d3fff5 Signed-off-by: Martin --- src/plugins/snat/in2out.c | 1 + src/plugins/snat/out2in.c | 1 + src/plugins/snat/snat.api | 66 ++++++++++++++++++++ src/plugins/snat/snat.c | 142 ++++++++++++++++++++++++++++++++++++++++++- src/plugins/snat/snat.h | 1 + src/plugins/snat/snat_test.c | 80 +++++++++++++++++++++++- test/test_snat.py | 59 ++++++++++++++++++ test/vpp_papi_provider.py | 23 +++++++ 8 files changed, 370 insertions(+), 3 deletions(-) diff --git a/src/plugins/snat/in2out.c b/src/plugins/snat/in2out.c index b4b7793d..c6dc7ca4 100644 --- a/src/plugins/snat/in2out.c +++ b/src/plugins/snat/in2out.c @@ -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); diff --git a/src/plugins/snat/out2in.c b/src/plugins/snat/out2in.c index 3bfc0aa3..7905436a 100644 --- a/src/plugins/snat/out2in.c +++ b/src/plugins/snat/out2in.c @@ -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); diff --git a/src/plugins/snat/snat.api b/src/plugins/snat/snat.api index c429f05f..3462a8d2 100644 --- a/src/plugins/snat/snat.api +++ b/src/plugins/snat/snat.api @@ -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; +}; diff --git a/src/plugins/snat/snat.c b/src/plugins/snat/snat.c index 8c2bacdb..0ec20ef7 100644 --- a/src/plugins/snat/snat.c +++ b/src/plugins/snat/snat.c @@ -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 * diff --git a/src/plugins/snat/snat.h b/src/plugins/snat/snat.h index 39cbd3f8..47f2e6ee 100644 --- a/src/plugins/snat/snat.h +++ b/src/plugins/snat/snat.h @@ -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; diff --git a/src/plugins/snat/snat_test.c b/src/plugins/snat/snat_test.c index a299e0ac..dae63a23 100644 --- a/src/plugins/snat/snat_test.c +++ b/src/plugins/snat/snat_test.c @@ -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, \ " | sw_if_index [del]") \ _(snat_interface_addr_dump, "") \ _(snat_ipfix_enable_disable, "[domain ] [src_port ] " \ - "[disable]") + "[disable]") \ +_(snat_user_dump, "") \ +_(snat_user_session_dump, "ip_address vrf_id ") static void snat_vat_api_hookup (vat_main_t *vam) diff --git a/test/test_snat.py b/test/test_snat.py index 01bbe10a..4cb51161 100644 --- a/test/test_snat.py +++ b/test/test_snat.py @@ -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 """ diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index ea574b36..de189c3e 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -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) -- cgit 1.2.3-korg