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