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