The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet/ip_fw_pfil.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 #if !defined(KLD_MODULE)
   31 #include "opt_ipfw.h"
   32 #include "opt_ipdn.h"
   33 #include "opt_inet.h"
   34 #ifndef INET
   35 #error IPFIREWALL requires INET.
   36 #endif /* INET */
   37 #endif /* KLD_MODULE */
   38 #include "opt_inet6.h"
   39 
   40 #include <sys/param.h>
   41 #include <sys/systm.h>
   42 #include <sys/malloc.h>
   43 #include <sys/mbuf.h>
   44 #include <sys/module.h>
   45 #include <sys/kernel.h>
   46 #include <sys/socket.h>
   47 #include <sys/socketvar.h>
   48 #include <sys/sysctl.h>
   49 #include <sys/ucred.h>
   50 
   51 #include <net/if.h>
   52 #include <net/route.h>
   53 #include <net/pfil.h>
   54 
   55 #include <netinet/in.h>
   56 #include <netinet/in_systm.h>
   57 #include <netinet/in_var.h>
   58 #include <netinet/ip.h>
   59 #include <netinet/ip_var.h>
   60 #include <netinet/ip_fw.h>
   61 #include <netinet/ip_divert.h>
   62 #include <netinet/ip_dummynet.h>
   63 
   64 #include <netgraph/ng_ipfw.h>
   65 
   66 #include <machine/in_cksum.h>
   67 
   68 int fw_enable = 1;
   69 #ifdef INET6
   70 int fw6_enable = 1;
   71 #endif
   72 
   73 int ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
   74 
   75 /* Dummynet hooks. */
   76 ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL;
   77 
   78 /* Divert hooks. */
   79 ip_divert_packet_t *ip_divert_ptr = NULL;
   80 
   81 /* ng_ipfw hooks. */
   82 ng_ipfw_input_t *ng_ipfw_input_p = NULL;
   83 
   84 /* Forward declarations. */
   85 static int      ipfw_divert(struct mbuf **, int, int);
   86 #define DIV_DIR_IN      1
   87 #define DIV_DIR_OUT     0
   88 
   89 int
   90 ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
   91     struct inpcb *inp)
   92 {
   93         struct ip_fw_args args;
   94         struct ng_ipfw_tag *ng_tag;
   95         struct m_tag *dn_tag;
   96         int ipfw = 0;
   97         int divert;
   98         int tee;
   99 #ifdef IPFIREWALL_FORWARD
  100         struct m_tag *fwd_tag;
  101 #endif
  102 
  103         KASSERT(dir == PFIL_IN, ("ipfw_check_in wrong direction!"));
  104 
  105         bzero(&args, sizeof(args));
  106 
  107         dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
  108         if (dn_tag != NULL){
  109                 struct dn_pkt_tag *dt;
  110 
  111                 dt = (struct dn_pkt_tag *)(dn_tag+1);
  112                 args.rule = dt->rule;
  113 
  114                 m_tag_delete(*m0, dn_tag);
  115         }
  116 
  117         ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
  118             NULL);
  119         if (ng_tag != NULL) {
  120                 KASSERT(ng_tag->dir == NG_IPFW_IN,
  121                     ("ng_ipfw tag with wrong direction"));
  122                 args.rule = ng_tag->rule;
  123                 m_tag_delete(*m0, (struct m_tag *)ng_tag);
  124         }
  125 
  126 again:
  127         args.m = *m0;
  128         args.inp = inp;
  129         ipfw = ipfw_chk(&args);
  130         *m0 = args.m;
  131         tee = 0;
  132 
  133         KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
  134             __func__));
  135 
  136         switch (ipfw) {
  137         case IP_FW_PASS:
  138                 if (args.next_hop == NULL)
  139                         goto pass;
  140 
  141 #ifdef IPFIREWALL_FORWARD
  142                 fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
  143                                 sizeof(struct sockaddr_in), M_NOWAIT);
  144                 if (fwd_tag == NULL)
  145                         goto drop;
  146                 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
  147                 m_tag_prepend(*m0, fwd_tag);
  148 
  149                 if (in_localip(args.next_hop->sin_addr))
  150                         (*m0)->m_flags |= M_FASTFWD_OURS;
  151                 goto pass;
  152 #endif
  153                 break;                  /* not reached */
  154 
  155         case IP_FW_DENY:
  156                 goto drop;
  157                 break;                  /* not reached */
  158 
  159         case IP_FW_DUMMYNET:
  160                 if (!DUMMYNET_LOADED)
  161                         goto drop;
  162                 if (mtod(*m0, struct ip *)->ip_v == 4)
  163                         ip_dn_io_ptr(*m0, DN_TO_IP_IN, &args);
  164                 else if (mtod(*m0, struct ip *)->ip_v == 6)
  165                         ip_dn_io_ptr(*m0, DN_TO_IP6_IN, &args);
  166                 *m0 = NULL;
  167                 return 0;               /* packet consumed */
  168 
  169         case IP_FW_TEE:
  170                 tee = 1;
  171                 /* fall through */
  172 
  173         case IP_FW_DIVERT:
  174                 divert = ipfw_divert(m0, DIV_DIR_IN, tee);
  175                 if (divert) {
  176                         *m0 = NULL;
  177                         return 0;       /* packet consumed */
  178                 } else {
  179                         args.rule = NULL;
  180                         goto again;     /* continue with packet */
  181                 }
  182 
  183         case IP_FW_NGTEE:
  184                 if (!NG_IPFW_LOADED)
  185                         goto drop;
  186                 (void)ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 1);
  187                 goto again;             /* continue with packet */
  188 
  189         case IP_FW_NETGRAPH:
  190                 if (!NG_IPFW_LOADED)
  191                         goto drop;
  192                 return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0);
  193                 
  194         case IP_FW_NAT:
  195                 goto again;             /* continue with packet */
  196 
  197         default:
  198                 KASSERT(0, ("%s: unknown retval", __func__));
  199         }
  200 
  201 drop:
  202         if (*m0)
  203                 m_freem(*m0);
  204         *m0 = NULL;
  205         return (EACCES);
  206 pass:
  207         return 0;       /* not filtered */
  208 }
  209 
  210 int
  211 ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
  212     struct inpcb *inp)
  213 {
  214         struct ip_fw_args args;
  215         struct ng_ipfw_tag *ng_tag;
  216         struct m_tag *dn_tag;
  217         int ipfw = 0;
  218         int divert;
  219         int tee;
  220 #ifdef IPFIREWALL_FORWARD
  221         struct m_tag *fwd_tag;
  222 #endif
  223 
  224         KASSERT(dir == PFIL_OUT, ("ipfw_check_out wrong direction!"));
  225 
  226         bzero(&args, sizeof(args));
  227 
  228         dn_tag = m_tag_find(*m0, PACKET_TAG_DUMMYNET, NULL);
  229         if (dn_tag != NULL) {
  230                 struct dn_pkt_tag *dt;
  231 
  232                 dt = (struct dn_pkt_tag *)(dn_tag+1);
  233                 args.rule = dt->rule;
  234 
  235                 m_tag_delete(*m0, dn_tag);
  236         }
  237 
  238         ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
  239             NULL);
  240         if (ng_tag != NULL) {
  241                 KASSERT(ng_tag->dir == NG_IPFW_OUT,
  242                     ("ng_ipfw tag with wrong direction"));
  243                 args.rule = ng_tag->rule;
  244                 m_tag_delete(*m0, (struct m_tag *)ng_tag);
  245         }
  246 
  247 again:
  248         args.m = *m0;
  249         args.oif = ifp;
  250         args.inp = inp;
  251         ipfw = ipfw_chk(&args);
  252         *m0 = args.m;
  253         tee = 0;
  254 
  255         KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL",
  256             __func__));
  257 
  258         switch (ipfw) {
  259         case IP_FW_PASS:
  260                 if (args.next_hop == NULL)
  261                         goto pass;
  262 #ifdef IPFIREWALL_FORWARD
  263                 /* Overwrite existing tag. */
  264                 fwd_tag = m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL);
  265                 if (fwd_tag == NULL) {
  266                         fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
  267                                 sizeof(struct sockaddr_in), M_NOWAIT);
  268                         if (fwd_tag == NULL)
  269                                 goto drop;
  270                 } else
  271                         m_tag_unlink(*m0, fwd_tag);
  272                 bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
  273                 m_tag_prepend(*m0, fwd_tag);
  274 
  275                 if (in_localip(args.next_hop->sin_addr))
  276                         (*m0)->m_flags |= M_FASTFWD_OURS;
  277                 goto pass;
  278 #endif
  279                 break;                  /* not reached */
  280 
  281         case IP_FW_DENY:
  282                 goto drop;
  283                 break;                  /* not reached */
  284 
  285         case IP_FW_DUMMYNET:
  286                 if (!DUMMYNET_LOADED)
  287                         break;
  288                 if (mtod(*m0, struct ip *)->ip_v == 4)
  289                         ip_dn_io_ptr(*m0, DN_TO_IP_OUT, &args);
  290                 else if (mtod(*m0, struct ip *)->ip_v == 6)
  291                         ip_dn_io_ptr(*m0, DN_TO_IP6_OUT, &args);
  292                 *m0 = NULL;
  293                 return 0;               /* packet consumed */
  294 
  295                 break;
  296 
  297         case IP_FW_TEE:
  298                 tee = 1;
  299                 /* fall through */
  300 
  301         case IP_FW_DIVERT:
  302                 divert = ipfw_divert(m0, DIV_DIR_OUT, tee);
  303                 if (divert) {
  304                         *m0 = NULL;
  305                         return 0;       /* packet consumed */
  306                 } else {
  307                         args.rule = NULL;
  308                         goto again;     /* continue with packet */
  309                 }
  310 
  311         case IP_FW_NGTEE:
  312                 if (!NG_IPFW_LOADED)
  313                         goto drop;
  314                 (void)ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 1);
  315                 goto again;             /* continue with packet */
  316 
  317         case IP_FW_NETGRAPH:
  318                 if (!NG_IPFW_LOADED)
  319                         goto drop;
  320                 return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0);
  321 
  322         case IP_FW_NAT:
  323                 goto again;             /* continue with packet */
  324                 
  325         default:
  326                 KASSERT(0, ("%s: unknown retval", __func__));
  327         }
  328 
  329 drop:
  330         if (*m0)
  331                 m_freem(*m0);
  332         *m0 = NULL;
  333         return (EACCES);
  334 pass:
  335         return 0;       /* not filtered */
  336 }
  337 
  338 static int
  339 ipfw_divert(struct mbuf **m, int incoming, int tee)
  340 {
  341         /*
  342          * ipfw_chk() has already tagged the packet with the divert tag.
  343          * If tee is set, copy packet and return original.
  344          * If not tee, consume packet and send it to divert socket.
  345          */
  346         struct mbuf *clone, *reass;
  347         struct ip *ip;
  348         int hlen;
  349 
  350         reass = NULL;
  351 
  352         /* Is divert module loaded? */
  353         if (ip_divert_ptr == NULL)
  354                 goto nodivert;
  355 
  356         /* Cloning needed for tee? */
  357         if (tee)
  358                 clone = m_dup(*m, M_DONTWAIT);
  359         else
  360                 clone = *m;
  361 
  362         /* In case m_dup was unable to allocate mbufs. */
  363         if (clone == NULL)
  364                 goto teeout;
  365 
  366         /*
  367          * Divert listeners can only handle non-fragmented packets.
  368          * However when tee is set we will *not* de-fragment the packets;
  369          * Doing do would put the reassembly into double-jeopardy.  On top
  370          * of that someone doing a tee will probably want to get the packet
  371          * in its original form.
  372          */
  373         ip = mtod(clone, struct ip *);
  374         if (!tee && ip->ip_off & (IP_MF | IP_OFFMASK)) {
  375 
  376                 /* Reassemble packet. */
  377                 reass = ip_reass(clone);
  378 
  379                 /*
  380                  * IP header checksum fixup after reassembly and leave header
  381                  * in network byte order.
  382                  */
  383                 if (reass != NULL) {
  384                         ip = mtod(reass, struct ip *);
  385                         hlen = ip->ip_hl << 2;
  386                         ip->ip_len = htons(ip->ip_len);
  387                         ip->ip_off = htons(ip->ip_off);
  388                         ip->ip_sum = 0;
  389                         if (hlen == sizeof(struct ip))
  390                                 ip->ip_sum = in_cksum_hdr(ip);
  391                         else
  392                                 ip->ip_sum = in_cksum(reass, hlen);
  393                         clone = reass;
  394                 } else
  395                         clone = NULL;
  396         } else {
  397                 /* Convert header to network byte order. */
  398                 ip->ip_len = htons(ip->ip_len);
  399                 ip->ip_off = htons(ip->ip_off);
  400         }
  401 
  402         /* Do the dirty job... */
  403         if (clone && ip_divert_ptr != NULL)
  404                 ip_divert_ptr(clone, incoming);
  405 
  406 teeout:
  407         /*
  408          * For tee we leave the divert tag attached to original packet.
  409          * It will then continue rule evaluation after the tee rule.
  410          */
  411         if (tee)
  412                 return 0;
  413 
  414         /* Packet diverted and consumed */
  415         return 1;
  416 
  417 nodivert:
  418         m_freem(*m);
  419         return 1;
  420 }
  421 
  422 static int
  423 ipfw_hook(void)
  424 {
  425         struct pfil_head *pfh_inet;
  426 
  427         pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
  428         if (pfh_inet == NULL)
  429                 return ENOENT;
  430 
  431         pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
  432         pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
  433 
  434         return 0;
  435 }
  436 
  437 static int
  438 ipfw_unhook(void)
  439 {
  440         struct pfil_head *pfh_inet;
  441 
  442         pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET);
  443         if (pfh_inet == NULL)
  444                 return ENOENT;
  445 
  446         pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet);
  447         pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet);
  448 
  449         return 0;
  450 }
  451 
  452 #ifdef INET6
  453 static int
  454 ipfw6_hook(void)
  455 {
  456         struct pfil_head *pfh_inet6;
  457 
  458         pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
  459         if (pfh_inet6 == NULL)
  460                 return ENOENT;
  461 
  462         pfil_add_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
  463         pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6);
  464 
  465         return 0;
  466 }
  467 
  468 static int
  469 ipfw6_unhook(void)
  470 {
  471         struct pfil_head *pfh_inet6;
  472 
  473         pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6);
  474         if (pfh_inet6 == NULL)
  475                 return ENOENT;
  476 
  477         pfil_remove_hook(ipfw_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
  478         pfil_remove_hook(ipfw_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6);
  479 
  480         return 0;
  481 }
  482 #endif /* INET6 */
  483 
  484 int
  485 ipfw_chg_hook(SYSCTL_HANDLER_ARGS)
  486 {
  487         int enable = *(int *)arg1;
  488         int error;
  489 
  490         error = sysctl_handle_int(oidp, &enable, 0, req);
  491         if (error)
  492                 return (error);
  493 
  494         enable = (enable) ? 1 : 0;
  495 
  496         if (enable == *(int *)arg1)
  497                 return (0);
  498 
  499         if (arg1 == &fw_enable) {
  500                 if (enable)
  501                         error = ipfw_hook();
  502                 else
  503                         error = ipfw_unhook();
  504         }
  505 #ifdef INET6
  506         if (arg1 == &fw6_enable) {
  507                 if (enable)
  508                         error = ipfw6_hook();
  509                 else
  510                         error = ipfw6_unhook();
  511         }
  512 #endif
  513 
  514         if (error)
  515                 return (error);
  516 
  517         *(int *)arg1 = enable;
  518 
  519         return (0);
  520 }
  521 
  522 static int
  523 ipfw_modevent(module_t mod, int type, void *unused)
  524 {
  525         int err = 0;
  526 
  527         switch (type) {
  528         case MOD_LOAD:
  529                 if ((err = ipfw_init()) != 0) {
  530                         printf("ipfw_init() error\n");
  531                         break;
  532                 }
  533                 if ((err = ipfw_hook()) != 0) {
  534                         printf("ipfw_hook() error\n");
  535                         break;
  536                 }
  537 #ifdef INET6
  538                 if ((err = ipfw6_hook()) != 0) {
  539                         printf("ipfw_hook() error\n");
  540                         break;
  541                 }
  542 #endif
  543                 break;
  544 
  545         case MOD_UNLOAD:
  546                 if ((err = ipfw_unhook()) > 0)
  547                         break;
  548 #ifdef INET6
  549                 if ((err = ipfw6_unhook()) > 0)
  550                         break;
  551 #endif
  552                 ipfw_destroy();
  553                 break;
  554 
  555         default:
  556                 return EOPNOTSUPP;
  557                 break;
  558         }
  559         return err;
  560 }
  561 
  562 static moduledata_t ipfwmod = {
  563         "ipfw",
  564         ipfw_modevent,
  565         0
  566 };
  567 DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
  568 MODULE_VERSION(ipfw, 2);

Cache object: 4a013feefdb56f7f8c200c0f39115161


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.