Optimize GRE Tunnel and add support for ERSPAN encap
[vpp.git] / src / vnet / gre / gre.h
index 83bab76..b3b0b54 100644 (file)
@@ -35,45 +35,56 @@ typedef enum
   GRE_N_ERROR,
 } gre_error_t;
 
-/**
- * A GRE payload protocol registration
- */
-typedef struct
-{
-  /** Name (a c string). */
-  char *name;
-
-  /** GRE protocol type in host byte order. */
-  gre_protocol_t protocol;
-
-  /** Node which handles this type. */
-  u32 node_index;
-
-  /** Next index for this type. */
-  u32 next_index;
-} gre_protocol_info_t;
-
 /**
  * @brief The GRE tunnel type
  */
-typedef enum gre_tunnel_tyoe_t_
+typedef enum gre_tunnel_type_t_
 {
   /**
    * L3 GRE (i.e. this tunnel is in L3 mode)
    */
-  GRE_TUNNEL_TYPE_L3,
+  GRE_TUNNEL_TYPE_L3 = 0,
   /**
    * Transparent Ethernet Bridging - the tunnel is in L2 mode
    */
-  GRE_TUNNEL_TYPE_TEB,
+  GRE_TUNNEL_TYPE_TEB = 1,
+  /**
+   * ERSPAN type 2 - the tunnel is for port mirror SPAN output. Each tunnel is
+   * associated with a session ID and expected to be used for encap and output
+   * of mirrored packet from a L2 network only. There is no support for
+   * receiving ERSPAN packets from a GRE ERSPAN tunnel in VPP.
+   */
+  GRE_TUNNEL_TYPE_ERSPAN = 2,
+
+  GRE_TUNNEL_TYPE_N
 } gre_tunnel_type_t;
 
 #define GRE_TUNNEL_TYPE_NAMES {    \
     [GRE_TUNNEL_TYPE_L3] = "L3",   \
     [GRE_TUNNEL_TYPE_TEB] = "TEB", \
+    [GRE_TUNNEL_TYPE_ERSPAN] = "ERSPAN", \
 }
 
-#define GRE_TUNNEL_N_TYPES ((gre_tunnel_type_t)GRE_TUNNEL_TYPE_TEB+1)
+/**
+ * A GRE payload protocol registration
+ */
+typedef struct
+{
+  /** Name (a c string). */
+  char *name;
+
+  /** GRE protocol type in host byte order. */
+  gre_protocol_t protocol;
+
+  /** GRE tunnel type */
+  gre_tunnel_type_t tunnel_type;
+
+  /** Node which handles this type. */
+  u32 node_index;
+
+  /** Next index for this type. */
+  u32 next_index;
+} gre_protocol_info_t;
 
 /**
  * @brief Key for a IPv4 GRE Tunnel
@@ -94,11 +105,12 @@ typedef struct gre_tunnel_key4_t_
   };
 
   /**
-   * The FIB table the src,dst addresses are in.
-   * tunnels with the same IP addresses in different FIBs are not
-   * the same tunnel
+   * FIB table index, ERSPAN session ID and tunnel type in u32 bit fields:
+   * - The FIB table index the src,dst addresses are in, top 20 bits
+   * - The Session ID for ERSPAN tunnel type and 0 otherwise, next 10 bits
+   * - Tunnel type, bottom 2 bits
    */
-  u32 gtk_fib_index;
+  u32 gtk_fidx_ssid_type;
 } __attribute__ ((packed)) gre_tunnel_key4_t;
 
 /**
@@ -114,13 +126,22 @@ typedef struct gre_tunnel_key6_t_
   ip6_address_t gtk_dst;
 
   /**
-   * The FIB table the src,dst addresses are in.
-   * tunnels with the same IP addresses in different FIBs are not
-   * the same tunnel
+   * FIB table index, ERSPAN session ID and tunnel type in u32 bit fields:
+   * - The FIB table index the src,dst addresses are in, top 20 bits
+   * - The Session ID for ERSPAN tunnel type and 0 otherwise, next 10 bits
+   * - Tunnel type, bottom 2 bits
    */
-  u32 gtk_fib_index;
+  u32 gtk_fidx_ssid_type;
 } __attribute__ ((packed)) gre_tunnel_key6_t;
 
