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
)
61 len
= sizeof(*eth
) + 2 * sizeof(struct vlan_hdr
) + sizeof(struct ipv6hdr
);
62 if (len
> info
->skb
->len
)
64 bpf_skb_pull_data(info
->skb
, len
);
66 eth
= skb_info_ptr(info
, sizeof(*eth
));
70 info
->proto
= eth
->h_proto
;
71 info
->offset
+= sizeof(*eth
);
76 static __always_inline
struct vlan_hdr
*
77 skb_parse_vlan(struct skb_parser_info
*info
)
81 if (info
->proto
!= bpf_htons(ETH_P_8021Q
) &&
82 info
->proto
!= bpf_htons(ETH_P_8021AD
))
85 vlh
= skb_info_ptr(info
, sizeof(*vlh
));
89 info
->proto
= vlh
->h_vlan_encapsulated_proto
;
90 info
->offset
+= sizeof(*vlh
);
95 static __always_inline
struct iphdr
*
96 skb_parse_ipv4(struct skb_parser_info
*info
, int min_l4_bytes
)
102 if (info
->proto
!= bpf_htons(ETH_P_IP
))
105 iph
= skb_info_ptr(info
, sizeof(*iph
));
109 hdr_len
= iph
->ihl
* 4;
110 if (hdr_len
< sizeof(*iph
))
113 pull_len
= info
->offset
+ hdr_len
+ min_l4_bytes
;
114 if (pull_len
> info
->skb
->len
)
115 pull_len
= info
->skb
->len
;
117 if (bpf_skb_pull_data(info
->skb
, pull_len
))
120 iph
= skb_info_ptr(info
, sizeof(*iph
));
124 info
->proto
= iph
->protocol
;
125 info
->offset
+= hdr_len
;
130 static __always_inline
struct ipv6hdr
*
131 skb_parse_ipv6(struct skb_parser_info
*info
, int max_l4_bytes
)
133 struct ipv6hdr
*ip6h
;
136 if (info
->proto
!= bpf_htons(ETH_P_IPV6
))
139 pull_len
= info
->offset
+ sizeof(*ip6h
) + max_l4_bytes
;
140 if (pull_len
> info
->skb
->len
)
141 pull_len
= info
->skb
->len
;
143 if (bpf_skb_pull_data(info
->skb
, pull_len
))
146 ip6h
= skb_info_ptr(info
, sizeof(*ip6h
));
150 info
->proto
= READ_ONCE(ip6h
->nexthdr
);
151 info
->offset
+= sizeof(*ip6h
);
156 static __always_inline
struct tcphdr
*
157 skb_parse_tcp(struct skb_parser_info
*info
)
161 if (info
->proto
!= IPPROTO_TCP
)
164 tcph
= skb_info_ptr(info
, sizeof(*tcph
));
168 info
->offset
+= tcph
->doff
* 4;