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