xref: /freebsd/sys/netpfil/ipfw/ip_fw_nat.c (revision d6164b77f8b779cd7357387dcfcd3407f1457579)
13b3a8eb9SGleb Smirnoff /*-
23b3a8eb9SGleb Smirnoff  * Copyright (c) 2008 Paolo Pisati
33b3a8eb9SGleb Smirnoff  * All rights reserved.
43b3a8eb9SGleb Smirnoff  *
53b3a8eb9SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
63b3a8eb9SGleb Smirnoff  * modification, are permitted provided that the following conditions
73b3a8eb9SGleb Smirnoff  * are met:
83b3a8eb9SGleb Smirnoff  * 1. Redistributions of source code must retain the above copyright
93b3a8eb9SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer.
103b3a8eb9SGleb Smirnoff  * 2. Redistributions in binary form must reproduce the above copyright
113b3a8eb9SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer in the
123b3a8eb9SGleb Smirnoff  *    documentation and/or other materials provided with the distribution.
133b3a8eb9SGleb Smirnoff  *
143b3a8eb9SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
153b3a8eb9SGleb Smirnoff  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
163b3a8eb9SGleb Smirnoff  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
173b3a8eb9SGleb Smirnoff  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
183b3a8eb9SGleb Smirnoff  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
193b3a8eb9SGleb Smirnoff  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
203b3a8eb9SGleb Smirnoff  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
213b3a8eb9SGleb Smirnoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
223b3a8eb9SGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
233b3a8eb9SGleb Smirnoff  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
243b3a8eb9SGleb Smirnoff  * SUCH DAMAGE.
253b3a8eb9SGleb Smirnoff  */
263b3a8eb9SGleb Smirnoff 
273b3a8eb9SGleb Smirnoff #include <sys/cdefs.h>
283b3a8eb9SGleb Smirnoff __FBSDID("$FreeBSD$");
293b3a8eb9SGleb Smirnoff 
303b3a8eb9SGleb Smirnoff #include <sys/param.h>
313b3a8eb9SGleb Smirnoff #include <sys/systm.h>
323b3a8eb9SGleb Smirnoff #include <sys/eventhandler.h>
333b3a8eb9SGleb Smirnoff #include <sys/malloc.h>
3476039bc8SGleb Smirnoff #include <sys/mbuf.h>
353b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
363b3a8eb9SGleb Smirnoff #include <sys/lock.h>
373b3a8eb9SGleb Smirnoff #include <sys/module.h>
383b3a8eb9SGleb Smirnoff #include <sys/rwlock.h>
393b3a8eb9SGleb Smirnoff 
403b3a8eb9SGleb Smirnoff #include <netinet/libalias/alias.h>
413b3a8eb9SGleb Smirnoff #include <netinet/libalias/alias_local.h>
423b3a8eb9SGleb Smirnoff 
433b3a8eb9SGleb Smirnoff #include <net/if.h>
4476039bc8SGleb Smirnoff #include <net/if_var.h>
453b3a8eb9SGleb Smirnoff #include <netinet/in.h>
463b3a8eb9SGleb Smirnoff #include <netinet/ip.h>
473b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h>
483b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h>
493b3a8eb9SGleb Smirnoff #include <netinet/tcp.h>
503b3a8eb9SGleb Smirnoff #include <netinet/udp.h>
513b3a8eb9SGleb Smirnoff 
523b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h>
533b3a8eb9SGleb Smirnoff 
543b3a8eb9SGleb Smirnoff #include <machine/in_cksum.h>	/* XXX for in_cksum */
553b3a8eb9SGleb Smirnoff 
56*d6164b77SAlexander V. Chernikov struct cfg_spool {
57*d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_spool)   _next;          /* chain of spool instances */
58*d6164b77SAlexander V. Chernikov 	struct in_addr          addr;
59*d6164b77SAlexander V. Chernikov 	uint16_t		port;
60*d6164b77SAlexander V. Chernikov };
61*d6164b77SAlexander V. Chernikov 
62*d6164b77SAlexander V. Chernikov /* Nat redirect configuration. */
63*d6164b77SAlexander V. Chernikov struct cfg_redir {
64*d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_redir)	_next;	/* chain of redir instances */
65*d6164b77SAlexander V. Chernikov 	uint16_t		mode;	/* type of redirect mode */
66*d6164b77SAlexander V. Chernikov 	uint16_t		proto;	/* protocol: tcp/udp */
67*d6164b77SAlexander V. Chernikov 	struct in_addr		laddr;	/* local ip address */
68*d6164b77SAlexander V. Chernikov 	struct in_addr		paddr;	/* public ip address */
69*d6164b77SAlexander V. Chernikov 	struct in_addr		raddr;	/* remote ip address */
70*d6164b77SAlexander V. Chernikov 	uint16_t		lport;	/* local port */
71*d6164b77SAlexander V. Chernikov 	uint16_t		pport;	/* public port */
72*d6164b77SAlexander V. Chernikov 	uint16_t		rport;	/* remote port	*/
73*d6164b77SAlexander V. Chernikov 	uint16_t		pport_cnt;	/* number of public ports */
74*d6164b77SAlexander V. Chernikov 	uint16_t		rport_cnt;	/* number of remote ports */
75*d6164b77SAlexander V. Chernikov 	struct alias_link	**alink;
76*d6164b77SAlexander V. Chernikov 	u_int16_t		spool_cnt; /* num of entry in spool chain */
77*d6164b77SAlexander V. Chernikov 	/* chain of spool instances */
78*d6164b77SAlexander V. Chernikov 	LIST_HEAD(spool_chain, cfg_spool) spool_chain;
79*d6164b77SAlexander V. Chernikov };
80*d6164b77SAlexander V. Chernikov 
81*d6164b77SAlexander V. Chernikov /* Nat configuration data struct. */
82*d6164b77SAlexander V. Chernikov struct cfg_nat {
83*d6164b77SAlexander V. Chernikov 	/* chain of nat instances */
84*d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_nat)	_next;
85*d6164b77SAlexander V. Chernikov 	int			id;		/* nat id  */
86*d6164b77SAlexander V. Chernikov 	struct in_addr		ip;		/* nat ip address */
87*d6164b77SAlexander V. Chernikov 	struct libalias		*lib;		/* libalias instance */
88*d6164b77SAlexander V. Chernikov 	int			mode;		/* aliasing mode */
89*d6164b77SAlexander V. Chernikov 	int			redir_cnt; /* number of entry in spool chain */
90*d6164b77SAlexander V. Chernikov 	/* chain of redir instances */
91*d6164b77SAlexander V. Chernikov 	LIST_HEAD(redir_chain, cfg_redir) redir_chain;
92*d6164b77SAlexander V. Chernikov 	char			if_name[IF_NAMESIZE];	/* interface name */
93*d6164b77SAlexander V. Chernikov };
94*d6164b77SAlexander V. Chernikov 
958856400bSMikolaj Golub static eventhandler_tag ifaddr_event_tag;
963b3a8eb9SGleb Smirnoff 
973b3a8eb9SGleb Smirnoff static void
983b3a8eb9SGleb Smirnoff ifaddr_change(void *arg __unused, struct ifnet *ifp)
993b3a8eb9SGleb Smirnoff {
1003b3a8eb9SGleb Smirnoff 	struct cfg_nat *ptr;
1013b3a8eb9SGleb Smirnoff 	struct ifaddr *ifa;
1023b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain;
1033b3a8eb9SGleb Smirnoff 
1048856400bSMikolaj Golub 	KASSERT(curvnet == ifp->if_vnet,
1058856400bSMikolaj Golub 	    ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet));
1063b3a8eb9SGleb Smirnoff 	chain = &V_layer3_chain;
107c254a209SAlexander V. Chernikov 	IPFW_WLOCK(chain);
1083b3a8eb9SGleb Smirnoff 	/* Check every nat entry... */
1093b3a8eb9SGleb Smirnoff 	LIST_FOREACH(ptr, &chain->nat, _next) {
1103b3a8eb9SGleb Smirnoff 		/* ...using nic 'ifp->if_xname' as dynamic alias address. */
111c254a209SAlexander V. Chernikov 		if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0)
1123b3a8eb9SGleb Smirnoff 			continue;
1133b3a8eb9SGleb Smirnoff 		if_addr_rlock(ifp);
1143b3a8eb9SGleb Smirnoff 		TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
1153b3a8eb9SGleb Smirnoff 			if (ifa->ifa_addr == NULL)
1163b3a8eb9SGleb Smirnoff 				continue;
1173b3a8eb9SGleb Smirnoff 			if (ifa->ifa_addr->sa_family != AF_INET)
1183b3a8eb9SGleb Smirnoff 				continue;
1193b3a8eb9SGleb Smirnoff 			ptr->ip = ((struct sockaddr_in *)
1203b3a8eb9SGleb Smirnoff 			    (ifa->ifa_addr))->sin_addr;
1213b3a8eb9SGleb Smirnoff 			LibAliasSetAddress(ptr->lib, ptr->ip);
1223b3a8eb9SGleb Smirnoff 		}
1233b3a8eb9SGleb Smirnoff 		if_addr_runlock(ifp);
1243b3a8eb9SGleb Smirnoff 	}
125c254a209SAlexander V. Chernikov 	IPFW_WUNLOCK(chain);
1263b3a8eb9SGleb Smirnoff }
1273b3a8eb9SGleb Smirnoff 
1283b3a8eb9SGleb Smirnoff /*
1293b3a8eb9SGleb Smirnoff  * delete the pointers for nat entry ix, or all of them if ix < 0
1303b3a8eb9SGleb Smirnoff  */
1313b3a8eb9SGleb Smirnoff static void
1323b3a8eb9SGleb Smirnoff flush_nat_ptrs(struct ip_fw_chain *chain, const int ix)
1333b3a8eb9SGleb Smirnoff {
1343b3a8eb9SGleb Smirnoff 	int i;
1353b3a8eb9SGleb Smirnoff 	ipfw_insn_nat *cmd;
1363b3a8eb9SGleb Smirnoff 
1373b3a8eb9SGleb Smirnoff 	IPFW_WLOCK_ASSERT(chain);
1383b3a8eb9SGleb Smirnoff 	for (i = 0; i < chain->n_rules; i++) {
1393b3a8eb9SGleb Smirnoff 		cmd = (ipfw_insn_nat *)ACTION_PTR(chain->map[i]);
1403b3a8eb9SGleb Smirnoff 		/* XXX skip log and the like ? */
1413b3a8eb9SGleb Smirnoff 		if (cmd->o.opcode == O_NAT && cmd->nat != NULL &&
1423b3a8eb9SGleb Smirnoff 			    (ix < 0 || cmd->nat->id == ix))
1433b3a8eb9SGleb Smirnoff 			cmd->nat = NULL;
1443b3a8eb9SGleb Smirnoff 	}
1453b3a8eb9SGleb Smirnoff }
1463b3a8eb9SGleb Smirnoff 
1473b3a8eb9SGleb Smirnoff static void
1483b3a8eb9SGleb Smirnoff del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
1493b3a8eb9SGleb Smirnoff {
1503b3a8eb9SGleb Smirnoff 	struct cfg_redir *r, *tmp_r;
1513b3a8eb9SGleb Smirnoff 	struct cfg_spool *s, *tmp_s;
1523b3a8eb9SGleb Smirnoff 	int i, num;
1533b3a8eb9SGleb Smirnoff 
1543b3a8eb9SGleb Smirnoff 	LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
1553b3a8eb9SGleb Smirnoff 		num = 1; /* Number of alias_link to delete. */
1563b3a8eb9SGleb Smirnoff 		switch (r->mode) {
157*d6164b77SAlexander V. Chernikov 		case NAT44_REDIR_PORT:
1583b3a8eb9SGleb Smirnoff 			num = r->pport_cnt;
1593b3a8eb9SGleb Smirnoff 			/* FALLTHROUGH */
160*d6164b77SAlexander V. Chernikov 		case NAT44_REDIR_ADDR:
161*d6164b77SAlexander V. Chernikov 		case NAT44_REDIR_PROTO:
1623b3a8eb9SGleb Smirnoff 			/* Delete all libalias redirect entry. */
1633b3a8eb9SGleb Smirnoff 			for (i = 0; i < num; i++)
1643b3a8eb9SGleb Smirnoff 				LibAliasRedirectDelete(n->lib, r->alink[i]);
1653b3a8eb9SGleb Smirnoff 			/* Del spool cfg if any. */
1663b3a8eb9SGleb Smirnoff 			LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) {
1673b3a8eb9SGleb Smirnoff 				LIST_REMOVE(s, _next);
1683b3a8eb9SGleb Smirnoff 				free(s, M_IPFW);
1693b3a8eb9SGleb Smirnoff 			}
1703b3a8eb9SGleb Smirnoff 			free(r->alink, M_IPFW);
1713b3a8eb9SGleb Smirnoff 			LIST_REMOVE(r, _next);
1723b3a8eb9SGleb Smirnoff 			free(r, M_IPFW);
1733b3a8eb9SGleb Smirnoff 			break;
1743b3a8eb9SGleb Smirnoff 		default:
1753b3a8eb9SGleb Smirnoff 			printf("unknown redirect mode: %u\n", r->mode);
1763b3a8eb9SGleb Smirnoff 			/* XXX - panic?!?!? */
1773b3a8eb9SGleb Smirnoff 			break;
1783b3a8eb9SGleb Smirnoff 		}
1793b3a8eb9SGleb Smirnoff 	}
1803b3a8eb9SGleb Smirnoff }
1813b3a8eb9SGleb Smirnoff 
182*d6164b77SAlexander V. Chernikov static int
1833b3a8eb9SGleb Smirnoff add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
1843b3a8eb9SGleb Smirnoff {
185*d6164b77SAlexander V. Chernikov 	struct cfg_redir *r;
186*d6164b77SAlexander V. Chernikov 	struct cfg_spool *s;
187*d6164b77SAlexander V. Chernikov 	struct nat44_cfg_redir *ser_r;
188*d6164b77SAlexander V. Chernikov 	struct nat44_cfg_spool *ser_s;
189*d6164b77SAlexander V. Chernikov 
1903b3a8eb9SGleb Smirnoff 	int cnt, off, i;
1913b3a8eb9SGleb Smirnoff 
1923b3a8eb9SGleb Smirnoff 	for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
193*d6164b77SAlexander V. Chernikov 		ser_r = (struct nat44_cfg_redir *)&buf[off];
194*d6164b77SAlexander V. Chernikov 		r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO);
195*d6164b77SAlexander V. Chernikov 		r->mode = ser_r->mode;
196*d6164b77SAlexander V. Chernikov 		r->laddr = ser_r->laddr;
197*d6164b77SAlexander V. Chernikov 		r->paddr = ser_r->paddr;
198*d6164b77SAlexander V. Chernikov 		r->raddr = ser_r->raddr;
199*d6164b77SAlexander V. Chernikov 		r->lport = ser_r->lport;
200*d6164b77SAlexander V. Chernikov 		r->pport = ser_r->pport;
201*d6164b77SAlexander V. Chernikov 		r->rport = ser_r->rport;
202*d6164b77SAlexander V. Chernikov 		r->pport_cnt = ser_r->pport_cnt;
203*d6164b77SAlexander V. Chernikov 		r->rport_cnt = ser_r->rport_cnt;
204*d6164b77SAlexander V. Chernikov 		r->proto = ser_r->proto;
205*d6164b77SAlexander V. Chernikov 		r->spool_cnt = ser_r->spool_cnt;
206*d6164b77SAlexander V. Chernikov 		//memcpy(r, ser_r, SOF_REDIR);
2073b3a8eb9SGleb Smirnoff 		LIST_INIT(&r->spool_chain);
208*d6164b77SAlexander V. Chernikov 		off += sizeof(struct nat44_cfg_redir);
2093b3a8eb9SGleb Smirnoff 		r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
2103b3a8eb9SGleb Smirnoff 		    M_IPFW, M_WAITOK | M_ZERO);
2113b3a8eb9SGleb Smirnoff 		switch (r->mode) {
212*d6164b77SAlexander V. Chernikov 		case NAT44_REDIR_ADDR:
2133b3a8eb9SGleb Smirnoff 			r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
2143b3a8eb9SGleb Smirnoff 			    r->paddr);
2153b3a8eb9SGleb Smirnoff 			break;
216*d6164b77SAlexander V. Chernikov 		case NAT44_REDIR_PORT:
2173b3a8eb9SGleb Smirnoff 			for (i = 0 ; i < r->pport_cnt; i++) {
2183b3a8eb9SGleb Smirnoff 				/* If remotePort is all ports, set it to 0. */
2193b3a8eb9SGleb Smirnoff 				u_short remotePortCopy = r->rport + i;
2203b3a8eb9SGleb Smirnoff 				if (r->rport_cnt == 1 && r->rport == 0)
2213b3a8eb9SGleb Smirnoff 					remotePortCopy = 0;
2223b3a8eb9SGleb Smirnoff 				r->alink[i] = LibAliasRedirectPort(ptr->lib,
2233b3a8eb9SGleb Smirnoff 				    r->laddr, htons(r->lport + i), r->raddr,
2243b3a8eb9SGleb Smirnoff 				    htons(remotePortCopy), r->paddr,
2253b3a8eb9SGleb Smirnoff 				    htons(r->pport + i), r->proto);
2263b3a8eb9SGleb Smirnoff 				if (r->alink[i] == NULL) {
2273b3a8eb9SGleb Smirnoff 					r->alink[0] = NULL;
2283b3a8eb9SGleb Smirnoff 					break;
2293b3a8eb9SGleb Smirnoff 				}
2303b3a8eb9SGleb Smirnoff 			}
2313b3a8eb9SGleb Smirnoff 			break;
232*d6164b77SAlexander V. Chernikov 		case NAT44_REDIR_PROTO:
2333b3a8eb9SGleb Smirnoff 			r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
2343b3a8eb9SGleb Smirnoff 			    r->raddr, r->paddr, r->proto);
2353b3a8eb9SGleb Smirnoff 			break;
2363b3a8eb9SGleb Smirnoff 		default:
2373b3a8eb9SGleb Smirnoff 			printf("unknown redirect mode: %u\n", r->mode);
2383b3a8eb9SGleb Smirnoff 			break;
2393b3a8eb9SGleb Smirnoff 		}
240*d6164b77SAlexander V. Chernikov 		if (r->alink[0] == NULL) {
241*d6164b77SAlexander V. Chernikov 			printf("LibAliasRedirect* returned NULL\n");
242*d6164b77SAlexander V. Chernikov 			return (EINVAL);
243*d6164b77SAlexander V. Chernikov 		}
2443b3a8eb9SGleb Smirnoff 		/* LSNAT handling. */
2453b3a8eb9SGleb Smirnoff 		for (i = 0; i < r->spool_cnt; i++) {
246*d6164b77SAlexander V. Chernikov 			ser_s = (struct nat44_cfg_spool *)&buf[off];
247*d6164b77SAlexander V. Chernikov 			s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO);
248*d6164b77SAlexander V. Chernikov 			s->addr = ser_s->addr;
249*d6164b77SAlexander V. Chernikov 			s->port = ser_s->port;
2503b3a8eb9SGleb Smirnoff 			LibAliasAddServer(ptr->lib, r->alink[0],
2513b3a8eb9SGleb Smirnoff 			    s->addr, htons(s->port));
252*d6164b77SAlexander V. Chernikov 			off += sizeof(struct nat44_cfg_spool);
2533b3a8eb9SGleb Smirnoff 			/* Hook spool entry. */
2543b3a8eb9SGleb Smirnoff 			LIST_INSERT_HEAD(&r->spool_chain, s, _next);
2553b3a8eb9SGleb Smirnoff 		}
2563b3a8eb9SGleb Smirnoff 		/* And finally hook this redir entry. */
2573b3a8eb9SGleb Smirnoff 		LIST_INSERT_HEAD(&ptr->redir_chain, r, _next);
2583b3a8eb9SGleb Smirnoff 	}
259*d6164b77SAlexander V. Chernikov 
260*d6164b77SAlexander V. Chernikov 	return (0);
2613b3a8eb9SGleb Smirnoff }
2623b3a8eb9SGleb Smirnoff 
26310ab2de0SAlexander V. Chernikov /*
26410ab2de0SAlexander V. Chernikov  * ipfw_nat - perform mbuf header translation.
26510ab2de0SAlexander V. Chernikov  *
26610ab2de0SAlexander V. Chernikov  * Note V_layer3_chain has to be locked while calling ipfw_nat() in
26710ab2de0SAlexander V. Chernikov  * 'global' operation mode (t == NULL).
26810ab2de0SAlexander V. Chernikov  *
26910ab2de0SAlexander V. Chernikov  */
2703b3a8eb9SGleb Smirnoff static int
2713b3a8eb9SGleb Smirnoff ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
2723b3a8eb9SGleb Smirnoff {
2733b3a8eb9SGleb Smirnoff 	struct mbuf *mcl;
2743b3a8eb9SGleb Smirnoff 	struct ip *ip;
2753b3a8eb9SGleb Smirnoff 	/* XXX - libalias duct tape */
2763b3a8eb9SGleb Smirnoff 	int ldt, retval, found;
2773b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain;
2783b3a8eb9SGleb Smirnoff 	char *c;
2793b3a8eb9SGleb Smirnoff 
2803b3a8eb9SGleb Smirnoff 	ldt = 0;
2813b3a8eb9SGleb Smirnoff 	retval = 0;
2823b3a8eb9SGleb Smirnoff 	mcl = m_megapullup(m, m->m_pkthdr.len);
2833b3a8eb9SGleb Smirnoff 	if (mcl == NULL) {
2843b3a8eb9SGleb Smirnoff 		args->m = NULL;
2853b3a8eb9SGleb Smirnoff 		return (IP_FW_DENY);
2863b3a8eb9SGleb Smirnoff 	}
2873b3a8eb9SGleb Smirnoff 	ip = mtod(mcl, struct ip *);
2883b3a8eb9SGleb Smirnoff 
2893b3a8eb9SGleb Smirnoff 	/*
2903b3a8eb9SGleb Smirnoff 	 * XXX - Libalias checksum offload 'duct tape':
2913b3a8eb9SGleb Smirnoff 	 *
2923b3a8eb9SGleb Smirnoff 	 * locally generated packets have only pseudo-header checksum
2933b3a8eb9SGleb Smirnoff 	 * calculated and libalias will break it[1], so mark them for
2943b3a8eb9SGleb Smirnoff 	 * later fix.  Moreover there are cases when libalias modifies
2953b3a8eb9SGleb Smirnoff 	 * tcp packet data[2], mark them for later fix too.
2963b3a8eb9SGleb Smirnoff 	 *
2973b3a8eb9SGleb Smirnoff 	 * [1] libalias was never meant to run in kernel, so it does
2983b3a8eb9SGleb Smirnoff 	 * not have any knowledge about checksum offloading, and
2993b3a8eb9SGleb Smirnoff 	 * expects a packet with a full internet checksum.
3003b3a8eb9SGleb Smirnoff 	 * Unfortunately, packets generated locally will have just the
3013b3a8eb9SGleb Smirnoff 	 * pseudo header calculated, and when libalias tries to adjust
3023b3a8eb9SGleb Smirnoff 	 * the checksum it will actually compute a wrong value.
3033b3a8eb9SGleb Smirnoff 	 *
3043b3a8eb9SGleb Smirnoff 	 * [2] when libalias modifies tcp's data content, full TCP
3053b3a8eb9SGleb Smirnoff 	 * checksum has to be recomputed: the problem is that
3063b3a8eb9SGleb Smirnoff 	 * libalias does not have any idea about checksum offloading.
3073b3a8eb9SGleb Smirnoff 	 * To work around this, we do not do checksumming in LibAlias,
3083b3a8eb9SGleb Smirnoff 	 * but only mark the packets in th_x2 field. If we receive a
3093b3a8eb9SGleb Smirnoff 	 * marked packet, we calculate correct checksum for it
3103b3a8eb9SGleb Smirnoff 	 * aware of offloading.  Why such a terrible hack instead of
3113b3a8eb9SGleb Smirnoff 	 * recalculating checksum for each packet?
3123b3a8eb9SGleb Smirnoff 	 * Because the previous checksum was not checked!
3133b3a8eb9SGleb Smirnoff 	 * Recalculating checksums for EVERY packet will hide ALL
3143b3a8eb9SGleb Smirnoff 	 * transmission errors. Yes, marked packets still suffer from
3153b3a8eb9SGleb Smirnoff 	 * this problem. But, sigh, natd(8) has this problem, too.
3163b3a8eb9SGleb Smirnoff 	 *
3173b3a8eb9SGleb Smirnoff 	 * TODO: -make libalias mbuf aware (so
3183b3a8eb9SGleb Smirnoff 	 * it can handle delayed checksum and tso)
3193b3a8eb9SGleb Smirnoff 	 */
3203b3a8eb9SGleb Smirnoff 
3213b3a8eb9SGleb Smirnoff 	if (mcl->m_pkthdr.rcvif == NULL &&
3223b3a8eb9SGleb Smirnoff 	    mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA)
3233b3a8eb9SGleb Smirnoff 		ldt = 1;
3243b3a8eb9SGleb Smirnoff 
3253b3a8eb9SGleb Smirnoff 	c = mtod(mcl, char *);
3263b3a8eb9SGleb Smirnoff 
3273b3a8eb9SGleb Smirnoff 	/* Check if this is 'global' instance */
3283b3a8eb9SGleb Smirnoff 	if (t == NULL) {
3293b3a8eb9SGleb Smirnoff 		if (args->oif == NULL) {
3303b3a8eb9SGleb Smirnoff 			/* Wrong direction, skip processing */
3313b3a8eb9SGleb Smirnoff 			args->m = mcl;
3323b3a8eb9SGleb Smirnoff 			return (IP_FW_NAT);
3333b3a8eb9SGleb Smirnoff 		}
3343b3a8eb9SGleb Smirnoff 
3353b3a8eb9SGleb Smirnoff 		found = 0;
3363b3a8eb9SGleb Smirnoff 		chain = &V_layer3_chain;
3375d0cd926SAlexander V. Chernikov 		IPFW_RLOCK_ASSERT(chain);
3383b3a8eb9SGleb Smirnoff 		/* Check every nat entry... */
3393b3a8eb9SGleb Smirnoff 		LIST_FOREACH(t, &chain->nat, _next) {
3403b3a8eb9SGleb Smirnoff 			if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0)
3413b3a8eb9SGleb Smirnoff 				continue;
3423b3a8eb9SGleb Smirnoff 			retval = LibAliasOutTry(t->lib, c,
3433b3a8eb9SGleb Smirnoff 			    mcl->m_len + M_TRAILINGSPACE(mcl), 0);
3443b3a8eb9SGleb Smirnoff 			if (retval == PKT_ALIAS_OK) {
3453b3a8eb9SGleb Smirnoff 				/* Nat instance recognises state */
3463b3a8eb9SGleb Smirnoff 				found = 1;
3473b3a8eb9SGleb Smirnoff 				break;
3483b3a8eb9SGleb Smirnoff 			}
3493b3a8eb9SGleb Smirnoff 		}
3503b3a8eb9SGleb Smirnoff 		if (found != 1) {
3513b3a8eb9SGleb Smirnoff 			/* No instance found, return ignore */
3523b3a8eb9SGleb Smirnoff 			args->m = mcl;
3533b3a8eb9SGleb Smirnoff 			return (IP_FW_NAT);
3543b3a8eb9SGleb Smirnoff 		}
3553b3a8eb9SGleb Smirnoff 	} else {
3563b3a8eb9SGleb Smirnoff 		if (args->oif == NULL)
3573b3a8eb9SGleb Smirnoff 			retval = LibAliasIn(t->lib, c,
3583b3a8eb9SGleb Smirnoff 				mcl->m_len + M_TRAILINGSPACE(mcl));
3593b3a8eb9SGleb Smirnoff 		else
3603b3a8eb9SGleb Smirnoff 			retval = LibAliasOut(t->lib, c,
3613b3a8eb9SGleb Smirnoff 				mcl->m_len + M_TRAILINGSPACE(mcl));
3623b3a8eb9SGleb Smirnoff 	}
3633b3a8eb9SGleb Smirnoff 
3643b3a8eb9SGleb Smirnoff 	/*
3653b3a8eb9SGleb Smirnoff 	 * We drop packet when:
3663b3a8eb9SGleb Smirnoff 	 * 1. libalias returns PKT_ALIAS_ERROR;
3673b3a8eb9SGleb Smirnoff 	 * 2. For incoming packets:
3683b3a8eb9SGleb Smirnoff 	 *	a) for unresolved fragments;
3693b3a8eb9SGleb Smirnoff 	 *	b) libalias returns PKT_ALIAS_IGNORED and
3703b3a8eb9SGleb Smirnoff 	 *		PKT_ALIAS_DENY_INCOMING flag is set.
3713b3a8eb9SGleb Smirnoff 	 */
3723b3a8eb9SGleb Smirnoff 	if (retval == PKT_ALIAS_ERROR ||
3733b3a8eb9SGleb Smirnoff 	    (args->oif == NULL && (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
3743b3a8eb9SGleb Smirnoff 	    (retval == PKT_ALIAS_IGNORED &&
3753b3a8eb9SGleb Smirnoff 	    (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) {
3763b3a8eb9SGleb Smirnoff 		/* XXX - should i add some logging? */
3773b3a8eb9SGleb Smirnoff 		m_free(mcl);
3783b3a8eb9SGleb Smirnoff 		args->m = NULL;
3793b3a8eb9SGleb Smirnoff 		return (IP_FW_DENY);
3803b3a8eb9SGleb Smirnoff 	}
3813b3a8eb9SGleb Smirnoff 
3823b3a8eb9SGleb Smirnoff 	if (retval == PKT_ALIAS_RESPOND)
3833b3a8eb9SGleb Smirnoff 		mcl->m_flags |= M_SKIP_FIREWALL;
3843b3a8eb9SGleb Smirnoff 	mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
3853b3a8eb9SGleb Smirnoff 
3863b3a8eb9SGleb Smirnoff 	/*
3873b3a8eb9SGleb Smirnoff 	 * XXX - libalias checksum offload
3883b3a8eb9SGleb Smirnoff 	 * 'duct tape' (see above)
3893b3a8eb9SGleb Smirnoff 	 */
3903b3a8eb9SGleb Smirnoff 
3913b3a8eb9SGleb Smirnoff 	if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
3923b3a8eb9SGleb Smirnoff 	    ip->ip_p == IPPROTO_TCP) {
3933b3a8eb9SGleb Smirnoff 		struct tcphdr 	*th;
3943b3a8eb9SGleb Smirnoff 
3953b3a8eb9SGleb Smirnoff 		th = (struct tcphdr *)(ip + 1);
3963b3a8eb9SGleb Smirnoff 		if (th->th_x2)
3973b3a8eb9SGleb Smirnoff 			ldt = 1;
3983b3a8eb9SGleb Smirnoff 	}
3993b3a8eb9SGleb Smirnoff 
4003b3a8eb9SGleb Smirnoff 	if (ldt) {
4013b3a8eb9SGleb Smirnoff 		struct tcphdr 	*th;
4023b3a8eb9SGleb Smirnoff 		struct udphdr 	*uh;
40323e9c6dcSGleb Smirnoff 		uint16_t ip_len, cksum;
4043b3a8eb9SGleb Smirnoff 
40523e9c6dcSGleb Smirnoff 		ip_len = ntohs(ip->ip_len);
4063b3a8eb9SGleb Smirnoff 		cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
40723e9c6dcSGleb Smirnoff 		    htons(ip->ip_p + ip_len - (ip->ip_hl << 2)));
4083b3a8eb9SGleb Smirnoff 
4093b3a8eb9SGleb Smirnoff 		switch (ip->ip_p) {
4103b3a8eb9SGleb Smirnoff 		case IPPROTO_TCP:
4113b3a8eb9SGleb Smirnoff 			th = (struct tcphdr *)(ip + 1);
4123b3a8eb9SGleb Smirnoff 			/*
4133b3a8eb9SGleb Smirnoff 			 * Maybe it was set in
4143b3a8eb9SGleb Smirnoff 			 * libalias...
4153b3a8eb9SGleb Smirnoff 			 */
4163b3a8eb9SGleb Smirnoff 			th->th_x2 = 0;
4173b3a8eb9SGleb Smirnoff 			th->th_sum = cksum;
4183b3a8eb9SGleb Smirnoff 			mcl->m_pkthdr.csum_data =
4193b3a8eb9SGleb Smirnoff 			    offsetof(struct tcphdr, th_sum);
4203b3a8eb9SGleb Smirnoff 			break;
4213b3a8eb9SGleb Smirnoff 		case IPPROTO_UDP:
4223b3a8eb9SGleb Smirnoff 			uh = (struct udphdr *)(ip + 1);
4233b3a8eb9SGleb Smirnoff 			uh->uh_sum = cksum;
4243b3a8eb9SGleb Smirnoff 			mcl->m_pkthdr.csum_data =
4253b3a8eb9SGleb Smirnoff 			    offsetof(struct udphdr, uh_sum);
4263b3a8eb9SGleb Smirnoff 			break;
4273b3a8eb9SGleb Smirnoff 		}
4283b3a8eb9SGleb Smirnoff 		/* No hw checksum offloading: do it ourselves */
4293b3a8eb9SGleb Smirnoff 		if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) {
4303b3a8eb9SGleb Smirnoff 			in_delayed_cksum(mcl);
4313b3a8eb9SGleb Smirnoff 			mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
4323b3a8eb9SGleb Smirnoff 		}
4333b3a8eb9SGleb Smirnoff 	}
4343b3a8eb9SGleb Smirnoff 	args->m = mcl;
4353b3a8eb9SGleb Smirnoff 	return (IP_FW_NAT);
4363b3a8eb9SGleb Smirnoff }
4373b3a8eb9SGleb Smirnoff 
4383b3a8eb9SGleb Smirnoff static struct cfg_nat *
4393b3a8eb9SGleb Smirnoff lookup_nat(struct nat_list *l, int nat_id)
4403b3a8eb9SGleb Smirnoff {
4413b3a8eb9SGleb Smirnoff 	struct cfg_nat *res;
4423b3a8eb9SGleb Smirnoff 
4433b3a8eb9SGleb Smirnoff 	LIST_FOREACH(res, l, _next) {
4443b3a8eb9SGleb Smirnoff 		if (res->id == nat_id)
4453b3a8eb9SGleb Smirnoff 			break;
4463b3a8eb9SGleb Smirnoff 	}
4473b3a8eb9SGleb Smirnoff 	return res;
4483b3a8eb9SGleb Smirnoff }
4493b3a8eb9SGleb Smirnoff 
450*d6164b77SAlexander V. Chernikov static struct cfg_nat *
451*d6164b77SAlexander V. Chernikov lookup_nat_name(struct nat_list *l, char *name)
4523b3a8eb9SGleb Smirnoff {
453*d6164b77SAlexander V. Chernikov 	struct cfg_nat *res;
454*d6164b77SAlexander V. Chernikov 	int id;
455*d6164b77SAlexander V. Chernikov 	char *errptr;
4563b3a8eb9SGleb Smirnoff 
457*d6164b77SAlexander V. Chernikov 	id = strtol(name, &errptr, 10);
458*d6164b77SAlexander V. Chernikov 	if (id == 0 || *errptr != '\0')
459*d6164b77SAlexander V. Chernikov 		return (NULL);
4603b3a8eb9SGleb Smirnoff 
461*d6164b77SAlexander V. Chernikov 	LIST_FOREACH(res, l, _next) {
462*d6164b77SAlexander V. Chernikov 		if (res->id == id)
463*d6164b77SAlexander V. Chernikov 			break;
4643b3a8eb9SGleb Smirnoff 	}
465*d6164b77SAlexander V. Chernikov 	return (res);
466*d6164b77SAlexander V. Chernikov }
467*d6164b77SAlexander V. Chernikov 
468*d6164b77SAlexander V. Chernikov /* IP_FW3 configuration routines */
469*d6164b77SAlexander V. Chernikov 
470*d6164b77SAlexander V. Chernikov static void
471*d6164b77SAlexander V. Chernikov nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg)
472*d6164b77SAlexander V. Chernikov {
473*d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr, *tcfg;
474*d6164b77SAlexander V. Chernikov 	int gencnt;
4753b3a8eb9SGleb Smirnoff 
4763b3a8eb9SGleb Smirnoff 	/*
4773b3a8eb9SGleb Smirnoff 	 * Find/create nat rule.
4783b3a8eb9SGleb Smirnoff 	 */
479*d6164b77SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
4803b3a8eb9SGleb Smirnoff 	gencnt = chain->gencnt;
481*d6164b77SAlexander V. Chernikov 	ptr = lookup_nat_name(&chain->nat, ucfg->name);
4823b3a8eb9SGleb Smirnoff 	if (ptr == NULL) {
483*d6164b77SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
4843b3a8eb9SGleb Smirnoff 		/* New rule: allocate and init new instance. */
4853b3a8eb9SGleb Smirnoff 		ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO);
4863b3a8eb9SGleb Smirnoff 		ptr->lib = LibAliasInit(NULL);
4873b3a8eb9SGleb Smirnoff 		LIST_INIT(&ptr->redir_chain);
4883b3a8eb9SGleb Smirnoff 	} else {
4893b3a8eb9SGleb Smirnoff 		/* Entry already present: temporarily unhook it. */
490*d6164b77SAlexander V. Chernikov 		IPFW_WLOCK(chain);
4913b3a8eb9SGleb Smirnoff 		LIST_REMOVE(ptr, _next);
492*d6164b77SAlexander V. Chernikov 		flush_nat_ptrs(chain, ptr->id);
4933b3a8eb9SGleb Smirnoff 		IPFW_WUNLOCK(chain);
494*d6164b77SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
4953b3a8eb9SGleb Smirnoff 	}
4963b3a8eb9SGleb Smirnoff 
4973b3a8eb9SGleb Smirnoff 	/*
498*d6164b77SAlexander V. Chernikov 	 * Basic nat (re)configuration.
4993b3a8eb9SGleb Smirnoff 	 */
500*d6164b77SAlexander V. Chernikov 	ptr->id = strtol(ucfg->name, NULL, 10);
5013b3a8eb9SGleb Smirnoff 	/*
5023b3a8eb9SGleb Smirnoff 	 * XXX - what if this rule doesn't nat any ip and just
5033b3a8eb9SGleb Smirnoff 	 * redirect?
5043b3a8eb9SGleb Smirnoff 	 * do we set aliasaddress to 0.0.0.0?
5053b3a8eb9SGleb Smirnoff 	 */
506*d6164b77SAlexander V. Chernikov 	ptr->ip = ucfg->ip;
507*d6164b77SAlexander V. Chernikov 	ptr->redir_cnt = ucfg->redir_cnt;
508*d6164b77SAlexander V. Chernikov 	ptr->mode = ucfg->mode;
509*d6164b77SAlexander V. Chernikov 	strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name));
510*d6164b77SAlexander V. Chernikov 	LibAliasSetMode(ptr->lib, ptr->mode, ~0);
5113b3a8eb9SGleb Smirnoff 	LibAliasSetAddress(ptr->lib, ptr->ip);
5123b3a8eb9SGleb Smirnoff 
5133b3a8eb9SGleb Smirnoff 	/*
5143b3a8eb9SGleb Smirnoff 	 * Redir and LSNAT configuration.
5153b3a8eb9SGleb Smirnoff 	 */
5163b3a8eb9SGleb Smirnoff 	/* Delete old cfgs. */
5173b3a8eb9SGleb Smirnoff 	del_redir_spool_cfg(ptr, &ptr->redir_chain);
5183b3a8eb9SGleb Smirnoff 	/* Add new entries. */
519*d6164b77SAlexander V. Chernikov 	add_redir_spool_cfg((char *)(ucfg + 1), ptr);
520*d6164b77SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
5213b3a8eb9SGleb Smirnoff 
5223b3a8eb9SGleb Smirnoff 	/* Extra check to avoid race with another ipfw_nat_cfg() */
523*d6164b77SAlexander V. Chernikov 	tcfg = NULL;
524*d6164b77SAlexander V. Chernikov 	if (gencnt != chain->gencnt)
525*d6164b77SAlexander V. Chernikov 	    tcfg = lookup_nat_name(&chain->nat, ucfg->name);
526*d6164b77SAlexander V. Chernikov 	IPFW_WLOCK(chain);
527*d6164b77SAlexander V. Chernikov 	if (tcfg != NULL)
528*d6164b77SAlexander V. Chernikov 		LIST_REMOVE(tcfg, _next);
5293b3a8eb9SGleb Smirnoff 	LIST_INSERT_HEAD(&chain->nat, ptr, _next);
5303b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(chain);
531*d6164b77SAlexander V. Chernikov 	chain->gencnt++;
532*d6164b77SAlexander V. Chernikov 
533*d6164b77SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
534*d6164b77SAlexander V. Chernikov 
535*d6164b77SAlexander V. Chernikov 	if (tcfg != NULL)
536*d6164b77SAlexander V. Chernikov 		free(tcfg, M_IPFW);
537*d6164b77SAlexander V. Chernikov }
538*d6164b77SAlexander V. Chernikov 
539*d6164b77SAlexander V. Chernikov /*
540*d6164b77SAlexander V. Chernikov  * Creates/configure nat44 instance
541*d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
542*d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
543*d6164b77SAlexander V. Chernikov  *
544*d6164b77SAlexander V. Chernikov  * Returns 0 on success
545*d6164b77SAlexander V. Chernikov  */
546*d6164b77SAlexander V. Chernikov static int
547*d6164b77SAlexander V. Chernikov nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
548*d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
549*d6164b77SAlexander V. Chernikov {
550*d6164b77SAlexander V. Chernikov 	ipfw_obj_header *oh;
551*d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
552*d6164b77SAlexander V. Chernikov 	int id;
553*d6164b77SAlexander V. Chernikov 	size_t read;
554*d6164b77SAlexander V. Chernikov 	char *errptr;
555*d6164b77SAlexander V. Chernikov 
556*d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
557*d6164b77SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg)))
558*d6164b77SAlexander V. Chernikov 		return (EINVAL);
559*d6164b77SAlexander V. Chernikov 
560*d6164b77SAlexander V. Chernikov 	oh = (ipfw_obj_header *)sd->kbuf;
561*d6164b77SAlexander V. Chernikov 
562*d6164b77SAlexander V. Chernikov 	/* Basic length checks for TLVs */
563*d6164b77SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
564*d6164b77SAlexander V. Chernikov 		return (EINVAL);
565*d6164b77SAlexander V. Chernikov 
566*d6164b77SAlexander V. Chernikov 	ucfg = (struct nat44_cfg_nat *)(oh + 1);
567*d6164b77SAlexander V. Chernikov 
568*d6164b77SAlexander V. Chernikov 	/* Check if name is properly terminated and looks like number */
569*d6164b77SAlexander V. Chernikov 	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
570*d6164b77SAlexander V. Chernikov 		return (EINVAL);
571*d6164b77SAlexander V. Chernikov 	id = strtol(ucfg->name, &errptr, 10);
572*d6164b77SAlexander V. Chernikov 	if (id == 0 || *errptr != '\0')
573*d6164b77SAlexander V. Chernikov 		return (EINVAL);
574*d6164b77SAlexander V. Chernikov 
575*d6164b77SAlexander V. Chernikov 	read = sizeof(*oh) + sizeof(*ucfg);
576*d6164b77SAlexander V. Chernikov 	/* Check number of redirs */
577*d6164b77SAlexander V. Chernikov 	if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir))
578*d6164b77SAlexander V. Chernikov 		return (EINVAL);
579*d6164b77SAlexander V. Chernikov 
580*d6164b77SAlexander V. Chernikov 	nat44_config(chain, ucfg);
581*d6164b77SAlexander V. Chernikov 	return (0);
582*d6164b77SAlexander V. Chernikov }
583*d6164b77SAlexander V. Chernikov 
584*d6164b77SAlexander V. Chernikov /*
585*d6164b77SAlexander V. Chernikov  * Destroys given nat instances.
586*d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
587*d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_header ]
588*d6164b77SAlexander V. Chernikov  *
589*d6164b77SAlexander V. Chernikov  * Returns 0 on success
590*d6164b77SAlexander V. Chernikov  */
591*d6164b77SAlexander V. Chernikov static int
592*d6164b77SAlexander V. Chernikov nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
593*d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
594*d6164b77SAlexander V. Chernikov {
595*d6164b77SAlexander V. Chernikov 	ipfw_obj_header *oh;
596*d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr;
597*d6164b77SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
598*d6164b77SAlexander V. Chernikov 
599*d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
600*d6164b77SAlexander V. Chernikov 	if (sd->valsize < sizeof(*oh))
601*d6164b77SAlexander V. Chernikov 		return (EINVAL);
602*d6164b77SAlexander V. Chernikov 
603*d6164b77SAlexander V. Chernikov 	oh = (ipfw_obj_header *)sd->kbuf;
604*d6164b77SAlexander V. Chernikov 
605*d6164b77SAlexander V. Chernikov 	/* Basic length checks for TLVs */
606*d6164b77SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
607*d6164b77SAlexander V. Chernikov 		return (EINVAL);
608*d6164b77SAlexander V. Chernikov 
609*d6164b77SAlexander V. Chernikov 	ntlv = &oh->ntlv;
610*d6164b77SAlexander V. Chernikov 	/* Check if name is properly terminated */
611*d6164b77SAlexander V. Chernikov 	if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name))
612*d6164b77SAlexander V. Chernikov 		return (EINVAL);
613*d6164b77SAlexander V. Chernikov 
614*d6164b77SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
615*d6164b77SAlexander V. Chernikov 	ptr = lookup_nat_name(&chain->nat, ntlv->name);
616*d6164b77SAlexander V. Chernikov 	if (ptr == NULL) {
617*d6164b77SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
618*d6164b77SAlexander V. Chernikov 		return (ESRCH);
619*d6164b77SAlexander V. Chernikov 	}
620*d6164b77SAlexander V. Chernikov 	IPFW_WLOCK(chain);
621*d6164b77SAlexander V. Chernikov 	LIST_REMOVE(ptr, _next);
622*d6164b77SAlexander V. Chernikov 	flush_nat_ptrs(chain, ptr->id);
623*d6164b77SAlexander V. Chernikov 	IPFW_WUNLOCK(chain);
624*d6164b77SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
625*d6164b77SAlexander V. Chernikov 
626*d6164b77SAlexander V. Chernikov 	del_redir_spool_cfg(ptr, &ptr->redir_chain);
627*d6164b77SAlexander V. Chernikov 	LibAliasUninit(ptr->lib);
628*d6164b77SAlexander V. Chernikov 	free(ptr, M_IPFW);
629*d6164b77SAlexander V. Chernikov 
630*d6164b77SAlexander V. Chernikov 	return (0);
631*d6164b77SAlexander V. Chernikov }
632*d6164b77SAlexander V. Chernikov 
633*d6164b77SAlexander V. Chernikov static void
634*d6164b77SAlexander V. Chernikov export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg)
635*d6164b77SAlexander V. Chernikov {
636*d6164b77SAlexander V. Chernikov 
637*d6164b77SAlexander V. Chernikov 	snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id);
638*d6164b77SAlexander V. Chernikov 	ucfg->ip = ptr->ip;
639*d6164b77SAlexander V. Chernikov 	ucfg->redir_cnt = ptr->redir_cnt;
640*d6164b77SAlexander V. Chernikov 	ucfg->mode = ptr->mode;
641*d6164b77SAlexander V. Chernikov 	strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name));
642*d6164b77SAlexander V. Chernikov }
643*d6164b77SAlexander V. Chernikov 
644*d6164b77SAlexander V. Chernikov /*
645*d6164b77SAlexander V. Chernikov  * Gets config for given nat instance
646*d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
647*d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
648*d6164b77SAlexander V. Chernikov  *
649*d6164b77SAlexander V. Chernikov  * Returns 0 on success
650*d6164b77SAlexander V. Chernikov  */
651*d6164b77SAlexander V. Chernikov static int
652*d6164b77SAlexander V. Chernikov nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
653*d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
654*d6164b77SAlexander V. Chernikov {
655*d6164b77SAlexander V. Chernikov 	ipfw_obj_header *oh;
656*d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
657*d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr;
658*d6164b77SAlexander V. Chernikov 	struct cfg_redir *r;
659*d6164b77SAlexander V. Chernikov 	struct cfg_spool *s;
660*d6164b77SAlexander V. Chernikov 	struct nat44_cfg_redir *ser_r;
661*d6164b77SAlexander V. Chernikov 	struct nat44_cfg_spool *ser_s;
662*d6164b77SAlexander V. Chernikov 	size_t sz;
663*d6164b77SAlexander V. Chernikov 
664*d6164b77SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(*ucfg);
665*d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
666*d6164b77SAlexander V. Chernikov 	if (sd->valsize < sz)
667*d6164b77SAlexander V. Chernikov 		return (EINVAL);
668*d6164b77SAlexander V. Chernikov 
669*d6164b77SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
670*d6164b77SAlexander V. Chernikov 
671*d6164b77SAlexander V. Chernikov 	/* Basic length checks for TLVs */
672*d6164b77SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
673*d6164b77SAlexander V. Chernikov 		return (EINVAL);
674*d6164b77SAlexander V. Chernikov 
675*d6164b77SAlexander V. Chernikov 	ucfg = (struct nat44_cfg_nat *)(oh + 1);
676*d6164b77SAlexander V. Chernikov 
677*d6164b77SAlexander V. Chernikov 	/* Check if name is properly terminated */
678*d6164b77SAlexander V. Chernikov 	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
679*d6164b77SAlexander V. Chernikov 		return (EINVAL);
680*d6164b77SAlexander V. Chernikov 
681*d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
682*d6164b77SAlexander V. Chernikov 	ptr = lookup_nat_name(&chain->nat, ucfg->name);
683*d6164b77SAlexander V. Chernikov 	if (ptr == NULL) {
684*d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
685*d6164b77SAlexander V. Chernikov 		return (ESRCH);
686*d6164b77SAlexander V. Chernikov 	}
687*d6164b77SAlexander V. Chernikov 
688*d6164b77SAlexander V. Chernikov 	export_nat_cfg(ptr, ucfg);
689*d6164b77SAlexander V. Chernikov 
690*d6164b77SAlexander V. Chernikov 	/* Estimate memory amount */
691*d6164b77SAlexander V. Chernikov 	sz = sizeof(struct nat44_cfg_nat);
692*d6164b77SAlexander V. Chernikov 	LIST_FOREACH(r, &ptr->redir_chain, _next) {
693*d6164b77SAlexander V. Chernikov 		sz += sizeof(struct nat44_cfg_redir);
694*d6164b77SAlexander V. Chernikov 		LIST_FOREACH(s, &r->spool_chain, _next)
695*d6164b77SAlexander V. Chernikov 			sz += sizeof(struct nat44_cfg_spool);
696*d6164b77SAlexander V. Chernikov 	}
697*d6164b77SAlexander V. Chernikov 
698*d6164b77SAlexander V. Chernikov 	ucfg->size = sz;
699*d6164b77SAlexander V. Chernikov 	if (sd->valsize < sz + sizeof(*oh)) {
700*d6164b77SAlexander V. Chernikov 
701*d6164b77SAlexander V. Chernikov 		/*
702*d6164b77SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
703*d6164b77SAlexander V. Chernikov 		 * WE've already filled in @ucfg structure with
704*d6164b77SAlexander V. Chernikov 		 * relevant info including size, so we
705*d6164b77SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
706*d6164b77SAlexander V. Chernikov 		 */
707*d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
708*d6164b77SAlexander V. Chernikov 		return (ENOMEM);
709*d6164b77SAlexander V. Chernikov 	}
710*d6164b77SAlexander V. Chernikov 
711*d6164b77SAlexander V. Chernikov 	/* Size OK, let's copy data */
712*d6164b77SAlexander V. Chernikov 	LIST_FOREACH(r, &ptr->redir_chain, _next) {
713*d6164b77SAlexander V. Chernikov 		ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd,
714*d6164b77SAlexander V. Chernikov 		    sizeof(*ser_r));
715*d6164b77SAlexander V. Chernikov 		ser_r->mode = r->mode;
716*d6164b77SAlexander V. Chernikov 		ser_r->laddr = r->laddr;
717*d6164b77SAlexander V. Chernikov 		ser_r->paddr = r->paddr;
718*d6164b77SAlexander V. Chernikov 		ser_r->raddr = r->raddr;
719*d6164b77SAlexander V. Chernikov 		ser_r->lport = r->lport;
720*d6164b77SAlexander V. Chernikov 		ser_r->pport = r->pport;
721*d6164b77SAlexander V. Chernikov 		ser_r->rport = r->rport;
722*d6164b77SAlexander V. Chernikov 		ser_r->pport_cnt = r->pport_cnt;
723*d6164b77SAlexander V. Chernikov 		ser_r->rport_cnt = r->rport_cnt;
724*d6164b77SAlexander V. Chernikov 		ser_r->proto = r->proto;
725*d6164b77SAlexander V. Chernikov 		ser_r->spool_cnt = r->spool_cnt;
726*d6164b77SAlexander V. Chernikov 
727*d6164b77SAlexander V. Chernikov 		LIST_FOREACH(s, &r->spool_chain, _next) {
728*d6164b77SAlexander V. Chernikov 			ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space(
729*d6164b77SAlexander V. Chernikov 			    sd, sizeof(*ser_s));
730*d6164b77SAlexander V. Chernikov 
731*d6164b77SAlexander V. Chernikov 			ser_s->addr = s->addr;
732*d6164b77SAlexander V. Chernikov 			ser_s->port = s->port;
733*d6164b77SAlexander V. Chernikov 		}
734*d6164b77SAlexander V. Chernikov 	}
735*d6164b77SAlexander V. Chernikov 
736*d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
737*d6164b77SAlexander V. Chernikov 
738*d6164b77SAlexander V. Chernikov 	return (0);
739*d6164b77SAlexander V. Chernikov }
740*d6164b77SAlexander V. Chernikov 
741*d6164b77SAlexander V. Chernikov /*
742*d6164b77SAlexander V. Chernikov  * Lists all nat44 instances currently available in kernel.
743*d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
744*d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ]
745*d6164b77SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ]
746*d6164b77SAlexander V. Chernikov  *
747*d6164b77SAlexander V. Chernikov  * Returns 0 on success
748*d6164b77SAlexander V. Chernikov  */
749*d6164b77SAlexander V. Chernikov static int
750*d6164b77SAlexander V. Chernikov nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
751*d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
752*d6164b77SAlexander V. Chernikov {
753*d6164b77SAlexander V. Chernikov 	ipfw_obj_lheader *olh;
754*d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
755*d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr;
756*d6164b77SAlexander V. Chernikov 	int nat_count;
757*d6164b77SAlexander V. Chernikov 
758*d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
759*d6164b77SAlexander V. Chernikov 	if (sd->valsize < sizeof(ipfw_obj_lheader))
760*d6164b77SAlexander V. Chernikov 		return (EINVAL);
761*d6164b77SAlexander V. Chernikov 
762*d6164b77SAlexander V. Chernikov 	olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
763*d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
764*d6164b77SAlexander V. Chernikov 	nat_count = 0;
765*d6164b77SAlexander V. Chernikov 	LIST_FOREACH(ptr, &chain->nat, _next)
766*d6164b77SAlexander V. Chernikov 		nat_count++;
767*d6164b77SAlexander V. Chernikov 
768*d6164b77SAlexander V. Chernikov 	olh->count = nat_count;
769*d6164b77SAlexander V. Chernikov 	olh->objsize = sizeof(struct nat44_cfg_nat);
770*d6164b77SAlexander V. Chernikov 	olh->size = sizeof(*olh) + olh->count * olh->objsize;
771*d6164b77SAlexander V. Chernikov 
772*d6164b77SAlexander V. Chernikov 	if (sd->valsize < olh->size) {
773*d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
774*d6164b77SAlexander V. Chernikov 		return (ENOMEM);
775*d6164b77SAlexander V. Chernikov 	}
776*d6164b77SAlexander V. Chernikov 
777*d6164b77SAlexander V. Chernikov 	LIST_FOREACH(ptr, &chain->nat, _next) {
778*d6164b77SAlexander V. Chernikov 		ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd,
779*d6164b77SAlexander V. Chernikov 		    sizeof(*ucfg));
780*d6164b77SAlexander V. Chernikov 		export_nat_cfg(ptr, ucfg);
781*d6164b77SAlexander V. Chernikov 	}
782*d6164b77SAlexander V. Chernikov 
783*d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
784*d6164b77SAlexander V. Chernikov 
785*d6164b77SAlexander V. Chernikov 	return (0);
786*d6164b77SAlexander V. Chernikov }
787*d6164b77SAlexander V. Chernikov 
788*d6164b77SAlexander V. Chernikov /*
789*d6164b77SAlexander V. Chernikov  * Gets log for given nat instance
790*d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
791*d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_header nat44_cfg_nat ]
792*d6164b77SAlexander V. Chernikov  * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ]
793*d6164b77SAlexander V. Chernikov  *
794*d6164b77SAlexander V. Chernikov  * Returns 0 on success
795*d6164b77SAlexander V. Chernikov  */
796*d6164b77SAlexander V. Chernikov static int
797*d6164b77SAlexander V. Chernikov nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
798*d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
799*d6164b77SAlexander V. Chernikov {
800*d6164b77SAlexander V. Chernikov 	ipfw_obj_header *oh;
801*d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
802*d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr;
803*d6164b77SAlexander V. Chernikov 	void *pbuf;
804*d6164b77SAlexander V. Chernikov 	size_t sz;
805*d6164b77SAlexander V. Chernikov 
806*d6164b77SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(*ucfg);
807*d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
808*d6164b77SAlexander V. Chernikov 	if (sd->valsize < sz)
809*d6164b77SAlexander V. Chernikov 		return (EINVAL);
810*d6164b77SAlexander V. Chernikov 
811*d6164b77SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
812*d6164b77SAlexander V. Chernikov 
813*d6164b77SAlexander V. Chernikov 	/* Basic length checks for TLVs */
814*d6164b77SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
815*d6164b77SAlexander V. Chernikov 		return (EINVAL);
816*d6164b77SAlexander V. Chernikov 
817*d6164b77SAlexander V. Chernikov 	ucfg = (struct nat44_cfg_nat *)(oh + 1);
818*d6164b77SAlexander V. Chernikov 
819*d6164b77SAlexander V. Chernikov 	/* Check if name is properly terminated */
820*d6164b77SAlexander V. Chernikov 	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
821*d6164b77SAlexander V. Chernikov 		return (EINVAL);
822*d6164b77SAlexander V. Chernikov 
823*d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
824*d6164b77SAlexander V. Chernikov 	ptr = lookup_nat_name(&chain->nat, ucfg->name);
825*d6164b77SAlexander V. Chernikov 	if (ptr == NULL) {
826*d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
827*d6164b77SAlexander V. Chernikov 		return (ESRCH);
828*d6164b77SAlexander V. Chernikov 	}
829*d6164b77SAlexander V. Chernikov 
830*d6164b77SAlexander V. Chernikov 	if (ptr->lib->logDesc == NULL) {
831*d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
832*d6164b77SAlexander V. Chernikov 		return (ENOENT);
833*d6164b77SAlexander V. Chernikov 	}
834*d6164b77SAlexander V. Chernikov 
835*d6164b77SAlexander V. Chernikov 	export_nat_cfg(ptr, ucfg);
836*d6164b77SAlexander V. Chernikov 
837*d6164b77SAlexander V. Chernikov 	/* Estimate memory amount */
838*d6164b77SAlexander V. Chernikov 	ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE;
839*d6164b77SAlexander V. Chernikov 	if (sd->valsize < sz + sizeof(*oh)) {
840*d6164b77SAlexander V. Chernikov 
841*d6164b77SAlexander V. Chernikov 		/*
842*d6164b77SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
843*d6164b77SAlexander V. Chernikov 		 * WE've already filled in @ucfg structure with
844*d6164b77SAlexander V. Chernikov 		 * relevant info including size, so we
845*d6164b77SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
846*d6164b77SAlexander V. Chernikov 		 */
847*d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
848*d6164b77SAlexander V. Chernikov 		return (ENOMEM);
849*d6164b77SAlexander V. Chernikov 	}
850*d6164b77SAlexander V. Chernikov 
851*d6164b77SAlexander V. Chernikov 	pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE);
852*d6164b77SAlexander V. Chernikov 	memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE);
853*d6164b77SAlexander V. Chernikov 
854*d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
855*d6164b77SAlexander V. Chernikov 
856*d6164b77SAlexander V. Chernikov 	return (0);
857*d6164b77SAlexander V. Chernikov }
858*d6164b77SAlexander V. Chernikov 
859*d6164b77SAlexander V. Chernikov static struct ipfw_sopt_handler	scodes[] = {
860*d6164b77SAlexander V. Chernikov 	{ IP_FW_NAT44_XCONFIG,	0,	HDIR_SET,	nat44_cfg },
861*d6164b77SAlexander V. Chernikov 	{ IP_FW_NAT44_DESTROY,	0,	HDIR_SET,	nat44_destroy },
862*d6164b77SAlexander V. Chernikov 	{ IP_FW_NAT44_XGETCONFIG,	0,	HDIR_GET,	nat44_get_cfg },
863*d6164b77SAlexander V. Chernikov 	{ IP_FW_NAT44_LIST_NAT,	0,	HDIR_GET,	nat44_list_nat },
864*d6164b77SAlexander V. Chernikov 	{ IP_FW_NAT44_XGETLOG,	0,	HDIR_GET,	nat44_get_log },
865*d6164b77SAlexander V. Chernikov };
866*d6164b77SAlexander V. Chernikov 
867*d6164b77SAlexander V. Chernikov 
868*d6164b77SAlexander V. Chernikov /*
869*d6164b77SAlexander V. Chernikov  * Legacy configuration routines
870*d6164b77SAlexander V. Chernikov  */
871*d6164b77SAlexander V. Chernikov 
872*d6164b77SAlexander V. Chernikov struct cfg_spool_legacy {
873*d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_spool_legacy)	_next;
874*d6164b77SAlexander V. Chernikov 	struct in_addr			addr;
875*d6164b77SAlexander V. Chernikov 	u_short				port;
876*d6164b77SAlexander V. Chernikov };
877*d6164b77SAlexander V. Chernikov 
878*d6164b77SAlexander V. Chernikov struct cfg_redir_legacy {
879*d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_redir)   _next;
880*d6164b77SAlexander V. Chernikov 	u_int16_t               mode;
881*d6164b77SAlexander V. Chernikov 	struct in_addr	        laddr;
882*d6164b77SAlexander V. Chernikov 	struct in_addr	        paddr;
883*d6164b77SAlexander V. Chernikov 	struct in_addr	        raddr;
884*d6164b77SAlexander V. Chernikov 	u_short                 lport;
885*d6164b77SAlexander V. Chernikov 	u_short                 pport;
886*d6164b77SAlexander V. Chernikov 	u_short                 rport;
887*d6164b77SAlexander V. Chernikov 	u_short                 pport_cnt;
888*d6164b77SAlexander V. Chernikov 	u_short                 rport_cnt;
889*d6164b77SAlexander V. Chernikov 	int                     proto;
890*d6164b77SAlexander V. Chernikov 	struct alias_link       **alink;
891*d6164b77SAlexander V. Chernikov 	u_int16_t               spool_cnt;
892*d6164b77SAlexander V. Chernikov 	LIST_HEAD(, cfg_spool_legacy) spool_chain;
893*d6164b77SAlexander V. Chernikov };
894*d6164b77SAlexander V. Chernikov 
895*d6164b77SAlexander V. Chernikov struct cfg_nat_legacy {
896*d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_nat_legacy)	_next;
897*d6164b77SAlexander V. Chernikov 	int				id;
898*d6164b77SAlexander V. Chernikov 	struct in_addr			ip;
899*d6164b77SAlexander V. Chernikov 	char				if_name[IF_NAMESIZE];
900*d6164b77SAlexander V. Chernikov 	int				mode;
901*d6164b77SAlexander V. Chernikov 	struct libalias			*lib;
902*d6164b77SAlexander V. Chernikov 	int				redir_cnt;
903*d6164b77SAlexander V. Chernikov 	LIST_HEAD(, cfg_redir_legacy)	redir_chain;
904*d6164b77SAlexander V. Chernikov };
905*d6164b77SAlexander V. Chernikov 
906*d6164b77SAlexander V. Chernikov static int
907*d6164b77SAlexander V. Chernikov ipfw_nat_cfg(struct sockopt *sopt)
908*d6164b77SAlexander V. Chernikov {
909*d6164b77SAlexander V. Chernikov 	struct cfg_nat_legacy *cfg;
910*d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
911*d6164b77SAlexander V. Chernikov 	struct cfg_redir_legacy *rdir;
912*d6164b77SAlexander V. Chernikov 	struct nat44_cfg_redir *urdir;
913*d6164b77SAlexander V. Chernikov 	char *buf;
914*d6164b77SAlexander V. Chernikov 	size_t len, len2;
915*d6164b77SAlexander V. Chernikov 	int error, i;
916*d6164b77SAlexander V. Chernikov 
917*d6164b77SAlexander V. Chernikov 	len = sopt->sopt_valsize;
918*d6164b77SAlexander V. Chernikov 	len2 = len + 128;
919*d6164b77SAlexander V. Chernikov 
920*d6164b77SAlexander V. Chernikov 	/*
921*d6164b77SAlexander V. Chernikov 	 * Allocate 2x buffer to store converted structures.
922*d6164b77SAlexander V. Chernikov 	 * new redir_cfg has shrinked, so we're sure that
923*d6164b77SAlexander V. Chernikov 	 * new buffer size is enough.
924*d6164b77SAlexander V. Chernikov 	 */
925*d6164b77SAlexander V. Chernikov 	buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO);
926*d6164b77SAlexander V. Chernikov 	error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy));
927*d6164b77SAlexander V. Chernikov 	if (error != 0)
928*d6164b77SAlexander V. Chernikov 		goto out;
929*d6164b77SAlexander V. Chernikov 
930*d6164b77SAlexander V. Chernikov 	cfg = (struct cfg_nat_legacy *)buf;
931*d6164b77SAlexander V. Chernikov 	if (cfg->id < 0) {
932*d6164b77SAlexander V. Chernikov 		error = EINVAL;
933*d6164b77SAlexander V. Chernikov 		goto out;
934*d6164b77SAlexander V. Chernikov 	}
935*d6164b77SAlexander V. Chernikov 
936*d6164b77SAlexander V. Chernikov 	ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)];
937*d6164b77SAlexander V. Chernikov 	snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id);
938*d6164b77SAlexander V. Chernikov 	strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name));
939*d6164b77SAlexander V. Chernikov 	ucfg->ip = cfg->ip;
940*d6164b77SAlexander V. Chernikov 	ucfg->mode = cfg->mode;
941*d6164b77SAlexander V. Chernikov 	ucfg->redir_cnt = cfg->redir_cnt;
942*d6164b77SAlexander V. Chernikov 
943*d6164b77SAlexander V. Chernikov 	if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) {
944*d6164b77SAlexander V. Chernikov 		error = EINVAL;
945*d6164b77SAlexander V. Chernikov 		goto out;
946*d6164b77SAlexander V. Chernikov 	}
947*d6164b77SAlexander V. Chernikov 
948*d6164b77SAlexander V. Chernikov 	urdir = (struct nat44_cfg_redir *)(ucfg + 1);
949*d6164b77SAlexander V. Chernikov 	rdir = (struct cfg_redir_legacy *)(cfg + 1);
950*d6164b77SAlexander V. Chernikov 	for (i = 0; i < cfg->redir_cnt; i++) {
951*d6164b77SAlexander V. Chernikov 		urdir->mode = rdir->mode;
952*d6164b77SAlexander V. Chernikov 		urdir->laddr = rdir->laddr;
953*d6164b77SAlexander V. Chernikov 		urdir->paddr = rdir->paddr;
954*d6164b77SAlexander V. Chernikov 		urdir->raddr = rdir->raddr;
955*d6164b77SAlexander V. Chernikov 		urdir->lport = rdir->lport;
956*d6164b77SAlexander V. Chernikov 		urdir->pport = rdir->pport;
957*d6164b77SAlexander V. Chernikov 		urdir->rport = rdir->rport;
958*d6164b77SAlexander V. Chernikov 		urdir->pport_cnt = rdir->pport_cnt;
959*d6164b77SAlexander V. Chernikov 		urdir->rport_cnt = rdir->rport_cnt;
960*d6164b77SAlexander V. Chernikov 		urdir->proto = rdir->proto;
961*d6164b77SAlexander V. Chernikov 		urdir->spool_cnt = rdir->spool_cnt;
962*d6164b77SAlexander V. Chernikov 
963*d6164b77SAlexander V. Chernikov 		urdir++;
964*d6164b77SAlexander V. Chernikov 		rdir++;
965*d6164b77SAlexander V. Chernikov 	}
966*d6164b77SAlexander V. Chernikov 
967*d6164b77SAlexander V. Chernikov 	nat44_config(&V_layer3_chain, ucfg);
9683b3a8eb9SGleb Smirnoff 
9693b3a8eb9SGleb Smirnoff out:
9703b3a8eb9SGleb Smirnoff 	free(buf, M_TEMP);
9713b3a8eb9SGleb Smirnoff 	return (error);
9723b3a8eb9SGleb Smirnoff }
9733b3a8eb9SGleb Smirnoff 
9743b3a8eb9SGleb Smirnoff static int
9753b3a8eb9SGleb Smirnoff ipfw_nat_del(struct sockopt *sopt)
9763b3a8eb9SGleb Smirnoff {
9773b3a8eb9SGleb Smirnoff 	struct cfg_nat *ptr;
9783b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain = &V_layer3_chain;
9793b3a8eb9SGleb Smirnoff 	int i;
9803b3a8eb9SGleb Smirnoff 
9813b3a8eb9SGleb Smirnoff 	sooptcopyin(sopt, &i, sizeof i, sizeof i);
9823b3a8eb9SGleb Smirnoff 	/* XXX validate i */
983*d6164b77SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
9843b3a8eb9SGleb Smirnoff 	ptr = lookup_nat(&chain->nat, i);
9853b3a8eb9SGleb Smirnoff 	if (ptr == NULL) {
986*d6164b77SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
9873b3a8eb9SGleb Smirnoff 		return (EINVAL);
9883b3a8eb9SGleb Smirnoff 	}
989*d6164b77SAlexander V. Chernikov 	IPFW_WLOCK(chain);
9903b3a8eb9SGleb Smirnoff 	LIST_REMOVE(ptr, _next);
9913b3a8eb9SGleb Smirnoff 	flush_nat_ptrs(chain, i);
9923b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(chain);
993*d6164b77SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
9943b3a8eb9SGleb Smirnoff 	del_redir_spool_cfg(ptr, &ptr->redir_chain);
9953b3a8eb9SGleb Smirnoff 	LibAliasUninit(ptr->lib);
9963b3a8eb9SGleb Smirnoff 	free(ptr, M_IPFW);
9973b3a8eb9SGleb Smirnoff 	return (0);
9983b3a8eb9SGleb Smirnoff }
9993b3a8eb9SGleb Smirnoff 
10003b3a8eb9SGleb Smirnoff static int
10013b3a8eb9SGleb Smirnoff ipfw_nat_get_cfg(struct sockopt *sopt)
10023b3a8eb9SGleb Smirnoff {
10033b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain = &V_layer3_chain;
10043b3a8eb9SGleb Smirnoff 	struct cfg_nat *n;
1005*d6164b77SAlexander V. Chernikov 	struct cfg_nat_legacy *ucfg;
10063b3a8eb9SGleb Smirnoff 	struct cfg_redir *r;
10073b3a8eb9SGleb Smirnoff 	struct cfg_spool *s;
1008*d6164b77SAlexander V. Chernikov 	struct cfg_redir_legacy *ser_r;
1009*d6164b77SAlexander V. Chernikov 	struct cfg_spool_legacy *ser_s;
10103b3a8eb9SGleb Smirnoff 	char *data;
10113b3a8eb9SGleb Smirnoff 	int gencnt, nat_cnt, len, error;
10123b3a8eb9SGleb Smirnoff 
10133b3a8eb9SGleb Smirnoff 	nat_cnt = 0;
10143b3a8eb9SGleb Smirnoff 	len = sizeof(nat_cnt);
10153b3a8eb9SGleb Smirnoff 
1016*d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
10173b3a8eb9SGleb Smirnoff retry:
10183b3a8eb9SGleb Smirnoff 	gencnt = chain->gencnt;
10193b3a8eb9SGleb Smirnoff 	/* Estimate memory amount */
10203b3a8eb9SGleb Smirnoff 	LIST_FOREACH(n, &chain->nat, _next) {
10213b3a8eb9SGleb Smirnoff 		nat_cnt++;
1022*d6164b77SAlexander V. Chernikov 		len += sizeof(struct cfg_nat_legacy);
10233b3a8eb9SGleb Smirnoff 		LIST_FOREACH(r, &n->redir_chain, _next) {
1024*d6164b77SAlexander V. Chernikov 			len += sizeof(struct cfg_redir_legacy);
10253b3a8eb9SGleb Smirnoff 			LIST_FOREACH(s, &r->spool_chain, _next)
1026*d6164b77SAlexander V. Chernikov 				len += sizeof(struct cfg_spool_legacy);
10273b3a8eb9SGleb Smirnoff 		}
10283b3a8eb9SGleb Smirnoff 	}
1029*d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
10303b3a8eb9SGleb Smirnoff 
10313b3a8eb9SGleb Smirnoff 	data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
10323b3a8eb9SGleb Smirnoff 	bcopy(&nat_cnt, data, sizeof(nat_cnt));
10333b3a8eb9SGleb Smirnoff 
10343b3a8eb9SGleb Smirnoff 	nat_cnt = 0;
10353b3a8eb9SGleb Smirnoff 	len = sizeof(nat_cnt);
10363b3a8eb9SGleb Smirnoff 
1037*d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
10383b3a8eb9SGleb Smirnoff 	if (gencnt != chain->gencnt) {
10393b3a8eb9SGleb Smirnoff 		free(data, M_TEMP);
10403b3a8eb9SGleb Smirnoff 		goto retry;
10413b3a8eb9SGleb Smirnoff 	}
10423b3a8eb9SGleb Smirnoff 	/* Serialize all the data. */
10433b3a8eb9SGleb Smirnoff 	LIST_FOREACH(n, &chain->nat, _next) {
1044*d6164b77SAlexander V. Chernikov 		ucfg = (struct cfg_nat_legacy *)&data[len];
1045*d6164b77SAlexander V. Chernikov 		ucfg->id = n->id;
1046*d6164b77SAlexander V. Chernikov 		ucfg->ip = n->ip;
1047*d6164b77SAlexander V. Chernikov 		ucfg->redir_cnt = n->redir_cnt;
1048*d6164b77SAlexander V. Chernikov 		ucfg->mode = n->mode;
1049*d6164b77SAlexander V. Chernikov 		strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name));
1050*d6164b77SAlexander V. Chernikov 		len += sizeof(struct cfg_nat_legacy);
10513b3a8eb9SGleb Smirnoff 		LIST_FOREACH(r, &n->redir_chain, _next) {
1052*d6164b77SAlexander V. Chernikov 			ser_r = (struct cfg_redir_legacy *)&data[len];
1053*d6164b77SAlexander V. Chernikov 			ser_r->mode = r->mode;
1054*d6164b77SAlexander V. Chernikov 			ser_r->laddr = r->laddr;
1055*d6164b77SAlexander V. Chernikov 			ser_r->paddr = r->paddr;
1056*d6164b77SAlexander V. Chernikov 			ser_r->raddr = r->raddr;
1057*d6164b77SAlexander V. Chernikov 			ser_r->lport = r->lport;
1058*d6164b77SAlexander V. Chernikov 			ser_r->pport = r->pport;
1059*d6164b77SAlexander V. Chernikov 			ser_r->rport = r->rport;
1060*d6164b77SAlexander V. Chernikov 			ser_r->pport_cnt = r->pport_cnt;
1061*d6164b77SAlexander V. Chernikov 			ser_r->rport_cnt = r->rport_cnt;
1062*d6164b77SAlexander V. Chernikov 			ser_r->proto = r->proto;
1063*d6164b77SAlexander V. Chernikov 			ser_r->spool_cnt = r->spool_cnt;
1064*d6164b77SAlexander V. Chernikov 			len += sizeof(struct cfg_redir_legacy);
10653b3a8eb9SGleb Smirnoff 			LIST_FOREACH(s, &r->spool_chain, _next) {
1066*d6164b77SAlexander V. Chernikov 				ser_s = (struct cfg_spool_legacy *)&data[len];
1067*d6164b77SAlexander V. Chernikov 				ser_s->addr = s->addr;
1068*d6164b77SAlexander V. Chernikov 				ser_s->port = s->port;
1069*d6164b77SAlexander V. Chernikov 				len += sizeof(struct cfg_spool_legacy);
10703b3a8eb9SGleb Smirnoff 			}
10713b3a8eb9SGleb Smirnoff 		}
10723b3a8eb9SGleb Smirnoff 	}
1073*d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
10743b3a8eb9SGleb Smirnoff 
10753b3a8eb9SGleb Smirnoff 	error = sooptcopyout(sopt, data, len);
10763b3a8eb9SGleb Smirnoff 	free(data, M_TEMP);
10773b3a8eb9SGleb Smirnoff 
10783b3a8eb9SGleb Smirnoff 	return (error);
10793b3a8eb9SGleb Smirnoff }
10803b3a8eb9SGleb Smirnoff 
10813b3a8eb9SGleb Smirnoff static int
10823b3a8eb9SGleb Smirnoff ipfw_nat_get_log(struct sockopt *sopt)
10833b3a8eb9SGleb Smirnoff {
10843b3a8eb9SGleb Smirnoff 	uint8_t *data;
10853b3a8eb9SGleb Smirnoff 	struct cfg_nat *ptr;
10863b3a8eb9SGleb Smirnoff 	int i, size;
10873b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain;
10883b3a8eb9SGleb Smirnoff 
10893b3a8eb9SGleb Smirnoff 	chain = &V_layer3_chain;
10903b3a8eb9SGleb Smirnoff 
10913b3a8eb9SGleb Smirnoff 	IPFW_RLOCK(chain);
10923b3a8eb9SGleb Smirnoff 	/* one pass to count, one to copy the data */
10933b3a8eb9SGleb Smirnoff 	i = 0;
10943b3a8eb9SGleb Smirnoff 	LIST_FOREACH(ptr, &chain->nat, _next) {
10953b3a8eb9SGleb Smirnoff 		if (ptr->lib->logDesc == NULL)
10963b3a8eb9SGleb Smirnoff 			continue;
10973b3a8eb9SGleb Smirnoff 		i++;
10983b3a8eb9SGleb Smirnoff 	}
10993b3a8eb9SGleb Smirnoff 	size = i * (LIBALIAS_BUF_SIZE + sizeof(int));
11003b3a8eb9SGleb Smirnoff 	data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO);
11013b3a8eb9SGleb Smirnoff 	if (data == NULL) {
11023b3a8eb9SGleb Smirnoff 		IPFW_RUNLOCK(chain);
11033b3a8eb9SGleb Smirnoff 		return (ENOSPC);
11043b3a8eb9SGleb Smirnoff 	}
11053b3a8eb9SGleb Smirnoff 	i = 0;
11063b3a8eb9SGleb Smirnoff 	LIST_FOREACH(ptr, &chain->nat, _next) {
11073b3a8eb9SGleb Smirnoff 		if (ptr->lib->logDesc == NULL)
11083b3a8eb9SGleb Smirnoff 			continue;
11093b3a8eb9SGleb Smirnoff 		bcopy(&ptr->id, &data[i], sizeof(int));
11103b3a8eb9SGleb Smirnoff 		i += sizeof(int);
11113b3a8eb9SGleb Smirnoff 		bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE);
11123b3a8eb9SGleb Smirnoff 		i += LIBALIAS_BUF_SIZE;
11133b3a8eb9SGleb Smirnoff 	}
11143b3a8eb9SGleb Smirnoff 	IPFW_RUNLOCK(chain);
11153b3a8eb9SGleb Smirnoff 	sooptcopyout(sopt, data, size);
11163b3a8eb9SGleb Smirnoff 	free(data, M_IPFW);
11173b3a8eb9SGleb Smirnoff 	return(0);
11183b3a8eb9SGleb Smirnoff }
11193b3a8eb9SGleb Smirnoff 
11208856400bSMikolaj Golub static int
11218856400bSMikolaj Golub vnet_ipfw_nat_init(const void *arg __unused)
11223b3a8eb9SGleb Smirnoff {
11233b3a8eb9SGleb Smirnoff 
11248856400bSMikolaj Golub 	V_ipfw_nat_ready = 1;
11258856400bSMikolaj Golub 	return (0);
11263b3a8eb9SGleb Smirnoff }
11273b3a8eb9SGleb Smirnoff 
11288856400bSMikolaj Golub static int
11298856400bSMikolaj Golub vnet_ipfw_nat_uninit(const void *arg __unused)
11303b3a8eb9SGleb Smirnoff {
11313b3a8eb9SGleb Smirnoff 	struct cfg_nat *ptr, *ptr_temp;
11323b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain;
11333b3a8eb9SGleb Smirnoff 
11343b3a8eb9SGleb Smirnoff 	chain = &V_layer3_chain;
11353b3a8eb9SGleb Smirnoff 	IPFW_WLOCK(chain);
11363b3a8eb9SGleb Smirnoff 	LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) {
11373b3a8eb9SGleb Smirnoff 		LIST_REMOVE(ptr, _next);
11383b3a8eb9SGleb Smirnoff 		del_redir_spool_cfg(ptr, &ptr->redir_chain);
11393b3a8eb9SGleb Smirnoff 		LibAliasUninit(ptr->lib);
11403b3a8eb9SGleb Smirnoff 		free(ptr, M_IPFW);
11413b3a8eb9SGleb Smirnoff 	}
11423b3a8eb9SGleb Smirnoff 	flush_nat_ptrs(chain, -1 /* flush all */);
11438856400bSMikolaj Golub 	V_ipfw_nat_ready = 0;
11448856400bSMikolaj Golub 	IPFW_WUNLOCK(chain);
11458856400bSMikolaj Golub 	return (0);
11468856400bSMikolaj Golub }
11478856400bSMikolaj Golub 
11488856400bSMikolaj Golub static void
11498856400bSMikolaj Golub ipfw_nat_init(void)
11508856400bSMikolaj Golub {
11518856400bSMikolaj Golub 
11528856400bSMikolaj Golub 	/* init ipfw hooks */
11538856400bSMikolaj Golub 	ipfw_nat_ptr = ipfw_nat;
11548856400bSMikolaj Golub 	lookup_nat_ptr = lookup_nat;
11558856400bSMikolaj Golub 	ipfw_nat_cfg_ptr = ipfw_nat_cfg;
11568856400bSMikolaj Golub 	ipfw_nat_del_ptr = ipfw_nat_del;
11578856400bSMikolaj Golub 	ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
11588856400bSMikolaj Golub 	ipfw_nat_get_log_ptr = ipfw_nat_get_log;
1159*d6164b77SAlexander V. Chernikov 	IPFW_ADD_SOPT_HANDLER(1, scodes);
11608856400bSMikolaj Golub 
11618856400bSMikolaj Golub 	ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
11628856400bSMikolaj Golub 	    NULL, EVENTHANDLER_PRI_ANY);
11638856400bSMikolaj Golub }
11648856400bSMikolaj Golub 
11658856400bSMikolaj Golub static void
11668856400bSMikolaj Golub ipfw_nat_destroy(void)
11678856400bSMikolaj Golub {
11688856400bSMikolaj Golub 
11698856400bSMikolaj Golub 	EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
11703b3a8eb9SGleb Smirnoff 	/* deregister ipfw_nat */
1171*d6164b77SAlexander V. Chernikov 	IPFW_DEL_SOPT_HANDLER(1, scodes);
11723b3a8eb9SGleb Smirnoff 	ipfw_nat_ptr = NULL;
11733b3a8eb9SGleb Smirnoff 	lookup_nat_ptr = NULL;
11743b3a8eb9SGleb Smirnoff 	ipfw_nat_cfg_ptr = NULL;
11753b3a8eb9SGleb Smirnoff 	ipfw_nat_del_ptr = NULL;
11763b3a8eb9SGleb Smirnoff 	ipfw_nat_get_cfg_ptr = NULL;
11773b3a8eb9SGleb Smirnoff 	ipfw_nat_get_log_ptr = NULL;
11783b3a8eb9SGleb Smirnoff }
11793b3a8eb9SGleb Smirnoff 
11803b3a8eb9SGleb Smirnoff static int
11813b3a8eb9SGleb Smirnoff ipfw_nat_modevent(module_t mod, int type, void *unused)
11823b3a8eb9SGleb Smirnoff {
11833b3a8eb9SGleb Smirnoff 	int err = 0;
11843b3a8eb9SGleb Smirnoff 
11853b3a8eb9SGleb Smirnoff 	switch (type) {
11863b3a8eb9SGleb Smirnoff 	case MOD_LOAD:
11873b3a8eb9SGleb Smirnoff 		break;
11883b3a8eb9SGleb Smirnoff 
11893b3a8eb9SGleb Smirnoff 	case MOD_UNLOAD:
11903b3a8eb9SGleb Smirnoff 		break;
11913b3a8eb9SGleb Smirnoff 
11923b3a8eb9SGleb Smirnoff 	default:
11933b3a8eb9SGleb Smirnoff 		return EOPNOTSUPP;
11943b3a8eb9SGleb Smirnoff 		break;
11953b3a8eb9SGleb Smirnoff 	}
11963b3a8eb9SGleb Smirnoff 	return err;
11973b3a8eb9SGleb Smirnoff }
11983b3a8eb9SGleb Smirnoff 
11993b3a8eb9SGleb Smirnoff static moduledata_t ipfw_nat_mod = {
12003b3a8eb9SGleb Smirnoff 	"ipfw_nat",
12013b3a8eb9SGleb Smirnoff 	ipfw_nat_modevent,
12029823d527SKevin Lo 	0
12033b3a8eb9SGleb Smirnoff };
12043b3a8eb9SGleb Smirnoff 
12058856400bSMikolaj Golub /* Define startup order. */
12068a477d48SMikolaj Golub #define	IPFW_NAT_SI_SUB_FIREWALL	SI_SUB_PROTO_IFATTACHDOMAIN
12078a477d48SMikolaj Golub #define	IPFW_NAT_MODEVENT_ORDER		(SI_ORDER_ANY - 128) /* after ipfw */
12088856400bSMikolaj Golub #define	IPFW_NAT_MODULE_ORDER		(IPFW_NAT_MODEVENT_ORDER + 1)
12098856400bSMikolaj Golub #define	IPFW_NAT_VNET_ORDER		(IPFW_NAT_MODEVENT_ORDER + 2)
12108856400bSMikolaj Golub 
12118856400bSMikolaj Golub DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY);
12123b3a8eb9SGleb Smirnoff MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1);
12133b3a8eb9SGleb Smirnoff MODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2);
12143b3a8eb9SGleb Smirnoff MODULE_VERSION(ipfw_nat, 1);
12158856400bSMikolaj Golub 
12168856400bSMikolaj Golub SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
12178856400bSMikolaj Golub     ipfw_nat_init, NULL);
12188856400bSMikolaj Golub VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER,
12198856400bSMikolaj Golub     vnet_ipfw_nat_init, NULL);
12208856400bSMikolaj Golub 
12218856400bSMikolaj Golub SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
12228856400bSMikolaj Golub     ipfw_nat_destroy, NULL);
12238856400bSMikolaj Golub VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL,
12248856400bSMikolaj Golub     IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL);
12258856400bSMikolaj Golub 
12263b3a8eb9SGleb Smirnoff /* end of file */
1227