1 /* $NetBSD: npf.c,v 1.18 2013/11/08 00:38:26 rmind Exp $ */ 2 3 /*- 4 * Copyright (c) 2009-2013 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.18 2013/11/08 00:38:26 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/sysctl.h> 52 #include <sys/uio.h> 53 54 #include "npf_impl.h" 55 56 /* 57 * Module and device structures. 58 */ 59 MODULE(MODULE_CLASS_DRIVER, npf, NULL); 60 61 void npfattach(int); 62 63 static int npf_fini(void); 64 static int npf_dev_open(dev_t, int, int, lwp_t *); 65 static int npf_dev_close(dev_t, int, int, lwp_t *); 66 static int npf_dev_ioctl(dev_t, u_long, void *, int, lwp_t *); 67 static int npf_dev_poll(dev_t, int, lwp_t *); 68 static int npf_dev_read(dev_t, struct uio *, int); 69 70 static int npfctl_stats(void *); 71 72 static percpu_t * npf_stats_percpu __read_mostly; 73 static struct sysctllog * npf_sysctl __read_mostly; 74 75 const struct cdevsw npf_cdevsw = { 76 npf_dev_open, npf_dev_close, npf_dev_read, nowrite, npf_dev_ioctl, 77 nostop, notty, npf_dev_poll, nommap, nokqfilter, D_OTHER | D_MPSAFE 78 }; 79 80 static int 81 npf_init(void) 82 { 83 #ifdef _MODULE 84 devmajor_t bmajor = NODEVMAJOR, cmajor = NODEVMAJOR; 85 #endif 86 int error = 0; 87 88 npf_stats_percpu = percpu_alloc(NPF_STATS_SIZE); 89 npf_sysctl = NULL; 90 91 npf_bpf_sysinit(); 92 npf_worker_sysinit(); 93 npf_tableset_sysinit(); 94 npf_session_sysinit(); 95 npf_nat_sysinit(); 96 npf_alg_sysinit(); 97 npf_ext_sysinit(); 98 99 /* Load empty configuration. */ 100 npf_pfil_register(true); 101 npf_config_init(); 102 103 #ifdef _MODULE 104 /* Attach /dev/npf device. */ 105 error = devsw_attach("npf", NULL, &bmajor, &npf_cdevsw, &cmajor); 106 if (error) { 107 /* It will call devsw_detach(), which is safe. */ 108 (void)npf_fini(); 109 } 110 #endif 111 return error; 112 } 113 114 static int 115 npf_fini(void) 116 { 117 /* At first, detach device and remove pfil hooks. */ 118 #ifdef _MODULE 119 devsw_detach(NULL, &npf_cdevsw); 120 #endif 121 npf_pfil_unregister(true); 122 123 /* Flush all sessions, destroy configuration (ruleset, etc). */ 124 npf_session_tracking(false); 125 npf_config_fini(); 126 127 /* Finally, safe to destroy the subsystems. */ 128 npf_ext_sysfini(); 129 npf_alg_sysfini(); 130 npf_nat_sysfini(); 131 npf_session_sysfini(); 132 npf_tableset_sysfini(); 133 npf_bpf_sysfini(); 134 135 /* Note: worker is the last. */ 136 npf_worker_sysfini(); 137 138 if (npf_sysctl) { 139 sysctl_teardown(&npf_sysctl); 140 } 141 percpu_free(npf_stats_percpu, NPF_STATS_SIZE); 142 143 return 0; 144 } 145 146 /* 147 * Module interface. 148 */ 149 static int 150 npf_modcmd(modcmd_t cmd, void *arg) 151 { 152 153 switch (cmd) { 154 case MODULE_CMD_INIT: 155 return npf_init(); 156 case MODULE_CMD_FINI: 157 return npf_fini(); 158 case MODULE_CMD_AUTOUNLOAD: 159 if (npf_autounload_p()) { 160 return EBUSY; 161 } 162 break; 163 default: 164 return ENOTTY; 165 } 166 return 0; 167 } 168 169 void 170 npfattach(int nunits) 171 { 172 173 /* Void. */ 174 } 175 176 static int 177 npf_dev_open(dev_t dev, int flag, int mode, lwp_t *l) 178 { 179 180 /* Available only for super-user. */ 181 if (kauth_authorize_network(l->l_cred, KAUTH_NETWORK_FIREWALL, 182 KAUTH_REQ_NETWORK_FIREWALL_FW, NULL, NULL, NULL)) { 183 return EPERM; 184 } 185 return 0; 186 } 187 188 static int 189 npf_dev_close(dev_t dev, int flag, int mode, lwp_t *l) 190 { 191 192 return 0; 193 } 194 195 static int 196 npf_dev_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 197 { 198 int error; 199 200 /* Available only for super-user. */ 201 if (kauth_authorize_network(l->l_cred, KAUTH_NETWORK_FIREWALL, 202 KAUTH_REQ_NETWORK_FIREWALL_FW, NULL, NULL, NULL)) { 203 return EPERM; 204 } 205 206 switch (cmd) { 207 case IOC_NPF_TABLE: 208 error = npfctl_table(data); 209 break; 210 case IOC_NPF_RULE: 211 error = npfctl_rule(cmd, data); 212 break; 213 case IOC_NPF_GETCONF: 214 error = npfctl_getconf(cmd, data); 215 break; 216 case IOC_NPF_STATS: 217 error = npfctl_stats(data); 218 break; 219 case IOC_NPF_SESSIONS_SAVE: 220 error = npfctl_sessions_save(cmd, data); 221 break; 222 case IOC_NPF_SESSIONS_LOAD: 223 error = npfctl_sessions_load(cmd, data); 224 break; 225 case IOC_NPF_SWITCH: 226 error = npfctl_switch(data); 227 break; 228 case IOC_NPF_RELOAD: 229 error = npfctl_reload(cmd, data); 230 break; 231 case IOC_NPF_VERSION: 232 *(int *)data = NPF_VERSION; 233 error = 0; 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 bool 257 npf_autounload_p(void) 258 { 259 return !npf_pfil_registered_p() && npf_default_pass(); 260 } 261 262 /* 263 * NPF statistics interface. 264 */ 265 266 void 267 npf_stats_inc(npf_stats_t st) 268 { 269 uint64_t *stats = percpu_getref(npf_stats_percpu); 270 stats[st]++; 271 percpu_putref(npf_stats_percpu); 272 } 273 274 void 275 npf_stats_dec(npf_stats_t st) 276 { 277 uint64_t *stats = percpu_getref(npf_stats_percpu); 278 stats[st]--; 279 percpu_putref(npf_stats_percpu); 280 } 281 282 static void 283 npf_stats_collect(void *mem, void *arg, struct cpu_info *ci) 284 { 285 uint64_t *percpu_stats = mem, *full_stats = arg; 286 int i; 287 288 for (i = 0; i < NPF_STATS_COUNT; i++) { 289 full_stats[i] += percpu_stats[i]; 290 } 291 } 292 293 /* 294 * npfctl_stats: export collected statistics. 295 */ 296 static int 297 npfctl_stats(void *data) 298 { 299 uint64_t *fullst, *uptr = *(uint64_t **)data; 300 int error; 301 302 fullst = kmem_zalloc(NPF_STATS_SIZE, KM_SLEEP); 303 percpu_foreach(npf_stats_percpu, npf_stats_collect, fullst); 304 error = copyout(fullst, uptr, NPF_STATS_SIZE); 305 kmem_free(fullst, NPF_STATS_SIZE); 306 return error; 307 } 308