Basic support for LISP-GPE encapsulated NSH packets 31/5031/13
authorFlorin Coras <fcoras@cisco.com>
Thu, 26 Jan 2017 22:25:34 +0000 (14:25 -0800)
committerNeale Ranns <nranns@cisco.com>
Mon, 13 Feb 2017 08:51:30 +0000 (08:51 +0000)
Change-Id: I97fedb0f70dd18ed9bbe985407cc5fe714e8a2e2
Signed-off-by: Florin Coras <fcoras@cisco.com>
28 files changed:
src/vnet.am
src/vnet/adj/adj_internal.h
src/vnet/adj/adj_midchain.c
src/vnet/adj/adj_nsh.c [new file with mode: 0644]
src/vnet/adj/adj_nsh.h [new file with mode: 0644]
src/vnet/dpo/dpo.c
src/vnet/dpo/dpo.h
src/vnet/fib/fib_entry_delegate.c
src/vnet/fib/fib_entry_delegate.h
src/vnet/fib/fib_entry_src.c
src/vnet/fib/fib_path.c
src/vnet/fib/fib_types.c
src/vnet/fib/fib_types.h
src/vnet/gre/gre.c
src/vnet/interface.c
src/vnet/interface.h
src/vnet/lisp-cp/control.c
src/vnet/lisp-cp/lisp_api.c
src/vnet/lisp-cp/lisp_cp_dpo.c
src/vnet/lisp-cp/lisp_types.c
src/vnet/lisp-cp/lisp_types.h
src/vnet/lisp-gpe/interface.c
src/vnet/lisp-gpe/lisp_gpe.c
src/vnet/lisp-gpe/lisp_gpe.h
src/vnet/lisp-gpe/lisp_gpe_adjacency.c
src/vnet/lisp-gpe/lisp_gpe_fwd_entry.c
src/vnet/lisp-gpe/lisp_gpe_fwd_entry.h
src/vnet/mfib/mfib_entry.c

index 9b148f6..a8cc696 100644 (file)
@@ -958,12 +958,14 @@ libvnet_la_SOURCES +=                             \
   vnet/adj/adj_midchain.c                      \
   vnet/adj/adj_mcast.c                         \
   vnet/adj/adj_l2.c                            \
+  vnet/adj/adj_nsh.c                           \
   vnet/adj/adj.c
 
 nobase_include_HEADERS +=                      \
   vnet/adj/adj.h                               \
   vnet/adj/adj_types.h                         \
   vnet/adj/adj_glean.h                         \
+  vnet/adj/adj_nsh.h                           \
   vnet/adj/adj_nbr.h
 
 ########################################
index ece5912..3066862 100644 (file)
@@ -20,7 +20,7 @@
 #include <vnet/ip/ip.h>
 #include <vnet/mpls/mpls.h>
 #include <vnet/adj/adj_l2.h>
