1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
6 #ifndef __BPF_SKB_UTILS_H
7 #define __BPF_SKB_UTILS_H
9 #include <uapi/linux/bpf.h>
10 #include <uapi/linux/if_ether.h>
11 #include <uapi/linux/ip.h>
12 #include <uapi/linux/ipv6.h>
15 #include <bpf/bpf_helpers.h>
16 #include <bpf/bpf_endian.h>
18 struct skb_parser_info
{
19 struct __sk_buff
*skb
;
24 static __always_inline
void *__skb_data(struct __sk_buff
*skb
)
26 return (void *)(long)READ_ONCE(skb
->data
);
29 static __always_inline
void *
30 skb_ptr(struct __sk_buff
*skb
, __u32 offset
, __u32 len
)
32 void *ptr
= __skb_data(skb
) + offset
;
33 void *end
= (void *)(long)(skb
->data_end
);
41 static __always_inline
void *
42 skb_info_ptr(struct skb_parser_info
*info
, __u32 len
)
44 __u32 offset
= info
->offset
;
45 return skb_ptr(info
->skb
, offset
, len
);
48 static __always_inline
void
49 skb_parse_init(struct skb_parser_info
*info
, struct __sk_buff
*skb
)
51 *info
= (struct skb_parser_info
){
56 static __always_inline
struct ethhdr
*
57 skb_parse_ethernet(struct skb_parser_info
*info
)
62 len
= sizeof(*eth
) + 2 * sizeof(struct vlan_hdr
) + sizeof(struct ipv6hdr
);
63 if (len
> info
->skb
->len
)
65 bpf_skb_pull_data(info
->skb
, len
);
67 eth
= skb_info_ptr(info
, sizeof(*eth
));
71 info
->proto
= eth
->h_proto
;
72 info
->offset
+= sizeof(*eth
);
77 static __always_inline
struct vlan_hdr
*
78 skb_parse_vlan(struct skb_parser_info
*info
)
82 if (info
->proto
!= bpf_htons(ETH_P_8021Q
) &&
83 info
->proto
!= bpf_htons(ETH_P_8021AD
))
86 vlh
= skb_info_ptr(info
, sizeof(*vlh
));
90 info
->proto
= vlh
->h_vlan_encapsulated_proto
;
91 info
->offset
+= sizeof(*vlh
);
96 static __always_inline
struct iphdr
*
97 skb_parse_ipv4(struct skb_parser_info
*info
, int min_l4_bytes
)
103 if (info
->proto
!= bpf_htons(ETH_P_IP
))
106 iph
= skb_info_ptr(info
, sizeof(*iph
));
110 hdr_len
= iph
->ihl
* 4;
111 hdr_len
= READ_ONCE(hdr_len
) & 0xff;
112 if (hdr_len
< sizeof(*iph
))
115 pull_len
= info
->offset
+ hdr_len
+ min_l4_bytes
;
116 if (pull_len
> info
->skb
->len
)
117 pull_len
= info
->skb
->len
;
119 if (bpf_skb_pull_data(info
->skb
, pull_len
))
122 iph
= skb_info_ptr(info
, sizeof(*iph
));
126 info
->proto
= iph
->protocol
;
127 info
->offset
+= hdr_len
;
132 static __always_inline
struct ipv6hdr
*
133 skb_parse_ipv6(struct skb_parser_info
*info
, int max_l4_bytes
)
135 struct ipv6hdr
*ip6h
;
138 if (info
->proto
!= bpf_htons(ETH_P_IPV6
))
141 pull_len
= info
->offset
+ sizeof(*ip6h
) + max_l4_bytes
;
142 if (pull_len
> info
->skb
->len
)
143 pull_len
= info
->skb
->len
;
145 if (bpf_skb_pull_data(info
->skb
, pull_len
))
148 ip6h
= skb_info_ptr(info
, sizeof(*ip6h
));
152 info
->proto
= READ_ONCE(ip6h
->nexthdr
);
153 info
->offset
+= sizeof(*ip6h
);
158 static __always_inline
struct tcphdr
*
159 skb_parse_tcp(struct skb_parser_info
*info
)
163 if (info
->proto
!= IPPROTO_TCP
)
166 tcph
= skb_info_ptr(info
, sizeof(*tcph
));
170 info
->offset
+= tcph
->doff
* 4;