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 bitmap_and(key->val, result->val);
169 result = NULL;
170 }
171
172 lpm_key.data = tuple->ipv4.daddr;
173 result = lookup_map(GET_MAP(ingress, daddr), &lpm_key, &other_lpm_key);
174 if (result) {
175 bitmap_and(key->val, result->val);
176 }
177 } else {
178 struct ipv6_lpm_key other_lpm_key = {
179 .prefixlen = 128,
180 };
181 struct ipv6_lpm_key lpm_key = {
182 .prefixlen = 128,
183 };
184 memset(&(other_lpm_key.data), 0xff, sizeof(other_lpm_key.data));
185
186 memcpy(&(lpm_key.data), &(tuple->ipv6.saddr), sizeof(lpm_key.data));
187 result = lookup_map(GET_MAP(ingress, saddr6), &lpm_key, &other_lpm_key);
188 if (result) {
189 bitmap_and(key->val, result->val);
190 result = NULL;
191 }
192
193 memcpy(&(lpm_key.data), &(tuple->ipv6.daddr), sizeof(lpm_key.data));
194 result = lookup_map(GET_MAP(ingress, daddr6), &lpm_key, &other_lpm_key);
195 if (result) {
196 bitmap_and(key->val, result->val);
197 }
198 }
199 return true;
200 }
201
202 /**
203 * @brief bitmap use the given tuple
204 *
205 * @param tuple struct match_tuple get from skb
206 * @param key out param for lookup result
207 * @return true if success or false if an error occurred
208 */
match_ports(struct match_tuple *tuple, struct bitmap *key)209 static __always_inline bool match_ports(struct match_tuple *tuple, struct bitmap *key)
210 {
211 if (!tuple || !key) {
212 return false;
213 }
214 __u8 protocol = tuple->protocol;
215 port_key other_port_key = OTHER_PORT_KEY;
216 bool ingress = tuple->dir == INGRESS;
217 struct bitmap *result = NULL;
218
219 result = lookup_map(GET_MAP(ingress, sport), &(tuple->sport), &other_port_key);
220 if (result) {
221 log_dbg2(DBG_MATCH_SPORT, tuple->dir, (__u32)tuple->sport, result->val[0]);
222 bitmap_and(key->val, result->val);
223 result = NULL;
224 }
225 result = lookup_map(GET_MAP(ingress, dport), &(tuple->dport), &other_port_key);
226 if (result) {
227 log_dbg2(DBG_MATCH_DPORT, tuple->dir, (__u32)tuple->dport, result->val[0]);
228 bitmap_and(key->val, result->val);
229 }
230 return true;
231 }
232
233 /**
234 * @brief lookup protocol bitmap use the given tuple
235 *
236 * @param tuple struct match_tuple get from skb
237 * @param key out param for lookup result
238 * @return true if success or false if an error occurred
239 */
match_protocol(struct match_tuple *tuple, struct bitmap *key)240 static __always_inline bool match_protocol(struct match_tuple *tuple, struct bitmap *key)
241 {
242 if (!tuple || !key) {
243 return false;
244 }
245
246 proto_key other_proto_key = OTHER_PROTO_KEY;
247 bool ingress = tuple->dir == INGRESS;
248 struct bitmap *result = NULL;
249
250 result = lookup_map(GET_MAP(ingress, proto), &(tuple->protocol), &other_proto_key);
251 if (result) {
252 log_dbg2(DBG_MATCH_PROTO, tuple->dir, (__u32)tuple->protocol, result->val[0]);
253 bitmap_and(key->val, result->val);
254 }
255
256 return true;
257 }
258
259 /**
260 * @brief lookup appuid bitmap use the given tuple
261 *
262 * @param tuple struct match_tuple get from skb
263 * @param key out param for lookup result
264 * @return true if success or false if an error occurred
265 */
match_appuid(struct match_tuple *tuple, struct bitmap *key)266 static __always_inline bool match_appuid(struct match_tuple *tuple, struct bitmap *key)
267 {
268 if (!tuple || !key) {
269 return false;
270 }
271
272 appuid_key other_appuid_key = OTHER_APPUID_KEY;
273 bool ingress = tuple->dir == INGRESS;
274 struct bitmap *result = NULL;
275
276 result = lookup_map(GET_MAP(ingress, appuid), &(tuple->appuid), &other_appuid_key);
277 if (result) {
278 log_dbg2(DBG_MATCH_APPUID, tuple->dir, tuple->appuid, result->val[0]);
279 bitmap_and(key->val, result->val);
280 }
281
282 return true;
283 }
284
285 /**
286 * @brief lookup user_id bitmap use the given tuple
287 *
288 * @param tuple struct match_tuple get from skb
289 * @param key out param for lookup result
290 * @return true if success or false if an error occurred
291 */
match_uid(struct match_tuple *tuple, struct bitmap *key)292 static __always_inline bool match_uid(struct match_tuple *tuple, struct bitmap *key)
293 {
294 if (!tuple || !key) {
295 return false;
296 }
297
298 uid_key other_uid_key = OTHER_UID_KEY;
299 bool ingress = tuple->dir == INGRESS;
300 struct bitmap *result = NULL;
301
302 result = lookup_map(GET_MAP(ingress, uid), &(tuple->uid), &other_uid_key);
303 if (result) {
304 log_dbg2(DBG_MATCH_UID, tuple->dir, tuple->uid, result->val[0]);
305 bitmap_and(key->val, result->val);
306 }
307
308 return true;
309 }
310
311 /**
312 * @brief lookup action key bitmap use the given tuple
313 *
314 * @param tuple struct match_tuple get from skb
315 * @param key out param for lookup result
316 * @return true if success or false if an error occurred
317 */
match_action_key(struct match_tuple *tuple, struct bitmap *key)318 static __always_inline bool match_action_key(struct match_tuple *tuple, struct bitmap *key)
319 {
320 if (!tuple || !key) {
321 return false;
322 }
323
324 memset(key, 0xff, sizeof(struct bitmap));
325
326 if (!match_addrs(tuple, key)) {
327 return false;
328 }
329
330 if (!match_protocol(tuple, key)) {
331 return false;
332 }
333
334 if (!match_ports(tuple, key)) {
335 return false;
336 }
337
338 if (!match_appuid(tuple, key)) {
339 return false;
340 }
341
342 if (!match_uid(tuple, key)) {
343 return false;
344 }
345
346 log_dbg(DBG_ACTION_KEY, tuple->dir, key->val[0]);
347 return true;
348 }
349
MatchDomain(const struct match_tuple *tuple)350 static __always_inline bool MatchDomain(const struct match_tuple *tuple)
351 {
352 if (!tuple || tuple->dir == INGRESS) {
353 return false;
354 }
355 domain_value *result = NULL;
356 if (tuple->family == AF_INET) {
357 struct ipv4_lpm_key key = {
358 .prefixlen = IPV4_MAX_PREFIXLEN,
359 .data = tuple->ipv4.saddr,
360 };
361 result = bpf_map_lookup_elem(&DOMAIN_IPV4_MAP, &key);
362 } else {
363 struct ipv6_lpm_key key = {
364 .prefixlen = IPV6_MAX_PREFIXLEN,
365 .data = tuple->ipv6.saddr,
366 };
367 result = bpf_map_lookup_elem(&DOMAIN_IPV6_MAP, &key);
368 }
369 return result != NULL;
370 }
371
372 /**
373 * @brief lookup action with action_key use the given tuple
374 *
375 * @param tuple struct match_tuple get from skb
376 * @param key out param for lookup result
377 * @return true if success or false if an error occurred
378 */
match_action(struct match_tuple *tuple, struct bitmap *key)379 static __always_inline enum sk_action match_action(struct match_tuple *tuple, struct bitmap *key)
380 {
381 if (!tuple || !key) {
382 return SK_PASS;
383 }
384 bool ingress = tuple->dir == INGRESS;
385 default_action_key default_key = ingress ? DEFAULT_ACT_IN_KEY : DEFAULT_ACT_OUT_KEY;
386 enum sk_action *default_action = bpf_map_lookup_elem(&DEFAULT_ACTION_MAP, &default_key);
387 enum sk_action sk_act = default_action ? *default_action : SK_PASS;
388 action_key akey = 1;
389 struct bitmap *action_bitmap = bpf_map_lookup_elem(GET_MAP(ingress, action), &akey);
390 /*
391 * Conflict & Repetition Algorithm
392 * eg: matched 0110, action 1100 : 1:drop 0:pass
393 * 1 default drop: Match the rule with the action's bitmap bit by and, and if any bit is 1, it is drop
394 * (0110&1100->0100)
395 * 2 default pass: 2.1 Reverse the action, 0011(1:pass, 0:drop) 2.2 Match results bit by and, and if
396 * any bit is 1, it is pass(0110&0011->0010)
397 */
398 if (action_bitmap && bitmap_positive(key->val)) {
399 if (sk_act == SK_DROP) {
400 bitmap_and(key->val, action_bitmap->val);
401 if (!bitmap_positive(key->val)) {
402 sk_act = SK_PASS;
403 }
404 } else {
405 bitmap_and_inv(key->val, action_bitmap->val);
406 if (!bitmap_positive(key->val)) {
407 sk_act = SK_DROP;
408 }
409 }
410 // If the outbound does not match the IP rule, check if there are any domain name rules
411 } else if (MatchDomain(tuple)) {
412 log_dbg(DBG_MATCH_DOMAIN, tuple->dir, sk_act);
413 sk_act = SK_PASS;
414 }
415 log_dbg(DBG_MATCH_ACTION, tuple->dir, sk_act);
416 return sk_act;
417 }
418 #endif // NET_FIREWALL_MATCH_H
419