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