ip: support flow-hash gtpv1teid 39/37939/5
authorTakeru Hayasaka <hayatake396@gmail.com>
Mon, 16 Jan 2023 19:45:58 +0000 (04:45 +0900)
committerNeale Ranns <neale@graphiant.com>
Fri, 31 Mar 2023 06:04:42 +0000 (06:04 +0000)
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>
src/vnet/ip/ip.api
src/vnet/ip/ip4_forward.c
src/vnet/ip/ip4_inlines.h
src/vnet/ip/ip6_inlines.h
src/vnet/ip/ip_api.c
src/vnet/ip/ip_flow_hash.h
src/vnet/ip/ip_test.c
src/vnet/ip/lookup.c
test/test_ip4.py

index 8a6ecc8..9ee2cc4 100644 (file)
@@ -366,6 +366,41 @@ autoreply define set_ip_flow_hash_v2
   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
index 35ebaa4..b3c9ff7 100644 (file)
@@ -2834,11 +2834,10 @@ set_ip_flow_hash_command_fn (vlib_main_t * vm,
  * @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* */
index ca7327f..b4fcebc 100644 (file)
@@ -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 @@ ip4_compute_flow_hash (const ip4_header_t * ip,
                       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 @@ ip4_compute_flow_hash (const ip4_header_t * ip,
   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);
index 9c2be60..4a2b91b 100644 (file)
@@ -50,14 +50,16 @@ ip6_compute_flow_hash (const ip6_header_t * ip,
                       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 @@ ip6_compute_flow_hash (const ip6_header_t * ip,
     ((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;
 }
index e03b010..667ea4c 100644 (file)
@@ -1297,6 +1297,22 @@ vl_api_set_ip_flow_hash_v2_t_handler (vl_api_set_ip_flow_hash_v2_t *mp)
   REPLY_MACRO (VL_API_SET_IP_FLOW_HASH_V2_REPLY);
 }
 
+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)
index bd37ef7..30dfcd7 100644 (file)
   _ (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
index 7c99486..727afba 100644 (file)
@@ -1276,6 +1276,12 @@ api_set_ip_flow_hash_v2 (vat_main_t *vat)
   return -1;
 }
 
+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)
 {
index 26bdaa6..5ac2a9c 100644 (file)
@@ -145,13 +145,13 @@ unformat_ip_flow_hash_config (unformat_input_t *input, va_list *args)
     {
       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
       {
index 736d8f7..e6597d2 100644 (file)
@@ -6,6 +6,7 @@ import unittest
 
 import scapy.compat
 from scapy.contrib.mpls import MPLS
+from scapy.contrib.gtp import GTP_U_Header
 from scapy.layers.inet import IP, UDP, TCP, ICMP, icmptypes, icmpcodes
 from scapy.layers.inet6 import IPv6
 from scapy.layers.l2 import Ether, Dot1Q, ARP
@@ -1210,6 +1211,7 @@ class TestIPLoadBalance(VppTestCase):
         """IP Load-Balancing"""
 
         fhc = VppEnum.vl_api_ip_flow_hash_config_t
+        fhcv2 = VppEnum.vl_api_ip_flow_hash_config_v2_t
         af = VppEnum.vl_api_address_family_t
 
         #
@@ -1217,16 +1219,20 @@ class TestIPLoadBalance(VppTestCase):
         #
         port_ip_pkts = []
         port_mpls_pkts = []
+        port_gtp_pkts = []
 
         #
         # An array of packets that differ only in the source address
         #
         src_ip_pkts = []
         src_mpls_pkts = []
+        src_gtp_pkts = []
 
         for ii in range(NUM_PKTS):
+            internal_src_ip_hdr = IP(dst="10.0.0.1", src="20.0.0.1")
+
             port_ip_hdr = (
-                IP(dst="10.0.0.1", src="20.0.0.1")
+                internal_src_ip_hdr
                 / UDP(sport=1234, dport=1234 + ii)
                 / Raw(b"\xa5" * 100)
             )
@@ -1240,6 +1246,15 @@ class TestIPLoadBalance(VppTestCase):
                     / port_ip_hdr
                 )
             )
+            port_gtp_pkts.append(
+                (
+                    Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
+                    / internal_src_ip_hdr
+                    / UDP(sport=2152, dport=2152, chksum=0)
+                    / GTP_U_Header(gtp_type="g_pdu", teid=200)
+                    / Raw(b"\xa5" * 100)
+                )
+            )
 
             src_ip_hdr = (
                 IP(dst="10.0.0.1", src="20.0.0.%d" % ii)
@@ -1256,6 +1271,15 @@ class TestIPLoadBalance(VppTestCase):
                     / src_ip_hdr
                 )
             )
+            src_gtp_pkts.append(
+                (
+                    Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac)
+                    / IP(dst="10.0.0.1", src="20.0.0.1")
+                    / UDP(sport=2152, dport=2152, chksum=0)
+                    / GTP_U_Header(gtp_type="g_pdu", teid=ii)
+                    / Raw(b"\xa5" * 100)
+                )
+            )
 
         route_10_0_0_1 = VppIpRoute(
             self,
@@ -1330,6 +1354,26 @@ class TestIPLoadBalance(VppTestCase):
 
         self.send_and_expect_only(self.pg0, port_ip_pkts, self.pg2)
 
+        #
+        # this case gtp v1 teid key LB
+        #
+        self.vapi.set_ip_flow_hash_v3(
+            af=af.ADDRESS_IP4,
+            table_id=0,
+            flow_hash_config=(
+                fhcv2.IP_API_V2_FLOW_HASH_SRC_IP
+                | fhcv2.IP_API_V2_FLOW_HASH_PROTO
+                | fhcv2.IP_API_V2_FLOW_HASH_GTPV1_TEID
+            ),
+        )
+        self.logger.info(self.vapi.cli("show ip fib"))
+
+        self.send_and_expect_load_balancing(
+            self.pg0, src_gtp_pkts, [self.pg1, self.pg2]
+        )
+
+        self.send_and_expect_only(self.pg0, port_gtp_pkts, self.pg2)
+
         #
         # change the flow hash config back to defaults
         #