ng_nat.c revision 146063
1326725Simp/*- 2326725Simp * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org> 3326725Simp * All rights reserved. 4326725Simp * 5326725Simp * Redistribution and use in source and binary forms, with or without 6326725Simp * modification, are permitted provided that the following conditions 7326725Simp * are met: 8326725Simp * 1. Redistributions of source code must retain the above copyright 9326725Simp * notice, this list of conditions and the following disclaimer. 10326725Simp * 2. Redistributions in binary form must reproduce the above copyright 11326725Simp * notice, this list of conditions and the following disclaimer in the 12326725Simp * documentation and/or other materials provided with the distribution. 13326725Simp * 14326725Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15326725Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16326725Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17326725Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18326725Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19326725Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20326725Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21326725Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22326725Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23326725Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24326725Simp * SUCH DAMAGE. 25326725Simp * 26326725Simp * $FreeBSD: head/sys/netgraph/ng_nat.c 146063 2005-05-10 14:19:10Z glebius $ 27326725Simp */ 28326725Simp 29326725Simp#include <sys/param.h> 30326725Simp#include <sys/systm.h> 31326725Simp#include <sys/kernel.h> 32326725Simp#include <sys/mbuf.h> 33326725Simp#include <sys/malloc.h> 34326725Simp#include <sys/ctype.h> 35326725Simp#include <sys/errno.h> 36326725Simp#include <sys/syslog.h> 37326725Simp 38326725Simp#include <netinet/in_systm.h> 39326725Simp#include <netinet/in.h> 40326725Simp#include <netinet/ip.h> 41326725Simp#include <netinet/ip_icmp.h> 42326725Simp#include <netinet/tcp.h> 43326725Simp#include <netinet/udp.h> 44326725Simp 45326725Simp#include <netinet/libalias/alias.h> 46326725Simp 47326725Simp#include <netgraph/ng_message.h> 48326725Simp#include <netgraph/ng_parse.h> 49326725Simp#include <netgraph/ng_nat.h> 50326725Simp#include <netgraph/netgraph.h> 51326725Simp 52326725Simpstatic ng_constructor_t ng_nat_constructor; 53326725Simpstatic ng_rcvmsg_t ng_nat_rcvmsg; 54326725Simpstatic ng_shutdown_t ng_nat_shutdown; 55326725Simpstatic ng_newhook_t ng_nat_newhook; 56326725Simpstatic ng_rcvdata_t ng_nat_rcvdata; 57326725Simpstatic ng_disconnect_t ng_nat_disconnect; 58326725Simp 59326725Simpstatic struct mbuf * m_megapullup(struct mbuf *, int); 60326725Simp 61326725Simp/* List of commands and how to convert arguments to/from ASCII. */ 62326725Simpstatic const struct ng_cmdlist ng_nat_cmdlist[] = { 63326725Simp { 64326725Simp NGM_NAT_COOKIE, 65326725Simp NGM_NAT_SET_IPADDR, 66326725Simp "setaliasaddr", 67326725Simp &ng_parse_ipaddr_type, 68326725Simp NULL 69326725Simp }, 70326725Simp { 0 } 71326725Simp}; 72326725Simp 73326725Simp/* Netgraph node type descriptor. */ 74326725Simpstatic struct ng_type typestruct = { 75326725Simp .version = NG_ABI_VERSION, 76326725Simp .name = NG_NAT_NODE_TYPE, 77326725Simp .constructor = ng_nat_constructor, 78326725Simp .rcvmsg = ng_nat_rcvmsg, 79326725Simp .shutdown = ng_nat_shutdown, 80326725Simp .newhook = ng_nat_newhook, 81326725Simp .rcvdata = ng_nat_rcvdata, 82326725Simp .disconnect = ng_nat_disconnect, 83326725Simp .cmdlist = ng_nat_cmdlist, 84326725Simp}; 85326725SimpNETGRAPH_INIT(nat, &typestruct); 86326725SimpMODULE_DEPEND(ng_nat, libalias, 1, 1, 1); 87326725Simp 88326725Simp/* Information we store for each node. */ 89326725Simpstruct ng_priv_priv { 90326725Simp node_p node; /* back pointer to node */ 91326725Simp hook_p in; /* hook for demasquerading */ 92326725Simp hook_p out; /* hook for masquerading */ 93326725Simp struct libalias *lib; /* libalias handler */ 94326725Simp uint32_t flags; /* status flags */ 95326725Simp}; 96326725Simptypedef struct ng_priv_priv *priv_p; 97326725Simp 98326725Simp/* Values of flags */ 99326725Simp#define NGNAT_READY 0x1 /* We have everything to work */ 100326725Simp#define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */ 101326725Simp 102363558Stsoomestatic int 103326725Simpng_nat_constructor(node_p node) 104326725Simp{ 105326725Simp priv_p priv; 106326725Simp 107326725Simp /* Initialize private descriptor. */ 108326725Simp MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, 109326725Simp M_NOWAIT | M_ZERO); 110326725Simp if (priv == NULL) 111326725Simp return (ENOMEM); 112326725Simp 113326725Simp /* Init aliasing engine. */ 114326725Simp priv->lib = LibAliasInit(NULL); 115326725Simp if (priv->lib == NULL) { 116326725Simp FREE(priv, M_NETGRAPH); 117326725Simp return (ENOMEM); 118326725Simp } 119326725Simp 120326725Simp /* Set same ports on. */ 121326725Simp (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS, 122326725Simp PKT_ALIAS_SAME_PORTS); 123326725Simp 124326725Simp /* Link structs together. */ 125326725Simp NG_NODE_SET_PRIVATE(node, priv); 126326725Simp priv->node = node; 127326725Simp 128326725Simp /* 129326725Simp * libalias is not thread safe, so our node 130326725Simp * must be single threaded. 131326725Simp */ 132326725Simp NG_NODE_FORCE_WRITER(node); 133326725Simp 134326725Simp return (0); 135326725Simp} 136326725Simp 137326725Simpstatic int 138326725Simpng_nat_newhook(node_p node, hook_p hook, const char *name) 139326725Simp{ 140326725Simp const priv_p priv = NG_NODE_PRIVATE(node); 141326725Simp 142326725Simp if (strcmp(name, NG_NAT_HOOK_IN) == 0) { 143326725Simp priv->in = hook; 144326725Simp } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) { 145326725Simp priv->out = hook; 146326725Simp } else 147326725Simp return (EINVAL); 148326725Simp 149326725Simp if (priv->out != NULL && 150326725Simp priv->in != NULL && 151326725Simp priv->flags & NGNAT_ADDR_DEFINED) 152326725Simp priv->flags |= NGNAT_READY; 153326725Simp 154326725Simp return(0); 155326725Simp} 156326725Simp 157326725Simpstatic int 158326725Simpng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook) 159326725Simp{ 160326725Simp const priv_p priv = NG_NODE_PRIVATE(node); 161326725Simp struct ng_mesg *resp = NULL; 162326725Simp struct ng_mesg *msg; 163326725Simp int error = 0; 164326725Simp 165326725Simp NGI_GET_MSG(item, msg); 166326725Simp 167326725Simp switch (msg->header.typecookie) { 168326725Simp case NGM_NAT_COOKIE: 169326725Simp switch (msg->header.cmd) { 170326725Simp case NGM_NAT_SET_IPADDR: 171326725Simp { 172326725Simp struct in_addr *const ia = (struct in_addr *)msg->data; 173332126Skevans 174332126Skevans if (msg->header.arglen < sizeof(*ia)) { 175326725Simp error = EINVAL; 176326725Simp break; 177332126Skevans } 178326725Simp 179326725Simp LibAliasSetAddress(priv->lib, *ia); 180326725Simp 181326725Simp priv->flags |= NGNAT_ADDR_DEFINED; 182326725Simp if (priv->out != NULL && 183326725Simp priv->in != NULL) 184326725Simp priv->flags |= NGNAT_READY; 185326725Simp } 186326725Simp break; 187326725Simp default: 188326725Simp error = EINVAL; /* unknown command */ 189326725Simp break; 190326725Simp } 191326725Simp break; 192326725Simp default: 193326725Simp error = EINVAL; /* unknown cookie type */ 194326727Simp break; 195326725Simp } 196326725Simp 197326725Simp NG_RESPOND_MSG(error, node, item, resp); 198326725Simp NG_FREE_MSG(msg); 199326725Simp return (error); 200326725Simp} 201326725Simp 202326725Simpstatic int 203326725Simpng_nat_rcvdata(hook_p hook, item_p item ) 204326725Simp{ 205326725Simp const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 206326725Simp struct mbuf *m; 207326725Simp struct ip *ip; 208326725Simp int rval, error = 0; 209326725Simp char *c; 210326725Simp 211326725Simp if (!(priv->flags & NGNAT_READY)) { 212326725Simp NG_FREE_ITEM(item); 213326725Simp return (ENXIO); 214326725Simp } 215326725Simp 216326725Simp m = NGI_M(item); 217326725Simp 218326725Simp if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) { 219332126Skevans NGI_M(item) = NULL; /* avoid double free */ 220326725Simp NG_FREE_ITEM(item); 221326725Simp return (ENOBUFS); 222326725Simp } 223326725Simp 224326725Simp NGI_M(item) = m; 225326725Simp 226326725Simp KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len), 227332126Skevans ("ng_nat: ip_len != m_pkthdr.len")); 228326725Simp 229326725Simp c = mtod(m, char *); 230326725Simp ip = mtod(m, struct ip *); 231332126Skevans 232326725Simp if (hook == priv->in) { 233326725Simp rval = LibAliasIn(priv->lib, c, MCLBYTES); 234326725Simp if (rval != PKT_ALIAS_OK) { 235332126Skevans printf("in %u\n", rval); 236326725Simp NG_FREE_ITEM(item); 237326725Simp return (EINVAL); 238326725Simp } 239326725Simp m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len); 240326725Simp NG_FWD_ITEM_HOOK(error, item, priv->out); 241326725Simp } else if (hook == priv->out) { 242326725Simp rval = LibAliasOut(priv->lib, c, MCLBYTES); 243326725Simp if (rval != PKT_ALIAS_OK) { 244326725Simp printf("out %u\n", rval); 245326725Simp NG_FREE_ITEM(item); 246326725Simp return (EINVAL); 247326725Simp } 248326725Simp m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len); 249326725Simp NG_FWD_ITEM_HOOK(error, item, priv->in); 250332126Skevans } else 251326725Simp panic("ng_nat: unknown hook!\n"); 252326725Simp 253326725Simp return (error); 254326725Simp} 255326725Simp 256326725Simpstatic int 257326725Simpng_nat_shutdown(node_p node) 258326725Simp{ 259326725Simp const priv_p priv = NG_NODE_PRIVATE(node); 260326725Simp 261326725Simp NG_NODE_SET_PRIVATE(node, NULL); 262326725Simp NG_NODE_UNREF(node); 263326725Simp LibAliasUninit(priv->lib); 264326725Simp FREE(priv, M_NETGRAPH); 265326725Simp 266326727Simp return (0); 267326725Simp} 268326725Simp 269326725Simpstatic int 270326725Simpng_nat_disconnect(hook_p hook) 271326725Simp{ 272326725Simp const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 273326725Simp 274326725Simp priv->flags &= ~NGNAT_READY; 275326725Simp 276326725Simp if (hook == priv->out) 277326725Simp priv->out = NULL; 278326725Simp if (hook == priv->in) 279326725Simp priv->in = NULL; 280326725Simp 281326725Simp if (priv->out == NULL && priv->in == NULL) 282332126Skevans ng_rmnode_self(NG_HOOK_NODE(hook)); 283326725Simp 284326725Simp return (0); 285326725Simp} 286326725Simp 287326725Simp/* 288332126Skevans * m_megapullup() function is a big hack. 289332126Skevans * 290332126Skevans * It allocates an mbuf with cluster and copies the whole 291332126Skevans * chain into cluster, so that it is all contigous and the 292326725Simp * whole packet can be accessed via char pointer. 293326725Simp * 294326725Simp * This is required, because libalias doesn't have idea 295326725Simp * about mbufs. 296326725Simp */ 297326725Simpstatic struct mbuf * 298326725Simpm_megapullup(struct mbuf *m, int len) 299326725Simp{ 300326725Simp struct mbuf *mcl; 301326725Simp caddr_t cp; 302326725Simp 303326725Simp if (len > MCLBYTES) 304326725Simp goto bad; 305326725Simp 306326725Simp if ((mcl = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR)) == NULL) 307326725Simp goto bad; 308326725Simp 309326725Simp cp = mtod(mcl, caddr_t); 310326725Simp m_copydata(m, 0, len, cp); 311326725Simp m_move_pkthdr(mcl, m); 312326725Simp mcl->m_len = mcl->m_pkthdr.len; 313326725Simp m_freem(m); 314326725Simp 315326726Simp return (mcl); 316326725Simpbad: 317326725Simp m_freem(m); 318326725Simp return (NULL); 319326725Simp} 320326725Simp