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