ip_fw_pfil.c revision 241359
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: head/sys/netpfil/ipfw/ip_fw_pfil.c 241359 2012-10-08 22:58:28Z glebius $"); 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 */ 162225793Sbz#if !defined(IPFIREWALL_FORWARD) || (!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; 202225044Sbz } 203225044Sbz#endif 204225044Sbz#ifdef INET 205225044Sbz if (args.next_hop != NULL) { 206225044Sbz bcopy(args.next_hop, (fwd_tag+1), len); 207225044Sbz if (in_localip(args.next_hop->sin_addr)) 208225044Sbz (*m0)->m_flags |= M_FASTFWD_OURS; 209225044Sbz } 210225044Sbz#endif 211133920Sandre m_tag_prepend(*m0, fwd_tag); 212201527Sluigi } 213225793Sbz#endif /* IPFIREWALL_FORWARD */ 214201122Sluigi break; 215133920Sandre 216140224Sglebius case IP_FW_DENY: 217201122Sluigi ret = EACCES; 218201122Sluigi break; /* i.e. drop */ 219140224Sglebius 220140224Sglebius case IP_FW_DUMMYNET: 221201122Sluigi ret = EACCES; 222193502Sluigi if (ip_dn_io_ptr == NULL) 223201122Sluigi break; /* i.e. drop */ 224145246Sbrooks if (mtod(*m0, struct ip *)->ip_v == 4) 225201122Sluigi ret = ip_dn_io_ptr(m0, dir, &args); 226145246Sbrooks else if (mtod(*m0, struct ip *)->ip_v == 6) 227201122Sluigi ret = ip_dn_io_ptr(m0, dir | PROTO_IPV6, &args); 228201122Sluigi else 229201122Sluigi break; /* drop it */ 230201122Sluigi /* 231201122Sluigi * XXX should read the return value. 232201122Sluigi * dummynet normally eats the packet and sets *m0=NULL 233201122Sluigi * unless the packet can be sent immediately. In this 234201122Sluigi * case args is updated and we should re-run the 235201122Sluigi * check without clearing args. 236201122Sluigi */ 237173399Soleg if (*m0 != NULL) 238173399Soleg goto again; 239140224Sglebius break; 240140224Sglebius 241140224Sglebius case IP_FW_TEE: 242140224Sglebius case IP_FW_DIVERT: 243201122Sluigi if (ip_divert_ptr == NULL) { 244201122Sluigi ret = EACCES; 245201122Sluigi break; /* i.e. drop */ 246201122Sluigi } 247201527Sluigi ret = ipfw_divert(m0, dir, &args.rule, 248201527Sluigi (ipfw == IP_FW_TEE) ? 1 : 0); 249201527Sluigi /* continue processing for the original packet (tee). */ 250201527Sluigi if (*m0) 251201527Sluigi goto again; 252201122Sluigi break; 253140224Sglebius 254141351Sglebius case IP_FW_NGTEE: 255141351Sglebius case IP_FW_NETGRAPH: 256201732Sluigi if (ng_ipfw_input_p == NULL) { 257201122Sluigi ret = EACCES; 258201122Sluigi break; /* i.e. drop */ 259201122Sluigi } 260201122Sluigi ret = ng_ipfw_input_p(m0, dir, &args, 261201122Sluigi (ipfw == IP_FW_NGTEE) ? 1 : 0); 262201122Sluigi if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */ 263201122Sluigi goto again; /* continue with packet */ 264201122Sluigi break; 265141351Sglebius 266165648Spiso case IP_FW_NAT: 267213254Sluigi /* honor one-pass in case of successful nat */ 268213254Sluigi if (V_fw_one_pass) 269213254Sluigi break; /* ret is already 0 */ 270213254Sluigi goto again; 271213254Sluigi 272201122Sluigi case IP_FW_REASS: 273165648Spiso goto again; /* continue with packet */ 274190633Spiso 275140224Sglebius default: 276140224Sglebius KASSERT(0, ("%s: unknown retval", __func__)); 277140224Sglebius } 278140224Sglebius 279201122Sluigi if (ret != 0) { 280201122Sluigi if (*m0) 281201527Sluigi FREE_PKT(*m0); 282201122Sluigi *m0 = NULL; 283201122Sluigi } 284241245Sglebius 285201122Sluigi return ret; 286133920Sandre} 287133920Sandre 288240099Smelifaro/* 289240099Smelifaro * ipfw processing for ethernet packets (in and out). 290240099Smelifaro * Inteface is NULL from ether_demux, and ifp from 291240099Smelifaro * ether_output_frame. 292240099Smelifaro */ 293240099Smelifarostatic int 294240099Smelifaroipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir, 295240099Smelifaro struct inpcb *inp) 296240099Smelifaro{ 297240099Smelifaro struct ether_header *eh; 298240099Smelifaro struct ether_header save_eh; 299240099Smelifaro struct mbuf *m; 300240099Smelifaro int i, ret; 301240099Smelifaro struct ip_fw_args args; 302240099Smelifaro struct m_tag *mtag; 303240099Smelifaro 304240099Smelifaro /* fetch start point from rule, if any */ 305240099Smelifaro mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); 306240099Smelifaro if (mtag == NULL) { 307240099Smelifaro args.rule.slot = 0; 308240099Smelifaro } else { 309240099Smelifaro /* dummynet packet, already partially processed */ 310240099Smelifaro struct ipfw_rule_ref *r; 311240099Smelifaro 312240099Smelifaro /* XXX can we free it after use ? */ 313240099Smelifaro mtag->m_tag_id = PACKET_TAG_NONE; 314240099Smelifaro r = (struct ipfw_rule_ref *)(mtag + 1); 315240099Smelifaro if (r->info & IPFW_ONEPASS) 316240099Smelifaro return (0); 317240099Smelifaro args.rule = *r; 318240099Smelifaro } 319240099Smelifaro 320240099Smelifaro /* I need some amt of data to be contiguous */ 321240099Smelifaro m = *m0; 322240099Smelifaro i = min(m->m_pkthdr.len, max_protohdr); 323240099Smelifaro if (m->m_len < i) { 324240099Smelifaro m = m_pullup(m, i); 325240099Smelifaro if (m == NULL) { 326240099Smelifaro *m0 = m; 327240099Smelifaro return (0); 328240099Smelifaro } 329240099Smelifaro } 330240099Smelifaro eh = mtod(m, struct ether_header *); 331240099Smelifaro save_eh = *eh; /* save copy for restore below */ 332240099Smelifaro m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ 333240099Smelifaro 334240099Smelifaro args.m = m; /* the packet we are looking at */ 335240099Smelifaro args.oif = dst; /* destination, if any */ 336240099Smelifaro args.next_hop = NULL; /* we do not support forward yet */ 337240099Smelifaro args.next_hop6 = NULL; /* we do not support forward yet */ 338240099Smelifaro args.eh = &save_eh; /* MAC header for bridged/MAC packets */ 339240099Smelifaro args.inp = NULL; /* used by ipfw uid/gid/jail rules */ 340240099Smelifaro i = ipfw_chk(&args); 341240099Smelifaro m = args.m; 342240099Smelifaro if (m != NULL) { 343240099Smelifaro /* 344240099Smelifaro * Restore Ethernet header, as needed, in case the 345240099Smelifaro * mbuf chain was replaced by ipfw. 346240099Smelifaro */ 347240099Smelifaro M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); 348240099Smelifaro if (m == NULL) { 349240099Smelifaro *m0 = NULL; 350240099Smelifaro return (0); 351240099Smelifaro } 352240099Smelifaro if (eh != mtod(m, struct ether_header *)) 353240099Smelifaro bcopy(&save_eh, mtod(m, struct ether_header *), 354240099Smelifaro ETHER_HDR_LEN); 355240099Smelifaro } 356240099Smelifaro *m0 = m; 357240099Smelifaro 358240099Smelifaro ret = 0; 359240099Smelifaro /* Check result of ipfw_chk() */ 360240099Smelifaro switch (i) { 361240099Smelifaro case IP_FW_PASS: 362240099Smelifaro break; 363240099Smelifaro 364240099Smelifaro case IP_FW_DENY: 365240099Smelifaro ret = EACCES; 366240099Smelifaro break; /* i.e. drop */ 367240099Smelifaro 368240099Smelifaro case IP_FW_DUMMYNET: 369240099Smelifaro ret = EACCES; 370240099Smelifaro int dir; 371240099Smelifaro 372240099Smelifaro if (ip_dn_io_ptr == NULL) 373240099Smelifaro break; /* i.e. drop */ 374240099Smelifaro 375240099Smelifaro *m0 = NULL; 376240099Smelifaro dir = PROTO_LAYER2 | (dst ? DIR_OUT : DIR_IN); 377240099Smelifaro ip_dn_io_ptr(&m, dir, &args); 378240099Smelifaro return 0; 379240099Smelifaro 380240099Smelifaro default: 381240099Smelifaro KASSERT(0, ("%s: unknown retval", __func__)); 382240099Smelifaro } 383240099Smelifaro 384240099Smelifaro if (ret != 0) { 385240099Smelifaro if (*m0) 386240099Smelifaro FREE_PKT(*m0); 387240099Smelifaro *m0 = NULL; 388240099Smelifaro } 389240099Smelifaro 390240099Smelifaro return ret; 391240099Smelifaro} 392240099Smelifaro 393201527Sluigi/* do the divert, return 1 on error 0 on success */ 394201527Sluigistatic int 395201527Sluigiipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule, 396201527Sluigi int tee) 397133920Sandre{ 398133920Sandre /* 399135154Sandre * ipfw_chk() has already tagged the packet with the divert tag. 400133920Sandre * If tee is set, copy packet and return original. 401133920Sandre * If not tee, consume packet and send it to divert socket. 402133920Sandre */ 403201122Sluigi struct mbuf *clone; 404223593Sglebius struct ip *ip = mtod(*m0, struct ip *); 405201527Sluigi struct m_tag *tag; 406133920Sandre 407133920Sandre /* Cloning needed for tee? */ 408201122Sluigi if (tee == 0) { 409201122Sluigi clone = *m0; /* use the original mbuf */ 410201122Sluigi *m0 = NULL; 411201122Sluigi } else { 412201122Sluigi clone = m_dup(*m0, M_DONTWAIT); 413201122Sluigi /* If we cannot duplicate the mbuf, we sacrifice the divert 414201122Sluigi * chain and continue with the tee-ed packet. 415201122Sluigi */ 416201122Sluigi if (clone == NULL) 417201527Sluigi return 1; 418201122Sluigi } 419133920Sandre 420133920Sandre /* 421201122Sluigi * Divert listeners can normally handle non-fragmented packets, 422201122Sluigi * but we can only reass in the non-tee case. 423201122Sluigi * This means that listeners on a tee rule may get fragments, 424201122Sluigi * and have to live with that. 425201122Sluigi * Note that we now have the 'reass' ipfw option so if we care 426201122Sluigi * we can do it before a 'tee'. 427133920Sandre */ 428223593Sglebius if (!tee) switch (ip->ip_v) { 429223593Sglebius case IPVERSION: 430223593Sglebius if (ntohs(ip->ip_off) & (IP_MF | IP_OFFMASK)) { 431201122Sluigi int hlen; 432201122Sluigi struct mbuf *reass; 433133920Sandre 434201527Sluigi SET_HOST_IPLEN(ip); /* ip_reass wants host order */ 435201122Sluigi reass = ip_reass(clone); /* Reassemble packet. */ 436201122Sluigi if (reass == NULL) 437201527Sluigi return 0; /* not an error */ 438201122Sluigi /* if reass = NULL then it was consumed by ip_reass */ 439133920Sandre /* 440133920Sandre * IP header checksum fixup after reassembly and leave header 441133920Sandre * in network byte order. 442133920Sandre */ 443201122Sluigi ip = mtod(reass, struct ip *); 444201122Sluigi hlen = ip->ip_hl << 2; 445201122Sluigi SET_NET_IPLEN(ip); 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 *enable; 540240099Smelifaro int newval; 541158470Smlaier int error; 542201122Sluigi int af; 543158470Smlaier 544197952Sjulian if (arg1 == &VNET_NAME(fw_enable)) { 545240099Smelifaro enable = &V_fw_enable; 546201122Sluigi af = AF_INET; 547197952Sjulian } 548197952Sjulian#ifdef INET6 549197952Sjulian else if (arg1 == &VNET_NAME(fw6_enable)) { 550240099Smelifaro enable = &V_fw6_enable; 551201122Sluigi af = AF_INET6; 552197952Sjulian } 553196423Sjulian#endif 554240099Smelifaro else if (arg1 == &VNET_NAME(fwlink_enable)) { 555240099Smelifaro enable = &V_fwlink_enable; 556240099Smelifaro af = AF_LINK; 557240099Smelifaro } 558197952Sjulian else 559197952Sjulian return (EINVAL); 560197952Sjulian 561240099Smelifaro newval = *enable; 562197952Sjulian 563240099Smelifaro /* Handle sysctl change */ 564240099Smelifaro error = sysctl_handle_int(oidp, &newval, 0, req); 565197952Sjulian 566158470Smlaier if (error) 567158470Smlaier return (error); 568158470Smlaier 569240099Smelifaro /* Formalize new value */ 570240099Smelifaro newval = (newval) ? 1 : 0; 571158470Smlaier 572240099Smelifaro if (*enable == newval) 573158470Smlaier return (0); 574158470Smlaier 575240099Smelifaro error = ipfw_hook(newval, af); 576201122Sluigi if (error) 577201122Sluigi return (error); 578240099Smelifaro *enable = newval; 579158470Smlaier 580158470Smlaier return (0); 581158470Smlaier} 582200601Sluigi/* end of file */ 583