xref: /netbsd/external/bsd/unbound/dist/ipset/ipset.c (revision 17b85d8b)
1 /**
2  * \file
3  * This file implements the ipset module.  It can handle packets by putting
4  * the A and AAAA addresses that are configured in unbound.conf as type
5  * ipset (local-zone statements) into a firewall rule IPSet.  For firewall
6  * blacklist and whitelist usage.
7  */
8 #include "config.h"
9 #include "ipset/ipset.h"
10 #include "util/regional.h"
11 #include "util/net_help.h"
12 #include "util/config_file.h"
13 
14 #include "services/cache/dns.h"
15 
16 #include "sldns/sbuffer.h"
17 #include "sldns/wire2str.h"
18 #include "sldns/parseutil.h"
19 
20 #include <libmnl/libmnl.h>
21 #include <linux/netfilter/nfnetlink.h>
22 #include <linux/netfilter/ipset/ip_set.h>
23 
24 #define BUFF_LEN 256
25 
26 /**
27  * Return an error
28  * @param qstate: our query state
29  * @param id: module id
30  * @param rcode: error code (DNS errcode).
31  * @return: 0 for use by caller, to make notation easy, like:
32  * 	return error_response(..).
33  */
error_response(struct module_qstate * qstate,int id,int rcode)34 static int error_response(struct module_qstate* qstate, int id, int rcode) {
35 	verbose(VERB_QUERY, "return error response %s",
36 		sldns_lookup_by_id(sldns_rcodes, rcode)?
37 		sldns_lookup_by_id(sldns_rcodes, rcode)->name:"??");
38 	qstate->return_rcode = rcode;
39 	qstate->return_msg = NULL;
40 	qstate->ext_state[id] = module_finished;
41 	return 0;
42 }
43 
open_mnl_socket()44 static struct mnl_socket * open_mnl_socket() {
45 	struct mnl_socket *mnl;
46 
47 	mnl = mnl_socket_open(NETLINK_NETFILTER);
48 	if (!mnl) {
49 		log_err("ipset: could not open netfilter.");
50 		return NULL;
51 	}
52 
53 	if (mnl_socket_bind(mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
54 		mnl_socket_close(mnl);
55 		log_err("ipset: could not bind netfilter.");
56 		return NULL;
57 	}
58 	return mnl;
59 }
60 
add_to_ipset(struct mnl_socket * mnl,const char * setname,const void * ipaddr,int af)61 static int add_to_ipset(struct mnl_socket *mnl, const char *setname, const void *ipaddr, int af) {
62 	struct nlmsghdr *nlh;
63 	struct nfgenmsg *nfg;
64 	struct nlattr *nested[2];
65 	static char buffer[BUFF_LEN];
66 
67 	if (strlen(setname) >= IPSET_MAXNAMELEN) {
68 		errno = ENAMETOOLONG;
69 		return -1;
70 	}
71 	if (af != AF_INET && af != AF_INET6) {
72 		errno = EAFNOSUPPORT;
73 		return -1;
74 	}
75 
76 	nlh = mnl_nlmsg_put_header(buffer);
77 	nlh->nlmsg_type = IPSET_CMD_ADD | (NFNL_SUBSYS_IPSET << 8);
78 	nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL;
79 
80 	nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
81 	nfg->nfgen_family = af;
82 	nfg->version = NFNETLINK_V0;
83 	nfg->res_id = htons(0);
84 
85 	mnl_attr_put_u8(nlh, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL);
86 	mnl_attr_put(nlh, IPSET_ATTR_SETNAME, strlen(setname) + 1, setname);
87 	nested[0] = mnl_attr_nest_start(nlh, IPSET_ATTR_DATA);
88 	nested[1] = mnl_attr_nest_start(nlh, IPSET_ATTR_IP);
89 	mnl_attr_put(nlh, (af == AF_INET ? IPSET_ATTR_IPADDR_IPV4 : IPSET_ATTR_IPADDR_IPV6)
90 			| NLA_F_NET_BYTEORDER, (af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr)), ipaddr);
91 	mnl_attr_nest_end(nlh, nested[1]);
92 	mnl_attr_nest_end(nlh, nested[0]);
93 
94 	if (mnl_socket_sendto(mnl, nlh, nlh->nlmsg_len) < 0) {
95 		return -1;
96 	}
97 	return 0;
98 }
99 
100 static void
ipset_add_rrset_data(struct ipset_env * ie,struct mnl_socket * mnl,struct packed_rrset_data * d,const char * setname,int af,const char * dname)101 ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl,
102 	struct packed_rrset_data *d, const char* setname, int af,
103 	const char* dname)
104 {
105 	int ret;
106 	size_t j, rr_len, rd_len;
107 	uint8_t *rr_data;
108 
109 	/* to d->count, not d->rrsig_count, because we do not want to add the RRSIGs, only the addresses */
110 	for (j = 0; j < d->count; j++) {
111 		rr_len = d->rr_len[j];
112 		rr_data = d->rr_data[j];
113 
114 		rd_len = sldns_read_uint16(rr_data);
115 		if(af == AF_INET && rd_len != INET_SIZE)
116 			continue;
117 		if(af == AF_INET6 && rd_len != INET6_SIZE)
118 			continue;
119 		if (rr_len - 2 >= rd_len) {
120 			if(verbosity >= VERB_QUERY) {
121 				char ip[128];
122 				if(inet_ntop(af, rr_data+2, ip, (socklen_t)sizeof(ip)) == 0)
123 					snprintf(ip, sizeof(ip), "(inet_ntop_error)");
124 				verbose(VERB_QUERY, "ipset: add %s to %s for %s", ip, setname, dname);
125 			}
126 			ret = add_to_ipset(mnl, setname, rr_data + 2, af);
127 			if (ret < 0) {
128 				log_err("ipset: could not add %s into %s", dname, setname);
129 
130 				mnl_socket_close(mnl);
131 				ie->mnl = NULL;
132 				break;
133 			}
134 		}
135 	}
136 }
137 
138 static int
ipset_check_zones_for_rrset(struct module_env * env,struct ipset_env * ie,struct mnl_socket * mnl,struct ub_packed_rrset_key * rrset,const char * qname,const int qlen,const char * setname,int af)139 ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie,
140 	struct mnl_socket *mnl, struct ub_packed_rrset_key *rrset,
141 	const char *qname, const int qlen, const char *setname, int af)
142 {
143 	static char dname[BUFF_LEN];
144 	const char *ds, *qs;
145 	int dlen, plen;
146 
147 	struct config_strlist *p;
148 	struct packed_rrset_data *d;
149 
150 	dlen = sldns_wire2str_dname_buf(rrset->rk.dname, rrset->rk.dname_len, dname, BUFF_LEN);
151 	if (dlen == 0) {
152 		log_err("bad domain name");
153 		return -1;
154 	}
155 
156 	for (p = env->cfg->local_zones_ipset; p; p = p->next) {
157 		ds = NULL;
158 		qs = NULL;
159 		plen = strlen(p->str);
160 
161 		if (dlen >= plen) {
162 			ds = dname + (dlen - plen);
163 		}
164 		if (qlen >= plen) {
165 			qs = qname + (qlen - plen);
166 		}
167 		if ((ds && strncasecmp(p->str, ds, plen) == 0)
168 			|| (qs && strncasecmp(p->str, qs, plen) == 0)) {
169 			d = (struct packed_rrset_data*)rrset->entry.data;
170 			ipset_add_rrset_data(ie, mnl, d, setname,
171 				af, dname);
172 			break;
173 		}
174 	}
175 	return 0;
176 }
177 
ipset_update(struct module_env * env,struct dns_msg * return_msg,struct query_info qinfo,struct ipset_env * ie)178 static int ipset_update(struct module_env *env, struct dns_msg *return_msg,
179 	struct query_info qinfo, struct ipset_env *ie)
180 {
181 	struct mnl_socket *mnl;
182 	size_t i;
183 	const char *setname;
184 	struct ub_packed_rrset_key *rrset;
185 	int af;
186 	static char qname[BUFF_LEN];
187 	int qlen;
188 
189 	mnl = (struct mnl_socket *)ie->mnl;
190 	if (!mnl) {
191 		/* retry to create mnl socket */
192 		mnl = open_mnl_socket();
193 		if (!mnl) {
194 			return -1;
195 		}
196 		ie->mnl = mnl;
197 	}
198 
199 	qlen = sldns_wire2str_dname_buf(qinfo.qname, qinfo.qname_len,
200 		qname, BUFF_LEN);
201 	if(qlen == 0) {
202 		log_err("bad domain name");
203 		return -1;
204 	}
205 
206 	for(i = 0; i < return_msg->rep->rrset_count; i++) {
207 		setname = NULL;
208 		rrset = return_msg->rep->rrsets[i];
209 		if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A &&
210 			ie->v4_enabled == 1) {
211 			af = AF_INET;
212 			setname = ie->name_v4;
213 		} else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA &&
214 			ie->v6_enabled == 1) {
215 			af = AF_INET6;
216 			setname = ie->name_v6;
217 		}
218 
219 		if (setname) {
220 			if(ipset_check_zones_for_rrset(env, ie, mnl, rrset,
221 				qname, qlen, setname, af) == -1)
222 				return -1;
223 		}
224 	}
225 
226 	return 0;
227 }
228 
ipset_init(struct module_env * env,int id)229 int ipset_init(struct module_env* env, int id) {
230 	struct ipset_env *ipset_env;
231 
232 	ipset_env = (struct ipset_env *)calloc(1, sizeof(struct ipset_env));
233 	if (!ipset_env) {
234 		log_err("malloc failure");
235 		return 0;
236 	}
237 
238 	env->modinfo[id] = (void *)ipset_env;
239 
240 	ipset_env->mnl = NULL;
241 
242 	ipset_env->name_v4 = env->cfg->ipset_name_v4;
243 	ipset_env->name_v6 = env->cfg->ipset_name_v6;
244 
245 	ipset_env->v4_enabled = !ipset_env->name_v4 || (strlen(ipset_env->name_v4) == 0) ? 0 : 1;
246 	ipset_env->v6_enabled = !ipset_env->name_v6 || (strlen(ipset_env->name_v6) == 0) ? 0 : 1;
247 
248 	if ((ipset_env->v4_enabled < 1) && (ipset_env->v6_enabled < 1)) {
249 		log_err("ipset: set name no configuration?");
250 		return 0;
251 	}
252 
253 	return 1;
254 }
255 
ipset_deinit(struct module_env * env,int id)256 void ipset_deinit(struct module_env *env, int id) {
257 	struct mnl_socket *mnl;
258 	struct ipset_env *ipset_env;
259 
260 	if (!env || !env->modinfo[id]) {
261 		return;
262 	}
263 
264 	ipset_env = (struct ipset_env *)env->modinfo[id];
265 
266 	mnl = (struct mnl_socket *)ipset_env->mnl;
267 	if (mnl) {
268 		mnl_socket_close(mnl);
269 		ipset_env->mnl = NULL;
270 	}
271 
272 	free(ipset_env);
273 	env->modinfo[id] = NULL;
274 }
275 
ipset_new(struct module_qstate * qstate,int id)276 static int ipset_new(struct module_qstate* qstate, int id) {
277 	struct ipset_qstate *iq = (struct ipset_qstate *)regional_alloc(
278 		qstate->region, sizeof(struct ipset_qstate));
279 	qstate->minfo[id] = iq;
280 	if (!iq) {
281 		return 0;
282 	}
283 
284 	memset(iq, 0, sizeof(*iq));
285 	/* initialise it */
286 	/* TODO */
287 
288 	return 1;
289 }
290 
ipset_operate(struct module_qstate * qstate,enum module_ev event,int id,struct outbound_entry * outbound)291 void ipset_operate(struct module_qstate *qstate, enum module_ev event, int id,
292 	struct outbound_entry *outbound) {
293 	struct ipset_env *ie = (struct ipset_env *)qstate->env->modinfo[id];
294 	struct ipset_qstate *iq = (struct ipset_qstate *)qstate->minfo[id];
295 	verbose(VERB_QUERY, "ipset[module %d] operate: extstate:%s event:%s",
296 		id, strextstate(qstate->ext_state[id]), strmodulevent(event));
297 	if (iq) {
298 		log_query_info(VERB_QUERY, "ipset operate: query", &qstate->qinfo);
299 	}
300 
301 	/* perform ipset state machine */
302 	if ((event == module_event_new || event == module_event_pass) && !iq) {
303 		if (!ipset_new(qstate, id)) {
304 			(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
305 			return;
306 		}
307 		iq = (struct ipset_qstate*)qstate->minfo[id];
308 	}
309 
310 	if (iq && (event == module_event_pass || event == module_event_new)) {
311 		qstate->ext_state[id] = module_wait_module;
312 		return;
313 	}
314 
315 	if (iq && (event == module_event_moddone)) {
316 		if (qstate->return_msg && qstate->return_msg->rep) {
317 			ipset_update(qstate->env, qstate->return_msg, qstate->qinfo, ie);
318 		}
319 		qstate->ext_state[id] = module_finished;
320 		return;
321 	}
322 
323 	if (iq && outbound) {
324 		/* ipset does not need to process responses at this time
325 		 * ignore it.
326 		ipset_process_response(qstate, iq, ie, id, outbound, event);
327 		*/
328 		return;
329 	}
330 
331 	if (event == module_event_error) {
332 		verbose(VERB_ALGO, "got called with event error, giving up");
333 		(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
334 		return;
335 	}
336 
337 	if (!iq && (event == module_event_moddone)) {
338 		/* during priming, module done but we never started */
339 		qstate->ext_state[id] = module_finished;
340 		return;
341 	}
342 
343 	log_err("bad event for ipset");
344 	(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
345 }
346 
ipset_inform_super(struct module_qstate * ATTR_UNUSED (qstate),int ATTR_UNUSED (id),struct module_qstate * ATTR_UNUSED (super))347 void ipset_inform_super(struct module_qstate *ATTR_UNUSED(qstate),
348 	int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super)) {
349 	/* ipset does not use subordinate requests at this time */
350 	verbose(VERB_ALGO, "ipset inform_super was called");
351 }
352 
ipset_clear(struct module_qstate * qstate,int id)353 void ipset_clear(struct module_qstate *qstate, int id) {
354 	struct cachedb_qstate *iq;
355 	if (!qstate) {
356 		return;
357 	}
358 	iq = (struct cachedb_qstate *)qstate->minfo[id];
359 	if (iq) {
360 		/* free contents of iq */
361 		/* TODO */
362 	}
363 	qstate->minfo[id] = NULL;
364 }
365 
ipset_get_mem(struct module_env * env,int id)366 size_t ipset_get_mem(struct module_env *env, int id) {
367 	struct ipset_env *ie = (struct ipset_env *)env->modinfo[id];
368 	if (!ie) {
369 		return 0;
370 	}
371 	return sizeof(*ie);
372 }
373 
374 /**
375  * The ipset function block
376  */
377 static struct module_func_block ipset_block = {
378 	"ipset",
379 	&ipset_init, &ipset_deinit, &ipset_operate,
380 	&ipset_inform_super, &ipset_clear, &ipset_get_mem
381 };
382 
ipset_get_funcblock(void)383 struct module_func_block * ipset_get_funcblock(void) {
384 	return &ipset_block;
385 }
386 
387