ip_fw_pfil.c revision 185937
1/*- 2 * Copyright (c) 2004 Andre Oppermann, Internet Business Solutions AG 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/netinet/ip_fw_pfil.c 185937 2008-12-11 16:26:38Z bz $"); 29 30#if !defined(KLD_MODULE) 31#include "opt_ipfw.h" 32#include "opt_ipdn.h" 33#include "opt_inet.h" 34#ifndef INET 35#error IPFIREWALL requires INET. 36#endif /* INET */ 37#endif /* KLD_MODULE */ 38#include "opt_inet6.h" 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/malloc.h> 43#include <sys/mbuf.h> 44#include <sys/module.h> 45#include <sys/kernel.h> 46#include <sys/lock.h> 47#include <sys/rwlock.h> 48#include <sys/socket.h> 49#include <sys/socketvar.h> 50#include <sys/sysctl.h> 51#include <sys/ucred.h> 52#include <sys/vimage.h> 53 54#include <net/if.h> 55#include <net/route.h> 56#include <net/pfil.h> 57 58#include <netinet/in.h> 59#include <netinet/in_systm.h> 60#include <netinet/in_var.h> 61#include <netinet/ip.h> 62#include <netinet/ip_var.h> 63#include <netinet/ip_fw.h> 64#include <netinet/ip_divert.h> 65#include <netinet/ip_dummynet.h> 66 67#include <netgraph/ng_ipfw.h> 68 69#include <machine/in_cksum.h> 70 71#ifdef VIMAGE_GLOBALS 72int fw_enable = 1; 73#ifdef INET6 74int fw6_enable = 1; 75#endif 76#endif 77 78int ipfw_chg_hook(SYSCTL_HANDLER_ARGS); 79 80/* Dummynet hooks. */ 81ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL; 82 83/* Divert hooks. */ 84ip_divert_packet_t *ip_divert_ptr = NULL; 85 86/* ng_ipfw hooks. */ 87ng_ipfw_input_t *ng_ipfw_input_p = NULL; 88 89/* Forward declarations. */ 90static int ipfw_divert(struct mbuf **, int, int); 91#define DIV_DIR_IN 1 92#define DIV_DIR_OUT 0 93 94int 95ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, 96 struct inpcb *inp) 97{ 98 struct ip_fw_args args; 99 struct ng_ipfw_tag *ng_tag; 100 struct m_tag *dn_tag; 101 int ipfw = 0; 102 int divert; 103 int tee; 104#ifdef IPFIREWALL_FORWARD 105 struct m_tag *fwd_tag; 106#endif 107 108 KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!")); 109 110 bzero(&args, sizeof(args)); 111 112 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0, 113 NULL); 114 if (ng_tag != NULL) { 115 KASSERT(ng_tag->dir == NG_IPFW_IN, 116 ("ng_ipfw tag with wrong direction")); 117 args.rule = ng_tag->rule; 118 m_tag_delete(*m0, (struct m_tag *)ng_tag); 119 } 120 121again: 122 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL); 123 if (dn_tag != NULL){ 124 struct dn_pkt_tag *dt; 125 126 dt = (struct dn_pkt_tag *)(dn_tag+1); 127 args.rule = dt->rule; 128 129 m_tag_delete(*m0, dn_tag); 130 } 131 132 args.m = *m0; 133 args.inp = inp; 134 ipfw = ipfw_chk(&args); 135 *m0 = args.m; 136 tee = 0; 137 138 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", 139 __func__)); 140 141 switch (ipfw) { 142 case IP_FW_PASS: 143 if (args.next_hop == NULL) 144 goto pass; 145 146#ifdef IPFIREWALL_FORWARD 147 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, 148 sizeof(struct sockaddr_in), M_NOWAIT); 149 if (fwd_tag == NULL) 150 goto drop; 151 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in)); 152 m_tag_prepend(*m0, fwd_tag); 153 154 if (in_localip(args.next_hop->sin_addr)) 155 (*m0)->m_flags |= M_FASTFWD_OURS; 156 goto pass; 157#endif 158 break; /* not reached */ 159 160 case IP_FW_DENY: 161 goto drop; 162 break; /* not reached */ 163 164 case IP_FW_DUMMYNET: 165 if (!DUMMYNET_LOADED) 166 goto drop; 167 if (mtod(*m0, struct ip *)->ip_v == 4) 168 ip_dn_io_ptr(m0, DN_TO_IP_IN, &args); 169 else if (mtod(*m0, struct ip *)->ip_v == 6) 170 ip_dn_io_ptr(m0, DN_TO_IP6_IN, &args); 171 if (*m0 != NULL) 172 goto again; 173 return 0; /* packet consumed */ 174 175 case IP_FW_TEE: 176 tee = 1; 177 /* fall through */ 178 179 case IP_FW_DIVERT: 180 divert = ipfw_divert(m0, DIV_DIR_IN, tee); 181 if (divert) { 182 *m0 = NULL; 183 return 0; /* packet consumed */ 184 } else { 185 args.rule = NULL; 186 goto again; /* continue with packet */ 187 } 188 189 case IP_FW_NGTEE: 190 if (!NG_IPFW_LOADED) 191 goto drop; 192 (void)ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 1); 193 goto again; /* continue with packet */ 194 195 case IP_FW_NETGRAPH: 196 if (!NG_IPFW_LOADED) 197 goto drop; 198 return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0); 199 200 case IP_FW_NAT: 201 goto again; /* continue with packet */ 202 203 default: 204 KASSERT(0, ("%s: unknown retval", __func__)); 205 } 206 207drop: 208 if (*m0) 209 m_freem(*m0); 210 *m0 = NULL; 211 return (EACCES); 212pass: 213 return 0; /* not filtered */ 214} 215 216int 217ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, 218 struct inpcb *inp) 219{ 220 struct ip_fw_args args; 221 struct ng_ipfw_tag *ng_tag; 222 struct m_tag *dn_tag; 223 int ipfw = 0; 224 int divert; 225 int tee; 226#ifdef IPFIREWALL_FORWARD 227 struct m_tag *fwd_tag; 228#endif 229 230 KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!")); 231 232 bzero(&args, sizeof(args)); 233 234 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0, 235 NULL); 236 if (ng_tag != NULL) { 237 KASSERT(ng_tag->dir == NG_IPFW_OUT, 238 ("ng_ipfw tag with wrong direction")); 239 args.rule = ng_tag->rule; 240 m_tag_delete(*m0, (struct m_tag *)ng_tag); 241 } 242 243again: 244 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL); 245 if (dn_tag != NULL) { 246 struct dn_pkt_tag *dt; 247 248 dt = (struct dn_pkt_tag *)(dn_tag+1); 249 args.rule = dt->rule; 250 251 m_tag_delete(*m0, dn_tag); 252 } 253 254 args.m = *m0; 255 args.oif = ifp; 256 args.inp = inp; 257 ipfw = ipfw_chk(&args); 258 *m0 = args.m; 259 tee = 0; 260 261 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", 262 __func__)); 263 264 switch (ipfw) { 265 case IP_FW_PASS: 266 if (args.next_hop == NULL) 267 goto pass; 268#ifdef IPFIREWALL_FORWARD 269 /* Overwrite existing tag. */ 270 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); 271 if (fwd_tag == NULL) { 272 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, 273 sizeof(struct sockaddr_in), M_NOWAIT); 274 if (fwd_tag == NULL) 275 goto drop; 276 } else 277 m_tag_unlink(*m0, fwd_tag); 278 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in)); 279 m_tag_prepend(*m0, fwd_tag); 280 281 if (in_localip(args.next_hop->sin_addr)) 282 (*m0)->m_flags |= M_FASTFWD_OURS; 283 goto pass; 284#endif 285 break; /* not reached */ 286 287 case IP_FW_DENY: 288 goto drop; 289 break; /* not reached */ 290 291 case IP_FW_DUMMYNET: 292 if (!DUMMYNET_LOADED) 293 break; 294 if (mtod(*m0, struct ip *)->ip_v == 4) 295 ip_dn_io_ptr(m0, DN_TO_IP_OUT, &args); 296 else if (mtod(*m0, struct ip *)->ip_v == 6) 297 ip_dn_io_ptr(m0, DN_TO_IP6_OUT, &args); 298 if (*m0 != NULL) 299 goto again; 300 return 0; /* packet consumed */ 301 302 break; 303 304 case IP_FW_TEE: 305 tee = 1; 306 /* fall through */ 307 308 case IP_FW_DIVERT: 309 divert = ipfw_divert(m0, DIV_DIR_OUT, tee); 310 if (divert) { 311 *m0 = NULL; 312 return 0; /* packet consumed */ 313 } else { 314 args.rule = NULL; 315 goto again; /* continue with packet */ 316 } 317 318 case IP_FW_NGTEE: 319 if (!NG_IPFW_LOADED) 320 goto drop; 321 (void)ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 1); 322 goto again; /* continue with packet */ 323 324 case IP_FW_NETGRAPH: 325 if (!NG_IPFW_LOADED) 326 goto drop; 327 return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0); 328 329 case IP_FW_NAT: 330 goto again; /* continue with packet */ 331 332 default: 333 KASSERT(0, ("%s: unknown retval", __func__)); 334 } 335 336drop: 337 if (*m0) 338 m_freem(*m0); 339 *m0 = NULL; 340 return (EACCES); 341pass: 342 return 0; /* not filtered */ 343} 344 345static int 346ipfw_divert(struct mbuf **m, int incoming, int tee) 347{ 348 /* 349 * ipfw_chk() has already tagged the packet with the divert tag. 350 * If tee is set, copy packet and return original. 351 * If not tee, consume packet and send it to divert socket. 352 */ 353 struct mbuf *clone, *reass; 354 struct ip *ip; 355 int hlen; 356 357 reass = NULL; 358 359 /* Is divert module loaded? */ 360 if (ip_divert_ptr == NULL) 361 goto nodivert; 362 363 /* Cloning needed for tee? */ 364 if (tee) 365 clone = m_dup(*m, M_DONTWAIT); 366 else 367 clone = *m; 368 369 /* In case m_dup was unable to allocate mbufs. */ 370 if (clone == NULL) 371 goto teeout; 372 373 /* 374 * Divert listeners can only handle non-fragmented packets. 375 * However when tee is set we will *not* de-fragment the packets; 376 * Doing do would put the reassembly into double-jeopardy. On top 377 * of that someone doing a tee will probably want to get the packet 378 * in its original form. 379 */ 380 ip = mtod(clone, struct ip *); 381 if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) { 382 383 /* Reassemble packet. */ 384 reass = ip_reass(clone); 385 386 /* 387 * IP header checksum fixup after reassembly and leave header 388 * in network byte order. 389 */ 390 if (reass != NULL) { 391 ip = mtod(reass, struct ip *); 392 hlen = ip->ip_hl << 2; 393 ip->ip_len = htons(ip->ip_len); 394 ip->ip_off = htons(ip->ip_off); 395 ip->ip_sum = 0; 396 if (hlen == sizeof(struct ip)) 397 ip->ip_sum = in_cksum_hdr(ip); 398 else 399 ip->ip_sum = in_cksum(reass, hlen); 400 clone = reass; 401 } else 402 clone = NULL; 403 } else { 404 /* Convert header to network byte order. */ 405 ip->ip_len = htons(ip->ip_len); 406 ip->ip_off = htons(ip->ip_off); 407 } 408 409 /* Do the dirty job... */ 410 if (clone && ip_divert_ptr != NULL) 411 ip_divert_ptr(clone, incoming); 412 413teeout: 414 /* 415 * For tee we leave the divert tag attached to original packet. 416 * It will then continue rule evaluation after the tee rule. 417 */ 418 if (tee) 419 return 0; 420 421 /* Packet diverted and consumed */ 422 return 1; 423 424nodivert: 425 m_freem(*m); 426 return 1; 427} 428 429static int 430ipfw_hook(void) 431{ 432 struct pfil_head *pfh_inet; 433 434 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 435 if (pfh_inet == NULL) 436 return ENOENT; 437 438 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); 439 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); 440 441 return 0; 442} 443 444static int 445ipfw_unhook(void) 446{ 447 struct pfil_head *pfh_inet; 448 449 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 450 if (pfh_inet == NULL) 451 return ENOENT; 452 453 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); 454 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); 455 456 return 0; 457} 458 459#ifdef INET6 460static int 461ipfw6_hook(void) 462{ 463 struct pfil_head *pfh_inet6; 464 465 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); 466 if (pfh_inet6 == NULL) 467 return ENOENT; 468 469 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); 470 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); 471 472 return 0; 473} 474 475static int 476ipfw6_unhook(void) 477{ 478 struct pfil_head *pfh_inet6; 479 480 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); 481 if (pfh_inet6 == NULL) 482 return ENOENT; 483 484 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); 485 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); 486 487 return 0; 488} 489#endif /* INET6 */ 490 491int 492ipfw_chg_hook(SYSCTL_HANDLER_ARGS) 493{ 494 INIT_VNET_IPFW(curvnet); 495 int enable = *(int *)arg1; 496 int error; 497 498 error = sysctl_handle_int(oidp, &enable, 0, req); 499 if (error) 500 return (error); 501 502 enable = (enable) ? 1 : 0; 503 504 if (enable == *(int *)arg1) 505 return (0); 506 507 if (arg1 == &V_fw_enable) { 508 if (enable) 509 error = ipfw_hook(); 510 else 511 error = ipfw_unhook(); 512 } 513#ifdef INET6 514 if (arg1 == &V_fw6_enable) { 515 if (enable) 516 error = ipfw6_hook(); 517 else 518 error = ipfw6_unhook(); 519 } 520#endif 521 522 if (error) 523 return (error); 524 525 *(int *)arg1 = enable; 526 527 return (0); 528} 529 530static int 531ipfw_modevent(module_t mod, int type, void *unused) 532{ 533 int err = 0; 534 535 switch (type) { 536 case MOD_LOAD: 537 if ((err = ipfw_init()) != 0) { 538 printf("ipfw_init() error\n"); 539 break; 540 } 541 if ((err = ipfw_hook()) != 0) { 542 printf("ipfw_hook() error\n"); 543 break; 544 } 545#ifdef INET6 546 if ((err = ipfw6_hook()) != 0) { 547 printf("ipfw_hook() error\n"); 548 break; 549 } 550#endif 551 break; 552 553 case MOD_UNLOAD: 554 if ((err = ipfw_unhook()) > 0) 555 break; 556#ifdef INET6 557 if ((err = ipfw6_unhook()) > 0) 558 break; 559#endif 560 ipfw_destroy(); 561 break; 562 563 default: 564 return EOPNOTSUPP; 565 break; 566 } 567 return err; 568} 569 570static moduledata_t ipfwmod = { 571 "ipfw", 572 ipfw_modevent, 573 0 574}; 575DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY - 256); 576MODULE_VERSION(ipfw, 2); 577