1 /*- 2 * Copyright (c) 2009-2016 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This material is based upon work partially supported by The 6 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 /* 31 * NPF main: dynamic load/initialisation and unload routines. 32 */ 33 34 #ifdef _KERNEL 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: npf_os.c,v 1.17 2019/08/25 17:38:25 rmind Exp $"); 37 38 #ifdef _KERNEL_OPT 39 #include "pf.h" 40 #if NPF > 0 41 #error "NPF and PF are mutually exclusive; please select one" 42 #endif 43 #endif 44 45 #include <sys/param.h> 46 #include <sys/types.h> 47 48 #include <sys/conf.h> 49 #include <sys/kauth.h> 50 #include <sys/kmem.h> 51 #include <sys/lwp.h> 52 #include <sys/module.h> 53 #include <sys/pserialize.h> 54 #include <sys/socketvar.h> 55 #include <sys/uio.h> 56 57 #include <netinet/in.h> 58 #include <netinet6/in6_var.h> 59 #endif 60 61 #include "npf_impl.h" 62 #include "npfkern.h" 63 64 #ifdef _KERNEL 65 #ifndef _MODULE 66 #include "opt_modular.h" 67 #include "opt_net_mpsafe.h" 68 #endif 69 #include "ioconf.h" 70 #endif 71 72 /* 73 * Module and device structures. 74 */ 75 #ifndef _MODULE 76 /* 77 * Modular kernels load drivers too early, and we need percpu to be inited 78 * So we make this misc; a better way would be to have early boot and late 79 * boot drivers. 80 */ 81 MODULE(MODULE_CLASS_MISC, npf, "bpf"); 82 #else 83 /* This module autoloads via /dev/npf so it needs to be a driver */ 84 MODULE(MODULE_CLASS_DRIVER, npf, "bpf"); 85 #endif 86 87 static int npf_pfil_register(bool); 88 static void npf_pfil_unregister(bool); 89 90 static int npf_dev_open(dev_t, int, int, lwp_t *); 91 static int npf_dev_close(dev_t, int, int, lwp_t *); 92 static int npf_dev_ioctl(dev_t, u_long, void *, int, lwp_t *); 93 static int npf_dev_poll(dev_t, int, lwp_t *); 94 static int npf_dev_read(dev_t, struct uio *, int); 95 96 const struct cdevsw npf_cdevsw = { 97 .d_open = npf_dev_open, 98 .d_close = npf_dev_close, 99 .d_read = npf_dev_read, 100 .d_write = nowrite, 101 .d_ioctl = npf_dev_ioctl, 102 .d_stop = nostop, 103 .d_tty = notty, 104 .d_poll = npf_dev_poll, 105 .d_mmap = nommap, 106 .d_kqfilter = nokqfilter, 107 .d_discard = nodiscard, 108 .d_flag = D_OTHER | D_MPSAFE 109 }; 110 111 static const char * npf_ifop_getname(ifnet_t *); 112 static ifnet_t * npf_ifop_lookup(const char *); 113 static void npf_ifop_flush(void *); 114 static void * npf_ifop_getmeta(const ifnet_t *); 115 static void npf_ifop_setmeta(ifnet_t *, void *); 116 117 static const unsigned nworkers = 1; 118 119 static bool pfil_registered = false; 120 static pfil_head_t * npf_ph_if = NULL; 121 static pfil_head_t * npf_ph_inet = NULL; 122 static pfil_head_t * npf_ph_inet6 = NULL; 123 124 static const npf_ifops_t kern_ifops = { 125 .getname = npf_ifop_getname, 126 .lookup = npf_ifop_lookup, 127 .flush = npf_ifop_flush, 128 .getmeta = npf_ifop_getmeta, 129 .setmeta = npf_ifop_setmeta, 130 }; 131 132 static int 133 npf_fini(void) 134 { 135 npf_t *npf = npf_getkernctx(); 136 137 /* At first, detach device and remove pfil hooks. */ 138 #ifdef _MODULE 139 devsw_detach(NULL, &npf_cdevsw); 140 #endif 141 npf_pfil_unregister(true); 142 npfk_destroy(npf); 143 npfk_sysfini(); 144 return 0; 145 } 146 147 static int 148 npf_init(void) 149 { 150 npf_t *npf; 151 int error = 0; 152 153 error = npfk_sysinit(nworkers); 154 if (error) 155 return error; 156 npf = npfk_create(0, NULL, &kern_ifops); 157 npf_setkernctx(npf); 158 npf_pfil_register(true); 159 160 #ifdef _MODULE 161 devmajor_t bmajor = NODEVMAJOR, cmajor = NODEVMAJOR; 162 163 /* Attach /dev/npf device. */ 164 error = devsw_attach("npf", NULL, &bmajor, &npf_cdevsw, &cmajor); 165 if (error) { 166 /* It will call devsw_detach(), which is safe. */ 167 (void)npf_fini(); 168 } 169 #endif 170 return error; 171 } 172 173 174 /* 175 * Module interface. 176 */ 177 static int 178 npf_modcmd(modcmd_t cmd, void *arg) 179 { 180 switch (cmd) { 181 case MODULE_CMD_INIT: 182 return npf_init(); 183 case MODULE_CMD_FINI: 184 return npf_fini(); 185 case MODULE_CMD_AUTOUNLOAD: 186 if (npf_autounload_p()) { 187 return EBUSY; 188 } 189 break; 190 default: 191 return ENOTTY; 192 } 193 return 0; 194 } 195 196 void 197 npfattach(int nunits) 198 { 199 /* Nothing */ 200 } 201 202 static int 203 npf_dev_open(dev_t dev, int flag, int mode, lwp_t *l) 204 { 205 /* Available only for super-user. */ 206 if (kauth_authorize_network(l->l_cred, KAUTH_NETWORK_FIREWALL, 207 KAUTH_REQ_NETWORK_FIREWALL_FW, NULL, NULL, NULL)) { 208 return EPERM; 209 } 210 return 0; 211 } 212 213 static int 214 npf_dev_close(dev_t dev, int flag, int mode, lwp_t *l) 215 { 216 return 0; 217 } 218 219 static int 220 npf_stats_export(npf_t *npf, void *data) 221 { 222 uint64_t *fullst, *uptr = *(uint64_t **)data; 223 int error; 224 225 fullst = kmem_alloc(NPF_STATS_SIZE, KM_SLEEP); 226 npfk_stats(npf, fullst); /* will zero the buffer */ 227 error = copyout(fullst, uptr, NPF_STATS_SIZE); 228 kmem_free(fullst, NPF_STATS_SIZE); 229 return error; 230 } 231 232 /* 233 * npfctl_switch: enable or disable packet inspection. 234 */ 235 static int 236 npfctl_switch(void *data) 237 { 238 const bool onoff = *(int *)data ? true : false; 239 int error; 240 241 if (onoff) { 242 /* Enable: add pfil hooks. */ 243 error = npf_pfil_register(false); 244 } else { 245 /* Disable: remove pfil hooks. */ 246 npf_pfil_unregister(false); 247 error = 0; 248 } 249 return error; 250 } 251 252 static int 253 npf_dev_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 254 { 255 npf_t *npf = npf_getkernctx(); 256 int error; 257 258 /* Available only for super-user. */ 259 if (kauth_authorize_network(l->l_cred, KAUTH_NETWORK_FIREWALL, 260 KAUTH_REQ_NETWORK_FIREWALL_FW, NULL, NULL, NULL)) { 261 return EPERM; 262 } 263 264 switch (cmd) { 265 case IOC_NPF_TABLE: 266 error = npfctl_table(npf, data); 267 break; 268 case IOC_NPF_RULE: 269 error = npfctl_rule(npf, cmd, data); 270 break; 271 case IOC_NPF_STATS: 272 error = npf_stats_export(npf, data); 273 break; 274 case IOC_NPF_SAVE: 275 error = npfctl_save(npf, cmd, data); 276 break; 277 case IOC_NPF_SWITCH: 278 error = npfctl_switch(data); 279 break; 280 case IOC_NPF_LOAD: 281 error = npfctl_load(npf, cmd, data); 282 break; 283 case IOC_NPF_CONN_LOOKUP: 284 error = npfctl_conn_lookup(npf, cmd, data); 285 break; 286 case IOC_NPF_TABLE_REPLACE: 287 error = npfctl_table_replace(npf, cmd, data); 288 break; 289 case IOC_NPF_VERSION: 290 *(int *)data = NPF_VERSION; 291 error = 0; 292 break; 293 default: 294 error = ENOTTY; 295 break; 296 } 297 return error; 298 } 299 300 static int 301 npf_dev_poll(dev_t dev, int events, lwp_t *l) 302 { 303 return ENOTSUP; 304 } 305 306 static int 307 npf_dev_read(dev_t dev, struct uio *uio, int flag) 308 { 309 return ENOTSUP; 310 } 311 312 bool 313 npf_autounload_p(void) 314 { 315 npf_t *npf = npf_getkernctx(); 316 return !npf_active_p() && npf_default_pass(npf); 317 } 318 319 /* 320 * Interface operations. 321 */ 322 323 static const char * 324 npf_ifop_getname(ifnet_t *ifp) 325 { 326 return ifp->if_xname; 327 } 328 329 static ifnet_t * 330 npf_ifop_lookup(const char *name) 331 { 332 return ifunit(name); 333 } 334 335 static void 336 npf_ifop_flush(void *arg) 337 { 338 ifnet_t *ifp; 339 340 KERNEL_LOCK(1, NULL); 341 IFNET_GLOBAL_LOCK(); 342 IFNET_WRITER_FOREACH(ifp) { 343 ifp->if_npf_private = arg; 344 } 345 IFNET_GLOBAL_UNLOCK(); 346 KERNEL_UNLOCK_ONE(NULL); 347 } 348 349 static void * 350 npf_ifop_getmeta(const ifnet_t *ifp) 351 { 352 return ifp->if_npf_private; 353 } 354 355 static void 356 npf_ifop_setmeta(ifnet_t *ifp, void *arg) 357 { 358 ifp->if_npf_private = arg; 359 } 360 361 #ifdef _KERNEL 362 363 /* 364 * Wrapper of the main packet handler to pass the kernel NPF context. 365 */ 366 static int 367 npfos_packet_handler(void *arg, struct mbuf **mp, ifnet_t *ifp, int di) 368 { 369 npf_t *npf = npf_getkernctx(); 370 return npfk_packet_handler(npf, mp, ifp, di); 371 } 372 373 /* 374 * npf_ifhook: hook handling interface changes. 375 */ 376 static void 377 npf_ifhook(void *arg, unsigned long cmd, void *arg2) 378 { 379 npf_t *npf = npf_getkernctx(); 380 ifnet_t *ifp = arg2; 381 382 switch (cmd) { 383 case PFIL_IFNET_ATTACH: 384 npfk_ifmap_attach(npf, ifp); 385 npf_ifaddr_sync(npf, ifp); 386 break; 387 case PFIL_IFNET_DETACH: 388 npfk_ifmap_detach(npf, ifp); 389 npf_ifaddr_flush(npf, ifp); 390 break; 391 } 392 } 393 394 static void 395 npf_ifaddrhook(void *arg, u_long cmd, void *arg2) 396 { 397 npf_t *npf = npf_getkernctx(); 398 struct ifaddr *ifa = arg2; 399 400 switch (cmd) { 401 case SIOCSIFADDR: 402 case SIOCAIFADDR: 403 case SIOCDIFADDR: 404 #ifdef INET6 405 case SIOCSIFADDR_IN6: 406 case SIOCAIFADDR_IN6: 407 case SIOCDIFADDR_IN6: 408 #endif 409 KASSERT(ifa != NULL); 410 break; 411 default: 412 return; 413 } 414 npf_ifaddr_sync(npf, ifa->ifa_ifp); 415 } 416 417 /* 418 * npf_pfil_register: register pfil(9) hooks. 419 */ 420 static int 421 npf_pfil_register(bool init) 422 { 423 npf_t *npf = npf_getkernctx(); 424 int error = 0; 425 426 SOFTNET_KERNEL_LOCK_UNLESS_NET_MPSAFE(); 427 428 /* Init: interface re-config and attach/detach hook. */ 429 if (!npf_ph_if) { 430 npf_ph_if = pfil_head_get(PFIL_TYPE_IFNET, 0); 431 if (!npf_ph_if) { 432 error = ENOENT; 433 goto out; 434 } 435 436 error = pfil_add_ihook(npf_ifhook, NULL, 437 PFIL_IFNET, npf_ph_if); 438 KASSERT(error == 0); 439 440 error = pfil_add_ihook(npf_ifaddrhook, NULL, 441 PFIL_IFADDR, npf_ph_if); 442 KASSERT(error == 0); 443 } 444 if (init) { 445 goto out; 446 } 447 448 /* Check if pfil hooks are not already registered. */ 449 if (pfil_registered) { 450 error = EEXIST; 451 goto out; 452 } 453 454 /* Capture points of the activity in the IP layer. */ 455 npf_ph_inet = pfil_head_get(PFIL_TYPE_AF, (void *)AF_INET); 456 npf_ph_inet6 = pfil_head_get(PFIL_TYPE_AF, (void *)AF_INET6); 457 if (!npf_ph_inet && !npf_ph_inet6) { 458 error = ENOENT; 459 goto out; 460 } 461 462 /* Packet IN/OUT handlers for IP layer. */ 463 if (npf_ph_inet) { 464 error = pfil_add_hook(npfos_packet_handler, npf, 465 PFIL_ALL, npf_ph_inet); 466 KASSERT(error == 0); 467 } 468 if (npf_ph_inet6) { 469 error = pfil_add_hook(npfos_packet_handler, npf, 470 PFIL_ALL, npf_ph_inet6); 471 KASSERT(error == 0); 472 } 473 474 /* 475 * It is necessary to re-sync all/any interface address tables, 476 * since we did not listen for any changes. 477 */ 478 npf_ifaddr_syncall(npf); 479 pfil_registered = true; 480 out: 481 SOFTNET_KERNEL_UNLOCK_UNLESS_NET_MPSAFE(); 482 483 return error; 484 } 485 486 /* 487 * npf_pfil_unregister: unregister pfil(9) hooks. 488 */ 489 static void 490 npf_pfil_unregister(bool fini) 491 { 492 npf_t *npf = npf_getkernctx(); 493 494 SOFTNET_KERNEL_LOCK_UNLESS_NET_MPSAFE(); 495 496 if (fini && npf_ph_if) { 497 (void)pfil_remove_ihook(npf_ifhook, NULL, 498 PFIL_IFNET, npf_ph_if); 499 (void)pfil_remove_ihook(npf_ifaddrhook, NULL, 500 PFIL_IFADDR, npf_ph_if); 501 } 502 if (npf_ph_inet) { 503 (void)pfil_remove_hook(npfos_packet_handler, npf, 504 PFIL_ALL, npf_ph_inet); 505 } 506 if (npf_ph_inet6) { 507 (void)pfil_remove_hook(npfos_packet_handler, npf, 508 PFIL_ALL, npf_ph_inet6); 509 } 510 pfil_registered = false; 511 512 SOFTNET_KERNEL_UNLOCK_UNLESS_NET_MPSAFE(); 513 } 514 515 bool 516 npf_active_p(void) 517 { 518 return pfil_registered; 519 } 520 521 #endif 522 523 #ifdef __NetBSD__ 524 525 ebr_t * 526 npf_ebr_create(void) 527 { 528 return pserialize_create(); 529 } 530 531 void 532 npf_ebr_destroy(ebr_t *ebr) 533 { 534 pserialize_destroy(ebr); 535 } 536 537 void 538 npf_ebr_register(ebr_t *ebr) 539 { 540 KASSERT(ebr != NULL); (void)ebr; 541 } 542 543 void 544 npf_ebr_unregister(ebr_t *ebr) 545 { 546 KASSERT(ebr != NULL); (void)ebr; 547 } 548 549 int 550 npf_ebr_enter(ebr_t *ebr) 551 { 552 KASSERT(ebr != NULL); (void)ebr; 553 return pserialize_read_enter(); 554 } 555 556 void 557 npf_ebr_exit(ebr_t *ebr, int s) 558 { 559 KASSERT(ebr != NULL); (void)ebr; 560 pserialize_read_exit(s); 561 } 562 563 void 564 npf_ebr_full_sync(ebr_t *ebr) 565 { 566 pserialize_perform(ebr); 567 } 568 569 bool 570 npf_ebr_incrit_p(ebr_t *ebr) 571 { 572 KASSERT(ebr != NULL); (void)ebr; 573 return pserialize_in_read_section(); 574 } 575 576 #endif 577