-
+#include <vnet/adj/adj_nsh.h>
 
 /**
  * big switch to turn on Adjacency debugging
@@ -53,6 +53,8 @@ adj_get_rewrite_node (vnet_link_t linkt)
        return (mpls_output_node.index);
     case VNET_LINK_ETHERNET:
        return (adj_l2_rewrite_node.index);
+    case VNET_LINK_NSH:
+        return (adj_nsh_rewrite_node.index);
     case VNET_LINK_ARP:
        break;
     }
index 8c6ab5a..35cdb00 100644 (file)
@@ -16,6 +16,7 @@
 #include <vnet/adj/adj_nbr.h>
 #include <vnet/adj/adj_internal.h>
 #include <vnet/adj/adj_l2.h>
+#include <vnet/adj/adj_nsh.h>
 #include <vnet/adj/adj_midchain.h>
 #include <vnet/ethernet/arp_packet.h>
 #include <vnet/dpo/drop_dpo.h>
@@ -308,6 +309,18 @@ VNET_FEATURE_INIT (adj_midchain_tx_no_count_ethernet, static) = {
     .runs_before = VNET_FEATURES ("error-drop"),
     .feature_index_ptr = &adj_midchain_tx_no_count_feature_node[VNET_LINK_ETHERNET],
 };
+VNET_FEATURE_INIT (adj_midchain_tx_nsh, static) = {
+    .arc_name = "nsh-output",
+    .node_name = "adj-midchain-tx",
+    .runs_before = VNET_FEATURES ("error-drop"),
+    .feature_index_ptr = &adj_midchain_tx_feature_node[VNET_LINK_NSH],
+};
+VNET_FEATURE_INIT (adj_midchain_tx_no_count_nsh, static) = {
+    .arc_name = "nsh-output",
+    .node_name = "adj-midchain-tx-no-count",
+    .runs_before = VNET_FEATURES ("error-drop"),
+    .feature_index_ptr = &adj_midchain_tx_no_count_feature_node[VNET_LINK_NSH],
+};
 
 static inline u32
 adj_get_midchain_node (vnet_link_t link)
@@ -321,6 +334,8 @@ adj_get_midchain_node (vnet_link_t link)
        return (mpls_midchain_node.index);
     case VNET_LINK_ETHERNET:
        return (adj_l2_midchain_node.index);
+    case VNET_LINK_NSH:
+        return (adj_nsh_midchain_node.index);
     case VNET_LINK_ARP:
        break;
     }
@@ -354,6 +369,11 @@ adj_midchain_get_feature_arc_index_for_link_type (const ip_adjacency_t *adj)
            arc = ethernet_main.output_feature_arc_index;
            break;
        }
+    case VNET_LINK_NSH:
+        {
+          arc = nsh_main_dummy.output_feature_arc_index;
+          break;
+        }
     case VNET_LINK_ARP:
        ASSERT(0);
        break;
diff --git a/src/vnet/adj/adj_nsh.c b/src/vnet/adj/adj_nsh.c
new file mode 100644 (file)
index 0000000..9a0f9d8
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/adj/adj_nsh.h>
+#include <vnet/ip/ip.h>
+
+nsh_main_dummy_t nsh_main_dummy;
+
+/**
+ * @brief Trace data for a NSH Midchain
+ */
+typedef struct adj_nsh_trace_t_ {
+    /** Adjacency index taken. */
+    u32 adj_index;
+} adj_nsh_trace_t;
+
+static u8 *
+format_adj_nsh_trace (u8 * s, va_list * args)
+{
+    CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+    CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+    adj_nsh_trace_t * t = va_arg (*args, adj_nsh_trace_t *);
+
+    s = format (s, "adj-idx %d : %U",
+                t->adj_index,
+                format_ip_adjacency, t->adj_index, FORMAT_IP_ADJACENCY_NONE);
+    return s;
+}
+
+typedef enum adj_nsh_rewrite_next_t_
+{
+    ADJ_NSH_REWRITE_NEXT_DROP,
+} adj_gpe_rewrite_next_t;
+
+always_inline uword
+adj_nsh_rewrite_inline (vlib_main_t * vm,
+                       vlib_node_runtime_t * node,
+                       vlib_frame_t * frame,
+                       int is_midchain)
+{
+    u32 * from = vlib_frame_vector_args (frame);
+    u32 n_left_from, n_left_to_next, * to_next, next_index;
+    u32 cpu_index = os_get_cpu_number();
+
+    n_left_from = frame->n_vectors;
+    next_index = node->cached_next_index;
+
+    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)
+        {
+            ip_adjacency_t * adj0;
+            vlib_buffer_t * p0;
+            char *h0;
+            u32 pi0, rw_len0, adj_index0, next0 = 0;
+            u32 tx_sw_if_index0;
+
+            pi0 = to_next[0] = from[0];
+            from += 1;
+            n_left_from -= 1;
+            to_next += 1;
+            n_left_to_next -= 1;
+
+            p0 = vlib_get_buffer (vm, pi0);
+            h0 = vlib_buffer_get_current (p0);
+
+            adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
+
+            /* We should never rewrite a pkt using the MISS adjacency */
+            ASSERT(adj_index0);
+
+            adj0 = adj_get (adj_index0);
+
+            /* Guess we are only writing on simple IP4 header. */
+            vnet_rewrite_one_header(adj0[0], h0, sizeof(ip4_header_t));
+
+            /* Update packet buffer attributes/set output interface. */
+            rw_len0 = adj0[0].rewrite_header.data_bytes;
+            vnet_buffer(p0)->ip.save_rewrite_length = rw_len0;
+
+            vlib_increment_combined_counter(&adjacency_counters,
+                                            cpu_index,
+                                            adj_index0,
+                                            /* packet increment */ 0,
+                                            /* byte increment */ rw_len0);
+
+            /* Check MTU of outgoing interface. */
+            if (PREDICT_TRUE((vlib_buffer_length_in_chain (vm, p0)  <=
+                              adj0[0].rewrite_header.max_l3_packet_bytes)))
+            {
+                /* Don't adjust the buffer for ttl issue; icmp-error node wants
+                 * to see the IP headerr */
+                p0->current_data -= rw_len0;
+                p0->current_length += rw_len0;
+                tx_sw_if_index0 = adj0[0].rewrite_header.sw_if_index;
+
+                if (is_midchain)
+                {
+                    adj0->sub_type.midchain.fixup_func(vm, adj0, p0);
+                }
+
+                vnet_buffer (p0)->sw_if_index[VLIB_TX] = tx_sw_if_index0;
+
+                /*
+                 * Follow the feature ARC. this will result eventually in
+                 * the midchain-tx node
+                 */
+                vnet_feature_arc_start (nsh_main_dummy.output_feature_arc_index,
+                                        tx_sw_if_index0, &next0, p0);
+            }
+            else
+            {
+                /* can't fragment NSH */
+                next0 = ADJ_NSH_REWRITE_NEXT_DROP;
+            }
+
+            if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
+            {
+                adj_nsh_trace_t *tr = vlib_add_trace (vm, node,
+                                                     p0, sizeof (*tr));
+                tr->adj_index = vnet_buffer(p0)->ip.adj_index[VLIB_TX];
+            }
+
+            vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                             to_next, n_left_to_next,
+                                             pi0, next0);
+        }
+
+        vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+    return frame->n_vectors;
+}
+
+static uword
+adj_nsh_rewrite (vlib_main_t * vm,
+                vlib_node_runtime_t * node,
+                vlib_frame_t * frame)
+{
+    return adj_nsh_rewrite_inline (vm, node, frame, 0);
+}
+
+static uword
+adj_nsh_midchain (vlib_main_t * vm,
+                 vlib_node_runtime_t * node,
+                 vlib_frame_t * frame)
+{
+    return adj_nsh_rewrite_inline (vm, node, frame, 1);
+}
+
+VLIB_REGISTER_NODE (adj_nsh_rewrite_node) = {
+    .function = adj_nsh_rewrite,
+    .name = "adj-nsh-rewrite",
+    .vector_size = sizeof (u32),
+
+    .format_trace = format_adj_nsh_trace,
+
+    .n_next_nodes = 1,
+    .next_nodes = {
+        [ADJ_NSH_REWRITE_NEXT_DROP] = "error-drop",
+    },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (adj_nsh_rewrite_node, adj_nsh_rewrite)
+
+VLIB_REGISTER_NODE (adj_nsh_midchain_node) = {
+    .function = adj_nsh_midchain,
+    .name = "adj-nsh-midchain",
+    .vector_size = sizeof (u32),
+
+    .format_trace = format_adj_nsh_trace,
+
+    .n_next_nodes = 1,
+    .next_nodes = {
+        [ADJ_NSH_REWRITE_NEXT_DROP] = "error-drop",
+    },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (adj_nsh_midchain_node, adj_nsh_midchain)
+
+/* Built-in ip4 tx feature path definition */
+/* *INDENT-OFF* */
+VNET_FEATURE_ARC_INIT (nsh_output, static) =
+{
+  .arc_name  = "nsh-output",
+  .start_nodes = VNET_FEATURES ("adj-nsh-midchain"),
+  .arc_index_ptr = &nsh_main_dummy.output_feature_arc_index,
+};
+
+VNET_FEATURE_INIT (nsh_tx_drop, static) =
+{
+  .arc_name = "nsh-output",
+  .node_name = "error-drop",
+  .runs_before = 0,     /* not before any other features */
+};
+/* *INDENT-ON* */
diff --git a/src/vnet/adj/adj_nsh.h b/src/vnet/adj/adj_nsh.h
new file mode 100644 (file)
index 0000000..5501fbb
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ADJ_NSH_H__
+#define __ADJ_NSH_H__
+
+#include <vnet/adj/adj.h>
+
+extern vlib_node_registration_t adj_nsh_midchain_node;
+extern vlib_node_registration_t adj_nsh_rewrite_node;
+
+typedef struct _nsh_main_dummy
+{
+  u8 output_feature_arc_index;
+} nsh_main_dummy_t;
+
+extern nsh_main_dummy_t nsh_main_dummy;
+
+#endif
index cc2fa0e..d8e075a 100644 (file)
@@ -98,6 +98,8 @@ vnet_link_to_dpo_proto (vnet_link_t linkt)
         return (DPO_PROTO_MPLS);
     case VNET_LINK_ETHERNET:
         return (DPO_PROTO_ETHERNET);
+    case VNET_LINK_NSH:
+        return (DPO_PROTO_NSH);
     case VNET_LINK_ARP:
        break;
     }
index aff4e1b..48b92d3 100644 (file)
@@ -67,9 +67,10 @@ typedef enum dpo_proto_t_
     DPO_PROTO_IP6,
     DPO_PROTO_ETHERNET,
     DPO_PROTO_MPLS,
+    DPO_PROTO_NSH,
 } __attribute__((packed)) dpo_proto_t;
 
-#define DPO_PROTO_NUM ((dpo_proto_t)(DPO_PROTO_MPLS+1))
+#define DPO_PROTO_NUM ((dpo_proto_t)(DPO_PROTO_NSH+1))
 #define DPO_PROTO_NONE ((dpo_proto_t)(DPO_PROTO_NUM+1))
 
 #define DPO_PROTOS {           \
@@ -77,11 +78,12 @@ typedef enum dpo_proto_t_
     [DPO_PROTO_IP6]  = "ip6",  \
     [DPO_PROTO_ETHERNET]  = "ethernet", \
     [DPO_PROTO_MPLS] = "mpls", \
