162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <limits.h> 362306a36Sopenharmony_ci#include <stddef.h> 462306a36Sopenharmony_ci#include <stdbool.h> 562306a36Sopenharmony_ci#include <string.h> 662306a36Sopenharmony_ci#include <linux/pkt_cls.h> 762306a36Sopenharmony_ci#include <linux/bpf.h> 862306a36Sopenharmony_ci#include <linux/in.h> 962306a36Sopenharmony_ci#include <linux/if_ether.h> 1062306a36Sopenharmony_ci#include <linux/icmp.h> 1162306a36Sopenharmony_ci#include <linux/ip.h> 1262306a36Sopenharmony_ci#include <linux/ipv6.h> 1362306a36Sopenharmony_ci#include <linux/tcp.h> 1462306a36Sopenharmony_ci#include <linux/udp.h> 1562306a36Sopenharmony_ci#include <linux/if_packet.h> 1662306a36Sopenharmony_ci#include <sys/socket.h> 1762306a36Sopenharmony_ci#include <linux/if_tunnel.h> 1862306a36Sopenharmony_ci#include <linux/mpls.h> 1962306a36Sopenharmony_ci#include <bpf/bpf_helpers.h> 2062306a36Sopenharmony_ci#include <bpf/bpf_endian.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define PROG(F) PROG_(F, _##F) 2362306a36Sopenharmony_ci#define PROG_(NUM, NAME) SEC("flow_dissector") int flow_dissector_##NUM 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define FLOW_CONTINUE_SADDR 0x7f00007f /* 127.0.0.127 */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* These are the identifiers of the BPF programs that will be used in tail 2862306a36Sopenharmony_ci * calls. Name is limited to 16 characters, with the terminating character and 2962306a36Sopenharmony_ci * bpf_func_ above, we have only 6 to work with, anything after will be cropped. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci#define IP 0 3262306a36Sopenharmony_ci#define IPV6 1 3362306a36Sopenharmony_ci#define IPV6OP 2 /* Destination/Hop-by-Hop Options IPv6 Ext. Header */ 3462306a36Sopenharmony_ci#define IPV6FR 3 /* Fragmentation IPv6 Extension Header */ 3562306a36Sopenharmony_ci#define MPLS 4 3662306a36Sopenharmony_ci#define VLAN 5 3762306a36Sopenharmony_ci#define MAX_PROG 6 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define IP_MF 0x2000 4062306a36Sopenharmony_ci#define IP_OFFSET 0x1FFF 4162306a36Sopenharmony_ci#define IP6_MF 0x0001 4262306a36Sopenharmony_ci#define IP6_OFFSET 0xFFF8 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct vlan_hdr { 4562306a36Sopenharmony_ci __be16 h_vlan_TCI; 4662306a36Sopenharmony_ci __be16 h_vlan_encapsulated_proto; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct gre_hdr { 5062306a36Sopenharmony_ci __be16 flags; 5162306a36Sopenharmony_ci __be16 proto; 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct frag_hdr { 5562306a36Sopenharmony_ci __u8 nexthdr; 5662306a36Sopenharmony_ci __u8 reserved; 5762306a36Sopenharmony_ci __be16 frag_off; 5862306a36Sopenharmony_ci __be32 identification; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct { 6262306a36Sopenharmony_ci __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 6362306a36Sopenharmony_ci __uint(max_entries, MAX_PROG); 6462306a36Sopenharmony_ci __uint(key_size, sizeof(__u32)); 6562306a36Sopenharmony_ci __uint(value_size, sizeof(__u32)); 6662306a36Sopenharmony_ci} jmp_table SEC(".maps"); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct { 6962306a36Sopenharmony_ci __uint(type, BPF_MAP_TYPE_HASH); 7062306a36Sopenharmony_ci __uint(max_entries, 1024); 7162306a36Sopenharmony_ci __type(key, __u32); 7262306a36Sopenharmony_ci __type(value, struct bpf_flow_keys); 7362306a36Sopenharmony_ci} last_dissection SEC(".maps"); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic __always_inline int export_flow_keys(struct bpf_flow_keys *keys, 7662306a36Sopenharmony_ci int ret) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci __u32 key = (__u32)(keys->sport) << 16 | keys->dport; 7962306a36Sopenharmony_ci struct bpf_flow_keys val; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci memcpy(&val, keys, sizeof(val)); 8262306a36Sopenharmony_ci bpf_map_update_elem(&last_dissection, &key, &val, BPF_ANY); 8362306a36Sopenharmony_ci return ret; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define IPV6_FLOWLABEL_MASK __bpf_constant_htonl(0x000FFFFF) 8762306a36Sopenharmony_cistatic inline __be32 ip6_flowlabel(const struct ipv6hdr *hdr) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci return *(__be32 *)hdr & IPV6_FLOWLABEL_MASK; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic __always_inline void *bpf_flow_dissect_get_header(struct __sk_buff *skb, 9362306a36Sopenharmony_ci __u16 hdr_size, 9462306a36Sopenharmony_ci void *buffer) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci void *data_end = (void *)(long)skb->data_end; 9762306a36Sopenharmony_ci void *data = (void *)(long)skb->data; 9862306a36Sopenharmony_ci __u16 thoff = skb->flow_keys->thoff; 9962306a36Sopenharmony_ci __u8 *hdr; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Verifies this variable offset does not overflow */ 10262306a36Sopenharmony_ci if (thoff > (USHRT_MAX - hdr_size)) 10362306a36Sopenharmony_ci return NULL; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci hdr = data + thoff; 10662306a36Sopenharmony_ci if (hdr + hdr_size <= data_end) 10762306a36Sopenharmony_ci return hdr; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (bpf_skb_load_bytes(skb, thoff, buffer, hdr_size)) 11062306a36Sopenharmony_ci return NULL; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return buffer; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* Dispatches on ETHERTYPE */ 11662306a36Sopenharmony_cistatic __always_inline int parse_eth_proto(struct __sk_buff *skb, __be16 proto) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct bpf_flow_keys *keys = skb->flow_keys; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci switch (proto) { 12162306a36Sopenharmony_ci case bpf_htons(ETH_P_IP): 12262306a36Sopenharmony_ci bpf_tail_call_static(skb, &jmp_table, IP); 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci case bpf_htons(ETH_P_IPV6): 12562306a36Sopenharmony_ci bpf_tail_call_static(skb, &jmp_table, IPV6); 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci case bpf_htons(ETH_P_MPLS_MC): 12862306a36Sopenharmony_ci case bpf_htons(ETH_P_MPLS_UC): 12962306a36Sopenharmony_ci bpf_tail_call_static(skb, &jmp_table, MPLS); 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci case bpf_htons(ETH_P_8021Q): 13262306a36Sopenharmony_ci case bpf_htons(ETH_P_8021AD): 13362306a36Sopenharmony_ci bpf_tail_call_static(skb, &jmp_table, VLAN); 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci default: 13662306a36Sopenharmony_ci /* Protocol not supported */ 13762306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ciSEC("flow_dissector") 14462306a36Sopenharmony_ciint _dissect(struct __sk_buff *skb) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct bpf_flow_keys *keys = skb->flow_keys; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (keys->n_proto == bpf_htons(ETH_P_IP)) { 14962306a36Sopenharmony_ci /* IP traffic from FLOW_CONTINUE_SADDR falls-back to 15062306a36Sopenharmony_ci * standard dissector 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci struct iphdr *iph, _iph; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci iph = bpf_flow_dissect_get_header(skb, sizeof(*iph), &_iph); 15562306a36Sopenharmony_ci if (iph && iph->ihl == 5 && 15662306a36Sopenharmony_ci iph->saddr == bpf_htonl(FLOW_CONTINUE_SADDR)) { 15762306a36Sopenharmony_ci return BPF_FLOW_DISSECTOR_CONTINUE; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return parse_eth_proto(skb, keys->n_proto); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* Parses on IPPROTO_* */ 16562306a36Sopenharmony_cistatic __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct bpf_flow_keys *keys = skb->flow_keys; 16862306a36Sopenharmony_ci void *data_end = (void *)(long)skb->data_end; 16962306a36Sopenharmony_ci struct icmphdr *icmp, _icmp; 17062306a36Sopenharmony_ci struct gre_hdr *gre, _gre; 17162306a36Sopenharmony_ci struct ethhdr *eth, _eth; 17262306a36Sopenharmony_ci struct tcphdr *tcp, _tcp; 17362306a36Sopenharmony_ci struct udphdr *udp, _udp; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci switch (proto) { 17662306a36Sopenharmony_ci case IPPROTO_ICMP: 17762306a36Sopenharmony_ci icmp = bpf_flow_dissect_get_header(skb, sizeof(*icmp), &_icmp); 17862306a36Sopenharmony_ci if (!icmp) 17962306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 18062306a36Sopenharmony_ci return export_flow_keys(keys, BPF_OK); 18162306a36Sopenharmony_ci case IPPROTO_IPIP: 18262306a36Sopenharmony_ci keys->is_encap = true; 18362306a36Sopenharmony_ci if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP) 18462306a36Sopenharmony_ci return export_flow_keys(keys, BPF_OK); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return parse_eth_proto(skb, bpf_htons(ETH_P_IP)); 18762306a36Sopenharmony_ci case IPPROTO_IPV6: 18862306a36Sopenharmony_ci keys->is_encap = true; 18962306a36Sopenharmony_ci if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP) 19062306a36Sopenharmony_ci return export_flow_keys(keys, BPF_OK); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return parse_eth_proto(skb, bpf_htons(ETH_P_IPV6)); 19362306a36Sopenharmony_ci case IPPROTO_GRE: 19462306a36Sopenharmony_ci gre = bpf_flow_dissect_get_header(skb, sizeof(*gre), &_gre); 19562306a36Sopenharmony_ci if (!gre) 19662306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (bpf_htons(gre->flags & GRE_VERSION)) 19962306a36Sopenharmony_ci /* Only inspect standard GRE packets with version 0 */ 20062306a36Sopenharmony_ci return export_flow_keys(keys, BPF_OK); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci keys->thoff += sizeof(*gre); /* Step over GRE Flags and Proto */ 20362306a36Sopenharmony_ci if (GRE_IS_CSUM(gre->flags)) 20462306a36Sopenharmony_ci keys->thoff += 4; /* Step over chksum and Padding */ 20562306a36Sopenharmony_ci if (GRE_IS_KEY(gre->flags)) 20662306a36Sopenharmony_ci keys->thoff += 4; /* Step over key */ 20762306a36Sopenharmony_ci if (GRE_IS_SEQ(gre->flags)) 20862306a36Sopenharmony_ci keys->thoff += 4; /* Step over sequence number */ 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci keys->is_encap = true; 21162306a36Sopenharmony_ci if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP) 21262306a36Sopenharmony_ci return export_flow_keys(keys, BPF_OK); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (gre->proto == bpf_htons(ETH_P_TEB)) { 21562306a36Sopenharmony_ci eth = bpf_flow_dissect_get_header(skb, sizeof(*eth), 21662306a36Sopenharmony_ci &_eth); 21762306a36Sopenharmony_ci if (!eth) 21862306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci keys->thoff += sizeof(*eth); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return parse_eth_proto(skb, eth->h_proto); 22362306a36Sopenharmony_ci } else { 22462306a36Sopenharmony_ci return parse_eth_proto(skb, gre->proto); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci case IPPROTO_TCP: 22762306a36Sopenharmony_ci tcp = bpf_flow_dissect_get_header(skb, sizeof(*tcp), &_tcp); 22862306a36Sopenharmony_ci if (!tcp) 22962306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (tcp->doff < 5) 23262306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if ((__u8 *)tcp + (tcp->doff << 2) > data_end) 23562306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci keys->sport = tcp->source; 23862306a36Sopenharmony_ci keys->dport = tcp->dest; 23962306a36Sopenharmony_ci return export_flow_keys(keys, BPF_OK); 24062306a36Sopenharmony_ci case IPPROTO_UDP: 24162306a36Sopenharmony_ci case IPPROTO_UDPLITE: 24262306a36Sopenharmony_ci udp = bpf_flow_dissect_get_header(skb, sizeof(*udp), &_udp); 24362306a36Sopenharmony_ci if (!udp) 24462306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci keys->sport = udp->source; 24762306a36Sopenharmony_ci keys->dport = udp->dest; 24862306a36Sopenharmony_ci return export_flow_keys(keys, BPF_OK); 24962306a36Sopenharmony_ci default: 25062306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic __always_inline int parse_ipv6_proto(struct __sk_buff *skb, __u8 nexthdr) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct bpf_flow_keys *keys = skb->flow_keys; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci switch (nexthdr) { 26162306a36Sopenharmony_ci case IPPROTO_HOPOPTS: 26262306a36Sopenharmony_ci case IPPROTO_DSTOPTS: 26362306a36Sopenharmony_ci bpf_tail_call_static(skb, &jmp_table, IPV6OP); 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci case IPPROTO_FRAGMENT: 26662306a36Sopenharmony_ci bpf_tail_call_static(skb, &jmp_table, IPV6FR); 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci default: 26962306a36Sopenharmony_ci return parse_ip_proto(skb, nexthdr); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ciPROG(IP)(struct __sk_buff *skb) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci void *data_end = (void *)(long)skb->data_end; 27862306a36Sopenharmony_ci struct bpf_flow_keys *keys = skb->flow_keys; 27962306a36Sopenharmony_ci void *data = (void *)(long)skb->data; 28062306a36Sopenharmony_ci struct iphdr *iph, _iph; 28162306a36Sopenharmony_ci bool done = false; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci iph = bpf_flow_dissect_get_header(skb, sizeof(*iph), &_iph); 28462306a36Sopenharmony_ci if (!iph) 28562306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* IP header cannot be smaller than 20 bytes */ 28862306a36Sopenharmony_ci if (iph->ihl < 5) 28962306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci keys->addr_proto = ETH_P_IP; 29262306a36Sopenharmony_ci keys->ipv4_src = iph->saddr; 29362306a36Sopenharmony_ci keys->ipv4_dst = iph->daddr; 29462306a36Sopenharmony_ci keys->ip_proto = iph->protocol; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci keys->thoff += iph->ihl << 2; 29762306a36Sopenharmony_ci if (data + keys->thoff > data_end) 29862306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (iph->frag_off & bpf_htons(IP_MF | IP_OFFSET)) { 30162306a36Sopenharmony_ci keys->is_frag = true; 30262306a36Sopenharmony_ci if (iph->frag_off & bpf_htons(IP_OFFSET)) { 30362306a36Sopenharmony_ci /* From second fragment on, packets do not have headers 30462306a36Sopenharmony_ci * we can parse. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci done = true; 30762306a36Sopenharmony_ci } else { 30862306a36Sopenharmony_ci keys->is_first_frag = true; 30962306a36Sopenharmony_ci /* No need to parse fragmented packet unless 31062306a36Sopenharmony_ci * explicitly asked for. 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci if (!(keys->flags & 31362306a36Sopenharmony_ci BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG)) 31462306a36Sopenharmony_ci done = true; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (done) 31962306a36Sopenharmony_ci return export_flow_keys(keys, BPF_OK); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return parse_ip_proto(skb, iph->protocol); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ciPROG(IPV6)(struct __sk_buff *skb) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct bpf_flow_keys *keys = skb->flow_keys; 32762306a36Sopenharmony_ci struct ipv6hdr *ip6h, _ip6h; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci ip6h = bpf_flow_dissect_get_header(skb, sizeof(*ip6h), &_ip6h); 33062306a36Sopenharmony_ci if (!ip6h) 33162306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci keys->addr_proto = ETH_P_IPV6; 33462306a36Sopenharmony_ci memcpy(&keys->ipv6_src, &ip6h->saddr, 2*sizeof(ip6h->saddr)); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci keys->thoff += sizeof(struct ipv6hdr); 33762306a36Sopenharmony_ci keys->ip_proto = ip6h->nexthdr; 33862306a36Sopenharmony_ci keys->flow_label = ip6_flowlabel(ip6h); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (keys->flow_label && keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL) 34162306a36Sopenharmony_ci return export_flow_keys(keys, BPF_OK); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return parse_ipv6_proto(skb, ip6h->nexthdr); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ciPROG(IPV6OP)(struct __sk_buff *skb) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct bpf_flow_keys *keys = skb->flow_keys; 34962306a36Sopenharmony_ci struct ipv6_opt_hdr *ip6h, _ip6h; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci ip6h = bpf_flow_dissect_get_header(skb, sizeof(*ip6h), &_ip6h); 35262306a36Sopenharmony_ci if (!ip6h) 35362306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* hlen is in 8-octets and does not include the first 8 bytes 35662306a36Sopenharmony_ci * of the header 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ci keys->thoff += (1 + ip6h->hdrlen) << 3; 35962306a36Sopenharmony_ci keys->ip_proto = ip6h->nexthdr; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return parse_ipv6_proto(skb, ip6h->nexthdr); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ciPROG(IPV6FR)(struct __sk_buff *skb) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct bpf_flow_keys *keys = skb->flow_keys; 36762306a36Sopenharmony_ci struct frag_hdr *fragh, _fragh; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci fragh = bpf_flow_dissect_get_header(skb, sizeof(*fragh), &_fragh); 37062306a36Sopenharmony_ci if (!fragh) 37162306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci keys->thoff += sizeof(*fragh); 37462306a36Sopenharmony_ci keys->is_frag = true; 37562306a36Sopenharmony_ci keys->ip_proto = fragh->nexthdr; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (!(fragh->frag_off & bpf_htons(IP6_OFFSET))) { 37862306a36Sopenharmony_ci keys->is_first_frag = true; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* No need to parse fragmented packet unless 38162306a36Sopenharmony_ci * explicitly asked for. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci if (!(keys->flags & BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG)) 38462306a36Sopenharmony_ci return export_flow_keys(keys, BPF_OK); 38562306a36Sopenharmony_ci } else { 38662306a36Sopenharmony_ci return export_flow_keys(keys, BPF_OK); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return parse_ipv6_proto(skb, fragh->nexthdr); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ciPROG(MPLS)(struct __sk_buff *skb) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct bpf_flow_keys *keys = skb->flow_keys; 39562306a36Sopenharmony_ci struct mpls_label *mpls, _mpls; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci mpls = bpf_flow_dissect_get_header(skb, sizeof(*mpls), &_mpls); 39862306a36Sopenharmony_ci if (!mpls) 39962306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return export_flow_keys(keys, BPF_OK); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ciPROG(VLAN)(struct __sk_buff *skb) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct bpf_flow_keys *keys = skb->flow_keys; 40762306a36Sopenharmony_ci struct vlan_hdr *vlan, _vlan; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Account for double-tagging */ 41062306a36Sopenharmony_ci if (keys->n_proto == bpf_htons(ETH_P_8021AD)) { 41162306a36Sopenharmony_ci vlan = bpf_flow_dissect_get_header(skb, sizeof(*vlan), &_vlan); 41262306a36Sopenharmony_ci if (!vlan) 41362306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (vlan->h_vlan_encapsulated_proto != bpf_htons(ETH_P_8021Q)) 41662306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci keys->nhoff += sizeof(*vlan); 41962306a36Sopenharmony_ci keys->thoff += sizeof(*vlan); 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci vlan = bpf_flow_dissect_get_header(skb, sizeof(*vlan), &_vlan); 42362306a36Sopenharmony_ci if (!vlan) 42462306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci keys->nhoff += sizeof(*vlan); 42762306a36Sopenharmony_ci keys->thoff += sizeof(*vlan); 42862306a36Sopenharmony_ci /* Only allow 8021AD + 8021Q double tagging and no triple tagging.*/ 42962306a36Sopenharmony_ci if (vlan->h_vlan_encapsulated_proto == bpf_htons(ETH_P_8021AD) || 43062306a36Sopenharmony_ci vlan->h_vlan_encapsulated_proto == bpf_htons(ETH_P_8021Q)) 43162306a36Sopenharmony_ci return export_flow_keys(keys, BPF_DROP); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci keys->n_proto = vlan->h_vlan_encapsulated_proto; 43462306a36Sopenharmony_ci return parse_eth_proto(skb, vlan->h_vlan_encapsulated_proto); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cichar __license[] SEC("license") = "GPL"; 438