+#define GTK_FIB_INDEX_SHIFT    12
+#define GTK_FIB_INDEX_MASK     0xfffff000
+#define GTK_TYPE_SHIFT         0
+#define GTK_TYPE_MASK          0x3
+#define GTK_SESSION_ID_SHIFT   2
+#define GTK_SESSION_ID_MASK    0xffc
+#define GTK_SESSION_ID_MAX     (GTK_SESSION_ID_MASK >> GTK_SESSION_ID_SHIFT)
+
 /**
  * Union of the two possible key types
  */
@@ -130,6 +151,25 @@ typedef union gre_tunnel_key_t_
   gre_tunnel_key6_t gtk_v6;
 } gre_tunnel_key_t;
 
+/**
+ * Used for GRE header seq number generation for ERSPAN encap
+ */
+typedef struct
+{
+  u32 seq_num;
+  u32 ref_count;
+} gre_sn_t;
+
+/**
+ * Hash key for GRE header seq number generation for ERSPAN encap
+ */
+typedef struct
+{
+  ip46_address_t src;
+  ip46_address_t dst;
+  u32 fib_index;
+} gre_sn_key_t;
+
 /**
  * @brief A representation of a GRE tunnel
  */
@@ -176,16 +216,34 @@ typedef struct
   u32 sibling_index;
 
   /**
-   * on a L2 tunnel this is the VLIB arc from the L2-tx to the l2-midchain
+   * an L2 tunnel always rquires an L2 midchain. cache here for DP.
    */
-  u32 l2_tx_arc;
+  adj_index_t l2_adj_index;
 
   /**
-   * an L2 tunnel always rquires an L2 midchain. cache here for DP.
+   * ERSPAN type 2 session ID, least significant 10 bits of u16
    */
-  adj_index_t l2_adj_index;
+  u16 session_id;
+
+  /**
+   * GRE header sequence number (SN) used for ERSPAN type 2 header, must be
+   * bumped automically to be thread safe. As multiple GRE tunnels are created
+   * for the same fib-idx/DIP/SIP with different ERSPAN session number, they all
+   * share the same SN which is kept per FIB/DIP/SIP, as specified by RFC2890.
+   */
+  gre_sn_t *gre_sn;
+
+
+  u32 dev_instance;            /* Real device instance in tunnel vector */
+  u32 user_instance;           /* Instance name being shown to user */
 } gre_tunnel_t;
 
+typedef struct
+{
+  u8 next_index;
+  u8 tunnel_type;
+} next_info_t;
+
 /**
  * @brief GRE related global data
  */
@@ -207,21 +265,19 @@ typedef struct
   uword *protocol_info_by_name, *protocol_info_by_protocol;
 
   /**
-   * Hash mapping ipv4 src/dst addr pair to tunnel
+   * Hash mapping to tunnels with ipv4 src/dst addr
    */
   uword *tunnel_by_key4;
 
   /**
-   * Hash mapping ipv6 src/dst addr pair to tunnel
+   * Hash mapping to tunnels with ipv6 src/dst addr
    */
   uword *tunnel_by_key6;
 
   /**
-   * Free vlib hw_if_indices.
-   * A free list per-tunnel type since the interfaces ctreated are fo different
-   * types and we cannot change the type.
+   * Hash mapping tunnel src/dst addr and fib-idx to sequence number
    */
-  u32 *free_gre_tunnel_hw_if_indices[GRE_TUNNEL_N_TYPES];
+  uword *seq_num_by_key;
 
   /**
    * Mapping from sw_if_index to tunnel index
@@ -230,11 +286,14 @@ typedef struct
 
   /* Sparse vector mapping gre protocol in network byte order
      to next index. */
-  u16 *next_by_protocol;
+  next_info_t *next_by_protocol;
 
   /* convenience */
   vlib_main_t *vlib_main;
   vnet_main_t *vnet_main;
+
+  /* Record used instances */
+  uword *instance_used;
 } gre_main_t;
 
 /**
@@ -266,11 +325,6 @@ gre_get_protocol_info (gre_main_t * em, gre_protocol_t protocol)
 
 extern gre_main_t gre_main;
 
-/* Register given node index to take input for given gre type. */
-void
-gre_register_input_type (vlib_main_t * vm,
-                        gre_protocol_t protocol, u32 node_index);
-
 extern clib_error_t *gre_interface_admin_up_down (vnet_main_t * vnm,
                                                  u32 hw_if_index, u32 flags);
 
