ng_nat.c revision 359698
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: stable/11/sys/netgraph/ng_nat.c 359698 2020-04-07 16:47:35Z eugen $
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
56static ng_constructor_t	ng_nat_constructor;
57static ng_rcvmsg_t	ng_nat_rcvmsg;
58static ng_shutdown_t	ng_nat_shutdown;
59static ng_newhook_t	ng_nat_newhook;
60static ng_rcvdata_t	ng_nat_rcvdata;
61static ng_disconnect_t	ng_nat_disconnect;
62
63static unsigned int	ng_nat_translate_flags(unsigned int x);
64
65/* Parse type for struct ng_nat_mode. */
66static const struct ng_parse_struct_field ng_nat_mode_fields[]
67	= NG_NAT_MODE_INFO;
68static 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. */
74static const struct ng_parse_fixedstring_info ng_nat_description_info
75	= { NG_NAT_DESC_LENGTH };
76static 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. */
82static const struct ng_parse_struct_field ng_nat_redirect_port_fields[]
83	= NG_NAT_REDIRECT_PORT_TYPE_INFO(&ng_nat_description_type);
84static 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. */
90static const struct ng_parse_struct_field ng_nat_redirect_addr_fields[]
91	= NG_NAT_REDIRECT_ADDR_TYPE_INFO(&ng_nat_description_type);
92static 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. */
98static const struct ng_parse_struct_field ng_nat_redirect_proto_fields[]
99	= NG_NAT_REDIRECT_PROTO_TYPE_INFO(&ng_nat_description_type);
100static 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. */
106static const struct ng_parse_struct_field ng_nat_add_server_fields[]
107	= NG_NAT_ADD_SERVER_TYPE_INFO;
108static 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. */
114static const struct ng_parse_struct_field ng_nat_listrdrs_entry_fields[]
115	= NG_NAT_LISTRDRS_ENTRY_TYPE_INFO(&ng_nat_description_type);
116static 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. */
122static int
123ng_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
133static 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};
138static 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. */
144static const struct ng_parse_struct_field ng_nat_list_redirects_fields[]
145	= NG_NAT_LIST_REDIRECTS_TYPE_INFO(&ng_nat_listrdrs_ary_type);
146static 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. */
152static const struct ng_parse_struct_field ng_nat_libalias_info_fields[]
153	= NG_NAT_LIBALIAS_INFO;
154static 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. */
160static 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. */
263static 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};
274NETGRAPH_INIT(nat, &typestruct);
275MODULE_DEPEND(ng_nat, libalias, 1, 1, 1);
276
277/* Element for list of redirects. */
278struct 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};
283STAILQ_HEAD(rdrhead, ng_nat_rdr_lst);
284
285/* Information we store for each node. */
286struct 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};
297typedef 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
303static int
304ng_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
337static int
338ng_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
356static int
357ng_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
752static int
753ng_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		case ETHERTYPE_IPV6:
797			ipofs = sizeof(struct ether_header);
798			break;
799		default:
800			goto send;
801		}
802		break;
803	    }
804	default:
805		panic("Corrupted priv->dlt: %u", priv->dlt);
806	}
807
808	if (m->m_pkthdr.len < ipofs + sizeof(struct ip))
809		goto send;		/* packet too short to hold IP */
810
811	c = (char *)mtodo(m, ipofs);
812	ip = (struct ip *)mtodo(m, ipofs);
813
814	if (ip->ip_v != IPVERSION)
815		goto send;		/* other IP version, let it pass */
816	if (m->m_pkthdr.len < ipofs + ntohs(ip->ip_len))
817		goto send;		/* packet too short (i.e. fragmented or broken) */
818
819	/*
820	 * We drop packet when:
821	 * 1. libalias returns PKT_ALIAS_ERROR;
822	 * 2. For incoming packets:
823	 *	a) for unresolved fragments;
824	 *	b) libalias returns PKT_ALIAS_IGNORED and
825	 *		PKT_ALIAS_DENY_INCOMING flag is set.
826	 */
827	if (hook == priv->in) {
828		rval = LibAliasIn(priv->lib, c, m->m_len - ipofs +
829		    M_TRAILINGSPACE(m));
830		if (rval == PKT_ALIAS_ERROR ||
831		    rval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
832		    (rval == PKT_ALIAS_IGNORED &&
833		     (priv->lib->packetAliasMode &
834		      PKT_ALIAS_DENY_INCOMING) != 0)) {
835			NG_FREE_ITEM(item);
836			return (EINVAL);
837		}
838	} else if (hook == priv->out) {
839		rval = LibAliasOut(priv->lib, c, m->m_len - ipofs +
840		    M_TRAILINGSPACE(m));
841		if (rval == PKT_ALIAS_ERROR) {
842			NG_FREE_ITEM(item);
843			return (EINVAL);
844		}
845	} else
846		panic("ng_nat: unknown hook!\n");
847
848	if (rval == PKT_ALIAS_RESPOND)
849		m->m_flags |= M_SKIP_FIREWALL;
850	m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len) + ipofs;
851
852	if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
853	    ip->ip_p == IPPROTO_TCP) {
854		struct tcphdr *th = (struct tcphdr *)((caddr_t)ip +
855		    (ip->ip_hl << 2));
856
857		/*
858		 * Here is our terrible HACK.
859		 *
860		 * Sometimes LibAlias edits contents of TCP packet.
861		 * In this case it needs to recompute full TCP
862		 * checksum. However, the problem is that LibAlias
863		 * doesn't have any idea about checksum offloading
864		 * in kernel. To workaround this, we do not do
865		 * checksumming in LibAlias, but only mark the
866		 * packets in th_x2 field. If we receive a marked
867		 * packet, we calculate correct checksum for it
868		 * aware of offloading.
869		 *
870		 * Why do I do such a terrible hack instead of
871		 * recalculating checksum for each packet?
872		 * Because the previous checksum was not checked!
873		 * Recalculating checksums for EVERY packet will
874		 * hide ALL transmission errors. Yes, marked packets
875		 * still suffer from this problem. But, sigh, natd(8)
876		 * has this problem, too.
877		 */
878
879		if (th->th_x2) {
880			uint16_t ip_len = ntohs(ip->ip_len);
881
882			th->th_x2 = 0;
883			th->th_sum = in_pseudo(ip->ip_src.s_addr,
884			    ip->ip_dst.s_addr, htons(IPPROTO_TCP +
885			    ip_len - (ip->ip_hl << 2)));
886
887			if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) {
888				m->m_pkthdr.csum_data = offsetof(struct tcphdr,
889				    th_sum);
890				in_delayed_cksum(m);
891			}
892		}
893	}
894
895send:
896	if (hook == priv->in)
897		NG_FWD_ITEM_HOOK(error, item, priv->out);
898	else
899		NG_FWD_ITEM_HOOK(error, item, priv->in);
900
901	return (error);
902}
903
904static int
905ng_nat_shutdown(node_p node)
906{
907	const priv_p priv = NG_NODE_PRIVATE(node);
908
909	NG_NODE_SET_PRIVATE(node, NULL);
910	NG_NODE_UNREF(node);
911
912	/* Free redirects list. */
913	while (!STAILQ_EMPTY(&priv->redirhead)) {
914		struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead);
915		STAILQ_REMOVE_HEAD(&priv->redirhead, entries);
916		free(entry, M_NETGRAPH);
917	}
918
919	/* Final free. */
920	LibAliasUninit(priv->lib);
921	free(priv, M_NETGRAPH);
922
923	return (0);
924}
925
926static int
927ng_nat_disconnect(hook_p hook)
928{
929	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
930
931	priv->flags &= ~NGNAT_CONNECTED;
932
933	if (hook == priv->out)
934		priv->out = NULL;
935	if (hook == priv->in)
936		priv->in = NULL;
937
938	if (priv->out == NULL && priv->in == NULL)
939		ng_rmnode_self(NG_HOOK_NODE(hook));
940
941	return (0);
942}
943
944static unsigned int
945ng_nat_translate_flags(unsigned int x)
946{
947	unsigned int	res = 0;
948
949	if (x & NG_NAT_LOG)
950		res |= PKT_ALIAS_LOG;
951	if (x & NG_NAT_DENY_INCOMING)
952		res |= PKT_ALIAS_DENY_INCOMING;
953	if (x & NG_NAT_SAME_PORTS)
954		res |= PKT_ALIAS_SAME_PORTS;
955	if (x & NG_NAT_UNREGISTERED_ONLY)
956		res |= PKT_ALIAS_UNREGISTERED_ONLY;
957	if (x & NG_NAT_RESET_ON_ADDR_CHANGE)
958		res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE;
959	if (x & NG_NAT_PROXY_ONLY)
960		res |= PKT_ALIAS_PROXY_ONLY;
961	if (x & NG_NAT_REVERSE)
962		res |= PKT_ALIAS_REVERSE;
963
964	return (res);
965}
966