linux-cp: Basic MPLS support.
[vpp.git] / src / plugins / linux-cp / lcp_node.c
index 7f099ff..241cc5e 100644 (file)
@@ -31,6 +31,7 @@
 #include <vnet/ip/ip4.h>
 #include <vnet/ip/ip6.h>
 #include <vnet/l2/l2_input.h>
+#include <vnet/mpls/mpls.h>
 
 #define foreach_lip_punt                                                      \
   _ (IO, "punt to host")                                                      \
@@ -339,6 +340,7 @@ lcp_xc_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame,
          const lcp_itf_pair_t *lip;
          u32 next0, bi0, lipi, ai;
          vlib_buffer_t *b0;
+         const ip_adjacency_t *adj;
 
          bi0 = to_next[0] = from[0];
 
@@ -357,30 +359,24 @@ lcp_xc_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame,
          vlib_buffer_advance (b0, -lip->lip_rewrite_len);
          eth = vlib_buffer_get_current (b0);
 
-         if (ethernet_address_cast (eth->dst_address))
-           ai = lip->lip_phy_adjs.adj_index[af];
-         else
+         ai = ADJ_INDEX_INVALID;
+         if (!ethernet_address_cast (eth->dst_address))
            ai = lcp_adj_lkup ((u8 *) eth, lip->lip_rewrite_len,
                               vnet_buffer (b0)->sw_if_index[VLIB_TX]);
+         if (ai == ADJ_INDEX_INVALID)
+           ai = lip->lip_phy_adjs.adj_index[af];
 
-         if (ADJ_INDEX_INVALID != ai)
-           {
-             const ip_adjacency_t *adj;
+         adj = adj_get (ai);
+         vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ai;
+         next0 = adj->rewrite_header.next_index;
+         vnet_buffer (b0)->ip.save_rewrite_length = lip->lip_rewrite_len;
 
-             adj = adj_get (ai);
-             vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ai;
-             next0 = adj->rewrite_header.next_index;
-             vnet_buffer (b0)->ip.save_rewrite_length = lip->lip_rewrite_len;
-
-             if (PREDICT_FALSE (adj->rewrite_header.flags &
-                                VNET_REWRITE_HAS_FEATURES))
-               vnet_feature_arc_start_w_cfg_index (
-                 lm->output_feature_arc_index,
-                 vnet_buffer (b0)->sw_if_index[VLIB_TX], &next0, b0,
-                 adj->ia_cfg_index);
-           }
-         else
-           next0 = LCP_XC_NEXT_DROP;
+         if (PREDICT_FALSE (adj->rewrite_header.flags &
+                            VNET_REWRITE_HAS_FEATURES))
+           vnet_feature_arc_start_w_cfg_index (
+             lm->output_feature_arc_index,
+             vnet_buffer (b0)->sw_if_index[VLIB_TX], &next0, b0,
+             adj->ia_cfg_index);
 
          if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
            {
@@ -441,16 +437,114 @@ VNET_FEATURE_INIT (lcp_xc_ip6_mcast_node, static) = {
   .node_name = "linux-cp-xc-ip6",
 };
 
+typedef enum
+{
+  LCP_XC_MPLS_NEXT_DROP,
+  LCP_XC_MPLS_NEXT_IO,
+  LCP_XC_MPLS_N_NEXT,
+} lcp_xc_mpls_next_t;
+
+static_always_inline uword
+lcp_xc_mpls_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
+                   vlib_frame_t *frame)
+{
+  u32 n_left_from, *from, *to_next, n_left_to_next;
+  lcp_xc_next_t next_index;
+
+  next_index = 0;
+  n_left_from = frame->n_vectors;
+  from = vlib_frame_vector_args (frame);
+
+  while (n_left_from > 0)
+    {
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         const ethernet_header_t *eth;
+         const lcp_itf_pair_t *lip;
+         u32 next0, bi0, lipi, ai;
+         vlib_buffer_t *b0;
+         // const ip_adjacency_t *adj;
+
+         bi0 = to_next[0] = from[0];
+
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+
+         lipi =
+           lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]);
+         lip = lcp_itf_pair_get (lipi);
+
+         vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip->lip_phy_sw_if_index;
+         vlib_buffer_advance (b0, -lip->lip_rewrite_len);
+         eth = vlib_buffer_get_current (b0);
+
+         ai = ADJ_INDEX_INVALID;
+         next0 = LCP_XC_MPLS_NEXT_DROP;
+         if (!ethernet_address_cast (eth->dst_address))
+           ai = lcp_adj_lkup ((u8 *) eth, lip->lip_rewrite_len,
+                              vnet_buffer (b0)->sw_if_index[VLIB_TX]);
+         if (ai != ADJ_INDEX_INVALID)
+           {
+             vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ai;
+             next0 = LCP_XC_MPLS_NEXT_IO;
+           }
+
+         if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             lcp_xc_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+             t->phy_sw_if_index = lip->lip_phy_sw_if_index;
+             t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
+           }
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return frame->n_vectors;
+}
+
+VLIB_NODE_FN (lcp_xc_mpls)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
+{
+  return (lcp_xc_mpls_inline (vm, node, frame));
+}
+
+VLIB_REGISTER_NODE (
+  lcp_xc_mpls) = { .name = "linux-cp-xc-mpls",
+                  .vector_size = sizeof (u32),
+                  .format_trace = format_lcp_xc_trace,
+                  .type = VLIB_NODE_TYPE_INTERNAL,
+                  .n_next_nodes = LCP_XC_MPLS_N_NEXT,
+                  .next_nodes = {
+                    [LCP_XC_MPLS_NEXT_DROP] = "error-drop",
+                    [LCP_XC_MPLS_NEXT_IO] = "interface-output",
+                  } };
+
+VNET_FEATURE_INIT (lcp_xc_mpls_node, static) = {
+  .arc_name = "mpls-input",
+  .node_name = "linux-cp-xc-mpls",
+};
+
 typedef enum
 {
   LCP_XC_L3_NEXT_XC,
+  LCP_XC_L3_NEXT_LOOKUP,
   LCP_XC_L3_N_NEXT,
 } lcp_xc_l3_next_t;
 
 /**
  * X-connect all packets from the HOST to the PHY on L3 interfaces
  *
- * There's only one adjacency that can be used on thises links.
+ * There's only one adjacency that can be used on these links.
  */
 static_always_inline u32
 lcp_xc_l3_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
