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