npf_ctl.c revision 1.2 1 /* $NetBSD: npf_ctl.c,v 1.2 2010/09/16 04:53:27 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.2 2010/09/16 04:53:27 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 taddr;
332 in_port_t tport;
333 int type, flags;
334
335 /* NAT policy - dictionary. */
336 if (prop_object_type(natdict) != PROP_TYPE_DICTIONARY) {
337 error = EINVAL;
338 break;
339 }
340
341 /* Translation type. */
342 obj = prop_dictionary_get(natdict, "type");
343 type = prop_number_integer_value(obj);
344
345 /* Translation type. */
346 obj = prop_dictionary_get(natdict, "flags");
347 flags = prop_number_integer_value(obj);
348
349 /* Translation IP. */
350 obj = prop_dictionary_get(natdict, "translation_ip");
351 taddr = (in_addr_t)prop_number_integer_value(obj);
352
353 /* Translation port (for redirect case). */
354 obj = prop_dictionary_get(natdict, "translation_port");
355 tport = (in_addr_t)prop_number_integer_value(obj);
356
357 /*
358 * NAT policies are standard rules, plus additional
359 * information for translation. Make a rule.
360 */
361 error = npf_mk_singlerule(natdict, nset, &rl);
362 if (error)
363 break;
364
365 /* Allocate a new NAT policy and assign to the rule. */
366 np = npf_nat_newpolicy(type, flags, taddr, tport);
367 if (np == NULL) {
368 error = ENOMEM;
369 break;
370 }
371 npf_rule_setnat(rl, np);
372 }
373 prop_object_iterator_release(it);
374 /*
375 * Note: in a case of error, caller will free entire NAT ruleset
376 * with assigned NAT policies.
377 */
378 return error;
379 }
380
381 /*
382 * npfctl_reload: store passed data i.e. update settings, create passed
383 * tables, rules and atomically activate all them.
384 */
385 int
386 npfctl_reload(u_long cmd, void *data)
387 {
388 const struct plistref *pref = data;
389 npf_tableset_t *tblset = NULL;
390 npf_ruleset_t *rlset = NULL;
391 npf_ruleset_t *nset = NULL;
392 prop_dictionary_t dict;
393 prop_array_t natlist, tables, rules;
394 prop_object_t ver;
395 int error;
396
397 /* Retrieve the dictionary. */
398 #ifdef _KERNEL
399 error = prop_dictionary_copyin_ioctl(pref, cmd, &dict);
400 if (error)
401 return error;
402 #else
403 dict = prop_dictionary_internalize_from_file(data);
404 if (dict == NULL)
405 return EINVAL;
406 #endif
407 /* Version. */
408 ver = prop_dictionary_get(dict, "version");
409 if (ver == NULL || prop_number_integer_value(ver) != NPF_VERSION) {
410 error = EINVAL;
411 goto fail;
412 }
413
414 /* XXX: Hard way for now. */
415 (void)npf_session_tracking(false);
416
417 /* NAT policies. */
418 nset = npf_ruleset_create();
419 natlist = prop_dictionary_get(dict, "translation");
420 error = npf_mk_natlist(nset, natlist);
421 if (error)
422 goto fail;
423
424 /* Tables. */
425 tblset = npf_tableset_create();
426 tables = prop_dictionary_get(dict, "tables");
427 error = npf_mk_tables(tblset, tables);
428 if (error)
429 goto fail;
430
431 /* Rules. */
432 rlset = npf_ruleset_create();
433 rules = prop_dictionary_get(dict, "rules");
434 error = npf_mk_rules(rlset, rules);
435 if (error)
436 goto fail;
437
438 /* Flush and reload NAT policies. */
439 npf_nat_reload(nset);
440
441 /*
442 * Finally, reload the ruleset. It will also reload the tableset.
443 * Operation will be performed as a single transaction.
444 */
445 npf_ruleset_reload(rlset, tblset);
446
447 (void)npf_session_tracking(true);
448
449 /* Done. Since data is consumed now, we shall not destroy it. */
450 tblset = NULL;
451 rlset = NULL;
452 nset = NULL;
453 fail:
454 prop_object_release(dict);
455 /*
456 * Note: destroy rulesets first, to drop references to the tableset.
457 */
458 KASSERT(error == 0 || (nset || rlset || tblset));
459 if (nset) {
460 npf_ruleset_destroy(nset);
461 }
462 if (rlset) {
463 npf_ruleset_destroy(rlset);
464 }
465 if (tblset) {
466 npf_tableset_destroy(tblset);
467 }
468 return error;
469 }
470
471 /*
472 * npfctl_table: add, remove or query entries in the specified table.
473 *
474 * For maximum performance, interface is avoiding proplib(3)'s overhead.
475 */
476 int
477 npfctl_table(void *data)
478 {
479 npf_ioctl_table_t *nct = data;
480 int error;
481
482 switch (nct->nct_action) {
483 case NPF_IOCTL_TBLENT_ADD:
484 error = npf_table_add_v4cidr(NULL, nct->nct_tid,
485 nct->nct_addr, nct->nct_mask);
486 break;
487 case NPF_IOCTL_TBLENT_REM:
488 error = npf_table_rem_v4cidr(NULL, nct->nct_tid,
489 nct->nct_addr, nct->nct_mask);
490 break;
491 default:
492 /* XXX */
493 error = npf_table_match_v4addr(nct->nct_tid, nct->nct_addr);
494 if (error) {
495 error = EINVAL;
496 }
497 }
498 return error;
499 }
500