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_var.h>
42#include <netinet/tcp.h>
43#include <machine/in_cksum.h>
44
45#include <netinet/libalias/alias.h>
46#include <netinet/libalias/alias_local.h>
47
48#include <netgraph/ng_message.h>
49#include <netgraph/ng_parse.h>
50#include <netgraph/ng_nat.h>
51#include <netgraph/netgraph.h>
52
53static ng_constructor_t	ng_nat_constructor;
54static ng_rcvmsg_t	ng_nat_rcvmsg;
55static ng_shutdown_t	ng_nat_shutdown;
56static ng_newhook_t	ng_nat_newhook;
57static ng_rcvdata_t	ng_nat_rcvdata;
58static ng_disconnect_t	ng_nat_disconnect;
59
60static unsigned int	ng_nat_translate_flags(unsigned int x);
61
62/* Parse type for struct ng_nat_mode. */
63static const struct ng_parse_struct_field ng_nat_mode_fields[]
64	= NG_NAT_MODE_INFO;
65static const struct ng_parse_type ng_nat_mode_type = {
66	&ng_parse_struct_type,
67	&ng_nat_mode_fields
68};
69
70/* Parse type for 'description' field in structs. */
71static const struct ng_parse_fixedstring_info ng_nat_description_info
72	= { NG_NAT_DESC_LENGTH };
73static const struct ng_parse_type ng_nat_description_type = {
74	&ng_parse_fixedstring_type,
75	&ng_nat_description_info
76};
77
78/* Parse type for struct ng_nat_redirect_port. */
79static const struct ng_parse_struct_field ng_nat_redirect_port_fields[]
80	= NG_NAT_REDIRECT_PORT_TYPE_INFO(&ng_nat_description_type);
81static const struct ng_parse_type ng_nat_redirect_port_type = {
82	&ng_parse_struct_type,
83	&ng_nat_redirect_port_fields
84};
85
86/* Parse type for struct ng_nat_redirect_addr. */
87static const struct ng_parse_struct_field ng_nat_redirect_addr_fields[]
88	= NG_NAT_REDIRECT_ADDR_TYPE_INFO(&ng_nat_description_type);
89static const struct ng_parse_type ng_nat_redirect_addr_type = {
90	&ng_parse_struct_type,
91	&ng_nat_redirect_addr_fields
92};
93
94/* Parse type for struct ng_nat_redirect_proto. */
95static const struct ng_parse_struct_field ng_nat_redirect_proto_fields[]
96	= NG_NAT_REDIRECT_PROTO_TYPE_INFO(&ng_nat_description_type);
97static const struct ng_parse_type ng_nat_redirect_proto_type = {
98	&ng_parse_struct_type,
99	&ng_nat_redirect_proto_fields
100};
101
102/* Parse type for struct ng_nat_add_server. */
103static const struct ng_parse_struct_field ng_nat_add_server_fields[]
104	= NG_NAT_ADD_SERVER_TYPE_INFO;
105static const struct ng_parse_type ng_nat_add_server_type = {
106	&ng_parse_struct_type,
107	&ng_nat_add_server_fields
108};
109
110/* Parse type for one struct ng_nat_listrdrs_entry. */
111static const struct ng_parse_struct_field ng_nat_listrdrs_entry_fields[]
112	= NG_NAT_LISTRDRS_ENTRY_TYPE_INFO(&ng_nat_description_type);
113static const struct ng_parse_type ng_nat_listrdrs_entry_type = {
114	&ng_parse_struct_type,
115	&ng_nat_listrdrs_entry_fields
116};
117
118/* Parse type for 'redirects' array in struct ng_nat_list_redirects. */
119static int
120ng_nat_listrdrs_ary_getLength(const struct ng_parse_type *type,
121	const u_char *start, const u_char *buf)
122{
123	const struct ng_nat_list_redirects *lr;
124
125	lr = (const struct ng_nat_list_redirects *)
126	    (buf - offsetof(struct ng_nat_list_redirects, redirects));
127	return lr->total_count;
128}
129
130static const struct ng_parse_array_info ng_nat_listrdrs_ary_info = {
131	&ng_nat_listrdrs_entry_type,
132	&ng_nat_listrdrs_ary_getLength,
133	NULL
134};
135static const struct ng_parse_type ng_nat_listrdrs_ary_type = {
136	&ng_parse_array_type,
137	&ng_nat_listrdrs_ary_info
138};
139
140/* Parse type for struct ng_nat_list_redirects. */
141static const struct ng_parse_struct_field ng_nat_list_redirects_fields[]
142	= NG_NAT_LIST_REDIRECTS_TYPE_INFO(&ng_nat_listrdrs_ary_type);
143static const struct ng_parse_type ng_nat_list_redirects_type = {
144	&ng_parse_struct_type,
145	&ng_nat_list_redirects_fields
146};
147
148/* Parse type for struct ng_nat_libalias_info. */
149static const struct ng_parse_struct_field ng_nat_libalias_info_fields[]
150	= NG_NAT_LIBALIAS_INFO;
151static const struct ng_parse_type ng_nat_libalias_info_type = {
152	&ng_parse_struct_type,
153	&ng_nat_libalias_info_fields
154};
155
156/* List of commands and how to convert arguments to/from ASCII. */
157static const struct ng_cmdlist ng_nat_cmdlist[] = {
158	{
159	  NGM_NAT_COOKIE,
160	  NGM_NAT_SET_IPADDR,
161	  "setaliasaddr",
162	  &ng_parse_ipaddr_type,
163	  NULL
164	},
165	{
166	  NGM_NAT_COOKIE,
167	  NGM_NAT_SET_MODE,
168	  "setmode",
169	  &ng_nat_mode_type,
170	  NULL
171	},
172	{
173	  NGM_NAT_COOKIE,
174	  NGM_NAT_SET_TARGET,
175	  "settarget",
176	  &ng_parse_ipaddr_type,
177	  NULL
178	},
179	{
180	  NGM_NAT_COOKIE,
181	  NGM_NAT_REDIRECT_PORT,
182	  "redirectport",
183	  &ng_nat_redirect_port_type,
184	  &ng_parse_uint32_type
185	},
186	{
187	  NGM_NAT_COOKIE,
188	  NGM_NAT_REDIRECT_ADDR,
189	  "redirectaddr",
190	  &ng_nat_redirect_addr_type,
191	  &ng_parse_uint32_type
192	},
193	{
194	  NGM_NAT_COOKIE,
195	  NGM_NAT_REDIRECT_PROTO,
196	  "redirectproto",
197	  &ng_nat_redirect_proto_type,
198	  &ng_parse_uint32_type
199	},
200	{
201	  NGM_NAT_COOKIE,
202	  NGM_NAT_REDIRECT_DYNAMIC,
203	  "redirectdynamic",
204	  &ng_parse_uint32_type,
205	  NULL
206	},
207	{
208	  NGM_NAT_COOKIE,
209	  NGM_NAT_REDIRECT_DELETE,
210	  "redirectdelete",
211	  &ng_parse_uint32_type,
212	  NULL
213	},
214	{
215	  NGM_NAT_COOKIE,
216	  NGM_NAT_ADD_SERVER,
217	  "addserver",
218	  &ng_nat_add_server_type,
219	  NULL
220	},
221	{
222	  NGM_NAT_COOKIE,
223	  NGM_NAT_LIST_REDIRECTS,
224	  "listredirects",
225	  NULL,
226	  &ng_nat_list_redirects_type
227	},
228	{
229	  NGM_NAT_COOKIE,
230	  NGM_NAT_PROXY_RULE,
231	  "proxyrule",
232	  &ng_parse_string_type,
233	  NULL
234	},
235	{
236	  NGM_NAT_COOKIE,
237	  NGM_NAT_LIBALIAS_INFO,
238	  "libaliasinfo",
239	  NULL,
240	  &ng_nat_libalias_info_type
241	},
242	{ 0 }
243};
244
245/* Netgraph node type descriptor. */
246static struct ng_type typestruct = {
247	.version =	NG_ABI_VERSION,
248	.name =		NG_NAT_NODE_TYPE,
249	.constructor =	ng_nat_constructor,
250	.rcvmsg =	ng_nat_rcvmsg,
251	.shutdown =	ng_nat_shutdown,
252	.newhook =	ng_nat_newhook,
253	.rcvdata =	ng_nat_rcvdata,
254	.disconnect =	ng_nat_disconnect,
255	.cmdlist =	ng_nat_cmdlist,
256};
257NETGRAPH_INIT(nat, &typestruct);
258MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
259
260/* Element for list of redirects. */
261struct ng_nat_rdr_lst {
262	STAILQ_ENTRY(ng_nat_rdr_lst) entries;
263	struct alias_link	*lnk;
264	struct ng_nat_listrdrs_entry rdr;
265};
266STAILQ_HEAD(rdrhead, ng_nat_rdr_lst);
267
268/* Information we store for each node. */
269struct ng_nat_priv {
270	node_p		node;		/* back pointer to node */
271	hook_p		in;		/* hook for demasquerading */
272	hook_p		out;		/* hook for masquerading */
273	struct libalias	*lib;		/* libalias handler */
274	uint32_t	flags;		/* status flags */
275	uint32_t	rdrcount;	/* number or redirects in list */
276	uint32_t	nextid;		/* for next in turn in list */
277	struct rdrhead	redirhead;	/* redirect list header */
278};
279typedef struct ng_nat_priv *priv_p;
280
281/* Values of flags */
282#define	NGNAT_CONNECTED		0x1	/* We have both hooks connected */
283#define	NGNAT_ADDR_DEFINED	0x2	/* NGM_NAT_SET_IPADDR happened */
284
285static int
286ng_nat_constructor(node_p node)
287{
288	priv_p priv;
289
290	/* Initialize private descriptor. */
291	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
292
293	/* Init aliasing engine. */
294	priv->lib = LibAliasInit(NULL);
295
296	/* Set same ports on. */
297	(void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
298	    PKT_ALIAS_SAME_PORTS);
299
300	/* Init redirects housekeeping. */
301	priv->rdrcount = 0;
302	priv->nextid = 1;
303	STAILQ_INIT(&priv->redirhead);
304
305	/* Link structs together. */
306	NG_NODE_SET_PRIVATE(node, priv);
307	priv->node = node;
308
309	/*
310	 * libalias is not thread safe, so our node
311	 * must be single threaded.
312	 */
313	NG_NODE_FORCE_WRITER(node);
314
315	return (0);
316}
317
318static int
319ng_nat_newhook(node_p node, hook_p hook, const char *name)
320{
321	const priv_p priv = NG_NODE_PRIVATE(node);
322
323	if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
324		priv->in = hook;
325	} else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
326		priv->out = hook;
327	} else
328		return (EINVAL);
329
330	if (priv->out != NULL &&
331	    priv->in != NULL)
332		priv->flags |= NGNAT_CONNECTED;
333
334	return(0);
335}
336
337static int
338ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
339{
340	const priv_p priv = NG_NODE_PRIVATE(node);
341	struct ng_mesg *resp = NULL;
342	struct ng_mesg *msg;
343	int error = 0;
344
345	NGI_GET_MSG(item, msg);
346
347	switch (msg->header.typecookie) {
348	case NGM_NAT_COOKIE:
349		switch (msg->header.cmd) {
350		case NGM_NAT_SET_IPADDR:
351		    {
352			struct in_addr *const ia = (struct in_addr *)msg->data;
353
354			if (msg->header.arglen < sizeof(*ia)) {
355				error = EINVAL;
356				break;
357			}
358
359			LibAliasSetAddress(priv->lib, *ia);
360
361			priv->flags |= NGNAT_ADDR_DEFINED;
362		    }
363			break;
364		case NGM_NAT_SET_MODE:
365		    {
366			struct ng_nat_mode *const mode =
367			    (struct ng_nat_mode *)msg->data;
368
369			if (msg->header.arglen < sizeof(*mode)) {
370				error = EINVAL;
371				break;
372			}
373
374			if (LibAliasSetMode(priv->lib,
375			    ng_nat_translate_flags(mode->flags),
376			    ng_nat_translate_flags(mode->mask)) < 0) {
377				error = ENOMEM;
378				break;
379			}
380		    }
381			break;
382		case NGM_NAT_SET_TARGET:
383		    {
384			struct in_addr *const ia = (struct in_addr *)msg->data;
385
386			if (msg->header.arglen < sizeof(*ia)) {
387				error = EINVAL;
388				break;
389			}
390
391			LibAliasSetTarget(priv->lib, *ia);
392		    }
393			break;
394		case NGM_NAT_REDIRECT_PORT:
395		    {
396			struct ng_nat_rdr_lst *entry;
397			struct ng_nat_redirect_port *const rp =
398			    (struct ng_nat_redirect_port *)msg->data;
399
400			if (msg->header.arglen < sizeof(*rp)) {
401				error = EINVAL;
402				break;
403			}
404
405			if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
406			    M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
407				error = ENOMEM;
408				break;
409			}
410
411			/* Try actual redirect. */
412			entry->lnk = LibAliasRedirectPort(priv->lib,
413				rp->local_addr, htons(rp->local_port),
414				rp->remote_addr, htons(rp->remote_port),
415				rp->alias_addr, htons(rp->alias_port),
416				rp->proto);
417
418			if (entry->lnk == NULL) {
419				error = ENOMEM;
420				free(entry, M_NETGRAPH);
421				break;
422			}
423
424			/* Successful, save info in our internal list. */
425			entry->rdr.local_addr = rp->local_addr;
426			entry->rdr.alias_addr = rp->alias_addr;
427			entry->rdr.remote_addr = rp->remote_addr;
428			entry->rdr.local_port = rp->local_port;
429			entry->rdr.alias_port = rp->alias_port;
430			entry->rdr.remote_port = rp->remote_port;
431			entry->rdr.proto = rp->proto;
432			bcopy(rp->description, entry->rdr.description,
433			    NG_NAT_DESC_LENGTH);
434
435			/* Safety precaution. */
436			entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
437
438			entry->rdr.id = priv->nextid++;
439			priv->rdrcount++;
440
441			/* Link to list of redirects. */
442			STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
443
444			/* Response with id of newly added entry. */
445			NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
446			if (resp == NULL) {
447				error = ENOMEM;
448				break;
449			}
450			bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
451		    }
452			break;
453		case NGM_NAT_REDIRECT_ADDR:
454		    {
455			struct ng_nat_rdr_lst *entry;
456			struct ng_nat_redirect_addr *const ra =
457			    (struct ng_nat_redirect_addr *)msg->data;
458
459			if (msg->header.arglen < sizeof(*ra)) {
460				error = EINVAL;
461				break;
462			}
463
464			if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
465			    M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
466				error = ENOMEM;
467				break;
468			}
469
470			/* Try actual redirect. */
471			entry->lnk = LibAliasRedirectAddr(priv->lib,
472				ra->local_addr, ra->alias_addr);
473
474			if (entry->lnk == NULL) {
475				error = ENOMEM;
476				free(entry, M_NETGRAPH);
477				break;
478			}
479
480			/* Successful, save info in our internal list. */
481			entry->rdr.local_addr = ra->local_addr;
482			entry->rdr.alias_addr = ra->alias_addr;
483			entry->rdr.proto = NG_NAT_REDIRPROTO_ADDR;
484			bcopy(ra->description, entry->rdr.description,
485			    NG_NAT_DESC_LENGTH);
486
487			/* Safety precaution. */
488			entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
489
490			entry->rdr.id = priv->nextid++;
491			priv->rdrcount++;
492
493			/* Link to list of redirects. */
494			STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
495
496			/* Response with id of newly added entry. */
497			NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
498			if (resp == NULL) {
499				error = ENOMEM;
500				break;
501			}
502			bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
503		    }
504			break;
505		case NGM_NAT_REDIRECT_PROTO:
506		    {
507			struct ng_nat_rdr_lst *entry;
508			struct ng_nat_redirect_proto *const rp =
509			    (struct ng_nat_redirect_proto *)msg->data;
510
511			if (msg->header.arglen < sizeof(*rp)) {
512				error = EINVAL;
513				break;
514			}
515
516			if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
517			    M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
518				error = ENOMEM;
519				break;
520			}
521
522			/* Try actual redirect. */
523			entry->lnk = LibAliasRedirectProto(priv->lib,
524				rp->local_addr, rp->remote_addr,
525				rp->alias_addr, rp->proto);
526
527			if (entry->lnk == NULL) {
528				error = ENOMEM;
529				free(entry, M_NETGRAPH);
530				break;
531			}
532
533			/* Successful, save info in our internal list. */
534			entry->rdr.local_addr = rp->local_addr;
535			entry->rdr.alias_addr = rp->alias_addr;
536			entry->rdr.remote_addr = rp->remote_addr;
537			entry->rdr.proto = rp->proto;
538			bcopy(rp->description, entry->rdr.description,
539			    NG_NAT_DESC_LENGTH);
540
541			/* Safety precaution. */
542			entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
543
544			entry->rdr.id = priv->nextid++;
545			priv->rdrcount++;
546
547			/* Link to list of redirects. */
548			STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
549
550			/* Response with id of newly added entry. */
551			NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
552			if (resp == NULL) {
553				error = ENOMEM;
554				break;
555			}
556			bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
557		    }
558			break;
559		case NGM_NAT_REDIRECT_DYNAMIC:
560		case NGM_NAT_REDIRECT_DELETE:
561		    {
562			struct ng_nat_rdr_lst *entry;
563			uint32_t *const id = (uint32_t *)msg->data;
564
565			if (msg->header.arglen < sizeof(*id)) {
566				error = EINVAL;
567				break;
568			}
569
570			/* Find entry with supplied id. */
571			STAILQ_FOREACH(entry, &priv->redirhead, entries) {
572				if (entry->rdr.id == *id)
573					break;
574			}
575
576			/* Not found. */
577			if (entry == NULL) {
578				error = ENOENT;
579				break;
580			}
581
582			if (msg->header.cmd == NGM_NAT_REDIRECT_DYNAMIC) {
583				if (LibAliasRedirectDynamic(priv->lib,
584				    entry->lnk) == -1) {
585					error = ENOTTY;	/* XXX Something better? */
586					break;
587				}
588			} else {	/* NGM_NAT_REDIRECT_DELETE */
589				LibAliasRedirectDelete(priv->lib, entry->lnk);
590			}
591
592			/* Delete entry from our internal list. */
593			priv->rdrcount--;
594			STAILQ_REMOVE(&priv->redirhead, entry, ng_nat_rdr_lst, entries);
595			free(entry, M_NETGRAPH);
596		    }
597			break;
598		case NGM_NAT_ADD_SERVER:
599		    {
600			struct ng_nat_rdr_lst *entry;
601			struct ng_nat_add_server *const as =
602			    (struct ng_nat_add_server *)msg->data;
603
604			if (msg->header.arglen < sizeof(*as)) {
605				error = EINVAL;
606				break;
607			}
608
609			/* Find entry with supplied id. */
610			STAILQ_FOREACH(entry, &priv->redirhead, entries) {
611				if (entry->rdr.id == as->id)
612					break;
613			}
614
615			/* Not found. */
616			if (entry == NULL) {
617				error = ENOENT;
618				break;
619			}
620
621			if (LibAliasAddServer(priv->lib, entry->lnk,
622			    as->addr, htons(as->port)) == -1) {
623				error = ENOMEM;
624				break;
625			}
626
627			entry->rdr.lsnat++;
628		    }
629			break;
630		case NGM_NAT_LIST_REDIRECTS:
631		    {
632			struct ng_nat_rdr_lst *entry;
633			struct ng_nat_list_redirects *ary;
634			int i = 0;
635
636			NG_MKRESPONSE(resp, msg, sizeof(*ary) +
637			    (priv->rdrcount) * sizeof(*entry), M_NOWAIT);
638			if (resp == NULL) {
639				error = ENOMEM;
640				break;
641			}
642
643			ary = (struct ng_nat_list_redirects *)resp->data;
644			ary->total_count = priv->rdrcount;
645
646			STAILQ_FOREACH(entry, &priv->redirhead, entries) {
647				bcopy(&entry->rdr, &ary->redirects[i++],
648				    sizeof(struct ng_nat_listrdrs_entry));
649			}
650		    }
651			break;
652		case NGM_NAT_PROXY_RULE:
653		    {
654			char *cmd = (char *)msg->data;
655
656			if (msg->header.arglen < 6) {
657				error = EINVAL;
658				break;
659			}
660
661			if (LibAliasProxyRule(priv->lib, cmd) != 0)
662				error = ENOMEM;
663		    }
664			break;
665		case NGM_NAT_LIBALIAS_INFO:
666		    {
667			struct ng_nat_libalias_info *i;
668
669			NG_MKRESPONSE(resp, msg,
670			    sizeof(struct ng_nat_libalias_info), M_NOWAIT);
671			if (resp == NULL) {
672				error = ENOMEM;
673				break;
674			}
675			i = (struct ng_nat_libalias_info *)resp->data;
676#define	COPY(F)	do {						\
677	if (priv->lib->F >= 0 && priv->lib->F < UINT32_MAX)	\
678		i->F = priv->lib->F;				\
679	else							\
680		i->F = UINT32_MAX;				\
681} while (0)
682
683			COPY(icmpLinkCount);
684			COPY(udpLinkCount);
685			COPY(tcpLinkCount);
686			COPY(pptpLinkCount);
687			COPY(sctpLinkCount);
688			COPY(protoLinkCount);
689			COPY(fragmentIdLinkCount);
690			COPY(fragmentPtrLinkCount);
691			COPY(sockCount);
692#undef COPY
693		    }
694			break;
695		default:
696			error = EINVAL;		/* unknown command */
697			break;
698		}
699		break;
700	default:
701		error = EINVAL;			/* unknown cookie type */
702		break;
703	}
704
705	NG_RESPOND_MSG(error, node, item, resp);
706	NG_FREE_MSG(msg);
707	return (error);
708}
709
710static int
711ng_nat_rcvdata(hook_p hook, item_p item )
712{
713	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
714	struct mbuf	*m;
715	struct ip	*ip;
716	int rval, error = 0;
717	char *c;
718
719	/* We have no required hooks. */
720	if (!(priv->flags & NGNAT_CONNECTED)) {
721		NG_FREE_ITEM(item);
722		return (ENXIO);
723	}
724
725	/* We have no alias address yet to do anything. */
726	if (!(priv->flags & NGNAT_ADDR_DEFINED))
727		goto send;
728
729	m = NGI_M(item);
730
731	if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
732		NGI_M(item) = NULL;	/* avoid double free */
733		NG_FREE_ITEM(item);
734		return (ENOBUFS);
735	}
736
737	NGI_M(item) = m;
738
739	c = mtod(m, char *);
740	ip = mtod(m, struct ip *);
741
742	KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
743	    ("ng_nat: ip_len != m_pkthdr.len"));
744
745	/*
746	 * We drop packet when:
747	 * 1. libalias returns PKT_ALIAS_ERROR;
748	 * 2. For incoming packets:
749	 *	a) for unresolved fragments;
750	 *	b) libalias returns PKT_ALIAS_IGNORED and
751	 *		PKT_ALIAS_DENY_INCOMING flag is set.
752	 */
753	if (hook == priv->in) {
754		rval = LibAliasIn(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
755		if (rval == PKT_ALIAS_ERROR ||
756		    rval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
757		    (rval == PKT_ALIAS_IGNORED &&
758		     (priv->lib->packetAliasMode &
759		      PKT_ALIAS_DENY_INCOMING) != 0)) {
760			NG_FREE_ITEM(item);
761			return (EINVAL);
762		}
763	} else if (hook == priv->out) {
764		rval = LibAliasOut(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
765		if (rval == PKT_ALIAS_ERROR) {
766			NG_FREE_ITEM(item);
767			return (EINVAL);
768		}
769	} else
770		panic("ng_nat: unknown hook!\n");
771
772	if (rval == PKT_ALIAS_RESPOND)
773		m->m_flags |= M_SKIP_FIREWALL;
774	m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
775
776	if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
777	    ip->ip_p == IPPROTO_TCP) {
778		struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
779		    (ip->ip_hl << 2));
780
781		/*
782		 * Here is our terrible HACK.
783		 *
784		 * Sometimes LibAlias edits contents of TCP packet.
785		 * In this case it needs to recompute full TCP
786		 * checksum. However, the problem is that LibAlias
787		 * doesn't have any idea about checksum offloading
788		 * in kernel. To workaround this, we do not do
789		 * checksumming in LibAlias, but only mark the
790		 * packets in th_x2 field. If we receive a marked
791		 * packet, we calculate correct checksum for it
792		 * aware of offloading.
793		 *
794		 * Why do I do such a terrible hack instead of
795		 * recalculating checksum for each packet?
796		 * Because the previous checksum was not checked!
797		 * Recalculating checksums for EVERY packet will
798		 * hide ALL transmission errors. Yes, marked packets
799		 * still suffer from this problem. But, sigh, natd(8)
800		 * has this problem, too.
801		 */
802
803		if (th->th_x2) {
804			uint16_t ip_len = ntohs(ip->ip_len);
805
806			th->th_x2 = 0;
807			th->th_sum = in_pseudo(ip->ip_src.s_addr,
808			    ip->ip_dst.s_addr, htons(IPPROTO_TCP +
809			    ip_len - (ip->ip_hl << 2)));
810
811			if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
812				m->m_pkthdr.csum_data = offsetof(struct tcphdr,
813				    th_sum);
814				in_delayed_cksum(m);
815			}
816		}
817	}
818
819send:
820	if (hook == priv->in)
821		NG_FWD_ITEM_HOOK(error, item, priv->out);
822	else
823		NG_FWD_ITEM_HOOK(error, item, priv->in);
824
825	return (error);
826}
827
828static int
829ng_nat_shutdown(node_p node)
830{
831	const priv_p priv = NG_NODE_PRIVATE(node);
832
833	NG_NODE_SET_PRIVATE(node, NULL);
834	NG_NODE_UNREF(node);
835
836	/* Free redirects list. */
837	while (!STAILQ_EMPTY(&priv->redirhead)) {
838		struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead);
839		STAILQ_REMOVE_HEAD(&priv->redirhead, entries);
840		free(entry, M_NETGRAPH);
841	};
842
843	/* Final free. */
844	LibAliasUninit(priv->lib);
845	free(priv, M_NETGRAPH);
846
847	return (0);
848}
849
850static int
851ng_nat_disconnect(hook_p hook)
852{
853	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
854
855	priv->flags &= ~NGNAT_CONNECTED;
856
857	if (hook == priv->out)
858		priv->out = NULL;
859	if (hook == priv->in)
860		priv->in = NULL;
861
862	if (priv->out == NULL && priv->in == NULL)
863		ng_rmnode_self(NG_HOOK_NODE(hook));
864
865	return (0);
866}
867
868static unsigned int
869ng_nat_translate_flags(unsigned int x)
870{
871	unsigned int	res = 0;
872
873	if (x & NG_NAT_LOG)
874		res |= PKT_ALIAS_LOG;
875	if (x & NG_NAT_DENY_INCOMING)
876		res |= PKT_ALIAS_DENY_INCOMING;
877	if (x & NG_NAT_SAME_PORTS)
878		res |= PKT_ALIAS_SAME_PORTS;
879	if (x & NG_NAT_UNREGISTERED_ONLY)
880		res |= PKT_ALIAS_UNREGISTERED_ONLY;
881	if (x & NG_NAT_RESET_ON_ADDR_CHANGE)
882		res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
883	if (x & NG_NAT_PROXY_ONLY)
884		res |= PKT_ALIAS_PROXY_ONLY;
885	if (x & NG_NAT_REVERSE)
886		res |= PKT_ALIAS_REVERSE;
887
888	return (res);
889}
890