+    [DPO_PROTO_NSH] = "nsh",    \
 }
 
 #define FOR_EACH_DPO_PROTO(_proto)    \
     for (_proto = DPO_PROTO_IP4;      \
-        _proto <= DPO_PROTO_MPLS;    \
+        _proto <= DPO_PROTO_NSH;    \
         _proto++)
 
 /**
index efe402d..70840b1 100644 (file)
@@ -122,6 +122,8 @@ fib_entry_chain_type_to_delegate_type (fib_forward_chain_type_t fct)
     case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
     case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
         break;
+    case FIB_FORW_CHAIN_TYPE_NSH:
+        return (FIB_ENTRY_DELEGATE_CHAIN_NSH);
     }
     ASSERT(0);
     return (FIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP4);
@@ -142,6 +144,8 @@ fib_entry_delegate_type_to_chain_type (fib_entry_delegate_type_t fdt)
         return (FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS);
     case FIB_ENTRY_DELEGATE_CHAIN_ETHERNET:
         return (FIB_FORW_CHAIN_TYPE_ETHERNET);
+    case FIB_ENTRY_DELEGATE_CHAIN_NSH:
+        return (FIB_FORW_CHAIN_TYPE_NSH);
     case FIB_ENTRY_DELEGATE_COVERED:
     case FIB_ENTRY_DELEGATE_ATTACHED_IMPORT:
     case FIB_ENTRY_DELEGATE_ATTACHED_EXPORT:
index 6d3a654..d9183c5 100644 (file)
@@ -35,6 +35,7 @@ typedef enum fib_entry_delegate_type_t_ {
     FIB_ENTRY_DELEGATE_CHAIN_MPLS_EOS = FIB_FORW_CHAIN_TYPE_MPLS_EOS,
     FIB_ENTRY_DELEGATE_CHAIN_MPLS_NON_EOS = FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
     FIB_ENTRY_DELEGATE_CHAIN_ETHERNET = FIB_FORW_CHAIN_TYPE_ETHERNET,
+    FIB_ENTRY_DELEGATE_CHAIN_NSH = FIB_FORW_CHAIN_TYPE_NSH,
     /**
      * Dependency list of covered entries.
      * these are more specific entries that are interested in changes
@@ -51,7 +52,7 @@ typedef enum fib_entry_delegate_type_t_ {
 #define FOR_EACH_DELEGATE_CHAIN(_entry, _fdt, _fed, _body)    \
 {                                                             \
     for (_fdt = FIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP4;         \
-         _fdt <= FIB_ENTRY_DELEGATE_CHAIN_ETHERNET;           \
+         _fdt <= FIB_ENTRY_DELEGATE_CHAIN_NSH;                \
          _fdt++)                                              \
     {                                                         \
         _fed = fib_entry_delegate_get(_entry, _fdt);          \
index d54787c..5710915 100644 (file)
@@ -355,6 +355,7 @@ fib_entry_src_collect_forwarding (fib_node_index_t pl_index,
             break;
         }
         case FIB_FORW_CHAIN_TYPE_ETHERNET:
+        case FIB_FORW_CHAIN_TYPE_NSH:
            ASSERT(0);
            break;
         }
index 080057f..aa545b5 100644 (file)
@@ -1755,6 +1755,7 @@ fib_path_contribute_forwarding (fib_node_index_t path_index,
            case FIB_FORW_CHAIN_TYPE_MPLS_EOS:
            case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS:
            case FIB_FORW_CHAIN_TYPE_ETHERNET:
+           case FIB_FORW_CHAIN_TYPE_NSH:
            {
                adj_index_t ai;
 
@@ -1787,6 +1788,7 @@ fib_path_contribute_forwarding (fib_node_index_t path_index,
            case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
            case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
            case FIB_FORW_CHAIN_TYPE_ETHERNET:
+           case FIB_FORW_CHAIN_TYPE_NSH:
                ASSERT(0);
                break;
            }
@@ -1809,6 +1811,7 @@ fib_path_contribute_forwarding (fib_node_index_t path_index,
            case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
            case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
            case FIB_FORW_CHAIN_TYPE_ETHERNET:
+           case FIB_FORW_CHAIN_TYPE_NSH:
                ASSERT(0);
                break;
             }
@@ -1824,6 +1827,7 @@ fib_path_contribute_forwarding (fib_node_index_t path_index,
            case FIB_FORW_CHAIN_TYPE_UNICAST_IP6:
            case FIB_FORW_CHAIN_TYPE_MPLS_EOS:
            case FIB_FORW_CHAIN_TYPE_ETHERNET:
+           case FIB_FORW_CHAIN_TYPE_NSH:
                 break;
            case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
            case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
index 3ecb38e..2837a59 100644 (file)
@@ -279,6 +279,8 @@ fib_forw_chain_type_from_dpo_proto (dpo_proto_t proto)
        return (FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS);
     case DPO_PROTO_ETHERNET:
        return (FIB_FORW_CHAIN_TYPE_ETHERNET);
+    case DPO_PROTO_NSH:
+        return (FIB_FORW_CHAIN_TYPE_NSH);
     }
     ASSERT(0);
     return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
@@ -297,6 +299,8 @@ fib_forw_chain_type_to_link_type (fib_forward_chain_type_t fct)
        return (VNET_LINK_IP6);
     case FIB_FORW_CHAIN_TYPE_ETHERNET:
        return (VNET_LINK_ETHERNET);
+    case FIB_FORW_CHAIN_TYPE_NSH:
+        return (VNET_LINK_NSH);
     case FIB_FORW_CHAIN_TYPE_MPLS_EOS:
        /*
         * insufficient information to to convert
@@ -322,6 +326,8 @@ fib_forw_chain_type_to_dpo_proto (fib_forward_chain_type_t fct)
        return (DPO_PROTO_IP6);
     case FIB_FORW_CHAIN_TYPE_ETHERNET:
        return (DPO_PROTO_ETHERNET);
+    case FIB_FORW_CHAIN_TYPE_NSH:
+        return (DPO_PROTO_NSH);
     case FIB_FORW_CHAIN_TYPE_MPLS_EOS:
     case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS:
        return (DPO_PROTO_MPLS);
index c51bc9c..05e0e0a 100644 (file)
@@ -105,10 +105,14 @@ typedef enum fib_forward_chain_type_t_ {
     FIB_FORW_CHAIN_TYPE_MCAST_IP6,
     /**
      * Contribute an object that is to be used to forward Ethernet packets.
+     */
+    FIB_FORW_CHAIN_TYPE_ETHERNET,
+    /**
+     * Contribute an object that is to be used to forward NSH packets.
      * This is last in the list since it is not valid for many FIB objects,
      * and thus their array of per-chain-type DPOs can be sized smaller.
      */
-    FIB_FORW_CHAIN_TYPE_ETHERNET,
+    FIB_FORW_CHAIN_TYPE_NSH,
 }  __attribute__ ((packed)) fib_forward_chain_type_t;
 
 #define FIB_FORW_CHAINS {                                      \
@@ -119,14 +123,15 @@ typedef enum fib_forward_chain_type_t_ {
     [FIB_FORW_CHAIN_TYPE_MCAST_IP6]     = "multicast-ip6",     \
     [FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS]  = "mpls-neos",         \
     [FIB_FORW_CHAIN_TYPE_MPLS_EOS]      = "mpls-eos",          \
+    [FIB_FORW_CHAIN_TYPE_NSH]           = "nsh",                \
 }
 
-#define FIB_FORW_CHAIN_NUM (FIB_FORW_CHAIN_TYPE_MPLS_ETHERNET+1)
+#define FIB_FORW_CHAIN_NUM (FIB_FORW_CHAIN_TYPE_NSH+1)
 #define FIB_FORW_CHAIN_MPLS_NUM (FIB_FORW_CHAIN_TYPE_MPLS_EOS+1)
 
 #define FOR_EACH_FIB_FORW_CHAIN(_item)                   \
     for (_item = FIB_FORW_CHAIN_TYPE_UNICAST_IP4;        \
-        _item <= FIB_FORW_CHAIN_TYPE_ETHERNET;           \
+        _item <= FIB_FORW_CHAIN_TYPE_NSH;                \
         _item++)
 
 #define FOR_EACH_FIB_FORW_MPLS_CHAIN(_item)              \
index 0faed13..cd43a3a 100644 (file)
@@ -177,6 +177,9 @@ gre_proto_from_vnet_link (vnet_link_t link)
         return (GRE_PROTOCOL_teb);
     case VNET_LINK_ARP:
         return (GRE_PROTOCOL_arp);
