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