From b23c6f4f29b53afa6be2735b30b08fcb115f20cc Mon Sep 17 00:00:00 2001 From: Takeru Hayasaka Date: Tue, 17 Jan 2023 04:45:58 +0900 Subject: [PATCH] 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 --- src/vnet/ip/ip.api | 35 +++++++++++++++++++++++++++++++++++ src/vnet/ip/ip4_forward.c | 7 +++---- src/vnet/ip/ip4_inlines.h | 14 ++++++++++++-- src/vnet/ip/ip6_inlines.h | 16 ++++++++++++---- src/vnet/ip/ip_api.c | 16 ++++++++++++++++ src/vnet/ip/ip_flow_hash.h | 12 +++++++++++- src/vnet/ip/ip_test.c | 6 ++++++ src/vnet/ip/lookup.c | 6 +++--- test/test_ip4.py | 46 +++++++++++++++++++++++++++++++++++++++++++++- 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 [src] [dst] [sport] [dport] [proto] [reverse]", + .short_help = "set ip flow-hash table [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 #include #include +#include #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 @@ -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) 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 @@ -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) { 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, @@ -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 # -- 2.16.6