devices: add support for l3 af_packet interface

Type: improvement

Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
Change-Id: Ia6b9d4ac55be2216887bfdb99be4021f6a96f166
diff --git a/src/vnet/devices/af_packet/af_packet.c b/src/vnet/devices/af_packet/af_packet.c
index 2dc2630..69e3c87 100644
--- a/src/vnet/devices/af_packet/af_packet.c
+++ b/src/vnet/devices/af_packet/af_packet.c
@@ -38,6 +38,11 @@
 
 af_packet_main_t af_packet_main;
 
+VNET_HW_INTERFACE_CLASS (af_packet_ip_device_hw_interface_class, static) = {
+  .name = "af-packet-ip-device",
+  .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
+};
+
 #define AF_PACKET_DEFAULT_TX_FRAMES_PER_BLOCK 1024
 #define AF_PACKET_DEFAULT_TX_FRAME_SIZE	      (2048 * 5)
 #define AF_PACKET_TX_BLOCK_NR		1
@@ -367,6 +372,7 @@
   apif->per_interface_next_index = ~0;
   apif->next_tx_frame = 0;
   apif->next_rx_frame = 0;
+  apif->mode = arg->mode;
 
   ret = af_packet_read_mtu (apif);
   if (ret != 0)
@@ -375,36 +381,44 @@
   if (tm->n_vlib_mains > 1)
     clib_spinlock_init (&apif->lockp);
 
-  /*use configured or generate random MAC address */
-  if (arg->hw_addr)
-    clib_memcpy (hw_addr, arg->hw_addr, 6);
+  if (apif->mode == AF_PACKET_IF_MODE_ETHERNET)
+    {
+      /*use configured or generate random MAC address */
+      if (arg->hw_addr)
+	clib_memcpy (hw_addr, arg->hw_addr, 6);
+      else
+	{
+	  f64 now = vlib_time_now (vm);
+	  u32 rnd;
+	  rnd = (u32) (now * 1e6);
+	  rnd = random_u32 (&rnd);
+
+	  clib_memcpy (hw_addr + 2, &rnd, sizeof (rnd));
+	  hw_addr[0] = 2;
+	  hw_addr[1] = 0xfe;
+	}
+
+      error = ethernet_register_interface (
+	vnm, af_packet_device_class.index, if_index, hw_addr,
+	&apif->hw_if_index, af_packet_eth_flag_change);
+
+      if (error)
+	{
+	  clib_memset (apif, 0, sizeof (*apif));
+	  pool_put (apm->interfaces, apif);
+	  vlib_log_err (apm->log_class, "Unable to register interface: %U",
+			format_clib_error, error);
+	  clib_error_free (error);
+	  ret = VNET_API_ERROR_SYSCALL_ERROR_1;
+	  goto error;
+	}
+    }
   else
     {
-      f64 now = vlib_time_now (vm);
-      u32 rnd;
-      rnd = (u32) (now * 1e6);
-      rnd = random_u32 (&rnd);
-
-      clib_memcpy (hw_addr + 2, &rnd, sizeof (rnd));
-      hw_addr[0] = 2;
-      hw_addr[1] = 0xfe;
+      apif->hw_if_index = vnet_register_interface (
+	vnm, af_packet_device_class.index, if_index,
+	af_packet_ip_device_hw_interface_class.index, if_index);
     }
-
-  error = ethernet_register_interface (vnm, af_packet_device_class.index,
-				       if_index, hw_addr, &apif->hw_if_index,
-				       af_packet_eth_flag_change);
-
-  if (error)
-    {
-      clib_memset (apif, 0, sizeof (*apif));
-      pool_put (apm->interfaces, apif);
-      vlib_log_err (apm->log_class, "Unable to register interface: %U",
-		    format_clib_error, error);
-      clib_error_free (error);
-      ret = VNET_API_ERROR_SYSCALL_ERROR_1;
-      goto error;
-    }
-
   sw = vnet_get_hw_sw_interface (vnm, apif->hw_if_index);
   hw = vnet_get_hw_interface (vnm, apif->hw_if_index);
   apif->sw_if_index = sw->sw_if_index;
@@ -504,7 +518,10 @@
 
   mhash_unset (&apm->if_index_by_host_if_name, host_if_name, &if_index);
 
-  ethernet_delete_interface (vnm, apif->hw_if_index);
+  if (apif->mode == AF_PACKET_IF_MODE_ETHERNET)
+    ethernet_delete_interface (vnm, apif->hw_if_index);
+  else
+    vnet_delete_hw_interface (vnm, apif->hw_if_index);
 
   pool_put (apm->interfaces, apif);
 
diff --git a/src/vnet/devices/af_packet/af_packet.h b/src/vnet/devices/af_packet/af_packet.h
index 3163aa0..652e173 100644
--- a/src/vnet/devices/af_packet/af_packet.h
+++ b/src/vnet/devices/af_packet/af_packet.h
@@ -18,9 +18,14 @@
  */
 
 #include <vppinfra/lock.h>
-
 #include <vlib/log.h>
 
+typedef enum
+{
+  AF_PACKET_IF_MODE_ETHERNET = 1,
+  AF_PACKET_IF_MODE_IP = 2
+} af_packet_if_mode_t;
+
 typedef struct
 {
   u32 sw_if_index;
@@ -49,6 +54,7 @@
   u8 is_admin_up;
   u32 queue_index;
   u32 host_mtu;
+  af_packet_if_mode_t mode;
 } af_packet_if_t;
 
 typedef struct
