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