CGN: IPFIX logging 31/6231/5
authorMatus Fabian <matfabia@cisco.com>
Tue, 18 Apr 2017 12:29:59 +0000 (05:29 -0700)
committerOle Trøan <otroan@employees.org>
Fri, 21 Apr 2017 08:21:45 +0000 (08:21 +0000)
maximum entries per user exceeded event

Change-Id: Ie35d7f40f55001e2ef4a38f934f176594f25b189
Signed-off-by: Matus Fabian <matfabia@cisco.com>
src/plugins/snat/snat_det.h
src/plugins/snat/snat_ipfix_logging.c
src/plugins/snat/snat_ipfix_logging.h
src/vnet/flow/ipfix_info_elements.h
test/test_snat.py

index 45e3682..f4fdb25 100644 (file)
@@ -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;
 }
 
index 9f1abb7..b099d32 100644 (file)
@@ -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, &quota_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;
index b968ee2..45c1a7b 100644 (file)
@@ -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__ */
index 5d7e935..1403db4 100644 (file)
@@ -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,
index 42b0719..ace1723 100644 (file)
@@ -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: