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