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