1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2008 Paolo Pisati
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/eventhandler.h>
32#include <sys/malloc.h>
33#include <sys/mbuf.h>
34#include <sys/kernel.h>
35#include <sys/lock.h>
36#include <sys/module.h>
37#include <sys/rwlock.h>
38#include <sys/rmlock.h>
39
40#include <netinet/libalias/alias.h>
41#include <netinet/libalias/alias_local.h>
42
43#include <net/if.h>
44#include <net/if_var.h>
45#include <net/if_private.h>
46#include <netinet/in.h>
47#include <netinet/ip.h>
48#include <netinet/ip_var.h>
49#include <netinet/ip_fw.h>
50#include <netinet/tcp.h>
51#include <netinet/udp.h>
52
53#include <netpfil/ipfw/ip_fw_private.h>
54
55#include <machine/in_cksum.h>	/* XXX for in_cksum */
56
57struct cfg_spool {
58	LIST_ENTRY(cfg_spool)   _next;          /* chain of spool instances */
59	struct in_addr          addr;
60	uint16_t		port;
61};
62
63/* Nat redirect configuration. */
64struct cfg_redir {
65	LIST_ENTRY(cfg_redir)	_next;	/* chain of redir instances */
66	uint16_t		mode;	/* type of redirect mode */
67	uint16_t		proto;	/* protocol: tcp/udp */
68	struct in_addr		laddr;	/* local ip address */
69	struct in_addr		paddr;	/* public ip address */
70	struct in_addr		raddr;	/* remote ip address */
71	uint16_t		lport;	/* local port */
72	uint16_t		pport;	/* public port */
73	uint16_t		rport;	/* remote port	*/
74	uint16_t		pport_cnt;	/* number of public ports */
75	uint16_t		rport_cnt;	/* number of remote ports */
76	struct alias_link	**alink;
77	u_int16_t		spool_cnt; /* num of entry in spool chain */
78	/* chain of spool instances */
79	LIST_HEAD(spool_chain, cfg_spool) spool_chain;
80};
81
82/* Nat configuration data struct. */
83struct cfg_nat {
84	/* chain of nat instances */
85	LIST_ENTRY(cfg_nat)	_next;
86	int			id;		/* nat id  */
87	struct in_addr		ip;		/* nat ip address */
88	struct libalias		*lib;		/* libalias instance */
89	int			mode;		/* aliasing mode */
90	int			redir_cnt; /* number of entry in spool chain */
91	/* chain of redir instances */
92	LIST_HEAD(redir_chain, cfg_redir) redir_chain;
93	char			if_name[IF_NAMESIZE];	/* interface name */
94	u_short			alias_port_lo;	/* low range for port aliasing */
95	u_short			alias_port_hi;	/* high range for port aliasing */
96};
97
98static eventhandler_tag ifaddr_event_tag;
99
100static void
101ifaddr_change(void *arg __unused, struct ifnet *ifp)
102{
103	struct cfg_nat *ptr;
104	struct ifaddr *ifa;
105	struct ip_fw_chain *chain;
106
107	KASSERT(curvnet == ifp->if_vnet,
108	    ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet));
109
110	if (V_ipfw_vnet_ready == 0 || V_ipfw_nat_ready == 0)
111		return;
112
113	chain = &V_layer3_chain;
114	IPFW_UH_WLOCK(chain);
115	/* Check every nat entry... */
116	LIST_FOREACH(ptr, &chain->nat, _next) {
117		struct epoch_tracker et;
118
119		/* ...using nic 'ifp->if_xname' as dynamic alias address. */
120		if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0)
121			continue;
122		NET_EPOCH_ENTER(et);
123		CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
124			if (ifa->ifa_addr == NULL)
125				continue;
126			if (ifa->ifa_addr->sa_family != AF_INET)
127				continue;
128			IPFW_WLOCK(chain);
129			ptr->ip = ((struct sockaddr_in *)
130			    (ifa->ifa_addr))->sin_addr;
131			LibAliasSetAddress(ptr->lib, ptr->ip);
132			IPFW_WUNLOCK(chain);
133		}
134		NET_EPOCH_EXIT(et);
135	}
136	IPFW_UH_WUNLOCK(chain);
137}
138
139/*
140 * delete the pointers for nat entry ix, or all of them if ix < 0
141 */
142static void
143flush_nat_ptrs(struct ip_fw_chain *chain, const int ix)
144{
145	ipfw_insn_nat *cmd;
146	int i;
147
148	IPFW_WLOCK_ASSERT(chain);
149	for (i = 0; i < chain->n_rules; i++) {
150		cmd = (ipfw_insn_nat *)ipfw_get_action(chain->map[i]);
151		if (cmd->o.opcode == O_NAT && cmd->nat != NULL &&
152			    (ix < 0 || cmd->nat->id == ix))
153			cmd->nat = NULL;
154	}
155}
156
157static void
158del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
159{
160	struct cfg_redir *r, *tmp_r;
161	struct cfg_spool *s, *tmp_s;
162	int i, num;
163
164	LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
165		num = 1; /* Number of alias_link to delete. */
166		switch (r->mode) {
167		case NAT44_REDIR_PORT:
168			num = r->pport_cnt;
169			/* FALLTHROUGH */
170		case NAT44_REDIR_ADDR:
171		case NAT44_REDIR_PROTO:
172			/* Delete all libalias redirect entry. */
173			for (i = 0; i < num; i++)
174				LibAliasRedirectDelete(n->lib, r->alink[i]);
175			/* Del spool cfg if any. */
176			LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) {
177				LIST_REMOVE(s, _next);
178				free(s, M_IPFW);
179			}
180			free(r->alink, M_IPFW);
181			LIST_REMOVE(r, _next);
182			free(r, M_IPFW);
183			break;
184		default:
185			printf("unknown redirect mode: %u\n", r->mode);
186			/* XXX - panic?!?!? */
187			break;
188		}
189	}
190}
191
192static int
193add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
194{
195	struct cfg_redir *r;
196	struct cfg_spool *s;
197	struct nat44_cfg_redir *ser_r;
198	struct nat44_cfg_spool *ser_s;
199
200	int cnt, off, i;
201
202	for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
203		ser_r = (struct nat44_cfg_redir *)&buf[off];
204		r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO);
205		r->mode = ser_r->mode;
206		r->laddr = ser_r->laddr;
207		r->paddr = ser_r->paddr;
208		r->raddr = ser_r->raddr;
209		r->lport = ser_r->lport;
210		r->pport = ser_r->pport;
211		r->rport = ser_r->rport;
212		r->pport_cnt = ser_r->pport_cnt;
213		r->rport_cnt = ser_r->rport_cnt;
214		r->proto = ser_r->proto;
215		r->spool_cnt = ser_r->spool_cnt;
216		//memcpy(r, ser_r, SOF_REDIR);
217		LIST_INIT(&r->spool_chain);
218		off += sizeof(struct nat44_cfg_redir);
219		r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
220		    M_IPFW, M_WAITOK | M_ZERO);
221		switch (r->mode) {
222		case NAT44_REDIR_ADDR:
223			r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
224			    r->paddr);
225			break;
226		case NAT44_REDIR_PORT:
227			for (i = 0 ; i < r->pport_cnt; i++) {
228				/* If remotePort is all ports, set it to 0. */
229				u_short remotePortCopy = r->rport + i;
230				if (r->rport_cnt == 1 && r->rport == 0)
231					remotePortCopy = 0;
232				r->alink[i] = LibAliasRedirectPort(ptr->lib,
233				    r->laddr, htons(r->lport + i), r->raddr,
234				    htons(remotePortCopy), r->paddr,
235				    htons(r->pport + i), r->proto);
236				if (r->alink[i] == NULL) {
237					r->alink[0] = NULL;
238					break;
239				}
240			}
241			break;
242		case NAT44_REDIR_PROTO:
243			r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
244			    r->raddr, r->paddr, r->proto);
245			break;
246		default:
247			printf("unknown redirect mode: %u\n", r->mode);
248			break;
249		}
250		if (r->alink[0] == NULL) {
251			printf("LibAliasRedirect* returned NULL\n");
252			free(r->alink, M_IPFW);
253			free(r, M_IPFW);
254			return (EINVAL);
255		}
256		/* LSNAT handling. */
257		for (i = 0; i < r->spool_cnt; i++) {
258			ser_s = (struct nat44_cfg_spool *)&buf[off];
259			s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO);
260			s->addr = ser_s->addr;
261			s->port = ser_s->port;
262			LibAliasAddServer(ptr->lib, r->alink[0],
263			    s->addr, htons(s->port));
264			off += sizeof(struct nat44_cfg_spool);
265			/* Hook spool entry. */
266			LIST_INSERT_HEAD(&r->spool_chain, s, _next);
267		}
268		/* And finally hook this redir entry. */
269		LIST_INSERT_HEAD(&ptr->redir_chain, r, _next);
270	}
271
272	return (0);
273}
274
275static void
276free_nat_instance(struct cfg_nat *ptr)
277{
278
279	del_redir_spool_cfg(ptr, &ptr->redir_chain);
280	LibAliasUninit(ptr->lib);
281	free(ptr, M_IPFW);
282}
283
284/*
285 * ipfw_nat - perform mbuf header translation.
286 *
287 * Note V_layer3_chain has to be locked while calling ipfw_nat() in
288 * 'global' operation mode (t == NULL).
289 *
290 */
291static int
292ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
293{
294	struct mbuf *mcl;
295	struct ip *ip;
296	/* XXX - libalias duct tape */
297	int ldt, retval, found;
298	struct ip_fw_chain *chain;
299	char *c;
300
301	ldt = 0;
302	retval = 0;
303	mcl = m_megapullup(m, m->m_pkthdr.len);
304	if (mcl == NULL) {
305		args->m = NULL;
306		return (IP_FW_DENY);
307	}
308	M_ASSERTMAPPED(mcl);
309	ip = mtod(mcl, struct ip *);
310
311	/*
312	 * XXX - Libalias checksum offload 'duct tape':
313	 *
314	 * locally generated packets have only pseudo-header checksum
315	 * calculated and libalias will break it[1], so mark them for
316	 * later fix.  Moreover there are cases when libalias modifies
317	 * tcp packet data[2], mark them for later fix too.
318	 *
319	 * [1] libalias was never meant to run in kernel, so it does
320	 * not have any knowledge about checksum offloading, and
321	 * expects a packet with a full internet checksum.
322	 * Unfortunately, packets generated locally will have just the
323	 * pseudo header calculated, and when libalias tries to adjust
324	 * the checksum it will actually compute a wrong value.
325	 *
326	 * [2] when libalias modifies tcp's data content, full TCP
327	 * checksum has to be recomputed: the problem is that
328	 * libalias does not have any idea about checksum offloading.
329	 * To work around this, we do not do checksumming in LibAlias,
330	 * but only mark the packets in th_x2 field. If we receive a
331	 * marked packet, we calculate correct checksum for it
332	 * aware of offloading.  Why such a terrible hack instead of
333	 * recalculating checksum for each packet?
334	 * Because the previous checksum was not checked!
335	 * Recalculating checksums for EVERY packet will hide ALL
336	 * transmission errors. Yes, marked packets still suffer from
337	 * this problem. But, sigh, natd(8) has this problem, too.
338	 *
339	 * TODO: -make libalias mbuf aware (so
340	 * it can handle delayed checksum and tso)
341	 */
342
343	if (mcl->m_pkthdr.rcvif == NULL &&
344	    mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA)
345		ldt = 1;
346
347	c = mtod(mcl, char *);
348
349	/* Check if this is 'global' instance */
350	if (t == NULL) {
351		if (args->flags & IPFW_ARGS_IN) {
352			/* Wrong direction, skip processing */
353			args->m = mcl;
354			return (IP_FW_NAT);
355		}
356
357		found = 0;
358		chain = &V_layer3_chain;
359		IPFW_RLOCK_ASSERT(chain);
360		/* Check every nat entry... */
361		LIST_FOREACH(t, &chain->nat, _next) {
362			if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0)
363				continue;
364			retval = LibAliasOutTry(t->lib, c,
365			    mcl->m_len + M_TRAILINGSPACE(mcl), 0);
366			if (retval == PKT_ALIAS_OK) {
367				/* Nat instance recognises state */
368				found = 1;
369				break;
370			}
371		}
372		if (found != 1) {
373			/* No instance found, return ignore */
374			args->m = mcl;
375			return (IP_FW_NAT);
376		}
377	} else {
378		if (args->flags & IPFW_ARGS_IN)
379			retval = LibAliasIn(t->lib, c,
380				mcl->m_len + M_TRAILINGSPACE(mcl));
381		else
382			retval = LibAliasOut(t->lib, c,
383				mcl->m_len + M_TRAILINGSPACE(mcl));
384	}
385
386	/*
387	 * We drop packet when:
388	 * 1. libalias returns PKT_ALIAS_ERROR;
389	 * 2. For incoming packets:
390	 *	a) for unresolved fragments;
391	 *	b) libalias returns PKT_ALIAS_IGNORED and
392	 *		PKT_ALIAS_DENY_INCOMING flag is set.
393	 */
394	if (retval == PKT_ALIAS_ERROR ||
395	    ((args->flags & IPFW_ARGS_IN) &&
396	    (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
397	    (retval == PKT_ALIAS_IGNORED &&
398	    (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) {
399		/* XXX - should i add some logging? */
400		m_free(mcl);
401		args->m = NULL;
402		return (IP_FW_DENY);
403	}
404
405	if (retval == PKT_ALIAS_RESPOND)
406		mcl->m_flags |= M_SKIP_FIREWALL;
407	mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
408
409	/*
410	 * XXX - libalias checksum offload
411	 * 'duct tape' (see above)
412	 */
413
414	if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
415	    ip->ip_p == IPPROTO_TCP) {
416		struct tcphdr 	*th;
417
418		th = (struct tcphdr *)(ip + 1);
419		if (th->th_x2 & (TH_RES1 >> 8))
420			ldt = 1;
421	}
422
423	if (ldt) {
424		struct tcphdr 	*th;
425		struct udphdr 	*uh;
426		uint16_t ip_len, cksum;
427
428		ip_len = ntohs(ip->ip_len);
429		cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
430		    htons(ip->ip_p + ip_len - (ip->ip_hl << 2)));
431
432		switch (ip->ip_p) {
433		case IPPROTO_TCP:
434			th = (struct tcphdr *)(ip + 1);
435			/*
436			 * Maybe it was set in
437			 * libalias...
438			 */
439			th->th_x2 &= ~(TH_RES1 >> 8);
440			th->th_sum = cksum;
441			mcl->m_pkthdr.csum_data =
442			    offsetof(struct tcphdr, th_sum);
443			break;
444		case IPPROTO_UDP:
445			uh = (struct udphdr *)(ip + 1);
446			uh->uh_sum = cksum;
447			mcl->m_pkthdr.csum_data =
448			    offsetof(struct udphdr, uh_sum);
449			break;
450		}
451		/* No hw checksum offloading: do it ourselves */
452		if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) {
453			in_delayed_cksum(mcl);
454			mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
455		}
456	}
457	args->m = mcl;
458	return (IP_FW_NAT);
459}
460
461static struct cfg_nat *
462lookup_nat(struct nat_list *l, int nat_id)
463{
464	struct cfg_nat *res;
465
466	LIST_FOREACH(res, l, _next) {
467		if (res->id == nat_id)
468			break;
469	}
470	return res;
471}
472
473static struct cfg_nat *
474lookup_nat_name(struct nat_list *l, char *name)
475{
476	struct cfg_nat *res;
477	int id;
478	char *errptr;
479
480	id = strtol(name, &errptr, 10);
481	if (id == 0 || *errptr != '\0')
482		return (NULL);
483
484	LIST_FOREACH(res, l, _next) {
485		if (res->id == id)
486			break;
487	}
488	return (res);
489}
490
491/* IP_FW3 configuration routines */
492
493static void
494nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg)
495{
496	struct cfg_nat *ptr, *tcfg;
497	int gencnt;
498
499	/*
500	 * Find/create nat rule.
501	 */
502	IPFW_UH_WLOCK(chain);
503	gencnt = chain->gencnt;
504	ptr = lookup_nat_name(&chain->nat, ucfg->name);
505	if (ptr == NULL) {
506		IPFW_UH_WUNLOCK(chain);
507		/* New rule: allocate and init new instance. */
508		ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO);
509		ptr->lib = LibAliasInit(NULL);
510		LIST_INIT(&ptr->redir_chain);
511	} else {
512		/* Entry already present: temporarily unhook it. */
513		IPFW_WLOCK(chain);
514		LIST_REMOVE(ptr, _next);
515		flush_nat_ptrs(chain, ptr->id);
516		IPFW_WUNLOCK(chain);
517		IPFW_UH_WUNLOCK(chain);
518	}
519
520	/*
521	 * Basic nat (re)configuration.
522	 */
523	ptr->id = strtol(ucfg->name, NULL, 10);
524	/*
525	 * XXX - what if this rule doesn't nat any ip and just
526	 * redirect?
527	 * do we set aliasaddress to 0.0.0.0?
528	 */
529	ptr->ip = ucfg->ip;
530	ptr->redir_cnt = ucfg->redir_cnt;
531	ptr->mode = ucfg->mode;
532	ptr->alias_port_lo = ucfg->alias_port_lo;
533	ptr->alias_port_hi = ucfg->alias_port_hi;
534	strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name));
535	LibAliasSetMode(ptr->lib, ptr->mode, ~0);
536	LibAliasSetAddress(ptr->lib, ptr->ip);
537	LibAliasSetAliasPortRange(ptr->lib, ptr->alias_port_lo, ptr->alias_port_hi);
538
539	/*
540	 * Redir and LSNAT configuration.
541	 */
542	/* Delete old cfgs. */
543	del_redir_spool_cfg(ptr, &ptr->redir_chain);
544	/* Add new entries. */
545	add_redir_spool_cfg((char *)(ucfg + 1), ptr);
546	IPFW_UH_WLOCK(chain);
547
548	/* Extra check to avoid race with another ipfw_nat_cfg() */
549	tcfg = NULL;
550	if (gencnt != chain->gencnt)
551	    tcfg = lookup_nat_name(&chain->nat, ucfg->name);
552	IPFW_WLOCK(chain);
553	if (tcfg != NULL)
554		LIST_REMOVE(tcfg, _next);
555	LIST_INSERT_HEAD(&chain->nat, ptr, _next);
556	IPFW_WUNLOCK(chain);
557	chain->gencnt++;
558
559	IPFW_UH_WUNLOCK(chain);
560
561	if (tcfg != NULL)
562		free_nat_instance(ptr);
563}
564
565/*
566 * Creates/configure nat44 instance
567 * Data layout (v0)(current):
568 * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
569 *
570 * Returns 0 on success
571 */
572static int
573nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
574    struct sockopt_data *sd)
575{
576	ipfw_obj_header *oh;
577	struct nat44_cfg_nat *ucfg;
578	int id;
579	size_t read;
580	char *errptr;
581
582	/* Check minimum header size */
583	if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg)))
584		return (EINVAL);
585
586	oh = (ipfw_obj_header *)sd->kbuf;
587
588	/* Basic length checks for TLVs */
589	if (oh->ntlv.head.length != sizeof(oh->ntlv))
590		return (EINVAL);
591
592	ucfg = (struct nat44_cfg_nat *)(oh + 1);
593
594	/* Check if name is properly terminated and looks like number */
595	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
596		return (EINVAL);
597	id = strtol(ucfg->name, &errptr, 10);
598	if (id == 0 || *errptr != '\0')
599		return (EINVAL);
600
601	read = sizeof(*oh) + sizeof(*ucfg);
602	/* Check number of redirs */
603	if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir))
604		return (EINVAL);
605
606	nat44_config(chain, ucfg);
607	return (0);
608}
609
610/*
611 * Destroys given nat instances.
612 * Data layout (v0)(current):
613 * Request: [ ipfw_obj_header ]
614 *
615 * Returns 0 on success
616 */
617static int
618nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
619    struct sockopt_data *sd)
620{
621	ipfw_obj_header *oh;
622	struct cfg_nat *ptr;
623	ipfw_obj_ntlv *ntlv;
624
625	/* Check minimum header size */
626	if (sd->valsize < sizeof(*oh))
627		return (EINVAL);
628
629	oh = (ipfw_obj_header *)sd->kbuf;
630
631	/* Basic length checks for TLVs */
632	if (oh->ntlv.head.length != sizeof(oh->ntlv))
633		return (EINVAL);
634
635	ntlv = &oh->ntlv;
636	/* Check if name is properly terminated */
637	if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name))
638		return (EINVAL);
639
640	IPFW_UH_WLOCK(chain);
641	ptr = lookup_nat_name(&chain->nat, ntlv->name);
642	if (ptr == NULL) {
643		IPFW_UH_WUNLOCK(chain);
644		return (ESRCH);
645	}
646	IPFW_WLOCK(chain);
647	LIST_REMOVE(ptr, _next);
648	flush_nat_ptrs(chain, ptr->id);
649	IPFW_WUNLOCK(chain);
650	IPFW_UH_WUNLOCK(chain);
651
652	free_nat_instance(ptr);
653
654	return (0);
655}
656
657static void
658export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg)
659{
660
661	snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id);
662	ucfg->ip = ptr->ip;
663	ucfg->redir_cnt = ptr->redir_cnt;
664	ucfg->mode = ptr->mode;
665	ucfg->alias_port_lo = ptr->alias_port_lo;
666	ucfg->alias_port_hi = ptr->alias_port_hi;
667	strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name));
668}
669
670/*
671 * Gets config for given nat instance
672 * Data layout (v0)(current):
673 * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
674 *
675 * Returns 0 on success
676 */
677static int
678nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
679    struct sockopt_data *sd)
680{
681	ipfw_obj_header *oh;
682	struct nat44_cfg_nat *ucfg;
683	struct cfg_nat *ptr;
684	struct cfg_redir *r;
685	struct cfg_spool *s;
686	struct nat44_cfg_redir *ser_r;
687	struct nat44_cfg_spool *ser_s;
688	size_t sz;
689
690	sz = sizeof(*oh) + sizeof(*ucfg);
691	/* Check minimum header size */
692	if (sd->valsize < sz)
693		return (EINVAL);
694
695	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
696
697	/* Basic length checks for TLVs */
698	if (oh->ntlv.head.length != sizeof(oh->ntlv))
699		return (EINVAL);
700
701	ucfg = (struct nat44_cfg_nat *)(oh + 1);
702
703	/* Check if name is properly terminated */
704	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
705		return (EINVAL);
706
707	IPFW_UH_RLOCK(chain);
708	ptr = lookup_nat_name(&chain->nat, ucfg->name);
709	if (ptr == NULL) {
710		IPFW_UH_RUNLOCK(chain);
711		return (ESRCH);
712	}
713
714	export_nat_cfg(ptr, ucfg);
715
716	/* Estimate memory amount */
717	sz = sizeof(ipfw_obj_header) + sizeof(struct nat44_cfg_nat);
718	LIST_FOREACH(r, &ptr->redir_chain, _next) {
719		sz += sizeof(struct nat44_cfg_redir);
720		LIST_FOREACH(s, &r->spool_chain, _next)
721			sz += sizeof(struct nat44_cfg_spool);
722	}
723
724	ucfg->size = sz;
725	if (sd->valsize < sz) {
726		/*
727		 * Submitted buffer size is not enough.
728		 * WE've already filled in @ucfg structure with
729		 * relevant info including size, so we
730		 * can return. Buffer will be flushed automatically.
731		 */
732		IPFW_UH_RUNLOCK(chain);
733		return (ENOMEM);
734	}
735
736	/* Size OK, let's copy data */
737	LIST_FOREACH(r, &ptr->redir_chain, _next) {
738		ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd,
739		    sizeof(*ser_r));
740		ser_r->mode = r->mode;
741		ser_r->laddr = r->laddr;
742		ser_r->paddr = r->paddr;
743		ser_r->raddr = r->raddr;
744		ser_r->lport = r->lport;
745		ser_r->pport = r->pport;
746		ser_r->rport = r->rport;
747		ser_r->pport_cnt = r->pport_cnt;
748		ser_r->rport_cnt = r->rport_cnt;
749		ser_r->proto = r->proto;
750		ser_r->spool_cnt = r->spool_cnt;
751
752		LIST_FOREACH(s, &r->spool_chain, _next) {
753			ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space(
754			    sd, sizeof(*ser_s));
755
756			ser_s->addr = s->addr;
757			ser_s->port = s->port;
758		}
759	}
760
761	IPFW_UH_RUNLOCK(chain);
762
763	return (0);
764}
765
766/*
767 * Lists all nat44 instances currently available in kernel.
768 * Data layout (v0)(current):
769 * Request: [ ipfw_obj_lheader ]
770 * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ]
771 *
772 * Returns 0 on success
773 */
774static int
775nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
776    struct sockopt_data *sd)
777{
778	ipfw_obj_lheader *olh;
779	struct nat44_cfg_nat *ucfg;
780	struct cfg_nat *ptr;
781	int nat_count;
782
783	/* Check minimum header size */
784	if (sd->valsize < sizeof(ipfw_obj_lheader))
785		return (EINVAL);
786
787	olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
788	IPFW_UH_RLOCK(chain);
789	nat_count = 0;
790	LIST_FOREACH(ptr, &chain->nat, _next)
791		nat_count++;
792
793	olh->count = nat_count;
794	olh->objsize = sizeof(struct nat44_cfg_nat);
795	olh->size = sizeof(*olh) + olh->count * olh->objsize;
796
797	if (sd->valsize < olh->size) {
798		IPFW_UH_RUNLOCK(chain);
799		return (ENOMEM);
800	}
801
802	LIST_FOREACH(ptr, &chain->nat, _next) {
803		ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd,
804		    sizeof(*ucfg));
805		export_nat_cfg(ptr, ucfg);
806	}
807
808	IPFW_UH_RUNLOCK(chain);
809
810	return (0);
811}
812
813/*
814 * Gets log for given nat instance
815 * Data layout (v0)(current):
816 * Request: [ ipfw_obj_header nat44_cfg_nat ]
817 * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ]
818 *
819 * Returns 0 on success
820 */
821static int
822nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
823    struct sockopt_data *sd)
824{
825	ipfw_obj_header *oh;
826	struct nat44_cfg_nat *ucfg;
827	struct cfg_nat *ptr;
828	void *pbuf;
829	size_t sz;
830
831	sz = sizeof(*oh) + sizeof(*ucfg);
832	/* Check minimum header size */
833	if (sd->valsize < sz)
834		return (EINVAL);
835
836	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
837
838	/* Basic length checks for TLVs */
839	if (oh->ntlv.head.length != sizeof(oh->ntlv))
840		return (EINVAL);
841
842	ucfg = (struct nat44_cfg_nat *)(oh + 1);
843
844	/* Check if name is properly terminated */
845	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
846		return (EINVAL);
847
848	IPFW_UH_RLOCK(chain);
849	ptr = lookup_nat_name(&chain->nat, ucfg->name);
850	if (ptr == NULL) {
851		IPFW_UH_RUNLOCK(chain);
852		return (ESRCH);
853	}
854
855	if (ptr->lib->logDesc == NULL) {
856		IPFW_UH_RUNLOCK(chain);
857		return (ENOENT);
858	}
859
860	export_nat_cfg(ptr, ucfg);
861
862	/* Estimate memory amount */
863	ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE;
864	if (sd->valsize < sz + sizeof(*oh)) {
865		/*
866		 * Submitted buffer size is not enough.
867		 * WE've already filled in @ucfg structure with
868		 * relevant info including size, so we
869		 * can return. Buffer will be flushed automatically.
870		 */
871		IPFW_UH_RUNLOCK(chain);
872		return (ENOMEM);
873	}
874
875	pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE);
876	memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE);
877
878	IPFW_UH_RUNLOCK(chain);
879
880	return (0);
881}
882
883static struct ipfw_sopt_handler	scodes[] = {
884	{ IP_FW_NAT44_XCONFIG,	0,	HDIR_SET,	nat44_cfg },
885	{ IP_FW_NAT44_DESTROY,	0,	HDIR_SET,	nat44_destroy },
886	{ IP_FW_NAT44_XGETCONFIG,	0,	HDIR_GET,	nat44_get_cfg },
887	{ IP_FW_NAT44_LIST_NAT,	0,	HDIR_GET,	nat44_list_nat },
888	{ IP_FW_NAT44_XGETLOG,	0,	HDIR_GET,	nat44_get_log },
889};
890
891/*
892 * Legacy configuration routines
893 */
894
895struct cfg_spool_legacy {
896	LIST_ENTRY(cfg_spool_legacy)	_next;
897	struct in_addr			addr;
898	u_short				port;
899};
900
901struct cfg_redir_legacy {
902	LIST_ENTRY(cfg_redir)   _next;
903	u_int16_t               mode;
904	struct in_addr	        laddr;
905	struct in_addr	        paddr;
906	struct in_addr	        raddr;
907	u_short                 lport;
908	u_short                 pport;
909	u_short                 rport;
910	u_short                 pport_cnt;
911	u_short                 rport_cnt;
912	int                     proto;
913	struct alias_link       **alink;
914	u_int16_t               spool_cnt;
915	LIST_HEAD(, cfg_spool_legacy) spool_chain;
916};
917
918struct cfg_nat_legacy {
919	LIST_ENTRY(cfg_nat_legacy)	_next;
920	int				id;
921	struct in_addr			ip;
922	char				if_name[IF_NAMESIZE];
923	int				mode;
924	struct libalias			*lib;
925	int				redir_cnt;
926	LIST_HEAD(, cfg_redir_legacy)	redir_chain;
927};
928
929static int
930ipfw_nat_cfg(struct sockopt *sopt)
931{
932	struct cfg_nat_legacy *cfg;
933	struct nat44_cfg_nat *ucfg;
934	struct cfg_redir_legacy *rdir;
935	struct nat44_cfg_redir *urdir;
936	char *buf;
937	size_t len, len2;
938	int error, i;
939
940	len = sopt->sopt_valsize;
941	len2 = len + 128;
942
943	/*
944	 * Allocate 2x buffer to store converted structures.
945	 * new redir_cfg has shrunk, so we're sure that
946	 * new buffer size is enough.
947	 */
948	buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO);
949	error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy));
950	if (error != 0)
951		goto out;
952
953	cfg = (struct cfg_nat_legacy *)buf;
954	if (cfg->id < 0) {
955		error = EINVAL;
956		goto out;
957	}
958
959	ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)];
960	snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id);
961	strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name));
962	ucfg->ip = cfg->ip;
963	ucfg->mode = cfg->mode;
964	ucfg->redir_cnt = cfg->redir_cnt;
965
966	if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) {
967		error = EINVAL;
968		goto out;
969	}
970
971	urdir = (struct nat44_cfg_redir *)(ucfg + 1);
972	rdir = (struct cfg_redir_legacy *)(cfg + 1);
973	for (i = 0; i < cfg->redir_cnt; i++) {
974		urdir->mode = rdir->mode;
975		urdir->laddr = rdir->laddr;
976		urdir->paddr = rdir->paddr;
977		urdir->raddr = rdir->raddr;
978		urdir->lport = rdir->lport;
979		urdir->pport = rdir->pport;
980		urdir->rport = rdir->rport;
981		urdir->pport_cnt = rdir->pport_cnt;
982		urdir->rport_cnt = rdir->rport_cnt;
983		urdir->proto = rdir->proto;
984		urdir->spool_cnt = rdir->spool_cnt;
985
986		urdir++;
987		rdir++;
988	}
989
990	nat44_config(&V_layer3_chain, ucfg);
991
992out:
993	free(buf, M_TEMP);
994	return (error);
995}
996
997static int
998ipfw_nat_del(struct sockopt *sopt)
999{
1000	struct cfg_nat *ptr;
1001	struct ip_fw_chain *chain = &V_layer3_chain;
1002	int i;
1003
1004	sooptcopyin(sopt, &i, sizeof i, sizeof i);
1005	/* XXX validate i */
1006	IPFW_UH_WLOCK(chain);
1007	ptr = lookup_nat(&chain->nat, i);
1008	if (ptr == NULL) {
1009		IPFW_UH_WUNLOCK(chain);
1010		return (EINVAL);
1011	}
1012	IPFW_WLOCK(chain);
1013	LIST_REMOVE(ptr, _next);
1014	flush_nat_ptrs(chain, i);
1015	IPFW_WUNLOCK(chain);
1016	IPFW_UH_WUNLOCK(chain);
1017	free_nat_instance(ptr);
1018	return (0);
1019}
1020
1021static int
1022ipfw_nat_get_cfg(struct sockopt *sopt)
1023{
1024	struct ip_fw_chain *chain = &V_layer3_chain;
1025	struct cfg_nat *n;
1026	struct cfg_nat_legacy *ucfg;
1027	struct cfg_redir *r;
1028	struct cfg_spool *s;
1029	struct cfg_redir_legacy *ser_r;
1030	struct cfg_spool_legacy *ser_s;
1031	char *data;
1032	int gencnt, nat_cnt, len, error;
1033
1034	nat_cnt = 0;
1035	len = sizeof(nat_cnt);
1036
1037	IPFW_UH_RLOCK(chain);
1038retry:
1039	gencnt = chain->gencnt;
1040	/* Estimate memory amount */
1041	LIST_FOREACH(n, &chain->nat, _next) {
1042		nat_cnt++;
1043		len += sizeof(struct cfg_nat_legacy);
1044		LIST_FOREACH(r, &n->redir_chain, _next) {
1045			len += sizeof(struct cfg_redir_legacy);
1046			LIST_FOREACH(s, &r->spool_chain, _next)
1047				len += sizeof(struct cfg_spool_legacy);
1048		}
1049	}
1050	IPFW_UH_RUNLOCK(chain);
1051
1052	data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
1053	bcopy(&nat_cnt, data, sizeof(nat_cnt));
1054
1055	nat_cnt = 0;
1056	len = sizeof(nat_cnt);
1057
1058	IPFW_UH_RLOCK(chain);
1059	if (gencnt != chain->gencnt) {
1060		free(data, M_TEMP);
1061		goto retry;
1062	}
1063	/* Serialize all the data. */
1064	LIST_FOREACH(n, &chain->nat, _next) {
1065		ucfg = (struct cfg_nat_legacy *)&data[len];
1066		ucfg->id = n->id;
1067		ucfg->ip = n->ip;
1068		ucfg->redir_cnt = n->redir_cnt;
1069		ucfg->mode = n->mode;
1070		strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name));
1071		len += sizeof(struct cfg_nat_legacy);
1072		LIST_FOREACH(r, &n->redir_chain, _next) {
1073			ser_r = (struct cfg_redir_legacy *)&data[len];
1074			ser_r->mode = r->mode;
1075			ser_r->laddr = r->laddr;
1076			ser_r->paddr = r->paddr;
1077			ser_r->raddr = r->raddr;
1078			ser_r->lport = r->lport;
1079			ser_r->pport = r->pport;
1080			ser_r->rport = r->rport;
1081			ser_r->pport_cnt = r->pport_cnt;
1082			ser_r->rport_cnt = r->rport_cnt;
1083			ser_r->proto = r->proto;
1084			ser_r->spool_cnt = r->spool_cnt;
1085			len += sizeof(struct cfg_redir_legacy);
1086			LIST_FOREACH(s, &r->spool_chain, _next) {
1087				ser_s = (struct cfg_spool_legacy *)&data[len];
1088				ser_s->addr = s->addr;
1089				ser_s->port = s->port;
1090				len += sizeof(struct cfg_spool_legacy);
1091			}
1092		}
1093	}
1094	IPFW_UH_RUNLOCK(chain);
1095
1096	error = sooptcopyout(sopt, data, len);
1097	free(data, M_TEMP);
1098
1099	return (error);
1100}
1101
1102static int
1103ipfw_nat_get_log(struct sockopt *sopt)
1104{
1105	uint8_t *data;
1106	struct cfg_nat *ptr;
1107	int i, size;
1108	struct ip_fw_chain *chain;
1109	IPFW_RLOCK_TRACKER;
1110
1111	chain = &V_layer3_chain;
1112
1113	IPFW_RLOCK(chain);
1114	/* one pass to count, one to copy the data */
1115	i = 0;
1116	LIST_FOREACH(ptr, &chain->nat, _next) {
1117		if (ptr->lib->logDesc == NULL)
1118			continue;
1119		i++;
1120	}
1121	size = i * (LIBALIAS_BUF_SIZE + sizeof(int));
1122	data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO);
1123	if (data == NULL) {
1124		IPFW_RUNLOCK(chain);
1125		return (ENOSPC);
1126	}
1127	i = 0;
1128	LIST_FOREACH(ptr, &chain->nat, _next) {
1129		if (ptr->lib->logDesc == NULL)
1130			continue;
1131		bcopy(&ptr->id, &data[i], sizeof(int));
1132		i += sizeof(int);
1133		bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE);
1134		i += LIBALIAS_BUF_SIZE;
1135	}
1136	IPFW_RUNLOCK(chain);
1137	sooptcopyout(sopt, data, size);
1138	free(data, M_IPFW);
1139	return(0);
1140}
1141
1142static int
1143vnet_ipfw_nat_init(const void *arg __unused)
1144{
1145
1146	V_ipfw_nat_ready = 1;
1147	return (0);
1148}
1149
1150static int
1151vnet_ipfw_nat_uninit(const void *arg __unused)
1152{
1153	struct cfg_nat *ptr, *ptr_temp;
1154	struct ip_fw_chain *chain;
1155
1156	chain = &V_layer3_chain;
1157	IPFW_WLOCK(chain);
1158	V_ipfw_nat_ready = 0;
1159	LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) {
1160		LIST_REMOVE(ptr, _next);
1161		free_nat_instance(ptr);
1162	}
1163	flush_nat_ptrs(chain, -1 /* flush all */);
1164	IPFW_WUNLOCK(chain);
1165	return (0);
1166}
1167
1168static void
1169ipfw_nat_init(void)
1170{
1171
1172	/* init ipfw hooks */
1173	ipfw_nat_ptr = ipfw_nat;
1174	lookup_nat_ptr = lookup_nat;
1175	ipfw_nat_cfg_ptr = ipfw_nat_cfg;
1176	ipfw_nat_del_ptr = ipfw_nat_del;
1177	ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
1178	ipfw_nat_get_log_ptr = ipfw_nat_get_log;
1179	IPFW_ADD_SOPT_HANDLER(1, scodes);
1180
1181	ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
1182	    NULL, EVENTHANDLER_PRI_ANY);
1183}
1184
1185static void
1186ipfw_nat_destroy(void)
1187{
1188
1189	EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
1190	/* deregister ipfw_nat */
1191	IPFW_DEL_SOPT_HANDLER(1, scodes);
1192	ipfw_nat_ptr = NULL;
1193	lookup_nat_ptr = NULL;
1194	ipfw_nat_cfg_ptr = NULL;
1195	ipfw_nat_del_ptr = NULL;
1196	ipfw_nat_get_cfg_ptr = NULL;
1197	ipfw_nat_get_log_ptr = NULL;
1198}
1199
1200static int
1201ipfw_nat_modevent(module_t mod, int type, void *unused)
1202{
1203	int err = 0;
1204
1205	switch (type) {
1206	case MOD_LOAD:
1207		break;
1208
1209	case MOD_UNLOAD:
1210		break;
1211
1212	default:
1213		return EOPNOTSUPP;
1214		break;
1215	}
1216	return err;
1217}
1218
1219static moduledata_t ipfw_nat_mod = {
1220	"ipfw_nat",
1221	ipfw_nat_modevent,
1222	0
1223};
1224
1225/* Define startup order. */
1226#define	IPFW_NAT_SI_SUB_FIREWALL	SI_SUB_PROTO_FIREWALL
1227#define	IPFW_NAT_MODEVENT_ORDER		(SI_ORDER_ANY - 128) /* after ipfw */
1228#define	IPFW_NAT_MODULE_ORDER		(IPFW_NAT_MODEVENT_ORDER + 1)
1229#define	IPFW_NAT_VNET_ORDER		(IPFW_NAT_MODEVENT_ORDER + 2)
1230
1231DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY);
1232MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1);
1233MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3);
1234MODULE_VERSION(ipfw_nat, 1);
1235
1236SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
1237    ipfw_nat_init, NULL);
1238VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER,
1239    vnet_ipfw_nat_init, NULL);
1240
1241SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
1242    ipfw_nat_destroy, NULL);
1243VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL,
1244    IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL);
1245
1246/* end of file */
1247