ip_fw_pfil.c revision 135167
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 * $FreeBSD: head/sys/netinet/ip_fw_pfil.c 135167 2004-09-13 19:20:14Z andre $ 27 */ 28 29#if !defined(KLD_MODULE) 30#include "opt_ipfw.h" 31#include "opt_ipdn.h" 32#include "opt_ipdivert.h" 33#include "opt_inet.h" 34#ifndef INET 35#error IPFIREWALL requires INET. 36#endif /* INET */ 37#endif /* KLD_MODULE */ 38 39#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/malloc.h> 42#include <sys/mbuf.h> 43#include <sys/module.h> 44#include <sys/kernel.h> 45#include <sys/socket.h> 46#include <sys/socketvar.h> 47#include <sys/sysctl.h> 48#include <sys/ucred.h> 49 50#include <net/if.h> 51#include <net/route.h> 52#include <net/pfil.h> 53 54#include <netinet/in.h> 55#include <netinet/in_systm.h> 56#include <netinet/in_var.h> 57#include <netinet/ip.h> 58#include <netinet/ip_var.h> 59#include <netinet/ip_fw.h> 60#include <netinet/ip_divert.h> 61#include <netinet/ip_dummynet.h> 62 63#include <machine/in_cksum.h> 64 65static int ipfw_pfil_hooked = 0; 66 67/* Dummynet hooks. */ 68ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL; 69 70#define DIV_DIR_IN 1 71#define DIV_DIR_OUT 0 72 73static int ipfw_divert(struct mbuf **, int, int); 74 75int 76ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir) 77{ 78 struct ip_fw_args args; 79 struct m_tag *dn_tag; 80 int ipfw = 0; 81 int divert; 82#ifdef IPFIREWALL_FORWARD 83 struct m_tag *fwd_tag; 84#endif 85 86 KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!")); 87 88 if (!fw_enable) 89 goto pass; 90 91 bzero(&args, sizeof(args)); 92 93 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL); 94 if (dn_tag != NULL){ 95 struct dn_pkt_tag *dt; 96 97 dt = (struct dn_pkt_tag *)(dn_tag+1); 98 args.rule = dt->rule; 99 100 m_tag_delete(*m0, dn_tag); 101 } 102 103again: 104 args.m = *m0; 105 ipfw = ipfw_chk(&args); 106 *m0 = args.m; 107 108 if ((ipfw & IP_FW_PORT_DENY_FLAG) || *m0 == NULL) 109 goto drop; 110 111 if (ipfw == 0 && args.next_hop == NULL) 112 goto pass; 113 114 if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) { 115 ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_IN, &args); 116 *m0 = NULL; 117 return 0; /* packet consumed */ 118 } 119 120 if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) { 121 if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0) 122 divert = ipfw_divert(m0, DIV_DIR_IN, 1); 123 else 124 divert = ipfw_divert(m0, DIV_DIR_IN, 0); 125 126 /* tee should continue again with the firewall. */ 127 if (divert) { 128 *m0 = NULL; 129 return 0; /* packet consumed */ 130 } else 131 goto again; /* continue with packet */ 132 } 133 134#ifdef IPFIREWALL_FORWARD 135 if (ipfw == 0 && args.next_hop != NULL) { 136 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, 137 sizeof(struct sockaddr_in), M_NOWAIT); 138 if (fwd_tag == NULL) 139 goto drop; 140 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in)); 141 m_tag_prepend(*m0, fwd_tag); 142 143 if (in_localip(args.next_hop->sin_addr)) 144 (*m0)->m_flags |= M_FASTFWD_OURS; 145 goto pass; 146 } 147#endif 148 149drop: 150 if (*m0) 151 m_freem(*m0); 152 *m0 = NULL; 153 return (EACCES); 154pass: 155 return 0; /* not filtered */ 156} 157 158int 159ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir) 160{ 161 struct ip_fw_args args; 162 struct m_tag *dn_tag; 163 int ipfw = 0; 164 int divert; 165#ifdef IPFIREWALL_FORWARD 166 struct m_tag *fwd_tag; 167#endif 168 169 KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!")); 170 171 if (!fw_enable) 172 goto pass; 173 174 bzero(&args, sizeof(args)); 175 176 dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL); 177 if (dn_tag != NULL) { 178 struct dn_pkt_tag *dt; 179 180 dt = (struct dn_pkt_tag *)(dn_tag+1); 181 args.rule = dt->rule; 182 183 m_tag_delete(*m0, dn_tag); 184 } 185 186again: 187 args.m = *m0; 188 args.oif = ifp; 189 ipfw = ipfw_chk(&args); 190 *m0 = args.m; 191 192 if ((ipfw & IP_FW_PORT_DENY_FLAG) || *m0 == NULL) 193 goto drop; 194 195 if (ipfw == 0 && args.next_hop == NULL) 196 goto pass; 197 198 if (DUMMYNET_LOADED && (ipfw & IP_FW_PORT_DYNT_FLAG) != 0) { 199 ip_dn_io_ptr(*m0, ipfw & 0xffff, DN_TO_IP_OUT, &args); 200 *m0 = NULL; 201 return 0; /* packet consumed */ 202 } 203 204 if (ipfw != 0 && (ipfw & IP_FW_PORT_DYNT_FLAG) == 0) { 205 if ((ipfw & IP_FW_PORT_TEE_FLAG) != 0) 206 divert = ipfw_divert(m0, DIV_DIR_OUT, 1); 207 else 208 divert = ipfw_divert(m0, DIV_DIR_OUT, 0); 209 210 if (divert) { 211 *m0 = NULL; 212 return 0; /* packet consumed */ 213 } else 214 goto again; /* continue with packet */ 215 } 216 217#ifdef IPFIREWALL_FORWARD 218 if (ipfw == 0 && args.next_hop != NULL) { 219 /* Overwrite existing tag. */ 220 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); 221 if (fwd_tag == NULL) { 222 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, 223 sizeof(struct sockaddr_in), M_NOWAIT); 224 if (fwd_tag == NULL) 225 goto drop; 226 } else 227 m_tag_unlink(*m0, fwd_tag); 228 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in)); 229 m_tag_prepend(*m0, fwd_tag); 230 231 if (in_localip(args.next_hop->sin_addr)) 232 (*m0)->m_flags |= M_FASTFWD_OURS; 233 goto pass; 234 } 235#endif 236 237drop: 238 if (*m0) 239 m_freem(*m0); 240 *m0 = NULL; 241 return (EACCES); 242pass: 243 return 0; /* not filtered */ 244} 245 246static int 247ipfw_divert(struct mbuf **m, int incoming, int tee) 248{ 249 /* 250 * ipfw_chk() has already tagged the packet with the divert tag. 251 * If tee is set, copy packet and return original. 252 * If not tee, consume packet and send it to divert socket. 253 */ 254#ifdef IPDIVERT 255 struct mbuf *clone, *reass; 256 struct ip *ip; 257 int hlen; 258 259 reass = NULL; 260 261 /* Cloning needed for tee? */ 262 if (tee) 263 clone = m_dup(*m, M_DONTWAIT); 264 else 265 clone = *m; 266 267 /* In case m_dup was unable to allocate mbufs. */ 268 if (clone == NULL) 269 goto teeout; 270 271 /* 272 * Divert listeners can only handle non-fragmented packets. 273 * However when tee is set we will *not* de-fragment the packets; 274 * Doing do would put the reassembly into double-jeopardy. On top 275 * of that someone doing a tee will probably want to get the packet 276 * in its original form. 277 */ 278 ip = mtod(clone, struct ip *); 279 if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) { 280 281 /* Reassemble packet. */ 282 reass = ip_reass(clone); 283 284 /* 285 * IP header checksum fixup after reassembly and leave header 286 * in network byte order. 287 */ 288 if (reass != NULL) { 289 ip = mtod(reass, struct ip *); 290 hlen = ip->ip_hl << 2; 291 ip->ip_len = htons(ip->ip_len); 292 ip->ip_off = htons(ip->ip_off); 293 ip->ip_sum = 0; 294 if (hlen == sizeof(struct ip)) 295 ip->ip_sum = in_cksum_hdr(ip); 296 else 297 ip->ip_sum = in_cksum(reass, hlen); 298 clone = reass; 299 } else 300 clone = NULL; 301 } else { 302 /* Convert header to network byte order. */ 303 ip->ip_len = htons(ip->ip_len); 304 ip->ip_off = htons(ip->ip_off); 305 } 306 307 /* Do the dirty job... */ 308 if (clone) 309 divert_packet(clone, incoming); 310 311teeout: 312 /* 313 * For tee we leave the divert tag attached to original packet. 314 * It will then continue rule evaluation after the tee rule. 315 */ 316 if (tee) 317 return 0; 318 319 /* Packet diverted and consumed */ 320 return 1; 321#else 322 m_freem(*m); 323 return 1; 324#endif /* ipdivert */ 325} 326 327static int 328ipfw_hook(void) 329{ 330 struct pfil_head *pfh_inet; 331 332 if (ipfw_pfil_hooked) 333 return EEXIST; 334 335 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 336 if (pfh_inet == NULL) 337 return ENOENT; 338 339 pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); 340 pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); 341 342 return 0; 343} 344 345static int 346ipfw_unhook(void) 347{ 348 struct pfil_head *pfh_inet; 349 350 if (!ipfw_pfil_hooked) 351 return ENOENT; 352 353 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 354 if (pfh_inet == NULL) 355 return ENOENT; 356 357 pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); 358 pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); 359 360 return 0; 361} 362 363static int 364ipfw_modevent(module_t mod, int type, void *unused) 365{ 366 int err = 0; 367 368 switch (type) { 369 case MOD_LOAD: 370 if (ipfw_pfil_hooked) { 371 printf("IP firewall already loaded\n"); 372 err = EEXIST; 373 } else { 374 if ((err = ipfw_init()) != 0) { 375 printf("ipfw_init() error\n"); 376 break; 377 } 378 if ((err = ipfw_hook()) != 0) { 379 printf("ipfw_hook() error\n"); 380 break; 381 } 382 ipfw_pfil_hooked = 1; 383 } 384 break; 385 386 case MOD_UNLOAD: 387 if (ipfw_pfil_hooked) { 388 if ((err = ipfw_unhook()) > 0) 389 break; 390 ipfw_destroy(); 391 ipfw_pfil_hooked = 0; 392 } else { 393 printf("IP firewall already unloaded\n"); 394 } 395 break; 396 397 default: 398 return EOPNOTSUPP; 399 break; 400 } 401 return err; 402} 403 404static moduledata_t ipfwmod = { 405 "ipfw", 406 ipfw_modevent, 407 0 408}; 409DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); 410MODULE_VERSION(ipfw, 2); 411