+    case VNET_LINK_NSH:
+        ASSERT(0);
+        break;
     }
     ASSERT(0);
     return (GRE_PROTOCOL_ip4);
index 9454ac1..2a1e70e 100644 (file)
@@ -1364,6 +1364,7 @@ vnet_link_to_l3_proto (vnet_link_t link)
     case VNET_LINK_ARP:
       return (VNET_L3_PACKET_TYPE_ARP);
     case VNET_LINK_ETHERNET:
+    case VNET_LINK_NSH:
       ASSERT (0);
       break;
     }
index d42e5fd..7b79175 100644 (file)
@@ -240,6 +240,7 @@ typedef enum vnet_link_t_
   VNET_LINK_MPLS,
   VNET_LINK_ETHERNET,
   VNET_LINK_ARP,
+  VNET_LINK_NSH,
 } __attribute__ ((packed)) vnet_link_t;
 
 #define VNET_LINKS {                   \
@@ -248,13 +249,14 @@ typedef enum vnet_link_t_
     [VNET_LINK_IP6] = "ipv6",          \
     [VNET_LINK_MPLS] = "mpls",         \
     [VNET_LINK_ARP] = "arp",          \
+    [VNET_LINK_NSH] = "nsh",           \
 }
 
 /**
  * @brief Number of link types. Not part of the enum so it does not have to be included in
  * switch statements
  */
-#define VNET_LINK_NUM (VNET_LINK_ARP+1)
+#define VNET_LINK_NUM (VNET_LINK_NSH+1)
 
 /**
  * @brief Convert a link to to an Ethertype
index cc73dfc..f0383e1 100644 (file)
@@ -2700,6 +2700,11 @@ get_src_and_dst_eids_from_buffer (lisp_cp_main_t * lcm, vlib_buffer_t * b,
       gid_address_vni (dst) = vni;
       gid_address_vni (src) = vni;
     }
+  else if (LISP_AFI_LCAF == type)
+    {
+      /* Eventually extend this to support NSH and other */
+      ASSERT (0);
+    }
 }
 
 static uword
@@ -2818,6 +2823,14 @@ lisp_cp_lookup_l2 (vlib_main_t * vm,
   return (lisp_cp_lookup_inline (vm, node, from_frame, LISP_AFI_MAC));
 }
 
+static uword
+lisp_cp_lookup_nsh (vlib_main_t * vm,
+                   vlib_node_runtime_t * node, vlib_frame_t * from_frame)
+{
+  /* TODO decide if NSH should be propagated as LCAF or not */
+  return (lisp_cp_lookup_inline (vm, node, from_frame, LISP_AFI_LCAF));
+}
+
 /* *INDENT-OFF* */
 VLIB_REGISTER_NODE (lisp_cp_lookup_ip4_node) = {
   .function = lisp_cp_lookup_ip4,
@@ -2875,6 +2888,25 @@ VLIB_REGISTER_NODE (lisp_cp_lookup_l2_node) = {
 };
 /* *INDENT-ON* */
 
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (lisp_cp_lookup_nsh_node) = {
+  .function = lisp_cp_lookup_nsh,
+  .name = "lisp-cp-lookup-nsh",
+  .vector_size = sizeof (u32),
+  .format_trace = format_lisp_cp_lookup_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = LISP_CP_LOOKUP_N_ERROR,
+  .error_strings = lisp_cp_lookup_error_strings,
+
+  .n_next_nodes = LISP_CP_LOOKUP_N_NEXT,
+
+  .next_nodes = {
+      [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop",
+  },
+};
+/* *INDENT-ON* */
+
 /* lisp_cp_input statistics */
 #define foreach_lisp_cp_input_error                               \
 _(DROP, "drop")                                                   \
index a877540..78d32e1 100644 (file)
@@ -714,6 +714,8 @@ fid_type_to_api_type (fid_address_t * fid)
 
     case FID_ADDR_MAC:
       return 2;
+    case FID_ADDR_NSH:
+      return 3;
     }
 
   return ~0;
index 185b07a..848f621 100644 (file)
@@ -79,12 +79,17 @@ const static char *const lisp_cp_ethernet_nodes[] = {
   NULL,
 };
 
+const static char *const lisp_cp_nsh_nodes[] = {
+  "lisp-cp-lookup-nsh",
+  NULL,
+};
 
 const static char *const *const lisp_cp_nodes[DPO_PROTO_NUM] = {
   [DPO_PROTO_IP4] = lisp_cp_ip4_nodes,
   [DPO_PROTO_IP6] = lisp_cp_ip6_nodes,
   [DPO_PROTO_ETHERNET] = lisp_cp_ethernet_nodes,
   [DPO_PROTO_MPLS] = NULL,
+  [DPO_PROTO_NSH] = lisp_cp_nsh_nodes,
 };
 
 clib_error_t *
index 748905d..4a3d05b 100644 (file)
@@ -202,6 +202,20 @@ format_mac_address (u8 * s, va_list * args)
                 a[0], a[1], a[2], a[3], a[4], a[5]);
 }
 
+uword
+unformat_nsh_address (unformat_input_t * input, va_list * args)
+{
+  nsh_t *a = va_arg (*args, nsh_t *);
+  return unformat (input, "SPI:%d SI:%d", &a->spi, &a->si);
+}
+
+u8 *
+format_nsh_address (u8 * s, va_list * args)
+{
+  nsh_t *a = va_arg (*args, nsh_t *);
+  return format (s, "SPI:%d SI:%d", a->spi, a->si);
+}
+
 u8 *
 format_fid_address (u8 * s, va_list * args)
 {
@@ -211,9 +225,10 @@ format_fid_address (u8 * s, va_list * args)
     {
     case FID_ADDR_IP_PREF:
       return format (s, "%U", format_ip_prefix, &fid_addr_ippref (a));
-
     case FID_ADDR_MAC:
       return format (s, "%U", format_mac_address, &fid_addr_mac (a));
+    case FID_ADDR_NSH:
+      return format (s, "%U", format_nsh_address, &fid_addr_nsh (a));
 
     default:
       clib_warning ("Can't format fid address type %d!", fid_addr_type (a));
@@ -239,6 +254,8 @@ format_gid_address (u8 * s, va_list * args)
     case GID_ADDR_MAC:
       return format (s, "[%d] %U", gid_address_vni (a), format_mac_address,
                     &gid_address_mac (a));
+    case GID_ADDR_NSH:
+      return format (s, "%U", format_nsh_address, &gid_address_nsh (a));
     default:
       clib_warning ("Can't format gid type %d", type);
       return 0;
@@ -252,6 +269,7 @@ unformat_fid_address (unformat_input_t * i, va_list * args)
   fid_address_t *a = va_arg (*args, fid_address_t *);
   ip_prefix_t ippref;
   u8 mac[6] = { 0 };
+  nsh_t nsh;
 
   if (unformat (i, "%U", unformat_ip_prefix, &ippref))
     {
@@ -263,6 +281,11 @@ unformat_fid_address (unformat_input_t * i, va_list * args)
       fid_addr_type (a) = FID_ADDR_MAC;
       mac_copy (fid_addr_mac (a), mac);
     }
+  else if (unformat (i, "%U", unformat_nsh_address, &nsh))
+    {
+      fid_addr_type (a) = FID_ADDR_NSH;
+      nsh_copy (&fid_addr_nsh (a), mac);
+    }
   else
     return 0;
 
@@ -301,6 +324,7 @@ unformat_gid_address (unformat_input_t * input, va_list * args)
   u8 mac[6] = { 0 };
   ip_prefix_t ippref;
   fid_address_t sim1, sim2;
+  nsh_t nsh;
 
   memset (&ippref, 0, sizeof (ippref));
   memset (&sim1, 0, sizeof (sim1));
@@ -323,6 +347,11 @@ unformat_gid_address (unformat_input_t * input, va_list * args)
       mac_copy (gid_address_mac (a), mac);
       gid_address_type (a) = GID_ADDR_MAC;
     }
+  else if (unformat (input, "%U", unformat_nsh_address, &nsh))
+    {
+      nsh_copy (&gid_address_nsh (a), &nsh);
+      gid_address_type (a) = GID_ADDR_NSH;
+    }
   else
     return 0;
 
@@ -588,6 +617,10 @@ fid_addr_parse (u8 * p, fid_address_t * a)
 
     case FID_ADDR_IP_PREF:
       return ip_address_parse (p, afi, ip_addr);
+
+    case FID_ADDR_NSH:
+      ASSERT (0);
+      break;
     }
   return ~0;
 }
