ng_nat.c revision 145937
12088Ssos/*- 2330449Seadler * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org> 3330449Seadler * All rights reserved. 4228976Suqs * 52088Ssos * Redistribution and use in source and binary forms, with or without 62088Ssos * modification, are permitted provided that the following conditions 72088Ssos * are met: 82088Ssos * 1. Redistributions of source code must retain the above copyright 92088Ssos * notice, this list of conditions and the following disclaimer. 102088Ssos * 2. Redistributions in binary form must reproduce the above copyright 115994Ssos * notice, this list of conditions and the following disclaimer in the 125994Ssos * documentation and/or other materials provided with the distribution. 132088Ssos * 142088Ssos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 152088Ssos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 162088Ssos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1797748Sschweikh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 182088Ssos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 192088Ssos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 202088Ssos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 212088Ssos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 222088Ssos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 232088Ssos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 242088Ssos * SUCH DAMAGE. 252088Ssos * 262088Ssos * $FreeBSD: head/sys/netgraph/ng_nat.c 145937 2005-05-05 23:41:21Z glebius $ 272088Ssos */ 282088Ssos 292088Ssos#include <sys/param.h> 302088Ssos#include <sys/systm.h> 31114601Sobrien#include <sys/kernel.h> 32114601Sobrien#include <sys/mbuf.h> 3329603Scharnier#include <sys/malloc.h> 342088Ssos#include <sys/ctype.h> 3529603Scharnier#include <sys/errno.h> 362088Ssos#include <sys/syslog.h> 3729603Scharnier 383864Sswallace#include <netinet/in_systm.h> 3929603Scharnier#include <netinet/in.h> 4042505Syokota#include <netinet/ip.h> 4166834Sphk#include <netinet/ip_icmp.h> 4266834Sphk#include <netinet/tcp.h> 43296926Semaste#include <netinet/udp.h> 44266839Sray 452088Ssos#include <netinet/libalias/alias.h> 462088Ssos 472088Ssos#include <netgraph/ng_message.h> 4876643Simp#include <netgraph/ng_parse.h> 4990394Sru#include <netgraph/ng_nat.h> 5090394Sru#include <netgraph/netgraph.h> 5176643Simp 5290394Srustatic ng_constructor_t ng_nat_constructor; 5390394Srustatic ng_rcvmsg_t ng_nat_rcvmsg; 5490394Srustatic ng_shutdown_t ng_nat_shutdown; 5590394Srustatic ng_newhook_t ng_nat_newhook; 5690394Srustatic ng_rcvdata_t ng_nat_rcvdata; 5790394Srustatic ng_disconnect_t ng_nat_disconnect; 5876643Simp 5976643Simpstatic struct mbuf * m_megapullup(struct mbuf *, int); 6076643Simp 6176643Simp/* List of commands and how to convert arguments to/from ASCII. */ 62196500Sedstatic const struct ng_cmdlist ng_nat_cmdlist[] = { 63196500Sed { 64228437Sed NGM_NAT_COOKIE, 658857Srgrimes NGM_NAT_SET_IPADDR, 662088Ssos "setaliasaddr", 672088Ssos &ng_parse_ipaddr_type, 6838139Syokota NULL 692088Ssos }, 702088Ssos { 0 } 71228437Sed}; 7232316Syokota 7332316Syokota/* Netgraph node type descriptor. */ 7432316Syokotastatic struct ng_type typestruct = { 7532316Syokota .version = NG_ABI_VERSION, 7632316Syokota .name = NG_NAT_NODE_TYPE, 77228437Sed .constructor = ng_nat_constructor, 7832316Syokota .rcvmsg = ng_nat_rcvmsg, 7932316Syokota .shutdown = ng_nat_shutdown, 8032316Syokota .newhook = ng_nat_newhook, 8132316Syokota .rcvdata = ng_nat_rcvdata, 8232316Syokota .disconnect = ng_nat_disconnect, 83228437Sed .cmdlist = ng_nat_cmdlist, 845994Ssos}; 855994SsosNETGRAPH_INIT(nat, &typestruct); 865994SsosMODULE_DEPEND(ng_nat, libalias, 1, 1, 1); 875994Ssos 885994Ssos/* Information we store for each node. */ 895994Ssosstruct ng_priv_priv { 905994Ssos node_p node; /* back pointer to node */ 915994Ssos hook_p in; /* hook for demasquerading */ 925994Ssos hook_p out; /* hook for masquerading */ 935994Ssos struct libalias *lib; /* libalias handler */ 945994Ssos uint32_t flags; /* status flags */ 959202Srgrimes}; 965994Ssostypedef struct ng_priv_priv *priv_p; 975994Ssos 985994Ssos/* Values of flags */ 999202Srgrimes#define NGNAT_READY 0x1 /* We have everything to work */ 1005994Ssos#define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */ 1015994Ssos 1025994Ssosstatic int 1035994Ssosng_nat_constructor(node_p node) 1045994Ssos{ 1055994Ssos priv_p priv; 1065994Ssos 1075994Ssos /* Initialize private descriptor. */ 1082088Ssos MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, 1092088Ssos M_NOWAIT | M_ZERO); 110228437Sed if (priv == NULL) 111228437Sed return (ENOMEM); 112228437Sed 113228437Sed /* Init aliasing engine. */ 114228437Sed priv->lib = LibAliasInit(NULL); 115228437Sed if (priv->lib == NULL) { 116228437Sed FREE(priv, M_NETGRAPH); 117228437Sed return (ENOMEM); 118296926Semaste } 119228437Sed 1202088Ssos /* Set same ports on. */ 121228437Sed (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS, 122228437Sed PKT_ALIAS_SAME_PORTS); 1232088Ssos 124296926Semaste /* Link structs together. */ 125228437Sed NG_NODE_SET_PRIVATE(node, priv); 126228437Sed priv->node = node; 127228437Sed 128228437Sed /* 129228437Sed * libalias is not thread safe, so our node 130228437Sed * must be single threaded. 131228437Sed */ 132228437Sed NG_NODE_FORCE_WRITER(node); 133228437Sed 134228437Sed return (0); 135228437Sed} 136228437Sed 137228437Sedstatic int 138228437Sedng_nat_newhook(node_p node, hook_p hook, const char *name) 139228437Sed{ 140228437Sed const priv_p priv = NG_NODE_PRIVATE(node); 141228437Sed 142228437Sed if (strcmp(name, NG_NAT_HOOK_IN) == 0) { 143228437Sed priv->in = hook; 144228437Sed } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) { 145228437Sed priv->out = hook; 146228437Sed } else 147228437Sed return (EINVAL); 148228437Sed 149228437Sed if (priv->out != NULL && 150296926Semaste priv->in != NULL && 151296926Semaste priv->flags & NGNAT_ADDR_DEFINED) 152296926Semaste priv->flags |= NGNAT_READY; 153296926Semaste 154296926Semaste return(0); 155296926Semaste} 156266839Sray 157266839Sraystatic int 158266839Srayng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook) 159266839Sray{ 160268175Semaste const priv_p priv = NG_NODE_PRIVATE(node); 161268175Semaste struct ng_mesg *resp = NULL; 162266839Sray struct ng_mesg *msg; 163268175Semaste int error = 0; 164268175Semaste 165268175Semaste NGI_GET_MSG(item, msg); 166266839Sray 167266839Sray switch (msg->header.typecookie) { 168228437Sed case NGM_NAT_COOKIE: 1692088Ssos switch (msg->header.cmd) { 1702088Ssos case NGM_NAT_SET_IPADDR: 1712088Ssos { 1722088Ssos struct in_addr *const ia = (struct in_addr *)msg->data; 17329603Scharnier 1742088Ssos if (msg->header.arglen < sizeof(*ia)) { 1752088Ssos error = EINVAL; 1762088Ssos break; 1772088Ssos } 178228437Sed 1792088Ssos LibAliasSetAddress(priv->lib, *ia); 1802088Ssos 1815536Ssos priv->flags |= NGNAT_ADDR_DEFINED; 1825536Ssos if (priv->out != NULL && 1835536Ssos priv->in != NULL) 1842088Ssos priv->flags |= NGNAT_READY; 1852088Ssos } 18677394Ssobomax break; 1872088Ssos default: 1882088Ssos error = EINVAL; /* unknown command */ 1892088Ssos break; 1902088Ssos } 19177394Ssobomax break; 1922088Ssos default: 1932088Ssos error = EINVAL; /* unknown cookie type */ 1942088Ssos break; 1952088Ssos } 1962088Ssos 1972088Ssos NG_RESPOND_MSG(error, node, item, resp); 1982088Ssos NG_FREE_MSG(msg); 1992088Ssos return (error); 2002088Ssos} 2012088Ssos 2022088Ssosstatic int 2032088Ssosng_nat_rcvdata(hook_p hook, item_p item ) 2042088Ssos{ 205228437Sed const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 20699816Salfred struct mbuf *m; 2072088Ssos int plen; 20832316Syokota int rval, error = 0; 2092088Ssos char *c; 210196500Sed 2112088Ssos if (!(priv->flags & NGNAT_READY)) { 212196500Sed NG_FREE_ITEM(item); 2132088Ssos return (ENXIO); 214196500Sed } 2152088Ssos 216196500Sed m = NGI_M(item); 2172088Ssos 218196500Sed if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) { 2192088Ssos NGI_M(item) = NULL; /* avoid double free */ 220196500Sed NG_FREE_ITEM(item); 2212088Ssos return (ENOBUFS); 222196500Sed } 2232088Ssos 224196500Sed plen = m->m_pkthdr.len; 2252088Ssos 226196500Sed NGI_M(item) = m; 2272088Ssos 228196500Sed c = mtod(m, char *); 22948105Syokota if (hook == priv->in) { 230196500Sed rval = LibAliasIn(priv->lib, c, plen); 2312088Ssos if (rval != PKT_ALIAS_OK) { 232196500Sed printf("in %u\n", rval); 2332088Ssos NG_FREE_ITEM(item); 234196500Sed return (EINVAL); 2352088Ssos } 236196500Sed NG_FWD_ITEM_HOOK(error, item, priv->out); 2372088Ssos } else if (hook == priv->out) { 238196500Sed rval = LibAliasOut(priv->lib, c, plen); 2392088Ssos if (rval != PKT_ALIAS_OK) { 240196500Sed printf("out %u\n", rval); 2412088Ssos NG_FREE_ITEM(item); 242196500Sed return (EINVAL); 2432088Ssos } 244196500Sed NG_FWD_ITEM_HOOK(error, item, priv->in); 2455994Ssos } else 246196500Sed panic("ng_nat: unknown hook!\n"); 24738053Syokota 248196500Sed return (error); 24954380Syokota} 250196500Sed 25154380Syokotastatic int 252196500Sedng_nat_shutdown(node_p node) 25354380Syokota{ 254196500Sed const priv_p priv = NG_NODE_PRIVATE(node); 25554380Syokota 256196500Sed NG_NODE_SET_PRIVATE(node, NULL); 25754380Syokota NG_NODE_UNREF(node); 258196500Sed LibAliasUninit(priv->lib); 25954380Syokota FREE(priv, M_NETGRAPH); 260196500Sed 26154380Syokota return (0); 262196500Sed} 26365759Sdwmalone 264196500Sedstatic int 26565759Sdwmaloneng_nat_disconnect(hook_p hook) 266196500Sed{ 26774118Sache const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 268196500Sed 26932316Syokota priv->flags &= ~NGNAT_READY; 27032316Syokota 27132316Syokota if (hook == priv->out) 272196500Sed priv->out = NULL; 2732088Ssos if (hook == priv->in) 2742088Ssos priv->in = NULL; 2752088Ssos 276196500Sed if (priv->out == NULL && priv->in == NULL) 2772088Ssos ng_rmnode_self(NG_HOOK_NODE(hook)); 2782088Ssos 2792088Ssos return (0); 280196500Sed} 2812088Ssos 2822088Ssos/* 2832088Ssos * m_megapullup() function is a big hack. 284197330Sed * 2852088Ssos * It allocates an mbuf with cluster and copies the whole 2862088Ssos * chain into cluster, so that it is all contigous and the 2872088Ssos * whole packet can be accessed via char pointer. 2882088Ssos * 2892088Ssos * This is required, because libalias doesn't have idea 2902088Ssos * about mbufs. 2912088Ssos */ 29277394Ssobomaxstatic struct mbuf * 293296926Semastem_megapullup(struct mbuf *m, int len) 2942088Ssos{ 29532316Syokota struct mbuf *mcl; 2962088Ssos caddr_t cp; 297296926Semaste 2982088Ssos if (len > MCLBYTES) 29932316Syokota goto bad; 30032316Syokota 30132316Syokota if ((mcl = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR)) == NULL) 30232316Syokota goto bad; 30332316Syokota 30432316Syokota cp = mtod(mcl, caddr_t); 30532316Syokota m_copydata(m, 0, len, cp); 30632316Syokota m_move_pkthdr(mcl, m); 30732316Syokota mcl->m_len = mcl->m_pkthdr.len; 30832316Syokota m_freem(m); 30932316Syokota 31032316Syokota return (mcl); 31132316Syokotabad: 31232316Syokota m_freem(m); 31332316Syokota return (NULL); 31432316Syokota} 31532316Syokota