xref: /freebsd/sys/netgraph/ng_nat.c (revision 7ef62cebc2f965b0f640263e179276928885e33d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/mbuf.h>
35 #include <sys/malloc.h>
36 #include <sys/ctype.h>
37 #include <sys/errno.h>
38 #include <sys/syslog.h>
39 
40 #include <netinet/in_systm.h>
41 #include <netinet/in.h>
42 #include <netinet/ip.h>
43 #include <netinet/ip_var.h>
44 #include <netinet/tcp.h>
45 #include <machine/in_cksum.h>
46 
47 #include <net/dlt.h>
48 #include <net/ethernet.h>
49 
50 #include <netinet/libalias/alias.h>
51 #include <netinet/libalias/alias_local.h>
52 
53 #include <netgraph/ng_message.h>
54 #include <netgraph/ng_parse.h>
55 #include <netgraph/ng_nat.h>
56 #include <netgraph/netgraph.h>
57 
58 static ng_constructor_t	ng_nat_constructor;
59 static ng_rcvmsg_t	ng_nat_rcvmsg;
60 static ng_shutdown_t	ng_nat_shutdown;
61 static ng_newhook_t	ng_nat_newhook;
62 static ng_rcvdata_t	ng_nat_rcvdata;
63 static ng_disconnect_t	ng_nat_disconnect;
64 
65 static unsigned int	ng_nat_translate_flags(unsigned int x);
66 
67 /* Parse type for struct ng_nat_mode. */
68 static const struct ng_parse_struct_field ng_nat_mode_fields[]
69 	= NG_NAT_MODE_INFO;
70 static const struct ng_parse_type ng_nat_mode_type = {
71 	&ng_parse_struct_type,
72 	&ng_nat_mode_fields
73 };
74 
75 /* Parse type for 'description' field in structs. */
76 static const struct ng_parse_fixedstring_info ng_nat_description_info
77 	= { NG_NAT_DESC_LENGTH };
78 static const struct ng_parse_type ng_nat_description_type = {
79 	&ng_parse_fixedstring_type,
80 	&ng_nat_description_info
81 };
82 
83 /* Parse type for struct ng_nat_redirect_port. */
84 static const struct ng_parse_struct_field ng_nat_redirect_port_fields[]
85 	= NG_NAT_REDIRECT_PORT_TYPE_INFO(&ng_nat_description_type);
86 static const struct ng_parse_type ng_nat_redirect_port_type = {
87 	&ng_parse_struct_type,
88 	&ng_nat_redirect_port_fields
89 };
90 
91 /* Parse type for struct ng_nat_redirect_addr. */
92 static const struct ng_parse_struct_field ng_nat_redirect_addr_fields[]
93 	= NG_NAT_REDIRECT_ADDR_TYPE_INFO(&ng_nat_description_type);
94 static const struct ng_parse_type ng_nat_redirect_addr_type = {
95 	&ng_parse_struct_type,
96 	&ng_nat_redirect_addr_fields
97 };
98 
99 /* Parse type for struct ng_nat_redirect_proto. */
100 static const struct ng_parse_struct_field ng_nat_redirect_proto_fields[]
101 	= NG_NAT_REDIRECT_PROTO_TYPE_INFO(&ng_nat_description_type);
102 static const struct ng_parse_type ng_nat_redirect_proto_type = {
103 	&ng_parse_struct_type,
104 	&ng_nat_redirect_proto_fields
105 };
106 
107 /* Parse type for struct ng_nat_add_server. */
108 static const struct ng_parse_struct_field ng_nat_add_server_fields[]
109 	= NG_NAT_ADD_SERVER_TYPE_INFO;
110 static const struct ng_parse_type ng_nat_add_server_type = {
111 	&ng_parse_struct_type,
112 	&ng_nat_add_server_fields
113 };
114 
115 /* Parse type for one struct ng_nat_listrdrs_entry. */
116 static const struct ng_parse_struct_field ng_nat_listrdrs_entry_fields[]
117 	= NG_NAT_LISTRDRS_ENTRY_TYPE_INFO(&ng_nat_description_type);
118 static const struct ng_parse_type ng_nat_listrdrs_entry_type = {
119 	&ng_parse_struct_type,
120 	&ng_nat_listrdrs_entry_fields
121 };
122 
123 /* Parse type for 'redirects' array in struct ng_nat_list_redirects. */
124 static int
125 ng_nat_listrdrs_ary_getLength(const struct ng_parse_type *type,
126 	const u_char *start, const u_char *buf)
127 {
128 	const struct ng_nat_list_redirects *lr;
129 
130 	lr = (const struct ng_nat_list_redirects *)
131 	    (buf - offsetof(struct ng_nat_list_redirects, redirects));
132 	return lr->total_count;
133 }
134 
135 static const struct ng_parse_array_info ng_nat_listrdrs_ary_info = {
136 	&ng_nat_listrdrs_entry_type,
137 	&ng_nat_listrdrs_ary_getLength,
138 	NULL
139 };
140 static const struct ng_parse_type ng_nat_listrdrs_ary_type = {
141 	&ng_parse_array_type,
142 	&ng_nat_listrdrs_ary_info
143 };
144 
145 /* Parse type for struct ng_nat_list_redirects. */
146 static const struct ng_parse_struct_field ng_nat_list_redirects_fields[]
147 	= NG_NAT_LIST_REDIRECTS_TYPE_INFO(&ng_nat_listrdrs_ary_type);
148 static const struct ng_parse_type ng_nat_list_redirects_type = {
149 	&ng_parse_struct_type,
150 	&ng_nat_list_redirects_fields
151 };
152 
153 /* Parse type for struct ng_nat_libalias_info. */
154 static const struct ng_parse_struct_field ng_nat_libalias_info_fields[]
155 	= NG_NAT_LIBALIAS_INFO;
156 static const struct ng_parse_type ng_nat_libalias_info_type = {
157 	&ng_parse_struct_type,
158 	&ng_nat_libalias_info_fields
159 };
160 
161 /* List of commands and how to convert arguments to/from ASCII. */
162 static const struct ng_cmdlist ng_nat_cmdlist[] = {
163 	{
164 	  NGM_NAT_COOKIE,
165 	  NGM_NAT_SET_IPADDR,
166 	  "setaliasaddr",
167 	  &ng_parse_ipaddr_type,
168 	  NULL
169 	},
170 	{
171 	  NGM_NAT_COOKIE,
172 	  NGM_NAT_SET_MODE,
173 	  "setmode",
174 	  &ng_nat_mode_type,
175 	  NULL
176 	},
177 	{
178 	  NGM_NAT_COOKIE,
179 	  NGM_NAT_SET_TARGET,
180 	  "settarget",
181 	  &ng_parse_ipaddr_type,
182 	  NULL
183 	},
184 	{
185 	  NGM_NAT_COOKIE,
186 	  NGM_NAT_REDIRECT_PORT,
187 	  "redirectport",
188 	  &ng_nat_redirect_port_type,
189 	  &ng_parse_uint32_type
190 	},
191 	{
192 	  NGM_NAT_COOKIE,
193 	  NGM_NAT_REDIRECT_ADDR,
194 	  "redirectaddr",
195 	  &ng_nat_redirect_addr_type,
196 	  &ng_parse_uint32_type
197 	},
198 	{
199 	  NGM_NAT_COOKIE,
200 	  NGM_NAT_REDIRECT_PROTO,
201 	  "redirectproto",
202 	  &ng_nat_redirect_proto_type,
203 	  &ng_parse_uint32_type
204 	},
205 	{
206 	  NGM_NAT_COOKIE,
207 	  NGM_NAT_REDIRECT_DYNAMIC,
208 	  "redirectdynamic",
209 	  &ng_parse_uint32_type,
210 	  NULL
211 	},
212 	{
213 	  NGM_NAT_COOKIE,
214 	  NGM_NAT_REDIRECT_DELETE,
215 	  "redirectdelete",
216 	  &ng_parse_uint32_type,
217 	  NULL
218 	},
219 	{
220 	  NGM_NAT_COOKIE,
221 	  NGM_NAT_ADD_SERVER,
222 	  "addserver",
223 	  &ng_nat_add_server_type,
224 	  NULL
225 	},
226 	{
227 	  NGM_NAT_COOKIE,
228 	  NGM_NAT_LIST_REDIRECTS,
229 	  "listredirects",
230 	  NULL,
231 	  &ng_nat_list_redirects_type
232 	},
233 	{
234 	  NGM_NAT_COOKIE,
235 	  NGM_NAT_PROXY_RULE,
236 	  "proxyrule",
237 	  &ng_parse_string_type,
238 	  NULL
239 	},
240 	{
241 	  NGM_NAT_COOKIE,
242 	  NGM_NAT_LIBALIAS_INFO,
243 	  "libaliasinfo",
244 	  NULL,
245 	  &ng_nat_libalias_info_type
246 	},
247 	{
248 	  NGM_NAT_COOKIE,
249 	  NGM_NAT_SET_DLT,
250 	  "setdlt",
251 	  &ng_parse_uint8_type,
252 	  NULL
253 	},
254 	{
255 	  NGM_NAT_COOKIE,
256 	  NGM_NAT_GET_DLT,
257 	  "getdlt",
258 	  NULL,
259 	  &ng_parse_uint8_type
260 	},
261 	{ 0 }
262 };
263 
264 /* Netgraph node type descriptor. */
265 static struct ng_type typestruct = {
266 	.version =	NG_ABI_VERSION,
267 	.name =		NG_NAT_NODE_TYPE,
268 	.constructor =	ng_nat_constructor,
269 	.rcvmsg =	ng_nat_rcvmsg,
270 	.shutdown =	ng_nat_shutdown,
271 	.newhook =	ng_nat_newhook,
272 	.rcvdata =	ng_nat_rcvdata,
273 	.disconnect =	ng_nat_disconnect,
274 	.cmdlist =	ng_nat_cmdlist,
275 };
276 NETGRAPH_INIT(nat, &typestruct);
277 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
278 
279 /* Element for list of redirects. */
280 struct ng_nat_rdr_lst {
281 	STAILQ_ENTRY(ng_nat_rdr_lst) entries;
282 	struct alias_link	*lnk;
283 	struct ng_nat_listrdrs_entry rdr;
284 };
285 STAILQ_HEAD(rdrhead, ng_nat_rdr_lst);
286 
287 /* Information we store for each node. */
288 struct ng_nat_priv {
289 	node_p		node;		/* back pointer to node */
290 	hook_p		in;		/* hook for demasquerading */
291 	hook_p		out;		/* hook for masquerading */
292 	struct libalias	*lib;		/* libalias handler */
293 	uint32_t	flags;		/* status flags */
294 	uint32_t	rdrcount;	/* number or redirects in list */
295 	uint32_t	nextid;		/* for next in turn in list */
296 	struct rdrhead	redirhead;	/* redirect list header */
297 	uint8_t		dlt;		/* DLT_XXX from bpf.h */
298 };
299 typedef struct ng_nat_priv *priv_p;
300 
301 /* Values of flags */
302 #define	NGNAT_CONNECTED		0x1	/* We have both hooks connected */
303 #define	NGNAT_ADDR_DEFINED	0x2	/* NGM_NAT_SET_IPADDR happened */
304 
305 static int
306 ng_nat_constructor(node_p node)
307 {
308 	priv_p priv;
309 
310 	/* Initialize private descriptor. */
311 	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
312 
313 	/* Init aliasing engine. */
314 	priv->lib = LibAliasInit(NULL);
315 
316 	/* Set same ports on. */
317 	(void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS,
318 	    PKT_ALIAS_SAME_PORTS);
319 
320 	/* Init redirects housekeeping. */
321 	priv->rdrcount = 0;
322 	priv->nextid = 1;
323 	priv->dlt = DLT_RAW;
324 	STAILQ_INIT(&priv->redirhead);
325 
326 	/* Link structs together. */
327 	NG_NODE_SET_PRIVATE(node, priv);
328 	priv->node = node;
329 
330 	/*
331 	 * libalias is not thread safe, so our node
332 	 * must be single threaded.
333 	 */
334 	NG_NODE_FORCE_WRITER(node);
335 
336 	return (0);
337 }
338 
339 static int
340 ng_nat_newhook(node_p node, hook_p hook, const char *name)
341 {
342 	const priv_p priv = NG_NODE_PRIVATE(node);
343 
344 	if (strcmp(name, NG_NAT_HOOK_IN) == 0) {
345 		priv->in = hook;
346 	} else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) {
347 		priv->out = hook;
348 	} else
349 		return (EINVAL);
350 
351 	if (priv->out != NULL &&
352 	    priv->in != NULL)
353 		priv->flags |= NGNAT_CONNECTED;
354 
355 	return(0);
356 }
357 
358 static int
359 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
360 {
361 	const priv_p priv = NG_NODE_PRIVATE(node);
362 	struct ng_mesg *resp = NULL;
363 	struct ng_mesg *msg;
364 	int error = 0;
365 
366 	NGI_GET_MSG(item, msg);
367 
368 	switch (msg->header.typecookie) {
369 	case NGM_NAT_COOKIE:
370 		switch (msg->header.cmd) {
371 		case NGM_NAT_SET_IPADDR:
372 		    {
373 			struct in_addr *const ia = (struct in_addr *)msg->data;
374 
375 			if (msg->header.arglen < sizeof(*ia)) {
376 				error = EINVAL;
377 				break;
378 			}
379 
380 			LibAliasSetAddress(priv->lib, *ia);
381 
382 			priv->flags |= NGNAT_ADDR_DEFINED;
383 		    }
384 			break;
385 		case NGM_NAT_SET_MODE:
386 		    {
387 			struct ng_nat_mode *const mode =
388 			    (struct ng_nat_mode *)msg->data;
389 
390 			if (msg->header.arglen < sizeof(*mode)) {
391 				error = EINVAL;
392 				break;
393 			}
394 
395 			if (LibAliasSetMode(priv->lib,
396 			    ng_nat_translate_flags(mode->flags),
397 			    ng_nat_translate_flags(mode->mask)) < 0) {
398 				error = ENOMEM;
399 				break;
400 			}
401 		    }
402 			break;
403 		case NGM_NAT_SET_TARGET:
404 		    {
405 			struct in_addr *const ia = (struct in_addr *)msg->data;
406 
407 			if (msg->header.arglen < sizeof(*ia)) {
408 				error = EINVAL;
409 				break;
410 			}
411 
412 			LibAliasSetTarget(priv->lib, *ia);
413 		    }
414 			break;
415 		case NGM_NAT_REDIRECT_PORT:
416 		    {
417 			struct ng_nat_rdr_lst *entry;
418 			struct ng_nat_redirect_port *const rp =
419 			    (struct ng_nat_redirect_port *)msg->data;
420 
421 			if (msg->header.arglen < sizeof(*rp)) {
422 				error = EINVAL;
423 				break;
424 			}
425 
426 			if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
427 			    M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
428 				error = ENOMEM;
429 				break;
430 			}
431 
432 			/* Try actual redirect. */
433 			entry->lnk = LibAliasRedirectPort(priv->lib,
434 				rp->local_addr, htons(rp->local_port),
435 				rp->remote_addr, htons(rp->remote_port),
436 				rp->alias_addr, htons(rp->alias_port),
437 				rp->proto);
438 
439 			if (entry->lnk == NULL) {
440 				error = ENOMEM;
441 				free(entry, M_NETGRAPH);
442 				break;
443 			}
444 
445 			/* Successful, save info in our internal list. */
446 			entry->rdr.local_addr = rp->local_addr;
447 			entry->rdr.alias_addr = rp->alias_addr;
448 			entry->rdr.remote_addr = rp->remote_addr;
449 			entry->rdr.local_port = rp->local_port;
450 			entry->rdr.alias_port = rp->alias_port;
451 			entry->rdr.remote_port = rp->remote_port;
452 			entry->rdr.proto = rp->proto;
453 			bcopy(rp->description, entry->rdr.description,
454 			    NG_NAT_DESC_LENGTH);
455 
456 			/* Safety precaution. */
457 			entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
458 
459 			entry->rdr.id = priv->nextid++;
460 			priv->rdrcount++;
461 
462 			/* Link to list of redirects. */
463 			STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
464 
465 			/* Response with id of newly added entry. */
466 			NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
467 			if (resp == NULL) {
468 				error = ENOMEM;
469 				break;
470 			}
471 			bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
472 		    }
473 			break;
474 		case NGM_NAT_REDIRECT_ADDR:
475 		    {
476 			struct ng_nat_rdr_lst *entry;
477 			struct ng_nat_redirect_addr *const ra =
478 			    (struct ng_nat_redirect_addr *)msg->data;
479 
480 			if (msg->header.arglen < sizeof(*ra)) {
481 				error = EINVAL;
482 				break;
483 			}
484 
485 			if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
486 			    M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
487 				error = ENOMEM;
488 				break;
489 			}
490 
491 			/* Try actual redirect. */
492 			entry->lnk = LibAliasRedirectAddr(priv->lib,
493 				ra->local_addr, ra->alias_addr);
494 
495 			if (entry->lnk == NULL) {
496 				error = ENOMEM;
497 				free(entry, M_NETGRAPH);
498 				break;
499 			}
500 
501 			/* Successful, save info in our internal list. */
502 			entry->rdr.local_addr = ra->local_addr;
503 			entry->rdr.alias_addr = ra->alias_addr;
504 			entry->rdr.proto = NG_NAT_REDIRPROTO_ADDR;
505 			bcopy(ra->description, entry->rdr.description,
506 			    NG_NAT_DESC_LENGTH);
507 
508 			/* Safety precaution. */
509 			entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
510 
511 			entry->rdr.id = priv->nextid++;
512 			priv->rdrcount++;
513 
514 			/* Link to list of redirects. */
515 			STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
516 
517 			/* Response with id of newly added entry. */
518 			NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
519 			if (resp == NULL) {
520 				error = ENOMEM;
521 				break;
522 			}
523 			bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
524 		    }
525 			break;
526 		case NGM_NAT_REDIRECT_PROTO:
527 		    {
528 			struct ng_nat_rdr_lst *entry;
529 			struct ng_nat_redirect_proto *const rp =
530 			    (struct ng_nat_redirect_proto *)msg->data;
531 
532 			if (msg->header.arglen < sizeof(*rp)) {
533 				error = EINVAL;
534 				break;
535 			}
536 
537 			if ((entry = malloc(sizeof(struct ng_nat_rdr_lst),
538 			    M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) {
539 				error = ENOMEM;
540 				break;
541 			}
542 
543 			/* Try actual redirect. */
544 			entry->lnk = LibAliasRedirectProto(priv->lib,
545 				rp->local_addr, rp->remote_addr,
546 				rp->alias_addr, rp->proto);
547 
548 			if (entry->lnk == NULL) {
549 				error = ENOMEM;
550 				free(entry, M_NETGRAPH);
551 				break;
552 			}
553 
554 			/* Successful, save info in our internal list. */
555 			entry->rdr.local_addr = rp->local_addr;
556 			entry->rdr.alias_addr = rp->alias_addr;
557 			entry->rdr.remote_addr = rp->remote_addr;
558 			entry->rdr.proto = rp->proto;
559 			bcopy(rp->description, entry->rdr.description,
560 			    NG_NAT_DESC_LENGTH);
561 
562 			/* Safety precaution. */
563 			entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0';
564 
565 			entry->rdr.id = priv->nextid++;
566 			priv->rdrcount++;
567 
568 			/* Link to list of redirects. */
569 			STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries);
570 
571 			/* Response with id of newly added entry. */
572 			NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT);
573 			if (resp == NULL) {
574 				error = ENOMEM;
575 				break;
576 			}
577 			bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id));
578 		    }
579 			break;
580 		case NGM_NAT_REDIRECT_DYNAMIC:
581 		case NGM_NAT_REDIRECT_DELETE:
582 		    {
583 			struct ng_nat_rdr_lst *entry;
584 			uint32_t *const id = (uint32_t *)msg->data;
585 
586 			if (msg->header.arglen < sizeof(*id)) {
587 				error = EINVAL;
588 				break;
589 			}
590 
591 			/* Find entry with supplied id. */
592 			STAILQ_FOREACH(entry, &priv->redirhead, entries) {
593 				if (entry->rdr.id == *id)
594 					break;
595 			}
596 
597 			/* Not found. */
598 			if (entry == NULL) {
599 				error = ENOENT;
600 				break;
601 			}
602 
603 			if (msg->header.cmd == NGM_NAT_REDIRECT_DYNAMIC) {
604 				if (LibAliasRedirectDynamic(priv->lib,
605 				    entry->lnk) == -1) {
606 					error = ENOTTY;	/* XXX Something better? */
607 					break;
608 				}
609 			} else {	/* NGM_NAT_REDIRECT_DELETE */
610 				LibAliasRedirectDelete(priv->lib, entry->lnk);
611 			}
612 
613 			/* Delete entry from our internal list. */
614 			priv->rdrcount--;
615 			STAILQ_REMOVE(&priv->redirhead, entry, ng_nat_rdr_lst, entries);
616 			free(entry, M_NETGRAPH);
617 		    }
618 			break;
619 		case NGM_NAT_ADD_SERVER:
620 		    {
621 			struct ng_nat_rdr_lst *entry;
622 			struct ng_nat_add_server *const as =
623 			    (struct ng_nat_add_server *)msg->data;
624 
625 			if (msg->header.arglen < sizeof(*as)) {
626 				error = EINVAL;
627 				break;
628 			}
629 
630 			/* Find entry with supplied id. */
631 			STAILQ_FOREACH(entry, &priv->redirhead, entries) {
632 				if (entry->rdr.id == as->id)
633 					break;
634 			}
635 
636 			/* Not found. */
637 			if (entry == NULL) {
638 				error = ENOENT;
639 				break;
640 			}
641 
642 			if (LibAliasAddServer(priv->lib, entry->lnk,
643 			    as->addr, htons(as->port)) == -1) {
644 				error = ENOMEM;
645 				break;
646 			}
647 
648 			entry->rdr.lsnat++;
649 		    }
650 			break;
651 		case NGM_NAT_LIST_REDIRECTS:
652 		    {
653 			struct ng_nat_rdr_lst *entry;
654 			struct ng_nat_list_redirects *ary;
655 			int i = 0;
656 
657 			NG_MKRESPONSE(resp, msg, sizeof(*ary) +
658 			    (priv->rdrcount) * sizeof(*entry), M_NOWAIT);
659 			if (resp == NULL) {
660 				error = ENOMEM;
661 				break;
662 			}
663 
664 			ary = (struct ng_nat_list_redirects *)resp->data;
665 			ary->total_count = priv->rdrcount;
666 
667 			STAILQ_FOREACH(entry, &priv->redirhead, entries) {
668 				bcopy(&entry->rdr, &ary->redirects[i++],
669 				    sizeof(struct ng_nat_listrdrs_entry));
670 			}
671 		    }
672 			break;
673 		case NGM_NAT_PROXY_RULE:
674 		    {
675 			char *cmd = (char *)msg->data;
676 
677 			if (msg->header.arglen < 6) {
678 				error = EINVAL;
679 				break;
680 			}
681 
682 			if (LibAliasProxyRule(priv->lib, cmd) != 0)
683 				error = ENOMEM;
684 		    }
685 			break;
686 		case NGM_NAT_LIBALIAS_INFO:
687 		    {
688 			struct ng_nat_libalias_info *i;
689 
690 			NG_MKRESPONSE(resp, msg,
691 			    sizeof(struct ng_nat_libalias_info), M_NOWAIT);
692 			if (resp == NULL) {
693 				error = ENOMEM;
694 				break;
695 			}
696 			i = (struct ng_nat_libalias_info *)resp->data;
697 #define	COPY(F)	do {						\
698 	if (priv->lib->F >= 0 && priv->lib->F < UINT32_MAX)	\
699 		i->F = priv->lib->F;				\
700 	else							\
701 		i->F = UINT32_MAX;				\
702 } while (0)
703 
704 			COPY(icmpLinkCount);
705 			COPY(udpLinkCount);
706 			COPY(tcpLinkCount);
707 			COPY(pptpLinkCount);
708 			COPY(sctpLinkCount);
709 			COPY(protoLinkCount);
710 			COPY(fragmentIdLinkCount);
711 			COPY(fragmentPtrLinkCount);
712 			COPY(sockCount);
713 #undef COPY
714 		    }
715 			break;
716 		case NGM_NAT_SET_DLT:
717 			if (msg->header.arglen != sizeof(uint8_t)) {
718 				error = EINVAL;
719 				break;
720 			}
721 			switch (*(uint8_t *) msg->data) {
722 			case DLT_EN10MB:
723 			case DLT_RAW:
724 				priv->dlt = *(uint8_t *) msg->data;
725 				break;
726 			default:
727 				error = EINVAL;
728 				break;
729 			}
730 			break;
731 		default:
732 			error = EINVAL;		/* unknown command */
733 			break;
734 		}
735 		break;
736 		case NGM_NAT_GET_DLT:
737 			NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK);
738                         if (resp == NULL) {
739                                 error = ENOMEM;
740 				break;
741 			}
742 			*((uint8_t *) resp->data) = priv->dlt;
743 			break;
744 	default:
745 		error = EINVAL;			/* unknown cookie type */
746 		break;
747 	}
748 
749 	NG_RESPOND_MSG(error, node, item, resp);
750 	NG_FREE_MSG(msg);
751 	return (error);
752 }
753 
754 static int
755 ng_nat_rcvdata(hook_p hook, item_p item )
756 {
757 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
758 	struct mbuf	*m;
759 	struct ip	*ip;
760 	int rval, ipofs, error = 0;
761 	char *c;
762 
763 	/* We have no required hooks. */
764 	if (!(priv->flags & NGNAT_CONNECTED)) {
765 		NG_FREE_ITEM(item);
766 		return (ENXIO);
767 	}
768 
769 	/* We have no alias address yet to do anything. */
770 	if (!(priv->flags & NGNAT_ADDR_DEFINED))
771 		goto send;
772 
773 	m = NGI_M(item);
774 
775 	if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) {
776 		NGI_M(item) = NULL;	/* avoid double free */
777 		NG_FREE_ITEM(item);
778 		return (ENOBUFS);
779 	}
780 
781 	NGI_M(item) = m;
782 
783 	switch (priv->dlt) {
784 	case DLT_RAW:
785 		ipofs = 0;
786 		break;
787 	case DLT_EN10MB:
788 	    {
789 		struct ether_header *eh;
790 
791 		if (m->m_pkthdr.len < sizeof(struct ether_header)) {
792 			NG_FREE_ITEM(item);
793 			return (ENXIO);
794 		}
795 		eh = mtod(m, struct ether_header *);
796 		switch (ntohs(eh->ether_type)) {
797 		case ETHERTYPE_IP:
798 			ipofs = sizeof(struct ether_header);
799 			break;
800 		default:
801 			goto send;
802 		}
803 		break;
804 	    }
805 	default:
806 		panic("Corrupted priv->dlt: %u", priv->dlt);
807 	}
808 
809 	if (m->m_pkthdr.len < ipofs + sizeof(struct ip))
810 		goto send;		/* packet too short to hold IP */
811 
812 	c = (char *)mtodo(m, ipofs);
813 	ip = (struct ip *)mtodo(m, ipofs);
814 
815 	if (ip->ip_v != IPVERSION)
816 		goto send;		/* other IP version, let it pass */
817 	if (m->m_pkthdr.len < ipofs + ntohs(ip->ip_len))
818 		goto send;		/* packet too short (i.e. fragmented or broken) */
819 
820 	/*
821 	 * We drop packet when:
822 	 * 1. libalias returns PKT_ALIAS_ERROR;
823 	 * 2. For incoming packets:
824 	 *	a) for unresolved fragments;
825 	 *	b) libalias returns PKT_ALIAS_IGNORED and
826 	 *		PKT_ALIAS_DENY_INCOMING flag is set.
827 	 */
828 	if (hook == priv->in) {
829 		rval = LibAliasIn(priv->lib, c, m->m_len - ipofs +
830 		    M_TRAILINGSPACE(m));
831 		if (rval == PKT_ALIAS_ERROR ||
832 		    rval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
833 		    (rval == PKT_ALIAS_IGNORED &&
834 		     (priv->lib->packetAliasMode &
835 		      PKT_ALIAS_DENY_INCOMING) != 0)) {
836 			NG_FREE_ITEM(item);
837 			return (EINVAL);
838 		}
839 	} else if (hook == priv->out) {
840 		rval = LibAliasOut(priv->lib, c, m->m_len - ipofs +
841 		    M_TRAILINGSPACE(m));
842 		if (rval == PKT_ALIAS_ERROR) {
843 			NG_FREE_ITEM(item);
844 			return (EINVAL);
845 		}
846 	} else
847 		panic("ng_nat: unknown hook!\n");
848 
849 	if (rval == PKT_ALIAS_RESPOND)
850 		m->m_flags |= M_SKIP_FIREWALL;
851 	m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len) + ipofs;
852 
853 	if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
854 	    ip->ip_p == IPPROTO_TCP) {
855 		struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
856 		    (ip->ip_hl << 2));
857 
858 		/*
859 		 * Here is our terrible HACK.
860 		 *
861 		 * Sometimes LibAlias edits contents of TCP packet.
862 		 * In this case it needs to recompute full TCP
863 		 * checksum. However, the problem is that LibAlias
864 		 * doesn't have any idea about checksum offloading
865 		 * in kernel. To workaround this, we do not do
866 		 * checksumming in LibAlias, but only mark the
867 		 * packets in th_x2 field. If we receive a marked
868 		 * packet, we calculate correct checksum for it
869 		 * aware of offloading.
870 		 *
871 		 * Why do I do such a terrible hack instead of
872 		 * recalculating checksum for each packet?
873 		 * Because the previous checksum was not checked!
874 		 * Recalculating checksums for EVERY packet will
875 		 * hide ALL transmission errors. Yes, marked packets
876 		 * still suffer from this problem. But, sigh, natd(8)
877 		 * has this problem, too.
878 		 */
879 
880 		if (th->th_x2) {
881 			uint16_t ip_len = ntohs(ip->ip_len);
882 
883 			th->th_x2 = 0;
884 			th->th_sum = in_pseudo(ip->ip_src.s_addr,
885 			    ip->ip_dst.s_addr, htons(IPPROTO_TCP +
886 			    ip_len - (ip->ip_hl << 2)));
887 
888 			if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
889 				m->m_pkthdr.csum_data = offsetof(struct tcphdr,
890 				    th_sum);
891 				in_delayed_cksum(m);
892 			}
893 		}
894 	}
895 
896 send:
897 	if (hook == priv->in)
898 		NG_FWD_ITEM_HOOK(error, item, priv->out);
899 	else
900 		NG_FWD_ITEM_HOOK(error, item, priv->in);
901 
902 	return (error);
903 }
904 
905 static int
906 ng_nat_shutdown(node_p node)
907 {
908 	const priv_p priv = NG_NODE_PRIVATE(node);
909 
910 	NG_NODE_SET_PRIVATE(node, NULL);
911 	NG_NODE_UNREF(node);
912 
913 	/* Free redirects list. */
914 	while (!STAILQ_EMPTY(&priv->redirhead)) {
915 		struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead);
916 		STAILQ_REMOVE_HEAD(&priv->redirhead, entries);
917 		free(entry, M_NETGRAPH);
918 	}
919 
920 	/* Final free. */
921 	LibAliasUninit(priv->lib);
922 	free(priv, M_NETGRAPH);
923 
924 	return (0);
925 }
926 
927 static int
928 ng_nat_disconnect(hook_p hook)
929 {
930 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
931 
932 	priv->flags &= ~NGNAT_CONNECTED;
933 
934 	if (hook == priv->out)
935 		priv->out = NULL;
936 	if (hook == priv->in)
937 		priv->in = NULL;
938 
939 	if (priv->out == NULL && priv->in == NULL)
940 		ng_rmnode_self(NG_HOOK_NODE(hook));
941 
942 	return (0);
943 }
944 
945 static unsigned int
946 ng_nat_translate_flags(unsigned int x)
947 {
948 	unsigned int	res = 0;
949 
950 	if (x & NG_NAT_LOG)
951 		res |= PKT_ALIAS_LOG;
952 	if (x & NG_NAT_DENY_INCOMING)
953 		res |= PKT_ALIAS_DENY_INCOMING;
954 	if (x & NG_NAT_SAME_PORTS)
955 		res |= PKT_ALIAS_SAME_PORTS;
956 	if (x & NG_NAT_UNREGISTERED_ONLY)
957 		res |= PKT_ALIAS_UNREGISTERED_ONLY;
958 	if (x & NG_NAT_RESET_ON_ADDR_CHANGE)
959 		res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
960 	if (x & NG_NAT_PROXY_ONLY)
961 		res |= PKT_ALIAS_PROXY_ONLY;
962 	if (x & NG_NAT_REVERSE)
963 		res |= PKT_ALIAS_REVERSE;
964 	if (x & NG_NAT_UNREGISTERED_CGN)
965 		res |= PKT_ALIAS_UNREGISTERED_CGN;
966 
967 	return (res);
968 }
969