tap: add num_tx_queues API
[vpp.git] / src / plugins / linux-cp / lcp_interface.c
index 0dcac48..d044635 100644 (file)
@@ -14,7 +14,6 @@
  */
 
 #define _GNU_SOURCE
-#include <sched.h>
 #include <fcntl.h>
 #include <ctype.h>
 #include <sys/socket.h>
 
 #include <linux-cp/lcp_interface.h>
 #include <netlink/route/link/vlan.h>
+#include <linux/if_ether.h>
 
 #include <vnet/plugin/plugin.h>
 #include <vnet/plugin/plugin.h>
 
+#include <vppinfra/linux/netns.h>
+
 #include <vnet/ip/ip_punt_drop.h>
 #include <vnet/fib/fib_table.h>
 #include <vnet/adj/adj_mcast.h>
@@ -42,7 +44,7 @@ static vlib_log_class_t lcp_itf_pair_logger;
 /**
  * Pool of LIP objects
  */
-lcp_itf_pair_t *lcp_itf_pair_pool;
+lcp_itf_pair_t *lcp_itf_pair_pool = NULL;
 
 u32
 lcp_itf_num_pairs (void)
@@ -60,12 +62,25 @@ static uword *lip_db_by_vif;
 index_t *lip_db_by_phy;
 u32 *lip_db_by_host;
 
+/**
+ * vector of virtual function table
+ */
+static lcp_itf_pair_vft_t *lcp_itf_vfts = NULL;
+
+void
+lcp_itf_pair_register_vft (lcp_itf_pair_vft_t *lcp_itf_vft)
+{
+  vec_add1 (lcp_itf_vfts, *lcp_itf_vft);
+}
+
 #define LCP_ITF_PAIR_DBG(...)                                                 \
   vlib_log_notice (lcp_itf_pair_logger, __VA_ARGS__);
 
 #define LCP_ITF_PAIR_INFO(...)                                                \
   vlib_log_notice (lcp_itf_pair_logger, __VA_ARGS__);
 
+#define LCP_ITF_PAIR_ERR(...) vlib_log_err (lcp_itf_pair_logger, __VA_ARGS__);
+
 u8 *
 format_lcp_itf_pair (u8 *s, va_list *args)
 {
@@ -140,6 +155,9 @@ lcp_itf_pair_show (u32 phy_sw_if_index)
 lcp_itf_pair_t *
 lcp_itf_pair_get (u32 index)
 {
+  if (!lcp_itf_pair_pool)
+    return NULL;
+
   return pool_elt_at_index (lcp_itf_pair_pool, index);
 }
 
@@ -163,6 +181,13 @@ lcp_itf_pair_add_sub (u32 vif, u8 *host_if_name, u32 sub_sw_if_index,
   lcp_itf_pair_t *lip;
 
   lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (phy_sw_if_index));
+  if (!lip)
+    {
+      LCP_ITF_PAIR_DBG ("lcp_itf_pair_add_sub: can't find LCP of parent %U",
+                       format_vnet_sw_if_index_name, vnet_get_main (),
+                       phy_sw_if_index);
+      return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+    }
 
   return lcp_itf_pair_add (lip->lip_host_sw_if_index, sub_sw_if_index,
                           host_if_name, vif, lip->lip_host_type, ns);
@@ -216,12 +241,6 @@ lcp_itf_set_adjs (lcp_itf_pair_t *lip)
   lip->lip_rewrite_len = adj->rewrite_header.data_bytes;
 }
 
-int __clib_weak
-lcp_nl_drain_messages (void)
-{
-  return 0;
-}
-
 int
 lcp_itf_pair_add (u32 host_sw_if_index, u32 phy_sw_if_index, u8 *host_name,
                  u32 host_index, lip_host_type_t host_type, u8 *ns)
