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: releng/6.1/sys/netgraph/ng_nat.c 147625 2005-06-27 07:39:13Z glebius $
   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 struct mbuf * m_megapullup(struct mbuf *, int);
   60 
   61 /* List of commands and how to convert arguments to/from ASCII. */
   62 static const struct ng_cmdlist ng_nat_cmdlist[] = {
   63         {
   64           NGM_NAT_COOKIE,
   65           NGM_NAT_SET_IPADDR,
   66           "setaliasaddr",
   67           &ng_parse_ipaddr_type,
   68           NULL
   69         },
   70         { 0 }
   71 };
   72 
   73 /* Netgraph node type descriptor. */
   74 static struct ng_type typestruct = {
   75         .version =      NG_ABI_VERSION,
   76         .name =         NG_NAT_NODE_TYPE,
   77         .constructor =  ng_nat_constructor,
   78         .rcvmsg =       ng_nat_rcvmsg,
   79         .shutdown =     ng_nat_shutdown,
   80         .newhook =      ng_nat_newhook,
   81         .rcvdata =      ng_nat_rcvdata,
   82         .disconnect =   ng_nat_disconnect,
   83         .cmdlist =      ng_nat_cmdlist,
   84 };
   85 NETGRAPH_INIT(nat, &typestruct);
   86 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
   87 
   88 /* Information we store for each node. */
   89 struct ng_priv_priv {
   90         node_p          node;           /* back pointer to node */
   91         hook_p          in;             /* hook for demasquerading */
   92         hook_p          out;            /* hook for masquerading */
   93         struct libalias *lib;           /* libalias handler */
   94         uint32_t        flags;          /* status flags */
   95 };
   96 typedef struct ng_priv_priv *priv_p;
   97 
   98 /* Values of flags */
   99 #define NGNAT_READY             0x1     /* We have everything to work */
  100 #define NGNAT_ADDR_DEFINED      0x2     /* NGM_NAT_SET_IPADDR happened */
  101 
  102 static int
  103 ng_nat_constructor(node_p node)
  104 {
  105         priv_p priv;
  106 
  107         /* Initialize private descriptor. */
  108         MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH,
  109                 M_NOWAIT | M_ZERO);
  110         if (priv == NULL)
  111                 return (ENOMEM);
  112 
  113         /* Init aliasing engine. */
  114         priv->lib = LibAliasInit(NULL);
  115         if (priv->lib == NULL) {
  116                 FREE(priv, M_NETGRAPH);
  117                 return (ENOMEM);
  118         }
  119 
  120         /* Set same ports on. */
  121         (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
  122             PKT_ALIAS_SAME_PORTS);
  123 
  124         /* Link structs together. */
  125         NG_NODE_SET_PRIVATE(node, priv);
  126         priv->node = node;
  127 
  128         /*
  129          * libalias is not thread safe, so our node
  130          * must be single threaded.
  131          */
  132         NG_NODE_FORCE_WRITER(node);
  133 
  134         return (0);
  135 }
  136 
  137 static int
  138 ng_nat_newhook(node_p node, hook_p hook, const char *name)
  139 {
  140         const priv_p priv = NG_NODE_PRIVATE(node);
  141 
  142         if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
  143                 priv->in = hook;
  144         } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
  145                 priv->out = hook;
  146         } else
  147                 return (EINVAL);
  148 
  149         if (priv->out != NULL &&
  150             priv->in != NULL &&
  151             priv->flags & NGNAT_ADDR_DEFINED)
  152                 priv->flags |= NGNAT_READY;
  153 
  154         return(0);
  155 }
  156 
  157 static int
  158 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
  159 {
  160         const priv_p priv = NG_NODE_PRIVATE(node);
  161         struct ng_mesg *resp = NULL;
  162         struct ng_mesg *msg;
  163         int error = 0;
  164 
  165         NGI_GET_MSG(item, msg);
  166 
  167         switch (msg->header.typecookie) {
  168         case NGM_NAT_COOKIE:
  169                 switch (msg->header.cmd) {
  170                 case NGM_NAT_SET_IPADDR:
  171                     {
  172                         struct in_addr *const ia = (struct in_addr *)msg->data;
  173 
  174                         if (msg->header.arglen < sizeof(*ia)) {
  175                                 error = EINVAL;
  176                                 break;
  177                         }
  178 
  179                         LibAliasSetAddress(priv->lib, *ia);
  180 
  181                         priv->flags |= NGNAT_ADDR_DEFINED;
  182                         if (priv->out != NULL &&
  183                             priv->in != NULL)
  184                                 priv->flags |= NGNAT_READY;
  185                     }
  186                         break;
  187                 default:
  188                         error = EINVAL;         /* unknown command */
  189                         break;
  190                 }
  191                 break;
  192         default:
  193                 error = EINVAL;                 /* unknown cookie type */
  194                 break;
  195         }
  196 
  197         NG_RESPOND_MSG(error, node, item, resp);
  198         NG_FREE_MSG(msg);
  199         return (error);
  200 }
  201 
  202 static int
  203 ng_nat_rcvdata(hook_p hook, item_p item )
  204 {
  205         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  206         struct mbuf     *m;
  207         struct ip       *ip;
  208         int rval, error = 0;
  209         char *c;
  210 
  211         if (!(priv->flags & NGNAT_READY)) {
  212                 NG_FREE_ITEM(item);
  213                 return (ENXIO);
  214         }
  215 
  216         m = NGI_M(item);
  217 
  218         if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
  219                 NGI_M(item) = NULL;     /* avoid double free */
  220                 NG_FREE_ITEM(item);
  221                 return (ENOBUFS);
  222         }
  223 
  224         NGI_M(item) = m;
  225 
  226         c = mtod(m, char *);
  227         ip = mtod(m, struct ip *);
  228 
  229         KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
  230             ("ng_nat: ip_len != m_pkthdr.len"));
  231 
  232         if (hook == priv->in) {
  233                 rval = LibAliasIn(priv->lib, c, MCLBYTES);
  234                 if (rval != PKT_ALIAS_OK) {
  235                         NG_FREE_ITEM(item);
  236                         return (EINVAL);
  237                 }
  238         } else if (hook == priv->out) {
  239                 rval = LibAliasOut(priv->lib, c, MCLBYTES);
  240                 if (rval != PKT_ALIAS_OK) {
  241                         NG_FREE_ITEM(item);
  242                         return (EINVAL);
  243                 }
  244         } else
  245                 panic("ng_nat: unknown hook!\n");
  246 
  247         m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
  248 
  249         if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
  250             ip->ip_p == IPPROTO_TCP) {
  251                 struct tcphdr   *th = (struct tcphdr *)(ip + 1);
  252 
  253                 /*
  254                  * Here is our terrible HACK.
  255                  *
  256                  * Sometimes LibAlias edits contents of TCP packet.
  257                  * In this case it needs to recompute full TCP
  258                  * checksum. However, the problem is that LibAlias
  259                  * doesn't have any idea about checksum offloading
  260                  * in kernel. To workaround this, we do not do
  261                  * checksumming in LibAlias, but only mark the
  262                  * packets in th_x2 field. If we receive a marked
  263                  * packet, we calculate correct checksum for it
  264                  * aware of offloading.
  265                  *
  266                  * Why do I do such a terrible hack instead of
  267                  * recalculating checksum for each packet?
  268                  * Because the previous checksum was not checked!
  269                  * Recalculating checksums for EVERY packet will
  270                  * hide ALL transmission errors. Yes, marked packets
  271                  * still suffer from this problem. But, sigh, natd(8)
  272                  * has this problem, too.
  273                  */
  274 
  275                 if (th->th_x2) {
  276                         th->th_x2 = 0;
  277                         ip->ip_len = ntohs(ip->ip_len);
  278                         th->th_sum = in_pseudo(ip->ip_src.s_addr,
  279                             ip->ip_dst.s_addr, htons(IPPROTO_TCP +
  280                             ip->ip_len - (ip->ip_hl << 2)));
  281         
  282                         if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
  283                                 m->m_pkthdr.csum_data = offsetof(struct tcphdr,
  284                                     th_sum);
  285                                 in_delayed_cksum(m);
  286                         }
  287                         ip->ip_len = htons(ip->ip_len);
  288                 }
  289         }
  290 
  291         if (hook == priv->in)
  292                 NG_FWD_ITEM_HOOK(error, item, priv->out);
  293         else
  294                 NG_FWD_ITEM_HOOK(error, item, priv->in);
  295 
  296         return (error);
  297 }
  298 
  299 static int
  300 ng_nat_shutdown(node_p node)
  301 {
  302         const priv_p priv = NG_NODE_PRIVATE(node);
  303 
  304         NG_NODE_SET_PRIVATE(node, NULL);
  305         NG_NODE_UNREF(node);
  306         LibAliasUninit(priv->lib);
  307         FREE(priv, M_NETGRAPH);
  308 
  309         return (0);
  310 }
  311 
  312 static int
  313 ng_nat_disconnect(hook_p hook)
  314 {
  315         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
  316 
  317         priv->flags &= ~NGNAT_READY;
  318 
  319         if (hook == priv->out)
  320                 priv->out = NULL;
  321         if (hook == priv->in)
  322                 priv->in = NULL;
  323 
  324         if (priv->out == NULL && priv->in == NULL)
  325                 ng_rmnode_self(NG_HOOK_NODE(hook));
  326 
  327         return (0);
  328 }
  329 
  330 /*
  331  * m_megapullup() function is a big hack.
  332  *
  333  * It allocates an mbuf with cluster and copies the whole
  334  * chain into cluster, so that it is all contigous and the
  335  * whole packet can be accessed via char pointer.
  336  *
  337  * This is required, because libalias doesn't have idea
  338  * about mbufs.
  339  */
  340 static struct mbuf *
  341 m_megapullup(struct mbuf *m, int len)
  342 {
  343         struct mbuf *mcl;
  344         caddr_t cp;
  345 
  346         if (len > MCLBYTES)
  347                 goto bad;
  348 
  349         if ((mcl = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR)) == NULL)
  350                 goto bad;
  351 
  352         cp = mtod(mcl, caddr_t);
  353         m_copydata(m, 0, len, cp);
  354         m_move_pkthdr(mcl, m);
  355         mcl->m_len = mcl->m_pkthdr.len;
  356         m_freem(m);
  357 
  358         return (mcl);
  359 bad:
  360         m_freem(m);
  361         return (NULL);
  362 }

Cache object: 2ae6f0a64da56e30a7bd51ff4904639a


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