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>
This commit is contained in:
Takeru Hayasaka
2023-01-17 04:45:58 +09:00
committed by Neale Ranns
parent 55686e1c59
commit b23c6f4f29
9 changed files with 143 additions and 15 deletions

View 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

View 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* */

View 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);

View 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;
}

View 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)

View File

@ -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

View 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)
{

View 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
{

View 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
#