dev: secondary interfaces support

Type: feature
Change-Id: I6cc4340431b8273022955fca1600061a722e3ace
Signed-off-by: Damjan Marion <damarion@cisco.com>
diff --git a/src/vnet/dev/api.c b/src/vnet/dev/api.c
index 48ceccf..d968f66 100644
--- a/src/vnet/dev/api.c
+++ b/src/vnet/dev/api.c
@@ -156,6 +156,7 @@
 {
   vnet_dev_t *dev = vnet_dev_by_index (args->dev_index);
   vnet_dev_port_t *port = 0;
+  vnet_dev_port_if_create_args_t a = {};
   u16 n_threads = vlib_get_n_threads ();
   int default_is_intr_mode;
   vnet_dev_rv_t rv;
@@ -181,7 +182,7 @@
   if (!port)
     return VNET_DEV_ERR_INVALID_DEVICE_ID;
 
-  if (port->interface_created)
+  if (port->interfaces)
     return VNET_DEV_ERR_ALREADY_EXISTS;
 
   if (args->args)
@@ -202,47 +203,82 @@
     {
       if (args->num_rx_queues > port->attr.max_rx_queues)
 	return VNET_DEV_ERR_INVALID_NUM_RX_QUEUES;
-      port->intf.num_rx_queues = args->num_rx_queues;
+      a.num_rx_queues = args->num_rx_queues;
     }
   else
-    port->intf.num_rx_queues = clib_min (port->attr.max_tx_queues, 1);
+    a.num_rx_queues = clib_min (port->attr.max_tx_queues, 1);
 
   if (args->num_tx_queues)
     {
       if (args->num_tx_queues > port->attr.max_tx_queues)
 	return VNET_DEV_ERR_INVALID_NUM_TX_QUEUES;
-      port->intf.num_tx_queues = args->num_tx_queues;
+      a.num_tx_queues = args->num_tx_queues;
     }
   else
-    port->intf.num_tx_queues = clib_min (port->attr.max_tx_queues, n_threads);
+    a.num_tx_queues = clib_min (port->attr.max_tx_queues, n_threads);
 
   if (args->rx_queue_size)
     {
       if (!_vnet_dev_queue_size_validate (args->rx_queue_size,
 					  port->rx_queue_config))
 	return VNET_DEV_ERR_INVALID_RX_QUEUE_SIZE;
-      port->intf.rxq_sz = args->rx_queue_size;
+      a.rxq_sz = args->rx_queue_size;
     }
   else
-    port->intf.rxq_sz = port->rx_queue_config.default_size;
+    a.rxq_sz = port->rx_queue_config.default_size;
 
   if (args->tx_queue_size)
     {
       if (!_vnet_dev_queue_size_validate (args->tx_queue_size,
 					  port->tx_queue_config))
 	return VNET_DEV_ERR_INVALID_TX_QUEUE_SIZE;
-      port->intf.txq_sz = args->tx_queue_size;
+      a.txq_sz = args->tx_queue_size;
     }
   else
-    port->intf.txq_sz = port->tx_queue_config.default_size;
+    a.txq_sz = port->tx_queue_config.default_size;
 
-  clib_memcpy (port->intf.name, args->intf_name, sizeof (port->intf.name));
-  port->intf.default_is_intr_mode = default_is_intr_mode;
-  port->intf.consistent_qp =
-    (args->flags.n & VNET_DEV_PORT_F_CONSISTENT_QP) != 0;
+  clib_memcpy (a.name, args->intf_name, sizeof (a.name));
+  a.default_is_intr_mode = default_is_intr_mode;
+  a.consistent_qp = (args->flags.n & VNET_DEV_PORT_F_CONSISTENT_QP) != 0;
 
-  rv = vnet_dev_process_call_port_op (vm, port, vnet_dev_port_if_create);
-  args->sw_if_index = (rv == VNET_DEV_OK) ? port->intf.sw_if_index : ~0;
+  rv = vnet_dev_process_call_port_op_with_ptr (vm, port,
+					       vnet_dev_port_if_create, &a);
+  args->sw_if_index = (rv == VNET_DEV_OK) ? a.sw_if_index : ~0;
+
+  return rv;
+}
+
+vnet_dev_rv_t
+vnet_dev_api_port_add_sec_if (vlib_main_t *vm,
+			      vnet_dev_api_port_add_sec_if_args_t *args)
+{
+  vnet_dev_port_t *port = 0;
+  vnet_dev_t *dev = 0;
+  vnet_dev_port_sec_if_create_args_t a = {};
+  vnet_dev_rv_t rv = VNET_DEV_OK;
+
+  port = vnet_dev_get_port_from_sw_if_index (args->primary_sw_if_index);
+  if (port == 0)
+    return VNET_DEV_ERR_NOT_FOUND;
+
+  log_debug (dev,
+	     "create_port_if: primary_sw_if_index %u intf_name '%s' "
+	     "args '%v'",
+	     args->primary_sw_if_index, args->intf_name, args->args);
+
+  if (port->interfaces == 0)
+    return VNET_DEV_ERR_PRIMARY_INTERFACE_MISSING;
+
+  clib_memcpy (a.name, args->intf_name, sizeof (a.name));
+  a.args = args->args;
+
+  rv = vnet_dev_process_call_port_op_with_ptr (vm, port,
+					       vnet_dev_port_add_sec_if, &a);
+
+  if (rv != VNET_DEV_OK)
+    args->sw_if_index = ~0;
+  else
+    args->sw_if_index = a.sw_if_index;
 
   return rv;
 }
@@ -251,9 +287,23 @@
 vnet_dev_api_remove_port_if (vlib_main_t *vm,
 			     vnet_dev_api_remove_port_if_args_t *args)
 {
+  vnet_dev_port_t *port;
+
+  port = vnet_dev_get_port_from_sw_if_index (args->sw_if_index);
+
+  if (port == 0)
+    return VNET_DEV_ERR_UNKNOWN_INTERFACE;
+
+  return vnet_dev_process_call_port_op (vm, port, vnet_dev_port_if_remove);
+}
+
+vnet_dev_rv_t
+vnet_dev_api_port_del_sec_if (vlib_main_t *vm,
+			      vnet_dev_api_port_del_sec_if_args_t *args)
+{
   vnet_dev_main_t *dm = &vnet_dev_main;
   vnet_main_t *vnm = vnet_get_main ();
-  vnet_sw_interface_t *si;
+  vnet_sw_interface_t *si, *sup_si;
   vnet_hw_interface_t *hi;
   vnet_dev_port_t *port;
 
@@ -261,7 +311,14 @@
   if (!si)
     return VNET_DEV_ERR_UNKNOWN_INTERFACE;
 
-  hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index);
+  if (si->sup_sw_if_index == si->sw_if_index)
+    return VNET_DEV_ERR_UNKNOWN_INTERFACE;
+
+  sup_si = vnet_get_sw_interface_or_null (vnm, si->sup_sw_if_index);
+  if (!sup_si)
+    return VNET_DEV_ERR_UNKNOWN_INTERFACE;
+
+  hi = vnet_get_hw_interface_or_null (vnm, sup_si->hw_if_index);
   if (!hi)
     return VNET_DEV_ERR_UNKNOWN_INTERFACE;
 
@@ -270,8 +327,10 @@
 
   port = vnet_dev_get_port_from_dev_instance (hi->dev_instance);
 
-  if (port->intf.hw_if_index != si->hw_if_index)
+  if (port->interfaces->primary_interface.hw_if_index != si->hw_if_index)
     return VNET_DEV_ERR_UNKNOWN_INTERFACE;
 
-  return vnet_dev_process_call_port_op (vm, port, vnet_dev_port_if_remove);
+  return vnet_dev_process_call_port_op_with_ptr (
+    vm, port, vnet_dev_port_del_sec_if,
+    &(vnet_dev_port_del_sec_if_args_t){ .sw_if_index = args->sw_if_index });
 }
diff --git a/src/vnet/dev/api.h b/src/vnet/dev/api.h
index 1b7bf27..3e552e4 100644
--- a/src/vnet/dev/api.h
+++ b/src/vnet/dev/api.h
@@ -65,4 +65,27 @@
 vnet_dev_api_remove_port_if (vlib_main_t *,
 			     vnet_dev_api_remove_port_if_args_t *);
 