@@ -917,6 +950,12 @@ mac_copy (void *dst, void *src)
   clib_memcpy (dst, src, 6);
 }
 
+void
+nsh_copy (void *dst, void *src)
+{
+  clib_memcpy (dst, src, sizeof (nsh_t));
+}
+
 void
 sd_copy (void *dst, void *src)
 {
@@ -1083,6 +1122,8 @@ fid_address_length (fid_address_t * a)
       return ip_prefix_length (&fid_addr_ippref (a));
     case FID_ADDR_MAC:
       return 0;
+    case FID_ADDR_NSH:
+      return 0;
     }
   return 0;
 }
index ac58b89..e43f5ab 100644 (file)
@@ -89,6 +89,7 @@ typedef enum
   GID_ADDR_LCAF,
   GID_ADDR_MAC,
   GID_ADDR_SRC_DST,
+  GID_ADDR_NSH,
   GID_ADDR_NO_ADDRESS,
   GID_ADDR_TYPES
 } gid_address_type_t;
@@ -106,7 +107,8 @@ typedef enum
 typedef enum fid_addr_type_t_
 {
   FID_ADDR_IP_PREF,
-  FID_ADDR_MAC
+  FID_ADDR_MAC,
+  FID_ADDR_NSH
 } __attribute__ ((packed)) fid_addr_type_t;
 
 /* flat address type */
@@ -116,6 +118,7 @@ typedef struct
   {
     ip_prefix_t ippref;
     u8 mac[6];
+    u32 nsh;
   };
   fid_addr_type_t type;
 } fid_address_t;
@@ -124,6 +127,7 @@ typedef fid_address_t dp_address_t;
 
 #define fid_addr_ippref(_a) (_a)->ippref
 #define fid_addr_mac(_a) (_a)->mac
+#define fid_addr_nsh(_a) (_a)->nsh
 #define fid_addr_type(_a) (_a)->type
 u8 *format_fid_address (u8 * s, va_list * args);
 
@@ -153,6 +157,12 @@ typedef struct
 #define vni_mask_len(_a) (_a)->vni_mask_len
 #define vni_gid(_a) (_a)->gid_addr
 
+typedef struct
+{
+  u32 spi;
+  u8 si;
+} nsh_t;
+
 typedef struct
 {
   /* the union needs to be at the beginning! */
@@ -177,6 +187,7 @@ typedef struct _gid_address_t
     lcaf_t lcaf;
     u8 mac[6];
     source_dest_t sd;
+    nsh_t nsh;
   };
   u8 type;
   u32 vni;
@@ -232,6 +243,7 @@ void gid_address_ip_set (gid_address_t * dst, void *src, u8 version);
 #define gid_address_ip_version(_a) ip_addr_version(&gid_address_ip(_a))
 #define gid_address_lcaf(_a) (_a)->lcaf
 #define gid_address_mac(_a) (_a)->mac
+#define gid_address_nsh(_a) (_a)->nsh
 #define gid_address_vni(_a) (_a)->vni
 #define gid_address_vni_mask(_a) (_a)->vni_mask
 #define gid_address_sd_dst_ippref(_a) sd_dst_ippref(&(_a)->sd)
@@ -249,6 +261,7 @@ void gid_address_ip_set (gid_address_t * dst, void *src, u8 version);
   _(ip_prefix)                    \
   _(lcaf)                         \
   _(mac)                          \
+  _(nsh)                          \
   _(sd)
 
 /* *INDENT-OFF* */
index 3288b24..d12dc36 100644 (file)
@@ -201,7 +201,7 @@ VNET_HW_INTERFACE_CLASS (lisp_gpe_hw_class) = {
 
 typedef struct
 {
-  u32 lb_index;
+  u32 dpo_index;
 } l2_lisp_gpe_tx_trace_t;
 
 static u8 *
@@ -211,7 +211,7 @@ format_l2_lisp_gpe_tx_trace (u8 * s, va_list * args)
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   l2_lisp_gpe_tx_trace_t *t = va_arg (*args, l2_lisp_gpe_tx_trace_t *);
 
-  s = format (s, "L2-LISP-GPE-TX: load-balance %d", t->lb_index);
+  s = format (s, "L2-LISP-GPE-TX: load-balance %d", t->dpo_index);
   return s;
 }
 
@@ -278,7 +278,7 @@ l2_lisp_gpe_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node,
            {
              l2_lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0,
                                                           sizeof (*tr));
-             tr->lb_index = lbi0;
+             tr->dpo_index = lbi0;
            }
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
                                           n_left_to_next, bi0, l2_arc_to_lb);
@@ -306,6 +306,110 @@ VNET_DEVICE_CLASS (l2_lisp_gpe_device_class,static) = {
 };
 /* *INDENT-ON* */
 
+typedef struct
+{
+  u32 dpo_index;
+} nsh_lisp_gpe_tx_trace_t;
+
+u8 *
+format_nsh_lisp_gpe_tx_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  nsh_lisp_gpe_tx_trace_t *t = va_arg (*args, nsh_lisp_gpe_tx_trace_t *);
+
+  s = format (s, "NSH-GPE-TX: tunnel %d", t->dpo_index);
+  return s;
+}
+
+/**
+ * @brief LISP-GPE interface TX for NSH overlays.
+ * @node nsh_lisp_gpe_interface_tx
+ *
+ * The NSH LISP-GPE interface TX function.
+ *
+ * @param[in]   vm        vlib_main_t corresponding to the current thread.
+ * @param[in]   node      vlib_node_runtime_t data for this node.
+ * @param[in]   frame     vlib_frame_t whose contents should be dispatched.
+ *
+ * @return number of vectors in frame.
+ */
+static uword
+nsh_lisp_gpe_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node,
+                          vlib_frame_t * from_frame)
+{
+  u32 n_left_from, next_index, *from, *to_next;
+  lisp_gpe_main_t *lgm = &lisp_gpe_main;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         vlib_buffer_t *b0;
+         u32 bi0;
+         u32 *nsh0, next0;
+         const dpo_id_t *dpo0;
+
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         nsh0 = vlib_buffer_get_current (b0);
+
+         vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_LCAF;
+
+         /* lookup SPI + SI (second word of the NSH header).
+          * NB: Load balancing was done by the control plane */
+         dpo0 = lisp_nsh_fib_lookup (lgm, nsh0[1]);
+
+         next0 = dpo0->dpoi_next_node;
+         vnet_buffer (b0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
+
+         if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             nsh_lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0,
+                                                           sizeof (*tr));
+             tr->dpo_index = dpo0->dpoi_index;
+           }
+         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 from_frame->n_vectors;
+}
+
+static u8 *
+format_nsh_lisp_gpe_name (u8 * s, va_list * args)
+{
+  u32 dev_instance = va_arg (*args, u32);
+  return format (s, "nsh_lisp_gpe%d", dev_instance);
+}
+
+/* *INDENT-OFF* */
+VNET_DEVICE_CLASS (nsh_lisp_gpe_device_class,static) = {
+  .name = "NSH_LISP_GPE",
+  .format_device_name = format_nsh_lisp_gpe_name,
+  .format_tx_trace = format_nsh_lisp_gpe_tx_trace,
+  .tx_function = nsh_lisp_gpe_interface_tx,
+};
+/* *INDENT-ON* */
+
 static vnet_hw_interface_t *
 lisp_gpe_create_iface (lisp_gpe_main_t * lgm, u32 vni, u32 dp_table,
                       vnet_device_class_t * dev_class,
@@ -615,6 +719,72 @@ lisp_gpe_del_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id)
   lisp_gpe_remove_iface (lgm, hip[0], bd_index, &lgm->l2_ifaces);
 }
 