@@ -231,7 +250,7 @@ lcp_itf_pair_add (u32 host_sw_if_index, u32 phy_sw_if_index, u8 *host_name,
 
   lipi = lcp_itf_pair_find_by_phy (phy_sw_if_index);
 
-  LCP_ITF_PAIR_INFO ("add: host:%U phy:%U, host_if:%v vif:%d ns:%v",
+  LCP_ITF_PAIR_INFO ("add: host:%U phy:%U, host_if:%v vif:%d ns:%s",
                     format_vnet_sw_if_index_name, vnet_get_main (),
                     host_sw_if_index, format_vnet_sw_if_index_name,
                     vnet_get_main (), phy_sw_if_index, host_name, host_index,
@@ -240,14 +259,6 @@ lcp_itf_pair_add (u32 host_sw_if_index, u32 phy_sw_if_index, u8 *host_name,
   if (lipi != INDEX_INVALID)
     return VNET_API_ERROR_VALUE_EXIST;
 
-  /*
-   * Drain netlink messages before adding the new pair.
-   * This avoids unnecessarily applying messages that were generated by
-   * the creation of the tap/tun interface. By processing them before we
-   * store the pair data, we will ensure that they are ignored.
-   */
-  lcp_nl_drain_messages ();
-
   /*
    * Create a new pair.
    */
@@ -267,7 +278,6 @@ lcp_itf_pair_add (u32 host_sw_if_index, u32 phy_sw_if_index, u8 *host_name,
   lip->lip_host_type = host_type;
   lip->lip_vif_index = host_index;
   lip->lip_namespace = vec_dup (ns);
-  lip->lip_create_ts = vlib_time_now (vlib_get_main ());
 
   if (lip->lip_host_sw_if_index == ~0)
     return 0;
@@ -323,11 +333,23 @@ lcp_itf_pair_add (u32 host_sw_if_index, u32 phy_sw_if_index, u8 *host_name,
                                   0);
     }
 
+  /* invoke registered callbacks for pair addition */
+  lcp_itf_pair_vft_t *vft;
+
+  vec_foreach (vft, lcp_itf_vfts)
+    {
+      if (vft->pair_add_fn)
+       vft->pair_add_fn (lip);
+    }
+
+  /* set timestamp when pair entered service */
+  lip->lip_create_ts = vlib_time_now (vlib_get_main ());
+
   return 0;
 }
 
 static clib_error_t *
-lcp_netlink_add_link_vlan (int parent, u32 vlan, const char *name)
+lcp_netlink_add_link_vlan (int parent, u32 vlan, u16 proto, const char *name)
 {
   struct rtnl_link *link;
   struct nl_sock *sk;
@@ -335,17 +357,25 @@ lcp_netlink_add_link_vlan (int parent, u32 vlan, const char *name)
 
   sk = nl_socket_alloc ();
   if ((err = nl_connect (sk, NETLINK_ROUTE)) < 0)
-    return clib_error_return (NULL, "Unable to connect socket: %d", err);
+    {
+      LCP_ITF_PAIR_ERR ("netlink_add_link_vlan: connect error: %s",
+                       nl_geterror (err));
+      return clib_error_return (NULL, "Unable to connect socket: %d", err);
+    }
 
   link = rtnl_link_vlan_alloc ();
 
   rtnl_link_set_link (link, parent);
   rtnl_link_set_name (link, name);
-
   rtnl_link_vlan_set_id (link, vlan);
+  rtnl_link_vlan_set_protocol (link, htons (proto));
 
   if ((err = rtnl_link_add (sk, link, NLM_F_CREATE)) < 0)
-    return clib_error_return (NULL, "Unable to add link %s: %d", name, err);
+    {
+      LCP_ITF_PAIR_ERR ("netlink_add_link_vlan: link add error: %s",
+                       nl_geterror (err));
+      return clib_error_return (NULL, "Unable to add link %s: %d", name, err);
+    }
 
   rtnl_link_put (link);
   nl_close (sk);
@@ -382,6 +412,7 @@ lcp_itf_pair_del (u32 phy_sw_if_index)
   ip_address_family_t af;
   lcp_itf_pair_t *lip;
   u32 lipi;
+  lcp_itf_pair_vft_t *vft;
 
   lipi = lcp_itf_pair_find_by_phy (phy_sw_if_index);
 
@@ -390,11 +421,18 @@ lcp_itf_pair_del (u32 phy_sw_if_index)
 
   lip = lcp_itf_pair_get (lipi);
 
-  LCP_ITF_PAIR_INFO ("pair delete: {%U, %U, %s}", format_vnet_sw_if_index_name,
+  LCP_ITF_PAIR_INFO ("pair delete: {%U, %U, %v}", format_vnet_sw_if_index_name,
                     vnet_get_main (), lip->lip_phy_sw_if_index,
                     format_vnet_sw_if_index_name, vnet_get_main (),
                     lip->lip_host_sw_if_index, lip->lip_host_name);
 
+  /* invoke registered callbacks for pair deletion */
+  vec_foreach (vft, lcp_itf_vfts)
+    {
+      if (vft->pair_del_fn)
+       vft->pair_del_fn (lip);
+    }
+
   FOR_EACH_IP_ADDRESS_FAMILY (af)
   ip_feature_enable_disable (af, N_SAFI, IP_FEATURE_INPUT,
                             lcp_itf_l3_feat_names[lip->lip_host_type][af],
@@ -422,7 +460,8 @@ lcp_itf_pair_del (u32 phy_sw_if_index)
     }
 
   lip_db_by_phy[phy_sw_if_index] = INDEX_INVALID;
-  lip_db_by_phy[lip->lip_host_sw_if_index] = INDEX_INVALID;
+  lip_db_by_host[lip->lip_host_sw_if_index] = INDEX_INVALID;
+  hash_unset (lip_db_by_vif, lip->lip_vif_index);
 
   vec_free (lip->lip_host_name);
   vec_free (lip->lip_namespace);
@@ -471,6 +510,23 @@ lcp_itf_pair_delete (u32 phy_sw_if_index)
   return 0;
 }
 
+/**
+ * lcp_itf_interface_add_del
+ *
+ * Registered to receive interface Add and delete notifications
+ */
+static clib_error_t *
+lcp_itf_interface_add_del (vnet_main_t *vnm, u32 sw_if_index, u32 is_add)
+{
+  if (!is_add)
+    /* remove any interface pair we have for this interface */
+    lcp_itf_pair_delete (sw_if_index);
+
+  return (NULL);
+}
+
+VNET_SW_INTERFACE_ADD_DEL_FUNCTION (lcp_itf_interface_add_del);
+
 void
 lcp_itf_pair_walk (lcp_itf_pair_walk_cb_t cb, void *ctx)
 {
@@ -513,7 +569,7 @@ lcp_itf_pair_config (vlib_main_t *vm, unformat_input_t *input)
          if (vec_len (ns) > LCP_NS_LEN)
            {
              return clib_error_return (0,
-                                       "linux-cp IF namespace must"
+                                       "linux-cp namespace must"
                                        " be less than %d characters",
                                        LCP_NS_LEN);
            }
@@ -597,71 +653,130 @@ lcp_validate_if_name (u8 *name)
   return 1;
 }
 
-static int
-lcp_itf_get_ns_fd (char *ns_name)
-{
-  char ns_path[256] = "/proc/self/ns/net";
-
-  if (ns_name)
-    snprintf (ns_path, sizeof (ns_path) - 1, "/var/run/netns/%s", ns_name);
-
-  return open (ns_path, O_RDONLY);
-}
-
 static void
-lcp_itf_set_vif_link_state (u32 vif_index, u8 up, u8 *ns)
+lcp_itf_set_link_state (const lcp_itf_pair_t *lip, u8 state)
 {
   int curr_ns_fd, vif_ns_fd;
 
+  if (!lip)
+    return;
+
   curr_ns_fd = vif_ns_fd = -1;
 
-  if (ns)
+  if (lip->lip_namespace)
     {
-      u8 *ns_path = 0;
-
-      curr_ns_fd = open ("/proc/self/ns/net", O_RDONLY);
-      ns_path = format (0, "/var/run/netns/%s%c", (char *) ns, 0);
-      vif_ns_fd = open ((char *) ns_path, O_RDONLY);
+      curr_ns_fd = clib_netns_open (NULL /* self */);
+      vif_ns_fd = clib_netns_open (lip->lip_namespace);
       if (vif_ns_fd != -1)
-       setns (vif_ns_fd, CLONE_NEWNET);
+       clib_setns (vif_ns_fd);
     }
 
-  vnet_netlink_set_link_state (vif_index, up);
+  vnet_netlink_set_link_state (lip->lip_vif_index, state);
 
   if (vif_ns_fd != -1)
     close (vif_ns_fd);
 
   if (curr_ns_fd != -1)
     {
-      setns (curr_ns_fd, CLONE_NEWNET);
+      clib_setns (curr_ns_fd);
       close (curr_ns_fd);
     }
+
+  return;
+}
+
+typedef struct
+{
+  u32 vlan;
+  bool dot1ad;
+
+  u32 matched_sw_if_index;
+} lcp_itf_match_t;
+
+static walk_rc_t
+lcp_itf_pair_find_walk (vnet_main_t *vnm, u32 sw_if_index, void *arg)
+{
+  lcp_itf_match_t *match = arg;
+  const vnet_sw_interface_t *sw;
+
+  sw = vnet_get_sw_interface (vnm, sw_if_index);
+  if (sw && (sw->sub.eth.inner_vlan_id == 0) &&
+      (sw->sub.eth.outer_vlan_id == match->vlan) &&
+      (sw->sub.eth.flags.dot1ad == match->dot1ad))
+    {
+      LCP_ITF_PAIR_DBG ("find_walk: found match outer %d dot1ad %d "
+                       "inner-dot1q %d: interface %U",
+                       sw->sub.eth.outer_vlan_id, sw->sub.eth.flags.dot1ad,
+                       sw->sub.eth.inner_vlan_id,
+                       format_vnet_sw_if_index_name, vnet_get_main (),
+                       sw->sw_if_index);
+      match->matched_sw_if_index = sw->sw_if_index;
+      return WALK_STOP;
+    }
+
+  return WALK_CONTINUE;
+}
+
+/* Return the index of the sub-int on the phy that has the given vlan and
+ * proto,
+ */
+static index_t
+lcp_itf_pair_find_by_outer_vlan (u32 sup_if_index, u16 vlan, bool dot1ad)
+{
+  lcp_itf_match_t match;
+  const vnet_hw_interface_t *hw;
+
+  match.vlan = vlan;
+  match.dot1ad = dot1ad;
+  match.matched_sw_if_index = INDEX_INVALID;
+  hw = vnet_get_sup_hw_interface (vnet_get_main (), sup_if_index);
+
+  vnet_hw_interface_walk_sw (vnet_get_main (), hw->hw_if_index,
+                            lcp_itf_pair_find_walk, &match);
+
+  if (match.matched_sw_if_index >= vec_len (lip_db_by_phy))
+    return INDEX_INVALID;
+
+  return lip_db_by_phy[match.matched_sw_if_index];
 }
 
 int
 lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
-                    lip_host_type_t host_if_type, u8 *ns)
+                    lip_host_type_t host_if_type, u8 *ns,
+                    u32 *host_sw_if_indexp)
 {
   vlib_main_t *vm;
   vnet_main_t *vnm;
   u32 vif_index = 0, host_sw_if_index;
   const vnet_sw_interface_t *sw;
   const vnet_hw_interface_t *hw;
+  const lcp_itf_pair_t *lip;
 
   if (!vnet_sw_if_index_is_api_valid (phy_sw_if_index))
-    return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+    {
+      LCP_ITF_PAIR_ERR ("pair_create: invalid phy index %u", phy_sw_if_index);
+      return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+    }
 
   if (!lcp_validate_if_name (host_if_name))
-    return VNET_API_ERROR_INVALID_ARGUMENT;
+    {
+      LCP_ITF_PAIR_ERR ("pair_create: invalid host-if-name '%s'",
+                       host_if_name);
+      return VNET_API_ERROR_INVALID_ARGUMENT;
+    }
 
   vnm = vnet_get_main ();
   sw = vnet_get_sw_interface (vnm, phy_sw_if_index);
   hw = vnet_get_sup_hw_interface (vnm, phy_sw_if_index);
+  if (!sw || !hw)
+    {
+      LCP_ITF_PAIR_ERR ("pair_create: invalid interface");
+      return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+    }
 
   /*
    * Use interface-specific netns if supplied.
-   * Otherwise, use default netns if defined.
-   * Otherwise ignore a netns and use the OS default.
+   * Otherwise, use netns if defined, otherwise use the OS default.
    */
   if (ns == 0 || ns[0] == 0)
     ns = lcp_get_default_ns ();
@@ -669,16 +784,51 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
   /* sub interfaces do not need a tap created */
   if (vnet_sw_interface_is_sub (vnm, phy_sw_if_index))
     {
-      const lcp_itf_pair_t *lip;
+      index_t parent_if_index;
       int orig_ns_fd, ns_fd;
       clib_error_t *err;
-      u16 vlan;
+      u16 outer_vlan, inner_vlan;
+      u16 outer_proto, inner_proto;
+      u16 vlan, proto;
+      u32 parent_vif_index;
+
+      // TODO(pim) replace with vnet_sw_interface_supports_addressing()
+      if (sw->type == VNET_SW_INTERFACE_TYPE_SUB &&
+         sw->sub.eth.flags.exact_match == 0)
+       {
+         LCP_ITF_PAIR_ERR ("pair_create: can't create LCP for a "
+                           "sub-interface without exact-match set");
+         return VNET_API_ERROR_INVALID_ARGUMENT;
+       }
 
-      /*
-       * Find the parent tap by finding the pair from the parent phy
-       */
-      lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw->sup_sw_if_index));
-      vlan = sw->sub.eth.outer_vlan_id;
+      outer_vlan = sw->sub.eth.outer_vlan_id;
+      inner_vlan = sw->sub.eth.inner_vlan_id;
+      outer_proto = inner_proto = ETH_P_8021Q;
+      if (1 == sw->sub.eth.flags.dot1ad)
+       outer_proto = ETH_P_8021AD;
+
+      LCP_ITF_PAIR_INFO ("pair_create: subif: dot1%s outer %d inner %d on %U",
+                        sw->sub.eth.flags.dot1ad ? "ad" : "q", outer_vlan,
+                        inner_vlan, format_vnet_sw_if_index_name, vnm,
+                        hw->sw_if_index);
+
+      parent_if_index = lcp_itf_pair_find_by_phy (sw->sup_sw_if_index);
+      if (INDEX_INVALID == parent_if_index)
+       {
+         LCP_ITF_PAIR_ERR ("pair_create: can't find LCP for %U",
+                           format_vnet_sw_if_index_name, vnet_get_main (),
+                           sw->sup_sw_if_index);
+         return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+       }
+      lip = lcp_itf_pair_get (parent_if_index);
+      if (!lip)
+       {
+         LCP_ITF_PAIR_ERR ("pair_create: can't create LCP for a "
+                           "sub-interface without an LCP on the parent");
+         return VNET_API_ERROR_INVALID_ARGUMENT;
+       }
+      LCP_ITF_PAIR_DBG ("pair_create: parent %U", format_lcp_itf_pair, lip);
+      parent_vif_index = lip->lip_vif_index;
 
       /*
        * see if the requested host interface has already been created
@@ -688,12 +838,12 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
 
       if (ns && ns[0] != 0)
        {
-         orig_ns_fd = lcp_itf_get_ns_fd (NULL);
-         ns_fd = lcp_itf_get_ns_fd ((char *) ns);
+         orig_ns_fd = clib_netns_open (NULL /* self */);
+         ns_fd = clib_netns_open (ns);
          if (orig_ns_fd == -1 || ns_fd == -1)
            goto socket_close;
 
-         setns (ns_fd, CLONE_NEWNET);
+         clib_setns (ns_fd);
        }
 
       vif_index = if_nametoindex ((const char *) host_if_name);