+typedef struct
+{
+  u32 primary_sw_if_index;
+  vnet_dev_if_name_t intf_name;
+  u8 *args;
+
+  /* return */
+  u32 sw_if_index;
+} vnet_dev_api_port_add_sec_if_args_t;
+
+vnet_dev_rv_t
+vnet_dev_api_port_add_sec_if (vlib_main_t *,
+			      vnet_dev_api_port_add_sec_if_args_t *);
+
+typedef struct
+{
+  u32 sw_if_index;
+} vnet_dev_api_port_del_sec_if_args_t;
+
+vnet_dev_rv_t
+vnet_dev_api_port_del_sec_if (vlib_main_t *,
+			      vnet_dev_api_port_del_sec_if_args_t *);
+
 #endif /* _VNET_DEV_API_H_ */
diff --git a/src/vnet/dev/cli.c b/src/vnet/dev/cli.c
index 608abcf..6002a2f 100644
--- a/src/vnet/dev/cli.c
+++ b/src/vnet/dev/cli.c
@@ -223,6 +223,94 @@
 };
 
 static clib_error_t *
+device_create_sec_if_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
+			     vlib_cli_command_t *cmd)
+{
+  vnet_dev_api_port_add_sec_if_args_t a = {};
+  vnet_main_t *vnm = vnet_get_main ();
+  vnet_dev_rv_t rv;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (!a.intf_name[0] &&
+	  unformat (input, "if-name %U", unformat_c_string_array, a.intf_name,
+		    sizeof (a.intf_name)))
+	;
+      else if (unformat (input, "primary-if-name %U",
+			 unformat_vnet_sw_interface, vnm,
+			 &a.primary_sw_if_index))
+	;
+      else if (unformat (input, "primary-sw-if-index %u",
+			 &a.primary_sw_if_index))
+	;
+      else if (!a.args && unformat (input, "args %v", &a.args))
+	;
+      else
+	return clib_error_return (0, "unknown input `%U'",
+				  format_unformat_error, input);
+    }
+
+  rv = vnet_dev_api_port_add_sec_if (vm, &a);
+
+  vec_free (a.args);
+
+  if (rv != VNET_DEV_OK)
+    return clib_error_return (0, "unable to create secondary interface: %U",
+			      format_vnet_dev_rv, rv);
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (device_create_sec_if_cmd, static) = {
+  .path = "device create-secondary-interface",
+  .short_help = "device create-secondary-interface [<interface-name> | "
+		"sw-if-index <n>] id <n> [args <sec-if-args>]",
+  .function = device_create_sec_if_cmd_fn,
+  .is_mp_safe = 1,
+};
+
+static clib_error_t *
+device_remove_sec_if_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
+			     vlib_cli_command_t *cmd)
+{
+  vnet_dev_api_port_del_sec_if_args_t a = { .sw_if_index = ~0 };
+  vnet_main_t *vnm = vnet_get_main ();
+  vnet_dev_rv_t rv;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "%U", unformat_vnet_sw_interface, vnm,
+		    &a.sw_if_index))
+	;
+      else if (unformat (input, "sw-if-index %u", &a.sw_if_index))
+	;
+      else
+	return clib_error_return (0, "unknown input `%U'",
+				  format_unformat_error, input);
+    }
+
+  if (a.sw_if_index == ~0)
+    return clib_error_return (
+      0, "please specify existing secondary interface name");
+
+  rv = vnet_dev_api_port_del_sec_if (vm, &a);
+
+  if (rv != VNET_DEV_OK)
+    return clib_error_return (0, "unable to remove secondary interface: %U",
+			      format_vnet_dev_rv, rv);
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (device_remove_sec_if_cmd, static) = {
+  .path = "device remove-secondary-interface",
+  .short_help =
+    "device remove-secondary-interface [<interface-name> | sw-if-index <n>]",
+  .function = device_remove_sec_if_cmd_fn,
+  .is_mp_safe = 1,
+};
+
+static clib_error_t *
 show_devices_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
 		     vlib_cli_command_t *cmd)
 {
diff --git a/src/vnet/dev/dev.c b/src/vnet/dev/dev.c
index e04fa16..7954707 100644
--- a/src/vnet/dev/dev.c
+++ b/src/vnet/dev/dev.c
@@ -130,7 +130,7 @@
   vnet_dev_validate (vm, dev);
 
   foreach_vnet_dev_port (p, dev)
-    ASSERT (p->interface_created == 0);
+    ASSERT (p->interfaces == 0);
 
   if (dev->ops.deinit)
     dev->ops.deinit (vm, dev);
@@ -188,7 +188,7 @@
 vnet_dev_detach (vlib_main_t *vm, vnet_dev_t *dev)
 {
   foreach_vnet_dev_port (p, dev)
-    if (p->interface_created)
+    if (p->interfaces)
       vnet_dev_port_if_remove (vm, p);
   vnet_dev_deinit (vm, dev);
   vnet_dev_free (vm, dev);
@@ -260,6 +260,8 @@
   vnet_feature_config_main_t *cm;
   vnet_dev_main_t *vdm = &vnet_dev_main;
   vnet_dev_port_t *port;
+  vnet_dev_port_interface_t *intf;
+  vnet_dev_instance_t *di;
   vnet_hw_interface_t *hw;
   u32 current_config_index = ~0;
   u32 next_index = ~0;
@@ -269,9 +271,18 @@
     return;
 
   hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
-  port = vnet_dev_get_port_from_dev_instance (hw->dev_instance);
+  di = vnet_dev_get_dev_instance (hw->dev_instance);
 
-  if (port == 0 || port->intf.sw_if_index != sw_if_index)
+  if (!di)
+    return;
+
+  intf = di->is_primary_if ?
+	   vnet_dev_port_get_primary_if (di->port) :
+	   vnet_dev_port_get_sec_if_by_index (di->port, di->sec_if_index);
+
+  port = di->port;
+
+  if (port == 0 || intf->sw_if_index != sw_if_index)
     return;
 
   if (vnet_have_features (arc_index, sw_if_index))
@@ -281,28 +292,27 @@
 	vec_elt (cm->config_index_by_sw_if_index, sw_if_index);
       vnet_get_config_data (&cm->config_main, &current_config_index,
 			    &next_index, 0);
-      if (port->intf.feature_arc == 0 ||
-	  port->intf.rx_next_index != next_index ||
-	  port->intf.current_config_index != current_config_index)
+      if (intf->feature_arc == 0 || intf->rx_next_index != next_index ||
+	  intf->current_config_index != current_config_index)
 	{
-	  port->intf.current_config_index = current_config_index;
-	  port->intf.rx_next_index = next_index;
-	  port->intf.feature_arc_index = arc_index;
-	  port->intf.feature_arc = 1;
+	  intf->current_config_index = current_config_index;
+	  intf->rx_next_index = next_index;
+	  intf->feature_arc_index = arc_index;
+	  intf->feature_arc = 1;
 	  update_runtime = 1;
 	}
     }
   else
     {
-      if (port->intf.feature_arc)
+      if (intf->feature_arc)
 	{
-	  port->intf.current_config_index = 0;
-	  port->intf.rx_next_index =
-	    port->intf.redirect_to_node ?
-		    port->intf.redirect_to_node_next_index :
-		    vnet_dev_default_next_index_by_port_type[port->attr.type];
-	  port->intf.feature_arc_index = 0;
-	  port->intf.feature_arc = 0;
+	  intf->current_config_index = 0;
+	  intf->rx_next_index =
+	    intf->redirect_to_node ?
+	      intf->redirect_to_node_next_index :
+	      vnet_dev_default_next_index_by_port_type[port->attr.type];
+	  intf->feature_arc_index = 0;
+	  intf->feature_arc = 0;
 	  update_runtime = 1;
 	}
     }
diff --git a/src/vnet/dev/dev.h b/src/vnet/dev/dev.h
index 76ff4f5..f3f7563 100644
--- a/src/vnet/dev/dev.h
+++ b/src/vnet/dev/dev.h
@@ -29,7 +29,8 @@
   _ (interrupt_mode)                                                          \
   _ (rss)                                                                     \
   _ (change_max_rx_frame_size)                                                \
-  _ (mac_filter)
+  _ (mac_filter)                                                              \
+  _ (secondary_interfaces)
 
 #define foreach_vnet_dev_port_rx_offloads _ (ip4_cksum)
 
@@ -253,6 +254,8 @@
   vnet_dev_port_op_no_rv_t *deinit;
   vnet_dev_port_op_no_rv_t *free;
   vnet_dev_port_op_no_rv_t *clear_counters;
+  vnet_dev_port_op_with_ptr_t *add_sec_if;
+  vnet_dev_port_op_with_ptr_t *del_sec_if;
   format_function_t *format_status;
   format_function_t *format_flow;
 } vnet_dev_port_ops_t;
@@ -269,30 +272,41 @@
   u8 as_number;
 } vnet_dev_rx_queue_rt_req_t;
 
