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;
 };
 
   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
 /** \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* */
  * @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",
   .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* */
   .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/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 */
 
 
 #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);
                       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;
   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;
 
   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;
   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);
   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;
                       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;
   u64 a, b, c;
   u64 t1, t2;
+  u32 t3;
   uword is_tcp_udp = 0;
   uword is_tcp_udp = 0;
+  uword is_udp = ip->protocol == IP_PROTOCOL_UDP;
   u8 protocol = ip->protocol;
 
   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);
     {
       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;
     ((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;
 }
   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);
 }
 
   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)
 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)                                    \
   _ (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
 
 /**
  * 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;
 }
 
   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)
 {
 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, "%_,"))
        ;
     {
       if (unformat (input, "%_,"))
        ;
-#define _(a, b)                                                               \
+#define _(a, b, c)                                                            \
   else if (unformat (input, "%_" #a))                                         \
   {                                                                           \
   else if (unformat (input, "%_" #a))                                         \
   {                                                                           \
-    *flow_hash_config |= b;                                                   \
+    *flow_hash_config |= c;                                                   \
     matched_once = 1;                                                         \
   }
     matched_once = 1;                                                         \
   }
-      foreach_flow_hash_bit_v1
+      foreach_flow_hash_bit
 #undef _
        else
       {
 #undef _
        else
       {
index 736d8f7..e6597d2 100644 (file)
@@ -6,6 +6,7 @@ import unittest
 
 import scapy.compat
 from scapy.contrib.mpls import MPLS
 
 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
 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
         """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
 
         #
         af = VppEnum.vl_api_address_family_t
 
         #
@@ -1217,16 +1219,20 @@ class TestIPLoadBalance(VppTestCase):
         #
         port_ip_pkts = []
         port_mpls_pkts = []
         #
         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 = []
 
         #
         # 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):
 
         for ii in range(NUM_PKTS):
+            internal_src_ip_hdr = IP(dst="10.0.0.1", src="20.0.0.1")
+
             port_ip_hdr = (
             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)
             )
                 / UDP(sport=1234, dport=1234 + ii)
                 / Raw(b"\xa5" * 100)
             )
@@ -1240,6 +1246,15 @@ class TestIPLoadBalance(VppTestCase):
                     / port_ip_hdr
                 )
             )
                     / 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)
 
             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_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,
 
         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)
 
 
         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
         #
         #
         # change the flow hash config back to defaults
         #