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