@@ -77,6 +83,7 @@
   u32 tx_frame_size;
   u32 rx_frames_per_block;
   u32 tx_frames_per_block;
+  af_packet_if_mode_t mode;
 
   /* return */
   u32 sw_if_index;
diff --git a/src/vnet/devices/af_packet/cli.c b/src/vnet/devices/af_packet/cli.c
index bae4f61..3dd3c8e 100644
--- a/src/vnet/devices/af_packet/cli.c
+++ b/src/vnet/devices/af_packet/cli.c
@@ -51,6 +51,9 @@
 
   clib_memset (arg, 0, sizeof (*arg));
 
+  // Default mode
+  arg->mode = AF_PACKET_IF_MODE_ETHERNET;
+
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
     return 0;
@@ -69,6 +72,8 @@
       else if (unformat (line_input, "tx-per-block %u",
 			 &arg->tx_frames_per_block))
 	;
+      else if (unformat (line_input, "mode ip"))
+	arg->mode = AF_PACKET_IF_MODE_IP;
       else if (unformat (line_input, "hw-addr %U", unformat_ethernet_address,
 			 hwaddr))
 	arg->hw_addr = hwaddr;
@@ -140,7 +145,8 @@
 ?*/
 VLIB_CLI_COMMAND (af_packet_create_command, static) = {
   .path = "create host-interface",
-  .short_help = "create host-interface name <ifname> [hw-addr <mac-addr>]",
+  .short_help =
+    "create host-interface name <ifname> [hw-addr <mac-addr>] [mode ip]",
   .function = af_packet_create_command_fn,
 };
 
diff --git a/src/vnet/devices/af_packet/device.c b/src/vnet/devices/af_packet/device.c
index 0542b16..8e4bc2b 100644
--- a/src/vnet/devices/af_packet/device.c
+++ b/src/vnet/devices/af_packet/device.c
@@ -339,6 +339,14 @@
   int rv, fd = socket (AF_UNIX, SOCK_DGRAM, 0);
   struct ifreq ifr;
 
+  if (apif->mode == AF_PACKET_IF_MODE_IP)
+    {
+      vlib_log_warn (apm->log_class, "af_packet_%s interface is in IP mode",
+		     apif->host_if_name);
+      return clib_error_return (0,
+				" MAC update failed, interface is in IP mode");
+    }
+
   if (0 > fd)
     {
       vlib_log_warn (apm->log_class, "af_packet_%s could not open socket",
diff --git a/src/vnet/devices/af_packet/node.c b/src/vnet/devices/af_packet/node.c
index e2f87b1..0fdae5c 100644
--- a/src/vnet/devices/af_packet/node.c
+++ b/src/vnet/devices/af_packet/node.c
@@ -194,7 +194,7 @@
 {
   af_packet_main_t *apm = &af_packet_main;
   struct tpacket2_hdr *tph;
-  u32 next_index = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT;
+  u32 next_index;
   u32 block = 0;
   u32 rx_frame;
   u32 n_free_bufs;
@@ -209,6 +209,21 @@
   u32 thread_index = vm->thread_index;
   u32 n_buffer_bytes = vlib_buffer_get_default_data_size (vm);
   u32 min_bufs = apif->rx_req->tp_frame_size / n_buffer_bytes;
+  vlib_buffer_t bt;
+
+  if (apif->mode == AF_PACKET_IF_MODE_IP)
+    {
+      next_index = VNET_DEVICE_INPUT_NEXT_IP4_INPUT;
+    }
+  else
+    {
+      next_index = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT;
+      if (PREDICT_FALSE (apif->per_interface_next_index != ~0))
+	next_index = apif->per_interface_next_index;
+
+      /* redirect if feature path enabled */
+      vnet_feature_start_device_input_x1 (apif->sw_if_index, &next_index, &bt);
+    }
 
   n_free_bufs = vec_len (apm->rx_buffers[thread_index]);
   if (PREDICT_FALSE (n_free_bufs < VLIB_FRAME_SIZE))
@@ -317,14 +332,30 @@
 	    }
 	  else
 	    {
-	      next0 = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT;
-
-	      if (PREDICT_FALSE (apif->per_interface_next_index != ~0))
-		next0 = apif->per_interface_next_index;
-
-	      /* redirect if feature path enabled */
-	      vnet_feature_start_device_input_x1 (apif->sw_if_index, &next0,
-						  first_b0);
+	      if (PREDICT_FALSE (apif->mode == AF_PACKET_IF_MODE_IP))
+		{
+		  switch (first_b0->data[0] & 0xf0)
+		    {
+		    case 0x40:
+		      next0 = VNET_DEVICE_INPUT_NEXT_IP4_INPUT;
+		      break;
+		    case 0x60:
+		      next0 = VNET_DEVICE_INPUT_NEXT_IP6_INPUT;
+		      break;
+		    default:
+		      next0 = VNET_DEVICE_INPUT_NEXT_DROP;
+		      break;
+		    }
+		  if (PREDICT_FALSE (apif->per_interface_next_index != ~0))
+		    next0 = apif->per_interface_next_index;
+		}
+	      else
+		{
+		  /* copy feature arc data from template */
+		  first_b0->current_config_index = bt.current_config_index;
+		  vnet_buffer (first_b0)->feature_arc_index =
+		    vnet_buffer (&bt)->feature_arc_index;
+		}
 	    }
 
 	  /* trace */