+typedef struct
+{
+  vlib_buffer_template_t buffer_template;
+  u32 sw_if_index;
+  u16 next_index;
+  u16 sec_if_index;
+} vnet_dev_rx_queue_if_rt_data_t;
+
 typedef struct vnet_dev_rx_queue
 {
   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
   vnet_dev_port_t *port;
   u16 rx_thread_index;
   u16 index;
-  vnet_dev_counter_main_t *counter_main;
-  CLIB_CACHE_LINE_ALIGN_MARK (runtime0);
-  vnet_dev_rx_queue_t *next_on_thread;
+  u16 size;
   u8 interrupt_mode : 1;
   u8 enabled : 1;
   u8 started : 1;
   u8 suspended : 1;
-  vnet_dev_queue_id_t queue_id;
-  u16 size;
-  u16 next_index;
   vnet_dev_rx_queue_rt_req_t runtime_request;
+  vnet_dev_counter_main_t *counter_main;
+  vnet_dev_rx_queue_t *next_on_thread;
+  vnet_dev_queue_id_t queue_id;
+  vnet_dev_rx_queue_if_rt_data_t **sec_if_rt_data;
   CLIB_CACHE_LINE_ALIGN_MARK (runtime1);
-  vlib_buffer_template_t buffer_template;
+  vnet_dev_rx_queue_if_rt_data_t if_rt_data;
   CLIB_CACHE_LINE_ALIGN_MARK (driver_data);
   u8 data[];
 } vnet_dev_rx_queue_t;
 
+#if CLIB_CACHE_LINE_BYTES > 64
+STATIC_ASSERT_SIZEOF (vnet_dev_rx_queue_t, 2 * CLIB_CACHE_LINE_BYTES);
+#else
 STATIC_ASSERT_SIZEOF (vnet_dev_rx_queue_t, 3 * CLIB_CACHE_LINE_BYTES);
+#endif
 
 typedef struct vnet_dev_tx_queue
 {
@@ -314,6 +328,38 @@
 
 STATIC_ASSERT_SIZEOF (vnet_dev_tx_queue_t, 2 * CLIB_CACHE_LINE_BYTES);
 
+typedef struct
+{
+  vnet_dev_if_name_t name;
+  u8 interface_created : 1;
+  u8 feature_arc : 1;
+  u8 redirect_to_node : 1;
+  u8 feature_arc_index;
+  u16 rx_next_index;
+  u32 index;
+  u32 sw_if_index;
+  u32 hw_if_index;
+  u32 dev_instance;
+  u32 tx_node_index;
+  u32 next_index;
+  u32 current_config_index;
+  u16 redirect_to_node_next_index;
+  u32 user_data;
+  vnet_dev_arg_t *args;
+} vnet_dev_port_interface_t;
+
+typedef struct
+{
+  u32 rx_node_index;
+  u8 default_is_intr_mode : 1;
+  u16 num_rx_queues;
+  u16 num_tx_queues;
+  u16 txq_sz;
+  u16 rxq_sz;
+  vnet_dev_port_interface_t primary_interface;
+  vnet_dev_port_interface_t **secondary_interfaces;
+} vnet_dev_port_interfaces_t;
+
 typedef struct vnet_dev_port
 {
   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
@@ -324,7 +370,6 @@
   u8 started : 1;
   u8 link_up : 1;
   u8 promisc : 1;
-  u8 interface_created : 1;
   u8 rx_node_assigned : 1;
   vnet_dev_counter_main_t *counter_main;
   vnet_dev_queue_config_t rx_queue_config;
@@ -339,32 +384,12 @@
   vnet_dev_tx_queue_t **tx_queues;
   vnet_dev_port_ops_t port_ops;
   vnet_dev_arg_t *args;
+  vnet_dev_arg_t *sec_if_args;
   vnet_dev_rx_queue_ops_t rx_queue_ops;
   vnet_dev_tx_queue_ops_t tx_queue_ops;
   vnet_dev_node_t rx_node;
   vnet_dev_node_t tx_node;
-
-  struct
-  {
-    vnet_dev_if_name_t name;
-    u32 dev_instance;
-    u32 rx_node_index;
-    u32 current_config_index;
-    u16 rx_next_index;
-    u16 redirect_to_node_next_index;
-    u8 feature_arc_index;
-    u8 feature_arc : 1;
-    u8 redirect_to_node : 1;
-    u8 default_is_intr_mode : 1;
-    u8 consistent_qp : 1;
-    u32 tx_node_index;
-    u32 hw_if_index;
-    u32 sw_if_index;
-    u16 num_rx_queues;
-    u16 num_tx_queues;
-    u16 txq_sz;
-    u16 rxq_sz;
-  } intf;
+  vnet_dev_port_interfaces_t *interfaces;
 
   CLIB_CACHE_LINE_ALIGN_MARK (data0);
   u8 data[];
@@ -463,6 +488,8 @@
 typedef struct
 {
   vnet_dev_port_t *port;
+  u32 sec_if_index;
+  u8 is_primary_if : 1;
 } vnet_dev_instance_t;
 
 typedef struct
@@ -493,6 +520,7 @@
     vnet_dev_port_attr_t attr;
     vnet_dev_port_ops_t ops;
     vnet_dev_arg_t *args;
+    vnet_dev_arg_t *sec_if_args;
     u16 data_size;
     void *initial_data;
   } port;
@@ -578,12 +606,44 @@
 void vnet_dev_set_interface_next_node (vnet_main_t *, u32, u32);
 
 /* port.c */
+
+typedef struct
+{
+  vnet_dev_if_name_t name;
+  u16 num_rx_queues;
+  u16 num_tx_queues;
+  u16 rxq_sz;
+  u16 txq_sz;
+  u8 default_is_intr_mode : 1;
+  u8 consistent_qp : 1;
+
+  /* return */
+  u32 sw_if_index;
+} vnet_dev_port_if_create_args_t;
+
+typedef struct
+{
+  vnet_dev_if_name_t name;
+  u8 *args;
+
+  /* return */
+  u32 sw_if_index;
+} vnet_dev_port_sec_if_create_args_t;
+
+typedef struct
+{
+  u32 sw_if_index;
+} vnet_dev_port_del_sec_if_args_t;
+
 vnet_dev_port_op_t vnet_dev_port_start;
 vnet_dev_port_op_t vnet_dev_port_start_all_rx_queues;
 vnet_dev_port_op_t vnet_dev_port_start_all_tx_queues;
 vnet_dev_port_op_no_rv_t vnet_dev_port_stop;
 vnet_dev_port_op_no_rv_t vnet_dev_port_deinit;
 vnet_dev_port_op_no_rv_t vnet_dev_port_free;
+vnet_dev_port_op_with_ptr_t vnet_dev_port_add_sec_if;
+vnet_dev_port_op_with_ptr_t vnet_dev_port_del_sec_if;
+
 void vnet_dev_port_add_counters (vlib_main_t *, vnet_dev_port_t *,
 				 vnet_dev_counter_t *, u16);
 vnet_dev_port_op_no_rv_t vnet_dev_port_free_counters;
@@ -596,7 +656,7 @@
 				       vnet_dev_port_cfg_change_req_t *);
 vnet_dev_rv_t vnet_dev_port_cfg_change (vlib_main_t *, vnet_dev_port_t *,
 					vnet_dev_port_cfg_change_req_t *);
-vnet_dev_port_op_t vnet_dev_port_if_create;
+vnet_dev_port_op_with_ptr_t vnet_dev_port_if_create;
 vnet_dev_port_op_t vnet_dev_port_if_remove;
 
 /* queue.c */
diff --git a/src/vnet/dev/dev_funcs.h b/src/vnet/dev/dev_funcs.h
index 0531b17..f47344b 100644
--- a/src/vnet/dev/dev_funcs.h
+++ b/src/vnet/dev/dev_funcs.h
@@ -60,6 +60,18 @@
   return pool_elt_at_index (dm->dev_instances, dev_instance);
 }
 
