18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <limits.h>
38c2ecf20Sopenharmony_ci#include <stddef.h>
48c2ecf20Sopenharmony_ci#include <stdbool.h>
58c2ecf20Sopenharmony_ci#include <string.h>
68c2ecf20Sopenharmony_ci#include <linux/pkt_cls.h>
78c2ecf20Sopenharmony_ci#include <linux/bpf.h>
88c2ecf20Sopenharmony_ci#include <linux/in.h>
98c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
108c2ecf20Sopenharmony_ci#include <linux/icmp.h>
118c2ecf20Sopenharmony_ci#include <linux/ip.h>
128c2ecf20Sopenharmony_ci#include <linux/ipv6.h>
138c2ecf20Sopenharmony_ci#include <linux/tcp.h>
148c2ecf20Sopenharmony_ci#include <linux/udp.h>
158c2ecf20Sopenharmony_ci#include <linux/if_packet.h>
168c2ecf20Sopenharmony_ci#include <sys/socket.h>
178c2ecf20Sopenharmony_ci#include <linux/if_tunnel.h>
188c2ecf20Sopenharmony_ci#include <linux/mpls.h>
198c2ecf20Sopenharmony_ci#include <bpf/bpf_helpers.h>
208c2ecf20Sopenharmony_ci#include <bpf/bpf_endian.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ciint _version SEC("version") = 1;
238c2ecf20Sopenharmony_ci#define PROG(F) PROG_(F, _##F)
248c2ecf20Sopenharmony_ci#define PROG_(NUM, NAME) SEC("flow_dissector/"#NUM) int bpf_func##NAME
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/* These are the identifiers of the BPF programs that will be used in tail
278c2ecf20Sopenharmony_ci * calls. Name is limited to 16 characters, with the terminating character and
288c2ecf20Sopenharmony_ci * bpf_func_ above, we have only 6 to work with, anything after will be cropped.
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci#define IP		0
318c2ecf20Sopenharmony_ci#define IPV6		1
328c2ecf20Sopenharmony_ci#define IPV6OP		2 /* Destination/Hop-by-Hop Options IPv6 Ext. Header */
338c2ecf20Sopenharmony_ci#define IPV6FR		3 /* Fragmentation IPv6 Extension Header */
348c2ecf20Sopenharmony_ci#define MPLS		4
358c2ecf20Sopenharmony_ci#define VLAN		5
368c2ecf20Sopenharmony_ci#define MAX_PROG	6
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define IP_MF		0x2000
398c2ecf20Sopenharmony_ci#define IP_OFFSET	0x1FFF
408c2ecf20Sopenharmony_ci#define IP6_MF		0x0001
418c2ecf20Sopenharmony_ci#define IP6_OFFSET	0xFFF8
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct vlan_hdr {
448c2ecf20Sopenharmony_ci	__be16 h_vlan_TCI;
458c2ecf20Sopenharmony_ci	__be16 h_vlan_encapsulated_proto;
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct gre_hdr {
498c2ecf20Sopenharmony_ci	__be16 flags;
508c2ecf20Sopenharmony_ci	__be16 proto;
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistruct frag_hdr {
548c2ecf20Sopenharmony_ci	__u8 nexthdr;
558c2ecf20Sopenharmony_ci	__u8 reserved;
568c2ecf20Sopenharmony_ci	__be16 frag_off;
578c2ecf20Sopenharmony_ci	__be32 identification;
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistruct {
618c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
628c2ecf20Sopenharmony_ci	__uint(max_entries, MAX_PROG);
638c2ecf20Sopenharmony_ci	__uint(key_size, sizeof(__u32));
648c2ecf20Sopenharmony_ci	__uint(value_size, sizeof(__u32));
658c2ecf20Sopenharmony_ci} jmp_table SEC(".maps");
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistruct {
688c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_HASH);
698c2ecf20Sopenharmony_ci	__uint(max_entries, 1024);
708c2ecf20Sopenharmony_ci	__type(key, __u32);
718c2ecf20Sopenharmony_ci	__type(value, struct bpf_flow_keys);
728c2ecf20Sopenharmony_ci} last_dissection SEC(".maps");
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic __always_inline int export_flow_keys(struct bpf_flow_keys *keys,
758c2ecf20Sopenharmony_ci					    int ret)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	__u32 key = (__u32)(keys->sport) << 16 | keys->dport;
788c2ecf20Sopenharmony_ci	struct bpf_flow_keys val;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	memcpy(&val, keys, sizeof(val));
818c2ecf20Sopenharmony_ci	bpf_map_update_elem(&last_dissection, &key, &val, BPF_ANY);
828c2ecf20Sopenharmony_ci	return ret;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci#define IPV6_FLOWLABEL_MASK		__bpf_constant_htonl(0x000FFFFF)
868c2ecf20Sopenharmony_cistatic inline __be32 ip6_flowlabel(const struct ipv6hdr *hdr)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	return *(__be32 *)hdr & IPV6_FLOWLABEL_MASK;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic __always_inline void *bpf_flow_dissect_get_header(struct __sk_buff *skb,
928c2ecf20Sopenharmony_ci							 __u16 hdr_size,
938c2ecf20Sopenharmony_ci							 void *buffer)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	void *data_end = (void *)(long)skb->data_end;
968c2ecf20Sopenharmony_ci	void *data = (void *)(long)skb->data;
978c2ecf20Sopenharmony_ci	__u16 thoff = skb->flow_keys->thoff;
988c2ecf20Sopenharmony_ci	__u8 *hdr;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* Verifies this variable offset does not overflow */
1018c2ecf20Sopenharmony_ci	if (thoff > (USHRT_MAX - hdr_size))
1028c2ecf20Sopenharmony_ci		return NULL;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	hdr = data + thoff;
1058c2ecf20Sopenharmony_ci	if (hdr + hdr_size <= data_end)
1068c2ecf20Sopenharmony_ci		return hdr;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (bpf_skb_load_bytes(skb, thoff, buffer, hdr_size))
1098c2ecf20Sopenharmony_ci		return NULL;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	return buffer;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* Dispatches on ETHERTYPE */
1158c2ecf20Sopenharmony_cistatic __always_inline int parse_eth_proto(struct __sk_buff *skb, __be16 proto)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct bpf_flow_keys *keys = skb->flow_keys;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	switch (proto) {
1208c2ecf20Sopenharmony_ci	case bpf_htons(ETH_P_IP):
1218c2ecf20Sopenharmony_ci		bpf_tail_call_static(skb, &jmp_table, IP);
1228c2ecf20Sopenharmony_ci		break;
1238c2ecf20Sopenharmony_ci	case bpf_htons(ETH_P_IPV6):
1248c2ecf20Sopenharmony_ci		bpf_tail_call_static(skb, &jmp_table, IPV6);
1258c2ecf20Sopenharmony_ci		break;
1268c2ecf20Sopenharmony_ci	case bpf_htons(ETH_P_MPLS_MC):
1278c2ecf20Sopenharmony_ci	case bpf_htons(ETH_P_MPLS_UC):
1288c2ecf20Sopenharmony_ci		bpf_tail_call_static(skb, &jmp_table, MPLS);
1298c2ecf20Sopenharmony_ci		break;
1308c2ecf20Sopenharmony_ci	case bpf_htons(ETH_P_8021Q):
1318c2ecf20Sopenharmony_ci	case bpf_htons(ETH_P_8021AD):
1328c2ecf20Sopenharmony_ci		bpf_tail_call_static(skb, &jmp_table, VLAN);
1338c2ecf20Sopenharmony_ci		break;
1348c2ecf20Sopenharmony_ci	default:
1358c2ecf20Sopenharmony_ci		/* Protocol not supported */
1368c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_DROP);
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return export_flow_keys(keys, BPF_DROP);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ciSEC("flow_dissector")
1438c2ecf20Sopenharmony_ciint _dissect(struct __sk_buff *skb)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct bpf_flow_keys *keys = skb->flow_keys;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return parse_eth_proto(skb, keys->n_proto);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/* Parses on IPPROTO_* */
1518c2ecf20Sopenharmony_cistatic __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct bpf_flow_keys *keys = skb->flow_keys;
1548c2ecf20Sopenharmony_ci	void *data_end = (void *)(long)skb->data_end;
1558c2ecf20Sopenharmony_ci	struct icmphdr *icmp, _icmp;
1568c2ecf20Sopenharmony_ci	struct gre_hdr *gre, _gre;
1578c2ecf20Sopenharmony_ci	struct ethhdr *eth, _eth;
1588c2ecf20Sopenharmony_ci	struct tcphdr *tcp, _tcp;
1598c2ecf20Sopenharmony_ci	struct udphdr *udp, _udp;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	switch (proto) {
1628c2ecf20Sopenharmony_ci	case IPPROTO_ICMP:
1638c2ecf20Sopenharmony_ci		icmp = bpf_flow_dissect_get_header(skb, sizeof(*icmp), &_icmp);
1648c2ecf20Sopenharmony_ci		if (!icmp)
1658c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_DROP);
1668c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_OK);
1678c2ecf20Sopenharmony_ci	case IPPROTO_IPIP:
1688c2ecf20Sopenharmony_ci		keys->is_encap = true;
1698c2ecf20Sopenharmony_ci		if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP)
1708c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_OK);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci		return parse_eth_proto(skb, bpf_htons(ETH_P_IP));
1738c2ecf20Sopenharmony_ci	case IPPROTO_IPV6:
1748c2ecf20Sopenharmony_ci		keys->is_encap = true;
1758c2ecf20Sopenharmony_ci		if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP)
1768c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_OK);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		return parse_eth_proto(skb, bpf_htons(ETH_P_IPV6));
1798c2ecf20Sopenharmony_ci	case IPPROTO_GRE:
1808c2ecf20Sopenharmony_ci		gre = bpf_flow_dissect_get_header(skb, sizeof(*gre), &_gre);
1818c2ecf20Sopenharmony_ci		if (!gre)
1828c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_DROP);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		if (bpf_htons(gre->flags & GRE_VERSION))
1858c2ecf20Sopenharmony_ci			/* Only inspect standard GRE packets with version 0 */
1868c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_OK);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci		keys->thoff += sizeof(*gre); /* Step over GRE Flags and Proto */
1898c2ecf20Sopenharmony_ci		if (GRE_IS_CSUM(gre->flags))
1908c2ecf20Sopenharmony_ci			keys->thoff += 4; /* Step over chksum and Padding */
1918c2ecf20Sopenharmony_ci		if (GRE_IS_KEY(gre->flags))
1928c2ecf20Sopenharmony_ci			keys->thoff += 4; /* Step over key */
1938c2ecf20Sopenharmony_ci		if (GRE_IS_SEQ(gre->flags))
1948c2ecf20Sopenharmony_ci			keys->thoff += 4; /* Step over sequence number */
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci		keys->is_encap = true;
1978c2ecf20Sopenharmony_ci		if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP)
1988c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_OK);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		if (gre->proto == bpf_htons(ETH_P_TEB)) {
2018c2ecf20Sopenharmony_ci			eth = bpf_flow_dissect_get_header(skb, sizeof(*eth),
2028c2ecf20Sopenharmony_ci							  &_eth);
2038c2ecf20Sopenharmony_ci			if (!eth)
2048c2ecf20Sopenharmony_ci				return export_flow_keys(keys, BPF_DROP);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci			keys->thoff += sizeof(*eth);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci			return parse_eth_proto(skb, eth->h_proto);
2098c2ecf20Sopenharmony_ci		} else {
2108c2ecf20Sopenharmony_ci			return parse_eth_proto(skb, gre->proto);
2118c2ecf20Sopenharmony_ci		}
2128c2ecf20Sopenharmony_ci	case IPPROTO_TCP:
2138c2ecf20Sopenharmony_ci		tcp = bpf_flow_dissect_get_header(skb, sizeof(*tcp), &_tcp);
2148c2ecf20Sopenharmony_ci		if (!tcp)
2158c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_DROP);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		if (tcp->doff < 5)
2188c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_DROP);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		if ((__u8 *)tcp + (tcp->doff << 2) > data_end)
2218c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_DROP);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		keys->sport = tcp->source;
2248c2ecf20Sopenharmony_ci		keys->dport = tcp->dest;
2258c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_OK);
2268c2ecf20Sopenharmony_ci	case IPPROTO_UDP:
2278c2ecf20Sopenharmony_ci	case IPPROTO_UDPLITE:
2288c2ecf20Sopenharmony_ci		udp = bpf_flow_dissect_get_header(skb, sizeof(*udp), &_udp);
2298c2ecf20Sopenharmony_ci		if (!udp)
2308c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_DROP);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		keys->sport = udp->source;
2338c2ecf20Sopenharmony_ci		keys->dport = udp->dest;
2348c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_OK);
2358c2ecf20Sopenharmony_ci	default:
2368c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_DROP);
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	return export_flow_keys(keys, BPF_DROP);
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic __always_inline int parse_ipv6_proto(struct __sk_buff *skb, __u8 nexthdr)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct bpf_flow_keys *keys = skb->flow_keys;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	switch (nexthdr) {
2478c2ecf20Sopenharmony_ci	case IPPROTO_HOPOPTS:
2488c2ecf20Sopenharmony_ci	case IPPROTO_DSTOPTS:
2498c2ecf20Sopenharmony_ci		bpf_tail_call_static(skb, &jmp_table, IPV6OP);
2508c2ecf20Sopenharmony_ci		break;
2518c2ecf20Sopenharmony_ci	case IPPROTO_FRAGMENT:
2528c2ecf20Sopenharmony_ci		bpf_tail_call_static(skb, &jmp_table, IPV6FR);
2538c2ecf20Sopenharmony_ci		break;
2548c2ecf20Sopenharmony_ci	default:
2558c2ecf20Sopenharmony_ci		return parse_ip_proto(skb, nexthdr);
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	return export_flow_keys(keys, BPF_DROP);
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ciPROG(IP)(struct __sk_buff *skb)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	void *data_end = (void *)(long)skb->data_end;
2648c2ecf20Sopenharmony_ci	struct bpf_flow_keys *keys = skb->flow_keys;
2658c2ecf20Sopenharmony_ci	void *data = (void *)(long)skb->data;
2668c2ecf20Sopenharmony_ci	struct iphdr *iph, _iph;
2678c2ecf20Sopenharmony_ci	bool done = false;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	iph = bpf_flow_dissect_get_header(skb, sizeof(*iph), &_iph);
2708c2ecf20Sopenharmony_ci	if (!iph)
2718c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_DROP);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* IP header cannot be smaller than 20 bytes */
2748c2ecf20Sopenharmony_ci	if (iph->ihl < 5)
2758c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_DROP);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	keys->addr_proto = ETH_P_IP;
2788c2ecf20Sopenharmony_ci	keys->ipv4_src = iph->saddr;
2798c2ecf20Sopenharmony_ci	keys->ipv4_dst = iph->daddr;
2808c2ecf20Sopenharmony_ci	keys->ip_proto = iph->protocol;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	keys->thoff += iph->ihl << 2;
2838c2ecf20Sopenharmony_ci	if (data + keys->thoff > data_end)
2848c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_DROP);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (iph->frag_off & bpf_htons(IP_MF | IP_OFFSET)) {
2878c2ecf20Sopenharmony_ci		keys->is_frag = true;
2888c2ecf20Sopenharmony_ci		if (iph->frag_off & bpf_htons(IP_OFFSET)) {
2898c2ecf20Sopenharmony_ci			/* From second fragment on, packets do not have headers
2908c2ecf20Sopenharmony_ci			 * we can parse.
2918c2ecf20Sopenharmony_ci			 */
2928c2ecf20Sopenharmony_ci			done = true;
2938c2ecf20Sopenharmony_ci		} else {
2948c2ecf20Sopenharmony_ci			keys->is_first_frag = true;
2958c2ecf20Sopenharmony_ci			/* No need to parse fragmented packet unless
2968c2ecf20Sopenharmony_ci			 * explicitly asked for.
2978c2ecf20Sopenharmony_ci			 */
2988c2ecf20Sopenharmony_ci			if (!(keys->flags &
2998c2ecf20Sopenharmony_ci			      BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG))
3008c2ecf20Sopenharmony_ci				done = true;
3018c2ecf20Sopenharmony_ci		}
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (done)
3058c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_OK);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	return parse_ip_proto(skb, iph->protocol);
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ciPROG(IPV6)(struct __sk_buff *skb)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	struct bpf_flow_keys *keys = skb->flow_keys;
3138c2ecf20Sopenharmony_ci	struct ipv6hdr *ip6h, _ip6h;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	ip6h = bpf_flow_dissect_get_header(skb, sizeof(*ip6h), &_ip6h);
3168c2ecf20Sopenharmony_ci	if (!ip6h)
3178c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_DROP);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	keys->addr_proto = ETH_P_IPV6;
3208c2ecf20Sopenharmony_ci	memcpy(&keys->ipv6_src, &ip6h->saddr, 2*sizeof(ip6h->saddr));
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	keys->thoff += sizeof(struct ipv6hdr);
3238c2ecf20Sopenharmony_ci	keys->ip_proto = ip6h->nexthdr;
3248c2ecf20Sopenharmony_ci	keys->flow_label = ip6_flowlabel(ip6h);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL)
3278c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_OK);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return parse_ipv6_proto(skb, ip6h->nexthdr);
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ciPROG(IPV6OP)(struct __sk_buff *skb)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	struct bpf_flow_keys *keys = skb->flow_keys;
3358c2ecf20Sopenharmony_ci	struct ipv6_opt_hdr *ip6h, _ip6h;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	ip6h = bpf_flow_dissect_get_header(skb, sizeof(*ip6h), &_ip6h);
3388c2ecf20Sopenharmony_ci	if (!ip6h)
3398c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_DROP);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/* hlen is in 8-octets and does not include the first 8 bytes
3428c2ecf20Sopenharmony_ci	 * of the header
3438c2ecf20Sopenharmony_ci	 */
3448c2ecf20Sopenharmony_ci	keys->thoff += (1 + ip6h->hdrlen) << 3;
3458c2ecf20Sopenharmony_ci	keys->ip_proto = ip6h->nexthdr;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return parse_ipv6_proto(skb, ip6h->nexthdr);
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ciPROG(IPV6FR)(struct __sk_buff *skb)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct bpf_flow_keys *keys = skb->flow_keys;
3538c2ecf20Sopenharmony_ci	struct frag_hdr *fragh, _fragh;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	fragh = bpf_flow_dissect_get_header(skb, sizeof(*fragh), &_fragh);
3568c2ecf20Sopenharmony_ci	if (!fragh)
3578c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_DROP);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	keys->thoff += sizeof(*fragh);
3608c2ecf20Sopenharmony_ci	keys->is_frag = true;
3618c2ecf20Sopenharmony_ci	keys->ip_proto = fragh->nexthdr;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	if (!(fragh->frag_off & bpf_htons(IP6_OFFSET))) {
3648c2ecf20Sopenharmony_ci		keys->is_first_frag = true;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci		/* No need to parse fragmented packet unless
3678c2ecf20Sopenharmony_ci		 * explicitly asked for.
3688c2ecf20Sopenharmony_ci		 */
3698c2ecf20Sopenharmony_ci		if (!(keys->flags & BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG))
3708c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_OK);
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return parse_ipv6_proto(skb, fragh->nexthdr);
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ciPROG(MPLS)(struct __sk_buff *skb)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct bpf_flow_keys *keys = skb->flow_keys;
3798c2ecf20Sopenharmony_ci	struct mpls_label *mpls, _mpls;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	mpls = bpf_flow_dissect_get_header(skb, sizeof(*mpls), &_mpls);
3828c2ecf20Sopenharmony_ci	if (!mpls)
3838c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_DROP);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return export_flow_keys(keys, BPF_OK);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ciPROG(VLAN)(struct __sk_buff *skb)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct bpf_flow_keys *keys = skb->flow_keys;
3918c2ecf20Sopenharmony_ci	struct vlan_hdr *vlan, _vlan;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	/* Account for double-tagging */
3948c2ecf20Sopenharmony_ci	if (keys->n_proto == bpf_htons(ETH_P_8021AD)) {
3958c2ecf20Sopenharmony_ci		vlan = bpf_flow_dissect_get_header(skb, sizeof(*vlan), &_vlan);
3968c2ecf20Sopenharmony_ci		if (!vlan)
3978c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_DROP);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		if (vlan->h_vlan_encapsulated_proto != bpf_htons(ETH_P_8021Q))
4008c2ecf20Sopenharmony_ci			return export_flow_keys(keys, BPF_DROP);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci		keys->nhoff += sizeof(*vlan);
4038c2ecf20Sopenharmony_ci		keys->thoff += sizeof(*vlan);
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	vlan = bpf_flow_dissect_get_header(skb, sizeof(*vlan), &_vlan);
4078c2ecf20Sopenharmony_ci	if (!vlan)
4088c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_DROP);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	keys->nhoff += sizeof(*vlan);
4118c2ecf20Sopenharmony_ci	keys->thoff += sizeof(*vlan);
4128c2ecf20Sopenharmony_ci	/* Only allow 8021AD + 8021Q double tagging and no triple tagging.*/
4138c2ecf20Sopenharmony_ci	if (vlan->h_vlan_encapsulated_proto == bpf_htons(ETH_P_8021AD) ||
4148c2ecf20Sopenharmony_ci	    vlan->h_vlan_encapsulated_proto == bpf_htons(ETH_P_8021Q))
4158c2ecf20Sopenharmony_ci		return export_flow_keys(keys, BPF_DROP);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	keys->n_proto = vlan->h_vlan_encapsulated_proto;
4188c2ecf20Sopenharmony_ci	return parse_eth_proto(skb, vlan->h_vlan_encapsulated_proto);
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cichar __license[] SEC("license") = "GPL";
422