unet-cli: strip initial newline in usage message
[project/unetd.git] / bpf_skb_utils.h
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #ifndef __BPF_SKB_UTILS_H
6 #define __BPF_SKB_UTILS_H
7
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>
12 #include <linux/ip.h>
13 #include <net/ipv6.h>
14 #include <bpf/bpf_helpers.h>
15 #include <bpf/bpf_endian.h>
16
17 struct skb_parser_info {
18 struct __sk_buff *skb;
19 __u32 offset;
20 int proto;
21 };
22
23 static __always_inline void *__skb_data(struct __sk_buff *skb)
24 {
25 return (void *)(long)READ_ONCE(skb->data);
26 }
27
28 static __always_inline void *
29 skb_ptr(struct __sk_buff *skb, __u32 offset, __u32 len)
30 {
31 void *ptr = __skb_data(skb) + offset;
32 void *end = (void *)(long)(skb->data_end);
33
34 if (ptr + len >= end)
35 return NULL;
36
37 return ptr;
38 }
39
40 static __always_inline void *
41 skb_info_ptr(struct skb_parser_info *info, __u32 len)
42 {
43 __u32 offset = info->offset;
44 return skb_ptr(info->skb, offset, len);
45 }
46
47 static __always_inline void
48 skb_parse_init(struct skb_parser_info *info, struct __sk_buff *skb)
49 {
50 *info = (struct skb_parser_info){
51 .skb = skb
52 };
53 }
54
55 static __always_inline struct ethhdr *
56 skb_parse_ethernet(struct skb_parser_info *info)
57 {
58 struct ethhdr *eth;
59 int len;
60
61 len = sizeof(*eth) + 2 * sizeof(struct vlan_hdr) + sizeof(struct ipv6hdr);
62 if (len > info->skb->len)
63 len = info->skb->len;
64 bpf_skb_pull_data(info->skb, len);
65
66 eth = skb_info_ptr(info, sizeof(*eth));
67 if (!eth)
68 return NULL;
69
70 info->proto = eth->h_proto;
71 info->offset += sizeof(*eth);
72
73 return eth;
74 }
75
76 static __always_inline struct vlan_hdr *
77 skb_parse_vlan(struct skb_parser_info *info)
78 {
79 struct vlan_hdr *vlh;
80
81 if (info->proto != bpf_htons(ETH_P_8021Q) &&
82 info->proto != bpf_htons(ETH_P_8021AD))
83 return NULL;
84
85 vlh = skb_info_ptr(info, sizeof(*vlh));
86 if (!vlh)
87 return NULL;
88
89 info->proto = vlh->h_vlan_encapsulated_proto;
90 info->offset += sizeof(*vlh);
91
92 return vlh;
93 }
94
95 static __always_inline struct iphdr *
96 skb_parse_ipv4(struct skb_parser_info *info, int min_l4_bytes)
97 {
98 struct iphdr *iph;
99 int proto, hdr_len;
100 __u32 pull_len;
101
102 if (info->proto != bpf_htons(ETH_P_IP))
103 return NULL;
104
105 iph = skb_info_ptr(info, sizeof(*iph));
106 if (!iph)
107 return NULL;
108
109 hdr_len = iph->ihl * 4;
110 if (hdr_len < sizeof(*iph))
111 return NULL;
112
113 pull_len = info->offset + hdr_len + min_l4_bytes;
114 if (pull_len > info->skb->len)
115 pull_len = info->skb->len;
116
117 if (bpf_skb_pull_data(info->skb, pull_len))
118 return NULL;
119
120 iph = skb_info_ptr(info, sizeof(*iph));
121 if (!iph)
122 return NULL;
123
124 info->proto = iph->protocol;
125 info->offset += hdr_len;
126
127 return iph;
128 }
129
130 static __always_inline struct ipv6hdr *
131 skb_parse_ipv6(struct skb_parser_info *info, int max_l4_bytes)
132 {
133 struct ipv6hdr *ip6h;
134 __u32 pull_len;
135
136 if (info->proto != bpf_htons(ETH_P_IPV6))
137 return NULL;
138
139 pull_len = info->offset + sizeof(*ip6h) + max_l4_bytes;
140 if (pull_len > info->skb->len)
141 pull_len = info->skb->len;
142
143 if (bpf_skb_pull_data(info->skb, pull_len))
144 return NULL;
145
146 ip6h = skb_info_ptr(info, sizeof(*ip6h));
147 if (!ip6h)
148 return NULL;
149
150 info->proto = READ_ONCE(ip6h->nexthdr);
151 info->offset += sizeof(*ip6h);
152
153 return ip6h;
154 }
155
156 static __always_inline struct tcphdr *
157 skb_parse_tcp(struct skb_parser_info *info)
158 {
159 struct tcphdr *tcph;
160
161 if (info->proto != IPPROTO_TCP)
162 return NULL;
163
164 tcph = skb_info_ptr(info, sizeof(*tcph));
165 if (!tcph)
166 return NULL;
167
168 info->offset += tcph->doff * 4;
169
170 return tcph;
171 }
172
173 #endif