l2: Separating scan-delay and learn-limit into a separate API from want_l2_macs_events

Type: feature

Signed-off-by: Jerome Tollet <jtollet@cisco.com>
Change-Id: I6de6dae7da4ec1001e2811975a9b67acfc1a148c
diff --git a/src/vnet/l2/l2.api b/src/vnet/l2/l2.api
index ef83082..b0ac23f 100644
--- a/src/vnet/l2/l2.api
+++ b/src/vnet/l2/l2.api
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-option version = "3.0.0";
+option version = "3.1.0";
 
 import "vnet/ip/ip_types.api";
 import "vnet/ethernet/ethernet_types.api";
@@ -151,6 +151,7 @@
 */
 autoreply define want_l2_macs_events
 {
+  option deprecated;
   u32 client_index;
   u32 context;
   u32 learn_limit [default=1000];
@@ -160,6 +161,35 @@
   u32 pid;
 };
 
+/** \brief Register to receive L2 MAC events for learned and aged MAC
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param max_macs_in_event - in units of 10 mac entries
+    @param enable_disable - 1 => register for MAC events, 0 => cancel registration
+    @param pid - sender's pid
+*/
+autoreply define want_l2_macs_events2
+{
+  u32 client_index;
+  u32 context;
+  u8  max_macs_in_event [default=10];
+  bool enable_disable [default=true];
+  u32 pid;
+};
+
+/** \brief set l2 table scan delay
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param scan_delay - event scan delay in 10 msec unit
+*/
+autoreply define l2fib_set_scan_delay
+{
+  u32 client_index;
+  u32 context;
+  u16 scan_delay [default=10];
+};
+
+
 enum mac_event_action
 {
 	MAC_EVENT_ACTION_API_ADD = 0,
diff --git a/src/vnet/l2/l2_api.c b/src/vnet/l2/l2_api.c
index 97b429d..85e9c31 100644
--- a/src/vnet/l2/l2_api.c
+++ b/src/vnet/l2/l2_api.c
@@ -58,7 +58,9 @@
   _ (L2FIB_FLUSH_INT, l2fib_flush_int)                                        \
   _ (L2FIB_FLUSH_BD, l2fib_flush_bd)                                          \
   _ (L2FIB_ADD_DEL, l2fib_add_del)                                            \
+  _ (L2FIB_SET_SCAN_DELAY, l2fib_set_scan_delay)                              \
   _ (WANT_L2_MACS_EVENTS, want_l2_macs_events)                                \
+  _ (WANT_L2_MACS_EVENTS2, want_l2_macs_events2)                              \
   _ (L2_FLAGS, l2_flags)                                                      \
   _ (SW_INTERFACE_SET_L2_XCONNECT, sw_interface_set_l2_xconnect)              \
   _ (SW_INTERFACE_SET_L2_BRIDGE, sw_interface_set_l2_bridge)                  \
@@ -262,22 +264,18 @@
 }
 
 static void
