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