1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #ifndef NET_FIREWALL_SKB_UTILS_H
16 #define NET_FIREWALL_SKB_UTILS_H
17 
18 #include <stdint.h>
19 #include <sys/socket.h>
20 #include <bpf/bpf_endian.h>
21 #include <linux/bpf.h>
22 #include <linux/icmp.h>
23 #include <linux/icmpv6.h>
24 #include <linux/if_ether.h>
25 #include <linux/if_packet.h>
26 #include <linux/if_vlan.h>
27 #include <linux/if_tunnel.h>
28 #include <linux/in.h>
29 #include <linux/in6.h>
30 #include <linux/ip.h>
31 #include <linux/ipv6.h>
32 #include <linux/tc_act/tc_bpf.h>
33 #include <linux/tcp.h>
34 #include <linux/udp.h>
35 #include <stdbool.h>
36 
37 #define TCP_FLAGS_OFFSET 12
38 #define TCP_FLAGS_SIZE 2
39 
40 #define L3_NHOFF 0
41 #define L4_NHOFF(ipv4) (L3_NHOFF + ((ipv4) ? sizeof(struct iphdr) : sizeof(struct ipv6hdr)))
42 
43 union tcp_flags {
44     struct {
45         __u8 upper_bits;
46         __u8 lower_bits;
47         __u16 pad;
48     };
49     __u32 value;
50 };
51 
52 /**
53  * @brief judget given skb is a layler 4 protocol or not
54  *
55  * @param skb struct __sk_buff
56  * @param l3_nhoff layer 3 network header offset
57  * @param protocol layer 4 protocol number
58  * @return true if match, otherwise false
59  */
is_l4_protocol(struct __sk_buff * skb,__u32 l3_nhoff,__u8 protocol)60 static __always_inline bool is_l4_protocol(struct __sk_buff *skb, __u32 l3_nhoff, __u8 protocol)
61 {
62     if (skb->family == AF_INET) {
63         struct iphdr iph = { 0 };
64         bpf_skb_load_bytes(skb, l3_nhoff, &iph, sizeof(struct iphdr));
65 
66         return iph.protocol == protocol;
67     }
68     if (skb->family == AF_INET6) {
69         struct ipv6hdr ip6h = { 0 };
70         bpf_skb_load_bytes(skb, l3_nhoff, &ip6h, sizeof(struct ipv6hdr));
71 
72         return ip6h.nexthdr == protocol;
73     }
74 
75     return false;
76 }
77 
78 /**
79  * Get the layer 3 network header offset
80  * offset
81  *
82  * @param skb struct __sk_buff
83  * @return layer 3 network header offset
84  */
get_l3_nhoff(struct __sk_buff * skb)85 static __always_inline __u32 get_l3_nhoff(struct __sk_buff *skb)
86 {
87     return L3_NHOFF;
88 }
89 
90 /**
91  * Get the layer 4 network header offset
92  * offset
93  *
94  * @param skb struct __sk_buff
95  * @return layer 4 network header offset
96  */
get_l4_nhoff(struct __sk_buff * skb)97 static __always_inline __u32 get_l4_nhoff(struct __sk_buff *skb)
98 {
99     return L4_NHOFF(skb->family == AF_INET);
100 }
101 
102 /**
103  * @brief load tcp flags from given skb
104  *
105  * @param skb struct __sk_buff
106  * @param l4_nhoff layer 4 network header offset
107  * @param flags union tcp_flags
108  * @return __always_inline
109  */
load_tcp_flags(struct __sk_buff * skb,__u32 l4_nhoff,union tcp_flags * flags)110 static __always_inline int load_tcp_flags(struct __sk_buff *skb, __u32 l4_nhoff, union tcp_flags *flags)
111 {
112     return bpf_skb_load_bytes(skb, l4_nhoff + TCP_FLAGS_OFFSET, flags, TCP_FLAGS_SIZE);
113 }
114 
115 /**
116  * @brief load layer 4 protocol from given skb
117  *
118  * @param skb struct __sk_buff
119  * @param l3_nhoff layer 3 network header offset
120  * @param protocol out layer 4 protocol
121  * @return true if success or false if an error occurred
122  */
load_l4_protocol(const struct __sk_buff * skb,__u32 l3_nhoff,__u8 * protocol)123 static __always_inline bool load_l4_protocol(const struct __sk_buff *skb, __u32 l3_nhoff, __u8 *protocol)
124 {
125     if (skb->family == AF_INET) {
126         struct iphdr iph = { 0 };
127         bpf_skb_load_bytes(skb, l3_nhoff, &iph, sizeof(struct iphdr));
128 
129         *protocol = iph.protocol;
130         return true;
131     } else if (skb->family == AF_INET6) {
132         struct ipv6hdr ip6h = { 0 };
133         bpf_skb_load_bytes(skb, l3_nhoff, &ip6h, sizeof(struct ipv6hdr));
134 
135         *protocol = ip6h.nexthdr;
136         return true;
137     }
138 
139     return false;
140 }
141 
142 /**
143  * @brief load layer 3 ipv4 addrs from given skb
144  *
145  * @param skb struct __sk_buff
146  * @param l3_nhoff layer 3 network header offset
147  * @param saddr out saddr
148  * @param daddr out daddr
149  * @return true if success or false if an error occurred
150  */
load_l3_v4_addrs(const struct __sk_buff * skb,__u32 l3_nhoff,__be32 * saddr,__be32 * daddr)151 static __always_inline bool load_l3_v4_addrs(const struct __sk_buff *skb, __u32 l3_nhoff, __be32 *saddr, __be32 *daddr)
152 {
153     if (skb->family != AF_INET) {
154         return false;
155     }
156 
157     struct iphdr iph = { 0 };
158     bpf_skb_load_bytes(skb, l3_nhoff, &iph, sizeof(struct iphdr));
159 
160     *saddr = iph.saddr;
161     *daddr = iph.daddr;
162 
163     return true;
164 }
165 
166 /**
167  * @brief load layer 3 ipv6 addrs from given skb
168  *
169  * @param skb struct __sk_buff
170  * @param l3_nhoff layer 3 network header offset
171  * @param saddr out saddr
172  * @param daddr out daddr
173  * @return true if success or false if an error occurred
174  */
load_l3_v6_addrs(const struct __sk_buff * skb,__u32 l3_nhoff,struct in6_addr * saddr,struct in6_addr * daddr)175 static __always_inline bool load_l3_v6_addrs(const struct __sk_buff *skb, __u32 l3_nhoff, struct in6_addr *saddr,
176     struct in6_addr *daddr)
177 {
178     if (skb->family != AF_INET6) {
179         return false;
180     }
181 
182     struct ipv6hdr ip6h = { 0 };
183     bpf_skb_load_bytes(skb, l3_nhoff, &ip6h, sizeof(struct ipv6hdr));
184 
185     *saddr = ip6h.saddr;
186     *daddr = ip6h.daddr;
187 
188     return true;
189 }
190 
191 /**
192  * @brief load layer 4 icmp info from given skb
193  *
194  * @param skb struct __sk_buff
195  * @param l4_nhoff layer 4 network header offset
196  * @param protocol IPPROTO_ICMP or IPPROTO_ICMPV6
197  * @param type out icmp type
198  * @param code out icmp code
199  * @return true if success or false if an error occurred
200  */
load_icmp_info(const struct __sk_buff * skb,__u32 l4_nhoff,__u8 protocol,__u8 * type,__u8 * code)201 static __always_inline bool load_icmp_info(const struct __sk_buff *skb, __u32 l4_nhoff, __u8 protocol, __u8 *type,
202     __u8 *code)
203 {
204     bool res = true;
205     switch (protocol) {
206         case IPPROTO_ICMP: {
207             struct icmphdr icmph = { 0 };
208             bpf_skb_load_bytes(skb, l4_nhoff, &icmph, sizeof(struct icmphdr));
209 
210             *type = icmph.type;
211             *code = icmph.code;
212         } break;
213         case IPPROTO_ICMPV6: {
214             struct icmp6hdr icmph = { 0 };
215             bpf_skb_load_bytes(skb, l4_nhoff, &icmph, sizeof(struct icmp6hdr));
216 
217             *type = icmph.icmp6_type;
218             *code = icmph.icmp6_code;
219         } break;
220         default:
221             res = false;
222             break;
223     }
224 
225     return res;
226 }
227 
228 /**
229  * @brief load layer 4 ports from given skb
230  *
231  * @param skb struct __sk_buff
232  * @param l4_nhoff layer 4 network header offset
233  * @param protocol IPPROTO_TCP or IPPROTO_UDP
234  * @param sport out sport
235  * @param dport out dport
236  * @return true if success or false if an error occurred
237  */
load_l4_ports(const struct __sk_buff * skb,__u32 l4_nhoff,__u8 protocol,__be16 * sport,__be16 * dport)238 static __always_inline bool load_l4_ports(const struct __sk_buff *skb, __u32 l4_nhoff, __u8 protocol, __be16 *sport,
239     __be16 *dport)
240 {
241     bool res = true;
242     switch (protocol) {
243         case IPPROTO_TCP: {
244             struct tcphdr tcph = { 0 };
245             bpf_skb_load_bytes(skb, l4_nhoff, &tcph, sizeof(struct tcphdr));
246             *sport = tcph.source;
247             *dport = tcph.dest;
248         } break;
249         case IPPROTO_UDP: {
250             struct udphdr udph = { 0 };
251             bpf_skb_load_bytes(skb, l4_nhoff, &udph, sizeof(struct udphdr));
252             *sport = udph.source;
253             *dport = udph.dest;
254         } break;
255         default:
256             res = false;
257             break;
258     }
259 
260     return res;
261 }
262 
263 /**
264  * @brief load layer 4 rst  from given skb
265  *
266  * @param skb struct __sk_buff
267  * @param l4_nhoff layer 4 network header offset
268  * @param rst out rst packet
269  * @return true if success or false if an error occurred
270  */
load_l4_header_flags(const struct __sk_buff * skb,__u32 l4_nhoff,__u16 * rst)271 static __always_inline bool load_l4_header_flags(const struct __sk_buff *skb, __u32 l4_nhoff, __u16 *rst)
272 {
273     struct tcphdr tcph = { 0 };
274     bpf_skb_load_bytes(skb, l4_nhoff, &tcph, sizeof(struct tcphdr));
275     *rst = tcph.rst;
276     return true;
277 }
278 
279 #endif // NET_FIREWALL_SKB_UTILS_H