ng_nat.c revision 179477
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 179477 2008-06-01 15:13:32Z mav $
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	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH,
276		M_NOWAIT | M_ZERO);
277	if (priv == NULL)
278		return (ENOMEM);
279
280	/* Init aliasing engine. */
281	priv->lib = LibAliasInit(NULL);
282	if (priv->lib == NULL) {
283		FREE(priv, M_NETGRAPH);
284		return (ENOMEM);
285	}
286
287	/* Set same ports on. */
288	(void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
289	    PKT_ALIAS_SAME_PORTS);
290
291	/* Init redirects housekeeping. */
292	priv->rdrcount = 0;
293	priv->nextid = 1;
294	STAILQ_INIT(&priv->redirhead);
295
296	/* Link structs together. */
297	NG_NODE_SET_PRIVATE(node, priv);
298	priv->node = node;
299
300	/*
301	 * libalias is not thread safe, so our node
302	 * must be single threaded.
303	 */
304	NG_NODE_FORCE_WRITER(node);
305
306	return (0);
307}
308
309static int
310ng_nat_newhook(node_p node, hook_p hook, const char *name)
311{
312	const priv_p priv = NG_NODE_PRIVATE(node);
313
314	if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
315		priv->in = hook;
316	} else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
317		priv->out = hook;
318	} else
319		return (EINVAL);
320
321	if (priv->out != NULL &&
322	    priv->in != NULL)
323		priv->flags |= NGNAT_CONNECTED;
324
325	return(0);
326}
327
328static int
329ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
330{
331	const priv_p priv = NG_NODE_PRIVATE(node);
332	struct ng_mesg *resp = NULL;
333	struct ng_mesg *msg;
334	int error = 0;
335
336	NGI_GET_MSG(item, msg);
337
338	switch (msg->header.typecookie) {
339	case NGM_NAT_COOKIE:
340		switch (msg->header.cmd) {
341		case NGM_NAT_SET_IPADDR:
342		    {
343			struct in_addr *const ia = (struct in_addr *)msg->data;
344
345			if (msg->header.arglen < sizeof(*ia)) {
346				error = EINVAL;
347				break;
348			}
349
350			LibAliasSetAddress(priv->lib, *ia);
351
352			priv->flags |= NGNAT_ADDR_DEFINED;
353		    }
354			break;
355		case NGM_NAT_SET_MODE:
356		    {
357			struct ng_nat_mode *const mode =
358			    (struct ng_nat_mode *)msg->data;
359
360			if (msg->header.arglen < sizeof(*mode)) {
361				error = EINVAL;
362				break;
363			}
364
365			if (LibAliasSetMode(priv->lib,
366			    ng_nat_translate_flags(mode->flags),
367			    ng_nat_translate_flags(mode->mask)) < 0) {
368				error = ENOMEM;
369				break;
370			}
371		    }
372			break;
373		case NGM_NAT_SET_TARGET:
374		    {
375			struct in_addr *const ia = (struct in_addr *)msg->data;
376
377			if (msg->header.arglen < sizeof(*ia)) {
378				error = EINVAL;
379				break;
380			}
381
382			LibAliasSetTarget(priv->lib, *ia);
383		    }
384			break;
385		case NGM_NAT_REDIRECT_PORT:
386		    {
387			struct ng_nat_rdr_lst *entry;
388			struct ng_nat_redirect_port *const rp =
389			    (struct ng_nat_redirect_port *)msg->data;
390
391			if (msg->header.arglen < sizeof(*rp)) {
392				error = EINVAL;
393				break;
394			}
395
396			if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
397			    M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
398				error = ENOMEM;
399				break;
400			}
401
402			/* Try actual redirect. */
403			entry->lnk = LibAliasRedirectPort(priv->lib,
404				rp->local_addr, htons(rp->local_port),
405				rp->remote_addr, htons(rp->remote_port),
406				rp->alias_addr, htons(rp->alias_port),
407				rp->proto);
408
409			if (entry->lnk == NULL) {
410				error = ENOMEM;
411				FREE(entry, M_NETGRAPH);
412				break;
413			}
414
415			/* Successful, save info in our internal list. */
416			entry->rdr.local_addr = rp->local_addr;
417			entry->rdr.alias_addr = rp->alias_addr;
418			entry->rdr.remote_addr = rp->remote_addr;
419			entry->rdr.local_port = rp->local_port;
420			entry->rdr.alias_port = rp->alias_port;
421			entry->rdr.remote_port = rp->remote_port;
422			entry->rdr.proto = rp->proto;
423			bcopy(rp->description, entry->rdr.description,
424			    NG_NAT_DESC_LENGTH);
425
426			/* Safety precaution. */
427			entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
428
429			entry->rdr.id = priv->nextid++;
430			priv->rdrcount++;
431
432			/* Link to list of redirects. */
433			STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
434
435			/* Response with id of newly added entry. */
436			NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
437			if (resp == NULL) {
438				error = ENOMEM;
439				break;
440			}
441			bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
442		    }
443			break;
444		case NGM_NAT_REDIRECT_ADDR:
445		    {
446			struct ng_nat_rdr_lst *entry;
447			struct ng_nat_redirect_addr *const ra =
448			    (struct ng_nat_redirect_addr *)msg->data;
449
450			if (msg->header.arglen < sizeof(*ra)) {
451				error = EINVAL;
452				break;
453			}
454
455			if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
456			    M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
457				error = ENOMEM;
458				break;
459			}
460
461			/* Try actual redirect. */
462			entry->lnk = LibAliasRedirectAddr(priv->lib,
463				ra->local_addr, ra->alias_addr);
464
465			if (entry->lnk == NULL) {
466				error = ENOMEM;
467				FREE(entry, M_NETGRAPH);
468				break;
469			}
470
471			/* Successful, save info in our internal list. */
472			entry->rdr.local_addr = ra->local_addr;
473			entry->rdr.alias_addr = ra->alias_addr;
474			entry->rdr.proto = NG_NAT_REDIRPROTO_ADDR;
475			bcopy(ra->description, entry->rdr.description,
476			    NG_NAT_DESC_LENGTH);
477
478			/* Safety precaution. */
479			entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
480
481			entry->rdr.id = priv->nextid++;
482			priv->rdrcount++;
483
484			/* Link to list of redirects. */
485			STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
486
487			/* Response with id of newly added entry. */
488			NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
489			if (resp == NULL) {
490				error = ENOMEM;
491				break;
492			}
493			bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
494		    }
495			break;
496		case NGM_NAT_REDIRECT_PROTO:
497		    {
498			struct ng_nat_rdr_lst *entry;
499			struct ng_nat_redirect_proto *const rp =
500			    (struct ng_nat_redirect_proto *)msg->data;
501
502			if (msg->header.arglen < sizeof(*rp)) {
503				error = EINVAL;
504				break;
505			}
506
507			if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
508			    M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
509				error = ENOMEM;
510				break;
511			}
512
513			/* Try actual redirect. */
514			entry->lnk = LibAliasRedirectProto(priv->lib,
515				rp->local_addr, rp->remote_addr,
516				rp->alias_addr, rp->proto);
517
518			if (entry->lnk == NULL) {
519				error = ENOMEM;
520				FREE(entry, M_NETGRAPH);
521				break;
522			}
523
524			/* Successful, save info in our internal list. */
525			entry->rdr.local_addr = rp->local_addr;
526			entry->rdr.alias_addr = rp->alias_addr;
527			entry->rdr.remote_addr = rp->remote_addr;
528			entry->rdr.proto = rp->proto;
529			bcopy(rp->description, entry->rdr.description,
530			    NG_NAT_DESC_LENGTH);
531
532			/* Safety precaution. */
533			entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
534
535			entry->rdr.id = priv->nextid++;
536			priv->rdrcount++;
537
538			/* Link to list of redirects. */
539			STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
540
541			/* Response with id of newly added entry. */
542			NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
543			if (resp == NULL) {
544				error = ENOMEM;
545				break;
546			}
547			bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
548		    }
549			break;
550		case NGM_NAT_REDIRECT_DYNAMIC:
551		case NGM_NAT_REDIRECT_DELETE:
552		    {
553			struct ng_nat_rdr_lst *entry;
554			uint32_t *const id = (uint32_t *)msg->data;
555
556			if (msg->header.arglen < sizeof(*id)) {
557				error = EINVAL;
558				break;
559			}
560
561			/* Find entry with supplied id. */
562			STAILQ_FOREACH(entry, &priv->redirhead, entries) {
563				if (entry->rdr.id == *id)
564					break;
565			}
566
567			/* Not found. */
568			if (entry == NULL) {
569				error = ENOENT;
570				break;
571			}
572
573			if (msg->header.cmd == NGM_NAT_REDIRECT_DYNAMIC) {
574				if (LibAliasRedirectDynamic(priv->lib,
575				    entry->lnk) == -1) {
576					error = ENOTTY;	/* XXX Something better? */
577					break;
578				}
579			} else {	/* NGM_NAT_REDIRECT_DELETE */
580				LibAliasRedirectDelete(priv->lib, entry->lnk);
581			}
582
583			/* Delete entry from our internal list. */
584			priv->rdrcount--;
585			STAILQ_REMOVE(&priv->redirhead, entry, ng_nat_rdr_lst, entries);
586			FREE(entry, M_NETGRAPH);
587		    }
588			break;
589		case NGM_NAT_ADD_SERVER:
590		    {
591			struct ng_nat_rdr_lst *entry;
592			struct ng_nat_add_server *const as =
593			    (struct ng_nat_add_server *)msg->data;
594
595			if (msg->header.arglen < sizeof(*as)) {
596				error = EINVAL;
597				break;
598			}
599
600			/* Find entry with supplied id. */
601			STAILQ_FOREACH(entry, &priv->redirhead, entries) {
602				if (entry->rdr.id == as->id)
603					break;
604			}
605
606			/* Not found. */
607			if (entry == NULL) {
608				error = ENOENT;
609				break;
610			}
611
612			if (LibAliasAddServer(priv->lib, entry->lnk,
613			    as->addr, htons(as->port)) == -1) {
614				error = ENOMEM;
615				break;
616			}
617
618			entry->rdr.lsnat++;
619		    }
620			break;
621		case NGM_NAT_LIST_REDIRECTS:
622		    {
623			struct ng_nat_rdr_lst *entry;
624			struct ng_nat_list_redirects *ary;
625			int i = 0;
626
627			NG_MKRESPONSE(resp, msg, sizeof(*ary) +
628			    (priv->rdrcount) * sizeof(*entry), M_NOWAIT);
629			if (resp == NULL) {
630				error = ENOMEM;
631				break;
632			}
633
634			ary = (struct ng_nat_list_redirects *)resp->data;
635			ary->total_count = priv->rdrcount;
636
637			STAILQ_FOREACH(entry, &priv->redirhead, entries) {
638				bcopy(&entry->rdr, &ary->redirects[i++],
639				    sizeof(struct ng_nat_listrdrs_entry));
640			}
641		    }
642			break;
643		case NGM_NAT_PROXY_RULE:
644		    {
645			char *cmd = (char *)msg->data;
646
647			if (msg->header.arglen < 6) {
648				error = EINVAL;
649				break;
650			}
651
652			if (LibAliasProxyRule(priv->lib, cmd) != 0)
653				error = ENOMEM;
654		    }
655			break;
656		default:
657			error = EINVAL;		/* unknown command */
658			break;
659		}
660		break;
661	default:
662		error = EINVAL;			/* unknown cookie type */
663		break;
664	}
665
666	NG_RESPOND_MSG(error, node, item, resp);
667	NG_FREE_MSG(msg);
668	return (error);
669}
670
671static int
672ng_nat_rcvdata(hook_p hook, item_p item )
673{
674	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
675	struct mbuf	*m;
676	struct ip	*ip;
677	int rval, error = 0;
678	char *c;
679
680	/* We have no required hooks. */
681	if (!(priv->flags & NGNAT_CONNECTED)) {
682		NG_FREE_ITEM(item);
683		return (ENXIO);
684	}
685
686	/* We have no alias address yet to do anything. */
687	if (!(priv->flags & NGNAT_ADDR_DEFINED))
688		goto send;
689
690	m = NGI_M(item);
691
692	if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
693		NGI_M(item) = NULL;	/* avoid double free */
694		NG_FREE_ITEM(item);
695		return (ENOBUFS);
696	}
697
698	NGI_M(item) = m;
699
700	c = mtod(m, char *);
701	ip = mtod(m, struct ip *);
702
703	KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
704	    ("ng_nat: ip_len != m_pkthdr.len"));
705
706	if (hook == priv->in) {
707		rval = LibAliasIn(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
708		if (rval != PKT_ALIAS_OK &&
709		    rval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
710			NG_FREE_ITEM(item);
711			return (EINVAL);
712		}
713	} else if (hook == priv->out) {
714		rval = LibAliasOut(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
715		if (rval != PKT_ALIAS_OK) {
716			NG_FREE_ITEM(item);
717			return (EINVAL);
718		}
719	} else
720		panic("ng_nat: unknown hook!\n");
721
722	m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
723
724	if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
725	    ip->ip_p == IPPROTO_TCP) {
726		struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
727		    (ip->ip_hl << 2));
728
729		/*
730		 * Here is our terrible HACK.
731		 *
732		 * Sometimes LibAlias edits contents of TCP packet.
733		 * In this case it needs to recompute full TCP
734		 * checksum. However, the problem is that LibAlias
735		 * doesn't have any idea about checksum offloading
736		 * in kernel. To workaround this, we do not do
737		 * checksumming in LibAlias, but only mark the
738		 * packets in th_x2 field. If we receive a marked
739		 * packet, we calculate correct checksum for it
740		 * aware of offloading.
741		 *
742		 * Why do I do such a terrible hack instead of
743		 * recalculating checksum for each packet?
744		 * Because the previous checksum was not checked!
745		 * Recalculating checksums for EVERY packet will
746		 * hide ALL transmission errors. Yes, marked packets
747		 * still suffer from this problem. But, sigh, natd(8)
748		 * has this problem, too.
749		 */
750
751		if (th->th_x2) {
752			th->th_x2 = 0;
753			ip->ip_len = ntohs(ip->ip_len);
754			th->th_sum = in_pseudo(ip->ip_src.s_addr,
755			    ip->ip_dst.s_addr, htons(IPPROTO_TCP +
756			    ip->ip_len - (ip->ip_hl << 2)));
757
758			if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
759				m->m_pkthdr.csum_data = offsetof(struct tcphdr,
760				    th_sum);
761				in_delayed_cksum(m);
762			}
763			ip->ip_len = htons(ip->ip_len);
764		}
765	}
766
767send:
768	if (hook == priv->in)
769		NG_FWD_ITEM_HOOK(error, item, priv->out);
770	else
771		NG_FWD_ITEM_HOOK(error, item, priv->in);
772
773	return (error);
774}
775
776static int
777ng_nat_shutdown(node_p node)
778{
779	const priv_p priv = NG_NODE_PRIVATE(node);
780
781	NG_NODE_SET_PRIVATE(node, NULL);
782	NG_NODE_UNREF(node);
783
784	/* Free redirects list. */
785	while (!STAILQ_EMPTY(&priv->redirhead)) {
786		struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead);
787		STAILQ_REMOVE_HEAD(&priv->redirhead, entries);
788		FREE(entry, M_NETGRAPH);
789	};
790
791	/* Final free. */
792	LibAliasUninit(priv->lib);
793	FREE(priv, M_NETGRAPH);
794
795	return (0);
796}
797
798static int
799ng_nat_disconnect(hook_p hook)
800{
801	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
802
803	priv->flags &= ~NGNAT_CONNECTED;
804
805	if (hook == priv->out)
806		priv->out = NULL;
807	if (hook == priv->in)
808		priv->in = NULL;
809
810	if (priv->out == NULL && priv->in == NULL)
811		ng_rmnode_self(NG_HOOK_NODE(hook));
812
813	return (0);
814}
815
816static unsigned int
817ng_nat_translate_flags(unsigned int x)
818{
819	unsigned int	res = 0;
820
821	if (x & NG_NAT_LOG)
822		res |= PKT_ALIAS_LOG;
823	if (x & NG_NAT_DENY_INCOMING)
824		res |= PKT_ALIAS_DENY_INCOMING;
825	if (x & NG_NAT_SAME_PORTS)
826		res |= PKT_ALIAS_SAME_PORTS;
827	if (x & NG_NAT_UNREGISTERED_ONLY)
828		res |= PKT_ALIAS_UNREGISTERED_ONLY;
829	if (x & NG_NAT_RESET_ON_ADDR_CHANGE)
830		res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
831	if (x & NG_NAT_PROXY_ONLY)
832		res |= PKT_ALIAS_PROXY_ONLY;
833	if (x & NG_NAT_REVERSE)
834		res |= PKT_ALIAS_REVERSE;
835
836	return (res);
837}
838