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

Cache object: 65eb16478d6de41bb5f725eede8395b4


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