ip_fw_pfil.c revision 135154
1133920Sandre/* 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 * $FreeBSD: head/sys/netinet/ip_fw_pfil.c 135154 2004-09-13 16:46:05Z andre $ 27133920Sandre */ 28133920Sandre 29134346Sru#if !defined(KLD_MODULE) 30133920Sandre#include "opt_ipfw.h" 31133920Sandre#include "opt_ipdn.h" 32133920Sandre#include "opt_ipdivert.h" 33133920Sandre#include "opt_inet.h" 34133920Sandre#ifndef INET 35133920Sandre#error IPFIREWALL requires INET. 36133920Sandre#endif /* INET */ 37134383Sandre#endif /* KLD_MODULE */ 38133920Sandre 39133920Sandre#include <sys/param.h> 40133920Sandre#include <sys/systm.h> 41133920Sandre#include <sys/malloc.h> 42133920Sandre#include <sys/mbuf.h> 43133920Sandre#include <sys/module.h> 44133920Sandre#include <sys/kernel.h> 45133920Sandre#include <sys/socket.h> 46133920Sandre#include <sys/socketvar.h> 47133920Sandre#include <sys/sysctl.h> 48133920Sandre#include <sys/ucred.h> 49133920Sandre 50133920Sandre#include <net/if.h> 51133920Sandre#include <net/route.h> 52133920Sandre#include <net/pfil.h> 53133920Sandre 54133920Sandre#include <netinet/in.h> 55133920Sandre#include <netinet/in_systm.h> 56133920Sandre#include <netinet/in_var.h> 57133920Sandre#include <netinet/ip.h> 58133920Sandre#include <netinet/ip_var.h> 59133920Sandre#include <netinet/ip_fw.h> 60133920Sandre#include <netinet/ip_divert.h> 61133920Sandre#include <netinet/ip_dummynet.h> 62133920Sandre 63133920Sandre#include <machine/in_cksum.h> 64133920Sandre 65133920Sandrestatic int ipfw_pfil_hooked = 0; 66133920Sandre 67133920Sandre/* Dummynet hooks. */ 68133920Sandreip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL; 69133920Sandre 70133920Sandre#define DIV_DIR_IN 1 71133920Sandre#define DIV_DIR_OUT 0 72133920Sandre 73133920Sandrestatic int ipfw_divert(struct mbuf **, int, int); 74133920Sandre 75133920Sandreint 76133920Sandreipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir) 77133920Sandre{ 78133920Sandre struct ip_fw_args args; 79133920Sandre struct m_tag *dn_tag; 80133920Sandre int ipfw = 0; 81133920Sandre int divert; 82133920Sandre#ifdef IPFIREWALL_FORWARD 83133920Sandre struct m_tag *fwd_tag; 84133920Sandre#endif 85133920Sandre 86133920Sandre KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!")); 87133920Sandre 88134022Sandre if (!fw_enable) 89134022Sandre goto pass; 90134022Sandre 91133920Sandre bzero(&args, sizeof(args)); 92133920Sandre 93133920Sandre dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL); 94133920Sandre if (dn_tag != NULL){ 95133920Sandre struct dn_pkt_tag *dt; 96133920Sandre 97133920Sandre dt = (struct dn_pkt_tag *)(dn_tag+1); 98133920Sandre args.rule = dt->rule; 99133920Sandre 100133920Sandre m_tag_delete(*m0, dn_tag); 101133920Sandre } 102133920Sandre 103135154Sandreagain: 104133920Sandre args.m = *m0; 105133920Sandre ipfw = ipfw_chk(&args); 106133920Sandre *m0 = args.m; 107133920Sandre 108133920Sandre if ((ipfw & IP_FW_PORT_DENY_FLAG) || *m0 == NULL) 109133920Sandre goto drop; 110133920Sandre 111133920Sandre if (ipfw == 0 && args.next_hop == NULL) 112133920Sandre goto pass; 113133920Sandre 114133920Sandre if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) { 115133920Sandre ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_IN, &args); 116133920Sandre *m0 = NULL; 117133920Sandre return 0; /* packet consumed */ 118133920Sandre } 119133920Sandre 120133920Sandre if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) { 121133920Sandre if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0) 122133920Sandre divert = ipfw_divert(m0, DIV_DIR_IN, 1); 123133920Sandre else 124133920Sandre divert = ipfw_divert(m0, DIV_DIR_IN, 0); 125133920Sandre 126133920Sandre /* tee should continue again with the firewall. */ 127133920Sandre if (divert) { 128133920Sandre *m0 = NULL; 129133920Sandre return 0; /* packet consumed */ 130133920Sandre } else 131135154Sandre goto again; /* continue with packet */ 132133920Sandre } 133133920Sandre 134133920Sandre#ifdef IPFIREWALL_FORWARD 135133920Sandre if (ipfw == 0 && args.next_hop != NULL) { 136133920Sandre fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, 137133920Sandre sizeof(struct sockaddr_in), M_NOWAIT); 138133920Sandre if (fwd_tag == NULL) 139133920Sandre goto drop; 140133920Sandre bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in)); 141133920Sandre m_tag_prepend(*m0, fwd_tag); 142133920Sandre 143133920Sandre if (in_localip(args.next_hop->sin_addr)) 144133920Sandre (*m0)->m_flags |= M_FASTFWD_OURS; 145133920Sandre goto pass; 146133920Sandre } 147133920Sandre#endif 148133920Sandre 149133920Sandredrop: 150133920Sandre if (*m0) 151133920Sandre m_freem(*m0); 152133920Sandre *m0 = NULL; 153133920Sandre return (EACCES); 154133920Sandrepass: 155133920Sandre return 0; /* not filtered */ 156133920Sandre} 157133920Sandre 158133920Sandreint 159133920Sandreipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir) 160133920Sandre{ 161133920Sandre struct ip_fw_args args; 162133920Sandre struct m_tag *dn_tag; 163133920Sandre int ipfw = 0; 164133920Sandre int divert; 165133920Sandre#ifdef IPFIREWALL_FORWARD 166133920Sandre struct m_tag *fwd_tag; 167133920Sandre#endif 168133920Sandre 169133920Sandre KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!")); 170133920Sandre 171134022Sandre if (!fw_enable) 172134022Sandre goto pass; 173134022Sandre 174133920Sandre bzero(&args, sizeof(args)); 175133920Sandre 176133920Sandre dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL); 177133920Sandre if (dn_tag != NULL) { 178133920Sandre struct dn_pkt_tag *dt; 179133920Sandre 180133920Sandre dt = (struct dn_pkt_tag *)(dn_tag+1); 181133920Sandre args.rule = dt->rule; 182133920Sandre 183133920Sandre m_tag_delete(*m0, dn_tag); 184133920Sandre } 185133920Sandre 186135154Sandreagain: 187133920Sandre args.m = *m0; 188133920Sandre args.oif = ifp; 189133920Sandre ipfw = ipfw_chk(&args); 190133920Sandre *m0 = args.m; 191133920Sandre 192133920Sandre if ((ipfw & IP_FW_PORT_DENY_FLAG) || *m0 == NULL) 193133920Sandre goto drop; 194133920Sandre 195133920Sandre if (ipfw == 0 && args.next_hop == NULL) 196133920Sandre goto pass; 197133920Sandre 198133920Sandre if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) { 199133920Sandre ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_OUT, &args); 200133920Sandre *m0 = NULL; 201133920Sandre return 0; /* packet consumed */ 202133920Sandre } 203133920Sandre 204133920Sandre if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) { 205133920Sandre if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0) 206133920Sandre divert = ipfw_divert(m0, DIV_DIR_OUT, 1); 207133920Sandre else 208133920Sandre divert = ipfw_divert(m0, DIV_DIR_OUT, 0); 209133920Sandre 210133920Sandre if (divert) { 211133920Sandre *m0 = NULL; 212133920Sandre return 0; /* packet consumed */ 213133920Sandre } else 214135154Sandre goto again; /* continue with packet */ 215133920Sandre } 216133920Sandre 217133920Sandre#ifdef IPFIREWALL_FORWARD 218133920Sandre if (ipfw == 0 && args.next_hop != NULL) { 219133920Sandre /* Overwrite existing tag. */ 220133920Sandre fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); 221133920Sandre if (fwd_tag == NULL) 222133920Sandre fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, 223133920Sandre sizeof(struct sockaddr_in), M_NOWAIT); 224133920Sandre if (fwd_tag == NULL) 225133920Sandre goto drop; 226133920Sandre bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in)); 227133920Sandre m_tag_prepend(*m0, fwd_tag); 228133920Sandre 229133920Sandre if (in_localip(args.next_hop->sin_addr)) 230133920Sandre (*m0)->m_flags |= M_FASTFWD_OURS; 231133920Sandre goto pass; 232133920Sandre } 233133920Sandre#endif 234133920Sandre 235133920Sandredrop: 236133920Sandre if (*m0) 237133920Sandre m_freem(*m0); 238133920Sandre *m0 = NULL; 239133920Sandre return (EACCES); 240133920Sandrepass: 241133920Sandre return 0; /* not filtered */ 242133920Sandre} 243133920Sandre 244133920Sandrestatic int 245133920Sandreipfw_divert(struct mbuf **m, int incoming, int tee) 246133920Sandre{ 247133920Sandre /* 248135154Sandre * ipfw_chk() has already tagged the packet with the divert tag. 249133920Sandre * If tee is set, copy packet and return original. 250133920Sandre * If not tee, consume packet and send it to divert socket. 251133920Sandre */ 252133920Sandre#ifdef IPDIVERT 253133920Sandre struct mbuf *clone, *reass; 254133920Sandre struct ip *ip; 255133920Sandre int hlen; 256133920Sandre 257133920Sandre reass = NULL; 258133920Sandre 259133920Sandre /* Cloning needed for tee? */ 260133920Sandre if (tee) 261133920Sandre clone = m_dup(*m, M_DONTWAIT); 262133920Sandre else 263133920Sandre clone = *m; 264133920Sandre 265133920Sandre /* In case m_dup was unable to allocate mbufs. */ 266133920Sandre if (clone == NULL) 267133920Sandre goto teeout; 268133920Sandre 269133920Sandre /* 270133920Sandre * Divert listeners can only handle non-fragmented packets. 271133920Sandre * However when tee is set we will *not* de-fragment the packets; 272133920Sandre * Doing do would put the reassembly into double-jeopardy. On top 273133920Sandre * of that someone doing a tee will probably want to get the packet 274133920Sandre * in its original form. 275133920Sandre */ 276133920Sandre ip = mtod(clone, struct ip *); 277133920Sandre if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) { 278133920Sandre 279133920Sandre /* Reassemble packet. */ 280133920Sandre reass = ip_reass(clone); 281133920Sandre 282133920Sandre /* 283133920Sandre * IP header checksum fixup after reassembly and leave header 284133920Sandre * in network byte order. 285133920Sandre */ 286133920Sandre if (reass != NULL) { 287133920Sandre ip = mtod(reass, struct ip *); 288133920Sandre hlen = ip->ip_hl << 2; 289133920Sandre ip->ip_len = htons(ip->ip_len); 290133920Sandre ip->ip_off = htons(ip->ip_off); 291133920Sandre ip->ip_sum = 0; 292133920Sandre if (hlen == sizeof(struct ip)) 293133920Sandre ip->ip_sum = in_cksum_hdr(ip); 294133920Sandre else 295133920Sandre ip->ip_sum = in_cksum(reass, hlen); 296133920Sandre clone = reass; 297133920Sandre } else 298133920Sandre clone = NULL; 299133920Sandre } else { 300133920Sandre /* Convert header to network byte order. */ 301133920Sandre ip->ip_len = htons(ip->ip_len); 302133920Sandre ip->ip_off = htons(ip->ip_off); 303133920Sandre } 304133920Sandre 305133920Sandre /* Do the dirty job... */ 306133920Sandre if (clone) 307133920Sandre divert_packet(clone, incoming); 308133920Sandre 309133920Sandreteeout: 310135154Sandre /* 311135154Sandre * For tee we leave the divert tag attached to original packet. 312135154Sandre * It will then continue rule evaluation after the tee rule. 313135154Sandre */ 314135154Sandre if (tee) 315135154Sandre return 0; 316133920Sandre 317133920Sandre /* Packet diverted and consumed */ 318133920Sandre return 1; 319133920Sandre#else 320133920Sandre m_freem(*m); 321133920Sandre return 1; 322133920Sandre#endif /* ipdivert */ 323133920Sandre} 324133920Sandre 325133920Sandrestatic int 326133920Sandreipfw_hook(void) 327133920Sandre{ 328133920Sandre struct pfil_head *pfh_inet; 329133920Sandre 330133920Sandre if (ipfw_pfil_hooked) 331133920Sandre return EEXIST; 332133920Sandre 333133920Sandre pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 334133920Sandre if (pfh_inet == NULL) 335133920Sandre return ENOENT; 336133920Sandre 337133920Sandre pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); 338133920Sandre pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); 339133920Sandre 340133920Sandre return 0; 341133920Sandre} 342133920Sandre 343133920Sandrestatic int 344133920Sandreipfw_unhook(void) 345133920Sandre{ 346133920Sandre struct pfil_head *pfh_inet; 347133920Sandre 348133920Sandre if (!ipfw_pfil_hooked) 349133920Sandre return ENOENT; 350133920Sandre 351133920Sandre pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 352133920Sandre if (pfh_inet == NULL) 353133920Sandre return ENOENT; 354133920Sandre 355133920Sandre pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); 356133920Sandre pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); 357133920Sandre 358133920Sandre return 0; 359133920Sandre} 360133920Sandre 361133920Sandrestatic int 362133920Sandreipfw_modevent(module_t mod, int type, void *unused) 363133920Sandre{ 364133920Sandre int err = 0; 365133920Sandre 366133920Sandre switch (type) { 367133920Sandre case MOD_LOAD: 368133920Sandre if (ipfw_pfil_hooked) { 369133920Sandre printf("IP firewall already loaded\n"); 370133920Sandre err = EEXIST; 371133920Sandre } else { 372133920Sandre if ((err = ipfw_init()) != 0) { 373133920Sandre printf("ipfw_init() error\n"); 374133920Sandre break; 375133920Sandre } 376133920Sandre if ((err = ipfw_hook()) != 0) { 377133920Sandre printf("ipfw_hook() error\n"); 378133920Sandre break; 379133920Sandre } 380133920Sandre ipfw_pfil_hooked = 1; 381133920Sandre } 382133920Sandre break; 383133920Sandre 384133920Sandre case MOD_UNLOAD: 385133920Sandre if (ipfw_pfil_hooked) { 386134055Sandre if ((err = ipfw_unhook()) > 0) 387133920Sandre break; 388133920Sandre ipfw_destroy(); 389133920Sandre ipfw_pfil_hooked = 0; 390133920Sandre } else { 391133920Sandre printf("IP firewall already unloaded\n"); 392133920Sandre } 393133920Sandre break; 394133920Sandre 395133920Sandre default: 396133920Sandre return EOPNOTSUPP; 397133920Sandre break; 398133920Sandre } 399133920Sandre return err; 400133920Sandre} 401133920Sandre 402133920Sandrestatic moduledata_t ipfwmod = { 403133920Sandre "ipfw", 404133920Sandre ipfw_modevent, 405133920Sandre 0 406133920Sandre}; 407133920SandreDECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); 408133920SandreMODULE_VERSION(ipfw, 2); 409