1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/eventhandler.h>
35#include <sys/malloc.h>
36#include <sys/mbuf.h>
37#include <sys/kernel.h>
38#include <sys/lock.h>
39#include <sys/module.h>
40#include <sys/rwlock.h>
41#include <sys/rmlock.h>
42
43#include <netinet/libalias/alias.h>
44#include <netinet/libalias/alias_local.h>
45
46#include <net/if.h>
47#include <net/if_var.h>
48#include <net/pfil.h>
49#include <netinet/in.h>
50#include <netinet/ip.h>
51#include <netinet/ip_var.h>
52#include <netinet/ip_fw.h>
53#include <netinet/tcp.h>
54#include <netinet/udp.h>
55
56#include <netpfil/ipfw/ip_fw_private.h>
57
58#include <machine/in_cksum.h>	/* XXX for in_cksum */
59
60struct cfg_spool {
61	LIST_ENTRY(cfg_spool)   _next;          /* chain of spool instances */
62	struct in_addr          addr;
63	uint16_t		port;
64};
65
66/* Nat redirect configuration. */
67struct cfg_redir {
68	LIST_ENTRY(cfg_redir)	_next;	/* chain of redir instances */
69	uint16_t		mode;	/* type of redirect mode */
70	uint16_t		proto;	/* protocol: tcp/udp */
71	struct in_addr		laddr;	/* local ip address */
72	struct in_addr		paddr;	/* public ip address */
73	struct in_addr		raddr;	/* remote ip address */
74	uint16_t		lport;	/* local port */
75	uint16_t		pport;	/* public port */
76	uint16_t		rport;	/* remote port	*/
77	uint16_t		pport_cnt;	/* number of public ports */
78	uint16_t		rport_cnt;	/* number of remote ports */
79	struct alias_link	**alink;
80	u_int16_t		spool_cnt; /* num of entry in spool chain */
81	/* chain of spool instances */
82	LIST_HEAD(spool_chain, cfg_spool) spool_chain;
83};
84
85/* Nat configuration data struct. */
86struct cfg_nat {
87	/* chain of nat instances */
88	LIST_ENTRY(cfg_nat)	_next;
89	int			id;		/* nat id  */
90	struct in_addr		ip;		/* nat ip address */
91	struct libalias		*lib;		/* libalias instance */
92	int			mode;		/* aliasing mode */
93	int			redir_cnt; /* number of entry in spool chain */
94	/* chain of redir instances */
95	LIST_HEAD(redir_chain, cfg_redir) redir_chain;
96	char			if_name[IF_NAMESIZE];	/* interface name */
97	u_short			alias_port_lo;	/* low range for port aliasing */
98	u_short			alias_port_hi;	/* high range for port aliasing */
99};
100
101static eventhandler_tag ifaddr_event_tag;
102
103static void
104ifaddr_change(void *arg __unused, struct ifnet *ifp)
105{
106	struct cfg_nat *ptr;
107	struct ifaddr *ifa;
108	struct ip_fw_chain *chain;
109
110	KASSERT(curvnet == ifp->if_vnet,
111	    ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet));
112
113	if (V_ipfw_vnet_ready == 0 || V_ipfw_nat_ready == 0)
114		return;
115
116	chain = &V_layer3_chain;
117	IPFW_UH_WLOCK(chain);
118	/* Check every nat entry... */
119	LIST_FOREACH(ptr, &chain->nat, _next) {
120		/* ...using nic 'ifp->if_xname' as dynamic alias address. */
121		if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0)
122			continue;
123		if_addr_rlock(ifp);
124		CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
125			if (ifa->ifa_addr == NULL)
126				continue;
127			if (ifa->ifa_addr->sa_family != AF_INET)
128				continue;
129			IPFW_WLOCK(chain);
130			ptr->ip = ((struct sockaddr_in *)
131			    (ifa->ifa_addr))->sin_addr;
132			LibAliasSetAddress(ptr->lib, ptr->ip);
133			IPFW_WUNLOCK(chain);
134		}
135		if_addr_runlock(ifp);
136	}
137	IPFW_UH_WUNLOCK(chain);
138}
139
140/*
141 * delete the pointers for nat entry ix, or all of them if ix < 0
142 */
143static void
144flush_nat_ptrs(struct ip_fw_chain *chain, const int ix)
145{
146	ipfw_insn_nat *cmd;
147	int i;
148
149	IPFW_WLOCK_ASSERT(chain);
150	for (i = 0; i < chain->n_rules; i++) {
151		cmd = (ipfw_insn_nat *)ipfw_get_action(chain->map[i]);
152		if (cmd->o.opcode == O_NAT && cmd->nat != NULL &&
153			    (ix < 0 || cmd->nat->id == ix))
154			cmd->nat = NULL;
155	}
156}
157
158static void
159del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
160{
161	struct cfg_redir *r, *tmp_r;
162	struct cfg_spool *s, *tmp_s;
163	int i, num;
164
165	LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
166		num = 1; /* Number of alias_link to delete. */
167		switch (r->mode) {
168		case NAT44_REDIR_PORT:
169			num = r->pport_cnt;
170			/* FALLTHROUGH */
171		case NAT44_REDIR_ADDR:
172		case NAT44_REDIR_PROTO:
173			/* Delete all libalias redirect entry. */
174			for (i = 0; i < num; i++)
175				LibAliasRedirectDelete(n->lib, r->alink[i]);
176			/* Del spool cfg if any. */
177			LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) {
178				LIST_REMOVE(s, _next);
179				free(s, M_IPFW);
180			}
181			free(r->alink, M_IPFW);
182			LIST_REMOVE(r, _next);
183			free(r, M_IPFW);
184			break;
185		default:
186			printf("unknown redirect mode: %u\n", r->mode);
187			/* XXX - panic?!?!? */
188			break;
189		}
190	}
191}
192
193static int
194add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
195{
196	struct cfg_redir *r;
197	struct cfg_spool *s;
198	struct nat44_cfg_redir *ser_r;
199	struct nat44_cfg_spool *ser_s;
200
201	int cnt, off, i;
202
203	for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
204		ser_r = (struct nat44_cfg_redir *)&buf[off];
205		r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO);
206		r->mode = ser_r->mode;
207		r->laddr = ser_r->laddr;
208		r->paddr = ser_r->paddr;
209		r->raddr = ser_r->raddr;
210		r->lport = ser_r->lport;
211		r->pport = ser_r->pport;
212		r->rport = ser_r->rport;
213		r->pport_cnt = ser_r->pport_cnt;
214		r->rport_cnt = ser_r->rport_cnt;
215		r->proto = ser_r->proto;
216		r->spool_cnt = ser_r->spool_cnt;
217		//memcpy(r, ser_r, SOF_REDIR);
218		LIST_INIT(&r->spool_chain);
219		off += sizeof(struct nat44_cfg_redir);
220		r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
221		    M_IPFW, M_WAITOK | M_ZERO);
222		switch (r->mode) {
223		case NAT44_REDIR_ADDR:
224			r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
225			    r->paddr);
226			break;
227		case NAT44_REDIR_PORT:
228			for (i = 0 ; i < r->pport_cnt; i++) {
229				/* If remotePort is all ports, set it to 0. */
230				u_short remotePortCopy = r->rport + i;
231				if (r->rport_cnt == 1 && r->rport == 0)
232					remotePortCopy = 0;
233				r->alink[i] = LibAliasRedirectPort(ptr->lib,
234				    r->laddr, htons(r->lport + i), r->raddr,
235				    htons(remotePortCopy), r->paddr,
236				    htons(r->pport + i), r->proto);
237				if (r->alink[i] == NULL) {
238					r->alink[0] = NULL;
239					break;
240				}
241			}
242			break;
243		case NAT44_REDIR_PROTO:
244			r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
245			    r->raddr, r->paddr, r->proto);
246			break;
247		default:
248			printf("unknown redirect mode: %u\n", r->mode);
249			break;
250		}
251		if (r->alink[0] == NULL) {
252			printf("LibAliasRedirect* returned NULL\n");
253			free(r->alink, M_IPFW);
254			free(r, M_IPFW);
255			return (EINVAL);
256		}
257		/* LSNAT handling. */
258		for (i = 0; i < r->spool_cnt; i++) {
259			ser_s = (struct nat44_cfg_spool *)&buf[off];
260			s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO);
261			s->addr = ser_s->addr;
262			s->port = ser_s->port;
263			LibAliasAddServer(ptr->lib, r->alink[0],
264			    s->addr, htons(s->port));
265			off += sizeof(struct nat44_cfg_spool);
266			/* Hook spool entry. */
267			LIST_INSERT_HEAD(&r->spool_chain, s, _next);
268		}
269		/* And finally hook this redir entry. */
270		LIST_INSERT_HEAD(&ptr->redir_chain, r, _next);
271	}
272
273	return (0);
274}
275
276static void
277free_nat_instance(struct cfg_nat *ptr)
278{
279
280	del_redir_spool_cfg(ptr, &ptr->redir_chain);
281	LibAliasUninit(ptr->lib);
282	free(ptr, M_IPFW);
283}
284
285
286/*
287 * ipfw_nat - perform mbuf header translation.
288 *
289 * Note V_layer3_chain has to be locked while calling ipfw_nat() in
290 * 'global' operation mode (t == NULL).
291 *
292 */
293static int
294ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
295{
296	struct mbuf *mcl;
297	struct ip *ip;
298	/* XXX - libalias duct tape */
299	int ldt, retval, found;
300	struct ip_fw_chain *chain;
301	char *c;
302
303	ldt = 0;
304	retval = 0;
305	mcl = m_megapullup(m, m->m_pkthdr.len);
306	if (mcl == NULL) {
307		args->m = NULL;
308		return (IP_FW_DENY);
309	}
310	ip = mtod(mcl, struct ip *);
311
312	/*
313	 * XXX - Libalias checksum offload 'duct tape':
314	 *
315	 * locally generated packets have only pseudo-header checksum
316	 * calculated and libalias will break it[1], so mark them for
317	 * later fix.  Moreover there are cases when libalias modifies
318	 * tcp packet data[2], mark them for later fix too.
319	 *
320	 * [1] libalias was never meant to run in kernel, so it does
321	 * not have any knowledge about checksum offloading, and
322	 * expects a packet with a full internet checksum.
323	 * Unfortunately, packets generated locally will have just the
324	 * pseudo header calculated, and when libalias tries to adjust
325	 * the checksum it will actually compute a wrong value.
326	 *
327	 * [2] when libalias modifies tcp's data content, full TCP
328	 * checksum has to be recomputed: the problem is that
329	 * libalias does not have any idea about checksum offloading.
330	 * To work around this, we do not do checksumming in LibAlias,
331	 * but only mark the packets in th_x2 field. If we receive a
332	 * marked packet, we calculate correct checksum for it
333	 * aware of offloading.  Why such a terrible hack instead of
334	 * recalculating checksum for each packet?
335	 * Because the previous checksum was not checked!
336	 * Recalculating checksums for EVERY packet will hide ALL
337	 * transmission errors. Yes, marked packets still suffer from
338	 * this problem. But, sigh, natd(8) has this problem, too.
339	 *
340	 * TODO: -make libalias mbuf aware (so
341	 * it can handle delayed checksum and tso)
342	 */
343
344	if (mcl->m_pkthdr.rcvif == NULL &&
345	    mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA)
346		ldt = 1;
347
348	c = mtod(mcl, char *);
349
350	/* Check if this is 'global' instance */
351	if (t == NULL) {
352		if (args->oif == NULL) {
353			/* Wrong direction, skip processing */
354			args->m = mcl;
355			return (IP_FW_NAT);
356		}
357
358		found = 0;
359		chain = &V_layer3_chain;
360		IPFW_RLOCK_ASSERT(chain);
361		/* Check every nat entry... */
362		LIST_FOREACH(t, &chain->nat, _next) {
363			if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0)
364				continue;
365			retval = LibAliasOutTry(t->lib, c,
366			    mcl->m_len + M_TRAILINGSPACE(mcl), 0);
367			if (retval == PKT_ALIAS_OK) {
368				/* Nat instance recognises state */
369				found = 1;
370				break;
371			}
372		}
373		if (found != 1) {
374			/* No instance found, return ignore */
375			args->m = mcl;
376			return (IP_FW_NAT);
377		}
378	} else {
379		if (args->oif == NULL)
380			retval = LibAliasIn(t->lib, c,
381				mcl->m_len + M_TRAILINGSPACE(mcl));
382		else
383			retval = LibAliasOut(t->lib, c,
384				mcl->m_len + M_TRAILINGSPACE(mcl));
385	}
386
387	/*
388	 * We drop packet when:
389	 * 1. libalias returns PKT_ALIAS_ERROR;
390	 * 2. For incoming packets:
391	 *	a) for unresolved fragments;
392	 *	b) libalias returns PKT_ALIAS_IGNORED and
393	 *		PKT_ALIAS_DENY_INCOMING flag is set.
394	 */
395	if (retval == PKT_ALIAS_ERROR ||
396	    (args->oif == NULL && (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)
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 = 0;
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		/*
728		 * Submitted buffer size is not enough.
729		 * WE've already filled in @ucfg structure with
730		 * relevant info including size, so we
731		 * can return. Buffer will be flushed automatically.
732		 */
733		IPFW_UH_RUNLOCK(chain);
734		return (ENOMEM);
735	}
736
737	/* Size OK, let's copy data */
738	LIST_FOREACH(r, &ptr->redir_chain, _next) {
739		ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd,
740		    sizeof(*ser_r));
741		ser_r->mode = r->mode;
742		ser_r->laddr = r->laddr;
743		ser_r->paddr = r->paddr;
744		ser_r->raddr = r->raddr;
745		ser_r->lport = r->lport;
746		ser_r->pport = r->pport;
747		ser_r->rport = r->rport;
748		ser_r->pport_cnt = r->pport_cnt;
749		ser_r->rport_cnt = r->rport_cnt;
750		ser_r->proto = r->proto;
751		ser_r->spool_cnt = r->spool_cnt;
752
753		LIST_FOREACH(s, &r->spool_chain, _next) {
754			ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space(
755			    sd, sizeof(*ser_s));
756
757			ser_s->addr = s->addr;
758			ser_s->port = s->port;
759		}
760	}
761
762	IPFW_UH_RUNLOCK(chain);
763
764	return (0);
765}
766
767/*
768 * Lists all nat44 instances currently available in kernel.
769 * Data layout (v0)(current):
770 * Request: [ ipfw_obj_lheader ]
771 * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ]
772 *
773 * Returns 0 on success
774 */
775static int
776nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
777    struct sockopt_data *sd)
778{
779	ipfw_obj_lheader *olh;
780	struct nat44_cfg_nat *ucfg;
781	struct cfg_nat *ptr;
782	int nat_count;
783
784	/* Check minimum header size */
785	if (sd->valsize < sizeof(ipfw_obj_lheader))
786		return (EINVAL);
787
788	olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
789	IPFW_UH_RLOCK(chain);
790	nat_count = 0;
791	LIST_FOREACH(ptr, &chain->nat, _next)
792		nat_count++;
793
794	olh->count = nat_count;
795	olh->objsize = sizeof(struct nat44_cfg_nat);
796	olh->size = sizeof(*olh) + olh->count * olh->objsize;
797
798	if (sd->valsize < olh->size) {
799		IPFW_UH_RUNLOCK(chain);
800		return (ENOMEM);
801	}
802
803	LIST_FOREACH(ptr, &chain->nat, _next) {
804		ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd,
805		    sizeof(*ucfg));
806		export_nat_cfg(ptr, ucfg);
807	}
808
809	IPFW_UH_RUNLOCK(chain);
810
811	return (0);
812}
813
814/*
815 * Gets log for given nat instance
816 * Data layout (v0)(current):
817 * Request: [ ipfw_obj_header nat44_cfg_nat ]
818 * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ]
819 *
820 * Returns 0 on success
821 */
822static int
823nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
824    struct sockopt_data *sd)
825{
826	ipfw_obj_header *oh;
827	struct nat44_cfg_nat *ucfg;
828	struct cfg_nat *ptr;
829	void *pbuf;
830	size_t sz;
831
832	sz = sizeof(*oh) + sizeof(*ucfg);
833	/* Check minimum header size */
834	if (sd->valsize < sz)
835		return (EINVAL);
836
837	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
838
839	/* Basic length checks for TLVs */
840	if (oh->ntlv.head.length != sizeof(oh->ntlv))
841		return (EINVAL);
842
843	ucfg = (struct nat44_cfg_nat *)(oh + 1);
844
845	/* Check if name is properly terminated */
846	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
847		return (EINVAL);
848
849	IPFW_UH_RLOCK(chain);
850	ptr = lookup_nat_name(&chain->nat, ucfg->name);
851	if (ptr == NULL) {
852		IPFW_UH_RUNLOCK(chain);
853		return (ESRCH);
854	}
855
856	if (ptr->lib->logDesc == NULL) {
857		IPFW_UH_RUNLOCK(chain);
858		return (ENOENT);
859	}
860
861	export_nat_cfg(ptr, ucfg);
862
863	/* Estimate memory amount */
864	ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE;
865	if (sd->valsize < sz + sizeof(*oh)) {
866
867		/*
868		 * Submitted buffer size is not enough.
869		 * WE've already filled in @ucfg structure with
870		 * relevant info including size, so we
871		 * can return. Buffer will be flushed automatically.
872		 */
873		IPFW_UH_RUNLOCK(chain);
874		return (ENOMEM);
875	}
876
877	pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE);
878	memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE);
879
880	IPFW_UH_RUNLOCK(chain);
881
882	return (0);
883}
884
885static struct ipfw_sopt_handler	scodes[] = {
886	{ IP_FW_NAT44_XCONFIG,	0,	HDIR_SET,	nat44_cfg },
887	{ IP_FW_NAT44_DESTROY,	0,	HDIR_SET,	nat44_destroy },
888	{ IP_FW_NAT44_XGETCONFIG,	0,	HDIR_GET,	nat44_get_cfg },
889	{ IP_FW_NAT44_LIST_NAT,	0,	HDIR_GET,	nat44_list_nat },
890	{ IP_FW_NAT44_XGETLOG,	0,	HDIR_GET,	nat44_get_log },
891};
892
893
894/*
895 * Legacy configuration routines
896 */
897
898struct cfg_spool_legacy {
899	LIST_ENTRY(cfg_spool_legacy)	_next;
900	struct in_addr			addr;
901	u_short				port;
902};
903
904struct cfg_redir_legacy {
905	LIST_ENTRY(cfg_redir)   _next;
906	u_int16_t               mode;
907	struct in_addr	        laddr;
908	struct in_addr	        paddr;
909	struct in_addr	        raddr;
910	u_short                 lport;
911	u_short                 pport;
912	u_short                 rport;
913	u_short                 pport_cnt;
914	u_short                 rport_cnt;
915	int                     proto;
916	struct alias_link       **alink;
917	u_int16_t               spool_cnt;
918	LIST_HEAD(, cfg_spool_legacy) spool_chain;
919};
920
921struct cfg_nat_legacy {
922	LIST_ENTRY(cfg_nat_legacy)	_next;
923	int				id;
924	struct in_addr			ip;
925	char				if_name[IF_NAMESIZE];
926	int				mode;
927	struct libalias			*lib;
928	int				redir_cnt;
929	LIST_HEAD(, cfg_redir_legacy)	redir_chain;
930};
931
932static int
933ipfw_nat_cfg(struct sockopt *sopt)
934{
935	struct cfg_nat_legacy *cfg;
936	struct nat44_cfg_nat *ucfg;
937	struct cfg_redir_legacy *rdir;
938	struct nat44_cfg_redir *urdir;
939	char *buf;
940	size_t len, len2;
941	int error, i;
942
943	len = sopt->sopt_valsize;
944	len2 = len + 128;
945
946	/*
947	 * Allocate 2x buffer to store converted structures.
948	 * new redir_cfg has shrunk, so we're sure that
949	 * new buffer size is enough.
950	 */
951	buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO);
952	error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy));
953	if (error != 0)
954		goto out;
955
956	cfg = (struct cfg_nat_legacy *)buf;
957	if (cfg->id < 0) {
958		error = EINVAL;
959		goto out;
960	}
961
962	ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)];
963	snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id);
964	strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name));
965	ucfg->ip = cfg->ip;
966	ucfg->mode = cfg->mode;
967	ucfg->redir_cnt = cfg->redir_cnt;
968
969	if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) {
970		error = EINVAL;
971		goto out;
972	}
973
974	urdir = (struct nat44_cfg_redir *)(ucfg + 1);
975	rdir = (struct cfg_redir_legacy *)(cfg + 1);
976	for (i = 0; i < cfg->redir_cnt; i++) {
977		urdir->mode = rdir->mode;
978		urdir->laddr = rdir->laddr;
979		urdir->paddr = rdir->paddr;
980		urdir->raddr = rdir->raddr;
981		urdir->lport = rdir->lport;
982		urdir->pport = rdir->pport;
983		urdir->rport = rdir->rport;
984		urdir->pport_cnt = rdir->pport_cnt;
985		urdir->rport_cnt = rdir->rport_cnt;
986		urdir->proto = rdir->proto;
987		urdir->spool_cnt = rdir->spool_cnt;
988
989		urdir++;
990		rdir++;
991	}
992
993	nat44_config(&V_layer3_chain, ucfg);
994
995out:
996	free(buf, M_TEMP);
997	return (error);
998}
999
1000static int
1001ipfw_nat_del(struct sockopt *sopt)
1002{
1003	struct cfg_nat *ptr;
1004	struct ip_fw_chain *chain = &V_layer3_chain;
1005	int i;
1006
1007	sooptcopyin(sopt, &i, sizeof i, sizeof i);
1008	/* XXX validate i */
1009	IPFW_UH_WLOCK(chain);
1010	ptr = lookup_nat(&chain->nat, i);
1011	if (ptr == NULL) {
1012		IPFW_UH_WUNLOCK(chain);
1013		return (EINVAL);
1014	}
1015	IPFW_WLOCK(chain);
1016	LIST_REMOVE(ptr, _next);
1017	flush_nat_ptrs(chain, i);
1018	IPFW_WUNLOCK(chain);
1019	IPFW_UH_WUNLOCK(chain);
1020	free_nat_instance(ptr);
1021	return (0);
1022}
1023
1024static int
1025ipfw_nat_get_cfg(struct sockopt *sopt)
1026{
1027	struct ip_fw_chain *chain = &V_layer3_chain;
1028	struct cfg_nat *n;
1029	struct cfg_nat_legacy *ucfg;
1030	struct cfg_redir *r;
1031	struct cfg_spool *s;
1032	struct cfg_redir_legacy *ser_r;
1033	struct cfg_spool_legacy *ser_s;
1034	char *data;
1035	int gencnt, nat_cnt, len, error;
1036
1037	nat_cnt = 0;
1038	len = sizeof(nat_cnt);
1039
1040	IPFW_UH_RLOCK(chain);
1041retry:
1042	gencnt = chain->gencnt;
1043	/* Estimate memory amount */
1044	LIST_FOREACH(n, &chain->nat, _next) {
1045		nat_cnt++;
1046		len += sizeof(struct cfg_nat_legacy);
1047		LIST_FOREACH(r, &n->redir_chain, _next) {
1048			len += sizeof(struct cfg_redir_legacy);
1049			LIST_FOREACH(s, &r->spool_chain, _next)
1050				len += sizeof(struct cfg_spool_legacy);
1051		}
1052	}
1053	IPFW_UH_RUNLOCK(chain);
1054
1055	data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
1056	bcopy(&nat_cnt, data, sizeof(nat_cnt));
1057
1058	nat_cnt = 0;
1059	len = sizeof(nat_cnt);
1060
1061	IPFW_UH_RLOCK(chain);
1062	if (gencnt != chain->gencnt) {
1063		free(data, M_TEMP);
1064		goto retry;
1065	}
1066	/* Serialize all the data. */
1067	LIST_FOREACH(n, &chain->nat, _next) {
1068		ucfg = (struct cfg_nat_legacy *)&data[len];
1069		ucfg->id = n->id;
1070		ucfg->ip = n->ip;
1071		ucfg->redir_cnt = n->redir_cnt;
1072		ucfg->mode = n->mode;
1073		strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name));
1074		len += sizeof(struct cfg_nat_legacy);
1075		LIST_FOREACH(r, &n->redir_chain, _next) {
1076			ser_r = (struct cfg_redir_legacy *)&data[len];
1077			ser_r->mode = r->mode;
1078			ser_r->laddr = r->laddr;
1079			ser_r->paddr = r->paddr;
1080			ser_r->raddr = r->raddr;
1081			ser_r->lport = r->lport;
1082			ser_r->pport = r->pport;
1083			ser_r->rport = r->rport;
1084			ser_r->pport_cnt = r->pport_cnt;
1085			ser_r->rport_cnt = r->rport_cnt;
1086			ser_r->proto = r->proto;
1087			ser_r->spool_cnt = r->spool_cnt;
1088			len += sizeof(struct cfg_redir_legacy);
1089			LIST_FOREACH(s, &r->spool_chain, _next) {
1090				ser_s = (struct cfg_spool_legacy *)&data[len];
1091				ser_s->addr = s->addr;
1092				ser_s->port = s->port;
1093				len += sizeof(struct cfg_spool_legacy);
1094			}
1095		}
1096	}
1097	IPFW_UH_RUNLOCK(chain);
1098
1099	error = sooptcopyout(sopt, data, len);
1100	free(data, M_TEMP);
1101
1102	return (error);
1103}
1104
1105static int
1106ipfw_nat_get_log(struct sockopt *sopt)
1107{
1108	uint8_t *data;
1109	struct cfg_nat *ptr;
1110	int i, size;
1111	struct ip_fw_chain *chain;
1112	IPFW_RLOCK_TRACKER;
1113
1114	chain = &V_layer3_chain;
1115
1116	IPFW_RLOCK(chain);
1117	/* one pass to count, one to copy the data */
1118	i = 0;
1119	LIST_FOREACH(ptr, &chain->nat, _next) {
1120		if (ptr->lib->logDesc == NULL)
1121			continue;
1122		i++;
1123	}
1124	size = i * (LIBALIAS_BUF_SIZE + sizeof(int));
1125	data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO);
1126	if (data == NULL) {
1127		IPFW_RUNLOCK(chain);
1128		return (ENOSPC);
1129	}
1130	i = 0;
1131	LIST_FOREACH(ptr, &chain->nat, _next) {
1132		if (ptr->lib->logDesc == NULL)
1133			continue;
1134		bcopy(&ptr->id, &data[i], sizeof(int));
1135		i += sizeof(int);
1136		bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE);
1137		i += LIBALIAS_BUF_SIZE;
1138	}
1139	IPFW_RUNLOCK(chain);
1140	sooptcopyout(sopt, data, size);
1141	free(data, M_IPFW);
1142	return(0);
1143}
1144
1145static int
1146vnet_ipfw_nat_init(const void *arg __unused)
1147{
1148
1149	V_ipfw_nat_ready = 1;
1150	return (0);
1151}
1152
1153static int
1154vnet_ipfw_nat_uninit(const void *arg __unused)
1155{
1156	struct cfg_nat *ptr, *ptr_temp;
1157	struct ip_fw_chain *chain;
1158
1159	chain = &V_layer3_chain;
1160	IPFW_WLOCK(chain);
1161	V_ipfw_nat_ready = 0;
1162	LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) {
1163		LIST_REMOVE(ptr, _next);
1164		free_nat_instance(ptr);
1165	}
1166	flush_nat_ptrs(chain, -1 /* flush all */);
1167	IPFW_WUNLOCK(chain);
1168	return (0);
1169}
1170
1171static void
1172ipfw_nat_init(void)
1173{
1174
1175	/* init ipfw hooks */
1176	ipfw_nat_ptr = ipfw_nat;
1177	lookup_nat_ptr = lookup_nat;
1178	ipfw_nat_cfg_ptr = ipfw_nat_cfg;
1179	ipfw_nat_del_ptr = ipfw_nat_del;
1180	ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
1181	ipfw_nat_get_log_ptr = ipfw_nat_get_log;
1182	IPFW_ADD_SOPT_HANDLER(1, scodes);
1183
1184	ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
1185	    NULL, EVENTHANDLER_PRI_ANY);
1186}
1187
1188static void
1189ipfw_nat_destroy(void)
1190{
1191
1192	EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
1193	/* deregister ipfw_nat */
1194	IPFW_DEL_SOPT_HANDLER(1, scodes);
1195	ipfw_nat_ptr = NULL;
1196	lookup_nat_ptr = NULL;
1197	ipfw_nat_cfg_ptr = NULL;
1198	ipfw_nat_del_ptr = NULL;
1199	ipfw_nat_get_cfg_ptr = NULL;
1200	ipfw_nat_get_log_ptr = NULL;
1201}
1202
1203static int
1204ipfw_nat_modevent(module_t mod, int type, void *unused)
1205{
1206	int err = 0;
1207
1208	switch (type) {
1209	case MOD_LOAD:
1210		break;
1211
1212	case MOD_UNLOAD:
1213		break;
1214
1215	default:
1216		return EOPNOTSUPP;
1217		break;
1218	}
1219	return err;
1220}
1221
1222static moduledata_t ipfw_nat_mod = {
1223	"ipfw_nat",
1224	ipfw_nat_modevent,
1225	0
1226};
1227
1228/* Define startup order. */
1229#define	IPFW_NAT_SI_SUB_FIREWALL	SI_SUB_PROTO_FIREWALL
1230#define	IPFW_NAT_MODEVENT_ORDER		(SI_ORDER_ANY - 128) /* after ipfw */
1231#define	IPFW_NAT_MODULE_ORDER		(IPFW_NAT_MODEVENT_ORDER + 1)
1232#define	IPFW_NAT_VNET_ORDER		(IPFW_NAT_MODEVENT_ORDER + 2)
1233
1234DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY);
1235MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1);
1236MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3);
1237MODULE_VERSION(ipfw_nat, 1);
1238
1239SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
1240    ipfw_nat_init, NULL);
1241VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER,
1242    vnet_ipfw_nat_init, NULL);
1243
1244SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
1245    ipfw_nat_destroy, NULL);
1246VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL,
1247    IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL);
1248
1249/* end of file */
1250