18c2ecf20Sopenharmony_ci/* Copyright (c) 2017 Facebook
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
48c2ecf20Sopenharmony_ci * modify it under the terms of version 2 of the GNU General Public
58c2ecf20Sopenharmony_ci * License as published by the Free Software Foundation.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <stddef.h>
88c2ecf20Sopenharmony_ci#include <stdbool.h>
98c2ecf20Sopenharmony_ci#include <string.h>
108c2ecf20Sopenharmony_ci#include <linux/pkt_cls.h>
118c2ecf20Sopenharmony_ci#include <linux/bpf.h>
128c2ecf20Sopenharmony_ci#include <linux/in.h>
138c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
148c2ecf20Sopenharmony_ci#include <linux/ip.h>
158c2ecf20Sopenharmony_ci#include <linux/ipv6.h>
168c2ecf20Sopenharmony_ci#include <linux/icmp.h>
178c2ecf20Sopenharmony_ci#include <linux/icmpv6.h>
188c2ecf20Sopenharmony_ci#include <linux/tcp.h>
198c2ecf20Sopenharmony_ci#include <linux/udp.h>
208c2ecf20Sopenharmony_ci#include <bpf/bpf_helpers.h>
218c2ecf20Sopenharmony_ci#include "test_iptunnel_common.h"
228c2ecf20Sopenharmony_ci#include <bpf/bpf_endian.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciint _version SEC("version") = 1;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic inline __u32 rol32(__u32 word, unsigned int shift)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	return (word << shift) | (word >> ((-shift) & 31));
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* copy paste of jhash from kernel sources to make sure llvm
328c2ecf20Sopenharmony_ci * can compile it into valid sequence of bpf instructions
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci#define __jhash_mix(a, b, c)			\
358c2ecf20Sopenharmony_ci{						\
368c2ecf20Sopenharmony_ci	a -= c;  a ^= rol32(c, 4);  c += b;	\
378c2ecf20Sopenharmony_ci	b -= a;  b ^= rol32(a, 6);  a += c;	\
388c2ecf20Sopenharmony_ci	c -= b;  c ^= rol32(b, 8);  b += a;	\
398c2ecf20Sopenharmony_ci	a -= c;  a ^= rol32(c, 16); c += b;	\
408c2ecf20Sopenharmony_ci	b -= a;  b ^= rol32(a, 19); a += c;	\
418c2ecf20Sopenharmony_ci	c -= b;  c ^= rol32(b, 4);  b += a;	\
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define __jhash_final(a, b, c)			\
458c2ecf20Sopenharmony_ci{						\
468c2ecf20Sopenharmony_ci	c ^= b; c -= rol32(b, 14);		\
478c2ecf20Sopenharmony_ci	a ^= c; a -= rol32(c, 11);		\
488c2ecf20Sopenharmony_ci	b ^= a; b -= rol32(a, 25);		\
498c2ecf20Sopenharmony_ci	c ^= b; c -= rol32(b, 16);		\
508c2ecf20Sopenharmony_ci	a ^= c; a -= rol32(c, 4);		\
518c2ecf20Sopenharmony_ci	b ^= a; b -= rol32(a, 14);		\
528c2ecf20Sopenharmony_ci	c ^= b; c -= rol32(b, 24);		\
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define JHASH_INITVAL		0xdeadbeef
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_citypedef unsigned int u32;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic inline u32 jhash(const void *key, u32 length, u32 initval)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	u32 a, b, c;
628c2ecf20Sopenharmony_ci	const unsigned char *k = key;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	a = b = c = JHASH_INITVAL + length + initval;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	while (length > 12) {
678c2ecf20Sopenharmony_ci		a += *(u32 *)(k);
688c2ecf20Sopenharmony_ci		b += *(u32 *)(k + 4);
698c2ecf20Sopenharmony_ci		c += *(u32 *)(k + 8);
708c2ecf20Sopenharmony_ci		__jhash_mix(a, b, c);
718c2ecf20Sopenharmony_ci		length -= 12;
728c2ecf20Sopenharmony_ci		k += 12;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci	switch (length) {
758c2ecf20Sopenharmony_ci	case 12: c += (u32)k[11]<<24;
768c2ecf20Sopenharmony_ci	case 11: c += (u32)k[10]<<16;
778c2ecf20Sopenharmony_ci	case 10: c += (u32)k[9]<<8;
788c2ecf20Sopenharmony_ci	case 9:  c += k[8];
798c2ecf20Sopenharmony_ci	case 8:  b += (u32)k[7]<<24;
808c2ecf20Sopenharmony_ci	case 7:  b += (u32)k[6]<<16;
818c2ecf20Sopenharmony_ci	case 6:  b += (u32)k[5]<<8;
828c2ecf20Sopenharmony_ci	case 5:  b += k[4];
838c2ecf20Sopenharmony_ci	case 4:  a += (u32)k[3]<<24;
848c2ecf20Sopenharmony_ci	case 3:  a += (u32)k[2]<<16;
858c2ecf20Sopenharmony_ci	case 2:  a += (u32)k[1]<<8;
868c2ecf20Sopenharmony_ci	case 1:  a += k[0];
878c2ecf20Sopenharmony_ci		 __jhash_final(a, b, c);
888c2ecf20Sopenharmony_ci	case 0: /* Nothing left to add */
898c2ecf20Sopenharmony_ci		break;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return c;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic inline u32 __jhash_nwords(u32 a, u32 b, u32 c, u32 initval)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	a += initval;
988c2ecf20Sopenharmony_ci	b += initval;
998c2ecf20Sopenharmony_ci	c += initval;
1008c2ecf20Sopenharmony_ci	__jhash_final(a, b, c);
1018c2ecf20Sopenharmony_ci	return c;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic inline u32 jhash_2words(u32 a, u32 b, u32 initval)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2));
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci#define PCKT_FRAGMENTED 65343
1108c2ecf20Sopenharmony_ci#define IPV4_HDR_LEN_NO_OPT 20
1118c2ecf20Sopenharmony_ci#define IPV4_PLUS_ICMP_HDR 28
1128c2ecf20Sopenharmony_ci#define IPV6_PLUS_ICMP_HDR 48
1138c2ecf20Sopenharmony_ci#define RING_SIZE 2
1148c2ecf20Sopenharmony_ci#define MAX_VIPS 12
1158c2ecf20Sopenharmony_ci#define MAX_REALS 5
1168c2ecf20Sopenharmony_ci#define CTL_MAP_SIZE 16
1178c2ecf20Sopenharmony_ci#define CH_RINGS_SIZE (MAX_VIPS * RING_SIZE)
1188c2ecf20Sopenharmony_ci#define F_IPV6 (1 << 0)
1198c2ecf20Sopenharmony_ci#define F_HASH_NO_SRC_PORT (1 << 0)
1208c2ecf20Sopenharmony_ci#define F_ICMP (1 << 0)
1218c2ecf20Sopenharmony_ci#define F_SYN_SET (1 << 1)
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistruct packet_description {
1248c2ecf20Sopenharmony_ci	union {
1258c2ecf20Sopenharmony_ci		__be32 src;
1268c2ecf20Sopenharmony_ci		__be32 srcv6[4];
1278c2ecf20Sopenharmony_ci	};
1288c2ecf20Sopenharmony_ci	union {
1298c2ecf20Sopenharmony_ci		__be32 dst;
1308c2ecf20Sopenharmony_ci		__be32 dstv6[4];
1318c2ecf20Sopenharmony_ci	};
1328c2ecf20Sopenharmony_ci	union {
1338c2ecf20Sopenharmony_ci		__u32 ports;
1348c2ecf20Sopenharmony_ci		__u16 port16[2];
1358c2ecf20Sopenharmony_ci	};
1368c2ecf20Sopenharmony_ci	__u8 proto;
1378c2ecf20Sopenharmony_ci	__u8 flags;
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistruct ctl_value {
1418c2ecf20Sopenharmony_ci	union {
1428c2ecf20Sopenharmony_ci		__u64 value;
1438c2ecf20Sopenharmony_ci		__u32 ifindex;
1448c2ecf20Sopenharmony_ci		__u8 mac[6];
1458c2ecf20Sopenharmony_ci	};
1468c2ecf20Sopenharmony_ci};
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistruct vip_meta {
1498c2ecf20Sopenharmony_ci	__u32 flags;
1508c2ecf20Sopenharmony_ci	__u32 vip_num;
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistruct real_definition {
1548c2ecf20Sopenharmony_ci	union {
1558c2ecf20Sopenharmony_ci		__be32 dst;
1568c2ecf20Sopenharmony_ci		__be32 dstv6[4];
1578c2ecf20Sopenharmony_ci	};
1588c2ecf20Sopenharmony_ci	__u8 flags;
1598c2ecf20Sopenharmony_ci};
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistruct vip_stats {
1628c2ecf20Sopenharmony_ci	__u64 bytes;
1638c2ecf20Sopenharmony_ci	__u64 pkts;
1648c2ecf20Sopenharmony_ci};
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistruct eth_hdr {
1678c2ecf20Sopenharmony_ci	unsigned char eth_dest[ETH_ALEN];
1688c2ecf20Sopenharmony_ci	unsigned char eth_source[ETH_ALEN];
1698c2ecf20Sopenharmony_ci	unsigned short eth_proto;
1708c2ecf20Sopenharmony_ci};
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistruct {
1738c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_HASH);
1748c2ecf20Sopenharmony_ci	__uint(max_entries, MAX_VIPS);
1758c2ecf20Sopenharmony_ci	__type(key, struct vip);
1768c2ecf20Sopenharmony_ci	__type(value, struct vip_meta);
1778c2ecf20Sopenharmony_ci} vip_map SEC(".maps");
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistruct {
1808c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_ARRAY);
1818c2ecf20Sopenharmony_ci	__uint(max_entries, CH_RINGS_SIZE);
1828c2ecf20Sopenharmony_ci	__type(key, __u32);
1838c2ecf20Sopenharmony_ci	__type(value, __u32);
1848c2ecf20Sopenharmony_ci} ch_rings SEC(".maps");
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistruct {
1878c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_ARRAY);
1888c2ecf20Sopenharmony_ci	__uint(max_entries, MAX_REALS);
1898c2ecf20Sopenharmony_ci	__type(key, __u32);
1908c2ecf20Sopenharmony_ci	__type(value, struct real_definition);
1918c2ecf20Sopenharmony_ci} reals SEC(".maps");
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistruct {
1948c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
1958c2ecf20Sopenharmony_ci	__uint(max_entries, MAX_VIPS);
1968c2ecf20Sopenharmony_ci	__type(key, __u32);
1978c2ecf20Sopenharmony_ci	__type(value, struct vip_stats);
1988c2ecf20Sopenharmony_ci} stats SEC(".maps");
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistruct {
2018c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_ARRAY);
2028c2ecf20Sopenharmony_ci	__uint(max_entries, CTL_MAP_SIZE);
2038c2ecf20Sopenharmony_ci	__type(key, __u32);
2048c2ecf20Sopenharmony_ci	__type(value, struct ctl_value);
2058c2ecf20Sopenharmony_ci} ctl_array SEC(".maps");
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic __always_inline __u32 get_packet_hash(struct packet_description *pckt,
2088c2ecf20Sopenharmony_ci					     bool ipv6)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	if (ipv6)
2118c2ecf20Sopenharmony_ci		return jhash_2words(jhash(pckt->srcv6, 16, MAX_VIPS),
2128c2ecf20Sopenharmony_ci				    pckt->ports, CH_RINGS_SIZE);
2138c2ecf20Sopenharmony_ci	else
2148c2ecf20Sopenharmony_ci		return jhash_2words(pckt->src, pckt->ports, CH_RINGS_SIZE);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic __always_inline bool get_packet_dst(struct real_definition **real,
2188c2ecf20Sopenharmony_ci					   struct packet_description *pckt,
2198c2ecf20Sopenharmony_ci					   struct vip_meta *vip_info,
2208c2ecf20Sopenharmony_ci					   bool is_ipv6)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	__u32 hash = get_packet_hash(pckt, is_ipv6) % RING_SIZE;
2238c2ecf20Sopenharmony_ci	__u32 key = RING_SIZE * vip_info->vip_num + hash;
2248c2ecf20Sopenharmony_ci	__u32 *real_pos;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	real_pos = bpf_map_lookup_elem(&ch_rings, &key);
2278c2ecf20Sopenharmony_ci	if (!real_pos)
2288c2ecf20Sopenharmony_ci		return false;
2298c2ecf20Sopenharmony_ci	key = *real_pos;
2308c2ecf20Sopenharmony_ci	*real = bpf_map_lookup_elem(&reals, &key);
2318c2ecf20Sopenharmony_ci	if (!(*real))
2328c2ecf20Sopenharmony_ci		return false;
2338c2ecf20Sopenharmony_ci	return true;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic __always_inline int parse_icmpv6(void *data, void *data_end, __u64 off,
2378c2ecf20Sopenharmony_ci					struct packet_description *pckt)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct icmp6hdr *icmp_hdr;
2408c2ecf20Sopenharmony_ci	struct ipv6hdr *ip6h;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	icmp_hdr = data + off;
2438c2ecf20Sopenharmony_ci	if (icmp_hdr + 1 > data_end)
2448c2ecf20Sopenharmony_ci		return TC_ACT_SHOT;
2458c2ecf20Sopenharmony_ci	if (icmp_hdr->icmp6_type != ICMPV6_PKT_TOOBIG)
2468c2ecf20Sopenharmony_ci		return TC_ACT_OK;
2478c2ecf20Sopenharmony_ci	off += sizeof(struct icmp6hdr);
2488c2ecf20Sopenharmony_ci	ip6h = data + off;
2498c2ecf20Sopenharmony_ci	if (ip6h + 1 > data_end)
2508c2ecf20Sopenharmony_ci		return TC_ACT_SHOT;
2518c2ecf20Sopenharmony_ci	pckt->proto = ip6h->nexthdr;
2528c2ecf20Sopenharmony_ci	pckt->flags |= F_ICMP;
2538c2ecf20Sopenharmony_ci	memcpy(pckt->srcv6, ip6h->daddr.s6_addr32, 16);
2548c2ecf20Sopenharmony_ci	memcpy(pckt->dstv6, ip6h->saddr.s6_addr32, 16);
2558c2ecf20Sopenharmony_ci	return TC_ACT_UNSPEC;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic __always_inline int parse_icmp(void *data, void *data_end, __u64 off,
2598c2ecf20Sopenharmony_ci				      struct packet_description *pckt)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct icmphdr *icmp_hdr;
2628c2ecf20Sopenharmony_ci	struct iphdr *iph;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	icmp_hdr = data + off;
2658c2ecf20Sopenharmony_ci	if (icmp_hdr + 1 > data_end)
2668c2ecf20Sopenharmony_ci		return TC_ACT_SHOT;
2678c2ecf20Sopenharmony_ci	if (icmp_hdr->type != ICMP_DEST_UNREACH ||
2688c2ecf20Sopenharmony_ci	    icmp_hdr->code != ICMP_FRAG_NEEDED)
2698c2ecf20Sopenharmony_ci		return TC_ACT_OK;
2708c2ecf20Sopenharmony_ci	off += sizeof(struct icmphdr);
2718c2ecf20Sopenharmony_ci	iph = data + off;
2728c2ecf20Sopenharmony_ci	if (iph + 1 > data_end)
2738c2ecf20Sopenharmony_ci		return TC_ACT_SHOT;
2748c2ecf20Sopenharmony_ci	if (iph->ihl != 5)
2758c2ecf20Sopenharmony_ci		return TC_ACT_SHOT;
2768c2ecf20Sopenharmony_ci	pckt->proto = iph->protocol;
2778c2ecf20Sopenharmony_ci	pckt->flags |= F_ICMP;
2788c2ecf20Sopenharmony_ci	pckt->src = iph->daddr;
2798c2ecf20Sopenharmony_ci	pckt->dst = iph->saddr;
2808c2ecf20Sopenharmony_ci	return TC_ACT_UNSPEC;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic __always_inline bool parse_udp(void *data, __u64 off, void *data_end,
2848c2ecf20Sopenharmony_ci				      struct packet_description *pckt)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct udphdr *udp;
2878c2ecf20Sopenharmony_ci	udp = data + off;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (udp + 1 > data_end)
2908c2ecf20Sopenharmony_ci		return false;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (!(pckt->flags & F_ICMP)) {
2938c2ecf20Sopenharmony_ci		pckt->port16[0] = udp->source;
2948c2ecf20Sopenharmony_ci		pckt->port16[1] = udp->dest;
2958c2ecf20Sopenharmony_ci	} else {
2968c2ecf20Sopenharmony_ci		pckt->port16[0] = udp->dest;
2978c2ecf20Sopenharmony_ci		pckt->port16[1] = udp->source;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci	return true;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic __always_inline bool parse_tcp(void *data, __u64 off, void *data_end,
3038c2ecf20Sopenharmony_ci				      struct packet_description *pckt)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct tcphdr *tcp;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	tcp = data + off;
3088c2ecf20Sopenharmony_ci	if (tcp + 1 > data_end)
3098c2ecf20Sopenharmony_ci		return false;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (tcp->syn)
3128c2ecf20Sopenharmony_ci		pckt->flags |= F_SYN_SET;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (!(pckt->flags & F_ICMP)) {
3158c2ecf20Sopenharmony_ci		pckt->port16[0] = tcp->source;
3168c2ecf20Sopenharmony_ci		pckt->port16[1] = tcp->dest;
3178c2ecf20Sopenharmony_ci	} else {
3188c2ecf20Sopenharmony_ci		pckt->port16[0] = tcp->dest;
3198c2ecf20Sopenharmony_ci		pckt->port16[1] = tcp->source;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci	return true;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic __always_inline int process_packet(void *data, __u64 off, void *data_end,
3258c2ecf20Sopenharmony_ci					  bool is_ipv6, struct __sk_buff *skb)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	void *pkt_start = (void *)(long)skb->data;
3288c2ecf20Sopenharmony_ci	struct packet_description pckt = {};
3298c2ecf20Sopenharmony_ci	struct eth_hdr *eth = pkt_start;
3308c2ecf20Sopenharmony_ci	struct bpf_tunnel_key tkey = {};
3318c2ecf20Sopenharmony_ci	struct vip_stats *data_stats;
3328c2ecf20Sopenharmony_ci	struct real_definition *dst;
3338c2ecf20Sopenharmony_ci	struct vip_meta *vip_info;
3348c2ecf20Sopenharmony_ci	struct ctl_value *cval;
3358c2ecf20Sopenharmony_ci	__u32 v4_intf_pos = 1;
3368c2ecf20Sopenharmony_ci	__u32 v6_intf_pos = 2;
3378c2ecf20Sopenharmony_ci	struct ipv6hdr *ip6h;
3388c2ecf20Sopenharmony_ci	struct vip vip = {};
3398c2ecf20Sopenharmony_ci	struct iphdr *iph;
3408c2ecf20Sopenharmony_ci	int tun_flag = 0;
3418c2ecf20Sopenharmony_ci	__u16 pkt_bytes;
3428c2ecf20Sopenharmony_ci	__u64 iph_len;
3438c2ecf20Sopenharmony_ci	__u32 ifindex;
3448c2ecf20Sopenharmony_ci	__u8 protocol;
3458c2ecf20Sopenharmony_ci	__u32 vip_num;
3468c2ecf20Sopenharmony_ci	int action;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	tkey.tunnel_ttl = 64;
3498c2ecf20Sopenharmony_ci	if (is_ipv6) {
3508c2ecf20Sopenharmony_ci		ip6h = data + off;
3518c2ecf20Sopenharmony_ci		if (ip6h + 1 > data_end)
3528c2ecf20Sopenharmony_ci			return TC_ACT_SHOT;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		iph_len = sizeof(struct ipv6hdr);
3558c2ecf20Sopenharmony_ci		protocol = ip6h->nexthdr;
3568c2ecf20Sopenharmony_ci		pckt.proto = protocol;
3578c2ecf20Sopenharmony_ci		pkt_bytes = bpf_ntohs(ip6h->payload_len);
3588c2ecf20Sopenharmony_ci		off += iph_len;
3598c2ecf20Sopenharmony_ci		if (protocol == IPPROTO_FRAGMENT) {
3608c2ecf20Sopenharmony_ci			return TC_ACT_SHOT;
3618c2ecf20Sopenharmony_ci		} else if (protocol == IPPROTO_ICMPV6) {
3628c2ecf20Sopenharmony_ci			action = parse_icmpv6(data, data_end, off, &pckt);
3638c2ecf20Sopenharmony_ci			if (action >= 0)
3648c2ecf20Sopenharmony_ci				return action;
3658c2ecf20Sopenharmony_ci			off += IPV6_PLUS_ICMP_HDR;
3668c2ecf20Sopenharmony_ci		} else {
3678c2ecf20Sopenharmony_ci			memcpy(pckt.srcv6, ip6h->saddr.s6_addr32, 16);
3688c2ecf20Sopenharmony_ci			memcpy(pckt.dstv6, ip6h->daddr.s6_addr32, 16);
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci	} else {
3718c2ecf20Sopenharmony_ci		iph = data + off;
3728c2ecf20Sopenharmony_ci		if (iph + 1 > data_end)
3738c2ecf20Sopenharmony_ci			return TC_ACT_SHOT;
3748c2ecf20Sopenharmony_ci		if (iph->ihl != 5)
3758c2ecf20Sopenharmony_ci			return TC_ACT_SHOT;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		protocol = iph->protocol;
3788c2ecf20Sopenharmony_ci		pckt.proto = protocol;
3798c2ecf20Sopenharmony_ci		pkt_bytes = bpf_ntohs(iph->tot_len);
3808c2ecf20Sopenharmony_ci		off += IPV4_HDR_LEN_NO_OPT;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci		if (iph->frag_off & PCKT_FRAGMENTED)
3838c2ecf20Sopenharmony_ci			return TC_ACT_SHOT;
3848c2ecf20Sopenharmony_ci		if (protocol == IPPROTO_ICMP) {
3858c2ecf20Sopenharmony_ci			action = parse_icmp(data, data_end, off, &pckt);
3868c2ecf20Sopenharmony_ci			if (action >= 0)
3878c2ecf20Sopenharmony_ci				return action;
3888c2ecf20Sopenharmony_ci			off += IPV4_PLUS_ICMP_HDR;
3898c2ecf20Sopenharmony_ci		} else {
3908c2ecf20Sopenharmony_ci			pckt.src = iph->saddr;
3918c2ecf20Sopenharmony_ci			pckt.dst = iph->daddr;
3928c2ecf20Sopenharmony_ci		}
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci	protocol = pckt.proto;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (protocol == IPPROTO_TCP) {
3978c2ecf20Sopenharmony_ci		if (!parse_tcp(data, off, data_end, &pckt))
3988c2ecf20Sopenharmony_ci			return TC_ACT_SHOT;
3998c2ecf20Sopenharmony_ci	} else if (protocol == IPPROTO_UDP) {
4008c2ecf20Sopenharmony_ci		if (!parse_udp(data, off, data_end, &pckt))
4018c2ecf20Sopenharmony_ci			return TC_ACT_SHOT;
4028c2ecf20Sopenharmony_ci	} else {
4038c2ecf20Sopenharmony_ci		return TC_ACT_SHOT;
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	if (is_ipv6)
4078c2ecf20Sopenharmony_ci		memcpy(vip.daddr.v6, pckt.dstv6, 16);
4088c2ecf20Sopenharmony_ci	else
4098c2ecf20Sopenharmony_ci		vip.daddr.v4 = pckt.dst;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	vip.dport = pckt.port16[1];
4128c2ecf20Sopenharmony_ci	vip.protocol = pckt.proto;
4138c2ecf20Sopenharmony_ci	vip_info = bpf_map_lookup_elem(&vip_map, &vip);
4148c2ecf20Sopenharmony_ci	if (!vip_info) {
4158c2ecf20Sopenharmony_ci		vip.dport = 0;
4168c2ecf20Sopenharmony_ci		vip_info = bpf_map_lookup_elem(&vip_map, &vip);
4178c2ecf20Sopenharmony_ci		if (!vip_info)
4188c2ecf20Sopenharmony_ci			return TC_ACT_SHOT;
4198c2ecf20Sopenharmony_ci		pckt.port16[1] = 0;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	if (vip_info->flags & F_HASH_NO_SRC_PORT)
4238c2ecf20Sopenharmony_ci		pckt.port16[0] = 0;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (!get_packet_dst(&dst, &pckt, vip_info, is_ipv6))
4268c2ecf20Sopenharmony_ci		return TC_ACT_SHOT;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (dst->flags & F_IPV6) {
4298c2ecf20Sopenharmony_ci		cval = bpf_map_lookup_elem(&ctl_array, &v6_intf_pos);
4308c2ecf20Sopenharmony_ci		if (!cval)
4318c2ecf20Sopenharmony_ci			return TC_ACT_SHOT;
4328c2ecf20Sopenharmony_ci		ifindex = cval->ifindex;
4338c2ecf20Sopenharmony_ci		memcpy(tkey.remote_ipv6, dst->dstv6, 16);
4348c2ecf20Sopenharmony_ci		tun_flag = BPF_F_TUNINFO_IPV6;
4358c2ecf20Sopenharmony_ci	} else {
4368c2ecf20Sopenharmony_ci		cval = bpf_map_lookup_elem(&ctl_array, &v4_intf_pos);
4378c2ecf20Sopenharmony_ci		if (!cval)
4388c2ecf20Sopenharmony_ci			return TC_ACT_SHOT;
4398c2ecf20Sopenharmony_ci		ifindex = cval->ifindex;
4408c2ecf20Sopenharmony_ci		tkey.remote_ipv4 = dst->dst;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci	vip_num = vip_info->vip_num;
4438c2ecf20Sopenharmony_ci	data_stats = bpf_map_lookup_elem(&stats, &vip_num);
4448c2ecf20Sopenharmony_ci	if (!data_stats)
4458c2ecf20Sopenharmony_ci		return TC_ACT_SHOT;
4468c2ecf20Sopenharmony_ci	data_stats->pkts++;
4478c2ecf20Sopenharmony_ci	data_stats->bytes += pkt_bytes;
4488c2ecf20Sopenharmony_ci	bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), tun_flag);
4498c2ecf20Sopenharmony_ci	*(u32 *)eth->eth_dest = tkey.remote_ipv4;
4508c2ecf20Sopenharmony_ci	return bpf_redirect(ifindex, 0);
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ciSEC("l4lb-demo")
4548c2ecf20Sopenharmony_ciint balancer_ingress(struct __sk_buff *ctx)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	void *data_end = (void *)(long)ctx->data_end;
4578c2ecf20Sopenharmony_ci	void *data = (void *)(long)ctx->data;
4588c2ecf20Sopenharmony_ci	struct eth_hdr *eth = data;
4598c2ecf20Sopenharmony_ci	__u32 eth_proto;
4608c2ecf20Sopenharmony_ci	__u32 nh_off;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	nh_off = sizeof(struct eth_hdr);
4638c2ecf20Sopenharmony_ci	if (data + nh_off > data_end)
4648c2ecf20Sopenharmony_ci		return TC_ACT_SHOT;
4658c2ecf20Sopenharmony_ci	eth_proto = eth->eth_proto;
4668c2ecf20Sopenharmony_ci	if (eth_proto == bpf_htons(ETH_P_IP))
4678c2ecf20Sopenharmony_ci		return process_packet(data, nh_off, data_end, false, ctx);
4688c2ecf20Sopenharmony_ci	else if (eth_proto == bpf_htons(ETH_P_IPV6))
4698c2ecf20Sopenharmony_ci		return process_packet(data, nh_off, data_end, true, ctx);
4708c2ecf20Sopenharmony_ci	else
4718c2ecf20Sopenharmony_ci		return TC_ACT_SHOT;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_cichar _license[] SEC("license") = "GPL";
474