diff --git a/sys/conf/files b/sys/conf/files index dfd2b5d89857..8ad7c01a4c3e 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1529,6 +1529,7 @@ netgraph/ng_l2tp.c optional netgraph_l2tp netgraph/ng_lmi.c optional netgraph_lmi netgraph/ng_mppc.c optional netgraph_mppc_compression netgraph/ng_mppc.c optional netgraph_mppc_encryption +netgraph/ng_nat.c optional netgraph_nat netgraph/ng_one2many.c optional netgraph_one2many netgraph/ng_parse.c optional netgraph netgraph/ng_ppp.c optional netgraph_ppp diff --git a/sys/conf/options b/sys/conf/options index 11be4e70a8bf..1dedaf456b40 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -411,6 +411,7 @@ NETGRAPH_LMI opt_netgraph.h # MPPC compression requires proprietary files (not included) NETGRAPH_MPPC_COMPRESSION opt_netgraph.h NETGRAPH_MPPC_ENCRYPTION opt_netgraph.h +NETGRAPH_NAT opt_netgraph.h NETGRAPH_NETFLOW opt_netgraph.h NETGRAPH_ONE2MANY opt_netgraph.h NETGRAPH_PPP opt_netgraph.h diff --git a/sys/modules/netgraph/nat/Makefile b/sys/modules/netgraph/nat/Makefile new file mode 100644 index 000000000000..f51bbe0b139f --- /dev/null +++ b/sys/modules/netgraph/nat/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= ng_nat +SRCS= ng_nat.c + +.include diff --git a/sys/netgraph/ng_nat.c b/sys/netgraph/ng_nat.c new file mode 100644 index 000000000000..b353e2ec22f7 --- /dev/null +++ b/sys/netgraph/ng_nat.c @@ -0,0 +1,314 @@ +/*- + * Copyright 2005, Gleb Smirnoff + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +static ng_constructor_t ng_nat_constructor; +static ng_rcvmsg_t ng_nat_rcvmsg; +static ng_shutdown_t ng_nat_shutdown; +static ng_newhook_t ng_nat_newhook; +static ng_rcvdata_t ng_nat_rcvdata; +static ng_disconnect_t ng_nat_disconnect; + +static struct mbuf * m_megapullup(struct mbuf *, int); + +/* List of commands and how to convert arguments to/from ASCII. */ +static const struct ng_cmdlist ng_nat_cmdlist[] = { + { + NGM_NAT_COOKIE, + NGM_NAT_SET_IPADDR, + "setaliasaddr", + &ng_parse_ipaddr_type, + NULL + }, + { 0 } +}; + +/* Netgraph node type descriptor. */ +static struct ng_type typestruct = { + .version = NG_ABI_VERSION, + .name = NG_NAT_NODE_TYPE, + .constructor = ng_nat_constructor, + .rcvmsg = ng_nat_rcvmsg, + .shutdown = ng_nat_shutdown, + .newhook = ng_nat_newhook, + .rcvdata = ng_nat_rcvdata, + .disconnect = ng_nat_disconnect, + .cmdlist = ng_nat_cmdlist, +}; +NETGRAPH_INIT(nat, &typestruct); +MODULE_DEPEND(ng_nat, libalias, 1, 1, 1); + +/* Information we store for each node. */ +struct ng_priv_priv { + node_p node; /* back pointer to node */ + hook_p in; /* hook for demasquerading */ + hook_p out; /* hook for masquerading */ + struct libalias *lib; /* libalias handler */ + uint32_t flags; /* status flags */ +}; +typedef struct ng_priv_priv *priv_p; + +/* Values of flags */ +#define NGNAT_READY 0x1 /* We have everything to work */ +#define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */ + +static int +ng_nat_constructor(node_p node) +{ + priv_p priv; + + /* Initialize private descriptor. */ + MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, + M_NOWAIT | M_ZERO); + if (priv == NULL) + return (ENOMEM); + + /* Init aliasing engine. */ + priv->lib = LibAliasInit(NULL); + if (priv->lib == NULL) { + FREE(priv, M_NETGRAPH); + return (ENOMEM); + } + + /* Set same ports on. */ + (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS, + PKT_ALIAS_SAME_PORTS); + + /* Link structs together. */ + NG_NODE_SET_PRIVATE(node, priv); + priv->node = node; + + /* + * libalias is not thread safe, so our node + * must be single threaded. + */ + NG_NODE_FORCE_WRITER(node); + + return (0); +} + +static int +ng_nat_newhook(node_p node, hook_p hook, const char *name) +{ + const priv_p priv = NG_NODE_PRIVATE(node); + + if (strcmp(name, NG_NAT_HOOK_IN) == 0) { + priv->in = hook; + } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) { + priv->out = hook; + } else + return (EINVAL); + + if (priv->out != NULL && + priv->in != NULL && + priv->flags & NGNAT_ADDR_DEFINED) + priv->flags |= NGNAT_READY; + + return(0); +} + +static int +ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook) +{ + const priv_p priv = NG_NODE_PRIVATE(node); + struct ng_mesg *resp = NULL; + struct ng_mesg *msg; + int error = 0; + + NGI_GET_MSG(item, msg); + + switch (msg->header.typecookie) { + case NGM_NAT_COOKIE: + switch (msg->header.cmd) { + case NGM_NAT_SET_IPADDR: + { + struct in_addr *const ia = (struct in_addr *)msg->data; + + if (msg->header.arglen < sizeof(*ia)) { + error = EINVAL; + break; + } + + LibAliasSetAddress(priv->lib, *ia); + + priv->flags |= NGNAT_ADDR_DEFINED; + if (priv->out != NULL && + priv->in != NULL) + priv->flags |= NGNAT_READY; + } + break; + default: + error = EINVAL; /* unknown command */ + break; + } + break; + default: + error = EINVAL; /* unknown cookie type */ + break; + } + + NG_RESPOND_MSG(error, node, item, resp); + NG_FREE_MSG(msg); + return (error); +} + +static int +ng_nat_rcvdata(hook_p hook, item_p item ) +{ + const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + struct mbuf *m; + int plen; + int rval, error = 0; + char *c; + + if (!(priv->flags & NGNAT_READY)) { + NG_FREE_ITEM(item); + return (ENXIO); + } + + m = NGI_M(item); + + if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) { + NGI_M(item) = NULL; /* avoid double free */ + NG_FREE_ITEM(item); + return (ENOBUFS); + } + + plen = m->m_pkthdr.len; + + NGI_M(item) = m; + + c = mtod(m, char *); + if (hook == priv->in) { + rval = LibAliasIn(priv->lib, c, plen); + if (rval != PKT_ALIAS_OK) { + printf("in %u\n", rval); + NG_FREE_ITEM(item); + return (EINVAL); + } + NG_FWD_ITEM_HOOK(error, item, priv->out); + } else if (hook == priv->out) { + rval = LibAliasOut(priv->lib, c, plen); + if (rval != PKT_ALIAS_OK) { + printf("out %u\n", rval); + NG_FREE_ITEM(item); + return (EINVAL); + } + NG_FWD_ITEM_HOOK(error, item, priv->in); + } else + panic("ng_nat: unknown hook!\n"); + + return (error); +} + +static int +ng_nat_shutdown(node_p node) +{ + const priv_p priv = NG_NODE_PRIVATE(node); + + NG_NODE_SET_PRIVATE(node, NULL); + NG_NODE_UNREF(node); + LibAliasUninit(priv->lib); + FREE(priv, M_NETGRAPH); + + return (0); +} + +static int +ng_nat_disconnect(hook_p hook) +{ + const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + + priv->flags &= ~NGNAT_READY; + + if (hook == priv->out) + priv->out = NULL; + if (hook == priv->in) + priv->in = NULL; + + if (priv->out == NULL && priv->in == NULL) + ng_rmnode_self(NG_HOOK_NODE(hook)); + + return (0); +} + +/* + * m_megapullup() function is a big hack. + * + * It allocates an mbuf with cluster and copies the whole + * chain into cluster, so that it is all contigous and the + * whole packet can be accessed via char pointer. + * + * This is required, because libalias doesn't have idea + * about mbufs. + */ +static struct mbuf * +m_megapullup(struct mbuf *m, int len) +{ + struct mbuf *mcl; + caddr_t cp; + + if (len > MCLBYTES) + goto bad; + + if ((mcl = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR)) == NULL) + goto bad; + + cp = mtod(mcl, caddr_t); + m_copydata(m, 0, len, cp); + m_move_pkthdr(mcl, m); + mcl->m_len = mcl->m_pkthdr.len; + m_freem(m); + + return (mcl); +bad: + m_freem(m); + return (NULL); +} diff --git a/sys/netgraph/ng_nat.h b/sys/netgraph/ng_nat.h new file mode 100644 index 000000000000..5afcec55a269 --- /dev/null +++ b/sys/netgraph/ng_nat.h @@ -0,0 +1,37 @@ +/*- + * Copyright 2005, Gleb Smirnoff + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define NG_NAT_NODE_TYPE "nat" +#define NGM_NAT_COOKIE 1107718711 + +#define NG_NAT_HOOK_IN "in" +#define NG_NAT_HOOK_OUT "out" + +enum { + NGM_NAT_SET_IPADDR = 1, +};