Index: stable/6/sys/netgraph/ng_nat.c =================================================================== --- stable/6/sys/netgraph/ng_nat.c (revision 171591) +++ stable/6/sys/netgraph/ng_nat.c (revision 171592) @@ -1,364 +1,445 @@ /*- * 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); +static unsigned int ng_nat_translate_flags(unsigned int x); + +/* Parse type for struct ng_nat_mode. */ +static const struct ng_parse_struct_field ng_nat_mode_fields[] + = NG_NAT_MODE_INFO; +static const struct ng_parse_type ng_nat_mode_type = { + &ng_parse_struct_type, + ng_nat_mode_fields +}; + /* 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 }, + { + NGM_NAT_COOKIE, + NGM_NAT_SET_MODE, + "setmode", + &ng_nat_mode_type, + NULL + }, + { + NGM_NAT_COOKIE, + NGM_NAT_SET_TARGET, + "settarget", + &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 { +struct ng_nat_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; +typedef struct ng_nat_priv *priv_p; /* Values of flags */ -#define NGNAT_READY 0x1 /* We have everything to work */ +#define NGNAT_CONNECTED 0x1 /* We have both hooks connected */ #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; + priv->in != NULL) + priv->flags |= NGNAT_CONNECTED; 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; + case NGM_NAT_SET_MODE: + { + struct ng_nat_mode *const mode = + (struct ng_nat_mode *)msg->data; + + if (msg->header.arglen < sizeof(*mode)) { + error = EINVAL; + break; + } + + if (LibAliasSetMode(priv->lib, + ng_nat_translate_flags(mode->flags), + ng_nat_translate_flags(mode->mask)) < 0) { + error = ENOMEM; + break; + } + } + break; + case NGM_NAT_SET_TARGET: + { + struct in_addr *const ia = (struct in_addr *)msg->data; + + if (msg->header.arglen < sizeof(*ia)) { + error = EINVAL; + break; + } + + LibAliasSetTarget(priv->lib, *ia); + } + 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; struct ip *ip; int rval, error = 0; char *c; - if (!(priv->flags & NGNAT_READY)) { + /* We have no required hooks. */ + if (!(priv->flags & NGNAT_CONNECTED)) { NG_FREE_ITEM(item); return (ENXIO); } + /* We have no alias address yet to do anything. */ + if (!(priv->flags & NGNAT_ADDR_DEFINED)) + goto send; + 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); } NGI_M(item) = m; c = mtod(m, char *); ip = mtod(m, struct ip *); KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len), ("ng_nat: ip_len != m_pkthdr.len")); if (hook == priv->in) { rval = LibAliasIn(priv->lib, c, MCLBYTES); if (rval != PKT_ALIAS_OK && rval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) { NG_FREE_ITEM(item); return (EINVAL); } } else if (hook == priv->out) { rval = LibAliasOut(priv->lib, c, MCLBYTES); if (rval != PKT_ALIAS_OK) { NG_FREE_ITEM(item); return (EINVAL); } } else panic("ng_nat: unknown hook!\n"); m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len); if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && ip->ip_p == IPPROTO_TCP) { struct tcphdr *th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); /* * Here is our terrible HACK. * * Sometimes LibAlias edits contents of TCP packet. * In this case it needs to recompute full TCP * checksum. However, the problem is that LibAlias * doesn't have any idea about checksum offloading * in kernel. To workaround this, we do not do * checksumming in LibAlias, but only mark the * packets in th_x2 field. If we receive a marked * packet, we calculate correct checksum for it * aware of offloading. * * Why do I do such a terrible hack instead of * recalculating checksum for each packet? * Because the previous checksum was not checked! * Recalculating checksums for EVERY packet will * hide ALL transmission errors. Yes, marked packets * still suffer from this problem. But, sigh, natd(8) * has this problem, too. */ if (th->th_x2) { th->th_x2 = 0; ip->ip_len = ntohs(ip->ip_len); th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons(IPPROTO_TCP + ip->ip_len - (ip->ip_hl << 2))); if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) { m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum); in_delayed_cksum(m); } ip->ip_len = htons(ip->ip_len); } } +send: if (hook == priv->in) NG_FWD_ITEM_HOOK(error, item, priv->out); else NG_FWD_ITEM_HOOK(error, item, priv->in); 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; + priv->flags &= ~NGNAT_CONNECTED; 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); +} + +static unsigned int +ng_nat_translate_flags(unsigned int x) +{ + unsigned int res = 0; + +#ifdef PKT_ALIAS_LOG + if (x & NG_NAT_LOG) + res |= PKT_ALIAS_LOG; +#endif + if (x & NG_NAT_DENY_INCOMING) + res |= PKT_ALIAS_DENY_INCOMING; + if (x & NG_NAT_SAME_PORTS) + res |= PKT_ALIAS_SAME_PORTS; + if (x & NG_NAT_UNREGISTERED_ONLY) + res |= PKT_ALIAS_UNREGISTERED_ONLY; + if (x & NG_NAT_RESET_ON_ADDR_CHANGE) + res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE; + if (x & NG_NAT_PROXY_ONLY) + res |= PKT_ALIAS_PROXY_ONLY; + if (x & NG_NAT_REVERSE) + res |= PKT_ALIAS_REVERSE; + + return (res); } /* * 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); } Index: stable/6/sys/netgraph/ng_nat.h =================================================================== --- stable/6/sys/netgraph/ng_nat.h (revision 171591) +++ stable/6/sys/netgraph/ng_nat.h (revision 171592) @@ -1,37 +1,60 @@ /*- * 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" +/* Arguments for NGM_NAT_SET_MODE message */ +struct ng_nat_mode { + uint32_t flags; + uint32_t mask; +}; + +/* Keep this in sync with the above structure definition */ +#define NG_NAT_MODE_INFO { \ + { "flags", &ng_parse_uint32_type }, \ + { "mask", &ng_parse_uint32_type }, \ + { NULL } \ +} + +#define NG_NAT_LOG 0x01 +#define NG_NAT_DENY_INCOMING 0x02 +#define NG_NAT_SAME_PORTS 0x04 +#define NG_NAT_UNREGISTERED_ONLY 0x10 +#define NG_NAT_RESET_ON_ADDR_CHANGE 0x20 +#define NG_NAT_PROXY_ONLY 0x40 +#define NG_NAT_REVERSE 0x80 + enum { NGM_NAT_SET_IPADDR = 1, + NGM_NAT_SET_MODE, + NGM_NAT_SET_TARGET, };