@@ -703,11 +853,56 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
          /*
           * no existing host interface, create it now
           */
-         err = lcp_netlink_add_link_vlan (lip->lip_vif_index, vlan,
-                                          (const char *) host_if_name);
 
-         if (!err && -1 != ns_fd)
-           err = vnet_netlink_set_link_netns (vif_index, ns_fd, NULL);
+         /*
+          * Find the parent tap:
+          * - if this is an outer VLAN, use the pair from the parent phy
+          * - if this is an inner VLAN, find the pair from the outer sub-int,
+          * which must exist.
+          */
+         if (inner_vlan)
+           {
+             index_t linux_parent_if_index;
+             const lcp_itf_pair_t *llip;
+
+             vlan = inner_vlan;
+             proto = inner_proto;
+             linux_parent_if_index = lcp_itf_pair_find_by_outer_vlan (
+               hw->sw_if_index, sw->sub.eth.outer_vlan_id,
+               sw->sub.eth.flags.dot1ad);
+             if (INDEX_INVALID == linux_parent_if_index ||
+                 !(llip = lcp_itf_pair_get (linux_parent_if_index)))
+               {
+                 LCP_ITF_PAIR_ERR (
+                   "pair_create: can't find LCP for outer vlan %d "
+                   "proto %s on %U",
+                   outer_vlan,
+                   outer_proto == ETH_P_8021AD ? "dot1ad" : "dot1q",
+                   format_vnet_sw_if_index_name, vnm, hw->sw_if_index);
+                 err = clib_error_return (0, "parent pair not found");
+                 goto socket_close;
+               }
+
+             LCP_ITF_PAIR_DBG ("pair_create: linux parent %U",
+                               format_lcp_itf_pair, llip);
+             parent_vif_index = llip->lip_vif_index;
+           }
+         else
+           {
+             vlan = outer_vlan;
+             proto = outer_proto;
+           }
+
+         err = lcp_netlink_add_link_vlan (parent_vif_index, vlan, proto,
+                                          (const char *) host_if_name);
+         if (err != 0)
+           {
+             LCP_ITF_PAIR_ERR ("pair_create: cannot create link "
+                               "outer(proto:0x%04x,vlan:%u).inner(proto:0x%"
+                               "04x,vlan:%u) name:'%s'",
+                               outer_proto, outer_vlan, inner_proto,
+                               inner_vlan, host_if_name);
+           }
 
          if (!err)
            vif_index = if_nametoindex ((char *) host_if_name);
