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_MATCH_H
16 #define NET_FIREWALL_MATCH_H
17
18 #include <bpf/bpf_helpers.h>
19
20 #include "netfirewall_utils.h"
21 #include "netfirewall_bitmap.h"
22 #include "netfirewall_map.h"
23 #include "netfirewall_event.h"
24 #include "netfirewall_def.h"
25
26 /**
27 * @brief Get the user id from sock_uid
28 *
29 * @param sock_uid bpf_get_socket_uid
30 * @return user id with type __u32
31 */
get_user_id(__u32 sock_uid)32 static __always_inline __u32 get_user_id(__u32 sock_uid)
33 {
34 __u32 user_id = sock_uid / USER_ID_DIVIDOR;
35 if (user_id > 0) {
36 return user_id;
37 }
38
39 current_user_id_key key = CURRENT_USER_ID_KEY;
40 uid_key *current_user_id = bpf_map_lookup_elem(&CURRENT_UID_MAP, &key);
41 if (!current_user_id) {
42 return DEFAULT_USER_ID;
43 }
44
45 return *current_user_id;
46 }
47
48 /**
49 * @brief swap tuple ports at egress direction
50 *
51 * @param tuple struct match_tuple
52 */
swap_tuple_ports(struct match_tuple * tuple)53 static __always_inline void swap_tuple_ports(struct match_tuple *tuple)
54 {
55 __be16 tmp = tuple->sport;
56 tuple->sport = tuple->dport;
57 tuple->dport = tmp;
58 }
59
60 /**
61 * @brief swap tuple addrs at egress direction
62 *
63 * @param tuple struct match_tuple
64 */
swap_tuple_addrs(struct match_tuple * tuple)65 static __always_inline void swap_tuple_addrs(struct match_tuple *tuple)
66 {
67 if (tuple->family == AF_INET) {
68 __be32 tmp = tuple->ipv4.saddr;
69 tuple->ipv4.saddr = tuple->ipv4.daddr;
70 tuple->ipv4.daddr = tmp;
71 } else {
72 struct in6_addr tmp = tuple->ipv6.saddr;
73 tuple->ipv6.saddr = tuple->ipv6.daddr;
74 tuple->ipv6.daddr = tmp;
75 }
76 }
77
78 /**
79 * @brief Get the match tuple from skb
80 *
81 * @param skb struct __sk_buff of packet
82 * @param tuple struct match_tuple
83 * @param dir enum stream_dir
84 * @return true if success or false if an error occurred
85 */
get_match_tuple(struct __sk_buff * skb,struct match_tuple * tuple,enum stream_dir dir)86 static __always_inline bool get_match_tuple(struct __sk_buff *skb, struct match_tuple *tuple, enum stream_dir dir)
87 {
88 if (!skb || !tuple) {
89 return false;
90 }
91
92 __u32 l3_nhoff = get_l3_nhoff(skb);
93 __u32 l4_nhoff = get_l4_nhoff(skb);
94 __u8 protocol = 0;
95 if (skb->family == AF_INET) {
96 load_l3_v4_addrs(skb, l3_nhoff, &(tuple->ipv4.saddr), &(tuple->ipv4.daddr));
97 } else {
98 load_l3_v6_addrs(skb, l3_nhoff, &(tuple->ipv6.saddr), &(tuple->ipv6.daddr));
99 }
100 if (!load_l4_protocol(skb, l3_nhoff, &protocol)) {
101 return false;
102 }
103 tuple->dir = dir;
104 tuple->family = skb->family;
105 __u32 sock_uid = bpf_get_socket_uid(skb);
106 tuple->appuid = sock_uid;
107 tuple->uid = get_user_id(sock_uid);
108 tuple->protocol = protocol;
109
110 if (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP) {
111 load_l4_ports(skb, l4_nhoff, protocol, &(tuple->sport), &(tuple->dport));
112 if (protocol == IPPROTO_TCP) {
113 load_l4_header_flags(skb, l4_nhoff, &(tuple->rst));
114 }
115 }
116 if (dir == EGRESS) {
117 swap_tuple_addrs(tuple);
118 swap_tuple_ports(tuple);
119 }
120 return true;
121 }
122
123 /**
124 * @brief lookup key or other_key from bpf map
125 *
126 * @param map bpf map pointer
127 * @param key key need to lookup
128 * @param other_key when key not found, then lookup other_key
129 * @return value with type struct bitmap of the key or other_key
130 */
lookup_map(void * map,void * key,void * other_key)131 static __always_inline struct bitmap *lookup_map(void *map, void *key, void *other_key)
132 {
133 struct bitmap *result = bpf_map_lookup_elem(map, key);
134 if (!result) {
135 result = bpf_map_lookup_elem(map, other_key);
136 }
137 return result;
138 }
139
140 /**
141 * @brief lookup addr bitmap use the given tuple
142 *
143 * @param tuple struct match_tuple get from skb
144 * @param key out param for lookup result
145 * @return true if success or false if an error occurred
146 */
match_addrs(struct match_tuple * tuple,struct bitmap * key)147 static __always_inline bool match_addrs(struct match_tuple *tuple, struct bitmap *key)
148 {
149 if (!tuple || !key) {
150 return false;
151 }
152
153 struct bitmap *result = NULL;
154 bool ingress = tuple->dir == INGRESS;
155
156 if (tuple->family == AF_INET) {
157 struct ipv4_lpm_key other_lpm_key = {
158 .prefixlen = 32,
159 .data = OTHER_IP4_KEY,
160 };
161 struct ipv4_lpm_key lpm_key = {
162 .prefixlen = 32,
163 .data = tuple->ipv4.saddr,
164 };
165
166 result = lookup_map(GET_MAP(ingress, saddr), &lpm_key, &other_lpm_key);
167 if (result) {
168 log_dbg2(DBG_MATCH_SADDR, tuple->dir, (__u32)tuple->ipv4.saddr, result->val[0]);
169 bitmap_and(key->val, result->val);
170 result = NULL;
171 }
172
173 lpm_key.data = tuple->ipv4.daddr;
174 result = lookup_map(GET_MAP(ingress, daddr), &lpm_key, &other_lpm_key);
175 if (result) {
176 log_dbg2(DBG_MATCH_DADDR, tuple->dir, (__u32)tuple->ipv4.daddr, result->val[0]);
177 bitmap_and(key->val, result->val);
178 }
179 } else {
180 struct ipv6_lpm_key other_lpm_key = {
181 .prefixlen = 128,
182 };
183 struct ipv6_lpm_key lpm_key = {
184 .prefixlen = 128,
185 };
186 memset(&(other_lpm_key.data), 0xff, sizeof(other_lpm_key.data));
187
188 memcpy(&(lpm_key.data), &(tuple->ipv6.saddr), sizeof(lpm_key.data));
189 result = lookup_map(GET_MAP(ingress, saddr6), &lpm_key, &other_lpm_key);
190 if (result) {
191 bitmap_and(key->val, result->val);
192 result = NULL;
193 }
194
195 memcpy(&(lpm_key.data), &(tuple->ipv6.daddr), sizeof(lpm_key.data));
196 result = lookup_map(GET_MAP(ingress, daddr6), &lpm_key, &other_lpm_key);
197 if (result) {
198 bitmap_and(key->val, result->val);
199 }
200 }
201 return true;
202 }
203
204 /**
205 * @brief bitmap use the given tuple
206 *
207 * @param tuple struct match_tuple get from skb
208 * @param key out param for lookup result
209 * @return true if success or false if an error occurred
210 */
match_ports(struct match_tuple * tuple,struct bitmap * key)211 static __always_inline bool match_ports(struct match_tuple *tuple, struct bitmap *key)
212 {
213 if (!tuple || !key) {
214 return false;
215 }
216 __u8 protocol = tuple->protocol;
217 port_key other_port_key = OTHER_PORT_KEY;
218 bool ingress = tuple->dir == INGRESS;
219 struct bitmap *result = NULL;
220
221 result = lookup_map(GET_MAP(ingress, sport), &(tuple->sport), &other_port_key);
222 if (result) {
223 log_dbg2(DBG_MATCH_SPORT, tuple->dir, (__u32)tuple->sport, result->val[0]);
224 bitmap_and(key->val, result->val);
225 result = NULL;
226 }
227 result = lookup_map(GET_MAP(ingress, dport), &(tuple->dport), &other_port_key);
228 if (result) {
229 log_dbg2(DBG_MATCH_DPORT, tuple->dir, (__u32)tuple->dport, result->val[0]);
230 bitmap_and(key->val, result->val);
231 }
232 return true;
233 }
234
235 /**
236 * @brief lookup protocol bitmap use the given tuple
237 *
238 * @param tuple struct match_tuple get from skb
239 * @param key out param for lookup result
240 * @return true if success or false if an error occurred
241 */
match_protocol(struct match_tuple * tuple,struct bitmap * key)242 static __always_inline bool match_protocol(struct match_tuple *tuple, struct bitmap *key)
243 {
244 if (!tuple || !key) {
245 return false;
246 }
247
248 proto_key other_proto_key = OTHER_PROTO_KEY;
249 bool ingress = tuple->dir == INGRESS;
250 struct bitmap *result = NULL;
251
252 result = lookup_map(GET_MAP(ingress, proto), &(tuple->protocol), &other_proto_key);
253 if (result) {
254 log_dbg2(DBG_MATCH_PROTO, tuple->dir, (__u32)tuple->protocol, result->val[0]);
255 bitmap_and(key->val, result->val);
256 }
257
258 return true;
259 }
260
261 /**
262 * @brief lookup appuid bitmap use the given tuple
263 *
264 * @param tuple struct match_tuple get from skb
265 * @param key out param for lookup result
266 * @return true if success or false if an error occurred
267 */
match_appuid(struct match_tuple * tuple,struct bitmap * key)268 static __always_inline bool match_appuid(struct match_tuple *tuple, struct bitmap *key)
269 {
270 if (!tuple || !key) {
271 return false;
272 }
273
274 appuid_key other_appuid_key = OTHER_APPUID_KEY;
275 bool ingress = tuple->dir == INGRESS;
276 struct bitmap *result = NULL;
277
278 result = lookup_map(GET_MAP(ingress, appuid), &(tuple->appuid), &other_appuid_key);
279 if (result) {
280 log_dbg2(DBG_MATCH_APPUID, tuple->dir, tuple->appuid, result->val[0]);
281 bitmap_and(key->val, result->val);
282 }
283
284 return true;
285 }
286
287 /**
288 * @brief lookup user_id bitmap use the given tuple
289 *
290 * @param tuple struct match_tuple get from skb
291 * @param key out param for lookup result
292 * @return true if success or false if an error occurred
293 */
match_uid(struct match_tuple * tuple,struct bitmap * key)294 static __always_inline bool match_uid(struct match_tuple *tuple, struct bitmap *key)
295 {
296 if (!tuple || !key) {
297 return false;
298 }
299
300 uid_key other_uid_key = OTHER_UID_KEY;
301 bool ingress = tuple->dir == INGRESS;
302 struct bitmap *result = NULL;
303
304 result = lookup_map(GET_MAP(ingress, uid), &(tuple->uid), &other_uid_key);
305 if (result) {
306 log_dbg2(DBG_MATCH_UID, tuple->dir, tuple->uid, result->val[0]);
307 bitmap_and(key->val, result->val);
308 }
309
310 return true;
311 }
312
313 /**
314 * @brief lookup action key bitmap use the given tuple
315 *
316 * @param tuple struct match_tuple get from skb
317 * @param key out param for lookup result
318 * @return true if success or false if an error occurred
319 */
match_action_key(struct match_tuple * tuple,struct bitmap * key)320 static __always_inline bool match_action_key(struct match_tuple *tuple, struct bitmap *key)
321 {
322 if (!tuple || !key) {
323 return false;
324 }
325
326 memset(key, 0xff, sizeof(struct bitmap));
327
328 if (!match_addrs(tuple, key)) {
329 return false;
330 }
331
332 if (!match_protocol(tuple, key)) {
333 return false;
334 }
335
336 if (!match_ports(tuple, key)) {
337 return false;
338 }
339
340 if (!match_appuid(tuple, key)) {
341 return false;
342 }
343
344 if (!match_uid(tuple, key)) {
345 return false;
346 }
347
348 log_dbg(DBG_ACTION_KEY, tuple->dir, key->val[0]);
349 return true;
350 }
351
352 /**
353 * @brief lookup action with action_key use the given tuple
354 *
355 * @param tuple struct match_tuple get from skb
356 * @param key out param for lookup result
357 * @return true if success or false if an error occurred
358 */
match_action(struct match_tuple * tuple,struct bitmap * key)359 static __always_inline enum sk_action match_action(struct match_tuple *tuple, struct bitmap *key)
360 {
361 if (!tuple || !key) {
362 return SK_PASS;
363 }
364 bool ingress = tuple->dir == INGRESS;
365 default_action_key default_key = ingress ? DEFAULT_ACT_IN_KEY : DEFAULT_ACT_OUT_KEY;
366 enum sk_action *default_action = bpf_map_lookup_elem(&DEFAULT_ACTION_MAP, &default_key);
367 enum sk_action sk_act = !default_action ? *default_action : SK_PASS;
368 action_key akey = 1;
369 struct bitmap *action_bitmap = bpf_map_lookup_elem(GET_MAP(ingress, action), &akey);
370 /*
371 * Conflict & Repetition Algorithm
372 * eg: matched 0110, action 1100 : 1:drop 0:pass
373 * 1 default drop: Match the rule with the action's bitmap bit by and, and if any bit is 1, it is drop
374 * (0110&1100->0100)
375 * 2 default pass: 2.1 Reverse the action, 0011(1:pass, 0:drop) 2.2 Match results bit by and, and if
376 * any bit is 1, it is pass(0110&0011->0010)
377 */
378 if (action_bitmap && bitmap_positive(key->val)) {
379 if (sk_act == SK_DROP) {
380 bitmap_and(key->val, action_bitmap->val);
381 if (!bitmap_positive(key->val)) {
382 sk_act = SK_PASS;
383 }
384 } else {
385 bitmap_and_inv(key->val, action_bitmap->val);
386 if (!bitmap_positive(key->val)) {
387 sk_act = SK_DROP;
388 }
389 }
390 }
391 log_dbg(DBG_MATCH_ACTION, tuple->dir, sk_act);
392 return sk_act;
393 }
394 #endif // NET_FIREWALL_MATCH_H
395