ip: support flow-hash gtpv1teid

support with  GTPv1 TEID added to the flow hash.
This can able to ECMP to PGW and parallelization.
Type: feature

Change-Id: I6f758579027caf6123831ef2db7afe17e424a6eb
Signed-off-by: Takeru Hayasaka <hayatake396@gmail.com>
diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api
index 8a6ecc8..9ee2cc4 100644
--- a/src/vnet/ip/ip.api
+++ b/src/vnet/ip/ip.api
@@ -366,6 +366,41 @@
   vl_api_ip_flow_hash_config_t flow_hash_config;
 };
 
+/**
+    @brief flow hash settings for an IP table
+    @param src - include src in flow hash
+    @param dst - include dst in flow hash
+    @param sport - include sport in flow hash
+    @param dport - include dport in flow hash
+    @param proto - include proto in flow hash
+    @param reverse - include reverse in flow hash
+    @param symmetric - include symmetry in flow hash
+    @param flowlabel - include flowlabel in flow hash
+    @param gtpv1teid - include gtpv1teid in flow hash
+*/
+enumflag ip_flow_hash_config_v2
+{
+  IP_API_V2_FLOW_HASH_SRC_IP = 0x01,
+  IP_API_V2_FLOW_HASH_DST_IP = 0x02,
+  IP_API_V2_FLOW_HASH_SRC_PORT = 0x04,
+  IP_API_V2_FLOW_HASH_DST_PORT = 0x08,
+  IP_API_V2_FLOW_HASH_PROTO = 0x10,
+  IP_API_V2_FLOW_HASH_REVERSE = 0x20,
+  IP_API_V2_FLOW_HASH_SYMETRIC = 0x40,
+  IP_API_V2_FLOW_HASH_FLOW_LABEL = 0x80,
+  IP_API_V2_FLOW_HASH_GTPV1_TEID = 0x100,
+};
+
+autoreply define set_ip_flow_hash_v3
+{
+  u32 client_index;
+  u32 context;
+  u32 table_id;
+  vl_api_address_family_t af;
+  vl_api_ip_flow_hash_config_v2_t flow_hash_config;
+  option status="in_progress";
+};
+
 /** \brief Set the ip flow hash router ID
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
diff --git a/src/vnet/ip/ip4_forward.c b/src/vnet/ip/ip4_forward.c
index 35ebaa4..b3c9ff7 100644
--- a/src/vnet/ip/ip4_forward.c
+++ b/src/vnet/ip/ip4_forward.c
@@ -2834,11 +2834,10 @@
  * @cliexend
 ?*/
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (set_ip_flow_hash_command, static) =
-{
+VLIB_CLI_COMMAND (set_ip_flow_hash_command, static) = {
   .path = "set ip flow-hash",
-  .short_help =
-  "set ip flow-hash table <table-id> [src] [dst] [sport] [dport] [proto] [reverse]",
+  .short_help = "set ip flow-hash table <table-id> [src] [dst] [sport] "
+		"[dport] [proto] [reverse] [gtpv1teid]",
   .function = set_ip_flow_hash_command_fn,
 };
 /* *INDENT-ON* */
diff --git a/src/vnet/ip/ip4_inlines.h b/src/vnet/ip/ip4_inlines.h
index ca7327f..b4fcebc 100644
--- a/src/vnet/ip/ip4_inlines.h
+++ b/src/vnet/ip/ip4_inlines.h
@@ -43,6 +43,7 @@
 #include <vnet/ip/ip_flow_hash.h>
 #include <vnet/ip/ip4_packet.h>
 #include <vnet/tcp/tcp_packet.h>
+#include <vnet/udp/udp_packet.h>
 
 #define IP_DF 0x4000		/* don't fragment */
 
@@ -53,9 +54,11 @@
 		       flow_hash_config_t flow_hash_config)
 {
   tcp_header_t *tcp = (void *) (ip + 1);
+  udp_header_t *udp = (void *) (ip + 1);
+  gtpv1u_header_t *gtpu = (void *) (udp + 1);
   u32 a, b, c, t1, t2;
-  uword is_tcp_udp = (ip->protocol == IP_PROTOCOL_TCP
-		      || ip->protocol == IP_PROTOCOL_UDP);
+  uword is_udp = ip->protocol == IP_PROTOCOL_UDP;
+  uword is_tcp_udp = (ip->protocol == IP_PROTOCOL_TCP || is_udp);
 
   t1 = (flow_hash_config & IP_FLOW_HASH_SRC_ADDR)
     ? ip->src_address.data_u32 : 0;
@@ -90,6 +93,13 @@
   b ^= (flow_hash_config & IP_FLOW_HASH_PROTO) ? ip->protocol : 0;
   c = (flow_hash_config & IP_FLOW_HASH_REVERSE_SRC_DST) ?
     (t1 << 16) | t2 : (t2 << 16) | t1;
+  if (PREDICT_TRUE (is_udp) &&
+      PREDICT_FALSE ((flow_hash_config & IP_FLOW_HASH_GTPV1_TEID) &&
+		     udp->dst_port == GTPV1_PORT_BE))
+    {
+      t1 = gtpu->teid;
+      c ^= t1;
+    }
   a ^= ip_flow_hash_router_id;
 
   hash_v3_mix32 (a, b, c);
diff --git a/src/vnet/ip/ip6_inlines.h b/src/vnet/ip/ip6_inlines.h
index 9c2be60..4a2b91b 100644
--- a/src/vnet/ip/ip6_inlines.h
+++ b/src/vnet/ip/ip6_inlines.h
@@ -50,14 +50,16 @@
 		       flow_hash_config_t flow_hash_config)
 {
   tcp_header_t *tcp;
+  udp_header_t *udp = (void *) (ip + 1);
+  gtpv1u_header_t *gtpu = (void *) (udp + 1);
   u64 a, b, c;
   u64 t1, t2;
+  u32 t3;
   uword is_tcp_udp = 0;
+  uword is_udp = ip->protocol == IP_PROTOCOL_UDP;
   u8 protocol = ip->protocol;
 
-  if (PREDICT_TRUE
-      ((ip->protocol == IP_PROTOCOL_TCP)
-       || (ip->protocol == IP_PROTOCOL_UDP)))
+  if (PREDICT_TRUE ((ip->protocol == IP_PROTOCOL_TCP) || is_udp))
     {
       is_tcp_udp = 1;
       tcp = (void *) (ip + 1);
@@ -113,7 +115,13 @@
     ((flow_hash_config & IP_FLOW_HASH_FL) ? ip6_flow_label_network_order (ip) :
 					    0);
   c ^= t1;
-
+  if (PREDICT_TRUE (is_udp) &&
+      PREDICT_FALSE ((flow_hash_config & IP_FLOW_HASH_GTPV1_TEID) &&
+		     udp->dst_port == GTPV1_PORT_BE))
+    {
+      t3 = gtpu->teid;
+      a ^= t3;
+    }
   hash_mix64 (a, b, c);
   return (u32) c;
 }
diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c
index e03b010..667ea4c 100644
--- a/src/vnet/ip/ip_api.c
+++ b/src/vnet/ip/ip_api.c
@@ -1298,6 +1298,22 @@
 }
 
 static void
