1 /* $NetBSD: npf_handler.c,v 1.33 2014/07/23 01:25:34 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 packet handler. 34 * 35 * Note: pfil(9) hooks are currently locked by softnet_lock and kernel-lock. 36 */ 37 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: npf_handler.c,v 1.33 2014/07/23 01:25:34 rmind Exp $"); 40 41 #include <sys/types.h> 42 #include <sys/param.h> 43 44 #include <sys/mbuf.h> 45 #include <sys/mutex.h> 46 #include <net/if.h> 47 #include <net/pfil.h> 48 #include <sys/socketvar.h> 49 50 #include <netinet/in_systm.h> 51 #include <netinet/in.h> 52 #include <netinet/ip_var.h> 53 #include <netinet/ip6.h> 54 #include <netinet6/ip6_var.h> 55 56 #include "npf_impl.h" 57 #include "npf_conn.h" 58 59 static bool pfil_registered = false; 60 static pfil_head_t * npf_ph_if = NULL; 61 static pfil_head_t * npf_ph_inet = NULL; 62 static pfil_head_t * npf_ph_inet6 = NULL; 63 64 #ifndef INET6 65 #define ip6_reass_packet(x, y) ENOTSUP 66 #endif 67 68 /* 69 * npf_ifhook: hook handling interface changes. 70 */ 71 static int 72 npf_ifhook(void *arg, struct mbuf **mp, ifnet_t *ifp, int di) 73 { 74 u_long cmd = (u_long)mp; 75 76 if (di == PFIL_IFNET) { 77 switch (cmd) { 78 case PFIL_IFNET_ATTACH: 79 npf_ifmap_attach(ifp); 80 break; 81 case PFIL_IFNET_DETACH: 82 npf_ifmap_detach(ifp); 83 break; 84 } 85 } 86 return 0; 87 } 88 89 static int 90 npf_reassembly(npf_cache_t *npc, struct mbuf **mp) 91 { 92 nbuf_t *nbuf = npc->npc_nbuf; 93 int error = EINVAL; 94 95 /* Reset the mbuf as it may have changed. */ 96 *mp = nbuf_head_mbuf(nbuf); 97 nbuf_reset(nbuf); 98 99 if (npf_iscached(npc, NPC_IP4)) { 100 struct ip *ip = nbuf_dataptr(nbuf); 101 error = ip_reass_packet(mp, ip); 102 } else if (npf_iscached(npc, NPC_IP6)) { 103 /* 104 * Note: ip6_reass_packet() offset is the start of 105 * the fragment header. 106 */ 107 error = ip6_reass_packet(mp, npc->npc_hlen); 108 if (error && *mp == NULL) { 109 memset(nbuf, 0, sizeof(nbuf_t)); 110 } 111 } 112 if (error) { 113 npf_stats_inc(NPF_STAT_REASSFAIL); 114 return error; 115 } 116 if (*mp == NULL) { 117 /* More fragments should come. */ 118 npf_stats_inc(NPF_STAT_FRAGMENTS); 119 return 0; 120 } 121 122 /* 123 * Reassembly is complete, we have the final packet. 124 * Cache again, since layer 4 data is accessible now. 125 */ 126 nbuf_init(nbuf, *mp, nbuf->nb_ifp); 127 npc->npc_info = 0; 128 129 if (npf_cache_all(npc) & NPC_IPFRAG) { 130 return EINVAL; 131 } 132 npf_stats_inc(NPF_STAT_REASSEMBLY); 133 return 0; 134 } 135 136 /* 137 * npf_packet_handler: main packet handling routine for layer 3. 138 * 139 * Note: packet flow and inspection logic is in strict order. 140 */ 141 int 142 npf_packet_handler(void *arg, struct mbuf **mp, ifnet_t *ifp, int di) 143 { 144 nbuf_t nbuf; 145 npf_cache_t npc; 146 npf_conn_t *con; 147 npf_rule_t *rl; 148 npf_rproc_t *rp; 149 int error, retfl; 150 int decision; 151 152 /* 153 * Initialise packet information cache. 154 * Note: it is enough to clear the info bits. 155 */ 156 KASSERT(ifp != NULL); 157 nbuf_init(&nbuf, *mp, ifp); 158 npc.npc_nbuf = &nbuf; 159 npc.npc_info = 0; 160 161 decision = NPF_DECISION_BLOCK; 162 error = 0; 163 retfl = 0; 164 rp = NULL; 165 166 /* Cache everything. Determine whether it is an IP fragment. */ 167 if (__predict_false(npf_cache_all(&npc) & NPC_IPFRAG)) { 168 /* 169 * Pass to IPv4 or IPv6 reassembly mechanism. 170 */ 171 error = npf_reassembly(&npc, mp); 172 if (error) { 173 con = NULL; 174 goto out; 175 } 176 if (*mp == NULL) { 177 /* More fragments should come; return. */ 178 return 0; 179 } 180 } 181 182 /* Inspect the list of connections (if found, acquires a reference). */ 183 con = npf_conn_inspect(&npc, di, &error); 184 185 /* If "passing" connection found - skip the ruleset inspection. */ 186 if (con && npf_conn_pass(con, &rp)) { 187 npf_stats_inc(NPF_STAT_PASS_CONN); 188 KASSERT(error == 0); 189 goto pass; 190 } 191 if (__predict_false(error)) { 192 if (error == ENETUNREACH) 193 goto block; 194 goto out; 195 } 196 197 /* Acquire the lock, inspect the ruleset using this packet. */ 198 int slock = npf_config_read_enter(); 199 npf_ruleset_t *rlset = npf_config_ruleset(); 200 201 rl = npf_ruleset_inspect(&npc, rlset, di, NPF_LAYER_3); 202 if (__predict_false(rl == NULL)) { 203 const bool pass = npf_default_pass(); 204 npf_config_read_exit(slock); 205 206 if (pass) { 207 npf_stats_inc(NPF_STAT_PASS_DEFAULT); 208 goto pass; 209 } 210 npf_stats_inc(NPF_STAT_BLOCK_DEFAULT); 211 goto block; 212 } 213 214 /* 215 * Get the rule procedure (acquires a reference) for association 216 * with a connection (if any) and execution. 217 */ 218 KASSERT(rp == NULL); 219 rp = npf_rule_getrproc(rl); 220 221 /* Conclude with the rule and release the lock. */ 222 error = npf_rule_conclude(rl, &retfl); 223 npf_config_read_exit(slock); 224 225 if (error) { 226 npf_stats_inc(NPF_STAT_BLOCK_RULESET); 227 goto block; 228 } 229 npf_stats_inc(NPF_STAT_PASS_RULESET); 230 231 /* 232 * Establish a "pass" connection, if required. Just proceed if 233 * connection creation fails (e.g. due to unsupported protocol). 234 */ 235 if ((retfl & NPF_RULE_STATEFUL) != 0 && !con) { 236 con = npf_conn_establish(&npc, di, 237 (retfl & NPF_RULE_MULTIENDS) == 0); 238 if (con) { 239 /* 240 * Note: the reference on the rule procedure is 241 * transfered to the connection. It will be 242 * released on connection destruction. 243 */ 244 npf_conn_setpass(con, rp); 245 } 246 } 247 pass: 248 decision = NPF_DECISION_PASS; 249 KASSERT(error == 0); 250 /* 251 * Perform NAT. 252 */ 253 error = npf_do_nat(&npc, con, di); 254 block: 255 /* 256 * Execute the rule procedure, if any is associated. 257 * It may reverse the decision from pass to block. 258 */ 259 if (rp && !npf_rproc_run(&npc, rp, &decision)) { 260 if (con) { 261 npf_conn_release(con); 262 } 263 npf_rproc_release(rp); 264 *mp = NULL; 265 return 0; 266 } 267 out: 268 /* 269 * Release the reference on a connection. Release the reference 270 * on a rule procedure only if there was no association. 271 */ 272 if (con) { 273 npf_conn_release(con); 274 } else if (rp) { 275 npf_rproc_release(rp); 276 } 277 278 /* Reset mbuf pointer before returning to the caller. */ 279 if ((*mp = nbuf_head_mbuf(&nbuf)) == NULL) { 280 return error ? error : ENOMEM; 281 } 282 283 /* Pass the packet if decided and there is no error. */ 284 if (decision == NPF_DECISION_PASS && !error) { 285 /* 286 * XXX: Disable for now, it will be set accordingly later, 287 * for optimisations (to reduce inspection). 288 */ 289 (*mp)->m_flags &= ~M_CANFASTFWD; 290 return 0; 291 } 292 293 /* 294 * Block the packet. ENETUNREACH is used to indicate blocking. 295 * Depending on the flags and protocol, return TCP reset (RST) or 296 * ICMP destination unreachable. 297 */ 298 if (retfl && npf_return_block(&npc, retfl)) { 299 *mp = NULL; 300 } 301 302 if (!error) { 303 error = ENETUNREACH; 304 } 305 306 if (*mp) { 307 m_freem(*mp); 308 *mp = NULL; 309 } 310 return error; 311 } 312 313 /* 314 * npf_pfil_register: register pfil(9) hooks. 315 */ 316 int 317 npf_pfil_register(bool init) 318 { 319 int error = 0; 320 321 mutex_enter(softnet_lock); 322 KERNEL_LOCK(1, NULL); 323 324 /* Init: interface re-config and attach/detach hook. */ 325 if (!npf_ph_if) { 326 npf_ph_if = pfil_head_get(PFIL_TYPE_IFNET, 0); 327 if (!npf_ph_if) { 328 error = ENOENT; 329 goto out; 330 } 331 error = pfil_add_hook(npf_ifhook, NULL, 332 PFIL_IFADDR | PFIL_IFNET, npf_ph_if); 333 KASSERT(error == 0); 334 } 335 if (init) { 336 goto out; 337 } 338 339 /* Check if pfil hooks are not already registered. */ 340 if (pfil_registered) { 341 error = EEXIST; 342 goto out; 343 } 344 345 /* Capture points of the activity in the IP layer. */ 346 npf_ph_inet = pfil_head_get(PFIL_TYPE_AF, (void *)AF_INET); 347 npf_ph_inet6 = pfil_head_get(PFIL_TYPE_AF, (void *)AF_INET6); 348 if (!npf_ph_inet && !npf_ph_inet6) { 349 error = ENOENT; 350 goto out; 351 } 352 353 /* Packet IN/OUT handlers for IP layer. */ 354 if (npf_ph_inet) { 355 error = pfil_add_hook(npf_packet_handler, NULL, 356 PFIL_ALL, npf_ph_inet); 357 KASSERT(error == 0); 358 } 359 if (npf_ph_inet6) { 360 error = pfil_add_hook(npf_packet_handler, NULL, 361 PFIL_ALL, npf_ph_inet6); 362 KASSERT(error == 0); 363 } 364 pfil_registered = true; 365 out: 366 KERNEL_UNLOCK_ONE(NULL); 367 mutex_exit(softnet_lock); 368 369 return error; 370 } 371 372 /* 373 * npf_pfil_unregister: unregister pfil(9) hooks. 374 */ 375 void 376 npf_pfil_unregister(bool fini) 377 { 378 mutex_enter(softnet_lock); 379 KERNEL_LOCK(1, NULL); 380 381 if (fini && npf_ph_if) { 382 (void)pfil_remove_hook(npf_ifhook, NULL, 383 PFIL_IFADDR | PFIL_IFNET, npf_ph_if); 384 } 385 if (npf_ph_inet) { 386 (void)pfil_remove_hook(npf_packet_handler, NULL, 387 PFIL_ALL, npf_ph_inet); 388 } 389 if (npf_ph_inet6) { 390 (void)pfil_remove_hook(npf_packet_handler, NULL, 391 PFIL_ALL, npf_ph_inet6); 392 } 393 pfil_registered = false; 394 395 KERNEL_UNLOCK_ONE(NULL); 396 mutex_exit(softnet_lock); 397 } 398 399 bool 400 npf_pfil_registered_p(void) 401 { 402 return pfil_registered; 403 } 404