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

Cache object: 1fd3ab64fd984fb8f3a5945d29331cf3


[ 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.