+/**
+ * @brief Add LISP-GPE NSH interface.
+ *
+ * Creates LISP-GPE interface, sets it in L3 mode.
+ *
+ * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
+ * @param[in]   a       Parameters to create interface.
+ *
+ * @return sw_if_index.
+ */
+u32
+lisp_gpe_add_nsh_iface (lisp_gpe_main_t * lgm)
+{
+  vnet_main_t *vnm = lgm->vnet_main;
+  tunnel_lookup_t *nsh_ifaces = &lgm->nsh_ifaces;
+  vnet_hw_interface_t *hi;
+  uword *hip, *si;
+
+  hip = hash_get (nsh_ifaces->hw_if_index_by_dp_table, 0);
+
+  if (hip)
+    {
+      clib_warning ("NSH interface 0 already exists");
+      return ~0;
+    }
+
+  si = hash_get (nsh_ifaces->sw_if_index_by_vni, 0);
+  if (si)
+    {
+      clib_warning ("NSH interface already exists");
+      return ~0;
+    }
+
+  /* create lisp iface and populate tunnel tables */
+  hi = lisp_gpe_create_iface (lgm, 0, 0,
+                             &nsh_lisp_gpe_device_class, &lgm->nsh_ifaces);
+
+  /* enable interface */
+  vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
+                              VNET_SW_INTERFACE_FLAG_ADMIN_UP);
+  vnet_hw_interface_set_flags (vnm, hi->hw_if_index,
+                              VNET_HW_INTERFACE_FLAG_LINK_UP);
+
+  return (hi->sw_if_index);
+}
+
+/**
+ * @brief Del LISP-GPE NSH interface.
+ *
+ */
+void
+lisp_gpe_del_nsh_iface (lisp_gpe_main_t * lgm)
+{
+  tunnel_lookup_t *nsh_ifaces = &lgm->nsh_ifaces;
+  uword *hip;
+
+  hip = hash_get (nsh_ifaces->hw_if_index_by_dp_table, 0);
+
+  if (hip == 0)
+    {
+      clib_warning ("The NSH 0 interface doesn't exist");
+      return;
+    }
+  lisp_gpe_remove_iface (lgm, hip[0], 0, &lgm->nsh_ifaces);
+}
+
 static clib_error_t *
 lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input,
                                   vlib_cli_command_t * cmd)
@@ -623,6 +793,7 @@ lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input,
   u8 is_add = 1;
   u32 table_id, vni, bd_id;
   u8 vni_is_set = 0, vrf_is_set = 0, bd_index_is_set = 0;
+  u8 nsh_iface = 0;
 
   if (vnet_lisp_gpe_enable_disable_status () == 0)
     {
@@ -651,6 +822,10 @@ lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input,
        {
          bd_index_is_set = 1;
        }
+      else if (unformat (line_input, "nsh"))
+       {
+         nsh_iface = 1;
+       }
       else
        {
          return clib_error_return (0, "parse error: '%U'",
@@ -689,6 +864,21 @@ lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input,
        lisp_gpe_tenant_l3_iface_unlock (vni);
     }
 
+  if (nsh_iface)
+    {
+      if (is_add)
+       {
+         if (~0 == lisp_gpe_add_nsh_iface (&lisp_gpe_main))
+           {
+             return clib_error_return (0, "NSH interface not created");
+           }
+         else
+           {
+             lisp_gpe_del_nsh_iface (&lisp_gpe_main);
+           }
+       }
+    }
+
   return (NULL);
 }
 
index e78d45c..e76c03f 100644 (file)
@@ -151,6 +151,7 @@ lisp_gpe_add_del_fwd_entry_command_fn (vlib_main_t * vm,
   gid_address_copy (&a->lcl_eid, leid);
   gid_address_copy (&a->rmt_eid, reid);
   a->locator_pairs = pairs;
+  a->action = action;
 
   rv = vnet_lisp_gpe_add_del_fwd_entry (a, 0);
   if (0 != rv)
@@ -291,7 +292,6 @@ format_vnet_lisp_gpe_status (u8 * s, va_list * args)
   return format (s, "%s", lgm->is_en ? "enabled" : "disabled");
 }
 
-
 /** LISP-GPE init function. */
 clib_error_t *
 lisp_gpe_init (vlib_main_t * vm)
index 3288c99..e92df38 100644 (file)
@@ -119,6 +119,15 @@ typedef struct lisp_gpe_main
   /** Load-balance for a miss in the table */
   dpo_id_t l2_lb_cp_lkup;
 
+  /* NSH data structures
+   * ================== */
+
+    BVT (clib_bihash) nsh_fib;
+
+  tunnel_lookup_t nsh_ifaces;
+
+  const dpo_id_t *nsh_cp_lkup;
+
   /** convenience */
   vlib_main_t *vlib_main;
   vnet_main_t *vnet_main;
index 8c96a25..1dbf867 100644 (file)
@@ -211,6 +211,8 @@ lisp_gpe_adj_proto_from_vnet_link_type (vnet_link_t linkt)
       return (LISP_GPE_NEXT_PROTO_IP6);
     case VNET_LINK_ETHERNET:
       return (LISP_GPE_NEXT_PROTO_ETHERNET);
+    case VNET_LINK_NSH:
+      return (LISP_GPE_NEXT_PROTO_NSH);
     default:
       ASSERT (0);
     }
@@ -254,14 +256,14 @@ lisp_gpe_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
   ladj = pool_elt_at_index (lisp_adj_pool, lai);
   lgt = lisp_gpe_tunnel_get (ladj->tunnel_index);
   linkt = adj_get_link_type (ai);
-
   adj_nbr_midchain_update_rewrite
     (ai, lisp_gpe_fixup,
      (VNET_LINK_ETHERNET == linkt ?
       ADJ_MIDCHAIN_FLAG_NO_COUNT :
       ADJ_MIDCHAIN_FLAG_NONE),
-     lisp_gpe_tunnel_build_rewrite
-     (lgt, ladj, lisp_gpe_adj_proto_from_vnet_link_type (linkt)));
+     lisp_gpe_tunnel_build_rewrite (lgt, ladj,
+                                   lisp_gpe_adj_proto_from_vnet_link_type
+                                   (linkt)));
 
   lisp_gpe_adj_stack_one (ladj, ai);
 }
index 7ad8679..e51b585 100644 (file)
@@ -340,10 +340,14 @@ gid_to_dp_address (gid_address_t * g, dp_address_t * d)
       d->type = FID_ADDR_IP_PREF;
       break;
     case GID_ADDR_MAC:
-    default:
       mac_copy (&d->mac, &gid_address_mac (g));
       d->type = FID_ADDR_MAC;
       break;
