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