ip_fw_pfil.c revision 193859
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/ipfw/ip_fw_pfil.c 193859 2009-06-09 21:27:11Z oleg $"); 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/ip.h> 61#include <netinet/ip_var.h> 62#include <netinet/ip_fw.h> 63#include <netinet/ip_divert.h> 64#include <netinet/ip_dummynet.h> 65#include <netinet/vinet.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/* Divert hooks. */ 81ip_divert_packet_t *ip_divert_ptr = NULL; 82 83/* ng_ipfw hooks. */ 84ng_ipfw_input_t *ng_ipfw_input_p = NULL; 85 86/* Forward declarations. */ 87static int ipfw_divert(struct mbuf **, int, int); 88#define DIV_DIR_IN 1 89#define DIV_DIR_OUT 0 90 91int 92ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, 93 struct inpcb *inp) 94{ 95 INIT_VNET_INET(curvnet); 96 struct ip_fw_args args; 97 struct ng_ipfw_tag *ng_tag; 98 struct m_tag *dn_tag; 99 int ipfw = 0; 100 int divert; 101 int tee; 102#ifdef IPFIREWALL_FORWARD 103 struct m_tag *fwd_tag; 104#endif 105 106 KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!")); 107 108 bzero(&args, sizeof(args)); 109 110 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0, 111 NULL); 112 if (ng_tag != NULL) { 113 KASSERT(ng_tag->dir == NG_IPFW_IN, 114 ("ng_ipfw tag with wrong direction")); 115 args.rule = ng_tag->rule; 116 args.rule_id = ng_tag->rule_id; 117 args.chain_id = ng_tag->chain_id; 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 args.rule_id = dt->rule_id; 129 args.chain_id = dt->chain_id; 130 131 m_tag_delete(*m0, dn_tag); 132 } 133 134 args.m = *m0; 135 args.inp = inp; 136 tee = 0; 137 138 if (V_fw_one_pass == 0 || args.rule == NULL) { 139 ipfw = ipfw_chk(&args); 140 *m0 = args.m; 141 } else 142 ipfw = IP_FW_PASS; 143 144 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", 145 __func__)); 146 147 switch (ipfw) { 148 case IP_FW_PASS: 149 if (args.next_hop == NULL) 150 goto pass; 151 152#ifdef IPFIREWALL_FORWARD 153 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, 154 sizeof(struct sockaddr_in), M_NOWAIT); 155 if (fwd_tag == NULL) 156 goto drop; 157 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in)); 158 m_tag_prepend(*m0, fwd_tag); 159 160 if (in_localip(args.next_hop->sin_addr)) 161 (*m0)->m_flags |= M_FASTFWD_OURS; 162 goto pass; 163#endif 164 break; /* not reached */ 165 166 case IP_FW_DENY: 167 goto drop; 168 break; /* not reached */ 169 170 case IP_FW_DUMMYNET: 171 if (ip_dn_io_ptr == NULL) 172 goto drop; 173 if (mtod(*m0, struct ip *)->ip_v == 4) 174 ip_dn_io_ptr(m0, DN_TO_IP_IN, &args); 175 else if (mtod(*m0, struct ip *)->ip_v == 6) 176 ip_dn_io_ptr(m0, DN_TO_IP6_IN, &args); 177 if (*m0 != NULL) 178 goto again; 179 return 0; /* packet consumed */ 180 181 case IP_FW_TEE: 182 tee = 1; 183 /* fall through */ 184 185 case IP_FW_DIVERT: 186 divert = ipfw_divert(m0, DIV_DIR_IN, tee); 187 if (divert) { 188 *m0 = NULL; 189 return 0; /* packet consumed */ 190 } else { 191 args.rule = NULL; 192 goto again; /* continue with packet */ 193 } 194 195 case IP_FW_NGTEE: 196 if (!NG_IPFW_LOADED) 197 goto drop; 198 (void)ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 1); 199 goto again; /* continue with packet */ 200 201 case IP_FW_NETGRAPH: 202 if (!NG_IPFW_LOADED) 203 goto drop; 204 return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0); 205 206 case IP_FW_NAT: 207 goto again; /* continue with packet */ 208 209 case IP_FW_REASS: 210 goto again; 211 212 default: 213 KASSERT(0, ("%s: unknown retval", __func__)); 214 } 215 216drop: 217 if (*m0) 218 m_freem(*m0); 219 *m0 = NULL; 220 return (EACCES); 221pass: 222 return 0; /* not filtered */ 223} 224 225int 226ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, 227 struct inpcb *inp) 228{ 229 INIT_VNET_INET(curvnet); 230 struct ip_fw_args args; 231 struct ng_ipfw_tag *ng_tag; 232 struct m_tag *dn_tag; 233 int ipfw = 0; 234 int divert; 235 int tee; 236#ifdef IPFIREWALL_FORWARD 237 struct m_tag *fwd_tag; 238#endif 239 240 KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!")); 241 242 bzero(&args, sizeof(args)); 243 244 ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0, 245 NULL); 246 if (ng_tag != NULL) { 247 KASSERT(ng_tag->dir == NG_IPFW_OUT, 248 ("ng_ipfw tag with wrong direction")); 249 args.rule = ng_tag->rule; 250 args.rule_id = ng_tag->rule_id; 251 args.chain_id = ng_tag->chain_id; 252 m_tag_delete(*m0, (struct m_tag *)ng_tag); 253 } 254 255again: 256 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL); 257 if (dn_tag != NULL) { 258 struct dn_pkt_tag *dt; 259 260 dt = (struct dn_pkt_tag *)(dn_tag+1); 261 args.rule = dt->rule; 262 args.rule_id = dt->rule_id; 263 args.chain_id = dt->chain_id; 264 265 m_tag_delete(*m0, dn_tag); 266 } 267 268 args.m = *m0; 269 args.oif = ifp; 270 args.inp = inp; 271 tee = 0; 272 273 if (V_fw_one_pass == 0 || args.rule == NULL) { 274 ipfw = ipfw_chk(&args); 275 *m0 = args.m; 276 } else 277 ipfw = IP_FW_PASS; 278 279 KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", 280 __func__)); 281 282 switch (ipfw) { 283 case IP_FW_PASS: 284 if (args.next_hop == NULL) 285 goto pass; 286#ifdef IPFIREWALL_FORWARD 287 /* Overwrite existing tag. */ 288 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); 289 if (fwd_tag == NULL) { 290 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, 291 sizeof(struct sockaddr_in), M_NOWAIT); 292 if (fwd_tag == NULL) 293 goto drop; 294 } else 295 m_tag_unlink(*m0, fwd_tag); 296 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in)); 297 m_tag_prepend(*m0, fwd_tag); 298 299 if (in_localip(args.next_hop->sin_addr)) 300 (*m0)->m_flags |= M_FASTFWD_OURS; 301 goto pass; 302#endif 303 break; /* not reached */ 304 305 case IP_FW_DENY: 306 goto drop; 307 break; /* not reached */ 308 309 case IP_FW_DUMMYNET: 310 if (ip_dn_io_ptr == NULL) 311 break; 312 if (mtod(*m0, struct ip *)->ip_v == 4) 313 ip_dn_io_ptr(m0, DN_TO_IP_OUT, &args); 314 else if (mtod(*m0, struct ip *)->ip_v == 6) 315 ip_dn_io_ptr(m0, DN_TO_IP6_OUT, &args); 316 if (*m0 != NULL) 317 goto again; 318 return 0; /* packet consumed */ 319 320 break; 321 322 case IP_FW_TEE: 323 tee = 1; 324 /* fall through */ 325 326 case IP_FW_DIVERT: 327 divert = ipfw_divert(m0, DIV_DIR_OUT, tee); 328 if (divert) { 329 *m0 = NULL; 330 return 0; /* packet consumed */ 331 } else { 332 args.rule = NULL; 333 goto again; /* continue with packet */ 334 } 335 336 case IP_FW_NGTEE: 337 if (!NG_IPFW_LOADED) 338 goto drop; 339 (void)ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 1); 340 goto again; /* continue with packet */ 341 342 case IP_FW_NETGRAPH: 343 if (!NG_IPFW_LOADED) 344 goto drop; 345 return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0); 346 347 case IP_FW_NAT: 348 goto again; /* continue with packet */ 349 350 case IP_FW_REASS: 351 goto again; 352 353 default: 354 KASSERT(0, ("%s: unknown retval", __func__)); 355 } 356 357drop: 358 if (*m0) 359 m_freem(*m0); 360 *m0 = NULL; 361 return (EACCES); 362pass: 363 return 0; /* not filtered */ 364} 365 366static int 367ipfw_divert(struct mbuf **m, int incoming, int tee) 368{ 369 /* 370 * ipfw_chk() has already tagged the packet with the divert tag. 371 * If tee is set, copy packet and return original. 372 * If not tee, consume packet and send it to divert socket. 373 */ 374 struct mbuf *clone, *reass; 375 struct ip *ip; 376 int hlen; 377 378 reass = NULL; 379 380 /* Is divert module loaded? */ 381 if (ip_divert_ptr == NULL) 382 goto nodivert; 383 384 /* Cloning needed for tee? */ 385 if (tee) 386 clone = m_dup(*m, M_DONTWAIT); 387 else 388 clone = *m; 389 390 /* In case m_dup was unable to allocate mbufs. */ 391 if (clone == NULL) 392 goto teeout; 393 394 /* 395 * Divert listeners can only handle non-fragmented packets. 396 * However when tee is set we will *not* de-fragment the packets; 397 * Doing do would put the reassembly into double-jeopardy. On top 398 * of that someone doing a tee will probably want to get the packet 399 * in its original form. 400 */ 401 ip = mtod(clone, struct ip *); 402 if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) { 403 404 /* Reassemble packet. */ 405 reass = ip_reass(clone); 406 407 /* 408 * IP header checksum fixup after reassembly and leave header 409 * in network byte order. 410 */ 411 if (reass != NULL) { 412 ip = mtod(reass, struct ip *); 413 hlen = ip->ip_hl << 2; 414 ip->ip_len = htons(ip->ip_len); 415 ip->ip_off = htons(ip->ip_off); 416 ip->ip_sum = 0; 417 if (hlen == sizeof(struct ip)) 418 ip->ip_sum = in_cksum_hdr(ip); 419 else 420 ip->ip_sum = in_cksum(reass, hlen); 421 clone = reass; 422 } else 423 clone = NULL; 424 } else { 425 /* Convert header to network byte order. */ 426 ip->ip_len = htons(ip->ip_len); 427 ip->ip_off = htons(ip->ip_off); 428 } 429 430 /* Do the dirty job... */ 431 if (clone && ip_divert_ptr != NULL) 432 ip_divert_ptr(clone, incoming); 433 434teeout: 435 /* 436 * For tee we leave the divert tag attached to original packet. 437 * It will then continue rule evaluation after the tee rule. 438 */ 439 if (tee) 440 return 0; 441 442 /* Packet diverted and consumed */ 443 return 1; 444 445nodivert: 446 m_freem(*m); 447 return 1; 448} 449 450static int 451ipfw_hook(void) 452{ 453 struct pfil_head *pfh_inet; 454 455 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 456 if (pfh_inet == NULL) 457 return ENOENT; 458 459 (void)pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, 460 pfh_inet); 461 (void)pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, 462 pfh_inet); 463 464 return 0; 465} 466 467static int 468ipfw_unhook(void) 469{ 470 struct pfil_head *pfh_inet; 471 472 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 473 if (pfh_inet == NULL) 474 return ENOENT; 475 476 (void)pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, 477 pfh_inet); 478 (void)pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, 479 pfh_inet); 480 481 return 0; 482} 483 484#ifdef INET6 485static int 486ipfw6_hook(void) 487{ 488 struct pfil_head *pfh_inet6; 489 490 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); 491 if (pfh_inet6 == NULL) 492 return ENOENT; 493 494 (void)pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, 495 pfh_inet6); 496 (void)pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, 497 pfh_inet6); 498 499 return 0; 500} 501 502static int 503ipfw6_unhook(void) 504{ 505 struct pfil_head *pfh_inet6; 506 507 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); 508 if (pfh_inet6 == NULL) 509 return ENOENT; 510 511 (void)pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, 512 pfh_inet6); 513 (void)pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, 514 pfh_inet6); 515 516 return 0; 517} 518#endif /* INET6 */ 519 520int 521ipfw_chg_hook(SYSCTL_HANDLER_ARGS) 522{ 523 INIT_VNET_IPFW(curvnet); 524 int enable = *(int *)arg1; 525 int error; 526 527 error = sysctl_handle_int(oidp, &enable, 0, req); 528 if (error) 529 return (error); 530 531 enable = (enable) ? 1 : 0; 532 533 if (enable == *(int *)arg1) 534 return (0); 535 536 if (arg1 == &V_fw_enable) { 537 if (enable) 538 error = ipfw_hook(); 539 else 540 error = ipfw_unhook(); 541 } 542#ifdef INET6 543 if (arg1 == &V_fw6_enable) { 544 if (enable) 545 error = ipfw6_hook(); 546 else 547 error = ipfw6_unhook(); 548 } 549#endif 550 551 if (error) 552 return (error); 553 554 *(int *)arg1 = enable; 555 556 return (0); 557} 558 559static int 560ipfw_modevent(module_t mod, int type, void *unused) 561{ 562 int err = 0; 563 564 switch (type) { 565 case MOD_LOAD: 566 if ((err = ipfw_init()) != 0) { 567 printf("ipfw_init() error\n"); 568 break; 569 } 570 if ((err = ipfw_hook()) != 0) { 571 printf("ipfw_hook() error\n"); 572 break; 573 } 574#ifdef INET6 575 if ((err = ipfw6_hook()) != 0) { 576 printf("ipfw_hook() error\n"); 577 break; 578 } 579#endif 580 break; 581 582 case MOD_UNLOAD: 583 if ((err = ipfw_unhook()) > 0) 584 break; 585#ifdef INET6 586 if ((err = ipfw6_unhook()) > 0) 587 break; 588#endif 589 ipfw_destroy(); 590 break; 591 592 default: 593 return EOPNOTSUPP; 594 break; 595 } 596 return err; 597} 598 599static moduledata_t ipfwmod = { 600 "ipfw", 601 ipfw_modevent, 602 0 603}; 604DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY - 256); 605MODULE_VERSION(ipfw, 2); 606