1145937Sglebius/*-
2145937Sglebius * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
3145937Sglebius * All rights reserved.
4145937Sglebius *
5145937Sglebius * Redistribution and use in source and binary forms, with or without
6145937Sglebius * modification, are permitted provided that the following conditions
7145937Sglebius * are met:
8145937Sglebius * 1. Redistributions of source code must retain the above copyright
9145937Sglebius *    notice, this list of conditions and the following disclaimer.
10145937Sglebius * 2. Redistributions in binary form must reproduce the above copyright
11145937Sglebius *    notice, this list of conditions and the following disclaimer in the
12145937Sglebius *    documentation and/or other materials provided with the distribution.
13145937Sglebius *
14145937Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15145937Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16145937Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17145937Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18145937Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19145937Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20145937Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21145937Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22145937Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23145937Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24145937Sglebius * SUCH DAMAGE.
25145937Sglebius *
26145937Sglebius * $FreeBSD$
27145937Sglebius */
28145937Sglebius
29145937Sglebius#include <sys/param.h>
30145937Sglebius#include <sys/systm.h>
31145937Sglebius#include <sys/kernel.h>
32145937Sglebius#include <sys/mbuf.h>
33145937Sglebius#include <sys/malloc.h>
34145937Sglebius#include <sys/ctype.h>
35145937Sglebius#include <sys/errno.h>
36145937Sglebius#include <sys/syslog.h>
37145937Sglebius
38145937Sglebius#include <netinet/in_systm.h>
39145937Sglebius#include <netinet/in.h>
40145937Sglebius#include <netinet/ip.h>
41147625Sglebius#include <netinet/ip_var.h>
42145937Sglebius#include <netinet/tcp.h>
43147625Sglebius#include <machine/in_cksum.h>
44145937Sglebius
45145937Sglebius#include <netinet/libalias/alias.h>
46222808Sae#include <netinet/libalias/alias_local.h>
47145937Sglebius
48145937Sglebius#include <netgraph/ng_message.h>
49145937Sglebius#include <netgraph/ng_parse.h>
50145937Sglebius#include <netgraph/ng_nat.h>
51145937Sglebius#include <netgraph/netgraph.h>
52145937Sglebius
53145937Sglebiusstatic ng_constructor_t	ng_nat_constructor;
54145937Sglebiusstatic ng_rcvmsg_t	ng_nat_rcvmsg;
55145937Sglebiusstatic ng_shutdown_t	ng_nat_shutdown;
56145937Sglebiusstatic ng_newhook_t	ng_nat_newhook;
57145937Sglebiusstatic ng_rcvdata_t	ng_nat_rcvdata;
58145937Sglebiusstatic ng_disconnect_t	ng_nat_disconnect;
59145937Sglebius
60169867Smavstatic unsigned int	ng_nat_translate_flags(unsigned int x);
61169867Smav
62169867Smav/* Parse type for struct ng_nat_mode. */
63169867Smavstatic const struct ng_parse_struct_field ng_nat_mode_fields[]
64169867Smav	= NG_NAT_MODE_INFO;
65169867Smavstatic const struct ng_parse_type ng_nat_mode_type = {
66169867Smav	&ng_parse_struct_type,
67176706Smav	&ng_nat_mode_fields
68169867Smav};
69169867Smav
70176706Smav/* Parse type for 'description' field in structs. */
71176706Smavstatic const struct ng_parse_fixedstring_info ng_nat_description_info
72176706Smav	= { NG_NAT_DESC_LENGTH };
73176706Smavstatic const struct ng_parse_type ng_nat_description_type = {
74176706Smav	&ng_parse_fixedstring_type,
75176706Smav	&ng_nat_description_info
76176706Smav};
77176706Smav
78176706Smav/* Parse type for struct ng_nat_redirect_port. */
79176706Smavstatic const struct ng_parse_struct_field ng_nat_redirect_port_fields[]
80176706Smav	= NG_NAT_REDIRECT_PORT_TYPE_INFO(&ng_nat_description_type);
81176706Smavstatic const struct ng_parse_type ng_nat_redirect_port_type = {
82176706Smav	&ng_parse_struct_type,
83176706Smav	&ng_nat_redirect_port_fields
84176706Smav};
85176706Smav
86176706Smav/* Parse type for struct ng_nat_redirect_addr. */
87176706Smavstatic const struct ng_parse_struct_field ng_nat_redirect_addr_fields[]
88176706Smav	= NG_NAT_REDIRECT_ADDR_TYPE_INFO(&ng_nat_description_type);
89176706Smavstatic const struct ng_parse_type ng_nat_redirect_addr_type = {
90176706Smav	&ng_parse_struct_type,
91176706Smav	&ng_nat_redirect_addr_fields
92176706Smav};
93176706Smav
94176706Smav/* Parse type for struct ng_nat_redirect_proto. */
95176706Smavstatic const struct ng_parse_struct_field ng_nat_redirect_proto_fields[]
96176706Smav	= NG_NAT_REDIRECT_PROTO_TYPE_INFO(&ng_nat_description_type);
97176706Smavstatic const struct ng_parse_type ng_nat_redirect_proto_type = {
98176706Smav	&ng_parse_struct_type,
99176706Smav	&ng_nat_redirect_proto_fields
100176706Smav};
101176706Smav
102176706Smav/* Parse type for struct ng_nat_add_server. */
103176706Smavstatic const struct ng_parse_struct_field ng_nat_add_server_fields[]
104176706Smav	= NG_NAT_ADD_SERVER_TYPE_INFO;
105176706Smavstatic const struct ng_parse_type ng_nat_add_server_type = {
106176706Smav	&ng_parse_struct_type,
107176706Smav	&ng_nat_add_server_fields
108176706Smav};
109176706Smav
110176706Smav/* Parse type for one struct ng_nat_listrdrs_entry. */
111176706Smavstatic const struct ng_parse_struct_field ng_nat_listrdrs_entry_fields[]
112176706Smav	= NG_NAT_LISTRDRS_ENTRY_TYPE_INFO(&ng_nat_description_type);
113176706Smavstatic const struct ng_parse_type ng_nat_listrdrs_entry_type = {
114176706Smav	&ng_parse_struct_type,
115176706Smav	&ng_nat_listrdrs_entry_fields
116176706Smav};
117176706Smav
118176706Smav/* Parse type for 'redirects' array in struct ng_nat_list_redirects. */
119176706Smavstatic int
120176706Smavng_nat_listrdrs_ary_getLength(const struct ng_parse_type *type,
121176706Smav	const u_char *start, const u_char *buf)
122176706Smav{
123176706Smav	const struct ng_nat_list_redirects *lr;
124176706Smav
125176706Smav	lr = (const struct ng_nat_list_redirects *)
126176706Smav	    (buf - offsetof(struct ng_nat_list_redirects, redirects));
127176706Smav	return lr->total_count;
128176706Smav}
129176706Smav
130176706Smavstatic const struct ng_parse_array_info ng_nat_listrdrs_ary_info = {
131176706Smav	&ng_nat_listrdrs_entry_type,
132176706Smav	&ng_nat_listrdrs_ary_getLength,
133176706Smav	NULL
134176706Smav};
135176706Smavstatic const struct ng_parse_type ng_nat_listrdrs_ary_type = {
136176706Smav	&ng_parse_array_type,
137176706Smav	&ng_nat_listrdrs_ary_info
138176706Smav};
139176706Smav
140176706Smav/* Parse type for struct ng_nat_list_redirects. */
141176706Smavstatic const struct ng_parse_struct_field ng_nat_list_redirects_fields[]
142176706Smav	= NG_NAT_LIST_REDIRECTS_TYPE_INFO(&ng_nat_listrdrs_ary_type);
143176706Smavstatic const struct ng_parse_type ng_nat_list_redirects_type = {
144176706Smav	&ng_parse_struct_type,
145176706Smav	&ng_nat_list_redirects_fields
146176706Smav};
147176706Smav
148248570Sglebius/* Parse type for struct ng_nat_libalias_info. */
149248570Sglebiusstatic const struct ng_parse_struct_field ng_nat_libalias_info_fields[]
150248570Sglebius	= NG_NAT_LIBALIAS_INFO;
151248570Sglebiusstatic const struct ng_parse_type ng_nat_libalias_info_type = {
152248570Sglebius	&ng_parse_struct_type,
153248570Sglebius	&ng_nat_libalias_info_fields
154248570Sglebius};
155248570Sglebius
156145937Sglebius/* List of commands and how to convert arguments to/from ASCII. */
157145937Sglebiusstatic const struct ng_cmdlist ng_nat_cmdlist[] = {
158145937Sglebius	{
159145937Sglebius	  NGM_NAT_COOKIE,
160145937Sglebius	  NGM_NAT_SET_IPADDR,
161145937Sglebius	  "setaliasaddr",
162145937Sglebius	  &ng_parse_ipaddr_type,
163145937Sglebius	  NULL
164145937Sglebius	},
165169867Smav	{
166169867Smav	  NGM_NAT_COOKIE,
167169867Smav	  NGM_NAT_SET_MODE,
168169867Smav	  "setmode",
169169867Smav	  &ng_nat_mode_type,
170169867Smav	  NULL
171169867Smav	},
172169867Smav	{
173169867Smav	  NGM_NAT_COOKIE,
174169867Smav	  NGM_NAT_SET_TARGET,
175169867Smav	  "settarget",
176169867Smav	  &ng_parse_ipaddr_type,
177169867Smav	  NULL
178169867Smav	},
179176706Smav	{
180176706Smav	  NGM_NAT_COOKIE,
181176706Smav	  NGM_NAT_REDIRECT_PORT,
182176706Smav	  "redirectport",
183176706Smav	  &ng_nat_redirect_port_type,
184176706Smav	  &ng_parse_uint32_type
185176706Smav	},
186176706Smav	{
187176706Smav	  NGM_NAT_COOKIE,
188176706Smav	  NGM_NAT_REDIRECT_ADDR,
189176706Smav	  "redirectaddr",
190176706Smav	  &ng_nat_redirect_addr_type,
191176706Smav	  &ng_parse_uint32_type
192176706Smav	},
193176706Smav	{
194176706Smav	  NGM_NAT_COOKIE,
195176706Smav	  NGM_NAT_REDIRECT_PROTO,
196176706Smav	  "redirectproto",
197176706Smav	  &ng_nat_redirect_proto_type,
198176706Smav	  &ng_parse_uint32_type
199176706Smav	},
200176706Smav	{
201176706Smav	  NGM_NAT_COOKIE,
202176706Smav	  NGM_NAT_REDIRECT_DYNAMIC,
203176706Smav	  "redirectdynamic",
204176706Smav	  &ng_parse_uint32_type,
205176706Smav	  NULL
206176706Smav	},
207176706Smav	{
208176706Smav	  NGM_NAT_COOKIE,
209176706Smav	  NGM_NAT_REDIRECT_DELETE,
210176706Smav	  "redirectdelete",
211176706Smav	  &ng_parse_uint32_type,
212176706Smav	  NULL
213176706Smav	},
214176706Smav	{
215176706Smav	  NGM_NAT_COOKIE,
216176706Smav	  NGM_NAT_ADD_SERVER,
217176706Smav	  "addserver",
218176706Smav	  &ng_nat_add_server_type,
219176706Smav	  NULL
220176706Smav	},
221176706Smav	{
222176706Smav	  NGM_NAT_COOKIE,
223176706Smav	  NGM_NAT_LIST_REDIRECTS,
224176706Smav	  "listredirects",
225176706Smav	  NULL,
226176706Smav	  &ng_nat_list_redirects_type
227176706Smav	},
228176706Smav	{
229176706Smav	  NGM_NAT_COOKIE,
230176706Smav	  NGM_NAT_PROXY_RULE,
231176706Smav	  "proxyrule",
232176706Smav	  &ng_parse_string_type,
233176706Smav	  NULL
234176706Smav	},
235248570Sglebius	{
236248570Sglebius	  NGM_NAT_COOKIE,
237248570Sglebius	  NGM_NAT_LIBALIAS_INFO,
238248570Sglebius	  "libaliasinfo",
239248570Sglebius	  NULL,
240248570Sglebius	  &ng_nat_libalias_info_type
241248570Sglebius	},
242145937Sglebius	{ 0 }
243145937Sglebius};
244145937Sglebius
245145937Sglebius/* Netgraph node type descriptor. */
246145937Sglebiusstatic struct ng_type typestruct = {
247145937Sglebius	.version =	NG_ABI_VERSION,
248145937Sglebius	.name =		NG_NAT_NODE_TYPE,
249145937Sglebius	.constructor =	ng_nat_constructor,
250145937Sglebius	.rcvmsg =	ng_nat_rcvmsg,
251145937Sglebius	.shutdown =	ng_nat_shutdown,
252145937Sglebius	.newhook =	ng_nat_newhook,
253145937Sglebius	.rcvdata =	ng_nat_rcvdata,
254145937Sglebius	.disconnect =	ng_nat_disconnect,
255145937Sglebius	.cmdlist =	ng_nat_cmdlist,
256145937Sglebius};
257145937SglebiusNETGRAPH_INIT(nat, &typestruct);
258145937SglebiusMODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
259145937Sglebius
260176706Smav/* Element for list of redirects. */
261176706Smavstruct ng_nat_rdr_lst {
262176706Smav	STAILQ_ENTRY(ng_nat_rdr_lst) entries;
263176706Smav	struct alias_link	*lnk;
264176706Smav	struct ng_nat_listrdrs_entry rdr;
265176706Smav};
266176706SmavSTAILQ_HEAD(rdrhead, ng_nat_rdr_lst);
267176706Smav
268145937Sglebius/* Information we store for each node. */
269163297Sglebiusstruct ng_nat_priv {
270145937Sglebius	node_p		node;		/* back pointer to node */
271145937Sglebius	hook_p		in;		/* hook for demasquerading */
272145937Sglebius	hook_p		out;		/* hook for masquerading */
273145937Sglebius	struct libalias	*lib;		/* libalias handler */
274145937Sglebius	uint32_t	flags;		/* status flags */
275176706Smav	uint32_t	rdrcount;	/* number or redirects in list */
276176706Smav	uint32_t	nextid;		/* for next in turn in list */
277176706Smav	struct rdrhead	redirhead;	/* redirect list header */
278145937Sglebius};
279163297Sglebiustypedef struct ng_nat_priv *priv_p;
280145937Sglebius
281145937Sglebius/* Values of flags */
282169866Smav#define	NGNAT_CONNECTED		0x1	/* We have both hooks connected */
283145937Sglebius#define	NGNAT_ADDR_DEFINED	0x2	/* NGM_NAT_SET_IPADDR happened */
284145937Sglebius
285145937Sglebiusstatic int
286145937Sglebiusng_nat_constructor(node_p node)
287145937Sglebius{
288145937Sglebius	priv_p priv;
289145937Sglebius
290145937Sglebius	/* Initialize private descriptor. */
291220768Sglebius	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
292145937Sglebius
293145937Sglebius	/* Init aliasing engine. */
294145937Sglebius	priv->lib = LibAliasInit(NULL);
295145937Sglebius
296145937Sglebius	/* Set same ports on. */
297145937Sglebius	(void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
298145937Sglebius	    PKT_ALIAS_SAME_PORTS);
299145937Sglebius
300176706Smav	/* Init redirects housekeeping. */
301176706Smav	priv->rdrcount = 0;
302176706Smav	priv->nextid = 1;
303176706Smav	STAILQ_INIT(&priv->redirhead);
304176706Smav
305145937Sglebius	/* Link structs together. */
306145937Sglebius	NG_NODE_SET_PRIVATE(node, priv);
307145937Sglebius	priv->node = node;
308145937Sglebius
309145937Sglebius	/*
310145937Sglebius	 * libalias is not thread safe, so our node
311145937Sglebius	 * must be single threaded.
312145937Sglebius	 */
313145937Sglebius	NG_NODE_FORCE_WRITER(node);
314145937Sglebius
315145937Sglebius	return (0);
316145937Sglebius}
317145937Sglebius
318145937Sglebiusstatic int
319145937Sglebiusng_nat_newhook(node_p node, hook_p hook, const char *name)
320145937Sglebius{
321145937Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
322145937Sglebius
323145937Sglebius	if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
324145937Sglebius		priv->in = hook;
325145937Sglebius	} else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
326145937Sglebius		priv->out = hook;
327145937Sglebius	} else
328145937Sglebius		return (EINVAL);
329145937Sglebius
330145937Sglebius	if (priv->out != NULL &&
331169866Smav	    priv->in != NULL)
332169866Smav		priv->flags |= NGNAT_CONNECTED;
333145937Sglebius
334145937Sglebius	return(0);
335145937Sglebius}
336145937Sglebius
337145937Sglebiusstatic int
338145937Sglebiusng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
339145937Sglebius{
340145937Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
341145937Sglebius	struct ng_mesg *resp = NULL;
342145937Sglebius	struct ng_mesg *msg;
343145937Sglebius	int error = 0;
344145937Sglebius
345145937Sglebius	NGI_GET_MSG(item, msg);
346145937Sglebius
347145937Sglebius	switch (msg->header.typecookie) {
348145937Sglebius	case NGM_NAT_COOKIE:
349145937Sglebius		switch (msg->header.cmd) {
350145937Sglebius		case NGM_NAT_SET_IPADDR:
351145937Sglebius		    {
352145937Sglebius			struct in_addr *const ia = (struct in_addr *)msg->data;
353145937Sglebius
354145937Sglebius			if (msg->header.arglen < sizeof(*ia)) {
355145937Sglebius				error = EINVAL;
356145937Sglebius				break;
357145937Sglebius			}
358145937Sglebius
359145937Sglebius			LibAliasSetAddress(priv->lib, *ia);
360145937Sglebius
361145937Sglebius			priv->flags |= NGNAT_ADDR_DEFINED;
362145937Sglebius		    }
363145937Sglebius			break;
364169867Smav		case NGM_NAT_SET_MODE:
365169867Smav		    {
366169867Smav			struct ng_nat_mode *const mode =
367169867Smav			    (struct ng_nat_mode *)msg->data;
368169867Smav
369169867Smav			if (msg->header.arglen < sizeof(*mode)) {
370169867Smav				error = EINVAL;
371169867Smav				break;
372169867Smav			}
373169867Smav
374169867Smav			if (LibAliasSetMode(priv->lib,
375169867Smav			    ng_nat_translate_flags(mode->flags),
376169867Smav			    ng_nat_translate_flags(mode->mask)) < 0) {
377169867Smav				error = ENOMEM;
378169867Smav				break;
379169867Smav			}
380169867Smav		    }
381169867Smav			break;
382169867Smav		case NGM_NAT_SET_TARGET:
383169867Smav		    {
384169867Smav			struct in_addr *const ia = (struct in_addr *)msg->data;
385169867Smav
386169867Smav			if (msg->header.arglen < sizeof(*ia)) {
387169867Smav				error = EINVAL;
388169867Smav				break;
389169867Smav			}
390169867Smav
391169867Smav			LibAliasSetTarget(priv->lib, *ia);
392169867Smav		    }
393169867Smav			break;
394176706Smav		case NGM_NAT_REDIRECT_PORT:
395176706Smav		    {
396176706Smav			struct ng_nat_rdr_lst *entry;
397176706Smav			struct ng_nat_redirect_port *const rp =
398176706Smav			    (struct ng_nat_redirect_port *)msg->data;
399176706Smav
400176706Smav			if (msg->header.arglen < sizeof(*rp)) {
401176706Smav				error = EINVAL;
402176706Smav				break;
403176706Smav			}
404176706Smav
405176706Smav			if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
406176706Smav			    M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
407176706Smav				error = ENOMEM;
408176706Smav				break;
409176706Smav			}
410176706Smav
411176706Smav			/* Try actual redirect. */
412176706Smav			entry->lnk = LibAliasRedirectPort(priv->lib,
413176706Smav				rp->local_addr, htons(rp->local_port),
414176706Smav				rp->remote_addr, htons(rp->remote_port),
415176706Smav				rp->alias_addr, htons(rp->alias_port),
416176706Smav				rp->proto);
417176706Smav
418176706Smav			if (entry->lnk == NULL) {
419176706Smav				error = ENOMEM;
420184205Sdes				free(entry, M_NETGRAPH);
421176706Smav				break;
422176706Smav			}
423176706Smav
424176706Smav			/* Successful, save info in our internal list. */
425176706Smav			entry->rdr.local_addr = rp->local_addr;
426176706Smav			entry->rdr.alias_addr = rp->alias_addr;
427176706Smav			entry->rdr.remote_addr = rp->remote_addr;
428176706Smav			entry->rdr.local_port = rp->local_port;
429176706Smav			entry->rdr.alias_port = rp->alias_port;
430176706Smav			entry->rdr.remote_port = rp->remote_port;
431176706Smav			entry->rdr.proto = rp->proto;
432176706Smav			bcopy(rp->description, entry->rdr.description,
433176706Smav			    NG_NAT_DESC_LENGTH);
434176706Smav
435176706Smav			/* Safety precaution. */
436176706Smav			entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
437176706Smav
438176706Smav			entry->rdr.id = priv->nextid++;
439176706Smav			priv->rdrcount++;
440176706Smav
441176706Smav			/* Link to list of redirects. */
442176706Smav			STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
443176706Smav
444176706Smav			/* Response with id of newly added entry. */
445176706Smav			NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
446176706Smav			if (resp == NULL) {
447176706Smav				error = ENOMEM;
448176706Smav				break;
449176706Smav			}
450176706Smav			bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
451176706Smav		    }
452176706Smav			break;
453176706Smav		case NGM_NAT_REDIRECT_ADDR:
454176706Smav		    {
455176706Smav			struct ng_nat_rdr_lst *entry;
456176706Smav			struct ng_nat_redirect_addr *const ra =
457176706Smav			    (struct ng_nat_redirect_addr *)msg->data;
458176706Smav
459176706Smav			if (msg->header.arglen < sizeof(*ra)) {
460176706Smav				error = EINVAL;
461176706Smav				break;
462176706Smav			}
463176706Smav
464176706Smav			if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
465176706Smav			    M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
466176706Smav				error = ENOMEM;
467176706Smav				break;
468176706Smav			}
469176706Smav
470176706Smav			/* Try actual redirect. */
471176706Smav			entry->lnk = LibAliasRedirectAddr(priv->lib,
472176706Smav				ra->local_addr, ra->alias_addr);
473176706Smav
474176706Smav			if (entry->lnk == NULL) {
475176706Smav				error = ENOMEM;
476184205Sdes				free(entry, M_NETGRAPH);
477176706Smav				break;
478176706Smav			}
479176706Smav
480176706Smav			/* Successful, save info in our internal list. */
481176706Smav			entry->rdr.local_addr = ra->local_addr;
482176706Smav			entry->rdr.alias_addr = ra->alias_addr;
483176706Smav			entry->rdr.proto = NG_NAT_REDIRPROTO_ADDR;
484176706Smav			bcopy(ra->description, entry->rdr.description,
485176706Smav			    NG_NAT_DESC_LENGTH);
486176706Smav
487176706Smav			/* Safety precaution. */
488176706Smav			entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
489176706Smav
490176706Smav			entry->rdr.id = priv->nextid++;
491176706Smav			priv->rdrcount++;
492176706Smav
493176706Smav			/* Link to list of redirects. */
494176706Smav			STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
495176706Smav
496176706Smav			/* Response with id of newly added entry. */
497176706Smav			NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
498176706Smav			if (resp == NULL) {
499176706Smav				error = ENOMEM;
500176706Smav				break;
501176706Smav			}
502176706Smav			bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
503176706Smav		    }
504176706Smav			break;
505176706Smav		case NGM_NAT_REDIRECT_PROTO:
506176706Smav		    {
507176706Smav			struct ng_nat_rdr_lst *entry;
508176706Smav			struct ng_nat_redirect_proto *const rp =
509176706Smav			    (struct ng_nat_redirect_proto *)msg->data;
510176706Smav
511176706Smav			if (msg->header.arglen < sizeof(*rp)) {
512176706Smav				error = EINVAL;
513176706Smav				break;
514176706Smav			}
515176706Smav
516176706Smav			if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
517176706Smav			    M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
518176706Smav				error = ENOMEM;
519176706Smav				break;
520176706Smav			}
521176706Smav
522176706Smav			/* Try actual redirect. */
523176706Smav			entry->lnk = LibAliasRedirectProto(priv->lib,
524176706Smav				rp->local_addr, rp->remote_addr,
525176706Smav				rp->alias_addr, rp->proto);
526176706Smav
527176706Smav			if (entry->lnk == NULL) {
528176706Smav				error = ENOMEM;
529184205Sdes				free(entry, M_NETGRAPH);
530176706Smav				break;
531176706Smav			}
532176706Smav
533176706Smav			/* Successful, save info in our internal list. */
534176706Smav			entry->rdr.local_addr = rp->local_addr;
535176706Smav			entry->rdr.alias_addr = rp->alias_addr;
536176706Smav			entry->rdr.remote_addr = rp->remote_addr;
537176706Smav			entry->rdr.proto = rp->proto;
538176706Smav			bcopy(rp->description, entry->rdr.description,
539176706Smav			    NG_NAT_DESC_LENGTH);
540176706Smav
541176706Smav			/* Safety precaution. */
542176706Smav			entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
543176706Smav
544176706Smav			entry->rdr.id = priv->nextid++;
545176706Smav			priv->rdrcount++;
546176706Smav
547176706Smav			/* Link to list of redirects. */
548176706Smav			STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
549176706Smav
550176706Smav			/* Response with id of newly added entry. */
551176706Smav			NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
552176706Smav			if (resp == NULL) {
553176706Smav				error = ENOMEM;
554176706Smav				break;
555176706Smav			}
556176706Smav			bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
557176706Smav		    }
558176706Smav			break;
559176706Smav		case NGM_NAT_REDIRECT_DYNAMIC:
560176706Smav		case NGM_NAT_REDIRECT_DELETE:
561176706Smav		    {
562176706Smav			struct ng_nat_rdr_lst *entry;
563176706Smav			uint32_t *const id = (uint32_t *)msg->data;
564176706Smav
565176706Smav			if (msg->header.arglen < sizeof(*id)) {
566176706Smav				error = EINVAL;
567176706Smav				break;
568176706Smav			}
569176706Smav
570176706Smav			/* Find entry with supplied id. */
571176706Smav			STAILQ_FOREACH(entry, &priv->redirhead, entries) {
572176706Smav				if (entry->rdr.id == *id)
573176706Smav					break;
574176706Smav			}
575176706Smav
576176706Smav			/* Not found. */
577176706Smav			if (entry == NULL) {
578176706Smav				error = ENOENT;
579176706Smav				break;
580176706Smav			}
581176706Smav
582176706Smav			if (msg->header.cmd == NGM_NAT_REDIRECT_DYNAMIC) {
583176706Smav				if (LibAliasRedirectDynamic(priv->lib,
584176706Smav				    entry->lnk) == -1) {
585176706Smav					error = ENOTTY;	/* XXX Something better? */
586176706Smav					break;
587176706Smav				}
588176706Smav			} else {	/* NGM_NAT_REDIRECT_DELETE */
589176706Smav				LibAliasRedirectDelete(priv->lib, entry->lnk);
590176706Smav			}
591176706Smav
592176706Smav			/* Delete entry from our internal list. */
593176706Smav			priv->rdrcount--;
594176706Smav			STAILQ_REMOVE(&priv->redirhead, entry, ng_nat_rdr_lst, entries);
595184205Sdes			free(entry, M_NETGRAPH);
596176706Smav		    }
597176706Smav			break;
598176706Smav		case NGM_NAT_ADD_SERVER:
599176706Smav		    {
600176706Smav			struct ng_nat_rdr_lst *entry;
601176706Smav			struct ng_nat_add_server *const as =
602176706Smav			    (struct ng_nat_add_server *)msg->data;
603176706Smav
604176706Smav			if (msg->header.arglen < sizeof(*as)) {
605176706Smav				error = EINVAL;
606176706Smav				break;
607176706Smav			}
608176706Smav
609176706Smav			/* Find entry with supplied id. */
610176706Smav			STAILQ_FOREACH(entry, &priv->redirhead, entries) {
611176706Smav				if (entry->rdr.id == as->id)
612176706Smav					break;
613176706Smav			}
614176706Smav
615176706Smav			/* Not found. */
616176706Smav			if (entry == NULL) {
617176706Smav				error = ENOENT;
618176706Smav				break;
619176706Smav			}
620176706Smav
621176706Smav			if (LibAliasAddServer(priv->lib, entry->lnk,
622176706Smav			    as->addr, htons(as->port)) == -1) {
623176706Smav				error = ENOMEM;
624176706Smav				break;
625176706Smav			}
626176706Smav
627176706Smav			entry->rdr.lsnat++;
628176706Smav		    }
629176706Smav			break;
630176706Smav		case NGM_NAT_LIST_REDIRECTS:
631176706Smav		    {
632176706Smav			struct ng_nat_rdr_lst *entry;
633176706Smav			struct ng_nat_list_redirects *ary;
634176706Smav			int i = 0;
635176706Smav
636176706Smav			NG_MKRESPONSE(resp, msg, sizeof(*ary) +
637176706Smav			    (priv->rdrcount) * sizeof(*entry), M_NOWAIT);
638176706Smav			if (resp == NULL) {
639176706Smav				error = ENOMEM;
640176706Smav				break;
641176706Smav			}
642176706Smav
643176706Smav			ary = (struct ng_nat_list_redirects *)resp->data;
644176706Smav			ary->total_count = priv->rdrcount;
645176706Smav
646176706Smav			STAILQ_FOREACH(entry, &priv->redirhead, entries) {
647176706Smav				bcopy(&entry->rdr, &ary->redirects[i++],
648176706Smav				    sizeof(struct ng_nat_listrdrs_entry));
649176706Smav			}
650176706Smav		    }
651176706Smav			break;
652176706Smav		case NGM_NAT_PROXY_RULE:
653176706Smav		    {
654176706Smav			char *cmd = (char *)msg->data;
655176706Smav
656176706Smav			if (msg->header.arglen < 6) {
657176706Smav				error = EINVAL;
658176706Smav				break;
659176706Smav			}
660176706Smav
661176706Smav			if (LibAliasProxyRule(priv->lib, cmd) != 0)
662176706Smav				error = ENOMEM;
663176706Smav		    }
664176706Smav			break;
665248570Sglebius		case NGM_NAT_LIBALIAS_INFO:
666248570Sglebius		    {
667248570Sglebius			struct ng_nat_libalias_info *i;
668248570Sglebius
669248570Sglebius			NG_MKRESPONSE(resp, msg,
670248570Sglebius			    sizeof(struct ng_nat_libalias_info), M_NOWAIT);
671248570Sglebius			if (resp == NULL) {
672248570Sglebius				error = ENOMEM;
673248570Sglebius				break;
674248570Sglebius			}
675248570Sglebius			i = (struct ng_nat_libalias_info *)resp->data;
676248570Sglebius#define	COPY(F)	do {						\
677248570Sglebius	if (priv->lib->F >= 0 && priv->lib->F < UINT32_MAX)	\
678248570Sglebius		i->F = priv->lib->F;				\
679248570Sglebius	else							\
680248570Sglebius		i->F = UINT32_MAX;				\
681248570Sglebius} while (0)
682248570Sglebius
683248570Sglebius			COPY(icmpLinkCount);
684248570Sglebius			COPY(udpLinkCount);
685248570Sglebius			COPY(tcpLinkCount);
686248570Sglebius			COPY(pptpLinkCount);
687248570Sglebius			COPY(sctpLinkCount);
688248570Sglebius			COPY(protoLinkCount);
689248570Sglebius			COPY(fragmentIdLinkCount);
690248570Sglebius			COPY(fragmentPtrLinkCount);
691248570Sglebius			COPY(sockCount);
692248570Sglebius#undef COPY
693248570Sglebius		    }
694248570Sglebius			break;
695145937Sglebius		default:
696145937Sglebius			error = EINVAL;		/* unknown command */
697145937Sglebius			break;
698145937Sglebius		}
699145937Sglebius		break;
700145937Sglebius	default:
701145937Sglebius		error = EINVAL;			/* unknown cookie type */
702145937Sglebius		break;
703145937Sglebius	}
704145937Sglebius
705145937Sglebius	NG_RESPOND_MSG(error, node, item, resp);
706145937Sglebius	NG_FREE_MSG(msg);
707145937Sglebius	return (error);
708145937Sglebius}
709145937Sglebius
710145937Sglebiusstatic int
711145937Sglebiusng_nat_rcvdata(hook_p hook, item_p item )
712145937Sglebius{
713145937Sglebius	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
714145937Sglebius	struct mbuf	*m;
715146063Sglebius	struct ip	*ip;
716145937Sglebius	int rval, error = 0;
717145937Sglebius	char *c;
718145937Sglebius
719169866Smav	/* We have no required hooks. */
720169866Smav	if (!(priv->flags & NGNAT_CONNECTED)) {
721145937Sglebius		NG_FREE_ITEM(item);
722145937Sglebius		return (ENXIO);
723145937Sglebius	}
724145937Sglebius
725169866Smav	/* We have no alias address yet to do anything. */
726169866Smav	if (!(priv->flags & NGNAT_ADDR_DEFINED))
727169866Smav		goto send;
728169866Smav
729145937Sglebius	m = NGI_M(item);
730145937Sglebius
731145937Sglebius	if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
732145937Sglebius		NGI_M(item) = NULL;	/* avoid double free */
733145937Sglebius		NG_FREE_ITEM(item);
734145937Sglebius		return (ENOBUFS);
735145937Sglebius	}
736145937Sglebius
737145937Sglebius	NGI_M(item) = m;
738145937Sglebius
739146084Sglebius	c = mtod(m, char *);
740146084Sglebius	ip = mtod(m, struct ip *);
741146084Sglebius
742146063Sglebius	KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
743146063Sglebius	    ("ng_nat: ip_len != m_pkthdr.len"));
744146063Sglebius
745222808Sae	/*
746222808Sae	 * We drop packet when:
747222808Sae	 * 1. libalias returns PKT_ALIAS_ERROR;
748222808Sae	 * 2. For incoming packets:
749222808Sae	 *	a) for unresolved fragments;
750222808Sae	 *	b) libalias returns PKT_ALIAS_IGNORED and
751222808Sae	 *		PKT_ALIAS_DENY_INCOMING flag is set.
752222808Sae	 */
753145937Sglebius	if (hook == priv->in) {
754179477Smav		rval = LibAliasIn(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
755222808Sae		if (rval == PKT_ALIAS_ERROR ||
756222808Sae		    rval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
757222808Sae		    (rval == PKT_ALIAS_IGNORED &&
758222808Sae		     (priv->lib->packetAliasMode &
759222808Sae		      PKT_ALIAS_DENY_INCOMING) != 0)) {
760145937Sglebius			NG_FREE_ITEM(item);
761145937Sglebius			return (EINVAL);
762145937Sglebius		}
763145937Sglebius	} else if (hook == priv->out) {
764179477Smav		rval = LibAliasOut(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
765222808Sae		if (rval == PKT_ALIAS_ERROR) {
766145937Sglebius			NG_FREE_ITEM(item);
767145937Sglebius			return (EINVAL);
768145937Sglebius		}
769145937Sglebius	} else
770145937Sglebius		panic("ng_nat: unknown hook!\n");
771145937Sglebius
772222808Sae	if (rval == PKT_ALIAS_RESPOND)
773222808Sae		m->m_flags |= M_SKIP_FIREWALL;
774147625Sglebius	m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
775147625Sglebius
776147625Sglebius	if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
777147625Sglebius	    ip->ip_p == IPPROTO_TCP) {
778165119Sglebius		struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
779165119Sglebius		    (ip->ip_hl << 2));
780147625Sglebius
781147625Sglebius		/*
782147625Sglebius		 * Here is our terrible HACK.
783147625Sglebius		 *
784147625Sglebius		 * Sometimes LibAlias edits contents of TCP packet.
785147625Sglebius		 * In this case it needs to recompute full TCP
786147625Sglebius		 * checksum. However, the problem is that LibAlias
787147625Sglebius		 * doesn't have any idea about checksum offloading
788147625Sglebius		 * in kernel. To workaround this, we do not do
789147625Sglebius		 * checksumming in LibAlias, but only mark the
790147625Sglebius		 * packets in th_x2 field. If we receive a marked
791147625Sglebius		 * packet, we calculate correct checksum for it
792147625Sglebius		 * aware of offloading.
793147625Sglebius		 *
794147625Sglebius		 * Why do I do such a terrible hack instead of
795147625Sglebius		 * recalculating checksum for each packet?
796147625Sglebius		 * Because the previous checksum was not checked!
797147625Sglebius		 * Recalculating checksums for EVERY packet will
798147625Sglebius		 * hide ALL transmission errors. Yes, marked packets
799147625Sglebius		 * still suffer from this problem. But, sigh, natd(8)
800147625Sglebius		 * has this problem, too.
801147625Sglebius		 */
802147625Sglebius
803147625Sglebius		if (th->th_x2) {
804241344Sglebius			uint16_t ip_len = ntohs(ip->ip_len);
805241344Sglebius
806147625Sglebius			th->th_x2 = 0;
807147625Sglebius			th->th_sum = in_pseudo(ip->ip_src.s_addr,
808147625Sglebius			    ip->ip_dst.s_addr, htons(IPPROTO_TCP +
809241344Sglebius			    ip_len - (ip->ip_hl << 2)));
810147625Sglebius
811147625Sglebius			if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
812147625Sglebius				m->m_pkthdr.csum_data = offsetof(struct tcphdr,
813147625Sglebius				    th_sum);
814147625Sglebius				in_delayed_cksum(m);
815147625Sglebius			}
816147625Sglebius		}
817147625Sglebius	}
818147625Sglebius
819169866Smavsend:
820147625Sglebius	if (hook == priv->in)
821147625Sglebius		NG_FWD_ITEM_HOOK(error, item, priv->out);
822147625Sglebius	else
823147625Sglebius		NG_FWD_ITEM_HOOK(error, item, priv->in);
824147625Sglebius
825145937Sglebius	return (error);
826145937Sglebius}
827145937Sglebius
828145937Sglebiusstatic int
829145937Sglebiusng_nat_shutdown(node_p node)
830145937Sglebius{
831145937Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
832145937Sglebius
833145937Sglebius	NG_NODE_SET_PRIVATE(node, NULL);
834145937Sglebius	NG_NODE_UNREF(node);
835176706Smav
836176706Smav	/* Free redirects list. */
837176706Smav	while (!STAILQ_EMPTY(&priv->redirhead)) {
838176706Smav		struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead);
839176706Smav		STAILQ_REMOVE_HEAD(&priv->redirhead, entries);
840184205Sdes		free(entry, M_NETGRAPH);
841176706Smav	};
842176706Smav
843176706Smav	/* Final free. */
844145937Sglebius	LibAliasUninit(priv->lib);
845184205Sdes	free(priv, M_NETGRAPH);
846145937Sglebius
847145937Sglebius	return (0);
848145937Sglebius}
849145937Sglebius
850145937Sglebiusstatic int
851145937Sglebiusng_nat_disconnect(hook_p hook)
852145937Sglebius{
853145937Sglebius	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
854145937Sglebius
855169866Smav	priv->flags &= ~NGNAT_CONNECTED;
856145937Sglebius
857145937Sglebius	if (hook == priv->out)
858145937Sglebius		priv->out = NULL;
859145937Sglebius	if (hook == priv->in)
860145937Sglebius		priv->in = NULL;
861145937Sglebius
862145937Sglebius	if (priv->out == NULL && priv->in == NULL)
863145937Sglebius		ng_rmnode_self(NG_HOOK_NODE(hook));
864145937Sglebius
865145937Sglebius	return (0);
866145937Sglebius}
867145937Sglebius
868169867Smavstatic unsigned int
869169867Smavng_nat_translate_flags(unsigned int x)
870169867Smav{
871169867Smav	unsigned int	res = 0;
872169867Smav
873169867Smav	if (x & NG_NAT_LOG)
874169867Smav		res |= PKT_ALIAS_LOG;
875169867Smav	if (x & NG_NAT_DENY_INCOMING)
876169867Smav		res |= PKT_ALIAS_DENY_INCOMING;
877169867Smav	if (x & NG_NAT_SAME_PORTS)
878169867Smav		res |= PKT_ALIAS_SAME_PORTS;
879169867Smav	if (x & NG_NAT_UNREGISTERED_ONLY)
880169867Smav		res |= PKT_ALIAS_UNREGISTERED_ONLY;
881169867Smav	if (x & NG_NAT_RESET_ON_ADDR_CHANGE)
882169867Smav		res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
883169867Smav	if (x & NG_NAT_PROXY_ONLY)
884169867Smav		res |= PKT_ALIAS_PROXY_ONLY;
885169867Smav	if (x & NG_NAT_REVERSE)
886169867Smav		res |= PKT_ALIAS_REVERSE;
887169867Smav
888169867Smav	return (res);
889169867Smav}
890