-vl_api_want_l2_macs_events_t_handler (vl_api_want_l2_macs_events_t * mp)
+vl_api_want_l2_macs_events2_t_handler (vl_api_want_l2_macs_events2_t *mp)
 {
   int rv = 0;
-  vl_api_want_l2_macs_events_reply_t *rmp;
+  vl_api_want_l2_macs_events2_reply_t *rmp;
   l2learn_main_t *lm = &l2learn_main;
   l2fib_main_t *fm = &l2fib_main;
   u32 pid = ntohl (mp->pid);
-  u32 learn_limit = ntohl (mp->learn_limit);
 
   if (mp->enable_disable)
     {
-      if (lm->client_pid == 0)
+      if ((lm->client_pid == 0) || (lm->client_pid == pid))
 	{
-	  lm->client_pid = pid;
-	  lm->client_index = mp->client_index;
-
 	  if (mp->max_macs_in_event)
 	    fm->max_macs_in_event = mp->max_macs_in_event * 10;
 	  else
@@ -286,23 +284,12 @@
 	      goto exit;
 	    }
 
-	  if (mp->scan_delay)
-	    fm->event_scan_delay = (f64) (mp->scan_delay) * 10e-3;
-	  else
-	    {
-	      rv = VNET_API_ERROR_INVALID_VALUE;
-	      goto exit;
-	    }
+	  /* if scan_delay was not set before */
+	  if (fm->event_scan_delay == 0.0)
+	    fm->event_scan_delay = (f64) (10) * 10e-3;
 
-	  /* change learn limit and flush all learned MACs */
-	  if (learn_limit && (learn_limit < L2LEARN_DEFAULT_LIMIT))
-	    lm->global_learn_limit = learn_limit;
-	  else
-	    {
-	      rv = VNET_API_ERROR_INVALID_VALUE;
-	      goto exit;
-	    }
-
+	  lm->client_pid = pid;
+	  lm->client_index = mp->client_index;
 	  l2fib_flush_all_mac (vlib_get_main ());
 	}
       else if (lm->client_pid != pid)
@@ -315,7 +302,53 @@
     {
       lm->client_pid = 0;
       lm->client_index = 0;
-      if (learn_limit && (learn_limit < L2LEARN_DEFAULT_LIMIT))
+    }
+
+exit:
+  REPLY_MACRO (VL_API_WANT_L2_MACS_EVENTS2_REPLY);
+}
+
+static void
+vl_api_want_l2_macs_events_t_handler (vl_api_want_l2_macs_events_t *mp)
+{
+  int rv = 0;
+  vl_api_want_l2_macs_events_reply_t *rmp;
+  l2learn_main_t *lm = &l2learn_main;
+  l2fib_main_t *fm = &l2fib_main;
+  u32 pid = ntohl (mp->pid);
+  u32 learn_limit = ntohl (mp->learn_limit);
+
+  if (mp->enable_disable)
+    {
+      if ((lm->client_pid == 0) || (lm->client_pid == pid))
+	{
+	  if ((mp->max_macs_in_event == 0) || (mp->scan_delay == 0) ||
+	      (learn_limit == 0) || (learn_limit > L2LEARN_DEFAULT_LIMIT))
+	    {
+	      rv = VNET_API_ERROR_INVALID_VALUE;
+	      goto exit;
+	    }
+	  lm->client_pid = pid;
+	  lm->client_index = mp->client_index;
+
+	  fm->max_macs_in_event = mp->max_macs_in_event * 10;
+	  fm->event_scan_delay = (f64) (mp->scan_delay) * 10e-3;
+
+	  /* change learn limit and flush all learned MACs */
+	  lm->global_learn_limit = learn_limit;
+	  l2fib_flush_all_mac (vlib_get_main ());
+	}
+      else if (lm->client_pid != pid)
+	{
+	  rv = VNET_API_ERROR_L2_MACS_EVENT_CLINET_PRESENT;
+	  goto exit;
+	}
+    }
+  else if (lm->client_pid)
+    {
+      lm->client_pid = 0;
+      lm->client_index = 0;
+      if (learn_limit && (learn_limit <= L2LEARN_DEFAULT_LIMIT))
 	lm->global_learn_limit = learn_limit;
       else
 	lm->global_learn_limit = L2LEARN_DEFAULT_LIMIT;
@@ -372,6 +405,29 @@
 }
 
 static void
+vl_api_l2fib_set_scan_delay_t_handler (vl_api_l2fib_set_scan_delay_t *mp)
+{
+  int rv = 0;
+  l2fib_main_t *fm = &l2fib_main;
+  vl_api_l2fib_set_scan_delay_reply_t *rmp;
+  u16 scan_delay = ntohs (mp->scan_delay);
+
+  if (mp->scan_delay)
+    {
+      fm->event_scan_delay = (f64) (scan_delay) *10e-3;
+      l2fib_flush_all_mac (vlib_get_main ());
+    }
+  else
+    {
+      rv = VNET_API_ERROR_INVALID_VALUE;
+      goto exit;
+    }
+
+exit:
+  REPLY_MACRO (VL_API_L2FIB_SET_SCAN_DELAY_REPLY);
+}
+
+static void
 vl_api_l2_flags_t_handler (vl_api_l2_flags_t * mp)
 {
   vl_api_l2_flags_reply_t *rmp;
diff --git a/src/vnet/l2/l2_fib.c b/src/vnet/l2/l2_fib.c
index 82be6bc..4407cb5 100644
--- a/src/vnet/l2/l2_fib.c
+++ b/src/vnet/l2/l2_fib.c
@@ -845,6 +845,36 @@
 };
 /* *INDENT-ON* */
 
+static clib_error_t *
+l2fib_set_scan_delay (vlib_main_t *vm, unformat_input_t *input,
+		      vlib_cli_command_t *cmd)
+{
+  clib_error_t *error = 0;
+  u32 scan_delay;
+  l2fib_main_t *fm = &l2fib_main;
+
+  if (!unformat (input, "%d", &scan_delay))
+    {
+      error = clib_error_return (0, "expecting delay but got `%U'",
+				 format_unformat_error, input);
+      goto done;
+    }
+  fm->event_scan_delay = (f64) (scan_delay) *10e-3;
+  l2fib_flush_all_mac (vlib_get_main ());
+done:
+  return error;
+}
+
+/*?
+ * This command set scan delay (in 1/10s unit)
+ *
+?*/
+VLIB_CLI_COMMAND (l2fib_set_scan_delay_cli, static) = {
+  .path = "set l2fib scan-delay",
+  .short_help = "set l2fib scan-delay <delay>",
+  .function = l2fib_set_scan_delay,
+};
+
 /**
     Kick off ager to scan MACs to age/delete MAC entries
 */
diff --git a/test/test_l2_fib.py b/test/test_l2_fib.py
index 51c1747..9ce289f 100644
--- a/test/test_l2_fib.py
+++ b/test/test_l2_fib.py
@@ -498,6 +498,29 @@
         self.vapi.want_l2_macs_events(enable_disable=0)
         self.assertEqual(len(learned_macs ^ macs), 0)
 
+    def test_l2_fib_mac_learn_evs2(self):
+        """ L2 FIB - mac learning events using want_l2_macs_events2
+        """
+        bd1 = 1
+        hosts = self.create_hosts(10, subnet=39)
+
+        self.vapi.l2fib_set_scan_delay(scan_delay=10)
+        self.vapi.want_l2_macs_events2()
+        self.sleep(1)
+        self.learn_hosts(bd1, hosts)
+
+        self.sleep(1)
+        self.logger.info(self.vapi.ppcli("show l2fib"))
+        evs = self.vapi.collect_events()
+        action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
+        learned_macs = {
+            e.mac[i].mac_addr.packed for e in evs for i in range(e.n_macs)
+            if e.mac[i].action == action}
+        macs = {h.bin_mac for swif in self.bd_ifs(bd1)
+                for h in hosts[self.pg_interfaces[swif].sw_if_index]}
+        self.vapi.want_l2_macs_events2(enable_disable=0)
+        self.assertEqual(len(learned_macs ^ macs), 0)
+
     def test_l2_fib_macs_learn_max(self):
         """ L2 FIB - mac learning max macs in event
         """
@@ -525,6 +548,35 @@
             self.assertLess(len(e), ev_macs * 10)
         self.assertEqual(len(learned_macs ^ macs), 0)
 
+    def test_l2_fib_macs_learn_max2(self):
+        """ L2 FIB - mac learning max macs in event using want_l2_macs_events2
+        """
+        bd1 = 1
+        hosts = self.create_hosts(10, subnet=40)
+
+        ev_macs = 1
+        self.vapi.l2fib_set_scan_delay(scan_delay=10)
+        self.vapi.want_l2_macs_events2(max_macs_in_event=ev_macs)
+        self.sleep(1)
+        self.learn_hosts(bd1, hosts)
+
+        self.sleep(1)
+        self.logger.info(self.vapi.ppcli("show l2fib"))
+        evs = self.vapi.collect_events()
+        self.vapi.want_l2_macs_events2(enable_disable=0)
+
+        self.assertGreater(len(evs), 0)
+        action = VppEnum.vl_api_mac_event_action_t.MAC_EVENT_ACTION_API_ADD
+        learned_macs = {
+            e.mac[i].mac_addr.packed for e in evs for i in range(e.n_macs)
+            if e.mac[i].action == action}
+        macs = {h.bin_mac for swif in self.bd_ifs(bd1)
+                for h in hosts[self.pg_interfaces[swif].sw_if_index]}
+
+        for e in evs:
+            self.assertLess(len(e), ev_macs * 10)
+        self.assertEqual(len(learned_macs ^ macs), 0)
+
 
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index d1a4eaf..c5e8ff0 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -104,6 +104,7 @@
     'want_igmp_events': {'enable': 1, },
     'want_interface_events': {'enable_disable': 1, },
     'want_l2_macs_events': {'enable_disable': 1, 'pid': os.getpid(), },
+    'want_l2_macs_events2': {'enable_disable': 1, 'pid': os.getpid(), },
 }