+    case GID_ADDR_NSH:
+    default:
+      d->nsh = gid_address_nsh (g).spi << 8 | gid_address_nsh (g).si;
+      d->type = FID_ADDR_NSH;
+      break;
     }
 }
 
@@ -671,7 +675,7 @@ del_l2_fwd_entry (lisp_gpe_main_t * lgm,
 }
 
 /**
- * @brief Construct and insert the forwarding information used by a L2 entry
+ * @brief Construct and insert the forwarding information used by an L2 entry
  */
 static void
 lisp_gpe_l2_update_fwding (lisp_gpe_fwd_entry_t * lfe)
@@ -688,7 +692,16 @@ lisp_gpe_l2_update_fwding (lisp_gpe_fwd_entry_t * lfe)
     }
   else
     {
-      dpo_copy (&dpo, &lgm->l2_lb_cp_lkup);
+      switch (lfe->action)
+       {
+       case SEND_MAP_REQUEST:
+         dpo_copy (&dpo, &lgm->l2_lb_cp_lkup);
+         break;
+       case NO_ACTION:
+       case FORWARD_NATIVE:
+       case DROP:
+         dpo_copy (&dpo, drop_dpo_get (DPO_PROTO_ETHERNET));
+       }
     }
 
   /* add entry to l2 lisp fib */
@@ -784,6 +797,276 @@ add_l2_fwd_entry (lisp_gpe_main_t * lgm,
   return 0;
 }
 
+/**
+ * @brief Lookup NSH SD FIB entry
+ *
+ * Does an SPI+SI lookup in the NSH LISP FIB.
+ *
+ * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
+ * @param[in]   spi_si          SPI + SI.
+ *
+ * @return next node index.
+ */
+const dpo_id_t *
+lisp_nsh_fib_lookup (lisp_gpe_main_t * lgm, u32 spi_si)
+{
+  int rv;
+  BVT (clib_bihash_kv) kv, value;
+
+  memset (&kv, 0, sizeof (kv));
+  kv.key[0] = spi_si;
+  rv = BV (clib_bihash_search_inline_2) (&lgm->nsh_fib, &kv, &value);
+
+  if (rv != 0)
+    {
+      return lgm->nsh_cp_lkup;
+    }
+  else
+    {
+      lisp_gpe_fwd_entry_t *lfe;
+      lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, value.value);
+      return &lfe->nsh.choice;
+    }
+}
+
+/**
+ * @brief Add/del NSH FIB entry
+ *
+ * Inserts value in NSH FIB keyed by SPI+SI. If entry is
+ * overwritten the associated value is returned.
+ *
+ * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
+ * @param[in]   spi_si          SPI + SI.
+ * @param[in]   dpo             Load balanced mapped to SPI + SI
+ *
+ * @return ~0 or value of overwritten entry.
+ */
+static u32
+lisp_nsh_fib_add_del_entry (u32 spi_si, u32 lfei, u8 is_add)
+{
+  lisp_gpe_main_t *lgm = &lisp_gpe_main;
+  BVT (clib_bihash_kv) kv, value;
+  u32 old_val = ~0;
+
+  memset (&kv, 0, sizeof (kv));
+  kv.key[0] = spi_si;
+  kv.value = 0ULL;
+
+  if (BV (clib_bihash_search) (&lgm->nsh_fib, &kv, &value) == 0)
+    old_val = value.value;
+
+  if (!is_add)
+    BV (clib_bihash_add_del) (&lgm->nsh_fib, &kv, 0 /* is_add */ );
+  else
+    {
+      kv.value = lfei;
+      BV (clib_bihash_add_del) (&lgm->nsh_fib, &kv, 1 /* is_add */ );
+    }
+  return old_val;
+}
+
+#define NSH_FIB_DEFAULT_HASH_NUM_BUCKETS (64 * 1024)
+#define NSH_FIB_DEFAULT_HASH_MEMORY_SIZE (32<<20)
+
+static void
+nsh_fib_init (lisp_gpe_main_t * lgm)
+{
+  BV (clib_bihash_init) (&lgm->nsh_fib, "nsh fib",
+                        1 << max_log2 (NSH_FIB_DEFAULT_HASH_NUM_BUCKETS),
+                        NSH_FIB_DEFAULT_HASH_MEMORY_SIZE);
+
+  /*
+   * the result from a 'miss' in a NSH Table
+   */
+  lgm->nsh_cp_lkup = lisp_cp_dpo_get (DPO_PROTO_NSH);
+}
+
+static void
+del_nsh_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe)
+{
+  lisp_fwd_path_t *path;
+
+  if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
+    {
+      vec_foreach (path, lfe->paths)
+      {
+       lisp_gpe_adjacency_unlock (path->lisp_adj);
+      }
+      fib_path_list_child_remove (lfe->nsh.path_list_index,
+                                 lfe->nsh.child_index);
+      dpo_reset (&lfe->nsh.choice);
+    }
+
+  lisp_nsh_fib_add_del_entry (fid_addr_nsh (&lfe->key->rmt), (u32) ~ 0, 0);
+
+  hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key);
+  clib_mem_free (lfe->key);
+  pool_put (lgm->lisp_fwd_entry_pool, lfe);
+}
+
+/**
+ * @brief Delete LISP NSH forwarding entry.
+ *
+ * Coordinates the removal of forwarding entries for NSH LISP overlay:
+ *
+ * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
+ * @param[in]   a       Parameters for building the forwarding entry.
+ *
+ * @return 0 on success.
+ */
+static int
+del_nsh_fwd_entry (lisp_gpe_main_t * lgm,
+                  vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
+{
+  lisp_gpe_fwd_entry_key_t key;
+  lisp_gpe_fwd_entry_t *lfe;
+
+  lfe = find_fwd_entry (lgm, a, &key);
+
+  if (NULL == lfe)
+    return VNET_API_ERROR_INVALID_VALUE;
+
+  del_nsh_fwd_entry_i (lgm, lfe);
+
+  return (0);
+}
+
+/**
+ * @brief Construct and insert the forwarding information used by an NSH entry
+ */
+static void
+lisp_gpe_nsh_update_fwding (lisp_gpe_fwd_entry_t * lfe)
+{
+  lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main ();
+  dpo_id_t dpo = DPO_INVALID;
+  vnet_hw_interface_t *hi;
+  uword *hip;
+
+  if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
+    {
+      fib_path_list_contribute_forwarding (lfe->nsh.path_list_index,
+                                          FIB_FORW_CHAIN_TYPE_NSH,
+                                          &lfe->nsh.dpo);
+
+      /*
+       * LISP encap is always the same for this SPI+SI so we do that hash now
+       * and stack on the choice.
+       */
+      if (DPO_LOAD_BALANCE == lfe->nsh.dpo.dpoi_type)
+       {
+         const dpo_id_t *tmp;
+         const load_balance_t *lb;
+         int hash;
+
+         lb = load_balance_get (lfe->nsh.dpo.dpoi_index);
+         hash = fid_addr_nsh (&lfe->key->rmt) % lb->lb_n_buckets;
+         tmp =
+           load_balance_get_bucket_i (lb, hash & lb->lb_n_buckets_minus_1);
+
+         dpo_copy (&dpo, tmp);
+       }
+    }
+  else
+    {
+      switch (lfe->action)
+       {
+       case SEND_MAP_REQUEST:
+         dpo_copy (&dpo, lgm->nsh_cp_lkup);
+         break;
+       case NO_ACTION:
+       case FORWARD_NATIVE:
+       case DROP:
+         dpo_copy (&dpo, drop_dpo_get (DPO_PROTO_NSH));
+       }
+    }
+
+  /* We have only one nsh-lisp interface (no NSH virtualization) */
+  hip = hash_get (lgm->nsh_ifaces.hw_if_index_by_dp_table, 0);
+  hi = vnet_get_hw_interface (lgm->vnet_main, hip[0]);
+
+  dpo_stack_from_node (hi->tx_node_index, &lfe->nsh.choice, &dpo);
+
+  /* add entry to nsh lisp fib */
+  lisp_nsh_fib_add_del_entry (fid_addr_nsh (&lfe->key->rmt),
+                             lfe - lgm->lisp_fwd_entry_pool, 1);
+
+  dpo_reset (&dpo);
+}
+
+/**
+ * @brief Add LISP NSH forwarding entry.
+ *
+ * Coordinates the creation of forwarding entries for L2 LISP overlay:
+ * creates lisp-gpe tunnel and injects new entry in Source/Dest L2 FIB.
+ *
+ * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
+ * @param[in]   a       Parameters for building the forwarding entry.
+ *
+ * @return 0 on success.
+ */
+static int
+add_nsh_fwd_entry (lisp_gpe_main_t * lgm,
+                  vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
+{
+  lisp_gpe_fwd_entry_key_t key;
+  lisp_gpe_fwd_entry_t *lfe;
+
+  lfe = find_fwd_entry (lgm, a, &key);
+
+  if (NULL != lfe)
+    /* don't support updates */
+    return VNET_API_ERROR_INVALID_VALUE;
+
+  pool_get (lgm->lisp_fwd_entry_pool, lfe);
+  memset (lfe, 0, sizeof (*lfe));
+  lfe->key = clib_mem_alloc (sizeof (key));
+  memcpy (lfe->key, &key, sizeof (key));
+
+  hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key,
+               lfe - lgm->lisp_fwd_entry_pool);
+
+  lfe->type = (a->is_negative ?
+              LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE :
+              LISP_GPE_FWD_ENTRY_TYPE_NORMAL);
+  lfe->tenant = 0;
+
+  if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
+    {
+      fib_route_path_t *rpaths;
+
+      /*
+       * Make the sorted array of LISP paths with their resp. adjacency
+       */
+      lisp_gpe_fwd_entry_mk_paths (lfe, a);
+
+      /*
+       * From the LISP paths, construct a FIB path list that will
+       * contribute a load-balance.
+       */
+      rpaths = lisp_gpe_mk_fib_paths (lfe->paths);
+
+      lfe->nsh.path_list_index =
+       fib_path_list_create (FIB_PATH_LIST_FLAG_NONE, rpaths);
+
+      /*
+       * become a child of the path-list so we receive updates when
+       * its forwarding state changes. this includes an implicit lock.
+       */
+      lfe->nsh.child_index =
+       fib_path_list_child_add (lfe->nsh.path_list_index,
+                                FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY,
+                                lfe - lgm->lisp_fwd_entry_pool);
+    }
+  else
+    {
+      lfe->action = a->action;
+    }
+
+  lisp_gpe_nsh_update_fwding (lfe);
+
+  return 0;
+}
+
 /**
  * @brief conver from the embedded fib_node_t struct to the LSIP entry
  */
@@ -802,7 +1085,12 @@ static fib_node_back_walk_rc_t
 lisp_gpe_fib_node_back_walk (fib_node_t * node,
                             fib_node_back_walk_ctx_t * ctx)
 {
-  lisp_gpe_l2_update_fwding (lisp_gpe_fwd_entry_from_fib_node (node));
+  lisp_gpe_fwd_entry_t *lfe = lisp_gpe_fwd_entry_from_fib_node (node);
+
+  if (fid_addr_type (&lfe->key->rmt) == FID_ADDR_MAC)
+    lisp_gpe_l2_update_fwding (lfe);
+  else if (fid_addr_type (&lfe->key->rmt) == FID_ADDR_NSH)
+    lisp_gpe_nsh_update_fwding (lfe);
 
   return (FIB_NODE_BACK_WALK_CONTINUE);
 }
@@ -877,6 +1165,11 @@ vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a,
        return add_l2_fwd_entry (lgm, a);
       else
        return del_l2_fwd_entry (lgm, a);
+    case GID_ADDR_NSH:
+      if (a->is_add)
+       return add_nsh_fwd_entry (lgm, a);
+      else
+       return del_nsh_fwd_entry (lgm, a);
     default:
       clib_warning ("Forwarding entries for type %d not supported!", type);
       return -1;
@@ -903,6 +1196,9 @@ vnet_lisp_gpe_fwd_entry_flush (void)
       case FID_ADDR_IP_PREF:
        del_ip_fwd_entry_i (lgm, lfe);
        break;
+      case FID_ADDR_NSH:
+        del_nsh_fwd_entry_i (lgm, lfe);
+        break;
       }
   }));
   /* *INDENT-ON* */