@@ -716,18 +911,25 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
       /*
        * create a sub-interface on the tap
        */
-      if (!err && vnet_create_sub_interface (lip->lip_host_sw_if_index,
-                                            sw->sub.id, sw->sub.eth.raw_flags,
-                                            sw->sub.eth.inner_vlan_id, vlan,
-                                            &host_sw_if_index))
-       LCP_ITF_PAIR_INFO ("failed create vlan: %d on %U", vlan,
-                          format_vnet_sw_if_index_name, vnet_get_main (),
-                          lip->lip_host_sw_if_index);
+      if (!err &&
+         vnet_create_sub_interface (lip->lip_host_sw_if_index, sw->sub.id,
+                                    sw->sub.eth.raw_flags, inner_vlan,
+                                    outer_vlan, &host_sw_if_index))
+       {
+         LCP_ITF_PAIR_ERR (
+           "pair_create: failed to create tap subint: %d.%d on %U",
+           outer_vlan, inner_vlan, format_vnet_sw_if_index_name, vnm,
+           lip->lip_host_sw_if_index);
+         err = clib_error_return (
+           0, "failed to create tap subinti: %d.%d. on %U", outer_vlan,
+           inner_vlan, format_vnet_sw_if_index_name, vnm,
+           lip->lip_host_sw_if_index);
+       }
 
     socket_close:
       if (orig_ns_fd != -1)
        {
-         setns (orig_ns_fd, CLONE_NEWNET);
+         clib_setns (orig_ns_fd);
          close (orig_ns_fd);
        }
       if (ns_fd != -1)