+static_always_inline vnet_dev_port_interface_t *
+vnet_dev_port_get_primary_if (vnet_dev_port_t *p)
+{
+  return &p->interfaces->primary_interface;
+}
+
+static_always_inline vnet_dev_port_interface_t *
+vnet_dev_port_get_sec_if_by_index (vnet_dev_port_t *p, u32 index)
+{
+  return *pool_elt_at_index (p->interfaces->secondary_interfaces, index);
+}
+
 static_always_inline vnet_dev_port_t *
 vnet_dev_get_port_from_dev_instance (u32 dev_instance)
 {
@@ -76,7 +88,8 @@
   hw = vnet_get_hw_interface (vnet_get_main (), hw_if_index);
   port = vnet_dev_get_port_from_dev_instance (hw->dev_instance);
 
-  if (!port || port->intf.hw_if_index != hw_if_index)
+  if (!port || !port->interfaces ||
+      port->interfaces->primary_interface.hw_if_index != hw_if_index)
     return 0;
 
   return port;
@@ -85,19 +98,32 @@
 static_always_inline u32
 vnet_dev_get_rx_queue_if_sw_if_index (vnet_dev_rx_queue_t *rxq)
 {
-  return rxq->port->intf.sw_if_index;
+  return rxq->port->interfaces->primary_interface.sw_if_index;
 }
 
 static_always_inline u32
 vnet_dev_get_rx_queue_if_hw_if_index (vnet_dev_rx_queue_t *rxq)
 {
-  return rxq->port->intf.hw_if_index;
+  return rxq->port->interfaces->primary_interface.hw_if_index;
 }
 
 static_always_inline u32
 vnet_dev_get_port_rx_node_index (vnet_dev_port_t *port)
 {
-  return port->intf.rx_node_index;
+  return port->interfaces->rx_node_index;
+}
+
+static_always_inline vnet_dev_port_t *
+vnet_dev_get_port_from_sw_if_index (u32 sw_if_index)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  vnet_sw_interface_t *si;
+
+  si = vnet_get_sw_interface_or_null (vnm, sw_if_index);
+  if (!si)
+    return 0;
+
+  return vnet_dev_get_port_from_hw_if_index (si->hw_if_index);
 }
 
 static_always_inline vnet_dev_t *
@@ -219,22 +245,49 @@
   __atomic_store_n (&txq->lock, 0, __ATOMIC_RELEASE);
 }
 
+static_always_inline vnet_dev_rx_queue_if_rt_data_t *
+vnet_dev_get_rx_queue_if_rt_data (vnet_dev_rx_queue_t *rxq)
+{
+  return &rxq->if_rt_data;
+}
+
+static_always_inline vnet_dev_rx_queue_if_rt_data_t *
+vnet_dev_get_rx_queue_sec_if_rt_data (vnet_dev_rx_queue_t *rxq,
+				      u32 sec_if_index)
+{
+  return rxq->sec_if_rt_data[sec_if_index];
+}
+
 static_always_inline vlib_buffer_template_t
 vnet_dev_get_rx_queue_if_buffer_template (vnet_dev_rx_queue_t *rxq)
 {
-  return rxq->buffer_template;
+  return rxq->if_rt_data.buffer_template;
+}
+
+static_always_inline vlib_buffer_template_t
+vnet_dev_get_rx_queue_sec_if_buffer_template (vnet_dev_rx_queue_t *rxq,
+					      u32 sec_if_index)
+{
+  return rxq->sec_if_rt_data[sec_if_index]->buffer_template;
 }
 
 static_always_inline u16
 vnet_dev_get_rx_queue_if_next_index (vnet_dev_rx_queue_t *rxq)
 {
-  return rxq->next_index;
+  return rxq->if_rt_data.next_index;
+}
+
+static_always_inline u16
+vnet_dev_get_rx_queue_sec_if_next_index (vnet_dev_rx_queue_t *rxq,
+					 u32 sec_if_index)
+{
+  return rxq->sec_if_rt_data[sec_if_index]->next_index;
 }
 
 static_always_inline u8
 vnet_dev_get_rx_queue_buffer_pool_index (vnet_dev_rx_queue_t *rxq)
 {
-  return rxq->buffer_template.buffer_pool_index;
+  return rxq->if_rt_data.buffer_template.buffer_pool_index;
 }
 
 static_always_inline u32
@@ -269,8 +322,8 @@
 foreach_vnet_dev_rx_queue_runtime_helper (vlib_node_runtime_t *node,
 					  vnet_dev_rx_queue_t *rxq)
 {
-  vnet_dev_port_t *port;
   vnet_dev_rx_queue_rt_req_t req;
+  vnet_dev_port_interfaces_t *ifs;
 
   if (rxq == 0)
     rxq = vnet_dev_get_rx_node_runtime (node)->first_rx_queue;
@@ -287,15 +340,34 @@
   req.as_number =
     __atomic_exchange_n (&rxq->runtime_request.as_number, 0, __ATOMIC_ACQUIRE);
 
-  port = rxq->port;
+  ifs = rxq->port->interfaces;
   if (req.update_next_index)
-    rxq->next_index = port->intf.rx_next_index;
+    {
+      vnet_dev_port_interface_t **si =
+	rxq->port->interfaces->secondary_interfaces;
+      rxq->if_rt_data.next_index = ifs->primary_interface.rx_next_index;
+      vec_foreach_pointer (rtd, rxq->sec_if_rt_data)
+	if (rtd)
+	  rtd->next_index = si[rtd->sec_if_index]->next_index;
+    }
 
   if (req.update_feature_arc)
     {
-      vlib_buffer_template_t *bt = &rxq->buffer_template;
-      bt->current_config_index = port->intf.current_config_index;
-      vnet_buffer (bt)->feature_arc_index = port->intf.feature_arc_index;
+      vnet_dev_port_interface_t **si =
+	rxq->port->interfaces->secondary_interfaces;
+      vlib_buffer_template_t *bt = &rxq->if_rt_data.buffer_template;
+      bt->current_config_index = ifs->primary_interface.current_config_index;
+      vnet_buffer (bt)->feature_arc_index =
+	ifs->primary_interface.feature_arc_index;
+      vec_foreach_pointer (rtd, rxq->sec_if_rt_data)
+	if (rtd)
+	  {
+	    vlib_buffer_template_t *bt = &rtd->buffer_template;
+	    bt->current_config_index =
+	      si[rtd->sec_if_index]->current_config_index;
+	    vnet_buffer (bt)->feature_arc_index =
+	      si[rtd->sec_if_index]->feature_arc_index;
+	  }
     }
 
   if (req.suspend_on)
diff --git a/src/vnet/dev/errors.h b/src/vnet/dev/errors.h
index 6ececad..243b10e 100644
--- a/src/vnet/dev/errors.h
+++ b/src/vnet/dev/errors.h
@@ -37,9 +37,12 @@
   _ (TIMEOUT, "timeout")                                                      \
   _ (UNKNOWN_DEVICE, "unknown device")                                        \
   _ (UNKNOWN_INTERFACE, "unknown interface")                                  \
+  _ (NOT_PRIMARY_INTERFACE, "not primary interface")                          \
+  _ (PRIMARY_INTERFACE_MISSING, "primary interface missing")                  \
   _ (UNSUPPORTED_CONFIG, "unsupported config")                                \
   _ (UNSUPPORTED_DEVICE, "unsupported device")                                \
   _ (UNSUPPORTED_DEVICE_VER, "unsupported device version")                    \
+  _ (UNSUPPORTED_INTERFACE, "unsupported interface")                          \
   _ (ALREADY_DONE, "already done")                                            \
   _ (NO_SUCH_INTERFACE, "no such interface")                                  \
   _ (INIT_FAILED, "init failed")
diff --git a/src/vnet/dev/format.c b/src/vnet/dev/format.c
index fb46fcc..ffc4a3a 100644
--- a/src/vnet/dev/format.c
+++ b/src/vnet/dev/format.c
@@ -44,9 +44,15 @@
 format_vnet_dev_interface_name (u8 *s, va_list *args)
 {
   u32 i = va_arg (*args, u32);
-  vnet_dev_port_t *port = vnet_dev_get_port_from_dev_instance (i);
+  vnet_dev_instance_t *di = vnet_dev_get_dev_instance (i);
+  vnet_dev_port_interface_t *si;
+  vnet_dev_port_t *p = di->port;
 
-  return format (s, "%s", port->intf.name);
+  if (di->is_primary_if)
+    return format (s, "%s", p->interfaces->primary_interface.name);
+
+  si = vnet_dev_port_get_sec_if_by_index (p, di->sec_if_index);
+  return format (s, "%s", si->name);
 }
 
 u8 *
@@ -138,12 +144,22 @@
 		format_vnet_dev_args, port->args);
 
   s = format (s, "\n%UInterface ", format_white_space, indent);