@@ -967,6 +1263,10 @@ format_lisp_gpe_fwd_entry (u8 * s, va_list ap)
          s = format (s, " fib-path-list:%d\n", lfe->l2.path_list_index);
          s = format (s, " dpo:%U\n", format_dpo_id, &lfe->l2.dpo, 0);
          break;
+       case FID_ADDR_NSH:
+         s = format (s, " fib-path-list:%d\n", lfe->nsh.path_list_index);
+         s = format (s, " dpo:%U\n", format_dpo_id, &lfe->nsh.dpo, 0);
+         break;
        case FID_ADDR_IP_PREF:
          break;
        }
@@ -1036,6 +1336,7 @@ lisp_gpe_fwd_entry_init (vlib_main_t * vm)
     return (error);
 
   l2_fib_init (lgm);
+  nsh_fib_init (lgm);
 
   fib_node_register_type (FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, &lisp_fwd_vft);
 
index f792367..d58895a 100644 (file)
@@ -81,7 +81,7 @@ typedef struct lisp_gpe_fwd_entry_t_
   fib_node_t node;
 
   /**
-   * The Entry's key: {lEID,r-EID,vni}
+   * The Entry's key: {lEID,rEID,vni}
    */
   lisp_gpe_fwd_entry_key_t *key;
 
@@ -150,6 +150,33 @@ typedef struct lisp_gpe_fwd_entry_t_
        */
       dpo_id_t dpo;
     } l2;
+
+    /**
+     * Fields relevant to an NSH entry
+     */
+    struct
+    {
+      /**
+       * The path-list created for the forwarding
+       */
+      fib_node_index_t path_list_index;
+
+      /**
+       * Child index of this entry on the path-list
+       */
+      u32 child_index;
+
+      /**
+       * The DPO contributed by NSH
+       */
+      dpo_id_t dpo;
+
+      /**
+       * The DPO used for forwarding. Obtained after stacking tx node
+       * onto lb choice
+       */
+      dpo_id_t choice;
+    } nsh;
   };
 
   union
@@ -177,6 +204,8 @@ extern void vnet_lisp_gpe_fwd_entry_flush (void);
 extern u32 lisp_l2_fib_lookup (lisp_gpe_main_t * lgm,
                               u16 bd_index, u8 src_mac[8], u8 dst_mac[8]);
 
+extern const dpo_id_t *lisp_nsh_fib_lookup (lisp_gpe_main_t * lgm,
+                                           u32 spi_si);
 #endif
 
 /*
index f1b6e8e..acbe90b 100644 (file)
@@ -465,6 +465,7 @@ mfib_entry_src_collect_forwarding (fib_node_index_t pl_index,
     case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS:
     case FIB_FORW_CHAIN_TYPE_MPLS_EOS:
     case FIB_FORW_CHAIN_TYPE_ETHERNET:
+    case FIB_FORW_CHAIN_TYPE_NSH:
         ASSERT(0);
         break;
     }