xref: /freebsd/sys/netgraph/ng_nat.c (revision 050570efa79efcc9cf5adeb545f1a679c8dc377b)
1 /*-
2  * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/mbuf.h>
33 #include <sys/malloc.h>
34 #include <sys/ctype.h>
35 #include <sys/errno.h>
36 #include <sys/syslog.h>
37 
38 #include <netinet/in_systm.h>
39 #include <netinet/in.h>
40 #include <netinet/ip.h>
41 #include <netinet/ip_var.h>
42 #include <netinet/tcp.h>
43 #include <machine/in_cksum.h>
44 
45 #include <netinet/libalias/alias.h>
46 
47 #include <netgraph/ng_message.h>
48 #include <netgraph/ng_parse.h>
49 #include <netgraph/ng_nat.h>
50 #include <netgraph/netgraph.h>
51 
52 static ng_constructor_t	ng_nat_constructor;
53 static ng_rcvmsg_t	ng_nat_rcvmsg;
54 static ng_shutdown_t	ng_nat_shutdown;
55 static ng_newhook_t	ng_nat_newhook;
56 static ng_rcvdata_t	ng_nat_rcvdata;
57 static ng_disconnect_t	ng_nat_disconnect;
58 
59 static unsigned int	ng_nat_translate_flags(unsigned int x);
60 
61 /* Parse type for struct ng_nat_mode. */
62 static const struct ng_parse_struct_field ng_nat_mode_fields[]
63 	= NG_NAT_MODE_INFO;
64 static 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. */
70 static const struct ng_parse_fixedstring_info ng_nat_description_info
71 	= { NG_NAT_DESC_LENGTH };
72 static 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. */
78 static const struct ng_parse_struct_field ng_nat_redirect_port_fields[]
79 	= NG_NAT_REDIRECT_PORT_TYPE_INFO(&ng_nat_description_type);
80 static 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. */
86 static const struct ng_parse_struct_field ng_nat_redirect_addr_fields[]
87 	= NG_NAT_REDIRECT_ADDR_TYPE_INFO(&ng_nat_description_type);
88 static 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. */
94 static const struct ng_parse_struct_field ng_nat_redirect_proto_fields[]
95 	= NG_NAT_REDIRECT_PROTO_TYPE_INFO(&ng_nat_description_type);
96 static 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. */
102 static const struct ng_parse_struct_field ng_nat_add_server_fields[]
103 	= NG_NAT_ADD_SERVER_TYPE_INFO;
104 static 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. */
110 static const struct ng_parse_struct_field ng_nat_listrdrs_entry_fields[]
111 	= NG_NAT_LISTRDRS_ENTRY_TYPE_INFO(&ng_nat_description_type);
112 static 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. */
118 static int
119 ng_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 
129 static 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 };
134 static 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. */
140 static const struct ng_parse_struct_field ng_nat_list_redirects_fields[]
141 	= NG_NAT_LIST_REDIRECTS_TYPE_INFO(&ng_nat_listrdrs_ary_type);
142 static 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. */
148 static 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. */
230 static 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 };
241 NETGRAPH_INIT(nat, &typestruct);
242 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
243 
244 /* Element for list of redirects. */
245 struct 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 };
250 STAILQ_HEAD(rdrhead, ng_nat_rdr_lst);
251 
252 /* Information we store for each node. */
253 struct 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 };
263 typedef 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 
269 static int
270 ng_nat_constructor(node_p node)
271 {
272 	priv_p priv;
273 
274 	/* Initialize private descriptor. */
275 	priv = malloc(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 
309 static int
310 ng_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 
328 static int
329 ng_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 
671 static int
672 ng_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 
767 send:
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 
776 static int
777 ng_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 
798 static int
799 ng_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 
816 static unsigned int
817 ng_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