1 /* $NetBSD: npf.c,v 1.8 2012/02/20 00:18:19 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 main: dynamic load/initialisation and unload routines. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.8 2012/02/20 00:18:19 rmind Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/types.h> 41 42 #include <sys/atomic.h> 43 #include <sys/conf.h> 44 #include <sys/kauth.h> 45 #include <sys/kmem.h> 46 #include <sys/lwp.h> 47 #include <sys/module.h> 48 #include <sys/percpu.h> 49 #include <sys/rwlock.h> 50 #include <sys/socketvar.h> 51 #include <sys/uio.h> 52 53 #include "npf_impl.h" 54 55 /* 56 * Module and device structures. 57 */ 58 MODULE(MODULE_CLASS_DRIVER, npf, NULL); 59 60 void npfattach(int); 61 62 static int npf_fini(void); 63 static int npf_dev_open(dev_t, int, int, lwp_t *); 64 static int npf_dev_close(dev_t, int, int, lwp_t *); 65 static int npf_dev_ioctl(dev_t, u_long, void *, int, lwp_t *); 66 static int npf_dev_poll(dev_t, int, lwp_t *); 67 static int npf_dev_read(dev_t, struct uio *, int); 68 69 typedef struct { 70 npf_ruleset_t * n_rules; 71 npf_tableset_t * n_tables; 72 npf_ruleset_t * n_nat_rules; 73 bool n_default_pass; 74 } npf_core_t; 75 76 static void npf_core_destroy(npf_core_t *); 77 static int npfctl_stats(void *); 78 79 static krwlock_t npf_lock __cacheline_aligned; 80 static npf_core_t * npf_core __cacheline_aligned; 81 static percpu_t * npf_stats_percpu __read_mostly; 82 83 const struct cdevsw npf_cdevsw = { 84 npf_dev_open, npf_dev_close, npf_dev_read, nowrite, npf_dev_ioctl, 85 nostop, notty, npf_dev_poll, nommap, nokqfilter, D_OTHER | D_MPSAFE 86 }; 87 88 static int 89 npf_init(void) 90 { 91 #ifdef _MODULE 92 devmajor_t bmajor = NODEVMAJOR, cmajor = NODEVMAJOR; 93 #endif 94 npf_ruleset_t *rset, *nset; 95 npf_tableset_t *tset; 96 int error = 0; 97 98 rw_init(&npf_lock); 99 npf_stats_percpu = percpu_alloc(NPF_STATS_SIZE); 100 npf_tableset_sysinit(); 101 npf_session_sysinit(); 102 npf_nat_sysinit(); 103 npf_alg_sysinit(); 104 npflogattach(1); 105 106 /* Load empty configuration. */ 107 rset = npf_ruleset_create(); 108 tset = npf_tableset_create(); 109 nset = npf_ruleset_create(); 110 npf_reload(rset, tset, nset, true); 111 KASSERT(npf_core != NULL); 112 113 #ifdef _MODULE 114 /* Attach /dev/npf device. */ 115 error = devsw_attach("npf", NULL, &bmajor, &npf_cdevsw, &cmajor); 116 if (error) { 117 /* It will call devsw_detach(), which is safe. */ 118 (void)npf_fini(); 119 } 120 #endif 121 return error; 122 } 123 124 static int 125 npf_fini(void) 126 { 127 128 /* 129 * At first, detach device, remove pfil hooks and unload existing 130 * configuration, destroy structures. 131 */ 132 #ifdef _MODULE 133 devsw_detach(NULL, &npf_cdevsw); 134 #endif 135 npf_unregister_pfil(); 136 npf_core_destroy(npf_core); 137 npflogdetach(); 138 139 /* Note: order is particular. */ 140 npf_nat_sysfini(); 141 npf_alg_sysfini(); 142 npf_session_sysfini(); 143 npf_tableset_sysfini(); 144 percpu_free(npf_stats_percpu, NPF_STATS_SIZE); 145 rw_destroy(&npf_lock); 146 147 return 0; 148 } 149 150 /* 151 * Module interface. 152 */ 153 static int 154 npf_modcmd(modcmd_t cmd, void *arg) 155 { 156 157 switch (cmd) { 158 case MODULE_CMD_INIT: 159 return npf_init(); 160 case MODULE_CMD_FINI: 161 return npf_fini(); 162 default: 163 return ENOTTY; 164 } 165 return 0; 166 } 167 168 void 169 npfattach(int nunits) 170 { 171 172 /* Void. */ 173 } 174 175 static int 176 npf_dev_open(dev_t dev, int flag, int mode, lwp_t *l) 177 { 178 179 /* Available only for super-user. */ 180 if (kauth_authorize_generic(l->l_cred, KAUTH_GENERIC_ISSUSER, NULL)) { 181 return EPERM; 182 } 183 return 0; 184 } 185 186 static int 187 npf_dev_close(dev_t dev, int flag, int mode, lwp_t *l) 188 { 189 190 return 0; 191 } 192 193 static int 194 npf_dev_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 195 { 196 int error; 197 198 /* Available only for super-user. */ 199 if (kauth_authorize_generic(l->l_cred, KAUTH_GENERIC_ISSUSER, NULL)) { 200 return EPERM; 201 } 202 203 switch (cmd) { 204 case IOC_NPF_VERSION: 205 *(int *)data = NPF_VERSION; 206 error = 0; 207 break; 208 case IOC_NPF_SWITCH: 209 error = npfctl_switch(data); 210 break; 211 case IOC_NPF_RELOAD: 212 error = npfctl_reload(cmd, data); 213 break; 214 case IOC_NPF_TABLE: 215 error = npfctl_table(data); 216 break; 217 case IOC_NPF_STATS: 218 error = npfctl_stats(data); 219 break; 220 case IOC_NPF_SESSIONS_SAVE: 221 error = npfctl_sessions_save(cmd, data); 222 break; 223 case IOC_NPF_SESSIONS_LOAD: 224 error = npfctl_sessions_load(cmd, data); 225 break; 226 case IOC_NPF_UPDATE_RULE: 227 error = npfctl_update_rule(cmd, data); 228 break; 229 default: 230 error = ENOTTY; 231 break; 232 } 233 return error; 234 } 235 236 static int 237 npf_dev_poll(dev_t dev, int events, lwp_t *l) 238 { 239 240 return ENOTSUP; 241 } 242 243 static int 244 npf_dev_read(dev_t dev, struct uio *uio, int flag) 245 { 246 247 return ENOTSUP; 248 } 249 250 /* 251 * NPF core loading/reloading/unloading mechanism. 252 */ 253 254 static void 255 npf_core_destroy(npf_core_t *nc) 256 { 257 258 npf_ruleset_destroy(nc->n_rules); 259 npf_ruleset_destroy(nc->n_nat_rules); 260 npf_tableset_destroy(nc->n_tables); 261 kmem_free(nc, sizeof(npf_core_t)); 262 } 263 264 /* 265 * npf_reload: atomically load new ruleset, tableset and NAT policies. 266 * Then destroy old (unloaded) structures. 267 */ 268 void 269 npf_reload(npf_ruleset_t *rset, npf_tableset_t *tset, npf_ruleset_t *nset, 270 bool flush) 271 { 272 npf_core_t *nc, *onc; 273 274 /* Setup a new core structure. */ 275 nc = kmem_zalloc(sizeof(npf_core_t), KM_SLEEP); 276 nc->n_default_pass = flush; 277 nc->n_rules = rset; 278 nc->n_tables = tset; 279 nc->n_nat_rules = nset; 280 281 /* Lock and load the core structure. */ 282 rw_enter(&npf_lock, RW_WRITER); 283 onc = atomic_swap_ptr(&npf_core, nc); 284 if (onc) { 285 /* Reload only necessary NAT policies. */ 286 npf_ruleset_natreload(nset, onc->n_nat_rules); 287 } 288 /* Unlock. Everything goes "live" now. */ 289 rw_exit(&npf_lock); 290 291 if (onc) { 292 /* Destroy unloaded structures. */ 293 npf_core_destroy(onc); 294 } 295 } 296 297 void 298 npf_core_enter(void) 299 { 300 rw_enter(&npf_lock, RW_READER); 301 } 302 303 npf_ruleset_t * 304 npf_core_ruleset(void) 305 { 306 KASSERT(rw_lock_held(&npf_lock)); 307 return npf_core->n_rules; 308 } 309 310 npf_ruleset_t * 311 npf_core_natset(void) 312 { 313 KASSERT(rw_lock_held(&npf_lock)); 314 return npf_core->n_nat_rules; 315 } 316 317 npf_tableset_t * 318 npf_core_tableset(void) 319 { 320 KASSERT(rw_lock_held(&npf_lock)); 321 return npf_core->n_tables; 322 } 323 324 void 325 npf_core_exit(void) 326 { 327 rw_exit(&npf_lock); 328 } 329 330 bool 331 npf_core_locked(void) 332 { 333 return rw_lock_held(&npf_lock); 334 } 335 336 bool 337 npf_default_pass(void) 338 { 339 KASSERT(rw_lock_held(&npf_lock)); 340 return npf_core->n_default_pass; 341 } 342 343 /* 344 * NPF statistics interface. 345 */ 346 347 void 348 npf_stats_inc(npf_stats_t st) 349 { 350 uint64_t *stats = percpu_getref(npf_stats_percpu); 351 stats[st]++; 352 percpu_putref(npf_stats_percpu); 353 } 354 355 void 356 npf_stats_dec(npf_stats_t st) 357 { 358 uint64_t *stats = percpu_getref(npf_stats_percpu); 359 stats[st]--; 360 percpu_putref(npf_stats_percpu); 361 } 362 363 static void 364 npf_stats_collect(void *mem, void *arg, struct cpu_info *ci) 365 { 366 uint64_t *percpu_stats = mem, *full_stats = arg; 367 int i; 368 369 for (i = 0; i < NPF_STATS_COUNT; i++) { 370 full_stats[i] += percpu_stats[i]; 371 } 372 } 373 374 /* 375 * npfctl_stats: export collected statistics. 376 */ 377 static int 378 npfctl_stats(void *data) 379 { 380 uint64_t *fullst, *uptr = *(uint64_t **)data; 381 int error; 382 383 fullst = kmem_zalloc(NPF_STATS_SIZE, KM_SLEEP); 384 percpu_foreach(npf_stats_percpu, npf_stats_collect, fullst); 385 error = copyout(fullst, uptr, NPF_STATS_SIZE); 386 kmem_free(fullst, NPF_STATS_SIZE); 387 return error; 388 } 389