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, ¤t_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