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