+vl_api_set_ip_flow_hash_v3_t_handler (vl_api_set_ip_flow_hash_v3_t *mp)
+{
+  vl_api_set_ip_flow_hash_v3_reply_t *rmp;
+  ip_address_family_t af;
+  int rv;
+
+  rv = ip_address_family_decode (mp->af, &af);
+
+  if (!rv)
+    rv = ip_flow_hash_set (af, htonl (mp->table_id),
+			   htonl (mp->flow_hash_config));
+
+  REPLY_MACRO (VL_API_SET_IP_FLOW_HASH_V3_REPLY);
+}
+
+static void
 vl_api_set_ip_flow_hash_router_id_t_handler (
   vl_api_set_ip_flow_hash_router_id_t *mp)
 {
diff --git a/src/vnet/ip/ip_flow_hash.h b/src/vnet/ip/ip_flow_hash.h
index bd37ef7..30dfcd7 100644
--- a/src/vnet/ip/ip_flow_hash.h
+++ b/src/vnet/ip/ip_flow_hash.h
@@ -38,7 +38,17 @@
   _ (proto, 4, IP_FLOW_HASH_PROTO)                                            \
   _ (reverse, 5, IP_FLOW_HASH_REVERSE_SRC_DST)                                \
   _ (symmetric, 6, IP_FLOW_HASH_SYMMETRIC)                                    \
-  _ (flowlabel, 7, IP_FLOW_HASH_FL)
+  _ (flowlabel, 7, IP_FLOW_HASH_FL)                                           \
+  _ (gtpv1teid, 8, IP_FLOW_HASH_GTPV1_TEID)
+
+typedef struct
+{
+  u8 ver_flags;
+  u8 type;
+  u16 length;
+  u32 teid;
+} __attribute__ ((packed)) gtpv1u_header_t;
+#define GTPV1_PORT_BE 0x6808
 
 /**
  * A flow hash configuration is a mask of the flow hash options
diff --git a/src/vnet/ip/ip_test.c b/src/vnet/ip/ip_test.c
index 7c99486..727afba 100644
--- a/src/vnet/ip/ip_test.c
+++ b/src/vnet/ip/ip_test.c
@@ -1277,6 +1277,12 @@
 }
 
 static int
+api_set_ip_flow_hash_v3 (vat_main_t *vat)
+{
+  return -1;
+}
+
+static int
 api_ip_mroute_add_del (vat_main_t *vam)
 {
   unformat_input_t *i = vam->input;
diff --git a/src/vnet/ip/lookup.c b/src/vnet/ip/lookup.c
index 26bdaa6..5ac2a9c 100644
--- a/src/vnet/ip/lookup.c
+++ b/src/vnet/ip/lookup.c
@@ -145,13 +145,13 @@
     {
       if (unformat (input, "%_,"))
 	;
-#define _(a, b)                                                               \
+#define _(a, b, c)                                                            \
   else if (unformat (input, "%_" #a))                                         \
   {                                                                           \
-    *flow_hash_config |= b;                                                   \
+    *flow_hash_config |= c;                                                   \
     matched_once = 1;                                                         \
   }
-      foreach_flow_hash_bit_v1
+      foreach_flow_hash_bit
 #undef _
 	else
       {