@@ -740,6 +942,7 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
     {
       tap_create_if_args_t args = {
        .num_rx_queues = clib_max (1, vlib_num_workers ()),
+       .num_tx_queues = 1,
        .id = hw->hw_if_index,
        .sw_if_index = ~0,
        .rx_ring_sz = 256,
@@ -771,24 +974,33 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
 
       if (args.rv < 0)
        {
+         LCP_ITF_PAIR_ERR ("pair_create: could not create tap, retval:%d",
+                           args.rv);
          return args.rv;
        }
 
       /*
-       * get the hw and ethernet of the tap
+       * The TAP interface does copy forward the host MTU based on the VPP
+       * interface's L3 MTU, but it should also ensure that the VPP tap
+       * interface has an MTU that is greater-or-equal to those. Considering
+       * users can set the interfaces at runtime (set interface mtu packet ...)
+       * ensure that the tap MTU is large enough, taking the VPP interface L3
+       * if it's set, and otherwise a sensible default.
        */
-      hw = vnet_get_sup_hw_interface (vnm, args.sw_if_index);
+      if (sw->mtu[VNET_MTU_L3])
+       vnet_sw_interface_set_mtu (vnm, args.sw_if_index,
+                                  sw->mtu[VNET_MTU_L3]);
+      else
+       vnet_sw_interface_set_mtu (vnm, args.sw_if_index,
+                                  ETHERNET_MAX_PACKET_BYTES);
 
       /*
-       * Set the interface down on the host side.
-       * This controls whether the host can RX/TX.
+       * get the hw and ethernet of the tap
        */
+      hw = vnet_get_sup_hw_interface (vnm, args.sw_if_index);
       virtio_main_t *mm = &virtio_main;
       virtio_if_t *vif = pool_elt_at_index (mm->interfaces, hw->dev_instance);
 
-      lcp_itf_set_vif_link_state (vif->ifindex, 0 /* down */,
-                                 args.host_namespace);
-
       /*
        * Leave the TAP permanently up on the VPP side.
        * This TAP will be shared by many sub-interface.
@@ -814,14 +1026,27 @@ lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
       return -1;
     }
 
-  vnet_sw_interface_admin_up (vnm, host_sw_if_index);
-  lcp_itf_pair_add (host_sw_if_index, phy_sw_if_index, host_if_name, vif_index,
-                   host_if_type, ns);
-
   LCP_ITF_PAIR_INFO ("pair create: {%U, %U, %s}", format_vnet_sw_if_index_name,
                     vnet_get_main (), phy_sw_if_index,
                     format_vnet_sw_if_index_name, vnet_get_main (),
                     host_sw_if_index, host_if_name);
+  lcp_itf_pair_add (host_sw_if_index, phy_sw_if_index, host_if_name, vif_index,
+                   host_if_type, ns);
+
+  /*
+   * Copy the link state from VPP into the host side.
+   * The TAP is shared by many interfaces, always keep it up.
+   * This controls whether the host can RX/TX.
+   */
+
+  lip = lcp_itf_pair_get (lcp_itf_pair_find_by_vif (vif_index));
+  LCP_ITF_PAIR_INFO ("pair create: %U sw-flags %u hw-flags %u",
+                    format_lcp_itf_pair, lip, sw->flags, hw->flags);
+  vnet_sw_interface_admin_up (vnm, host_sw_if_index);
+  lcp_itf_set_link_state (lip, sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP);
+
+  if (host_sw_if_indexp)
+    *host_sw_if_indexp = host_sw_if_index;
 
   return 0;
 }
@@ -902,7 +1127,7 @@ lcp_itf_pair_process (vlib_main_t *vm, vlib_node_runtime_t *rt,
          lipn = &lipn_names[*lipn_index];
          lcp_itf_pair_create (lipn->lipn_phy_sw_if_index,
                               lipn->lipn_host_name, LCP_ITF_HOST_TAP,
-                              lipn->lipn_namespace);
+                              lipn->lipn_namespace, NULL);
        }
 
       vec_reset_length (event_data);