• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_DOMAIN_H
16 #define NET_FIREWALL_DOMAIN_H
17 
18 #include <bpf/bpf_helpers.h>
19 #include <linux/bpf.h>
20 #include <linux/udp.h>
21 
22 #include "netfirewall_event.h"
23 #include "netfirewall_match.h"
24 #include "netfirewall_map.h"
25 #include "netfirewall_types.h"
26 #include "netfirewall_utils.h"
27 
get_current_uid(struct __sk_buff * skb)28 static __always_inline __u32 get_current_uid(struct __sk_buff *skb)
29 {
30     if (skb == NULL) {
31         return DEFAULT_USER_ID;
32     }
33     __u32 sock_uid = bpf_get_socket_uid(skb);
34     return get_user_id(sock_uid);
35 }
36 
add_domain_cache(struct __sk_buff * skb,const __u8 * payload,const __u32 family,__u32 appuid)37 static __always_inline bool add_domain_cache(struct __sk_buff *skb, const __u8 *payload, const __u32 family,
38     __u32 appuid)
39 {
40     bool ret = false;
41     struct domain_value value = { 0 };
42     value.appuid = bpf_get_socket_uid(skb);
43     value.uid = get_current_uid(skb);
44 
45     if (appuid == 0) {
46         value.appuid = 0;
47     }
48     if (family == AF_INET) {
49         struct ipv4_lpm_key lpm_key = {
50             .prefixlen = IPV4_MAX_PREFIXLEN,
51         };
52         memcpy(&(lpm_key.data), payload, sizeof(lpm_key.data));
53         ret = bpf_map_update_elem(&DOMAIN_IPV4_MAP, &lpm_key, &value, 0) == 0;
54     } else if (family == AF_INET6) {
55         struct ipv6_lpm_key lpm_key = {
56             .prefixlen = IPV6_MAX_PREFIXLEN,
57         };
58         memcpy(&(lpm_key.data), payload, sizeof(lpm_key.data));
59         ret = bpf_map_update_elem(&DOMAIN_IPV6_MAP, &lpm_key, &value, 0) == 0;
60     }
61     return ret;
62 }
63 
parse_queries_name(struct __sk_buff * skb,__u16 dns_qry_off,__u8 * key_data,__u16 * key_len)64 static __always_inline __u16 parse_queries_name(struct __sk_buff *skb, __u16 dns_qry_off, __u8 *key_data,
65     __u16 *key_len)
66 {
67     __u8 offset = dns_qry_off;
68     __u8 i = 0;
69 
70     for (; i < DNS_DOMAIN_LEN; i++) {
71         bpf_skb_load_bytes(skb, offset, key_data + i, sizeof(__u8));
72         offset += 1;
73         if (*(key_data + i) == 0) {
74             *key_len = i;
75             break;
76         }
77     }
78     if (i == DNS_DOMAIN_LEN) {
79         return 0;
80     }
81 
82     return offset;
83 }
84 
parse_queries(struct __sk_buff * skb,__u16 dns_qry_off,__u8 * key_data,__u16 * key_len)85 static __always_inline __u16 parse_queries(struct __sk_buff *skb, __u16 dns_qry_off, __u8 *key_data,
86     __u16 *key_len)
87 {
88     __u16 offset = parse_queries_name(skb, dns_qry_off, key_data, key_len);
89     if (offset == 0) {
90         return 0;
91     }
92     // Type & Class
93     offset += sizeof(__u32);
94 
95     return offset;
96 }
97 
parse_answers(struct __sk_buff * skb,__u16 dns_qry_off,__u8 save_ip,__u32 appuid)98 static __always_inline __u16 parse_answers(struct __sk_buff *skb, __u16 dns_qry_off, __u8 save_ip, __u32 appuid)
99 {
100     __u16 type;
101     __u16 str_len;
102     __u16 offset = dns_qry_off;
103 
104     // Name : u16
105     offset += sizeof(__u16);
106     // Type : u16
107     bpf_skb_load_bytes(skb, offset, &type, sizeof(__u16));
108     type = bpf_ntohs(type);
109     offset += sizeof(__u16);
110     // Class : u16
111     offset += sizeof(__u16);
112     // Ttl : u32
113     offset += sizeof(__u32);
114     // Data len : u16
115     bpf_skb_load_bytes(skb, offset, &str_len, sizeof(__u16));
116     str_len = bpf_ntohs(str_len);
117     offset += sizeof(__u16);
118     if (str_len == 0) {
119         return offset;
120     }
121     // ipv4 or ipv6 addr
122     if (type == DNS_QRS_IPV4_TYPE && str_len == DNS_QRS_IPV4_LEN) {
123         __u32 addr;
124         bpf_skb_load_bytes(skb, offset, &addr, sizeof(__u32));
125         if (save_ip) {
126             add_domain_cache(skb, (__u8*)&addr, AF_INET, appuid);
127         }
128     } else if (type == DNS_QRS_IPV6_TYPE && str_len == DNS_QRS_IPV6_LEN) {
129         ip6_key ip6_addr;
130         bpf_skb_load_bytes(skb, offset, &ip6_addr, sizeof(ip6_key));
131         if (save_ip) {
132             add_domain_cache(skb, (__u8*)&ip6_addr, AF_INET6, appuid);
133         }
134     }
135     offset += str_len;
136 
137     return offset;
138 }
139 
match_domain_value(struct __sk_buff * skb,const struct domain_value * value)140 static __always_inline bool match_domain_value(struct __sk_buff *skb, const struct domain_value *value)
141 {
142     if (skb == NULL || value == NULL) {
143         return false;
144     }
145 
146     if (value->uid == get_current_uid(skb) &&
147         (value->appuid == bpf_get_socket_uid(skb) || value->appuid == 0)) {
148         return true;
149     }
150     return false;
151 }
152 
parse_dns_query(struct __sk_buff * skb,__u16 dns_qry_off,__u16 qu_num)153 static __always_inline __u8 parse_dns_query(struct __sk_buff *skb, __u16 dns_qry_off, __u16 qu_num)
154 {
155     if (qu_num == 1) {
156         __u16 res;
157         __u16 key_len = 0;
158         struct domain_hash_key key = { 0 };
159 
160         res = parse_queries(skb, dns_qry_off, (__u8*)&key.data, &key_len);
161         if (res == 0) {
162             return 0;
163         }
164 
165         struct domain_value *deny_value = bpf_map_lookup_elem(&DOMAIN_DENY_MAP, &key);
166 
167         if (deny_value != NULL && match_domain_value(skb, deny_value)) {
168             return 1;
169         }
170     }
171     return 0;
172 }
173 
parse_dns_response(struct __sk_buff * skb,__u16 dns_qry_off,__u16 qu_num,__u16 as_num)174 static __always_inline __u16 parse_dns_response(struct __sk_buff *skb, __u16 dns_qry_off, __u16 qu_num,
175     __u16 as_num)
176 {
177     __u16 offset;
178     bool is_in_pass = 0;
179     __u32 appuid = 0;
180 
181     if (qu_num == 1) {
182         __u16 key_len = 0;
183         struct domain_hash_key key = { 0 };
184 
185         offset = parse_queries(skb, dns_qry_off, (__u8*)&key.data, &key_len);
186         if (offset == 0) {
187             return 0;
188         }
189 
190         struct domain_value *deny_value = bpf_map_lookup_elem(&DOMAIN_DENY_MAP, &key);
191         if (deny_value != NULL && match_domain_value(skb, deny_value)) {
192             return 1;
193         }
194 
195         struct domain_value *allow_value = bpf_map_lookup_elem(&DOMAIN_PASS_MAP, &key);
196         __u32 current_uid = get_current_uid(skb);
197         struct defalut_action_value *default_value = bpf_map_lookup_elem(&DEFAULT_ACTION_MAP, &current_uid);
198         enum sk_action sk_act = SK_PASS;
199         if (default_value) {
200             sk_act = default_value->outaction;
201         }
202 
203         if (allow_value != NULL && match_domain_value(skb, allow_value)) {
204             is_in_pass = 1;
205             appuid = allow_value->appuid;
206             log_dbg(DBG_MATCH_DOMAIN_ACTION, EGRESS, SK_PASS);
207         } else {
208             return 0;
209         }
210     } else {
211         // not support multi questions
212         return 0;
213     }
214 
215     for (__u16 i = 0; i < DNS_ANSWER_CNT; i++) {
216         if (i >= as_num) {
217             break;
218         }
219         offset = parse_answers(skb, offset, is_in_pass, appuid);
220     }
221 
222     return 0;
223 }
224 
match_dns_query(struct __sk_buff * skb)225 static __always_inline enum sk_action match_dns_query(struct __sk_buff *skb)
226 {
227     if (!skb) {
228         return SK_PASS;
229     }
230 
231     __u8 protocol = 0;
232     __u32 l4_nhoff = 0;
233     if (skb->family == AF_INET) {
234         struct iphdr iph = { 0 };
235         bpf_skb_load_bytes(skb, 0, &iph, sizeof(struct iphdr));
236         protocol = iph.protocol;
237         l4_nhoff = sizeof(struct iphdr);
238     } else if (skb->family == AF_INET6) {
239         struct ipv6hdr ip6h = { 0 };
240         bpf_skb_load_bytes(skb, 0, &ip6h, sizeof(struct ipv6hdr));
241         protocol = ip6h.nexthdr;
242         l4_nhoff = sizeof(struct ipv6hdr);
243     }
244 
245     if (protocol == IPPROTO_UDP) {
246         __u16 src_port;
247         __u16 dst_port;
248         struct udphdr udph = { 0 };
249         bpf_skb_load_bytes(skb, l4_nhoff, &udph, sizeof(struct udphdr));
250 
251         src_port = bpf_ntohs(udph.source);
252         dst_port = bpf_ntohs(udph.dest);
253         if (src_port == DNS_PORT || dst_port == DNS_PORT) {
254             struct dnshdr dnsh = { 0 };
255             bpf_skb_load_bytes(skb, l4_nhoff + sizeof(struct udphdr), &dnsh, sizeof(struct dnshdr));
256             __u16 qu_num = bpf_ntohs(dnsh.qdcount);
257             __u16 as_num = bpf_ntohs(dnsh.ancount);
258 
259             __u16 dns_qry_off = l4_nhoff + sizeof(struct udphdr) + sizeof(struct dnshdr);
260             __u16 flag = bpf_ntohs(dnsh.flag);
261             __u16 bit_mask = (1 << DNS_QR_DEFALUT_MASK);
262             __u16 qr = (flag & bit_mask);
263             __u16 res = 0;
264             if (qr == bit_mask) {
265                 res = parse_dns_response(skb, dns_qry_off, qu_num, as_num);
266             } else {
267                 res = parse_dns_query(skb, dns_qry_off, qu_num);
268             }
269             if (res == 1) {
270                 log_dbg(DBG_MATCH_DOMAIN_ACTION, EGRESS, SK_DROP);
271                 return SK_DROP;
272             }
273         }
274         return SK_PASS;
275     }
276     return SK_PASS;
277 }
278 
279 #endif // NET_FIREWALL_DOMAIN_H