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