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