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