@@ -458,6 +552,7 @@ lcp_xc_l3_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
 {
   u32 n_left_from, *from, *to_next, n_left_to_next;
   lcp_xc_next_t next_index;
+  vnet_main_t *vnm = vnet_get_main ();
 
   next_index = 0;
   n_left_from = frame->n_vectors;
@@ -493,10 +588,24 @@ lcp_xc_l3_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
            lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]);
          lip = lcp_itf_pair_get (lipi);
 
-         vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip->lip_phy_sw_if_index;
-         next0 = LCP_XC_L3_NEXT_XC;
-         vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
-           lip->lip_phy_adjs.adj_index[af];
+         /* P2P tunnels can use generic adjacency */
+         if (PREDICT_TRUE (
+               vnet_sw_interface_is_p2p (vnm, lip->lip_phy_sw_if_index)))
+           {
+             vnet_buffer (b0)->sw_if_index[VLIB_TX] =
+               lip->lip_phy_sw_if_index;
+             vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
+               lip->lip_phy_adjs.adj_index[af];
+             next0 = LCP_XC_L3_NEXT_XC;
+           }
+         /* P2MP tunnels require a fib lookup to find the right adjacency */
+         else
+           {
+             /* lookup should use FIB table associated with phy interface */
+             vnet_buffer (b0)->sw_if_index[VLIB_RX] =
+               lip->lip_phy_sw_if_index;
+             next0 = LCP_XC_L3_NEXT_LOOKUP;
+           }
 
          if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
            {
@@ -539,6 +648,7 @@ VLIB_REGISTER_NODE (lcp_xc_l3_ip4_node) = {
   .n_next_nodes = LCP_XC_L3_N_NEXT,
   .next_nodes = {
     [LCP_XC_L3_NEXT_XC] = "ip4-midchain",
+    [LCP_XC_L3_NEXT_LOOKUP] = "ip4-lookup",
   },
 };
 
@@ -561,6 +671,7 @@ VLIB_REGISTER_NODE (lcp_xc_l3_ip6_node) = {
   .n_next_nodes = LCP_XC_L3_N_NEXT,
   .next_nodes = {
     [LCP_XC_L3_NEXT_XC] = "ip6-midchain",
+    [LCP_XC_L3_NEXT_LOOKUP] = "ip6-lookup",
   },
 };
 
@@ -676,10 +787,14 @@ VLIB_NODE_FN (lcp_arp_phy_node)
                  c0 = vlib_buffer_copy (vm, b0);
                  vlib_buffer_advance (b0, len0);
 
-                 /* Send to the host */
-                 vnet_buffer (c0)->sw_if_index[VLIB_TX] =
-                   lip0->lip_host_sw_if_index;
-                 reply_copies[n_copies++] = vlib_get_buffer_index (vm, c0);
+                 if (c0)
+                   {
+                     /* Send to the host */
+                     vnet_buffer (c0)->sw_if_index[VLIB_TX] =
+                       lip0->lip_host_sw_if_index;
+                     reply_copies[n_copies++] =
+                       vlib_get_buffer_index (vm, c0);
+                   }
                }
            }
          if (arp1->opcode == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply))
@@ -704,10 +819,14 @@ VLIB_NODE_FN (lcp_arp_phy_node)
                  c1 = vlib_buffer_copy (vm, b1);
                  vlib_buffer_advance (b1, len1);
 
-                 /* Send to the host */
-                 vnet_buffer (c1)->sw_if_index[VLIB_TX] =
-                   lip1->lip_host_sw_if_index;
-                 reply_copies[n_copies++] = vlib_get_buffer_index (vm, c1);
+                 if (c1)
+                   {
+                     /* Send to the host */
+                     vnet_buffer (c1)->sw_if_index[VLIB_TX] =
+                       lip1->lip_host_sw_if_index;
+                     reply_copies[n_copies++] =
+                       vlib_get_buffer_index (vm, c1);
+                   }
                }
            }
 
@@ -776,10 +895,14 @@ VLIB_NODE_FN (lcp_arp_phy_node)
                  c0 = vlib_buffer_copy (vm, b0);
                  vlib_buffer_advance (b0, len0);
 
-                 /* Send to the host */
-                 vnet_buffer (c0)->sw_if_index[VLIB_TX] =
-                   lip0->lip_host_sw_if_index;
-                 reply_copies[n_copies++] = vlib_get_buffer_index (vm, c0);
+                 if (c0)
+                   {
+                     /* Send to the host */
+                     vnet_buffer (c0)->sw_if_index[VLIB_TX] =
+                       lip0->lip_host_sw_if_index;
+                     reply_copies[n_copies++] =
+                       vlib_get_buffer_index (vm, c0);
+                   }
                }
            }