xref: /freebsd/sys/netgraph/ng_nat.c (revision d429ea332342fcb98d27a350d0c4944bf9aec3f9)
1 /*-
2  * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/mbuf.h>
33 #include <sys/malloc.h>
34 #include <sys/ctype.h>
35 #include <sys/errno.h>
36 #include <sys/syslog.h>
37 
38 #include <netinet/in_systm.h>
39 #include <netinet/in.h>
40 #include <netinet/ip.h>
41 #include <netinet/ip_icmp.h>
42 #include <netinet/tcp.h>
43 #include <netinet/udp.h>
44 
45 #include <netinet/libalias/alias.h>
46 
47 #include <netgraph/ng_message.h>
48 #include <netgraph/ng_parse.h>
49 #include <netgraph/ng_nat.h>
50 #include <netgraph/netgraph.h>
51 
52 static ng_constructor_t	ng_nat_constructor;
53 static ng_rcvmsg_t	ng_nat_rcvmsg;
54 static ng_shutdown_t	ng_nat_shutdown;
55 static ng_newhook_t	ng_nat_newhook;
56 static ng_rcvdata_t	ng_nat_rcvdata;
57 static ng_disconnect_t	ng_nat_disconnect;
58 
59 static struct mbuf * m_megapullup(struct mbuf *, int);
60 
61 /* List of commands and how to convert arguments to/from ASCII. */
62 static const struct ng_cmdlist ng_nat_cmdlist[] = {
63 	{
64 	  NGM_NAT_COOKIE,
65 	  NGM_NAT_SET_IPADDR,
66 	  "setaliasaddr",
67 	  &ng_parse_ipaddr_type,
68 	  NULL
69 	},
70 	{ 0 }
71 };
72 
73 /* Netgraph node type descriptor. */
74 static struct ng_type typestruct = {
75 	.version =	NG_ABI_VERSION,
76 	.name =		NG_NAT_NODE_TYPE,
77 	.constructor =	ng_nat_constructor,
78 	.rcvmsg =	ng_nat_rcvmsg,
79 	.shutdown =	ng_nat_shutdown,
80 	.newhook =	ng_nat_newhook,
81 	.rcvdata =	ng_nat_rcvdata,
82 	.disconnect =	ng_nat_disconnect,
83 	.cmdlist =	ng_nat_cmdlist,
84 };
85 NETGRAPH_INIT(nat, &typestruct);
86 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
87 
88 /* Information we store for each node. */
89 struct ng_priv_priv {
90 	node_p		node;		/* back pointer to node */
91 	hook_p		in;		/* hook for demasquerading */
92 	hook_p		out;		/* hook for masquerading */
93 	struct libalias	*lib;		/* libalias handler */
94 	uint32_t	flags;		/* status flags */
95 };
96 typedef struct ng_priv_priv *priv_p;
97 
98 /* Values of flags */
99 #define	NGNAT_READY		0x1	/* We have everything to work */
100 #define	NGNAT_ADDR_DEFINED	0x2	/* NGM_NAT_SET_IPADDR happened */
101 
102 static int
103 ng_nat_constructor(node_p node)
104 {
105 	priv_p priv;
106 
107 	/* Initialize private descriptor. */
108 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH,
109 		M_NOWAIT | M_ZERO);
110 	if (priv == NULL)
111 		return (ENOMEM);
112 
113 	/* Init aliasing engine. */
114 	priv->lib = LibAliasInit(NULL);
115 	if (priv->lib == NULL) {
116 		FREE(priv, M_NETGRAPH);
117 		return (ENOMEM);
118 	}
119 
120 	/* Set same ports on. */
121 	(void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
122 	    PKT_ALIAS_SAME_PORTS);
123 
124 	/* Link structs together. */
125 	NG_NODE_SET_PRIVATE(node, priv);
126 	priv->node = node;
127 
128 	/*
129 	 * libalias is not thread safe, so our node
130 	 * must be single threaded.
131 	 */
132 	NG_NODE_FORCE_WRITER(node);
133 
134 	return (0);
135 }
136 
137 static int
138 ng_nat_newhook(node_p node, hook_p hook, const char *name)
139 {
140 	const priv_p priv = NG_NODE_PRIVATE(node);
141 
142 	if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
143 		priv->in = hook;
144 	} else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
145 		priv->out = hook;
146 	} else
147 		return (EINVAL);
148 
149 	if (priv->out != NULL &&
150 	    priv->in != NULL &&
151 	    priv->flags & NGNAT_ADDR_DEFINED)
152 		priv->flags |= NGNAT_READY;
153 
154 	return(0);
155 }
156 
157 static int
158 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
159 {
160 	const priv_p priv = NG_NODE_PRIVATE(node);
161 	struct ng_mesg *resp = NULL;
162 	struct ng_mesg *msg;
163 	int error = 0;
164 
165 	NGI_GET_MSG(item, msg);
166 
167 	switch (msg->header.typecookie) {
168 	case NGM_NAT_COOKIE:
169 		switch (msg->header.cmd) {
170 		case NGM_NAT_SET_IPADDR:
171 		    {
172 			struct in_addr *const ia = (struct in_addr *)msg->data;
173 
174 			if (msg->header.arglen < sizeof(*ia)) {
175 				error = EINVAL;
176 				break;
177 			}
178 
179 			LibAliasSetAddress(priv->lib, *ia);
180 
181 			priv->flags |= NGNAT_ADDR_DEFINED;
182 			if (priv->out != NULL &&
183 			    priv->in != NULL)
184 				priv->flags |= NGNAT_READY;
185 		    }
186 			break;
187 		default:
188 			error = EINVAL;		/* unknown command */
189 			break;
190 		}
191 		break;
192 	default:
193 		error = EINVAL;			/* unknown cookie type */
194 		break;
195 	}
196 
197 	NG_RESPOND_MSG(error, node, item, resp);
198 	NG_FREE_MSG(msg);
199 	return (error);
200 }
201 
202 static int
203 ng_nat_rcvdata(hook_p hook, item_p item )
204 {
205 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
206 	struct mbuf	*m;
207 	struct ip	*ip;
208 	int rval, error = 0;
209 	char *c;
210 
211 	if (!(priv->flags & NGNAT_READY)) {
212 		NG_FREE_ITEM(item);
213 		return (ENXIO);
214 	}
215 
216 	m = NGI_M(item);
217 
218 	if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
219 		NGI_M(item) = NULL;	/* avoid double free */
220 		NG_FREE_ITEM(item);
221 		return (ENOBUFS);
222 	}
223 
224 	NGI_M(item) = m;
225 
226 	c = mtod(m, char *);
227 	ip = mtod(m, struct ip *);
228 
229 	KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
230 	    ("ng_nat: ip_len != m_pkthdr.len"));
231 
232 	if (hook == priv->in) {
233 		rval = LibAliasIn(priv->lib, c, MCLBYTES);
234 		if (rval != PKT_ALIAS_OK) {
235 			printf("in %u\n", rval);
236 			NG_FREE_ITEM(item);
237 			return (EINVAL);
238 		}
239 		m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
240 		NG_FWD_ITEM_HOOK(error, item, priv->out);
241 	} else if (hook == priv->out) {
242 		rval = LibAliasOut(priv->lib, c, MCLBYTES);
243 		if (rval != PKT_ALIAS_OK) {
244 			printf("out %u\n", rval);
245 			NG_FREE_ITEM(item);
246 			return (EINVAL);
247 		}
248 		m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
249 		NG_FWD_ITEM_HOOK(error, item, priv->in);
250 	} else
251 		panic("ng_nat: unknown hook!\n");
252 
253 	return (error);
254 }
255 
256 static int
257 ng_nat_shutdown(node_p node)
258 {
259 	const priv_p priv = NG_NODE_PRIVATE(node);
260 
261 	NG_NODE_SET_PRIVATE(node, NULL);
262 	NG_NODE_UNREF(node);
263 	LibAliasUninit(priv->lib);
264 	FREE(priv, M_NETGRAPH);
265 
266 	return (0);
267 }
268 
269 static int
270 ng_nat_disconnect(hook_p hook)
271 {
272 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
273 
274 	priv->flags &= ~NGNAT_READY;
275 
276 	if (hook == priv->out)
277 		priv->out = NULL;
278 	if (hook == priv->in)
279 		priv->in = NULL;
280 
281 	if (priv->out == NULL && priv->in == NULL)
282 		ng_rmnode_self(NG_HOOK_NODE(hook));
283 
284 	return (0);
285 }
286 
287 /*
288  * m_megapullup() function is a big hack.
289  *
290  * It allocates an mbuf with cluster and copies the whole
291  * chain into cluster, so that it is all contigous and the
292  * whole packet can be accessed via char pointer.
293  *
294  * This is required, because libalias doesn't have idea
295  * about mbufs.
296  */
297 static struct mbuf *
298 m_megapullup(struct mbuf *m, int len)
299 {
300 	struct mbuf *mcl;
301 	caddr_t cp;
302 
303 	if (len > MCLBYTES)
304 		goto bad;
305 
306 	if ((mcl = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR)) == NULL)
307 		goto bad;
308 
309 	cp = mtod(mcl, caddr_t);
310 	m_copydata(m, 0, len, cp);
311 	m_move_pkthdr(mcl, m);
312 	mcl->m_len = mcl->m_pkthdr.len;
313 	m_freem(m);
314 
315 	return (mcl);
316 bad:
317 	m_freem(m);
318 	return (NULL);
319 }
320