-  if (port->interface_created)
+  if (port->interfaces)
     {
-      s = format (s, "assigned, interface name is '%U', RX node is '%U'",
-		  format_vnet_sw_if_index_name, vnm, port->intf.sw_if_index,
-		  format_vlib_node_name, vm,
-		  vnet_dev_get_port_rx_node_index (port));
+      s = format (
+	s, "assigned, primary interface name is '%U', RX node is '%U'",
+	format_vnet_sw_if_index_name, vnm,
+	port->interfaces->primary_interface.sw_if_index, format_vlib_node_name,
+	vm, vnet_dev_get_port_rx_node_index (port));
+      pool_foreach_pointer (sif, port->interfaces->secondary_interfaces)
+	{
+	  s = format (s, "\n%USecondary interface '%U'", format_white_space,
+		      indent, format_vnet_sw_if_index_name, vnm,
+		      sif->sw_if_index);
+	  if (sif->args)
+	    s = format (s, "\n%U args '%U", format_white_space, indent,
+			format_vnet_dev_args, sif->args);
+	}
     }
   else
     s = format (s, "not assigned");
diff --git a/src/vnet/dev/handlers.c b/src/vnet/dev/handlers.c
index 2a55aff..bfacbe2 100644
--- a/src/vnet/dev/handlers.c
+++ b/src/vnet/dev/handlers.c
@@ -19,7 +19,8 @@
 				  u32 frame_size)
 {
   vlib_main_t *vm = vlib_get_main ();
-  vnet_dev_port_t *p = vnet_dev_get_port_from_dev_instance (hw->dev_instance);
+  vnet_dev_instance_t *di = vnet_dev_get_dev_instance (hw->dev_instance);
+  vnet_dev_port_t *p;
   vnet_dev_rv_t rv;
 
   vnet_dev_port_cfg_change_req_t req = {
@@ -27,6 +28,11 @@
     .max_rx_frame_size = frame_size,
   };
 
+  p = di->port;
+
+  if (!di->is_primary_if)
+    return vnet_dev_port_err (vm, p, VNET_DEV_ERR_NOT_PRIMARY_INTERFACE, "");
+
   log_debug (p->dev, "size %u", frame_size);
 
   rv = vnet_dev_port_cfg_change_req_validate (vm, p, &req);
@@ -49,13 +55,17 @@
 			       u32 flags)
 {
   vlib_main_t *vm = vlib_get_main ();
-  vnet_dev_port_t *p = vnet_dev_get_port_from_dev_instance (hw->dev_instance);
+  vnet_dev_instance_t *di = vnet_dev_get_dev_instance (hw->dev_instance);
+  vnet_dev_port_t *p = di->port;
   vnet_dev_rv_t rv;
 
   vnet_dev_port_cfg_change_req_t req = {
     .type = VNET_DEV_PORT_CFG_PROMISC_MODE,
   };
 
+  if (!di->is_primary_if)
+    return ~0;
+
   switch (flags)
     {
     case ETHERNET_INTERFACE_FLAG_DEFAULT_L3:
@@ -87,13 +97,17 @@
 			  const u8 *new)
 {
   vlib_main_t *vm = vlib_get_main ();
-  vnet_dev_port_t *p = vnet_dev_get_port_from_dev_instance (hi->dev_instance);
+  vnet_dev_instance_t *di = vnet_dev_get_dev_instance (hi->dev_instance);
+  vnet_dev_port_t *p = di->port;
   vnet_dev_rv_t rv;
 
   vnet_dev_port_cfg_change_req_t req = {
     .type = VNET_DEV_PORT_CFG_CHANGE_PRIMARY_HW_ADDR,
   };
 
+  if (!di->is_primary_if)
+    return vnet_dev_port_err (vm, p, VNET_DEV_ERR_NOT_PRIMARY_INTERFACE, "");
+
   vnet_dev_set_hw_addr_eth_mac (&req.addr, new);
 
   log_debug (p->dev, "new mac  %U", format_vnet_dev_hw_addr, &req.addr);
@@ -116,7 +130,8 @@
 			      u8 is_add)
 {
   vlib_main_t *vm = vlib_get_main ();
-  vnet_dev_port_t *p = vnet_dev_get_port_from_dev_instance (hi->dev_instance);
+  vnet_dev_instance_t *di = vnet_dev_get_dev_instance (hi->dev_instance);
+  vnet_dev_port_t *p = di->port;
   vnet_dev_rv_t rv;
 
   vnet_dev_port_cfg_change_req_t req = {
@@ -124,6 +139,9 @@
 			   VNET_DEV_PORT_CFG_REMOVE_SECONDARY_HW_ADDR,
   };
 
+  if (!di->is_primary_if)
+    return vnet_dev_port_err (vm, p, VNET_DEV_ERR_NOT_PRIMARY_INTERFACE, "");
+
   vnet_dev_set_hw_addr_eth_mac (&req.addr, address);
 
   log_debug (p->dev, "received (addr %U is_add %u", format_vnet_dev_hw_addr,
@@ -147,10 +165,19 @@
 		      u32 dev_instance, u32 flow_index, uword *private_data)
 {
   vlib_main_t *vm = vlib_get_main ();
-  vnet_dev_port_t *p = vnet_dev_get_port_from_dev_instance (dev_instance);
+  vnet_dev_instance_t *di = vnet_dev_get_dev_instance (dev_instance);
+  vnet_dev_port_t *p;
   vnet_dev_port_cfg_change_req_t req;
   vnet_dev_rv_t rv;
 
+  if (!di)
+    return VNET_FLOW_ERROR_NO_SUCH_INTERFACE;
+
+  if (di->is_primary_if)
+    return VNET_FLOW_ERROR_NOT_SUPPORTED;
+
+  p = di->port;
+
   switch (op)
     {
     case VNET_FLOW_DEV_OP_ADD_FLOW:
@@ -201,10 +228,12 @@
 void
 vnet_dev_clear_hw_interface_counters (u32 instance)
 {
-  vnet_dev_port_t *port = vnet_dev_get_port_from_dev_instance (instance);
+  vnet_dev_instance_t *di = vnet_dev_get_dev_instance (instance);
   vlib_main_t *vm = vlib_get_main ();
 
-  vnet_dev_process_call_port_op_no_rv (vm, port, vnet_dev_port_clear_counters);
+  if (di->is_primary_if)
+    vnet_dev_process_call_port_op_no_rv (vm, di->port,
+					 vnet_dev_port_clear_counters);
 }
 
 void
@@ -213,44 +242,49 @@
 {
   vlib_main_t *vm = vlib_get_main ();
   vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
-  vnet_dev_port_t *port =
-    vnet_dev_get_port_from_dev_instance (hw->dev_instance);
+  vnet_dev_instance_t *di = vnet_dev_get_dev_instance (hw->dev_instance);
+  vnet_dev_port_interface_t *intf;
   int runtime_update = 0;
 
+  if (di->is_primary_if)
+    intf = vnet_dev_port_get_primary_if (di->port);
+  else
+    intf = vnet_dev_port_get_sec_if_by_index (di->port, di->sec_if_index);
+
   if (node_index == ~0)
     {
-      port->intf.redirect_to_node_next_index = 0;
-      if (port->intf.feature_arc == 0)
+      intf->redirect_to_node_next_index = 0;
+      if (intf->feature_arc == 0)
 	{
-	  port->intf.rx_next_index =
-	    vnet_dev_default_next_index_by_port_type[port->attr.type];
+	  intf->rx_next_index =
+	    vnet_dev_default_next_index_by_port_type[di->port->attr.type];
 	  runtime_update = 1;
 	}
-      port->intf.redirect_to_node = 0;
+      intf->redirect_to_node = 0;
     }
   else
     {
       u16 next_index = vlib_node_add_next (vlib_get_main (),
 					   port_rx_eth_node.index, node_index);
-      port->intf.redirect_to_node_next_index = next_index;
-      if (port->intf.feature_arc == 0)
+      intf->redirect_to_node_next_index = next_index;
+      if (intf->feature_arc == 0)
 	{
-	  port->intf.rx_next_index = next_index;
+	  intf->rx_next_index = next_index;
 	  runtime_update = 1;
 	}
-      port->intf.redirect_to_node = 1;
+      intf->redirect_to_node = 1;
     }
-  port->intf.rx_next_index =
+  intf->rx_next_index =
     node_index == ~0 ?
-	    vnet_dev_default_next_index_by_port_type[port->attr.type] :
-	    node_index;
+      vnet_dev_default_next_index_by_port_type[di->port->attr.type] :
+      node_index;
 
   if (runtime_update)
     {
-      foreach_vnet_dev_port_rx_queue (rxq, port)
+      foreach_vnet_dev_port_rx_queue (rxq, di->port)
 	vnet_dev_rx_queue_rt_request (
 	  vm, rxq, (vnet_dev_rx_queue_rt_req_t){ .update_next_index = 1 });
-      log_debug (port->dev, "runtime update requested due to chgange in "
-			    "reditect-to-next configuration");
+      log_debug (di->port->dev, "runtime update requested due to chgange in "
+				"reditect-to-next configuration");
     }
 }
diff --git a/src/vnet/dev/port.c b/src/vnet/dev/port.c
index 5bd4edf..fccedeb 100644
--- a/src/vnet/dev/port.c
+++ b/src/vnet/dev/port.c
@@ -94,6 +94,7 @@
   pool_free (port->rx_queues);
   pool_free (port->tx_queues);
   vnet_dev_arg_free (&port->args);
+  vnet_dev_arg_free (&port->sec_if_args);
   pool_put_index (dev->ports, port->index);
   clib_mem_free (port);
 }
@@ -109,11 +110,23 @@
       clib_bitmap_foreach (ti, q->assigned_threads)
 	{
 	  vlib_main_t *tvm = vlib_get_main_by_index (ti);
-	  vlib_node_runtime_t *nr =
-	    vlib_node_get_runtime (tvm, port->intf.tx_node_index);
-	  vnet_dev_tx_node_runtime_t *tnr = vnet_dev_get_tx_node_runtime (nr);
-	  tnr->hw_if_index = port->intf.hw_if_index;
+	  vlib_node_runtime_t *nr;
+	  vnet_dev_tx_node_runtime_t *tnr;
+	  vnet_dev_port_interfaces_t *ifs = port->interfaces;
+
+	  nr =
+	    vlib_node_get_runtime (tvm, ifs->primary_interface.tx_node_index);
+	  tnr = vnet_dev_get_tx_node_runtime (nr);
+	  tnr->hw_if_index = ifs->primary_interface.hw_if_index;
 	  tnr->tx_queue = q;
+
+	  pool_foreach_pointer (sif, port->interfaces->secondary_interfaces)
+	    {
+	      nr = vlib_node_get_runtime (tvm, sif->tx_node_index);
+	      tnr = vnet_dev_get_tx_node_runtime (nr);
+	      tnr->hw_if_index = sif->hw_if_index;
+	      tnr->tx_queue = q;
+	    }
 	}
     }
 }
@@ -271,6 +284,11 @@
     for (vnet_dev_arg_t *a = args->port.args; a->type != VNET_DEV_ARG_END; a++)
       vec_add1 (port->args, *a);
 
+  if (args->port.sec_if_args)
+    for (vnet_dev_arg_t *a = args->port.sec_if_args;
+	 a->type != VNET_DEV_ARG_END; a++)
+      vec_add1 (port->sec_if_args, *a);
+
   /* defaults out of port attributes */
   port->max_rx_frame_size = args->port.attr.max_supported_rx_frame_size;
   port->primary_hw_addr = args->port.attr.hw_addr;
@@ -466,25 +484,34 @@
 			    vnet_dev_port_state_changes_t changes)
 {
   vnet_main_t *vnm = vnet_get_main ();
+  vnet_dev_port_interfaces_t *ifs = port->interfaces;
 
   vnet_dev_port_validate (vm, port);
 
   if (changes.change.link_speed)
     {
       port->speed = changes.link_speed;
-      if (port->interface_created)
-	vnet_hw_interface_set_link_speed (vnm, port->intf.hw_if_index,
-					  changes.link_speed);
+      if (port->interfaces)
+	vnet_hw_interface_set_link_speed (
+	  vnm, ifs->primary_interface.hw_if_index, changes.link_speed);
       log_debug (port->dev, "port speed changed to %u", changes.link_speed);
     }
 
   if (changes.change.link_state)
     {
       port->link_up = changes.link_state;
-      if (port->interface_created)
-	vnet_hw_interface_set_flags (
-	  vnm, port->intf.hw_if_index,
-	  changes.link_state ? VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
+      if (ifs)
+	{
+	  vnet_hw_interface_set_flags (
+	    vnm, ifs->primary_interface.hw_if_index,
+	    changes.link_state ? VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
+	  pool_foreach_pointer (sif, ifs->secondary_interfaces)
+	    {
+	      vnet_hw_interface_set_flags (
+		vnm, sif->hw_if_index,
+		changes.link_state ? VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
+	    }
+	}
       log_debug (port->dev, "port link state changed to %s",
 		 changes.link_state ? "up" : "down");
     }
@@ -510,19 +537,51 @@
     vnet_dev_counters_free (vm, port->counter_main);
 }
 
+static void
+vnet_dev_port_init_if_rt_data (vlib_main_t *vm, vnet_dev_port_t *port,
+			       vnet_dev_rx_queue_if_rt_data_t *rtd,
+			       u32 sw_if_index)
+{
+  vnet_dev_t *dev = port->dev;
+  u8 buffer_pool_index =
+    vlib_buffer_pool_get_default_for_numa (vm, dev->numa_node);
+  vlib_buffer_pool_t *bp = vlib_get_buffer_pool (vm, buffer_pool_index);
+
+  rtd->buffer_template = bp->buffer_template;
+  vnet_buffer (&rtd->buffer_template)->sw_if_index[VLIB_RX] = sw_if_index;
+  vnet_buffer (&rtd->buffer_template)->sw_if_index[VLIB_TX] = ~0;
+  rtd->next_index = ~0;
+  rtd->sw_if_index = sw_if_index;
+}
+
 vnet_dev_rv_t
-vnet_dev_port_if_create (vlib_main_t *vm, vnet_dev_port_t *port)
+vnet_dev_port_if_create (vlib_main_t *vm, vnet_dev_port_t *port, void *ptr)
 {
   vnet_main_t *vnm = vnet_get_main ();
   u16 n_threads = vlib_get_n_threads ();
   vnet_dev_main_t *dm = &vnet_dev_main;
   vnet_dev_t *dev = port->dev;
+  vnet_dev_port_if_create_args_t *a = ptr;
+  vnet_dev_port_interfaces_t *ifs = port->interfaces;
   vnet_dev_instance_t *di;
   vnet_dev_rv_t rv;
   u16 ti = 0;
-  u8 is_consistent_qp = port->intf.consistent_qp;
 
-  if (port->intf.name[0] == 0)
+  if (ifs)
+    return VNET_DEV_ERR_ALREADY_EXISTS;
+
+  port->interfaces = ifs =
+    clib_mem_alloc (sizeof (vnet_dev_port_interfaces_t));
+
+  *(ifs) = (vnet_dev_port_interfaces_t){
+    .num_rx_queues = a->num_rx_queues,
+    .num_tx_queues = a->num_tx_queues,
+    .rxq_sz = a->rxq_sz,
+    .txq_sz = a->txq_sz,
+    .default_is_intr_mode = a->default_is_intr_mode,
+  };
+
+  if (a->name[0] == 0)
     {
       u8 *s;
       s = format (0, "%s%u/%u",
@@ -530,36 +589,36 @@
 		  port->dev->index, port->index);
       u32 n = vec_len (s);
 
-      if (n >= sizeof (port->intf.name))
+      if (n >= sizeof (a->name))
 	{
 	  vec_free (s);
 	  return VNET_DEV_ERR_BUG;
 	}
-      clib_memcpy (port->intf.name, s, n);
-      port->intf.name[n] = 0;
+      clib_memcpy (ifs->primary_interface.name, s, n);
+      ifs->primary_interface.name[n] = 0;
       vec_free (s);
     }
+  else
+    clib_memcpy (ifs->primary_interface.name, a->name,
+		 sizeof (ifs->primary_interface.name));
 
   log_debug (
     dev, "allocating %u rx queues with size %u and %u tx queues with size %u",
-    port->intf.num_rx_queues, port->intf.rxq_sz, port->intf.num_tx_queues,
-    port->intf.txq_sz);
+    a->num_rx_queues, a->rxq_sz, a->num_tx_queues, a->txq_sz);
 
-  for (int i = 0; i < port->intf.num_rx_queues; i++)
-    if ((rv = vnet_dev_rx_queue_alloc (vm, port, port->intf.rxq_sz)) !=
-	VNET_DEV_OK)
+  for (int i = 0; i < ifs->num_rx_queues; i++)
+    if ((rv = vnet_dev_rx_queue_alloc (vm, port, ifs->rxq_sz)) != VNET_DEV_OK)
       goto error;
 
-  for (u32 i = 0; i < port->intf.num_tx_queues; i++)
-    if ((rv = vnet_dev_tx_queue_alloc (vm, port, port->intf.txq_sz)) !=
-	VNET_DEV_OK)
+  for (u32 i = 0; i < ifs->num_tx_queues; i++)
+    if ((rv = vnet_dev_tx_queue_alloc (vm, port, ifs->txq_sz)) != VNET_DEV_OK)
       goto error;
 
   foreach_vnet_dev_port_tx_queue (q, port)
     {
       /* if consistent_qp is enabled, we start by assigning queues to workers
        * and we end with main */
-      u16 real_ti = (ti + is_consistent_qp) % n_threads;
+      u16 real_ti = (ti + a->consistent_qp) % n_threads;
       q->assigned_threads = clib_bitmap_set (q->assigned_threads, real_ti, 1);
       log_debug (dev, "port %u tx queue %u assigned to thread %u",
 		 port->port_id, q->queue_id, real_ti);
@@ -568,8 +627,9 @@
     }
 
   pool_get (dm->dev_instances, di);
-  port->intf.dev_instance = di - dm->dev_instances;
+  ifs->primary_interface.dev_instance = di - dm->dev_instances;
   di->port = port;
+  di->is_primary_if = 1;
 
   if (port->attr.type == VNET_DEV_PORT_TYPE_ETHERNET)
     {
@@ -578,7 +638,7 @@
       vnet_sw_interface_t *sw;
       vnet_hw_interface_t *hw;
       vnet_hw_if_caps_t caps = 0;
-      u32 rx_node_index;
+      u32 rx_node_index, hw_if_index, sw_if_index;
 
       driver = pool_elt_at_index (dm->drivers, dev->driver_index);
 
@@ -590,27 +650,28 @@
       dev_class->tx_function_n_errors = port->tx_node.n_error_counters;
 
       /* create new interface including tx and output nodes */
-      port->intf.hw_if_index = vnet_eth_register_interface (
+      hw_if_index = vnet_eth_register_interface (
 	vnm, &(vnet_eth_interface_registration_t){
 	       .address = port->primary_hw_addr.eth_mac,
 	       .max_frame_size = port->max_rx_frame_size,
 	       .dev_class_index = driver->dev_class_index,
-	       .dev_instance = port->intf.dev_instance,
+	       .dev_instance = ifs->primary_interface.dev_instance,
 	       .cb.set_max_frame_size = vnet_dev_port_set_max_frame_size,
 	       .cb.flag_change = vnet_dev_port_eth_flag_change,
 	     });
+      ifs->primary_interface.hw_if_index = hw_if_index;
 
-      sw = vnet_get_hw_sw_interface (vnm, port->intf.hw_if_index);
-      hw = vnet_get_hw_interface (vnm, port->intf.hw_if_index);
-      port->intf.sw_if_index = sw->sw_if_index;
+      sw = vnet_get_hw_sw_interface (vnm, hw_if_index);
+      hw = vnet_get_hw_interface (vnm, hw_if_index);
+      sw_if_index = ifs->primary_interface.sw_if_index = sw->sw_if_index;
       vnet_hw_interface_set_flags (
-	vnm, port->intf.hw_if_index,
+	vnm, ifs->primary_interface.hw_if_index,
 	port->link_up ? VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
       if (port->speed)
-	vnet_hw_interface_set_link_speed (vnm, port->intf.hw_if_index,
-					  port->speed);
+	vnet_hw_interface_set_link_speed (
+	  vnm, ifs->primary_interface.hw_if_index, port->speed);
 
-      port->intf.tx_node_index = hw->tx_node_index;
+      ifs->primary_interface.tx_node_index = hw->tx_node_index;
 
       caps |= port->attr.caps.interrupt_mode ? VNET_HW_IF_CAP_INT_MODE : 0;
       caps |= port->attr.caps.mac_filter ? VNET_HW_IF_CAP_MAC_FILTER : 0;
@@ -618,14 +679,15 @@
       caps |= port->attr.tx_offloads.ip4_cksum ? VNET_HW_IF_CAP_TX_CKSUM : 0;
 
       if (caps)
-	vnet_hw_if_set_caps (vnm, port->intf.hw_if_index, caps);
+	vnet_hw_if_set_caps (vnm, hw_if_index, caps);
 
       /* create / reuse rx node */
       if (vec_len (dm->free_rx_node_indices))
 	{
 	  vlib_node_t *n;
 	  rx_node_index = vec_pop (dm->free_rx_node_indices);
-	  vlib_node_rename (vm, rx_node_index, "%s-rx", port->intf.name);
+	  vlib_node_rename (vm, rx_node_index, "%s-rx",
+			    port->interfaces->primary_interface.name);
 	  n = vlib_get_node (vm, rx_node_index);
 	  n->function = vlib_node_get_preferred_node_fn_variant (
 	    vm, port->rx_node.registrations);
@@ -649,30 +711,28 @@
 	    .error_counters = port->rx_node.error_counters,
 	    .n_errors = port->rx_node.n_error_counters,
 	  };
-	  rx_node_index =
-	    vlib_register_node (vm, &rx_node_reg, "%s-rx", port->intf.name);
+	  rx_node_index = vlib_register_node (vm, &rx_node_reg, "%s-rx",
+					      ifs->primary_interface.name);
 	}
       port->rx_node_assigned = 1;
-      port->intf.rx_node_index = rx_node_index;
-      port->intf.rx_next_index =
+      ifs->rx_node_index = rx_node_index;
+      ifs->primary_interface.rx_next_index =
 	vnet_dev_default_next_index_by_port_type[port->attr.type];
 
       vlib_worker_thread_node_runtime_update ();
       log_debug (dev,
 		 "ethernet interface created, hw_if_index %u sw_if_index %u "
 		 "rx_node_index %u tx_node_index %u",
-		 port->intf.hw_if_index, port->intf.sw_if_index,
-		 port->intf.rx_node_index, port->intf.tx_node_index);
+		 hw_if_index, sw_if_index, rx_node_index,
+		 ifs->primary_interface.tx_node_index);
     }
 
-  port->interface_created = 1;
   foreach_vnet_dev_port_rx_queue (q, port)
     {
-      vnet_buffer (&q->buffer_template)->sw_if_index[VLIB_RX] =
-	port->intf.sw_if_index;
+      vnet_dev_port_init_if_rt_data (vm, port, &q->if_rt_data,
+				     ifs->primary_interface.sw_if_index);
       /* poison to catch node not calling runtime update function */
-      q->next_index = ~0;
-      q->interrupt_mode = port->intf.default_is_intr_mode;
+      q->interrupt_mode = ifs->default_is_intr_mode;
       vnet_dev_rx_queue_rt_request (
 	vm, q, (vnet_dev_rx_queue_rt_req_t){ .update_next_index = 1 });
     }
@@ -685,6 +745,8 @@
 error:
   if (rv != VNET_DEV_OK)
     vnet_dev_port_if_remove (vm, port);
+  else
+    a->sw_if_index = ifs->primary_interface.sw_if_index;
   return rv;
 }
 
@@ -693,6 +755,7 @@
 {
   vnet_dev_main_t *dm = &vnet_dev_main;
   vnet_main_t *vnm = vnet_get_main ();
+  vnet_dev_port_interfaces_t *ifs = port->interfaces;
 
   vnet_dev_port_validate (vm, port);
 
@@ -701,23 +764,22 @@
 
   if (port->rx_node_assigned)
     {
-      vlib_node_rename (vm, port->intf.rx_node_index, "deleted-%u",
-			port->intf.rx_node_index);
-      vec_add1 (dm->free_rx_node_indices, port->intf.rx_node_index);
+      vlib_node_rename (vm, ifs->rx_node_index, "deleted-%u",
+			ifs->rx_node_index);
+      vec_add1 (dm->free_rx_node_indices, ifs->rx_node_index);
       port->rx_node_assigned = 0;
     }
 
-  if (port->interface_created)
+  if (ifs)
     {
       vlib_worker_thread_barrier_sync (vm);
-      vnet_delete_hw_interface (vnm, port->intf.hw_if_index);
+      vnet_delete_hw_interface (vnm, ifs->primary_interface.hw_if_index);
       vlib_worker_thread_barrier_release (vm);
-      pool_put_index (dm->dev_instances, port->intf.dev_instance);
-      port->interface_created = 0;
+      pool_put_index (dm->dev_instances, ifs->primary_interface.dev_instance);
+      clib_mem_free (port->interfaces);
+      port->interfaces = 0;
     }
 
-  port->intf = (typeof (port->intf)){};
-
   if (port->port_ops.deinit)
     port->port_ops.deinit (vm, port);
 
@@ -734,6 +796,171 @@
 
   return VNET_DEV_OK;
 }
+
+vnet_dev_rv_t
+vnet_dev_port_del_sec_if_internal (vlib_main_t *vm, vnet_dev_port_t *port,
+				   vnet_dev_port_interface_t *sif)
+{
+  vnet_dev_rv_t rv = VNET_DEV_OK;
+
+  if (sif && port->port_ops.add_sec_if)
+    rv = port->port_ops.add_sec_if (vm, port, sif);
+
+  if (rv != VNET_DEV_OK)
+    return rv;
+
+  foreach_vnet_dev_port_rx_queue (q, port)
+    {
+      vec_foreach_pointer (p, q->sec_if_rt_data)
+	if (p)
+	  clib_mem_free (p);
+      vec_free (q->sec_if_rt_data);
+    }
+
+  if (sif->interface_created)
+    ethernet_delete_interface (vnet_get_main (), sif->hw_if_index);
+
+  pool_put_index (port->interfaces->secondary_interfaces, sif->index);
+  vnet_dev_arg_free (&sif->args);
+  clib_mem_free (sif);
+  return rv;
+}
+
+vnet_dev_rv_t
+vnet_dev_port_add_sec_if (vlib_main_t *vm, vnet_dev_port_t *port, void *ptr)
+{
+  vnet_dev_main_t *dm = &vnet_dev_main;
+  vnet_dev_port_sec_if_create_args_t *a = ptr;
+  vnet_main_t *vnm = vnet_get_main ();
+  vnet_dev_t *dev = port->dev;
+  vnet_dev_port_interface_t *sif = 0;
+  vnet_dev_port_interface_t **sip;
+  vnet_dev_rv_t rv = VNET_DEV_OK;
+
+  sif = clib_mem_alloc (sizeof (vnet_dev_port_interface_t));
+  pool_get (port->interfaces->secondary_interfaces, sip);
+  *sip = sif;
+
+  *sif = (vnet_dev_port_interface_t){
+    .index = sip - port->interfaces->secondary_interfaces,
+    .args = vec_dup (port->sec_if_args),
+  };
+
+  clib_memcpy (sif->name, a->name, sizeof (sif->name));
+
+  if (sif->args)
+    {
+      rv = vnet_dev_arg_parse (vm, dev, sif->args, a->args);
+      if (rv != VNET_DEV_OK)
+	return rv;
+    }
+
+  if (port->attr.type == VNET_DEV_PORT_TYPE_ETHERNET)
+    {
+      vnet_device_class_t *dev_class;
+      vnet_dev_driver_t *driver;
+      vnet_sw_interface_t *sw;
+      vnet_hw_interface_t *hw;
+      vnet_dev_instance_t *di;
+      vnet_hw_if_caps_t caps = 0;
+
+      pool_get (dm->dev_instances, di);
+      sif->dev_instance = di - dm->dev_instances;
+      di->port = port;
+      di->sec_if_index = sip - port->interfaces->secondary_interfaces;
+
+      driver = pool_elt_at_index (dm->drivers, dev->driver_index);
+
+      /* hack to provide per-port tx node function */
+      dev_class = vnet_get_device_class (vnm, driver->dev_class_index);
+      dev_class->tx_fn_registrations = port->tx_node.registrations;
+      dev_class->format_tx_trace = port->tx_node.format_trace;
+      dev_class->tx_function_error_counters = port->tx_node.error_counters;
+      dev_class->tx_function_n_errors = port->tx_node.n_error_counters;
+
+      /* create new interface including tx and output nodes */
+      sif->hw_if_index = vnet_eth_register_interface (
+	vnm, &(vnet_eth_interface_registration_t){
+	       .address = port->primary_hw_addr.eth_mac,
+	       .max_frame_size = port->max_rx_frame_size,
+	       .dev_class_index = driver->dev_class_index,
+	       .dev_instance = sif->dev_instance,
+	       .cb.set_max_frame_size = vnet_dev_port_set_max_frame_size,
+	       .cb.flag_change = vnet_dev_port_eth_flag_change,
+	     });
+
+      sw = vnet_get_hw_sw_interface (vnm, sif->hw_if_index);
+      hw = vnet_get_hw_interface (vnm, sif->hw_if_index);
+      sif->sw_if_index = sw->sw_if_index;
+      sif->next_index =
+	vnet_dev_default_next_index_by_port_type[port->attr.type];
+      sif->interface_created = 1;
+      vnet_dev_port_update_tx_node_runtime (vm, port);
+      vnet_hw_interface_set_flags (
+	vnm, sif->hw_if_index,
+	port->link_up ? VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
+      if (port->speed)
+	vnet_hw_interface_set_link_speed (vnm, sif->hw_if_index, port->speed);
+
+      sif->tx_node_index = hw->tx_node_index;
+
+      caps |= port->attr.caps.interrupt_mode ? VNET_HW_IF_CAP_INT_MODE : 0;
+      caps |= port->attr.caps.mac_filter ? VNET_HW_IF_CAP_MAC_FILTER : 0;
+      caps |= port->attr.tx_offloads.tcp_gso ? VNET_HW_IF_CAP_TCP_GSO : 0;
+      caps |= port->attr.tx_offloads.ip4_cksum ? VNET_HW_IF_CAP_TX_CKSUM : 0;
+
+      if (caps)
+	vnet_hw_if_set_caps (vnm, sif->hw_if_index, caps);
+    }
+  else
+    return VNET_DEV_ERR_NOT_SUPPORTED;
+
+  foreach_vnet_dev_port_rx_queue (q, port)
+    {
+      vnet_dev_rx_queue_if_rt_data_t *rtd;
+      vec_validate (q->sec_if_rt_data, sif->index);
+
+      rtd = clib_mem_alloc_aligned (sizeof (vnet_dev_rx_queue_if_rt_data_t),
+				    CLIB_CACHE_LINE_BYTES);
+
+      q->sec_if_rt_data[sif->index] = rtd;
+
+      vnet_dev_port_init_if_rt_data (vm, port, rtd, sif->sw_if_index);
+      vnet_dev_rx_queue_rt_request (
+	vm, q, (vnet_dev_rx_queue_rt_req_t){ .update_next_index = 1 });
+    }
+
+  if (sif && port->port_ops.add_sec_if)
+    rv = port->port_ops.add_sec_if (vm, port, sif);
+
+  if (rv != VNET_DEV_OK)
+    vnet_dev_port_del_sec_if_internal (vm, port, sif);
+
+  return rv;
+}
+
+vnet_dev_rv_t
+vnet_dev_port_del_sec_if (vlib_main_t *vm, vnet_dev_port_t *port, void *ptr)
+{
+  vnet_dev_port_del_sec_if_args_t *a = ptr;
+  vnet_sw_interface_t *si;
+  vnet_hw_interface_t *hi;
+  vnet_dev_instance_t *di;
+  vnet_main_t *vnm = vnet_get_main ();
+
+  log_debug (port->dev, "%u", a->sw_if_index);
+
+  si = vnet_get_sw_interface_or_null (vnm, a->sw_if_index);
+  if (!si)
+    return VNET_DEV_ERR_UNKNOWN_INTERFACE;
+
+  hi = vnet_get_hw_interface (vnm, si->hw_if_index);
+  di = vnet_dev_get_dev_instance (hi->dev_instance);
+
+  return vnet_dev_port_del_sec_if_internal (
+    vm, port, vnet_dev_port_get_sec_if_by_index (port, di->sec_if_index));
+}
+
 void
 vnet_dev_port_clear_counters (vlib_main_t *vm, vnet_dev_port_t *port)
 {
diff --git a/src/vnet/dev/queue.c b/src/vnet/dev/queue.c
index 7efea5f..57ed3dc 100644
--- a/src/vnet/dev/queue.c
+++ b/src/vnet/dev/queue.c
@@ -36,7 +36,6 @@
   vnet_dev_t *dev = port->dev;
   vnet_dev_rv_t rv = VNET_DEV_OK;
   u16 n_threads = vlib_get_n_threads ();
-  u8 buffer_pool_index;
 
   vnet_dev_port_validate (vm, port);
 
@@ -65,15 +64,6 @@
 	dm->next_rx_queue_thread = 1;
     }
 
-  buffer_pool_index =
-    vlib_buffer_pool_get_default_for_numa (vm, dev->numa_node);
-  vlib_buffer_pool_t *bp = vlib_get_buffer_pool (vm, buffer_pool_index);
-
-  rxq->buffer_template = bp->buffer_template;
-  vnet_buffer (&rxq->buffer_template)->sw_if_index[VLIB_TX] = ~0;
-
-  rxq->next_index = vnet_dev_default_next_index_by_port_type[port->attr.type];
-
   if (port->rx_queue_ops.alloc)
     rv = port->rx_queue_ops.alloc (vm, rxq);