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

Cache object: 8b103479fa7050acd3b6377c168fe215


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