@@ -284,8 +338,8 @@ format_function_t format_gre_header_with_length;
 
 extern vlib_node_registration_t gre4_input_node;
 extern vlib_node_registration_t gre6_input_node;
+extern vlib_node_registration_t gre_encap_node;
 extern vnet_device_class_t gre_device_class;
-extern vnet_device_class_t gre_device_teb_class;
 
 /* Parse gre protocol as 0xXXXX or protocol name.
    In either host or network byte order. */
@@ -297,8 +351,8 @@ unformat_function_t unformat_gre_header;
 unformat_function_t unformat_pg_gre_header;
 
 void
-gre_register_input_protocol (vlib_main_t * vm,
-                            gre_protocol_t protocol, u32 node_index);
+gre_register_input_protocol (vlib_main_t * vm, gre_protocol_t protocol,
+                            u32 node_index, gre_tunnel_type_t tunnel_type);
 
 /* manually added to the interface output node in gre.c */
 #define GRE_OUTPUT_NEXT_LOOKUP 1
@@ -306,24 +360,26 @@ gre_register_input_protocol (vlib_main_t * vm,
 typedef struct
 {
   u8 is_add;
-
-  ip46_address_t src, dst;
+  u8 tunnel_type;
   u8 is_ipv6;
+  u32 instance;
+  ip46_address_t src, dst;
   u32 outer_fib_id;
-  u8 teb;
+  u16 session_id;
 } vnet_gre_add_del_tunnel_args_t;
 
 int vnet_gre_add_del_tunnel
   (vnet_gre_add_del_tunnel_args_t * a, u32 * sw_if_indexp);
 
 static inline void
-gre_mk_key4 (const ip4_address_t * src,
-            const ip4_address_t * dst,
-            u32 fib_index, gre_tunnel_key4_t * key)
+gre_mk_key4 (ip4_address_t src,
+            ip4_address_t dst,
+            u32 fib_index, u8 ttype, u16 session_id, gre_tunnel_key4_t * key)
 {
-  key->gtk_src = *src;
-  key->gtk_dst = *dst;
-  key->gtk_fib_index = fib_index;
+  key->gtk_src = src;
+  key->gtk_dst = dst;
+  key->gtk_fidx_ssid_type = ttype |
+    (fib_index << GTK_FIB_INDEX_SHIFT) | (session_id << GTK_SESSION_ID_SHIFT);
 }
 
 static inline int
@@ -331,17 +387,18 @@ gre_match_key4 (const gre_tunnel_key4_t * key1,
                const gre_tunnel_key4_t * key2)
 {
   return ((key1->gtk_as_u64 == key2->gtk_as_u64) &&
-         (key1->gtk_fib_index == key2->gtk_fib_index));
+         (key1->gtk_fidx_ssid_type == key2->gtk_fidx_ssid_type));
 }
 
 static inline void
 gre_mk_key6 (const ip6_address_t * src,
             const ip6_address_t * dst,
-            u32 fib_index, gre_tunnel_key6_t * key)
+            u32 fib_index, u8 ttype, u16 session_id, gre_tunnel_key6_t * key)
 {
   key->gtk_src = *src;
   key->gtk_dst = *dst;
-  key->gtk_fib_index = fib_index;
+  key->gtk_fidx_ssid_type = ttype |
+    (fib_index << GTK_FIB_INDEX_SHIFT) | (session_id << GTK_SESSION_ID_SHIFT);
 }
 
 static inline int
@@ -352,7 +409,15 @@ gre_match_key6 (const gre_tunnel_key6_t * key1,
          (key1->gtk_src.as_u64[1] == key2->gtk_src.as_u64[1]) &&
          (key1->gtk_dst.as_u64[0] == key2->gtk_dst.as_u64[0]) &&
          (key1->gtk_dst.as_u64[1] == key2->gtk_dst.as_u64[1]) &&
-         (key1->gtk_fib_index == key2->gtk_fib_index));
+         (key1->gtk_fidx_ssid_type == key2->gtk_fidx_ssid_type));
+}
+
+static inline void
+gre_mk_sn_key (const gre_tunnel_t * gt, gre_sn_key_t * key)
+{
+  key->src = gt->tunnel_src;
+  key->dst = gt->tunnel_dst.fp_addr;
+  key->fib_index = gt->outer_fib_index;
 }
 
 #endif /* included_gre_h */