1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
5 #ifndef __BPF_SKB_UTILS_H
6 #define __BPF_SKB_UTILS_H
8 #include <uapi/linux/bpf.h>
9 #include <uapi/linux/if_ether.h>
10 #include <uapi/linux/ip.h>
11 #include <uapi/linux/ipv6.h>
14 #include <bpf/bpf_helpers.h>
15 #include <bpf/bpf_endian.h>
17 struct skb_parser_info
{
18 struct __sk_buff
*skb
;
23 static __always_inline
void *__skb_data(struct __sk_buff
*skb
)
25 return (void *)(long)READ_ONCE(skb
->data
);
28 static __always_inline
void *
29 skb_ptr(struct __sk_buff
*skb
, __u32 offset
, __u32 len
)
31 void *ptr
= __skb_data(skb
) + offset
;
32 void *end
= (void *)(long)(skb
->data_end
);
40 static __always_inline
void *
41 skb_info_ptr(struct skb_parser_info
*info
, __u32 len
)
43 __u32 offset
= info
->offset
;
44 return skb_ptr(info
->skb
, offset
, len
);
47 static __always_inline
void
48 skb_parse_init(struct skb_parser_info
*info
, struct __sk_buff
*skb
)
50 *info
= (struct skb_parser_info
){
55 static __always_inline
struct ethhdr
*
56 skb_parse_ethernet(struct skb_parser_info
*info
)
60 eth
= skb_info_ptr(info
, sizeof(*eth
));
64 info
->proto
= eth
->h_proto
;
65 info
->offset
+= sizeof(*eth
);
70 static __always_inline
struct vlan_hdr
*
71 skb_parse_vlan(struct skb_parser_info
*info
)
75 if (info
->proto
!= bpf_htons(ETH_P_8021Q
) &&
76 info
->proto
!= bpf_htons(ETH_P_8021AD
))
79 vlh
= skb_info_ptr(info
, sizeof(*vlh
));
83 info
->proto
= vlh
->h_vlan_encapsulated_proto
;
84 info
->offset
+= sizeof(*vlh
);
89 static __always_inline
struct iphdr
*
90 skb_parse_ipv4(struct skb_parser_info
*info
, int min_l4_bytes
)
96 if (info
->proto
!= bpf_htons(ETH_P_IP
))
99 iph
= skb_info_ptr(info
, sizeof(*iph
));
103 hdr_len
= iph
->ihl
* 4;
104 if (hdr_len
< sizeof(*iph
))
107 pull_len
= info
->offset
+ hdr_len
+ min_l4_bytes
;
108 if (pull_len
> info
->skb
->len
)
109 pull_len
= info
->skb
->len
;
111 if (bpf_skb_pull_data(info
->skb
, pull_len
))
114 iph
= skb_info_ptr(info
, sizeof(*iph
));
118 info
->proto
= iph
->protocol
;
119 info
->offset
+= hdr_len
;
124 static __always_inline
struct ipv6hdr
*
125 skb_parse_ipv6(struct skb_parser_info
*info
, int max_l4_bytes
)
127 struct ipv6hdr
*ip6h
;
130 if (info
->proto
!= bpf_htons(ETH_P_IPV6
))
133 pull_len
= info
->offset
+ sizeof(*ip6h
) + max_l4_bytes
;
134 if (pull_len
> info
->skb
->len
)
135 pull_len
= info
->skb
->len
;
137 if (bpf_skb_pull_data(info
->skb
, pull_len
))
140 ip6h
= skb_info_ptr(info
, sizeof(*ip6h
));
144 info
->proto
= READ_ONCE(ip6h
->nexthdr
);
145 info
->offset
+= sizeof(*ip6h
);
150 static __always_inline
struct tcphdr
*
151 skb_parse_tcp(struct skb_parser_info
*info
)
155 if (info
->proto
!= IPPROTO_TCP
)
158 tcph
= skb_info_ptr(info
, sizeof(*tcph
));
162 info
->offset
+= tcph
->doff
* 4;