aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakeru Hayasaka <hayatake396@gmail.com>2023-01-17 04:45:58 +0900
committerNeale Ranns <neale@graphiant.com>2023-03-31 06:04:42 +0000
commitb23c6f4f29b53afa6be2735b30b08fcb115f20cc (patch)
tree59cca6452dca4b17fbce112ca8f4e5b3004565b7
parent55686e1c59f8bcf399f5ff58b3ca1030a415009c (diff)
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>
-rw-r--r--src/vnet/ip/ip.api35
-rw-r--r--src/vnet/ip/ip4_forward.c7
-rw-r--r--src/vnet/ip/ip4_inlines.h14
-rw-r--r--src/vnet/ip/ip6_inlines.h16
-rw-r--r--src/vnet/ip/ip_api.c16
-rw-r--r--src/vnet/ip/ip_flow_hash.h12
-rw-r--r--src/vnet/ip/ip_test.c6
-rw-r--r--src/vnet/ip/lookup.c6
-rw-r--r--test/test_ip4.py46
9 files changed, 143 insertions, 15 deletions
diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api
index 8a6ecc8da2f..9ee2cc4f9fc 100644
--- a/src/vnet/ip/ip.api
+++ b/src/vnet/ip/ip.api
@@ -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
diff --git a/src/vnet/ip/ip4_forward.c b/src/vnet/ip/ip4_forward.c
index 35ebaa436d1..b3c9ff7874c 100644
--- a/src/vnet/ip/ip4_forward.c
+++ b/src/vnet/ip/ip4_forward.c
@@ -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* */
diff --git a/src/vnet/ip/ip4_inlines.h b/src/vnet/ip/ip4_inlines.h
index ca7327fbcdc..b4fcebc9896 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 @@ 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);
diff --git a/src/vnet/ip/ip6_inlines.h b/src/vnet/ip/ip6_inlines.h
index 9c2be60b267..4a2b91b9ec9 100644
--- a/src/vnet/ip/ip6_inlines.h
+++ b/src/vnet/ip/ip6_inlines.h
@@ -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;
}
diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c
index e03b0103391..667ea4c45b4 100644
--- a/src/vnet/ip/ip_api.c
+++ b/src/vnet/ip/ip_api.c
@@ -1298,6 +1298,22 @@ vl_api_set_ip_flow_hash_v2_t_handler (vl_api_set_ip_flow_hash_v2_t *mp)
}
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 bd37ef7307b..30dfcd70a1b 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 7c994868d87..727afba67f4 100644
--- a/src/vnet/ip/ip_test.c
+++ b/src/vnet/ip/ip_test.c
@@ -1277,6 +1277,12 @@ api_set_ip_flow_hash_v2 (vat_main_t *vat)
}
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 26bdaa635aa..5ac2a9c17e2 100644
--- a/src/vnet/ip/lookup.c
+++ b/src/vnet/ip/lookup.c
@@ -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
{
diff --git a/test/test_ip4.py b/test/test_ip4.py
index 736d8f7bc4c..e6597d24210 100644
--- a/test/test_ip4.py
+++ b/test/test_ip4.py
@@ -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,
@@ -1331,6 +1355,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
#
self.vapi.set_ip_flow_hash(vrf_id=0, src=1, dst=1, proto=1, sport=1, dport=1)