1 /* $NetBSD: npf_os.c,v 1.5 2017/01/03 00:58:05 rmind Exp $ */ 2 3 /*- 4 * Copyright (c) 2009-2016 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 main: dynamic load/initialisation and unload routines. 34 */ 35 36 #ifdef _KERNEL 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: npf_os.c,v 1.5 2017/01/03 00:58:05 rmind Exp $"); 39 40 #ifdef _KERNEL_OPT 41 #include "pf.h" 42 #if NPF > 0 43 #error "NPF and PF are mutually exclusive; please select one" 44 #endif 45 #endif 46 47 #include <sys/param.h> 48 #include <sys/types.h> 49 50 #include <sys/conf.h> 51 #include <sys/kauth.h> 52 #include <sys/kmem.h> 53 #include <sys/lwp.h> 54 #include <sys/module.h> 55 #include <sys/socketvar.h> 56 #include <sys/uio.h> 57 58 #include <netinet/in.h> 59 #include <netinet6/in6_var.h> 60 #endif 61 62 #include "npf_impl.h" 63 #include "npfkern.h" 64 65 #ifdef _KERNEL 66 #ifndef _MODULE 67 #include "opt_modular.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, NULL); 82 #else 83 /* This module autoloads via /dev/npf so it needs to be a driver */ 84 MODULE(MODULE_CLASS_DRIVER, npf, NULL); 85 #endif 86 87 static int npf_dev_open(dev_t, int, int, lwp_t *); 88 static int npf_dev_close(dev_t, int, int, lwp_t *); 89 static int npf_dev_ioctl(dev_t, u_long, void *, int, lwp_t *); 90 static int npf_dev_poll(dev_t, int, lwp_t *); 91 static int npf_dev_read(dev_t, struct uio *, int); 92 93 const struct cdevsw npf_cdevsw = { 94 .d_open = npf_dev_open, 95 .d_close = npf_dev_close, 96 .d_read = npf_dev_read, 97 .d_write = nowrite, 98 .d_ioctl = npf_dev_ioctl, 99 .d_stop = nostop, 100 .d_tty = notty, 101 .d_poll = npf_dev_poll, 102 .d_mmap = nommap, 103 .d_kqfilter = nokqfilter, 104 .d_discard = nodiscard, 105 .d_flag = D_OTHER | D_MPSAFE 106 }; 107 108 static const char * npf_ifop_getname(ifnet_t *); 109 static ifnet_t * npf_ifop_lookup(const char *); 110 static void npf_ifop_flush(void *); 111 static void * npf_ifop_getmeta(const ifnet_t *); 112 static void npf_ifop_setmeta(ifnet_t *, void *); 113 114 static const unsigned nworkers = 1; 115 116 static bool pfil_registered = false; 117 static pfil_head_t * npf_ph_if = NULL; 118 static pfil_head_t * npf_ph_inet = NULL; 119 static pfil_head_t * npf_ph_inet6 = NULL; 120 121 static const npf_ifops_t kern_ifops = { 122 .getname = npf_ifop_getname, 123 .lookup = npf_ifop_lookup, 124 .flush = npf_ifop_flush, 125 .getmeta = npf_ifop_getmeta, 126 .setmeta = npf_ifop_setmeta, 127 }; 128 129 static int 130 npf_fini(void) 131 { 132 npf_t *npf = npf_getkernctx(); 133 134 /* At first, detach device and remove pfil hooks. */ 135 #ifdef _MODULE 136 devsw_detach(NULL, &npf_cdevsw); 137 #endif 138 npf_pfil_unregister(true); 139 npf_destroy(npf); 140 npf_sysfini(); 141 return 0; 142 } 143 144 static int 145 npf_init(void) 146 { 147 npf_t *npf; 148 int error = 0; 149 150 error = npf_sysinit(nworkers); 151 if (error) 152 return error; 153 npf = npf_create(0, NULL, &kern_ifops); 154 npf_setkernctx(npf); 155 npf_pfil_register(true); 156 157 #ifdef _MODULE 158 devmajor_t bmajor = NODEVMAJOR, cmajor = NODEVMAJOR; 159 160 /* Attach /dev/npf device. */ 161 error = devsw_attach("npf", NULL, &bmajor, &npf_cdevsw, &cmajor); 162 if (error) { 163 /* It will call devsw_detach(), which is safe. */ 164 (void)npf_fini(); 165 } 166 #endif 167 return error; 168 } 169 170 171 /* 172 * Module interface. 173 */ 174 static int 175 npf_modcmd(modcmd_t cmd, void *arg) 176 { 177 switch (cmd) { 178 case MODULE_CMD_INIT: 179 return npf_init(); 180 case MODULE_CMD_FINI: 181 return npf_fini(); 182 case MODULE_CMD_AUTOUNLOAD: 183 if (npf_autounload_p()) { 184 return EBUSY; 185 } 186 break; 187 default: 188 return ENOTTY; 189 } 190 return 0; 191 } 192 193 void 194 npfattach(int nunits) 195 { 196 /* Nothing */ 197 } 198 199 static int 200 npf_dev_open(dev_t dev, int flag, int mode, lwp_t *l) 201 { 202 /* Available only for super-user. */ 203 if (kauth_authorize_network(l->l_cred, KAUTH_NETWORK_FIREWALL, 204 KAUTH_REQ_NETWORK_FIREWALL_FW, NULL, NULL, NULL)) { 205 return EPERM; 206 } 207 return 0; 208 } 209 210 static int 211 npf_dev_close(dev_t dev, int flag, int mode, lwp_t *l) 212 { 213 return 0; 214 } 215 216 static int 217 npf_stats_export(npf_t *npf, void *data) 218 { 219 uint64_t *fullst, *uptr = *(uint64_t **)data; 220 int error; 221 222 fullst = kmem_alloc(NPF_STATS_SIZE, KM_SLEEP); 223 npf_stats(npf, fullst); /* will zero the buffer */ 224 error = copyout(fullst, uptr, NPF_STATS_SIZE); 225 kmem_free(fullst, NPF_STATS_SIZE); 226 return error; 227 } 228 229 static int 230 npf_dev_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 231 { 232 npf_t *npf = npf_getkernctx(); 233 int error; 234 235 /* Available only for super-user. */ 236 if (kauth_authorize_network(l->l_cred, KAUTH_NETWORK_FIREWALL, 237 KAUTH_REQ_NETWORK_FIREWALL_FW, NULL, NULL, NULL)) { 238 return EPERM; 239 } 240 241 switch (cmd) { 242 case IOC_NPF_TABLE: 243 error = npfctl_table(npf, data); 244 break; 245 case IOC_NPF_RULE: 246 error = npfctl_rule(npf, cmd, data); 247 break; 248 case IOC_NPF_STATS: 249 error = npf_stats_export(npf, data); 250 break; 251 case IOC_NPF_SAVE: 252 error = npfctl_save(npf, cmd, data); 253 break; 254 case IOC_NPF_SWITCH: 255 error = npfctl_switch(data); 256 break; 257 case IOC_NPF_LOAD: 258 error = npfctl_load(npf, cmd, data); 259 break; 260 case IOC_NPF_CONN_LOOKUP: 261 error = npfctl_conn_lookup(npf, cmd, data); 262 break; 263 case IOC_NPF_VERSION: 264 *(int *)data = NPF_VERSION; 265 error = 0; 266 break; 267 default: 268 error = ENOTTY; 269 break; 270 } 271 return error; 272 } 273 274 static int 275 npf_dev_poll(dev_t dev, int events, lwp_t *l) 276 { 277 return ENOTSUP; 278 } 279 280 static int 281 npf_dev_read(dev_t dev, struct uio *uio, int flag) 282 { 283 return ENOTSUP; 284 } 285 286 bool 287 npf_autounload_p(void) 288 { 289 npf_t *npf = npf_getkernctx(); 290 return !npf_pfil_registered_p() && npf_default_pass(npf); 291 } 292 293 /* 294 * Interface operations. 295 */ 296 297 static const char * 298 npf_ifop_getname(ifnet_t *ifp) 299 { 300 return ifp->if_xname; 301 } 302 303 static ifnet_t * 304 npf_ifop_lookup(const char *name) 305 { 306 return ifunit(name); 307 } 308 309 static void 310 npf_ifop_flush(void *arg) 311 { 312 ifnet_t *ifp; 313 314 KERNEL_LOCK(1, NULL); 315 IFNET_LOCK(); 316 IFNET_WRITER_FOREACH(ifp) { 317 ifp->if_pf_kif = arg; 318 } 319 IFNET_UNLOCK(); 320 KERNEL_UNLOCK_ONE(NULL); 321 } 322 323 static void * 324 npf_ifop_getmeta(const ifnet_t *ifp) 325 { 326 return ifp->if_pf_kif; 327 } 328 329 static void 330 npf_ifop_setmeta(ifnet_t *ifp, void *arg) 331 { 332 ifp->if_pf_kif = arg; 333 } 334 335 #ifdef _KERNEL 336 337 /* 338 * Wrapper of the main packet handler to pass the kernel NPF context. 339 */ 340 static int 341 npfkern_packet_handler(void *arg, struct mbuf **mp, ifnet_t *ifp, int di) 342 { 343 npf_t *npf = npf_getkernctx(); 344 return npf_packet_handler(npf, mp, ifp, di); 345 } 346 347 /* 348 * npf_ifhook: hook handling interface changes. 349 */ 350 static void 351 npf_ifhook(void *arg, unsigned long cmd, void *arg2) 352 { 353 npf_t *npf = npf_getkernctx(); 354 ifnet_t *ifp = arg2; 355 356 switch (cmd) { 357 case PFIL_IFNET_ATTACH: 358 npf_ifmap_attach(npf, ifp); 359 npf_ifaddr_sync(npf, ifp); 360 break; 361 case PFIL_IFNET_DETACH: 362 npf_ifmap_detach(npf, ifp); 363 npf_ifaddr_flush(npf, ifp); 364 break; 365 } 366 } 367 368 static void 369 npf_ifaddrhook(void *arg, u_long cmd, void *arg2) 370 { 371 npf_t *npf = npf_getkernctx(); 372 struct ifaddr *ifa = arg2; 373 374 switch (cmd) { 375 case SIOCSIFADDR: 376 case SIOCAIFADDR: 377 case SIOCDIFADDR: 378 #ifdef INET6 379 case SIOCSIFADDR_IN6: 380 case SIOCAIFADDR_IN6: 381 case SIOCDIFADDR_IN6: 382 #endif 383 break; 384 default: 385 return; 386 } 387 npf_ifaddr_sync(npf, ifa->ifa_ifp); 388 } 389 390 /* 391 * npf_pfil_register: register pfil(9) hooks. 392 */ 393 int 394 npf_pfil_register(bool init) 395 { 396 npf_t *npf = npf_getkernctx(); 397 int error = 0; 398 399 mutex_enter(softnet_lock); 400 KERNEL_LOCK(1, NULL); 401 402 /* Init: interface re-config and attach/detach hook. */ 403 if (!npf_ph_if) { 404 npf_ph_if = pfil_head_get(PFIL_TYPE_IFNET, 0); 405 if (!npf_ph_if) { 406 error = ENOENT; 407 goto out; 408 } 409 410 error = pfil_add_ihook(npf_ifhook, NULL, 411 PFIL_IFNET, npf_ph_if); 412 KASSERT(error == 0); 413 414 error = pfil_add_ihook(npf_ifaddrhook, NULL, 415 PFIL_IFADDR, npf_ph_if); 416 KASSERT(error == 0); 417 } 418 if (init) { 419 goto out; 420 } 421 422 /* Check if pfil hooks are not already registered. */ 423 if (pfil_registered) { 424 error = EEXIST; 425 goto out; 426 } 427 428 /* Capture points of the activity in the IP layer. */ 429 npf_ph_inet = pfil_head_get(PFIL_TYPE_AF, (void *)AF_INET); 430 npf_ph_inet6 = pfil_head_get(PFIL_TYPE_AF, (void *)AF_INET6); 431 if (!npf_ph_inet && !npf_ph_inet6) { 432 error = ENOENT; 433 goto out; 434 } 435 436 /* Packet IN/OUT handlers for IP layer. */ 437 if (npf_ph_inet) { 438 error = pfil_add_hook(npfkern_packet_handler, npf, 439 PFIL_ALL, npf_ph_inet); 440 KASSERT(error == 0); 441 } 442 if (npf_ph_inet6) { 443 error = pfil_add_hook(npfkern_packet_handler, npf, 444 PFIL_ALL, npf_ph_inet6); 445 KASSERT(error == 0); 446 } 447 448 /* 449 * It is necessary to re-sync all/any interface address tables, 450 * since we did not listen for any changes. 451 */ 452 npf_ifaddr_syncall(npf); 453 pfil_registered = true; 454 out: 455 KERNEL_UNLOCK_ONE(NULL); 456 mutex_exit(softnet_lock); 457 458 return error; 459 } 460 461 /* 462 * npf_pfil_unregister: unregister pfil(9) hooks. 463 */ 464 void 465 npf_pfil_unregister(bool fini) 466 { 467 npf_t *npf = npf_getkernctx(); 468 469 mutex_enter(softnet_lock); 470 KERNEL_LOCK(1, NULL); 471 472 if (fini && npf_ph_if) { 473 (void)pfil_remove_ihook(npf_ifhook, NULL, 474 PFIL_IFNET, npf_ph_if); 475 (void)pfil_remove_ihook(npf_ifaddrhook, NULL, 476 PFIL_IFADDR, npf_ph_if); 477 } 478 if (npf_ph_inet) { 479 (void)pfil_remove_hook(npfkern_packet_handler, npf, 480 PFIL_ALL, npf_ph_inet); 481 } 482 if (npf_ph_inet6) { 483 (void)pfil_remove_hook(npfkern_packet_handler, npf, 484 PFIL_ALL, npf_ph_inet6); 485 } 486 pfil_registered = false; 487 488 KERNEL_UNLOCK_ONE(NULL); 489 mutex_exit(softnet_lock); 490 } 491 492 bool 493 npf_pfil_registered_p(void) 494 { 495 return pfil_registered; 496 } 497 #endif 498