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

Cache object: 67ec5a0b7cd57630b2917ba9041cea0a


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