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