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/netgraph/ng_nat.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 2005, Gleb Smirnoff <glebius@FreeBSD.org>
    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 #include <sys/param.h>
   30 #include <sys/systm.h>
   31 #include <sys/kernel.h>
   32 #include <sys/mbuf.h>
   33 #include <sys/malloc.h>
   34 #include <sys/ctype.h>
   35 #include <sys/errno.h>
   36 #include <sys/syslog.h>
   37 
   38 #include <netinet/in_systm.h>
   39 #include <netinet/in.h>
   40 #include <netinet/ip.h>
   41 #include <netinet/ip_var.h>
   42 #include <netinet/tcp.h>
   43 #include <machine/in_cksum.h>
   44 
   45 #include <netinet/libalias/alias.h>
   46 
   47 #include <netgraph/ng_message.h>
   48 #include <netgraph/ng_parse.h>
   49 #include <netgraph/ng_nat.h>
   50 #include <netgraph/netgraph.h>
   51 
   52 static ng_constructor_t ng_nat_constructor;
   53 static ng_rcvmsg_t      ng_nat_rcvmsg;
   54 static ng_shutdown_t    ng_nat_shutdown;
   55 static ng_newhook_t     ng_nat_newhook;
   56 static ng_rcvdata_t     ng_nat_rcvdata;
   57 static ng_disconnect_t  ng_nat_disconnect;
   58 
   59 static unsigned int     ng_nat_translate_flags(unsigned int x);
   60 
   61 /* Parse type for struct ng_nat_mode. */
   62 static const struct ng_parse_struct_field ng_nat_mode_fields[]
   63         = NG_NAT_MODE_INFO;
   64 static const struct ng_parse_type ng_nat_mode_type = {
   65         &ng_parse_struct_type,
   66         ng_nat_mode_fields
   67 };
   68 
   69 /* List of commands and how to convert arguments to/from ASCII. */
   70 static const struct ng_cmdlist ng_nat_cmdlist[] = {
   71         {
   72           NGM_NAT_COOKIE,
   73           NGM_NAT_SET_IPADDR,
   74           "setaliasaddr",
   75           &ng_parse_ipaddr_type,
   76           NULL
   77         },
   78         {
   79           NGM_NAT_COOKIE,
   80           NGM_NAT_SET_MODE,
   81           "setmode",
   82           &ng_nat_mode_type,
   83           NULL
   84         },
   85         {
   86           NGM_NAT_COOKIE,
   87           NGM_NAT_SET_TARGET,
   88           "settarget",
   89           &ng_parse_ipaddr_type,
   90           NULL
   91         },
   92         { 0 }
   93 };
   94 
   95 /* Netgraph node type descriptor. */
   96 static struct ng_type typestruct = {
   97         .version =      NG_ABI_VERSION,
   98         .name =         NG_NAT_NODE_TYPE,
   99         .constructor =  ng_nat_constructor,
  100         .rcvmsg =       ng_nat_rcvmsg,
  101         .shutdown =     ng_nat_shutdown,
  102         .newhook =      ng_nat_newhook,
  103         .rcvdata =      ng_nat_rcvdata,
  104         .disconnect =   ng_nat_disconnect,
  105         .cmdlist =      ng_nat_cmdlist,
  106 };
  107 NETGRAPH_INIT(nat, &typestruct);
  108 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
  109 
  110 /* Information we store for each node. */
  111 struct ng_nat_priv {
  112         node_p          node;           /* back pointer to node */
  113         hook_p          in;             /* hook for demasquerading */
  114         hook_p          out;            /* hook for masquerading */
  115         struct libalias *lib;           /* libalias handler */
  116         uint32_t        flags;          /* status flags */
  117 };
  118 typedef struct ng_nat_priv *priv_p;
  119 
  120 /* Values of flags */
  121 #define NGNAT_CONNECTED         0x1     /* We have both hooks connected */
  122 #define NGNAT_ADDR_DEFINED      0x2     /* NGM_NAT_SET_IPADDR happened */
  123 
  124 static int
  125 ng_nat_constructor(node_p node)
  126 {
  127         priv_p priv;
  128 
  129         /* Initialize private descriptor. */
  130         MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH,
  131                 M_NOWAIT | M_ZERO);
  132         if (priv == NULL)
  133                 return (ENOMEM);
  134 
  135         /* Init aliasing engine. */
  136         priv->lib = LibAliasInit(NULL);
  137         if (priv->lib == NULL) {
  138                 FREE(priv, M_NETGRAPH);
  139                 return (ENOMEM);
  140         }
  141 
  142         /* Set same ports on. */
  143         (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
  144             PKT_ALIAS_SAME_PORTS);
  145 
  146         /* Link structs together. */
  147         NG_NODE_SET_PRIVATE(node, priv);
  148         priv->node = node;
  149 
  150         /*
  151          * libalias is not thread safe, so our node
  152          * must be single threaded.
  153          */
  154         NG_NODE_FORCE_WRITER(node);
  155 
  156         return (0);
  157 }
  158 
  159 static int
  160 ng_nat_newhook(node_p node, hook_p hook, const char *name)
  161 {
  162         const priv_p priv = NG_NODE_PRIVATE(node);
  163 
  164         if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
  165                 priv->in = hook;
  166         } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
  167                 priv->out = hook;
  168         } else
  169                 return (EINVAL);
  170 
  171         if (priv->out != NULL &&
  172             priv->in != NULL)
  173                 priv->flags |= NGNAT_CONNECTED;
  174 
  175         return(0);
  176 }
  177 
  178 static int
  179 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
  180 {
  181         const priv_p priv = NG_NODE_PRIVATE(node);
  182         struct ng_mesg *resp = NULL;
  183         struct ng_mesg *msg;
  184         int error = 0;
  185 
  186         NGI_GET_MSG(item, msg);
  187 
  188         switch (msg->header.typecookie) {
  189         case NGM_NAT_COOKIE:
  190                 switch (msg->header.cmd) {
  191                 case NGM_NAT_SET_IPADDR:
  192                     {
  193                         struct in_addr *const ia = (struct in_addr *)msg->data;
  194 
  195                         if (msg->header.arglen < sizeof(*ia)) {
  196                                 error = EINVAL;
  197                                 break;
  198                         }
  199 
  200                         LibAliasSetAddress(priv->lib, *ia);
  201 
  202                         priv->flags |= NGNAT_ADDR_DEFINED;
  203                     }
  204                         break;
  205                 case NGM_NAT_SET_MODE:
  206                     {
  207                         struct ng_nat_mode *const mode = 
  208                             (struct ng_nat_mode *)msg->data;
  209 
  210                         if (msg->header.arglen < sizeof(*mode)) {
  211                                 error = EINVAL;
  212                                 break;
  213                         }
  214                         
  215                         if (LibAliasSetMode(priv->lib, 
  216                             ng_nat_translate_flags(mode->flags),
  217                             ng_nat_translate_flags(mode->mask)) < 0) {
  218                                 error = ENOMEM;
  219                                 break;
  220                         }
  221                     }
  222                         break;
  223                 case NGM_NAT_SET_TARGET:
  224                     {
  225                         struct in_addr *const ia = (struct in_addr *)msg->data;
  226 
  227                         if (msg->header.arglen < sizeof(*ia)) {
  228                                 error = EINVAL;
  229                                 break;
  230                         }
  231 
  232                         LibAliasSetTarget(priv->lib, *ia);
  233                     }
  234                         break;
  235                 default:
  236                         error = EINVAL;         /* unknown command */
  237                         break;
  238                 }
  239                 break;
  240         default:
  241                 error = EINVAL;                 /* unknown cookie type */
  242                 break;
  243         }
  244 
  245         NG_RESPOND_MSG(error, node, item, resp);
  246         NG_FREE_MSG(msg);
  247         return (error);
  248 }
  249 
  250 static int
  251 ng_nat_rcvdata(hook_p hook, item_p item )
  252 {
  253         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  254         struct mbuf     *m;
  255         struct ip       *ip;
  256         int rval, error = 0;
  257         char *c;
  258 
  259         /* We have no required hooks. */
  260         if (!(priv->flags & NGNAT_CONNECTED)) {
  261                 NG_FREE_ITEM(item);
  262                 return (ENXIO);
  263         }
  264 
  265         /* We have no alias address yet to do anything. */
  266         if (!(priv->flags & NGNAT_ADDR_DEFINED))
  267                 goto send;
  268 
  269         m = NGI_M(item);
  270 
  271         if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
  272                 NGI_M(item) = NULL;     /* avoid double free */
  273                 NG_FREE_ITEM(item);
  274                 return (ENOBUFS);
  275         }
  276 
  277         NGI_M(item) = m;
  278 
  279         c = mtod(m, char *);
  280         ip = mtod(m, struct ip *);
  281 
  282         KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
  283             ("ng_nat: ip_len != m_pkthdr.len"));
  284 
  285         if (hook == priv->in) {
  286                 rval = LibAliasIn(priv->lib, c, MCLBYTES);
  287                 if (rval != PKT_ALIAS_OK &&
  288                     rval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
  289                         NG_FREE_ITEM(item);
  290                         return (EINVAL);
  291                 }
  292         } else if (hook == priv->out) {
  293                 rval = LibAliasOut(priv->lib, c, MCLBYTES);
  294                 if (rval != PKT_ALIAS_OK) {
  295                         NG_FREE_ITEM(item);
  296                         return (EINVAL);
  297                 }
  298         } else
  299                 panic("ng_nat: unknown hook!\n");
  300 
  301         m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
  302 
  303         if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
  304             ip->ip_p == IPPROTO_TCP) {
  305                 struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
  306                     (ip->ip_hl << 2));
  307 
  308                 /*
  309                  * Here is our terrible HACK.
  310                  *
  311                  * Sometimes LibAlias edits contents of TCP packet.
  312                  * In this case it needs to recompute full TCP
  313                  * checksum. However, the problem is that LibAlias
  314                  * doesn't have any idea about checksum offloading
  315                  * in kernel. To workaround this, we do not do
  316                  * checksumming in LibAlias, but only mark the
  317                  * packets in th_x2 field. If we receive a marked
  318                  * packet, we calculate correct checksum for it
  319                  * aware of offloading.
  320                  *
  321                  * Why do I do such a terrible hack instead of
  322                  * recalculating checksum for each packet?
  323                  * Because the previous checksum was not checked!
  324                  * Recalculating checksums for EVERY packet will
  325                  * hide ALL transmission errors. Yes, marked packets
  326                  * still suffer from this problem. But, sigh, natd(8)
  327                  * has this problem, too.
  328                  */
  329 
  330                 if (th->th_x2) {
  331                         th->th_x2 = 0;
  332                         ip->ip_len = ntohs(ip->ip_len);
  333                         th->th_sum = in_pseudo(ip->ip_src.s_addr,
  334                             ip->ip_dst.s_addr, htons(IPPROTO_TCP +
  335                             ip->ip_len - (ip->ip_hl << 2)));
  336         
  337                         if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
  338                                 m->m_pkthdr.csum_data = offsetof(struct tcphdr,
  339                                     th_sum);
  340                                 in_delayed_cksum(m);
  341                         }
  342                         ip->ip_len = htons(ip->ip_len);
  343                 }
  344         }
  345 
  346 send:
  347         if (hook == priv->in)
  348                 NG_FWD_ITEM_HOOK(error, item, priv->out);
  349         else
  350                 NG_FWD_ITEM_HOOK(error, item, priv->in);
  351 
  352         return (error);
  353 }
  354 
  355 static int
  356 ng_nat_shutdown(node_p node)
  357 {
  358         const priv_p priv = NG_NODE_PRIVATE(node);
  359 
  360         NG_NODE_SET_PRIVATE(node, NULL);
  361         NG_NODE_UNREF(node);
  362         LibAliasUninit(priv->lib);
  363         FREE(priv, M_NETGRAPH);
  364 
  365         return (0);
  366 }
  367 
  368 static int
  369 ng_nat_disconnect(hook_p hook)
  370 {
  371         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  372 
  373         priv->flags &= ~NGNAT_CONNECTED;
  374 
  375         if (hook == priv->out)
  376                 priv->out = NULL;
  377         if (hook == priv->in)
  378                 priv->in = NULL;
  379 
  380         if (priv->out == NULL && priv->in == NULL)
  381                 ng_rmnode_self(NG_HOOK_NODE(hook));
  382 
  383         return (0);
  384 }
  385 
  386 static unsigned int
  387 ng_nat_translate_flags(unsigned int x)
  388 {
  389         unsigned int    res = 0;
  390         
  391         if (x & NG_NAT_LOG)
  392                 res |= PKT_ALIAS_LOG;
  393         if (x & NG_NAT_DENY_INCOMING)
  394                 res |= PKT_ALIAS_DENY_INCOMING;
  395         if (x & NG_NAT_SAME_PORTS)
  396                 res |= PKT_ALIAS_SAME_PORTS;
  397         if (x & NG_NAT_UNREGISTERED_ONLY)
  398                 res |= PKT_ALIAS_UNREGISTERED_ONLY;
  399         if (x & NG_NAT_RESET_ON_ADDR_CHANGE)
  400                 res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
  401         if (x & NG_NAT_PROXY_ONLY)
  402                 res |= PKT_ALIAS_PROXY_ONLY;
  403         if (x & NG_NAT_REVERSE)
  404                 res |= PKT_ALIAS_REVERSE;
  405 
  406         return (res);
  407 }

Cache object: ca5aa02d81834f6e3f0a58b7654b103e


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