From d2c4e38a75d8c981a7e17bf0b1d16739cf91c1bb Mon Sep 17 00:00:00 2001 From: Nikhil Dixit Limaye Date: Thu, 24 Oct 2024 16:52:47 -0700 Subject: [PATCH] Support stable rt header in katran Summary: For UDP stable routing use case for Remote presence. We introduce a new vip flag F_UDP_STABLE_ROUTING_VIP. If we receive packets for this vip, we will parse the stable routing header to get server-id and then route to it. Currently stable routing is 8bytes header, format same as QUIC conn id v2. (may change 1st byte later based on more experiments). Stats collection will be added in next diffs Reviewed By: avasylev Differential Revision: D64728833 fbshipit-source-id: c2d85d69157f9a48688cbfd3c41b09f6a2a5b1dc --- katran/lib/BalancerStructs.h | 9 +++++ katran/lib/bpf/balancer.bpf.c | 56 +++++++++++++++++++++++++++++++ katran/lib/bpf/balancer_consts.h | 11 ++++++ katran/lib/bpf/balancer_maps.h | 9 +++++ katran/lib/bpf/balancer_structs.h | 9 +++++ katran/lib/bpf/pckt_parsing.h | 46 +++++++++++++++++++++++++ katran/lib/testing/BpfTester.cpp | 2 ++ 7 files changed, 142 insertions(+) diff --git a/katran/lib/BalancerStructs.h b/katran/lib/BalancerStructs.h index 58705a9fe..0ec8e3555 100644 --- a/katran/lib/BalancerStructs.h +++ b/katran/lib/BalancerStructs.h @@ -137,4 +137,13 @@ struct lb_tpr_packets_stats { uint64_t tcp_syn; }; +// struct for udp stable routing statistics counters +struct lb_stable_rt_packets_stats { + uint64_t ch_routed; + uint64_t cid_routed; + uint64_t cid_invalid_server_id; + uint64_t cid_unknown_real_dropped; + uint64_t invalid_packet_type; +}; + } // namespace katran diff --git a/katran/lib/bpf/balancer.bpf.c b/katran/lib/bpf/balancer.bpf.c index 86e980c21..09d0977ed 100644 --- a/katran/lib/bpf/balancer.bpf.c +++ b/katran/lib/bpf/balancer.bpf.c @@ -551,6 +551,56 @@ __attribute__((__always_inline__)) static inline int process_encaped_gue_pckt( } #endif // of INLINE_DECAP_GUE +#ifdef UDP_STABLE_ROUTING +__attribute__((__always_inline__)) static inline bool +process_udp_stable_routing( + void* data, + void* data_end, + struct real_definition** dst, + struct packet_description* pckt, + bool is_ipv6) { + __u32 stable_rt_stats_key = 0; + struct lb_stable_rt_packets_stats* stable_rt_packets_stats = + bpf_map_lookup_elem(&stable_rt_stats, &stable_rt_stats_key); + if (!stable_rt_packets_stats) { + return XDP_DROP; + } + struct udp_stable_rt_result usr = + parse_udp_stable_rt_hdr(data, data_end, is_ipv6, pckt); + if (usr.server_id > 0) { + // server_id is expected to always be positive + __u32 key = usr.server_id; + __u32* real_pos = bpf_map_lookup_elem(&server_id_map, &key); + if (real_pos) { + // get a real position for the server id + key = *real_pos; + if (key != 0) { + pckt->real_index = key; + *dst = bpf_map_lookup_elem(&reals, &key); + if (!*dst) { + // fail to find a real server with the real pos, drop the packet + stable_rt_packets_stats->cid_unknown_real_dropped += 1; + return XDP_DROP; + } + // increment cid routed stats + stable_rt_packets_stats->cid_routed += 1; + } + } else { + // increment invalid server id stats + stable_rt_packets_stats->cid_invalid_server_id += 1; + stable_rt_packets_stats->ch_routed += 1; + } + } else { + // cannot get a server id , fallback to lru or ch + if (!usr.is_stable_rt_pkt) { + // invalid packet type + stable_rt_packets_stats->invalid_packet_type += 1; + } + stable_rt_packets_stats->ch_routed += 1; + } +} +#endif // of UDP_STABLE_ROUTING + __attribute__((__always_inline__)) static inline void increment_quic_cid_version_stats( struct lb_quic_packets_stats* quic_packets_stats, @@ -889,6 +939,12 @@ process_packet(struct xdp_md* xdp, __u64 off, bool is_ipv6) { } } } +#ifdef UDP_STABLE_ROUTING + if (pckt.flow.proto == IPPROTO_UDP && + vip_info->flags & F_UDP_STABLE_ROUTING_VIP) { + process_udp_stable_routing(data, data_end, &dst, &pckt, is_ipv6); + } +#endif // UDP_STABLE_ROUTING // save the original sport before making real selection, possibly changing its // value. diff --git a/katran/lib/bpf/balancer_consts.h b/katran/lib/bpf/balancer_consts.h index f4b32ac1d..b9bf0ec29 100644 --- a/katran/lib/bpf/balancer_consts.h +++ b/katran/lib/bpf/balancer_consts.h @@ -34,6 +34,9 @@ // process a packet to figure out what to do with it #define FURTHER_PROCESSING -1 +// indicates no server id was found in the packet +#define STABLE_RT_NO_SERVER_ID 0 + // 3FFF mask covers more fragments flag and fragment offset field. // 65343 = 3FFF in BigEndian #define PCKT_FRAGMENTED 65343 @@ -96,6 +99,8 @@ #define TPR_STATS_MAP_SIZE 1 +#define STABLE_RT_STATS_MAP_SIZE 1 + // for LRU's map in map we will support up to this number of cpus #ifndef MAX_SUPPORTED_CPUS #define MAX_SUPPORTED_CPUS 128 @@ -145,6 +150,8 @@ // use both src and dst port for the hash calculation for vips which normally // wouldn't #define F_HASH_SRC_DST_PORT (1 << 7) +// parse udp stable routing header to get server-id +#define F_UDP_STABLE_ROUTING_VIP (1 << 8) // packet_description flags: // the description has been created from icmp msg #define F_ICMP (1 << 0) @@ -164,6 +171,7 @@ // draft-ietf-quic-invariants-06 #define QUIC_LONG_HEADER 0x80 #define QUIC_SHORT_HEADER 0x00 +#define STABLE_ROUTING_HEADER 0x80 // Long header packet types (with alignment of 8-bits for packet-type) #define QUIC_CLIENT_INITIAL 0x00 #define QUIC_0RTT 0x10 @@ -176,6 +184,9 @@ #ifndef QUIC_MIN_CONNID_LEN #define QUIC_MIN_CONNID_LEN 8 #endif + +#define STABLE_RT_LEN 8 + // explicitly version the connection id #ifndef QUIC_CONNID_VERSION_V1 #define QUIC_CONNID_VERSION_V1 0x1 diff --git a/katran/lib/bpf/balancer_maps.h b/katran/lib/bpf/balancer_maps.h index 2281e8e48..aef04b2aa 100644 --- a/katran/lib/bpf/balancer_maps.h +++ b/katran/lib/bpf/balancer_maps.h @@ -125,6 +125,15 @@ struct { __uint(map_flags, NO_FLAGS); } quic_stats_map SEC(".maps"); +// map for udp stable routing stats +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __type(key, __u32); + __type(value, struct lb_stable_rt_packets_stats); + __uint(max_entries, STABLE_RT_STATS_MAP_SIZE); + __uint(map_flags, NO_FLAGS); +} stable_rt_stats SEC(".maps"); + // map w/ per vip decap statistics struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); diff --git a/katran/lib/bpf/balancer_structs.h b/katran/lib/bpf/balancer_structs.h index e65db536d..cbb21b2ea 100644 --- a/katran/lib/bpf/balancer_structs.h +++ b/katran/lib/bpf/balancer_structs.h @@ -163,4 +163,13 @@ struct lb_tpr_packets_stats { __u64 tcp_syn; }; +// struct for udp stable routing statistics counters +struct lb_stable_rt_packets_stats { + __u64 ch_routed; + __u64 cid_routed; + __u64 cid_invalid_server_id; + __u64 cid_unknown_real_dropped; + __u64 invalid_packet_type; +}; + #endif // of _BALANCER_STRUCTS diff --git a/katran/lib/bpf/pckt_parsing.h b/katran/lib/bpf/pckt_parsing.h index 92eba42a7..a9034426f 100644 --- a/katran/lib/bpf/pckt_parsing.h +++ b/katran/lib/bpf/pckt_parsing.h @@ -62,6 +62,15 @@ struct quic_parse_result { bool is_initial; }; +struct stable_routing_header { + __u8 connection_id[STABLE_RT_LEN]; +} __attribute__((__packed__)); + +struct udp_stable_rt_result { + __be32 server_id; + bool is_stable_rt_pkt; +}; + __attribute__((__always_inline__)) static inline __u64 calc_offset( bool is_ipv6, bool is_icmp) { @@ -418,4 +427,41 @@ parse_quic( return result; } +__attribute__((__always_inline__)) static inline struct udp_stable_rt_result +parse_udp_stable_rt_hdr( + void* data, + void* data_end, + bool is_ipv6, + struct packet_description* pckt) { + struct udp_stable_rt_result result = { + .server_id = STABLE_RT_NO_SERVER_ID, .is_stable_rt_pkt = false}; + + bool is_icmp = (pckt->flags & F_ICMP); + __u64 off = calc_offset(is_ipv6, is_icmp); + // offset points to the beginning of transport header (udp) + /* |PKT TYPE| */ + if ((data + off + sizeof(struct udphdr) + sizeof(__u8)) > data_end) { + return result; + } + + __u8* udp_data = data + off + sizeof(struct udphdr); + __u8* pkt_type = udp_data; + __u8* connId = NULL; + if ((*pkt_type & STABLE_ROUTING_HEADER) == STABLE_ROUTING_HEADER) { + // packet with stable routing header + if (udp_data + sizeof(struct stable_routing_header) > data_end) { + return result; + } + connId = ((struct stable_routing_header*)udp_data)->connection_id; + result.is_stable_rt_pkt = true; + } + if (!connId) { + return result; + } + + // same as QUIC connId v2 schema + result.server_id = (connId[1] << 16) | (connId[2] << 8) | (connId[3]); + return result; +} + #endif // of __PCKT_PARSING_H diff --git a/katran/lib/testing/BpfTester.cpp b/katran/lib/testing/BpfTester.cpp index 1c736b447..a1eca0b94 100644 --- a/katran/lib/testing/BpfTester.cpp +++ b/katran/lib/testing/BpfTester.cpp @@ -181,6 +181,8 @@ bool BpfTester::runBpfTesterFromFixtures( if (config_.testData[i].routedThroughGlobalLru) { packetsRoutedGlobalLruBefore = getGlobalLruRoutedPackets(); } + VLOG(2) << "Running test for pckt #" << pckt_num + << " with description: " << config_.testData[i].description; auto res = adapter_.testXdpProg( progFd, kTestRepeatCount,