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