npf_ctl.c revision 1.3 1 /* $NetBSD: npf_ctl.c,v 1.3 2010/11/11 06:30:39 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 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.3 2010/11/11 06:30:39 rmind Exp $");
44
45 #include <sys/param.h>
46 #include <sys/conf.h>
47 #include <sys/kernel.h>
48
49 #include <prop/proplib.h>
50
51 #include "npf_ncode.h"
52 #include "npf_impl.h"
53
54 /*
55 * npfctl_switch: enable or disable packet inspection.
56 */
57 int
58 npfctl_switch(void *data)
59 {
60 const bool onoff = *(int *)data ? true : false;
61 int error;
62
63 if (onoff) {
64 /* Enable: add pfil hooks. */
65 error = npf_register_pfil();
66 } else {
67 /* Disable: remove pfil hooks. */
68 npf_unregister_pfil();
69 error = 0;
70 }
71 return error;
72 }
73
74 static int
75 npf_mk_tables(npf_tableset_t *tblset, prop_array_t tables)
76 {
77 prop_object_iterator_t it;
78 prop_dictionary_t tbldict;
79 prop_object_t obj;
80 int error = 0;
81
82 /* Tables - array. */
83 if (prop_object_type(tables) != PROP_TYPE_ARRAY)
84 return EINVAL;
85
86 it = prop_array_iterator(tables);
87 if (it == NULL)
88 return ENOMEM;
89
90 while ((tbldict = prop_object_iterator_next(it)) != NULL) {
91 prop_dictionary_t ent;
92 prop_object_iterator_t eit;
93 prop_array_t entries;
94 npf_table_t *t;
95 u_int tid;
96 int type;
97
98 /* Table - dictionary. */
99 if (prop_object_type(tbldict) != PROP_TYPE_DICTIONARY) {
100 error = EINVAL;
101 break;
102 }
103
104 /* Table ID and type. */
105 obj = prop_dictionary_get(tbldict, "id");
106 tid = (u_int)prop_number_integer_value(obj);
107 obj = prop_dictionary_get(tbldict, "type");
108 type = (int)prop_number_integer_value(obj);
109 /* Validate them. */
110 error = npf_table_check(tblset, tid, type);
111 if (error)
112 break;
113
114 /* Create and insert the table. */
115 t = npf_table_create(tid, type, 1024); /* XXX */
116 if (t == NULL) {
117 error = ENOMEM;
118 break;
119 }
120 error = npf_tableset_insert(tblset, t);
121 KASSERT(error == 0);
122
123 /* Entries. */
124 entries = prop_dictionary_get(tbldict, "entries");
125 if (prop_object_type(entries) != PROP_TYPE_ARRAY) {
126 error = EINVAL;
127 break;
128 }
129 eit = prop_array_iterator(entries);
130 if (eit == NULL) {
131 error = ENOMEM;
132 break;
133 }
134 while ((ent = prop_object_iterator_next(eit)) != NULL) {
135 in_addr_t addr, mask; /* XXX: IPv6 */
136
137 /* Address. */
138 obj = prop_dictionary_get(ent, "addr");
139 addr = (in_addr_t)prop_number_integer_value(obj);
140 /* Mask. */
141 obj = prop_dictionary_get(ent, "mask");
142 mask = (in_addr_t)prop_number_integer_value(obj);
143 /* Add a table entry. */
144 error = npf_table_add_v4cidr(tblset, tid, addr, mask);
145 if (error)
146 break;
147 }
148 prop_object_iterator_release(eit);
149 if (error)
150 break;
151 }
152 prop_object_iterator_release(it);
153 /*
154 * Note: in a case of error, caller will free entire tableset.
155 */
156 return error;
157 }
158
159 static void *
160 npf_mk_ncode(const void *ncptr, size_t nc_size)
161 {
162 int npf_err, errat;
163 void *nc;
164
165 /*
166 * Allocate and copy n-code.
167 *
168 * XXX: Inefficient; consider extending proplib(9) to provide
169 * interface for custom allocator and avoid copy.
170 */
171 nc = npf_ncode_alloc(nc_size);
172 if (nc == NULL) {
173 return NULL;
174 }
175 memcpy(nc, ncptr, nc_size);
176 npf_err = npf_ncode_validate(nc, nc_size, &errat);
177 if (npf_err) {
178 npf_ncode_free(nc, nc_size);
179 /* TODO: return error details via proplib */
180 return NULL;
181 }
182 return nc;
183 }
184
185 static int
186 npf_mk_singlerule(prop_dictionary_t rldict,
187 npf_ruleset_t *rlset, npf_rule_t **parent)
188 {
189 npf_rule_t *rl;
190 prop_object_t obj;
191 int attr, ifidx, minttl, maxmss;
192 pri_t pri;
193 bool rnd_ipid;
194 size_t nc_size;
195 void *nc;
196
197 /* Rule - dictionary. */
198 if (prop_object_type(rldict) != PROP_TYPE_DICTIONARY)
199 return EINVAL;
200
201 /* Attributes (integer). */
202 obj = prop_dictionary_get(rldict, "attributes");
203 attr = prop_number_integer_value(obj);
204
205 /* Priority (integer). */
206 obj = prop_dictionary_get(rldict, "priority");
207 pri = prop_number_integer_value(obj);
208
209 /* Interface ID (integer). */
210 obj = prop_dictionary_get(rldict, "interface");
211 ifidx = prop_number_integer_value(obj);
212
213 /* Randomize IP ID (bool). */
214 obj = prop_dictionary_get(rldict, "randomize-id");
215 rnd_ipid = prop_bool_true(obj);
216
217 /* Minimum IP TTL (integer). */
218 obj = prop_dictionary_get(rldict, "min-ttl");
219 minttl = prop_number_integer_value(obj);
220
221 /* Maximum TCP MSS (integer). */
222 obj = prop_dictionary_get(rldict, "max-mss");
223 maxmss = prop_number_integer_value(obj);
224
225 /* N-code (binary data). */
226 obj = prop_dictionary_get(rldict, "ncode");
227 if (obj) {
228 const void *ncptr;
229
230 /* Perform n-code validation. */
231 nc_size = prop_data_size(obj);
232 ncptr = prop_data_data_nocopy(obj);
233 if (ncptr == NULL || nc_size > NPF_NCODE_LIMIT) {
234 return EINVAL;
235 }
236 nc = npf_mk_ncode(ncptr, nc_size);
237 if (nc == NULL) {
238 return EINVAL;
239 }
240 } else {
241 /* No n-code. */
242 nc = NULL;
243 nc_size = 0;
244 }
245
246 /* Allocate and setup NPF rule. */
247 rl = npf_rule_alloc(attr, pri, ifidx, nc, nc_size,
248 rnd_ipid, minttl, maxmss);
249 if (rl == NULL) {
250 if (nc) {
251 npf_ncode_free(nc, nc_size); /* XXX */
252 }
253 return ENOMEM;
254 }
255 npf_ruleset_insert(rlset, rl);
256 if (parent) {
257 *parent = rl;
258 }
259 return 0;
260 }
261
262 static int
263 npf_mk_rules(npf_ruleset_t *rlset, prop_array_t rules)
264 {
265 prop_object_iterator_t it;
266 prop_dictionary_t rldict;
267 int error;
268
269 /* Ruleset - array. */
270 if (prop_object_type(rules) != PROP_TYPE_ARRAY)
271 return EINVAL;
272
273 it = prop_array_iterator(rules);
274 if (it == NULL)
275 return ENOMEM;
276
277 error = 0;
278 while ((rldict = prop_object_iterator_next(it)) != NULL) {
279 prop_object_iterator_t sit;
280 prop_array_t subrules;
281 prop_dictionary_t srldict;
282 npf_rule_t *myrl;
283
284 /* Generate a single rule. */
285 error = npf_mk_singlerule(rldict, rlset, &myrl);
286 if (error)
287 break;
288
289 /* Check for subrules. */
290 subrules = prop_dictionary_get(rldict, "subrules");
291 if (subrules == NULL) {
292 /* No subrules, next.. */
293 continue;
294 }
295 /* Generate subrules, if any. */
296 if (prop_object_type(subrules) != PROP_TYPE_ARRAY) {
297 error = EINVAL;
298 break;
299 }
300 sit = prop_array_iterator(subrules);
301 if (sit == NULL) {
302 error = ENOMEM;
303 break;
304 }
305 while ((srldict = prop_object_iterator_next(sit)) != NULL) {
306 /* For subrule, pass ruleset pointer of parent. */
307 error = npf_mk_singlerule(srldict,
308 npf_rule_subset(myrl), NULL);
309 if (error)
310 break;
311 }
312 prop_object_iterator_release(sit);
313 if (error)
314 break;
315 }
316 prop_object_iterator_release(it);
317 /*
318 * Note: in a case of error, caller will free entire ruleset.
319 */
320 return error;
321 }
322
323 static int
324 npf_mk_natlist(npf_ruleset_t *nset, prop_array_t natlist)
325 {
326 prop_object_iterator_t it;
327 prop_dictionary_t natdict;
328 int error;
329
330 /* NAT policies - array. */
331 if (prop_object_type(natlist) != PROP_TYPE_ARRAY)
332 return EINVAL;
333
334 it = prop_array_iterator(natlist);
335 if (it == NULL)
336 return ENOMEM;
337
338 error = 0;
339 while ((natdict = prop_object_iterator_next(it)) != NULL) {
340 prop_object_t obj;
341 npf_natpolicy_t *np;
342 npf_rule_t *rl;
343 const npf_addr_t *taddr;
344 size_t taddr_sz;
345 in_port_t tport;
346 int type, flags;
347
348 /* NAT policy - dictionary. */
349 if (prop_object_type(natdict) != PROP_TYPE_DICTIONARY) {
350 error = EINVAL;
351 break;
352 }
353
354 /* Translation type. */
355 obj = prop_dictionary_get(natdict, "type");
356 type = prop_number_integer_value(obj);
357
358 /* Translation type. */
359 obj = prop_dictionary_get(natdict, "flags");
360 flags = prop_number_integer_value(obj);
361
362 /* Translation IP. */
363 obj = prop_dictionary_get(natdict, "translation-ip");
364 taddr_sz = prop_data_size(obj);
365 taddr = (const npf_addr_t *)prop_data_data_nocopy(obj);
366
367 /* Translation port (for redirect case). */
368 obj = prop_dictionary_get(natdict, "translation-port");
369 tport = (in_port_t)prop_number_integer_value(obj);
370
371 /*
372 * NAT policies are standard rules, plus additional
373 * information for translation. Make a rule.
374 */
375 error = npf_mk_singlerule(natdict, nset, &rl);
376 if (error)
377 break;
378
379 /* Allocate a new NAT policy and assign to the rule. */
380 np = npf_nat_newpolicy(type, flags, taddr, taddr_sz, tport);
381 if (np == NULL) {
382 error = ENOMEM;
383 break;
384 }
385 npf_rule_setnat(rl, np);
386 }
387 prop_object_iterator_release(it);
388 /*
389 * Note: in a case of error, caller will free entire NAT ruleset
390 * with assigned NAT policies.
391 */
392 return error;
393 }
394
395 /*
396 * npfctl_reload: store passed data i.e. update settings, create passed
397 * tables, rules and atomically activate all them.
398 */
399 int
400 npfctl_reload(u_long cmd, void *data)
401 {
402 const struct plistref *pref = data;
403 npf_tableset_t *tblset = NULL;
404 npf_ruleset_t *rlset = NULL;
405 npf_ruleset_t *nset = NULL;
406 prop_dictionary_t dict;
407 prop_array_t natlist, tables, rules;
408 prop_object_t ver;
409 int error;
410
411 /* Retrieve the dictionary. */
412 #ifdef _KERNEL
413 error = prop_dictionary_copyin_ioctl(pref, cmd, &dict);
414 if (error)
415 return error;
416 #else
417 dict = prop_dictionary_internalize_from_file(data);
418 if (dict == NULL)
419 return EINVAL;
420 #endif
421 /* Version. */
422 ver = prop_dictionary_get(dict, "version");
423 if (ver == NULL || prop_number_integer_value(ver) != NPF_VERSION) {
424 error = EINVAL;
425 goto fail;
426 }
427
428 /* XXX: Hard way for now. */
429 (void)npf_session_tracking(false);
430
431 /* NAT policies. */
432 nset = npf_ruleset_create();
433 natlist = prop_dictionary_get(dict, "translation");
434 error = npf_mk_natlist(nset, natlist);
435 if (error)
436 goto fail;
437
438 /* Tables. */
439 tblset = npf_tableset_create();
440 tables = prop_dictionary_get(dict, "tables");
441 error = npf_mk_tables(tblset, tables);
442 if (error)
443 goto fail;
444
445 /* Rules. */
446 rlset = npf_ruleset_create();
447 rules = prop_dictionary_get(dict, "rules");
448 error = npf_mk_rules(rlset, rules);
449 if (error)
450 goto fail;
451
452 /* Flush and reload NAT policies. */
453 npf_nat_reload(nset);
454
455 /*
456 * Finally, reload the ruleset. It will also reload the tableset.
457 * Operation will be performed as a single transaction.
458 */
459 npf_ruleset_reload(rlset, tblset);
460
461 (void)npf_session_tracking(true);
462
463 /* Done. Since data is consumed now, we shall not destroy it. */
464 tblset = NULL;
465 rlset = NULL;
466 nset = NULL;
467 fail:
468 prop_object_release(dict);
469 /*
470 * Note: destroy rulesets first, to drop references to the tableset.
471 */
472 KASSERT(error == 0 || (nset || rlset || tblset));
473 if (nset) {
474 npf_ruleset_destroy(nset);
475 }
476 if (rlset) {
477 npf_ruleset_destroy(rlset);
478 }
479 if (tblset) {
480 npf_tableset_destroy(tblset);
481 }
482 return error;
483 }
484
485 /*
486 * npfctl_table: add, remove or query entries in the specified table.
487 *
488 * For maximum performance, interface is avoiding proplib(3)'s overhead.
489 */
490 int
491 npfctl_table(void *data)
492 {
493 npf_ioctl_table_t *nct = data;
494 int error;
495
496 switch (nct->nct_action) {
497 case NPF_IOCTL_TBLENT_ADD:
498 error = npf_table_add_v4cidr(NULL, nct->nct_tid,
499 nct->nct_addr, nct->nct_mask);
500 break;
501 case NPF_IOCTL_TBLENT_REM:
502 error = npf_table_rem_v4cidr(NULL, nct->nct_tid,
503 nct->nct_addr, nct->nct_mask);
504 break;
505 default:
506 /* XXX */
507 error = npf_table_match_v4addr(nct->nct_tid, nct->nct_addr);
508 if (error) {
509 error = EINVAL;
510 }
511 }
512 return error;
513 }
514