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