diff options
-rw-r--r-- | src/plugins/snat/snat_det.h | 2 | ||||
-rw-r--r-- | src/plugins/snat/snat_ipfix_logging.c | 220 | ||||
-rw-r--r-- | src/plugins/snat/snat_ipfix_logging.h | 11 | ||||
-rw-r--r-- | src/vnet/flow/ipfix_info_elements.h | 3 | ||||
-rw-r--r-- | test/test_snat.py | 38 |
5 files changed, 254 insertions, 20 deletions
diff --git a/src/plugins/snat/snat_det.h b/src/plugins/snat/snat_det.h index 45e36829a20..f4fdb256637 100644 --- a/src/plugins/snat/snat_det.h +++ b/src/plugins/snat/snat_det.h @@ -24,6 +24,7 @@ #include <vnet/ip/ip.h> #include <snat/snat.h> +#include <snat/snat_ipfix_logging.h> #define SNAT_DET_SES_PER_USER 1000 @@ -170,6 +171,7 @@ snat_det_ses_create (snat_det_map_t * dm, ip4_address_t * in_addr, } } + snat_ipfix_logging_max_entries_per_user (in_addr->as_u32); return 0; } diff --git a/src/plugins/snat/snat_ipfix_logging.c b/src/plugins/snat/snat_ipfix_logging.c index 9f1abb7d9f4..b099d321e90 100644 --- a/src/plugins/snat/snat_ipfix_logging.c +++ b/src/plugins/snat/snat_ipfix_logging.c @@ -24,9 +24,11 @@ snat_ipfix_logging_main_t snat_ipfix_logging_main; #define NAT44_SESSION_CREATE_LEN 26 #define NAT_ADDRESSES_EXHAUTED_LEN 13 +#define MAX_ENTRIES_PER_USER_LEN 17 #define NAT44_SESSION_CREATE_FIELD_COUNT 8 #define NAT_ADDRESSES_EXHAUTED_FIELD_COUNT 3 +#define MAX_ENTRIES_PER_USER_FIELD_COUNT 4 typedef struct { u8 nat_event; @@ -42,6 +44,10 @@ typedef struct { u32 pool_id; } snat_ipfix_logging_addr_exhausted_args_t; +typedef struct { + u32 src_ip; +} snat_ipfix_logging_max_entries_per_user_args_t; + /** * @brief Create an IPFIX template packet rewrite string * @@ -51,6 +57,7 @@ typedef struct { * @param src_address source address * @param collector_port collector * @param event NAT event ID + * @param quota_event NAT quota exceeded event ID * * @returns template packet */ @@ -60,7 +67,8 @@ snat_template_rewrite (flow_report_main_t * frm, ip4_address_t * collector_address, ip4_address_t * src_address, u16 collector_port, - nat_event_t event) + nat_event_t event, + quota_exceed_event_t quota_event) { snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; ip4_header_t *ip; @@ -88,6 +96,14 @@ snat_template_rewrite (flow_report_main_t * frm, field_count = NAT44_SESSION_CREATE_FIELD_COUNT; silm->nat44_session_template_id = fr->template_id; } + else if (event == QUOTA_EXCEEDED) + { + if (quota_event == MAX_ENTRIES_PER_USER) + { + field_count = MAX_ENTRIES_PER_USER_FIELD_COUNT; + silm->max_entries_per_user_template_id = fr->template_id; + } + } /* allocate rewrite space */ vec_validate_aligned (rewrite, @@ -144,6 +160,21 @@ snat_template_rewrite (flow_report_main_t * frm, f->e_id_length = ipfix_e_id_length (0, ingressVRFID, 4); f++; } + else if (event == QUOTA_EXCEEDED) + { + if (quota_event == MAX_ENTRIES_PER_USER) + { + f->e_id_length = ipfix_e_id_length (0, observationTimeMilliseconds, + 8); + f++; + f->e_id_length = ipfix_e_id_length (0, natEvent, 1); + f++; + f->e_id_length = ipfix_e_id_length (0, natQuotaExceededEvent, 4); + f++; + f->e_id_length = ipfix_e_id_length (0, sourceIPv4Address, 4); + f++; + } + } /* Back to the template packet... */ ip = (ip4_header_t *) & tp->ip4; @@ -174,7 +205,7 @@ snat_template_rewrite_addr_exhausted (flow_report_main_t * frm, u16 collector_port) { return snat_template_rewrite (frm, fr, collector_address, src_address, - collector_port, NAT_ADDRESSES_EXHAUTED); + collector_port, NAT_ADDRESSES_EXHAUTED, 0); } u8 * @@ -185,7 +216,19 @@ snat_template_rewrite_nat44_session (flow_report_main_t * frm, u16 collector_port) { return snat_template_rewrite (frm, fr, collector_address, src_address, - collector_port, NAT44_SESSION_CREATE); + collector_port, NAT44_SESSION_CREATE, 0); +} + +u8 * +snat_template_rewrite_max_entries_per_usr (flow_report_main_t * frm, + flow_report_t * fr, + ip4_address_t * collector_address, + ip4_address_t * src_address, + u16 collector_port) +{ + return snat_template_rewrite (frm, fr, collector_address, src_address, + collector_port, QUOTA_EXCEEDED, + MAX_ENTRIES_PER_USER); } static inline void @@ -200,7 +243,7 @@ snat_ipfix_header_create (flow_report_main_t * frm, ipfix_set_header_t * s = 0; ip4_header_t * ip; udp_header_t * udp; - + stream = &frm->streams[silm->stream_index]; b0->current_data = 0; @@ -463,7 +506,96 @@ snat_ipfix_logging_addr_exhausted (u32 pool_id, int do_flush) offset = 0; } silm->addr_exhausted_next_record_offset = offset; - } +} + +static void +snat_ipfix_logging_max_entries_per_usr (u32 src_ip, int do_flush) +{ + snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; + flow_report_main_t *frm = &flow_report_main; + vlib_frame_t *f; + vlib_buffer_t *b0 = 0; + u32 bi0 = ~0; + u32 offset; + vlib_main_t * vm = frm->vlib_main; + u64 now; + vlib_buffer_free_list_t *fl; + u8 nat_event = QUOTA_EXCEEDED; + u32 quota_event = MAX_ENTRIES_PER_USER; + + if (!silm->enabled) + return; + + now = (u64) ((vlib_time_now (vm) - silm->vlib_time_0) * 1e3); + now += silm->milisecond_time_0; + + b0 = silm->max_entries_per_user_buffer; + + if (PREDICT_FALSE (b0 == 0)) + { + if (do_flush) + return; + + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + { + clib_warning ("can't allocate buffer for NAT IPFIX event"); + return; + } + + b0 = silm->max_entries_per_user_buffer = + vlib_get_buffer (vm, bi0); + fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); + vlib_buffer_init_for_free_list (b0, fl); + VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); + offset = 0; + } + else + { + bi0 = vlib_get_buffer_index (vm, b0); + offset = silm->max_entries_per_user_next_record_offset; + } + + f = silm->max_entries_per_user_frame; + if (PREDICT_FALSE (f == 0)) + { + u32 * to_next; + f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); + silm->max_entries_per_user_frame = f; + to_next = vlib_frame_vector_args (f); + to_next[0] = bi0; + f->n_vectors = 1; + } + + if (PREDICT_FALSE (offset == 0)) + snat_ipfix_header_create (frm, b0, &offset); + + if (PREDICT_TRUE (do_flush == 0)) + { + u64 time_stamp = clib_host_to_net_u64 (now); + clib_memcpy (b0->data + offset, &time_stamp, sizeof (time_stamp)); + offset += sizeof (time_stamp); + + clib_memcpy (b0->data + offset, &nat_event, sizeof (nat_event)); + offset += sizeof (nat_event); + + clib_memcpy (b0->data + offset, "a_event, sizeof(quota_event)); + offset += sizeof (quota_event); + + clib_memcpy (b0->data + offset, &src_ip, sizeof (src_ip)); + offset += sizeof (src_ip); + + b0->current_length += MAX_ENTRIES_PER_USER_LEN; + } + + if (PREDICT_FALSE (do_flush || (offset + MAX_ENTRIES_PER_USER_LEN) > frm->path_mtu)) + { + snat_ipfix_send (frm, f, b0, silm->max_entries_per_user_template_id); + silm->max_entries_per_user_frame = 0; + silm->max_entries_per_user_buffer = 0; + offset = 0; + } + silm->max_entries_per_user_next_record_offset = offset; +} static void snat_ipfix_logging_nat44_ses_rpc_cb (snat_ipfix_logging_nat44_ses_args_t *a) @@ -583,6 +715,41 @@ snat_data_callback_addr_exhausted (flow_report_main_t * frm, return f; } +static void +snat_ipfix_logging_max_entries_per_usr_rpc_cb + (snat_ipfix_logging_max_entries_per_user_args_t * a) +{ + snat_ipfix_logging_max_entries_per_usr(a->src_ip, 0); +} + +/** + * @brief Generate maximum entries per user exceeded event + * + * @param src_ip source IPv4 address + */ +void +snat_ipfix_logging_max_entries_per_user(u32 src_ip) +{ + //TODO: This event SHOULD be rate limited + snat_ipfix_logging_max_entries_per_user_args_t a; + + a.src_ip = src_ip; + + vl_api_rpc_call_main_thread (snat_ipfix_logging_max_entries_per_usr_rpc_cb, + (u8 *) &a, sizeof (a)); +} + +vlib_frame_t * +snat_data_callback_max_entries_per_usr (flow_report_main_t * frm, + flow_report_t * fr, + vlib_frame_t * f, + u32 * to_next, + u32 node_index) +{ + snat_ipfix_logging_max_entries_per_usr(0, 1); + return f; +} + /** * @brief Enable/disable SNAT IPFIX logging * @@ -595,6 +762,7 @@ snat_data_callback_addr_exhausted (flow_report_main_t * frm, int snat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port) { + snat_main_t * sm = &snat_main; snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; flow_report_main_t *frm = &flow_report_main; vnet_flow_report_add_del_args_t a; @@ -607,27 +775,43 @@ snat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port) silm->enabled = e; memset (&a, 0, sizeof (a)); - a.rewrite_callback = snat_template_rewrite_nat44_session; - a.flow_data_callback = snat_data_callback_nat44_session; a.is_add = enable; a.domain_id = domain_id ? domain_id : 1; a.src_port = src_port ? src_port : UDP_DST_PORT_ipfix; - rv = vnet_flow_report_add_del (frm, &a); - if (rv) + if (sm->deterministic) { - clib_warning ("vnet_flow_report_add_del returned %d", rv); - return -1; + a.rewrite_callback = snat_template_rewrite_max_entries_per_usr; + a.flow_data_callback = snat_data_callback_max_entries_per_usr; + + rv = vnet_flow_report_add_del (frm, &a); + if (rv) + { + clib_warning ("vnet_flow_report_add_del returned %d", rv); + return -1; + } } + else + { + a.rewrite_callback = snat_template_rewrite_nat44_session; + a.flow_data_callback = snat_data_callback_nat44_session; - a.rewrite_callback = snat_template_rewrite_addr_exhausted; - a.flow_data_callback = snat_data_callback_addr_exhausted; + rv = vnet_flow_report_add_del (frm, &a); + if (rv) + { + clib_warning ("vnet_flow_report_add_del returned %d", rv); + return -1; + } - rv = vnet_flow_report_add_del (frm, &a); - if (rv) - { - clib_warning ("vnet_flow_report_add_del returned %d", rv); - return -1; + a.rewrite_callback = snat_template_rewrite_addr_exhausted; + a.flow_data_callback = snat_data_callback_addr_exhausted; + + rv = vnet_flow_report_add_del (frm, &a); + if (rv) + { + clib_warning ("vnet_flow_report_add_del returned %d", rv); + return -1; + } } return 0; diff --git a/src/plugins/snat/snat_ipfix_logging.h b/src/plugins/snat/snat_ipfix_logging.h index b968ee21e9c..45c1a7bf8e1 100644 --- a/src/plugins/snat/snat_ipfix_logging.h +++ b/src/plugins/snat/snat_ipfix_logging.h @@ -22,8 +22,13 @@ typedef enum { NAT44_SESSION_CREATE = 4, NAT44_SESSION_DELETE = 5, NAT_PORTS_EXHAUSTED = 12, + QUOTA_EXCEEDED = 13, } nat_event_t; +typedef enum { + MAX_ENTRIES_PER_USER = 3, +} quota_exceed_event_t; + typedef struct { /** S-NAT IPFIX logging enabled */ u8 enabled; @@ -31,14 +36,17 @@ typedef struct { /** ipfix buffers under construction */ vlib_buffer_t *nat44_session_buffer; vlib_buffer_t *addr_exhausted_buffer; + vlib_buffer_t *max_entries_per_user_buffer; /** frames containing ipfix buffers */ vlib_frame_t *nat44_session_frame; vlib_frame_t *addr_exhausted_frame; + vlib_frame_t *max_entries_per_user_frame; /** next record offset */ u32 nat44_session_next_record_offset; u32 addr_exhausted_next_record_offset; + u32 max_entries_per_user_next_record_offset; /** Time reference pair */ u64 milisecond_time_0; @@ -47,6 +55,7 @@ typedef struct { /** template IDs */ u16 nat44_session_template_id; u16 addr_exhausted_template_id; + u16 max_entries_per_user_template_id; /** stream index */ u32 stream_index; @@ -65,4 +74,6 @@ void snat_ipfix_logging_nat44_ses_delete (u32 src_ip, u32 nat_src_ip, u16 src_port, u16 nat_src_port, u32 vrf_id); void snat_ipfix_logging_addresses_exhausted(u32 pool_id); +void snat_ipfix_logging_max_entries_per_user(u32 src_ip); + #endif /* __included_snat_ipfix_logging_h__ */ diff --git a/src/vnet/flow/ipfix_info_elements.h b/src/vnet/flow/ipfix_info_elements.h index 5d7e935dabb..1403db4393b 100644 --- a/src/vnet/flow/ipfix_info_elements.h +++ b/src/vnet/flow/ipfix_info_elements.h @@ -418,7 +418,8 @@ _(layer2OctetTotalSumOfSquares, 429, u64) \ _(layer2FrameDeltaCount, 430, u64) \ _(layer2FrameTotalCount, 431, u64) \ _(pseudoWireDestinationIPv4Address, 432, ip4_address_t) \ -_(ignoredLayer2FrameTotalCount, 433, u64) +_(ignoredLayer2FrameTotalCount, 433, u64) \ +_(natQuotaExceededEvent, 466, u32) typedef enum { #define _(n,v,t) n = v, diff --git a/test/test_snat.py b/test/test_snat.py index 42b07193178..ace17237b38 100644 --- a/test/test_snat.py +++ b/test/test_snat.py @@ -1338,7 +1338,7 @@ class TestDeterministicNAT(MethodHolder): cls.icmp_id_in = 6305 cls.snat_addr = '10.0.0.3' - cls.create_pg_interfaces(range(2)) + cls.create_pg_interfaces(range(3)) cls.interfaces = list(cls.pg_interfaces) for i in cls.interfaces: @@ -1483,6 +1483,21 @@ class TestDeterministicNAT(MethodHolder): self.logger.error("TCP 3 way handshake failed") raise + def verify_ipfix_max_entries_per_user(self, data): + """ + Verify IPFIX maximum entries per user exceeded event + + :param data: Decoded IPFIX data records + """ + self.assertEqual(1, len(data)) + record = data[0] + # natEvent + self.assertEqual(ord(record[230]), 13) + # natQuotaExceededEvent + self.assertEqual('\x03\x00\x00\x00', record[466]) + # sourceIPv4Address + self.assertEqual(self.pg0.remote_ip4n, record[8]) + def test_deterministic_mode(self): """ S-NAT run deterministic mode """ in_addr = '172.16.255.0' @@ -1827,6 +1842,11 @@ class TestDeterministicNAT(MethodHolder): self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, is_inside=0) + self.vapi.set_ipfix_exporter(collector_address=self.pg2.remote_ip4n, + src_address=self.pg2.local_ip4n, + path_mtu=512, + template_interval=10) + self.vapi.snat_ipfix() pkts = [] for port in range(1025, 2025): @@ -1852,10 +1872,26 @@ class TestDeterministicNAT(MethodHolder): self.assertEqual(1000, dms[0].ses_num) + # verify IPFIX logging + self.vapi.cli("ipfix flush") # FIXME this should be an API call + capture = self.pg2.get_capture(2) + ipfix = IPFIXDecoder() + # first load template + for p in capture: + self.assertTrue(p.haslayer(IPFIX)) + if p.haslayer(Template): + ipfix.add_template(p.getlayer(Template)) + # verify events in data set + for p in capture: + if p.haslayer(Data): + data = ipfix.decode_data_set(p.getlayer(Set)) + self.verify_ipfix_max_entries_per_user(data) + def clear_snat(self): """ Clear SNAT configuration. """ + self.vapi.snat_ipfix(enable=0) self.vapi.snat_det_set_timeouts() deterministic_mappings = self.vapi.snat_det_map_dump() for dsm in deterministic_mappings: |