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