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