xref: /netbsd-src/sys/net/npf/npf_ctl.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: npf_ctl.c,v 1.1 2010/08/22 18:56:22 rmind Exp $	*/
2 
3 /*-
4  * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This material is based upon work partially supported by The
8  * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * NPF device control.
34  *
35  * Implementation of (re)loading, construction of tables and rules.
36  * NPF proplib(9) dictionary consumer.
37  *
38  * TODO:
39  * - Consider implementing 'sync' functionality.
40  */
41 
42 #ifdef _KERNEL
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.1 2010/08/22 18:56:22 rmind Exp $");
45 
46 #include <sys/param.h>
47 #include <sys/conf.h>
48 #include <sys/kernel.h>
49 #endif
50 
51 #include <prop/proplib.h>
52 
53 #include "npf_ncode.h"
54 #include "npf_impl.h"
55 
56 /*
57  * npfctl_switch: enable or disable packet inspection.
58  */
59 int
60 npfctl_switch(void *data)
61 {
62 	const bool onoff = *(int *)data ? true : false;
63 	int error;
64 
65 	if (onoff) {
66 		/* Enable: add pfil hooks. */
67 		error = npf_register_pfil();
68 	} else {
69 		/* Disable: remove pfil hooks. */
70 		npf_unregister_pfil();
71 		error = 0;
72 	}
73 	return error;
74 }
75 
76 static int
77 npf_mk_tables(npf_tableset_t *tblset, prop_array_t tables)
78 {
79 	prop_object_iterator_t it;
80 	prop_dictionary_t tbldict;
81 	prop_object_t obj;
82 	int error = 0;
83 
84 	/* Tables - array. */
85 	if (prop_object_type(tables) != PROP_TYPE_ARRAY)
86 		return EINVAL;
87 
88 	it = prop_array_iterator(tables);
89 	if (it == NULL)
90 		return ENOMEM;
91 
92 	while ((tbldict = prop_object_iterator_next(it)) != NULL) {
93 		prop_dictionary_t ent;
94 		prop_object_iterator_t eit;
95 		prop_array_t entries;
96 		npf_table_t *t;
97 		u_int tid;
98 		int type;
99 
100 		/* Table - dictionary. */
101 		if (prop_object_type(tbldict) != PROP_TYPE_DICTIONARY) {
102 			error = EINVAL;
103 			break;
104 		}
105 
106 		/* Table ID and type. */
107 		obj = prop_dictionary_get(tbldict, "id");
108 		tid = (u_int)prop_number_integer_value(obj);
109 		obj = prop_dictionary_get(tbldict, "type");
110 		type = (int)prop_number_integer_value(obj);
111 		/* Validate them. */
112 		error = npf_table_check(tblset, tid, type);
113 		if (error)
114 			break;
115 
116 		/* Create and insert the table. */
117 		t = npf_table_create(tid, type, 1024);	/* XXX */
118 		if (t == NULL) {
119 			error = ENOMEM;
120 			break;
121 		}
122 		error = npf_tableset_insert(tblset, t);
123 		KASSERT(error == 0);
124 
125 		/* Entries. */
126 		entries = prop_dictionary_get(tbldict, "entries");
127 		if (prop_object_type(entries) != PROP_TYPE_ARRAY) {
128 			error = EINVAL;
129 			break;
130 		}
131 		eit = prop_array_iterator(entries);
132 		if (eit == NULL) {
133 			error = ENOMEM;
134 			break;
135 		}
136 		while ((ent = prop_object_iterator_next(eit)) != NULL) {
137 			in_addr_t addr, mask;	/* XXX: IPv6 */
138 
139 			/* Address. */
140 			obj = prop_dictionary_get(ent, "addr");
141 			addr = (in_addr_t)prop_number_integer_value(obj);
142 			/* Mask. */
143 			obj = prop_dictionary_get(ent, "mask");
144 			mask = (in_addr_t)prop_number_integer_value(obj);
145 			/* Add a table entry. */
146 			error = npf_table_add_v4cidr(tblset, tid, addr, mask);
147 			if (error)
148 				break;
149 		}
150 		prop_object_iterator_release(eit);
151 		if (error)
152 			break;
153 	}
154 	prop_object_iterator_release(it);
155 	/*
156 	 * Note: in a case of error, caller will free entire tableset.
157 	 */
158 	return error;
159 }
160 
161 static void *
162 npf_mk_ncode(const void *ncptr, size_t nc_size)
163 {
164 	int npf_err, errat;
165 	void *nc;
166 
167 	/*
168 	 * Allocate and copy n-code.
169 	 *
170 	 * XXX: Inefficient; consider extending proplib(9) to provide
171 	 * interface for custom allocator and avoid copy.
172 	 */
173 	nc = npf_ncode_alloc(nc_size);
174 	if (nc == NULL) {
175 		return NULL;
176 	}
177 	memcpy(nc, ncptr, nc_size);
178 	npf_err = npf_ncode_validate(nc, nc_size, &errat);
179 	if (npf_err) {
180 		npf_ncode_free(nc, nc_size);
181 		/* TODO: return error details via proplib */
182 		return NULL;
183 	}
184 	return nc;
185 }
186 
187 static int
188 npf_mk_singlerule(prop_dictionary_t rldict,
189     npf_ruleset_t *rlset, npf_rule_t **parent)
190 {
191 	npf_rule_t *rl;
192 	prop_object_t obj;
193 	int attr, ifidx;
194 	pri_t pri;
195 	size_t nc_size;
196 	void *nc;
197 
198 	/* Rule - dictionary. */
199 	if (prop_object_type(rldict) != PROP_TYPE_DICTIONARY)
200 		return EINVAL;
201 
202 	/* Attributes (integer). */
203 	obj = prop_dictionary_get(rldict, "attributes");
204 	attr = prop_number_integer_value(obj);
205 
206 	/* Priority (integer). */
207 	obj = prop_dictionary_get(rldict, "priority");
208 	pri = prop_number_integer_value(obj);
209 
210 	/* Interface ID (integer). */
211 	obj = prop_dictionary_get(rldict, "interface");
212 	ifidx = prop_number_integer_value(obj);
213 
214 	/* N-code (binary data). */
215 	obj = prop_dictionary_get(rldict, "ncode");
216 	if (obj) {
217 		const void *ncptr;
218 
219 		/* Perform n-code validation. */
220 		nc_size = prop_data_size(obj);
221 		ncptr = prop_data_data_nocopy(obj);
222 		if (ncptr == NULL || nc_size > NPF_NCODE_LIMIT) {
223 			return EINVAL;
224 		}
225 		nc = npf_mk_ncode(ncptr, nc_size);
226 		if (nc == NULL) {
227 			return EINVAL;
228 		}
229 	} else {
230 		/* No n-code. */
231 		nc = NULL;
232 		nc_size = 0;
233 	}
234 
235 	/* Allocate and setup NPF rule. */
236 	rl = npf_rule_alloc(attr, pri, ifidx, nc, nc_size);
237 	if (rl == NULL) {
238 		if (nc) {
239 			npf_ncode_free(nc, nc_size);	/* XXX */
240 		}
241 		return ENOMEM;
242 	}
243 	npf_ruleset_insert(rlset, rl);
244 	if (parent) {
245 		*parent = rl;
246 	}
247 	return 0;
248 }
249 
250 static int
251 npf_mk_rules(npf_ruleset_t *rlset, prop_array_t rules)
252 {
253 	prop_object_iterator_t it;
254 	prop_dictionary_t rldict;
255 	int error;
256 
257 	/* Ruleset - array. */
258 	if (prop_object_type(rules) != PROP_TYPE_ARRAY)
259 		return EINVAL;
260 
261 	it = prop_array_iterator(rules);
262 	if (it == NULL)
263 		return ENOMEM;
264 
265 	error = 0;
266 	while ((rldict = prop_object_iterator_next(it)) != NULL) {
267 		prop_object_iterator_t sit;
268 		prop_array_t subrules;
269 		prop_dictionary_t srldict;
270 		npf_rule_t *myrl;
271 
272 		/* Generate a single rule. */
273 		error = npf_mk_singlerule(rldict, rlset, &myrl);
274 		if (error)
275 			break;
276 
277 		/* Check for subrules. */
278 		subrules = prop_dictionary_get(rldict, "subrules");
279 		if (subrules == NULL) {
280 			/* No subrules, next.. */
281 			continue;
282 		}
283 		/* Generate subrules, if any. */
284 		if (prop_object_type(subrules) != PROP_TYPE_ARRAY) {
285 			error = EINVAL;
286 			break;
287 		}
288 		sit = prop_array_iterator(subrules);
289 		if (sit == NULL) {
290 			error = ENOMEM;
291 			break;
292 		}
293 		while ((srldict = prop_object_iterator_next(sit)) != NULL) {
294 			/* For subrule, pass ruleset pointer of parent. */
295 			error = npf_mk_singlerule(srldict,
296 			    npf_rule_subset(myrl), NULL);
297 			if (error)
298 				break;
299 		}
300 		prop_object_iterator_release(sit);
301 		if (error)
302 			break;
303 	}
304 	prop_object_iterator_release(it);
305 	/*
306 	 * Note: in a case of error, caller will free entire ruleset.
307 	 */
308 	return error;
309 }
310 
311 static int
312 npf_mk_natlist(npf_ruleset_t *nset, prop_array_t natlist)
313 {
314 	prop_object_iterator_t it;
315 	prop_dictionary_t natdict;
316 	int error;
317 
318 	/* NAT policies - array. */
319 	if (prop_object_type(natlist) != PROP_TYPE_ARRAY)
320 		return EINVAL;
321 
322 	it = prop_array_iterator(natlist);
323 	if (it == NULL)
324 		return ENOMEM;
325 
326 	error = 0;
327 	while ((natdict = prop_object_iterator_next(it)) != NULL) {
328 		prop_object_t obj;
329 		npf_natpolicy_t *np;
330 		npf_rule_t *rl;
331 		in_addr_t gip;
332 
333 		/* NAT policy - dictionary. */
334 		if (prop_object_type(natdict) != PROP_TYPE_DICTIONARY) {
335 			error = EINVAL;
336 			break;
337 		}
338 
339 		/* Gateway IP. */
340 		obj = prop_dictionary_get(natdict, "gateway_ip");
341 		gip = (in_addr_t)prop_number_integer_value(obj);
342 
343 		/*
344 		 * NAT policies are standard rules, plus additional
345 		 * information for translation.  Make a rule.
346 		 */
347 		error = npf_mk_singlerule(natdict, nset, &rl);
348 		if (error)
349 			break;
350 
351 		/* Allocate a new NAT policy and assign to the rule. */
352 		np = npf_nat_newpolicy(gip);
353 		if (np == NULL) {
354 			error = ENOMEM;
355 			break;
356 		}
357 		npf_rule_setnat(rl, np);
358 	}
359 	prop_object_iterator_release(it);
360 	/*
361 	 * Note: in a case of error, caller will free entire NAT ruleset
362 	 * with assigned NAT policies.
363 	 */
364 	return error;
365 }
366 
367 /*
368  * npfctl_reload: store passed data i.e. update settings, create passed
369  * tables, rules and atomically activate all them.
370  */
371 int
372 npfctl_reload(u_long cmd, void *data)
373 {
374 	const struct plistref *pref = data;
375 	npf_tableset_t *tblset = NULL;
376 	npf_ruleset_t *rlset = NULL;
377 	npf_ruleset_t *nset = NULL;
378 	prop_dictionary_t dict;
379 	prop_array_t natlist, tables, rules;
380 	prop_object_t ver;
381 	int error;
382 
383 	/* Retrieve the dictionary. */
384 #ifdef _KERNEL
385 	error = prop_dictionary_copyin_ioctl(pref, cmd, &dict);
386 	if (error)
387 		return error;
388 #else
389 	dict = prop_dictionary_internalize_from_file(data);
390 	if (dict == NULL)
391 		return EINVAL;
392 #endif
393 	/* Version. */
394 	ver = prop_dictionary_get(dict, "version");
395 	if (ver == NULL || prop_number_integer_value(ver) != NPF_VERSION) {
396 		error = EINVAL;
397 		goto fail;
398 	}
399 
400 	/* XXX: Hard way for now. */
401 	(void)npf_session_tracking(false);
402 
403 	/* NAT policies. */
404 	nset = npf_ruleset_create();
405 	natlist = prop_dictionary_get(dict, "nat");
406 	error = npf_mk_natlist(nset, natlist);
407 	if (error)
408 		goto fail;
409 
410 	/* Tables. */
411 	tblset = npf_tableset_create();
412 	tables = prop_dictionary_get(dict, "tables");
413 	error = npf_mk_tables(tblset, tables);
414 	if (error)
415 		goto fail;
416 
417 	/* Rules. */
418 	rlset = npf_ruleset_create();
419 	rules = prop_dictionary_get(dict, "rules");
420 	error = npf_mk_rules(rlset, rules);
421 	if (error)
422 		goto fail;
423 
424 	/* Flush and reload NAT policies. */
425 	npf_nat_reload(nset);
426 
427 	/*
428 	 * Finally, reload the ruleset.  It will also reload the tableset.
429 	 * Operation will be performed as a single transaction.
430 	 */
431 	npf_ruleset_reload(rlset, tblset);
432 
433 	(void)npf_session_tracking(true);
434 
435 	/* Done.  Since data is consumed now, we shall not destroy it. */
436 	tblset = NULL;
437 	rlset = NULL;
438 	nset = NULL;
439 fail:
440 	prop_object_release(dict);
441 	/*
442 	 * Note: destroy rulesets first, to drop references to the tableset.
443 	 */
444 	KASSERT(error == 0 || (nset || rlset || tblset));
445 	if (nset) {
446 		npf_ruleset_destroy(nset);
447 	}
448 	if (rlset) {
449 		npf_ruleset_destroy(rlset);
450 	}
451 	if (tblset) {
452 		npf_tableset_destroy(tblset);
453 	}
454 	return error;
455 }
456 
457 /*
458  * npf_table_ctl: add, remove or query entries in the specified table.
459  *
460  * For maximum performance, interface is avoiding proplib(3)'s overhead.
461  */
462 int
463 npfctl_table(void *data)
464 {
465 	npf_ioctl_table_t *nct = data;
466 	int error;
467 
468 	switch (nct->nct_action) {
469 	case NPF_IOCTL_TBLENT_ADD:
470 		error = npf_table_add_v4cidr(NULL, nct->nct_tid,
471 		    nct->nct_addr, nct->nct_mask);
472 		break;
473 	case NPF_IOCTL_TBLENT_REM:
474 		error = npf_table_rem_v4cidr(NULL, nct->nct_tid,
475 		    nct->nct_addr, nct->nct_mask);
476 		break;
477 	default:
478 		/* XXX */
479 		error = npf_table_match_v4addr(nct->nct_tid, nct->nct_addr);
480 		if (error) {
481 			error = EINVAL;
482 		}
483 	}
484 	return error;
485 }
486