18c2ecf20Sopenharmony_ci#include <uapi/linux/bpf.h>
28c2ecf20Sopenharmony_ci#include <uapi/linux/in.h>
38c2ecf20Sopenharmony_ci#include <uapi/linux/if.h>
48c2ecf20Sopenharmony_ci#include <uapi/linux/if_ether.h>
58c2ecf20Sopenharmony_ci#include <uapi/linux/ip.h>
68c2ecf20Sopenharmony_ci#include <uapi/linux/ipv6.h>
78c2ecf20Sopenharmony_ci#include <uapi/linux/if_tunnel.h>
88c2ecf20Sopenharmony_ci#include <bpf/bpf_helpers.h>
98c2ecf20Sopenharmony_ci#include "bpf_legacy.h"
108c2ecf20Sopenharmony_ci#define IP_MF		0x2000
118c2ecf20Sopenharmony_ci#define IP_OFFSET	0x1FFF
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistruct vlan_hdr {
148c2ecf20Sopenharmony_ci	__be16 h_vlan_TCI;
158c2ecf20Sopenharmony_ci	__be16 h_vlan_encapsulated_proto;
168c2ecf20Sopenharmony_ci};
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistruct flow_key_record {
198c2ecf20Sopenharmony_ci	__be32 src;
208c2ecf20Sopenharmony_ci	__be32 dst;
218c2ecf20Sopenharmony_ci	union {
228c2ecf20Sopenharmony_ci		__be32 ports;
238c2ecf20Sopenharmony_ci		__be16 port16[2];
248c2ecf20Sopenharmony_ci	};
258c2ecf20Sopenharmony_ci	__u16 thoff;
268c2ecf20Sopenharmony_ci	__u8 ip_proto;
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic inline int proto_ports_offset(__u64 proto)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	switch (proto) {
328c2ecf20Sopenharmony_ci	case IPPROTO_TCP:
338c2ecf20Sopenharmony_ci	case IPPROTO_UDP:
348c2ecf20Sopenharmony_ci	case IPPROTO_DCCP:
358c2ecf20Sopenharmony_ci	case IPPROTO_ESP:
368c2ecf20Sopenharmony_ci	case IPPROTO_SCTP:
378c2ecf20Sopenharmony_ci	case IPPROTO_UDPLITE:
388c2ecf20Sopenharmony_ci		return 0;
398c2ecf20Sopenharmony_ci	case IPPROTO_AH:
408c2ecf20Sopenharmony_ci		return 4;
418c2ecf20Sopenharmony_ci	default:
428c2ecf20Sopenharmony_ci		return 0;
438c2ecf20Sopenharmony_ci	}
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic inline int ip_is_fragment(struct __sk_buff *ctx, __u64 nhoff)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off))
498c2ecf20Sopenharmony_ci		& (IP_MF | IP_OFFSET);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic inline __u32 ipv6_addr_hash(struct __sk_buff *ctx, __u64 off)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	__u64 w0 = load_word(ctx, off);
558c2ecf20Sopenharmony_ci	__u64 w1 = load_word(ctx, off + 4);
568c2ecf20Sopenharmony_ci	__u64 w2 = load_word(ctx, off + 8);
578c2ecf20Sopenharmony_ci	__u64 w3 = load_word(ctx, off + 12);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return (__u32)(w0 ^ w1 ^ w2 ^ w3);
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic inline __u64 parse_ip(struct __sk_buff *skb, __u64 nhoff, __u64 *ip_proto,
638c2ecf20Sopenharmony_ci			     struct flow_key_record *flow)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	__u64 verlen;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (unlikely(ip_is_fragment(skb, nhoff)))
688c2ecf20Sopenharmony_ci		*ip_proto = 0;
698c2ecf20Sopenharmony_ci	else
708c2ecf20Sopenharmony_ci		*ip_proto = load_byte(skb, nhoff + offsetof(struct iphdr, protocol));
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (*ip_proto != IPPROTO_GRE) {
738c2ecf20Sopenharmony_ci		flow->src = load_word(skb, nhoff + offsetof(struct iphdr, saddr));
748c2ecf20Sopenharmony_ci		flow->dst = load_word(skb, nhoff + offsetof(struct iphdr, daddr));
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	verlen = load_byte(skb, nhoff + 0/*offsetof(struct iphdr, ihl)*/);
788c2ecf20Sopenharmony_ci	if (likely(verlen == 0x45))
798c2ecf20Sopenharmony_ci		nhoff += 20;
808c2ecf20Sopenharmony_ci	else
818c2ecf20Sopenharmony_ci		nhoff += (verlen & 0xF) << 2;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return nhoff;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic inline __u64 parse_ipv6(struct __sk_buff *skb, __u64 nhoff, __u64 *ip_proto,
878c2ecf20Sopenharmony_ci			       struct flow_key_record *flow)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	*ip_proto = load_byte(skb,
908c2ecf20Sopenharmony_ci			      nhoff + offsetof(struct ipv6hdr, nexthdr));
918c2ecf20Sopenharmony_ci	flow->src = ipv6_addr_hash(skb,
928c2ecf20Sopenharmony_ci				   nhoff + offsetof(struct ipv6hdr, saddr));
938c2ecf20Sopenharmony_ci	flow->dst = ipv6_addr_hash(skb,
948c2ecf20Sopenharmony_ci				   nhoff + offsetof(struct ipv6hdr, daddr));
958c2ecf20Sopenharmony_ci	nhoff += sizeof(struct ipv6hdr);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return nhoff;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic inline bool flow_dissector(struct __sk_buff *skb,
1018c2ecf20Sopenharmony_ci				  struct flow_key_record *flow)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	__u64 nhoff = ETH_HLEN;
1048c2ecf20Sopenharmony_ci	__u64 ip_proto;
1058c2ecf20Sopenharmony_ci	__u64 proto = load_half(skb, 12);
1068c2ecf20Sopenharmony_ci	int poff;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (proto == ETH_P_8021AD) {
1098c2ecf20Sopenharmony_ci		proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
1108c2ecf20Sopenharmony_ci							h_vlan_encapsulated_proto));
1118c2ecf20Sopenharmony_ci		nhoff += sizeof(struct vlan_hdr);
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (proto == ETH_P_8021Q) {
1158c2ecf20Sopenharmony_ci		proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
1168c2ecf20Sopenharmony_ci							h_vlan_encapsulated_proto));
1178c2ecf20Sopenharmony_ci		nhoff += sizeof(struct vlan_hdr);
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (likely(proto == ETH_P_IP))
1218c2ecf20Sopenharmony_ci		nhoff = parse_ip(skb, nhoff, &ip_proto, flow);
1228c2ecf20Sopenharmony_ci	else if (proto == ETH_P_IPV6)
1238c2ecf20Sopenharmony_ci		nhoff = parse_ipv6(skb, nhoff, &ip_proto, flow);
1248c2ecf20Sopenharmony_ci	else
1258c2ecf20Sopenharmony_ci		return false;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	switch (ip_proto) {
1288c2ecf20Sopenharmony_ci	case IPPROTO_GRE: {
1298c2ecf20Sopenharmony_ci		struct gre_hdr {
1308c2ecf20Sopenharmony_ci			__be16 flags;
1318c2ecf20Sopenharmony_ci			__be16 proto;
1328c2ecf20Sopenharmony_ci		};
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		__u64 gre_flags = load_half(skb,
1358c2ecf20Sopenharmony_ci					    nhoff + offsetof(struct gre_hdr, flags));
1368c2ecf20Sopenharmony_ci		__u64 gre_proto = load_half(skb,
1378c2ecf20Sopenharmony_ci					    nhoff + offsetof(struct gre_hdr, proto));
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		if (gre_flags & (GRE_VERSION|GRE_ROUTING))
1408c2ecf20Sopenharmony_ci			break;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci		proto = gre_proto;
1438c2ecf20Sopenharmony_ci		nhoff += 4;
1448c2ecf20Sopenharmony_ci		if (gre_flags & GRE_CSUM)
1458c2ecf20Sopenharmony_ci			nhoff += 4;
1468c2ecf20Sopenharmony_ci		if (gre_flags & GRE_KEY)
1478c2ecf20Sopenharmony_ci			nhoff += 4;
1488c2ecf20Sopenharmony_ci		if (gre_flags & GRE_SEQ)
1498c2ecf20Sopenharmony_ci			nhoff += 4;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		if (proto == ETH_P_8021Q) {
1528c2ecf20Sopenharmony_ci			proto = load_half(skb,
1538c2ecf20Sopenharmony_ci					  nhoff + offsetof(struct vlan_hdr,
1548c2ecf20Sopenharmony_ci							   h_vlan_encapsulated_proto));
1558c2ecf20Sopenharmony_ci			nhoff += sizeof(struct vlan_hdr);
1568c2ecf20Sopenharmony_ci		}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		if (proto == ETH_P_IP)
1598c2ecf20Sopenharmony_ci			nhoff = parse_ip(skb, nhoff, &ip_proto, flow);
1608c2ecf20Sopenharmony_ci		else if (proto == ETH_P_IPV6)
1618c2ecf20Sopenharmony_ci			nhoff = parse_ipv6(skb, nhoff, &ip_proto, flow);
1628c2ecf20Sopenharmony_ci		else
1638c2ecf20Sopenharmony_ci			return false;
1648c2ecf20Sopenharmony_ci		break;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci	case IPPROTO_IPIP:
1678c2ecf20Sopenharmony_ci		nhoff = parse_ip(skb, nhoff, &ip_proto, flow);
1688c2ecf20Sopenharmony_ci		break;
1698c2ecf20Sopenharmony_ci	case IPPROTO_IPV6:
1708c2ecf20Sopenharmony_ci		nhoff = parse_ipv6(skb, nhoff, &ip_proto, flow);
1718c2ecf20Sopenharmony_ci		break;
1728c2ecf20Sopenharmony_ci	default:
1738c2ecf20Sopenharmony_ci		break;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	flow->ip_proto = ip_proto;
1778c2ecf20Sopenharmony_ci	poff = proto_ports_offset(ip_proto);
1788c2ecf20Sopenharmony_ci	if (poff >= 0) {
1798c2ecf20Sopenharmony_ci		nhoff += poff;
1808c2ecf20Sopenharmony_ci		flow->ports = load_word(skb, nhoff);
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	flow->thoff = (__u16) nhoff;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return true;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistruct pair {
1898c2ecf20Sopenharmony_ci	long packets;
1908c2ecf20Sopenharmony_ci	long bytes;
1918c2ecf20Sopenharmony_ci};
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistruct {
1948c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_HASH);
1958c2ecf20Sopenharmony_ci	__type(key, __be32);
1968c2ecf20Sopenharmony_ci	__type(value, struct pair);
1978c2ecf20Sopenharmony_ci	__uint(max_entries, 1024);
1988c2ecf20Sopenharmony_ci} hash_map SEC(".maps");
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciSEC("socket2")
2018c2ecf20Sopenharmony_ciint bpf_prog2(struct __sk_buff *skb)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct flow_key_record flow = {};
2048c2ecf20Sopenharmony_ci	struct pair *value;
2058c2ecf20Sopenharmony_ci	u32 key;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (!flow_dissector(skb, &flow))
2088c2ecf20Sopenharmony_ci		return 0;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	key = flow.dst;
2118c2ecf20Sopenharmony_ci	value = bpf_map_lookup_elem(&hash_map, &key);
2128c2ecf20Sopenharmony_ci	if (value) {
2138c2ecf20Sopenharmony_ci		__sync_fetch_and_add(&value->packets, 1);
2148c2ecf20Sopenharmony_ci		__sync_fetch_and_add(&value->bytes, skb->len);
2158c2ecf20Sopenharmony_ci	} else {
2168c2ecf20Sopenharmony_ci		struct pair val = {1, skb->len};
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci		bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci	return 0;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cichar _license[] SEC("license") = "GPL";
224