1139823Simp/*- 2133920Sandre * Copyright (c) 2004 Andre Oppermann, Internet Business Solutions AG 3133920Sandre * All rights reserved. 4133920Sandre * 5133920Sandre * Redistribution and use in source and binary forms, with or without 6133920Sandre * modification, are permitted provided that the following conditions 7133920Sandre * are met: 8133920Sandre * 1. Redistributions of source code must retain the above copyright 9133920Sandre * notice, this list of conditions and the following disclaimer. 10133920Sandre * 2. Redistributions in binary form must reproduce the above copyright 11133920Sandre * notice, this list of conditions and the following disclaimer in the 12133920Sandre * documentation and/or other materials provided with the distribution. 13133920Sandre * 14133920Sandre * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15133920Sandre * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16133920Sandre * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17133920Sandre * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18133920Sandre * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19133920Sandre * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20133920Sandre * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21133920Sandre * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22133920Sandre * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23133920Sandre * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24133920Sandre * SUCH DAMAGE. 25133920Sandre */ 26133920Sandre 27172467Ssilby#include <sys/cdefs.h> 28172467Ssilby__FBSDID("$FreeBSD$"); 29172467Ssilby 30225518Sjhb#include "opt_ipfw.h" 31133920Sandre#include "opt_inet.h" 32225793Sbz#include "opt_inet6.h" 33133920Sandre#ifndef INET 34133920Sandre#error IPFIREWALL requires INET. 35133920Sandre#endif /* INET */ 36133920Sandre 37133920Sandre#include <sys/param.h> 38133920Sandre#include <sys/systm.h> 39133920Sandre#include <sys/malloc.h> 40133920Sandre#include <sys/mbuf.h> 41133920Sandre#include <sys/module.h> 42133920Sandre#include <sys/kernel.h> 43185895Szec#include <sys/lock.h> 44185895Szec#include <sys/rwlock.h> 45133920Sandre#include <sys/socket.h> 46133920Sandre#include <sys/sysctl.h> 47133920Sandre 48133920Sandre#include <net/if.h> 49133920Sandre#include <net/route.h> 50240099Smelifaro#include <net/ethernet.h> 51133920Sandre#include <net/pfil.h> 52196423Sjulian#include <net/vnet.h> 53133920Sandre 54133920Sandre#include <netinet/in.h> 55133920Sandre#include <netinet/in_systm.h> 56133920Sandre#include <netinet/ip.h> 57133920Sandre#include <netinet/ip_var.h> 58133920Sandre#include <netinet/ip_fw.h> 59223593Sglebius#ifdef INET6 60223593Sglebius#include <netinet/ip6.h> 61223593Sglebius#include <netinet6/ip6_var.h> 62223593Sglebius#endif 63240494Sglebius 64201124Sluigi#include <netgraph/ng_ipfw.h> 65133920Sandre 66240494Sglebius#include <netpfil/ipfw/ip_fw_private.h> 67240494Sglebius 68133920Sandre#include <machine/in_cksum.h> 69133920Sandre 70215701Sdimstatic VNET_DEFINE(int, fw_enable) = 1; 71200601Sluigi#define V_fw_enable VNET(fw_enable) 72200601Sluigi 73158470Smlaier#ifdef INET6 74215701Sdimstatic VNET_DEFINE(int, fw6_enable) = 1; 75200601Sluigi#define V_fw6_enable VNET(fw6_enable) 76158470Smlaier#endif 77133920Sandre 78240099Smelifarostatic VNET_DEFINE(int, fwlink_enable) = 0; 79240099Smelifaro#define V_fwlink_enable VNET(fwlink_enable) 80240099Smelifaro 81158470Smlaierint ipfw_chg_hook(SYSCTL_HANDLER_ARGS); 82158470Smlaier 83136714Sandre/* Forward declarations. */ 84201527Sluigistatic int ipfw_divert(struct mbuf **, int, struct ipfw_rule_ref *, int); 85240099Smelifarostatic int ipfw_check_packet(void *, struct mbuf **, struct ifnet *, int, 86240099Smelifaro struct inpcb *); 87240099Smelifarostatic int ipfw_check_frame(void *, struct mbuf **, struct ifnet *, int, 88240099Smelifaro struct inpcb *); 89133920Sandre 90200601Sluigi#ifdef SYSCTL_NODE 91204591Sluigi 92204591SluigiSYSBEGIN(f1) 93204591Sluigi 94200601SluigiSYSCTL_DECL(_net_inet_ip_fw); 95200601SluigiSYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, enable, 96200601Sluigi CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw_enable), 0, 97200601Sluigi ipfw_chg_hook, "I", "Enable ipfw"); 98200601Sluigi#ifdef INET6 99200601SluigiSYSCTL_DECL(_net_inet6_ip6_fw); 100200601SluigiSYSCTL_VNET_PROC(_net_inet6_ip6_fw, OID_AUTO, enable, 101200601Sluigi CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw6_enable), 0, 102200601Sluigi ipfw_chg_hook, "I", "Enable ipfw+6"); 103200601Sluigi#endif /* INET6 */ 104204591Sluigi 105240099SmelifaroSYSCTL_DECL(_net_link_ether); 106240099SmelifaroSYSCTL_VNET_PROC(_net_link_ether, OID_AUTO, ipfw, 107240099Smelifaro CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fwlink_enable), 0, 108240099Smelifaro ipfw_chg_hook, "I", "Pass ether pkts through firewall"); 109240099Smelifaro 110204591SluigiSYSEND 111204591Sluigi 112200601Sluigi#endif /* SYSCTL_NODE */ 113200601Sluigi 114201122Sluigi/* 115201122Sluigi * The pfilter hook to pass packets to ipfw_chk and then to 116201122Sluigi * dummynet, divert, netgraph or other modules. 117201122Sluigi * The packet may be consumed. 118201122Sluigi */ 119240099Smelifarostatic int 120240099Smelifaroipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, 121135920Smlaier struct inpcb *inp) 122133920Sandre{ 123133920Sandre struct ip_fw_args args; 124201527Sluigi struct m_tag *tag; 125201122Sluigi int ipfw; 126201122Sluigi int ret; 127133920Sandre 128201122Sluigi /* convert dir to IPFW values */ 129201122Sluigi dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT; 130133920Sandre bzero(&args, sizeof(args)); 131133920Sandre 132173399Solegagain: 133201527Sluigi /* 134201527Sluigi * extract and remove the tag if present. If we are left 135201527Sluigi * with onepass, optimize the outgoing path. 136201527Sluigi */ 137201527Sluigi tag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); 138201527Sluigi if (tag != NULL) { 139201527Sluigi args.rule = *((struct ipfw_rule_ref *)(tag+1)); 140201527Sluigi m_tag_delete(*m0, tag); 141241359Sglebius if (args.rule.info & IPFW_ONEPASS) 142223358Sae return (0); 143133920Sandre } 144133920Sandre 145133920Sandre args.m = *m0; 146201122Sluigi args.oif = dir == DIR_OUT ? ifp : NULL; 147135920Smlaier args.inp = inp; 148133920Sandre 149201527Sluigi ipfw = ipfw_chk(&args); 150201527Sluigi *m0 = args.m; 151191570Soleg 152140224Sglebius KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", 153140224Sglebius __func__)); 154133920Sandre 155201122Sluigi /* breaking out of the switch means drop */ 156201122Sluigi ret = 0; /* default return value for pass */ 157140224Sglebius switch (ipfw) { 158140224Sglebius case IP_FW_PASS: 159201122Sluigi /* next_hop may be set by ipfw_chk */ 160225044Sbz if (args.next_hop == NULL && args.next_hop6 == NULL) 161206845Sluigi break; /* pass */ 162242079Sae#if (!defined(INET6) && !defined(INET)) 163201122Sluigi ret = EACCES; 164201122Sluigi#else 165201527Sluigi { 166201527Sluigi struct m_tag *fwd_tag; 167225044Sbz size_t len; 168201527Sluigi 169225044Sbz KASSERT(args.next_hop == NULL || args.next_hop6 == NULL, 170225044Sbz ("%s: both next_hop=%p and next_hop6=%p not NULL", __func__, 171225044Sbz args.next_hop, args.next_hop6)); 172225044Sbz#ifdef INET6 173225044Sbz if (args.next_hop6 != NULL) 174225044Sbz len = sizeof(struct sockaddr_in6); 175225044Sbz#endif 176225044Sbz#ifdef INET 177225044Sbz if (args.next_hop != NULL) 178225044Sbz len = sizeof(struct sockaddr_in); 179225044Sbz#endif 180225044Sbz 181201122Sluigi /* Incoming packets should not be tagged so we do not 182201122Sluigi * m_tag_find. Outgoing packets may be tagged, so we 183201122Sluigi * reuse the tag if present. 184201122Sluigi */ 185201122Sluigi fwd_tag = (dir == DIR_IN) ? NULL : 186201122Sluigi m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); 187201122Sluigi if (fwd_tag != NULL) { 188201122Sluigi m_tag_unlink(*m0, fwd_tag); 189201122Sluigi } else { 190225044Sbz fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, len, 191225044Sbz M_NOWAIT); 192201122Sluigi if (fwd_tag == NULL) { 193201122Sluigi ret = EACCES; 194201122Sluigi break; /* i.e. drop */ 195201122Sluigi } 196201122Sluigi } 197225044Sbz#ifdef INET6 198225044Sbz if (args.next_hop6 != NULL) { 199225044Sbz bcopy(args.next_hop6, (fwd_tag+1), len); 200225044Sbz if (in6_localip(&args.next_hop6->sin6_addr)) 201225044Sbz (*m0)->m_flags |= M_FASTFWD_OURS; 202242463Sae (*m0)->m_flags |= M_IP6_NEXTHOP; 203225044Sbz } 204225044Sbz#endif 205225044Sbz#ifdef INET 206225044Sbz if (args.next_hop != NULL) { 207225044Sbz bcopy(args.next_hop, (fwd_tag+1), len); 208225044Sbz if (in_localip(args.next_hop->sin_addr)) 209225044Sbz (*m0)->m_flags |= M_FASTFWD_OURS; 210242463Sae (*m0)->m_flags |= M_IP_NEXTHOP; 211225044Sbz } 212225044Sbz#endif 213133920Sandre m_tag_prepend(*m0, fwd_tag); 214201527Sluigi } 215242079Sae#endif /* INET || INET6 */ 216201122Sluigi break; 217133920Sandre 218140224Sglebius case IP_FW_DENY: 219201122Sluigi ret = EACCES; 220201122Sluigi break; /* i.e. drop */ 221140224Sglebius 222140224Sglebius case IP_FW_DUMMYNET: 223201122Sluigi ret = EACCES; 224193502Sluigi if (ip_dn_io_ptr == NULL) 225201122Sluigi break; /* i.e. drop */ 226145246Sbrooks if (mtod(*m0, struct ip *)->ip_v == 4) 227201122Sluigi ret = ip_dn_io_ptr(m0, dir, &args); 228145246Sbrooks else if (mtod(*m0, struct ip *)->ip_v == 6) 229201122Sluigi ret = ip_dn_io_ptr(m0, dir | PROTO_IPV6, &args); 230201122Sluigi else 231201122Sluigi break; /* drop it */ 232201122Sluigi /* 233201122Sluigi * XXX should read the return value. 234201122Sluigi * dummynet normally eats the packet and sets *m0=NULL 235201122Sluigi * unless the packet can be sent immediately. In this 236201122Sluigi * case args is updated and we should re-run the 237201122Sluigi * check without clearing args. 238201122Sluigi */ 239173399Soleg if (*m0 != NULL) 240173399Soleg goto again; 241140224Sglebius break; 242140224Sglebius 243140224Sglebius case IP_FW_TEE: 244140224Sglebius case IP_FW_DIVERT: 245201122Sluigi if (ip_divert_ptr == NULL) { 246201122Sluigi ret = EACCES; 247201122Sluigi break; /* i.e. drop */ 248201122Sluigi } 249201527Sluigi ret = ipfw_divert(m0, dir, &args.rule, 250201527Sluigi (ipfw == IP_FW_TEE) ? 1 : 0); 251201527Sluigi /* continue processing for the original packet (tee). */ 252201527Sluigi if (*m0) 253201527Sluigi goto again; 254201122Sluigi break; 255140224Sglebius 256141351Sglebius case IP_FW_NGTEE: 257141351Sglebius case IP_FW_NETGRAPH: 258201732Sluigi if (ng_ipfw_input_p == NULL) { 259201122Sluigi ret = EACCES; 260201122Sluigi break; /* i.e. drop */ 261201122Sluigi } 262201122Sluigi ret = ng_ipfw_input_p(m0, dir, &args, 263201122Sluigi (ipfw == IP_FW_NGTEE) ? 1 : 0); 264201122Sluigi if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */ 265201122Sluigi goto again; /* continue with packet */ 266201122Sluigi break; 267141351Sglebius 268165648Spiso case IP_FW_NAT: 269213254Sluigi /* honor one-pass in case of successful nat */ 270213254Sluigi if (V_fw_one_pass) 271213254Sluigi break; /* ret is already 0 */ 272213254Sluigi goto again; 273213254Sluigi 274201122Sluigi case IP_FW_REASS: 275165648Spiso goto again; /* continue with packet */ 276190633Spiso 277140224Sglebius default: 278140224Sglebius KASSERT(0, ("%s: unknown retval", __func__)); 279140224Sglebius } 280140224Sglebius 281201122Sluigi if (ret != 0) { 282201122Sluigi if (*m0) 283201527Sluigi FREE_PKT(*m0); 284201122Sluigi *m0 = NULL; 285201122Sluigi } 286241245Sglebius 287201122Sluigi return ret; 288133920Sandre} 289133920Sandre 290240099Smelifaro/* 291240099Smelifaro * ipfw processing for ethernet packets (in and out). 292240099Smelifaro * Inteface is NULL from ether_demux, and ifp from 293240099Smelifaro * ether_output_frame. 294240099Smelifaro */ 295240099Smelifarostatic int 296240099Smelifaroipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir, 297240099Smelifaro struct inpcb *inp) 298240099Smelifaro{ 299240099Smelifaro struct ether_header *eh; 300240099Smelifaro struct ether_header save_eh; 301240099Smelifaro struct mbuf *m; 302240099Smelifaro int i, ret; 303240099Smelifaro struct ip_fw_args args; 304240099Smelifaro struct m_tag *mtag; 305240099Smelifaro 306240099Smelifaro /* fetch start point from rule, if any */ 307240099Smelifaro mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); 308240099Smelifaro if (mtag == NULL) { 309240099Smelifaro args.rule.slot = 0; 310240099Smelifaro } else { 311240099Smelifaro /* dummynet packet, already partially processed */ 312240099Smelifaro struct ipfw_rule_ref *r; 313240099Smelifaro 314240099Smelifaro /* XXX can we free it after use ? */ 315240099Smelifaro mtag->m_tag_id = PACKET_TAG_NONE; 316240099Smelifaro r = (struct ipfw_rule_ref *)(mtag + 1); 317240099Smelifaro if (r->info & IPFW_ONEPASS) 318240099Smelifaro return (0); 319240099Smelifaro args.rule = *r; 320240099Smelifaro } 321240099Smelifaro 322240099Smelifaro /* I need some amt of data to be contiguous */ 323240099Smelifaro m = *m0; 324240099Smelifaro i = min(m->m_pkthdr.len, max_protohdr); 325240099Smelifaro if (m->m_len < i) { 326240099Smelifaro m = m_pullup(m, i); 327240099Smelifaro if (m == NULL) { 328240099Smelifaro *m0 = m; 329240099Smelifaro return (0); 330240099Smelifaro } 331240099Smelifaro } 332240099Smelifaro eh = mtod(m, struct ether_header *); 333240099Smelifaro save_eh = *eh; /* save copy for restore below */ 334240099Smelifaro m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ 335240099Smelifaro 336240099Smelifaro args.m = m; /* the packet we are looking at */ 337264813Sae args.oif = dir == PFIL_OUT ? dst: NULL; /* destination, if any */ 338240099Smelifaro args.next_hop = NULL; /* we do not support forward yet */ 339240099Smelifaro args.next_hop6 = NULL; /* we do not support forward yet */ 340240099Smelifaro args.eh = &save_eh; /* MAC header for bridged/MAC packets */ 341240099Smelifaro args.inp = NULL; /* used by ipfw uid/gid/jail rules */ 342240099Smelifaro i = ipfw_chk(&args); 343240099Smelifaro m = args.m; 344240099Smelifaro if (m != NULL) { 345240099Smelifaro /* 346240099Smelifaro * Restore Ethernet header, as needed, in case the 347240099Smelifaro * mbuf chain was replaced by ipfw. 348240099Smelifaro */ 349243882Sglebius M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); 350240099Smelifaro if (m == NULL) { 351240099Smelifaro *m0 = NULL; 352240099Smelifaro return (0); 353240099Smelifaro } 354240099Smelifaro if (eh != mtod(m, struct ether_header *)) 355240099Smelifaro bcopy(&save_eh, mtod(m, struct ether_header *), 356240099Smelifaro ETHER_HDR_LEN); 357240099Smelifaro } 358240099Smelifaro *m0 = m; 359240099Smelifaro 360240099Smelifaro ret = 0; 361240099Smelifaro /* Check result of ipfw_chk() */ 362240099Smelifaro switch (i) { 363240099Smelifaro case IP_FW_PASS: 364240099Smelifaro break; 365240099Smelifaro 366240099Smelifaro case IP_FW_DENY: 367240099Smelifaro ret = EACCES; 368240099Smelifaro break; /* i.e. drop */ 369240099Smelifaro 370240099Smelifaro case IP_FW_DUMMYNET: 371240099Smelifaro ret = EACCES; 372240099Smelifaro int dir; 373240099Smelifaro 374240099Smelifaro if (ip_dn_io_ptr == NULL) 375240099Smelifaro break; /* i.e. drop */ 376240099Smelifaro 377240099Smelifaro *m0 = NULL; 378240099Smelifaro dir = PROTO_LAYER2 | (dst ? DIR_OUT : DIR_IN); 379240099Smelifaro ip_dn_io_ptr(&m, dir, &args); 380240099Smelifaro return 0; 381240099Smelifaro 382240099Smelifaro default: 383240099Smelifaro KASSERT(0, ("%s: unknown retval", __func__)); 384240099Smelifaro } 385240099Smelifaro 386240099Smelifaro if (ret != 0) { 387240099Smelifaro if (*m0) 388240099Smelifaro FREE_PKT(*m0); 389240099Smelifaro *m0 = NULL; 390240099Smelifaro } 391240099Smelifaro 392240099Smelifaro return ret; 393240099Smelifaro} 394240099Smelifaro 395201527Sluigi/* do the divert, return 1 on error 0 on success */ 396201527Sluigistatic int 397201527Sluigiipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule, 398201527Sluigi int tee) 399133920Sandre{ 400133920Sandre /* 401135154Sandre * ipfw_chk() has already tagged the packet with the divert tag. 402133920Sandre * If tee is set, copy packet and return original. 403133920Sandre * If not tee, consume packet and send it to divert socket. 404133920Sandre */ 405201122Sluigi struct mbuf *clone; 406223593Sglebius struct ip *ip = mtod(*m0, struct ip *); 407201527Sluigi struct m_tag *tag; 408133920Sandre 409133920Sandre /* Cloning needed for tee? */ 410201122Sluigi if (tee == 0) { 411201122Sluigi clone = *m0; /* use the original mbuf */ 412201122Sluigi *m0 = NULL; 413201122Sluigi } else { 414243882Sglebius clone = m_dup(*m0, M_NOWAIT); 415201122Sluigi /* If we cannot duplicate the mbuf, we sacrifice the divert 416201122Sluigi * chain and continue with the tee-ed packet. 417201122Sluigi */ 418201122Sluigi if (clone == NULL) 419201527Sluigi return 1; 420201122Sluigi } 421133920Sandre 422133920Sandre /* 423201122Sluigi * Divert listeners can normally handle non-fragmented packets, 424201122Sluigi * but we can only reass in the non-tee case. 425201122Sluigi * This means that listeners on a tee rule may get fragments, 426201122Sluigi * and have to live with that. 427201122Sluigi * Note that we now have the 'reass' ipfw option so if we care 428201122Sluigi * we can do it before a 'tee'. 429133920Sandre */ 430223593Sglebius if (!tee) switch (ip->ip_v) { 431223593Sglebius case IPVERSION: 432223593Sglebius if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) { 433201122Sluigi int hlen; 434201122Sluigi struct mbuf *reass; 435133920Sandre 436201122Sluigi reass = ip_reass(clone); /* Reassemble packet. */ 437201122Sluigi if (reass == NULL) 438201527Sluigi return 0; /* not an error */ 439201122Sluigi /* if reass = NULL then it was consumed by ip_reass */ 440133920Sandre /* 441133920Sandre * IP header checksum fixup after reassembly and leave header 442133920Sandre * in network byte order. 443133920Sandre */ 444201122Sluigi ip = mtod(reass, struct ip *); 445201122Sluigi hlen = ip->ip_hl << 2; 446201122Sluigi ip->ip_sum = 0; 447201122Sluigi if (hlen == sizeof(struct ip)) 448201122Sluigi ip->ip_sum = in_cksum_hdr(ip); 449201122Sluigi else 450201122Sluigi ip->ip_sum = in_cksum(reass, hlen); 451201122Sluigi clone = reass; 452223593Sglebius } 453223593Sglebius break; 454223593Sglebius#ifdef INET6 455223593Sglebius case IPV6_VERSION >> 4: 456223593Sglebius { 457223593Sglebius struct ip6_hdr *const ip6 = mtod(clone, struct ip6_hdr *); 458223593Sglebius 459223593Sglebius if (ip6->ip6_nxt == IPPROTO_FRAGMENT) { 460223593Sglebius int nxt, off; 461223593Sglebius 462223593Sglebius off = sizeof(struct ip6_hdr); 463223593Sglebius nxt = frag6_input(&clone, &off, 0); 464223593Sglebius if (nxt == IPPROTO_DONE) 465223593Sglebius return (0); 466223593Sglebius } 467223593Sglebius break; 468223593Sglebius } 469223593Sglebius#endif 470133920Sandre } 471223593Sglebius 472201527Sluigi /* attach a tag to the packet with the reinject info */ 473201527Sluigi tag = m_tag_alloc(MTAG_IPFW_RULE, 0, 474201527Sluigi sizeof(struct ipfw_rule_ref), M_NOWAIT); 475201527Sluigi if (tag == NULL) { 476201527Sluigi FREE_PKT(clone); 477201527Sluigi return 1; 478201527Sluigi } 479201527Sluigi *((struct ipfw_rule_ref *)(tag+1)) = *rule; 480201527Sluigi m_tag_prepend(clone, tag); 481133920Sandre 482133920Sandre /* Do the dirty job... */ 483201122Sluigi ip_divert_ptr(clone, incoming); 484201527Sluigi return 0; 485133920Sandre} 486133920Sandre 487201122Sluigi/* 488201122Sluigi * attach or detach hooks for a given protocol family 489201122Sluigi */ 490200601Sluigistatic int 491201122Sluigiipfw_hook(int onoff, int pf) 492133920Sandre{ 493201122Sluigi struct pfil_head *pfh; 494240099Smelifaro void *hook_func; 495133920Sandre 496201122Sluigi pfh = pfil_head_get(PFIL_TYPE_AF, pf); 497201122Sluigi if (pfh == NULL) 498133920Sandre return ENOENT; 499133920Sandre 500240099Smelifaro hook_func = (pf == AF_LINK) ? ipfw_check_frame : ipfw_check_packet; 501240099Smelifaro 502201527Sluigi (void) (onoff ? pfil_add_hook : pfil_remove_hook) 503240099Smelifaro (hook_func, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh); 504133920Sandre 505133920Sandre return 0; 506133920Sandre} 507133920Sandre 508196423Sjulianint 509201122Sluigiipfw_attach_hooks(int arg) 510133920Sandre{ 511200601Sluigi int error = 0; 512200601Sluigi 513201122Sluigi if (arg == 0) /* detach */ 514201122Sluigi ipfw_hook(0, AF_INET); 515206845Sluigi else if (V_fw_enable && ipfw_hook(1, AF_INET) != 0) { 516200601Sluigi error = ENOENT; /* see ip_fw_pfil.c::ipfw_hook() */ 517200601Sluigi printf("ipfw_hook() error\n"); 518200601Sluigi } 519200601Sluigi#ifdef INET6 520201122Sluigi if (arg == 0) /* detach */ 521201122Sluigi ipfw_hook(0, AF_INET6); 522206845Sluigi else if (V_fw6_enable && ipfw_hook(1, AF_INET6) != 0) { 523200601Sluigi error = ENOENT; 524200601Sluigi printf("ipfw6_hook() error\n"); 525200601Sluigi } 526200601Sluigi#endif 527240099Smelifaro if (arg == 0) /* detach */ 528240099Smelifaro ipfw_hook(0, AF_LINK); 529240099Smelifaro else if (V_fwlink_enable && ipfw_hook(1, AF_LINK) != 0) { 530240099Smelifaro error = ENOENT; 531240099Smelifaro printf("ipfw_link_hook() error\n"); 532240099Smelifaro } 533200601Sluigi return error; 534200601Sluigi} 535200601Sluigi 536200601Sluigiint 537158470Smlaieripfw_chg_hook(SYSCTL_HANDLER_ARGS) 538158470Smlaier{ 539240099Smelifaro int newval; 540158470Smlaier int error; 541201122Sluigi int af; 542158470Smlaier 543263680Sglebius if (arg1 == &V_fw_enable) 544201122Sluigi af = AF_INET; 545197952Sjulian#ifdef INET6 546263680Sglebius else if (arg1 == &V_fw6_enable) 547201122Sluigi af = AF_INET6; 548196423Sjulian#endif 549263680Sglebius else if (arg1 == &V_fwlink_enable) 550240099Smelifaro af = AF_LINK; 551197952Sjulian else 552197952Sjulian return (EINVAL); 553197952Sjulian 554263680Sglebius newval = *(int *)arg1; 555240099Smelifaro /* Handle sysctl change */ 556240099Smelifaro error = sysctl_handle_int(oidp, &newval, 0, req); 557197952Sjulian 558158470Smlaier if (error) 559158470Smlaier return (error); 560158470Smlaier 561240099Smelifaro /* Formalize new value */ 562240099Smelifaro newval = (newval) ? 1 : 0; 563158470Smlaier 564263680Sglebius if (*(int *)arg1 == newval) 565158470Smlaier return (0); 566158470Smlaier 567240099Smelifaro error = ipfw_hook(newval, af); 568201122Sluigi if (error) 569201122Sluigi return (error); 570263680Sglebius *(int *)arg1 = newval; 571158470Smlaier 572158470Smlaier return (0); 573158470Smlaier} 574200601Sluigi/* end of file */ 575