xref: /freebsd/sys/netpfil/ipfw/ip_fw_nat.c (revision 16a72f53e25ba8c25c98c4495dea8f30ae4bee70)
13b3a8eb9SGleb Smirnoff /*-
2fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3fe267a55SPedro F. Giffuni  *
43b3a8eb9SGleb Smirnoff  * Copyright (c) 2008 Paolo Pisati
53b3a8eb9SGleb Smirnoff  * All rights reserved.
63b3a8eb9SGleb Smirnoff  *
73b3a8eb9SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
83b3a8eb9SGleb Smirnoff  * modification, are permitted provided that the following conditions
93b3a8eb9SGleb Smirnoff  * are met:
103b3a8eb9SGleb Smirnoff  * 1. Redistributions of source code must retain the above copyright
113b3a8eb9SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer.
123b3a8eb9SGleb Smirnoff  * 2. Redistributions in binary form must reproduce the above copyright
133b3a8eb9SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer in the
143b3a8eb9SGleb Smirnoff  *    documentation and/or other materials provided with the distribution.
153b3a8eb9SGleb Smirnoff  *
163b3a8eb9SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
173b3a8eb9SGleb Smirnoff  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
183b3a8eb9SGleb Smirnoff  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
193b3a8eb9SGleb Smirnoff  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
203b3a8eb9SGleb Smirnoff  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
213b3a8eb9SGleb Smirnoff  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
223b3a8eb9SGleb Smirnoff  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
233b3a8eb9SGleb Smirnoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
243b3a8eb9SGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
253b3a8eb9SGleb Smirnoff  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
263b3a8eb9SGleb Smirnoff  * SUCH DAMAGE.
273b3a8eb9SGleb Smirnoff  */
283b3a8eb9SGleb Smirnoff 
293b3a8eb9SGleb Smirnoff #include <sys/cdefs.h>
303b3a8eb9SGleb Smirnoff __FBSDID("$FreeBSD$");
313b3a8eb9SGleb Smirnoff 
323b3a8eb9SGleb Smirnoff #include <sys/param.h>
333b3a8eb9SGleb Smirnoff #include <sys/systm.h>
343b3a8eb9SGleb Smirnoff #include <sys/eventhandler.h>
353b3a8eb9SGleb Smirnoff #include <sys/malloc.h>
3676039bc8SGleb Smirnoff #include <sys/mbuf.h>
373b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
383b3a8eb9SGleb Smirnoff #include <sys/lock.h>
393b3a8eb9SGleb Smirnoff #include <sys/module.h>
403b3a8eb9SGleb Smirnoff #include <sys/rwlock.h>
41ccba94b8SAlexander V. Chernikov #include <sys/rmlock.h>
423b3a8eb9SGleb Smirnoff 
433b3a8eb9SGleb Smirnoff #include <netinet/libalias/alias.h>
443b3a8eb9SGleb Smirnoff #include <netinet/libalias/alias_local.h>
453b3a8eb9SGleb Smirnoff 
463b3a8eb9SGleb Smirnoff #include <net/if.h>
4776039bc8SGleb Smirnoff #include <net/if_var.h>
483b3a8eb9SGleb Smirnoff #include <netinet/in.h>
493b3a8eb9SGleb Smirnoff #include <netinet/ip.h>
503b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h>
513b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h>
523b3a8eb9SGleb Smirnoff #include <netinet/tcp.h>
533b3a8eb9SGleb Smirnoff #include <netinet/udp.h>
543b3a8eb9SGleb Smirnoff 
553b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h>
563b3a8eb9SGleb Smirnoff 
573b3a8eb9SGleb Smirnoff #include <machine/in_cksum.h>	/* XXX for in_cksum */
583b3a8eb9SGleb Smirnoff 
59d6164b77SAlexander V. Chernikov struct cfg_spool {
60d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_spool)   _next;          /* chain of spool instances */
61d6164b77SAlexander V. Chernikov 	struct in_addr          addr;
62d6164b77SAlexander V. Chernikov 	uint16_t		port;
63d6164b77SAlexander V. Chernikov };
64d6164b77SAlexander V. Chernikov 
65d6164b77SAlexander V. Chernikov /* Nat redirect configuration. */
66d6164b77SAlexander V. Chernikov struct cfg_redir {
67d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_redir)	_next;	/* chain of redir instances */
68d6164b77SAlexander V. Chernikov 	uint16_t		mode;	/* type of redirect mode */
69d6164b77SAlexander V. Chernikov 	uint16_t		proto;	/* protocol: tcp/udp */
70d6164b77SAlexander V. Chernikov 	struct in_addr		laddr;	/* local ip address */
71d6164b77SAlexander V. Chernikov 	struct in_addr		paddr;	/* public ip address */
72d6164b77SAlexander V. Chernikov 	struct in_addr		raddr;	/* remote ip address */
73d6164b77SAlexander V. Chernikov 	uint16_t		lport;	/* local port */
74d6164b77SAlexander V. Chernikov 	uint16_t		pport;	/* public port */
75d6164b77SAlexander V. Chernikov 	uint16_t		rport;	/* remote port	*/
76d6164b77SAlexander V. Chernikov 	uint16_t		pport_cnt;	/* number of public ports */
77d6164b77SAlexander V. Chernikov 	uint16_t		rport_cnt;	/* number of remote ports */
78d6164b77SAlexander V. Chernikov 	struct alias_link	**alink;
79d6164b77SAlexander V. Chernikov 	u_int16_t		spool_cnt; /* num of entry in spool chain */
80d6164b77SAlexander V. Chernikov 	/* chain of spool instances */
81d6164b77SAlexander V. Chernikov 	LIST_HEAD(spool_chain, cfg_spool) spool_chain;
82d6164b77SAlexander V. Chernikov };
83d6164b77SAlexander V. Chernikov 
84d6164b77SAlexander V. Chernikov /* Nat configuration data struct. */
85d6164b77SAlexander V. Chernikov struct cfg_nat {
86d6164b77SAlexander V. Chernikov 	/* chain of nat instances */
87d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_nat)	_next;
88d6164b77SAlexander V. Chernikov 	int			id;		/* nat id  */
89d6164b77SAlexander V. Chernikov 	struct in_addr		ip;		/* nat ip address */
90d6164b77SAlexander V. Chernikov 	struct libalias		*lib;		/* libalias instance */
91d6164b77SAlexander V. Chernikov 	int			mode;		/* aliasing mode */
92d6164b77SAlexander V. Chernikov 	int			redir_cnt; /* number of entry in spool chain */
93d6164b77SAlexander V. Chernikov 	/* chain of redir instances */
94d6164b77SAlexander V. Chernikov 	LIST_HEAD(redir_chain, cfg_redir) redir_chain;
95d6164b77SAlexander V. Chernikov 	char			if_name[IF_NAMESIZE];	/* interface name */
96d6164b77SAlexander V. Chernikov };
97d6164b77SAlexander V. Chernikov 
988856400bSMikolaj Golub static eventhandler_tag ifaddr_event_tag;
993b3a8eb9SGleb Smirnoff 
1003b3a8eb9SGleb Smirnoff static void
1013b3a8eb9SGleb Smirnoff ifaddr_change(void *arg __unused, struct ifnet *ifp)
1023b3a8eb9SGleb Smirnoff {
1033b3a8eb9SGleb Smirnoff 	struct cfg_nat *ptr;
1043b3a8eb9SGleb Smirnoff 	struct ifaddr *ifa;
1053b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain;
1063b3a8eb9SGleb Smirnoff 
1078856400bSMikolaj Golub 	KASSERT(curvnet == ifp->if_vnet,
1088856400bSMikolaj Golub 	    ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet));
1099ac51e79SBjoern A. Zeeb 
1109ac51e79SBjoern A. Zeeb 	if (V_ipfw_vnet_ready == 0 || V_ipfw_nat_ready == 0)
1119ac51e79SBjoern A. Zeeb 		return;
1129ac51e79SBjoern A. Zeeb 
1133b3a8eb9SGleb Smirnoff 	chain = &V_layer3_chain;
1140b47e42bSAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
1153b3a8eb9SGleb Smirnoff 	/* Check every nat entry... */
1163b3a8eb9SGleb Smirnoff 	LIST_FOREACH(ptr, &chain->nat, _next) {
117*16a72f53SGleb Smirnoff 		struct epoch_tracker et;
118*16a72f53SGleb Smirnoff 
1193b3a8eb9SGleb Smirnoff 		/* ...using nic 'ifp->if_xname' as dynamic alias address. */
120c254a209SAlexander V. Chernikov 		if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0)
1213b3a8eb9SGleb Smirnoff 			continue;
122*16a72f53SGleb Smirnoff 		NET_EPOCH_ENTER(et);
123d7c5a620SMatt Macy 		CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
1243b3a8eb9SGleb Smirnoff 			if (ifa->ifa_addr == NULL)
1253b3a8eb9SGleb Smirnoff 				continue;
1263b3a8eb9SGleb Smirnoff 			if (ifa->ifa_addr->sa_family != AF_INET)
1273b3a8eb9SGleb Smirnoff 				continue;
1280b47e42bSAlexander V. Chernikov 			IPFW_WLOCK(chain);
1293b3a8eb9SGleb Smirnoff 			ptr->ip = ((struct sockaddr_in *)
1303b3a8eb9SGleb Smirnoff 			    (ifa->ifa_addr))->sin_addr;
1313b3a8eb9SGleb Smirnoff 			LibAliasSetAddress(ptr->lib, ptr->ip);
1320b47e42bSAlexander V. Chernikov 			IPFW_WUNLOCK(chain);
1333b3a8eb9SGleb Smirnoff 		}
134*16a72f53SGleb Smirnoff 		NET_EPOCH_EXIT(et);
1353b3a8eb9SGleb Smirnoff 	}
1360b47e42bSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
1373b3a8eb9SGleb Smirnoff }
1383b3a8eb9SGleb Smirnoff 
1393b3a8eb9SGleb Smirnoff /*
1403b3a8eb9SGleb Smirnoff  * delete the pointers for nat entry ix, or all of them if ix < 0
1413b3a8eb9SGleb Smirnoff  */
1423b3a8eb9SGleb Smirnoff static void
1433b3a8eb9SGleb Smirnoff flush_nat_ptrs(struct ip_fw_chain *chain, const int ix)
1443b3a8eb9SGleb Smirnoff {
1453b3a8eb9SGleb Smirnoff 	ipfw_insn_nat *cmd;
146e758846cSAndrey V. Elsukov 	int i;
1473b3a8eb9SGleb Smirnoff 
1483b3a8eb9SGleb Smirnoff 	IPFW_WLOCK_ASSERT(chain);
1493b3a8eb9SGleb Smirnoff 	for (i = 0; i < chain->n_rules; i++) {
150e758846cSAndrey V. Elsukov 		cmd = (ipfw_insn_nat *)ipfw_get_action(chain->map[i]);
1513b3a8eb9SGleb Smirnoff 		if (cmd->o.opcode == O_NAT && cmd->nat != NULL &&
1523b3a8eb9SGleb Smirnoff 			    (ix < 0 || cmd->nat->id == ix))
1533b3a8eb9SGleb Smirnoff 			cmd->nat = NULL;
1543b3a8eb9SGleb Smirnoff 	}
1553b3a8eb9SGleb Smirnoff }
1563b3a8eb9SGleb Smirnoff 
1573b3a8eb9SGleb Smirnoff static void
1583b3a8eb9SGleb Smirnoff del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
1593b3a8eb9SGleb Smirnoff {
1603b3a8eb9SGleb Smirnoff 	struct cfg_redir *r, *tmp_r;
1613b3a8eb9SGleb Smirnoff 	struct cfg_spool *s, *tmp_s;
1623b3a8eb9SGleb Smirnoff 	int i, num;
1633b3a8eb9SGleb Smirnoff 
1643b3a8eb9SGleb Smirnoff 	LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
1653b3a8eb9SGleb Smirnoff 		num = 1; /* Number of alias_link to delete. */
1663b3a8eb9SGleb Smirnoff 		switch (r->mode) {
167d6164b77SAlexander V. Chernikov 		case NAT44_REDIR_PORT:
1683b3a8eb9SGleb Smirnoff 			num = r->pport_cnt;
1693b3a8eb9SGleb Smirnoff 			/* FALLTHROUGH */
170d6164b77SAlexander V. Chernikov 		case NAT44_REDIR_ADDR:
171d6164b77SAlexander V. Chernikov 		case NAT44_REDIR_PROTO:
1723b3a8eb9SGleb Smirnoff 			/* Delete all libalias redirect entry. */
1733b3a8eb9SGleb Smirnoff 			for (i = 0; i < num; i++)
1743b3a8eb9SGleb Smirnoff 				LibAliasRedirectDelete(n->lib, r->alink[i]);
1753b3a8eb9SGleb Smirnoff 			/* Del spool cfg if any. */
1763b3a8eb9SGleb Smirnoff 			LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) {
1773b3a8eb9SGleb Smirnoff 				LIST_REMOVE(s, _next);
1783b3a8eb9SGleb Smirnoff 				free(s, M_IPFW);
1793b3a8eb9SGleb Smirnoff 			}
1803b3a8eb9SGleb Smirnoff 			free(r->alink, M_IPFW);
1813b3a8eb9SGleb Smirnoff 			LIST_REMOVE(r, _next);
1823b3a8eb9SGleb Smirnoff 			free(r, M_IPFW);
1833b3a8eb9SGleb Smirnoff 			break;
1843b3a8eb9SGleb Smirnoff 		default:
1853b3a8eb9SGleb Smirnoff 			printf("unknown redirect mode: %u\n", r->mode);
1863b3a8eb9SGleb Smirnoff 			/* XXX - panic?!?!? */
1873b3a8eb9SGleb Smirnoff 			break;
1883b3a8eb9SGleb Smirnoff 		}
1893b3a8eb9SGleb Smirnoff 	}
1903b3a8eb9SGleb Smirnoff }
1913b3a8eb9SGleb Smirnoff 
192d6164b77SAlexander V. Chernikov static int
1933b3a8eb9SGleb Smirnoff add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
1943b3a8eb9SGleb Smirnoff {
195d6164b77SAlexander V. Chernikov 	struct cfg_redir *r;
196d6164b77SAlexander V. Chernikov 	struct cfg_spool *s;
197d6164b77SAlexander V. Chernikov 	struct nat44_cfg_redir *ser_r;
198d6164b77SAlexander V. Chernikov 	struct nat44_cfg_spool *ser_s;
199d6164b77SAlexander V. Chernikov 
2003b3a8eb9SGleb Smirnoff 	int cnt, off, i;
2013b3a8eb9SGleb Smirnoff 
2023b3a8eb9SGleb Smirnoff 	for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
203d6164b77SAlexander V. Chernikov 		ser_r = (struct nat44_cfg_redir *)&buf[off];
204d6164b77SAlexander V. Chernikov 		r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO);
205d6164b77SAlexander V. Chernikov 		r->mode = ser_r->mode;
206d6164b77SAlexander V. Chernikov 		r->laddr = ser_r->laddr;
207d6164b77SAlexander V. Chernikov 		r->paddr = ser_r->paddr;
208d6164b77SAlexander V. Chernikov 		r->raddr = ser_r->raddr;
209d6164b77SAlexander V. Chernikov 		r->lport = ser_r->lport;
210d6164b77SAlexander V. Chernikov 		r->pport = ser_r->pport;
211d6164b77SAlexander V. Chernikov 		r->rport = ser_r->rport;
212d6164b77SAlexander V. Chernikov 		r->pport_cnt = ser_r->pport_cnt;
213d6164b77SAlexander V. Chernikov 		r->rport_cnt = ser_r->rport_cnt;
214d6164b77SAlexander V. Chernikov 		r->proto = ser_r->proto;
215d6164b77SAlexander V. Chernikov 		r->spool_cnt = ser_r->spool_cnt;
216d6164b77SAlexander V. Chernikov 		//memcpy(r, ser_r, SOF_REDIR);
2173b3a8eb9SGleb Smirnoff 		LIST_INIT(&r->spool_chain);
218d6164b77SAlexander V. Chernikov 		off += sizeof(struct nat44_cfg_redir);
2193b3a8eb9SGleb Smirnoff 		r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
2203b3a8eb9SGleb Smirnoff 		    M_IPFW, M_WAITOK | M_ZERO);
2213b3a8eb9SGleb Smirnoff 		switch (r->mode) {
222d6164b77SAlexander V. Chernikov 		case NAT44_REDIR_ADDR:
2233b3a8eb9SGleb Smirnoff 			r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
2243b3a8eb9SGleb Smirnoff 			    r->paddr);
2253b3a8eb9SGleb Smirnoff 			break;
226d6164b77SAlexander V. Chernikov 		case NAT44_REDIR_PORT:
2273b3a8eb9SGleb Smirnoff 			for (i = 0 ; i < r->pport_cnt; i++) {
2283b3a8eb9SGleb Smirnoff 				/* If remotePort is all ports, set it to 0. */
2293b3a8eb9SGleb Smirnoff 				u_short remotePortCopy = r->rport + i;
2303b3a8eb9SGleb Smirnoff 				if (r->rport_cnt == 1 && r->rport == 0)
2313b3a8eb9SGleb Smirnoff 					remotePortCopy = 0;
2323b3a8eb9SGleb Smirnoff 				r->alink[i] = LibAliasRedirectPort(ptr->lib,
2333b3a8eb9SGleb Smirnoff 				    r->laddr, htons(r->lport + i), r->raddr,
2343b3a8eb9SGleb Smirnoff 				    htons(remotePortCopy), r->paddr,
2353b3a8eb9SGleb Smirnoff 				    htons(r->pport + i), r->proto);
2363b3a8eb9SGleb Smirnoff 				if (r->alink[i] == NULL) {
2373b3a8eb9SGleb Smirnoff 					r->alink[0] = NULL;
2383b3a8eb9SGleb Smirnoff 					break;
2393b3a8eb9SGleb Smirnoff 				}
2403b3a8eb9SGleb Smirnoff 			}
2413b3a8eb9SGleb Smirnoff 			break;
242d6164b77SAlexander V. Chernikov 		case NAT44_REDIR_PROTO:
2433b3a8eb9SGleb Smirnoff 			r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
2443b3a8eb9SGleb Smirnoff 			    r->raddr, r->paddr, r->proto);
2453b3a8eb9SGleb Smirnoff 			break;
2463b3a8eb9SGleb Smirnoff 		default:
2473b3a8eb9SGleb Smirnoff 			printf("unknown redirect mode: %u\n", r->mode);
2483b3a8eb9SGleb Smirnoff 			break;
2493b3a8eb9SGleb Smirnoff 		}
250d6164b77SAlexander V. Chernikov 		if (r->alink[0] == NULL) {
251d6164b77SAlexander V. Chernikov 			printf("LibAliasRedirect* returned NULL\n");
252fdf6290eSGleb Smirnoff 			free(r->alink, M_IPFW);
253fdf6290eSGleb Smirnoff 			free(r, M_IPFW);
254d6164b77SAlexander V. Chernikov 			return (EINVAL);
255d6164b77SAlexander V. Chernikov 		}
2563b3a8eb9SGleb Smirnoff 		/* LSNAT handling. */
2573b3a8eb9SGleb Smirnoff 		for (i = 0; i < r->spool_cnt; i++) {
258d6164b77SAlexander V. Chernikov 			ser_s = (struct nat44_cfg_spool *)&buf[off];
259d6164b77SAlexander V. Chernikov 			s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO);
260d6164b77SAlexander V. Chernikov 			s->addr = ser_s->addr;
261d6164b77SAlexander V. Chernikov 			s->port = ser_s->port;
2623b3a8eb9SGleb Smirnoff 			LibAliasAddServer(ptr->lib, r->alink[0],
2633b3a8eb9SGleb Smirnoff 			    s->addr, htons(s->port));
264d6164b77SAlexander V. Chernikov 			off += sizeof(struct nat44_cfg_spool);
2653b3a8eb9SGleb Smirnoff 			/* Hook spool entry. */
2663b3a8eb9SGleb Smirnoff 			LIST_INSERT_HEAD(&r->spool_chain, s, _next);
2673b3a8eb9SGleb Smirnoff 		}
2683b3a8eb9SGleb Smirnoff 		/* And finally hook this redir entry. */
2693b3a8eb9SGleb Smirnoff 		LIST_INSERT_HEAD(&ptr->redir_chain, r, _next);
2703b3a8eb9SGleb Smirnoff 	}
271d6164b77SAlexander V. Chernikov 
272d6164b77SAlexander V. Chernikov 	return (0);
2733b3a8eb9SGleb Smirnoff }
2743b3a8eb9SGleb Smirnoff 
2751a458088SAlexander V. Chernikov static void
2761a458088SAlexander V. Chernikov free_nat_instance(struct cfg_nat *ptr)
2771a458088SAlexander V. Chernikov {
2781a458088SAlexander V. Chernikov 
2791a458088SAlexander V. Chernikov 	del_redir_spool_cfg(ptr, &ptr->redir_chain);
2801a458088SAlexander V. Chernikov 	LibAliasUninit(ptr->lib);
2811a458088SAlexander V. Chernikov 	free(ptr, M_IPFW);
2821a458088SAlexander V. Chernikov }
2831a458088SAlexander V. Chernikov 
2841a458088SAlexander V. Chernikov 
28510ab2de0SAlexander V. Chernikov /*
28610ab2de0SAlexander V. Chernikov  * ipfw_nat - perform mbuf header translation.
28710ab2de0SAlexander V. Chernikov  *
28810ab2de0SAlexander V. Chernikov  * Note V_layer3_chain has to be locked while calling ipfw_nat() in
28910ab2de0SAlexander V. Chernikov  * 'global' operation mode (t == NULL).
29010ab2de0SAlexander V. Chernikov  *
29110ab2de0SAlexander V. Chernikov  */
2923b3a8eb9SGleb Smirnoff static int
2933b3a8eb9SGleb Smirnoff ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
2943b3a8eb9SGleb Smirnoff {
2953b3a8eb9SGleb Smirnoff 	struct mbuf *mcl;
2963b3a8eb9SGleb Smirnoff 	struct ip *ip;
2973b3a8eb9SGleb Smirnoff 	/* XXX - libalias duct tape */
2983b3a8eb9SGleb Smirnoff 	int ldt, retval, found;
2993b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain;
3003b3a8eb9SGleb Smirnoff 	char *c;
3013b3a8eb9SGleb Smirnoff 
3023b3a8eb9SGleb Smirnoff 	ldt = 0;
3033b3a8eb9SGleb Smirnoff 	retval = 0;
3043b3a8eb9SGleb Smirnoff 	mcl = m_megapullup(m, m->m_pkthdr.len);
3053b3a8eb9SGleb Smirnoff 	if (mcl == NULL) {
3063b3a8eb9SGleb Smirnoff 		args->m = NULL;
3073b3a8eb9SGleb Smirnoff 		return (IP_FW_DENY);
3083b3a8eb9SGleb Smirnoff 	}
3093b3a8eb9SGleb Smirnoff 	ip = mtod(mcl, struct ip *);
3103b3a8eb9SGleb Smirnoff 
3113b3a8eb9SGleb Smirnoff 	/*
3123b3a8eb9SGleb Smirnoff 	 * XXX - Libalias checksum offload 'duct tape':
3133b3a8eb9SGleb Smirnoff 	 *
3143b3a8eb9SGleb Smirnoff 	 * locally generated packets have only pseudo-header checksum
3153b3a8eb9SGleb Smirnoff 	 * calculated and libalias will break it[1], so mark them for
3163b3a8eb9SGleb Smirnoff 	 * later fix.  Moreover there are cases when libalias modifies
3173b3a8eb9SGleb Smirnoff 	 * tcp packet data[2], mark them for later fix too.
3183b3a8eb9SGleb Smirnoff 	 *
3193b3a8eb9SGleb Smirnoff 	 * [1] libalias was never meant to run in kernel, so it does
3203b3a8eb9SGleb Smirnoff 	 * not have any knowledge about checksum offloading, and
3213b3a8eb9SGleb Smirnoff 	 * expects a packet with a full internet checksum.
3223b3a8eb9SGleb Smirnoff 	 * Unfortunately, packets generated locally will have just the
3233b3a8eb9SGleb Smirnoff 	 * pseudo header calculated, and when libalias tries to adjust
3243b3a8eb9SGleb Smirnoff 	 * the checksum it will actually compute a wrong value.
3253b3a8eb9SGleb Smirnoff 	 *
3263b3a8eb9SGleb Smirnoff 	 * [2] when libalias modifies tcp's data content, full TCP
3273b3a8eb9SGleb Smirnoff 	 * checksum has to be recomputed: the problem is that
3283b3a8eb9SGleb Smirnoff 	 * libalias does not have any idea about checksum offloading.
3293b3a8eb9SGleb Smirnoff 	 * To work around this, we do not do checksumming in LibAlias,
3303b3a8eb9SGleb Smirnoff 	 * but only mark the packets in th_x2 field. If we receive a
3313b3a8eb9SGleb Smirnoff 	 * marked packet, we calculate correct checksum for it
3323b3a8eb9SGleb Smirnoff 	 * aware of offloading.  Why such a terrible hack instead of
3333b3a8eb9SGleb Smirnoff 	 * recalculating checksum for each packet?
3343b3a8eb9SGleb Smirnoff 	 * Because the previous checksum was not checked!
3353b3a8eb9SGleb Smirnoff 	 * Recalculating checksums for EVERY packet will hide ALL
3363b3a8eb9SGleb Smirnoff 	 * transmission errors. Yes, marked packets still suffer from
3373b3a8eb9SGleb Smirnoff 	 * this problem. But, sigh, natd(8) has this problem, too.
3383b3a8eb9SGleb Smirnoff 	 *
3393b3a8eb9SGleb Smirnoff 	 * TODO: -make libalias mbuf aware (so
3403b3a8eb9SGleb Smirnoff 	 * it can handle delayed checksum and tso)
3413b3a8eb9SGleb Smirnoff 	 */
3423b3a8eb9SGleb Smirnoff 
3433b3a8eb9SGleb Smirnoff 	if (mcl->m_pkthdr.rcvif == NULL &&
3443b3a8eb9SGleb Smirnoff 	    mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA)
3453b3a8eb9SGleb Smirnoff 		ldt = 1;
3463b3a8eb9SGleb Smirnoff 
3473b3a8eb9SGleb Smirnoff 	c = mtod(mcl, char *);
3483b3a8eb9SGleb Smirnoff 
3493b3a8eb9SGleb Smirnoff 	/* Check if this is 'global' instance */
3503b3a8eb9SGleb Smirnoff 	if (t == NULL) {
351b7795b67SGleb Smirnoff 		if (args->flags & IPFW_ARGS_IN) {
3523b3a8eb9SGleb Smirnoff 			/* Wrong direction, skip processing */
3533b3a8eb9SGleb Smirnoff 			args->m = mcl;
3543b3a8eb9SGleb Smirnoff 			return (IP_FW_NAT);
3553b3a8eb9SGleb Smirnoff 		}
3563b3a8eb9SGleb Smirnoff 
3573b3a8eb9SGleb Smirnoff 		found = 0;
3583b3a8eb9SGleb Smirnoff 		chain = &V_layer3_chain;
3595d0cd926SAlexander V. Chernikov 		IPFW_RLOCK_ASSERT(chain);
3603b3a8eb9SGleb Smirnoff 		/* Check every nat entry... */
3613b3a8eb9SGleb Smirnoff 		LIST_FOREACH(t, &chain->nat, _next) {
3623b3a8eb9SGleb Smirnoff 			if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0)
3633b3a8eb9SGleb Smirnoff 				continue;
3643b3a8eb9SGleb Smirnoff 			retval = LibAliasOutTry(t->lib, c,
3653b3a8eb9SGleb Smirnoff 			    mcl->m_len + M_TRAILINGSPACE(mcl), 0);
3663b3a8eb9SGleb Smirnoff 			if (retval == PKT_ALIAS_OK) {
3673b3a8eb9SGleb Smirnoff 				/* Nat instance recognises state */
3683b3a8eb9SGleb Smirnoff 				found = 1;
3693b3a8eb9SGleb Smirnoff 				break;
3703b3a8eb9SGleb Smirnoff 			}
3713b3a8eb9SGleb Smirnoff 		}
3723b3a8eb9SGleb Smirnoff 		if (found != 1) {
3733b3a8eb9SGleb Smirnoff 			/* No instance found, return ignore */
3743b3a8eb9SGleb Smirnoff 			args->m = mcl;
3753b3a8eb9SGleb Smirnoff 			return (IP_FW_NAT);
3763b3a8eb9SGleb Smirnoff 		}
3773b3a8eb9SGleb Smirnoff 	} else {
378b7795b67SGleb Smirnoff 		if (args->flags & IPFW_ARGS_IN)
3793b3a8eb9SGleb Smirnoff 			retval = LibAliasIn(t->lib, c,
3803b3a8eb9SGleb Smirnoff 				mcl->m_len + M_TRAILINGSPACE(mcl));
3813b3a8eb9SGleb Smirnoff 		else
3823b3a8eb9SGleb Smirnoff 			retval = LibAliasOut(t->lib, c,
3833b3a8eb9SGleb Smirnoff 				mcl->m_len + M_TRAILINGSPACE(mcl));
3843b3a8eb9SGleb Smirnoff 	}
3853b3a8eb9SGleb Smirnoff 
3863b3a8eb9SGleb Smirnoff 	/*
3873b3a8eb9SGleb Smirnoff 	 * We drop packet when:
3883b3a8eb9SGleb Smirnoff 	 * 1. libalias returns PKT_ALIAS_ERROR;
3893b3a8eb9SGleb Smirnoff 	 * 2. For incoming packets:
3903b3a8eb9SGleb Smirnoff 	 *	a) for unresolved fragments;
3913b3a8eb9SGleb Smirnoff 	 *	b) libalias returns PKT_ALIAS_IGNORED and
3923b3a8eb9SGleb Smirnoff 	 *		PKT_ALIAS_DENY_INCOMING flag is set.
3933b3a8eb9SGleb Smirnoff 	 */
3943b3a8eb9SGleb Smirnoff 	if (retval == PKT_ALIAS_ERROR ||
395b7795b67SGleb Smirnoff 	    ((args->flags & IPFW_ARGS_IN) &&
396b7795b67SGleb Smirnoff 	    (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
3973b3a8eb9SGleb Smirnoff 	    (retval == PKT_ALIAS_IGNORED &&
3983b3a8eb9SGleb Smirnoff 	    (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) {
3993b3a8eb9SGleb Smirnoff 		/* XXX - should i add some logging? */
4003b3a8eb9SGleb Smirnoff 		m_free(mcl);
4013b3a8eb9SGleb Smirnoff 		args->m = NULL;
4023b3a8eb9SGleb Smirnoff 		return (IP_FW_DENY);
4033b3a8eb9SGleb Smirnoff 	}
4043b3a8eb9SGleb Smirnoff 
4053b3a8eb9SGleb Smirnoff 	if (retval == PKT_ALIAS_RESPOND)
4063b3a8eb9SGleb Smirnoff 		mcl->m_flags |= M_SKIP_FIREWALL;
4073b3a8eb9SGleb Smirnoff 	mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
4083b3a8eb9SGleb Smirnoff 
4093b3a8eb9SGleb Smirnoff 	/*
4103b3a8eb9SGleb Smirnoff 	 * XXX - libalias checksum offload
4113b3a8eb9SGleb Smirnoff 	 * 'duct tape' (see above)
4123b3a8eb9SGleb Smirnoff 	 */
4133b3a8eb9SGleb Smirnoff 
4143b3a8eb9SGleb Smirnoff 	if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
4153b3a8eb9SGleb Smirnoff 	    ip->ip_p == IPPROTO_TCP) {
4163b3a8eb9SGleb Smirnoff 		struct tcphdr 	*th;
4173b3a8eb9SGleb Smirnoff 
4183b3a8eb9SGleb Smirnoff 		th = (struct tcphdr *)(ip + 1);
4193b3a8eb9SGleb Smirnoff 		if (th->th_x2)
4203b3a8eb9SGleb Smirnoff 			ldt = 1;
4213b3a8eb9SGleb Smirnoff 	}
4223b3a8eb9SGleb Smirnoff 
4233b3a8eb9SGleb Smirnoff 	if (ldt) {
4243b3a8eb9SGleb Smirnoff 		struct tcphdr 	*th;
4253b3a8eb9SGleb Smirnoff 		struct udphdr 	*uh;
42623e9c6dcSGleb Smirnoff 		uint16_t ip_len, cksum;
4273b3a8eb9SGleb Smirnoff 
42823e9c6dcSGleb Smirnoff 		ip_len = ntohs(ip->ip_len);
4293b3a8eb9SGleb Smirnoff 		cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
43023e9c6dcSGleb Smirnoff 		    htons(ip->ip_p + ip_len - (ip->ip_hl << 2)));
4313b3a8eb9SGleb Smirnoff 
4323b3a8eb9SGleb Smirnoff 		switch (ip->ip_p) {
4333b3a8eb9SGleb Smirnoff 		case IPPROTO_TCP:
4343b3a8eb9SGleb Smirnoff 			th = (struct tcphdr *)(ip + 1);
4353b3a8eb9SGleb Smirnoff 			/*
4363b3a8eb9SGleb Smirnoff 			 * Maybe it was set in
4373b3a8eb9SGleb Smirnoff 			 * libalias...
4383b3a8eb9SGleb Smirnoff 			 */
4393b3a8eb9SGleb Smirnoff 			th->th_x2 = 0;
4403b3a8eb9SGleb Smirnoff 			th->th_sum = cksum;
4413b3a8eb9SGleb Smirnoff 			mcl->m_pkthdr.csum_data =
4423b3a8eb9SGleb Smirnoff 			    offsetof(struct tcphdr, th_sum);
4433b3a8eb9SGleb Smirnoff 			break;
4443b3a8eb9SGleb Smirnoff 		case IPPROTO_UDP:
4453b3a8eb9SGleb Smirnoff 			uh = (struct udphdr *)(ip + 1);
4463b3a8eb9SGleb Smirnoff 			uh->uh_sum = cksum;
4473b3a8eb9SGleb Smirnoff 			mcl->m_pkthdr.csum_data =
4483b3a8eb9SGleb Smirnoff 			    offsetof(struct udphdr, uh_sum);
4493b3a8eb9SGleb Smirnoff 			break;
4503b3a8eb9SGleb Smirnoff 		}
4513b3a8eb9SGleb Smirnoff 		/* No hw checksum offloading: do it ourselves */
4523b3a8eb9SGleb Smirnoff 		if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) {
4533b3a8eb9SGleb Smirnoff 			in_delayed_cksum(mcl);
4543b3a8eb9SGleb Smirnoff 			mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
4553b3a8eb9SGleb Smirnoff 		}
4563b3a8eb9SGleb Smirnoff 	}
4573b3a8eb9SGleb Smirnoff 	args->m = mcl;
4583b3a8eb9SGleb Smirnoff 	return (IP_FW_NAT);
4593b3a8eb9SGleb Smirnoff }
4603b3a8eb9SGleb Smirnoff 
4613b3a8eb9SGleb Smirnoff static struct cfg_nat *
4623b3a8eb9SGleb Smirnoff lookup_nat(struct nat_list *l, int nat_id)
4633b3a8eb9SGleb Smirnoff {
4643b3a8eb9SGleb Smirnoff 	struct cfg_nat *res;
4653b3a8eb9SGleb Smirnoff 
4663b3a8eb9SGleb Smirnoff 	LIST_FOREACH(res, l, _next) {
4673b3a8eb9SGleb Smirnoff 		if (res->id == nat_id)
4683b3a8eb9SGleb Smirnoff 			break;
4693b3a8eb9SGleb Smirnoff 	}
4703b3a8eb9SGleb Smirnoff 	return res;
4713b3a8eb9SGleb Smirnoff }
4723b3a8eb9SGleb Smirnoff 
473d6164b77SAlexander V. Chernikov static struct cfg_nat *
474d6164b77SAlexander V. Chernikov lookup_nat_name(struct nat_list *l, char *name)
4753b3a8eb9SGleb Smirnoff {
476d6164b77SAlexander V. Chernikov 	struct cfg_nat *res;
477d6164b77SAlexander V. Chernikov 	int id;
478d6164b77SAlexander V. Chernikov 	char *errptr;
4793b3a8eb9SGleb Smirnoff 
480d6164b77SAlexander V. Chernikov 	id = strtol(name, &errptr, 10);
481d6164b77SAlexander V. Chernikov 	if (id == 0 || *errptr != '\0')
482d6164b77SAlexander V. Chernikov 		return (NULL);
4833b3a8eb9SGleb Smirnoff 
484d6164b77SAlexander V. Chernikov 	LIST_FOREACH(res, l, _next) {
485d6164b77SAlexander V. Chernikov 		if (res->id == id)
486d6164b77SAlexander V. Chernikov 			break;
4873b3a8eb9SGleb Smirnoff 	}
488d6164b77SAlexander V. Chernikov 	return (res);
489d6164b77SAlexander V. Chernikov }
490d6164b77SAlexander V. Chernikov 
491d6164b77SAlexander V. Chernikov /* IP_FW3 configuration routines */
492d6164b77SAlexander V. Chernikov 
493d6164b77SAlexander V. Chernikov static void
494d6164b77SAlexander V. Chernikov nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg)
495d6164b77SAlexander V. Chernikov {
496d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr, *tcfg;
497d6164b77SAlexander V. Chernikov 	int gencnt;
4983b3a8eb9SGleb Smirnoff 
4993b3a8eb9SGleb Smirnoff 	/*
5003b3a8eb9SGleb Smirnoff 	 * Find/create nat rule.
5013b3a8eb9SGleb Smirnoff 	 */
502d6164b77SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
5033b3a8eb9SGleb Smirnoff 	gencnt = chain->gencnt;
504d6164b77SAlexander V. Chernikov 	ptr = lookup_nat_name(&chain->nat, ucfg->name);
5053b3a8eb9SGleb Smirnoff 	if (ptr == NULL) {
506d6164b77SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
5073b3a8eb9SGleb Smirnoff 		/* New rule: allocate and init new instance. */
5083b3a8eb9SGleb Smirnoff 		ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO);
5093b3a8eb9SGleb Smirnoff 		ptr->lib = LibAliasInit(NULL);
5103b3a8eb9SGleb Smirnoff 		LIST_INIT(&ptr->redir_chain);
5113b3a8eb9SGleb Smirnoff 	} else {
5123b3a8eb9SGleb Smirnoff 		/* Entry already present: temporarily unhook it. */
513d6164b77SAlexander V. Chernikov 		IPFW_WLOCK(chain);
5143b3a8eb9SGleb Smirnoff 		LIST_REMOVE(ptr, _next);
515d6164b77SAlexander V. Chernikov 		flush_nat_ptrs(chain, ptr->id);
5163b3a8eb9SGleb Smirnoff 		IPFW_WUNLOCK(chain);
517d6164b77SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
5183b3a8eb9SGleb Smirnoff 	}
5193b3a8eb9SGleb Smirnoff 
5203b3a8eb9SGleb Smirnoff 	/*
521d6164b77SAlexander V. Chernikov 	 * Basic nat (re)configuration.
5223b3a8eb9SGleb Smirnoff 	 */
523d6164b77SAlexander V. Chernikov 	ptr->id = strtol(ucfg->name, NULL, 10);
5243b3a8eb9SGleb Smirnoff 	/*
5253b3a8eb9SGleb Smirnoff 	 * XXX - what if this rule doesn't nat any ip and just
5263b3a8eb9SGleb Smirnoff 	 * redirect?
5273b3a8eb9SGleb Smirnoff 	 * do we set aliasaddress to 0.0.0.0?
5283b3a8eb9SGleb Smirnoff 	 */
529d6164b77SAlexander V. Chernikov 	ptr->ip = ucfg->ip;
530d6164b77SAlexander V. Chernikov 	ptr->redir_cnt = ucfg->redir_cnt;
531d6164b77SAlexander V. Chernikov 	ptr->mode = ucfg->mode;
532d6164b77SAlexander V. Chernikov 	strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name));
533d6164b77SAlexander V. Chernikov 	LibAliasSetMode(ptr->lib, ptr->mode, ~0);
5343b3a8eb9SGleb Smirnoff 	LibAliasSetAddress(ptr->lib, ptr->ip);
5353b3a8eb9SGleb Smirnoff 
5363b3a8eb9SGleb Smirnoff 	/*
5373b3a8eb9SGleb Smirnoff 	 * Redir and LSNAT configuration.
5383b3a8eb9SGleb Smirnoff 	 */
5393b3a8eb9SGleb Smirnoff 	/* Delete old cfgs. */
5403b3a8eb9SGleb Smirnoff 	del_redir_spool_cfg(ptr, &ptr->redir_chain);
5413b3a8eb9SGleb Smirnoff 	/* Add new entries. */
542d6164b77SAlexander V. Chernikov 	add_redir_spool_cfg((char *)(ucfg + 1), ptr);
543d6164b77SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
5443b3a8eb9SGleb Smirnoff 
5453b3a8eb9SGleb Smirnoff 	/* Extra check to avoid race with another ipfw_nat_cfg() */
546d6164b77SAlexander V. Chernikov 	tcfg = NULL;
547d6164b77SAlexander V. Chernikov 	if (gencnt != chain->gencnt)
548d6164b77SAlexander V. Chernikov 	    tcfg = lookup_nat_name(&chain->nat, ucfg->name);
549d6164b77SAlexander V. Chernikov 	IPFW_WLOCK(chain);
550d6164b77SAlexander V. Chernikov 	if (tcfg != NULL)
551d6164b77SAlexander V. Chernikov 		LIST_REMOVE(tcfg, _next);
5523b3a8eb9SGleb Smirnoff 	LIST_INSERT_HEAD(&chain->nat, ptr, _next);
5533b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(chain);
554d6164b77SAlexander V. Chernikov 	chain->gencnt++;
555d6164b77SAlexander V. Chernikov 
556d6164b77SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
557d6164b77SAlexander V. Chernikov 
558d6164b77SAlexander V. Chernikov 	if (tcfg != NULL)
5591a458088SAlexander V. Chernikov 		free_nat_instance(ptr);
560d6164b77SAlexander V. Chernikov }
561d6164b77SAlexander V. Chernikov 
562d6164b77SAlexander V. Chernikov /*
563d6164b77SAlexander V. Chernikov  * Creates/configure nat44 instance
564d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
565d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
566d6164b77SAlexander V. Chernikov  *
567d6164b77SAlexander V. Chernikov  * Returns 0 on success
568d6164b77SAlexander V. Chernikov  */
569d6164b77SAlexander V. Chernikov static int
570d6164b77SAlexander V. Chernikov nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
571d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
572d6164b77SAlexander V. Chernikov {
573d6164b77SAlexander V. Chernikov 	ipfw_obj_header *oh;
574d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
575d6164b77SAlexander V. Chernikov 	int id;
576d6164b77SAlexander V. Chernikov 	size_t read;
577d6164b77SAlexander V. Chernikov 	char *errptr;
578d6164b77SAlexander V. Chernikov 
579d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
580d6164b77SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg)))
581d6164b77SAlexander V. Chernikov 		return (EINVAL);
582d6164b77SAlexander V. Chernikov 
583d6164b77SAlexander V. Chernikov 	oh = (ipfw_obj_header *)sd->kbuf;
584d6164b77SAlexander V. Chernikov 
585d6164b77SAlexander V. Chernikov 	/* Basic length checks for TLVs */
586d6164b77SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
587d6164b77SAlexander V. Chernikov 		return (EINVAL);
588d6164b77SAlexander V. Chernikov 
589d6164b77SAlexander V. Chernikov 	ucfg = (struct nat44_cfg_nat *)(oh + 1);
590d6164b77SAlexander V. Chernikov 
591d6164b77SAlexander V. Chernikov 	/* Check if name is properly terminated and looks like number */
592d6164b77SAlexander V. Chernikov 	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
593d6164b77SAlexander V. Chernikov 		return (EINVAL);
594d6164b77SAlexander V. Chernikov 	id = strtol(ucfg->name, &errptr, 10);
595d6164b77SAlexander V. Chernikov 	if (id == 0 || *errptr != '\0')
596d6164b77SAlexander V. Chernikov 		return (EINVAL);
597d6164b77SAlexander V. Chernikov 
598d6164b77SAlexander V. Chernikov 	read = sizeof(*oh) + sizeof(*ucfg);
599d6164b77SAlexander V. Chernikov 	/* Check number of redirs */
600d6164b77SAlexander V. Chernikov 	if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir))
601d6164b77SAlexander V. Chernikov 		return (EINVAL);
602d6164b77SAlexander V. Chernikov 
603d6164b77SAlexander V. Chernikov 	nat44_config(chain, ucfg);
604d6164b77SAlexander V. Chernikov 	return (0);
605d6164b77SAlexander V. Chernikov }
606d6164b77SAlexander V. Chernikov 
607d6164b77SAlexander V. Chernikov /*
608d6164b77SAlexander V. Chernikov  * Destroys given nat instances.
609d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
610d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_header ]
611d6164b77SAlexander V. Chernikov  *
612d6164b77SAlexander V. Chernikov  * Returns 0 on success
613d6164b77SAlexander V. Chernikov  */
614d6164b77SAlexander V. Chernikov static int
615d6164b77SAlexander V. Chernikov nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
616d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
617d6164b77SAlexander V. Chernikov {
618d6164b77SAlexander V. Chernikov 	ipfw_obj_header *oh;
619d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr;
620d6164b77SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
621d6164b77SAlexander V. Chernikov 
622d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
623d6164b77SAlexander V. Chernikov 	if (sd->valsize < sizeof(*oh))
624d6164b77SAlexander V. Chernikov 		return (EINVAL);
625d6164b77SAlexander V. Chernikov 
626d6164b77SAlexander V. Chernikov 	oh = (ipfw_obj_header *)sd->kbuf;
627d6164b77SAlexander V. Chernikov 
628d6164b77SAlexander V. Chernikov 	/* Basic length checks for TLVs */
629d6164b77SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
630d6164b77SAlexander V. Chernikov 		return (EINVAL);
631d6164b77SAlexander V. Chernikov 
632d6164b77SAlexander V. Chernikov 	ntlv = &oh->ntlv;
633d6164b77SAlexander V. Chernikov 	/* Check if name is properly terminated */
634d6164b77SAlexander V. Chernikov 	if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name))
635d6164b77SAlexander V. Chernikov 		return (EINVAL);
636d6164b77SAlexander V. Chernikov 
637d6164b77SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
638d6164b77SAlexander V. Chernikov 	ptr = lookup_nat_name(&chain->nat, ntlv->name);
639d6164b77SAlexander V. Chernikov 	if (ptr == NULL) {
640d6164b77SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
641d6164b77SAlexander V. Chernikov 		return (ESRCH);
642d6164b77SAlexander V. Chernikov 	}
643d6164b77SAlexander V. Chernikov 	IPFW_WLOCK(chain);
644d6164b77SAlexander V. Chernikov 	LIST_REMOVE(ptr, _next);
645d6164b77SAlexander V. Chernikov 	flush_nat_ptrs(chain, ptr->id);
646d6164b77SAlexander V. Chernikov 	IPFW_WUNLOCK(chain);
647d6164b77SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
648d6164b77SAlexander V. Chernikov 
6491a458088SAlexander V. Chernikov 	free_nat_instance(ptr);
650d6164b77SAlexander V. Chernikov 
651d6164b77SAlexander V. Chernikov 	return (0);
652d6164b77SAlexander V. Chernikov }
653d6164b77SAlexander V. Chernikov 
654d6164b77SAlexander V. Chernikov static void
655d6164b77SAlexander V. Chernikov export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg)
656d6164b77SAlexander V. Chernikov {
657d6164b77SAlexander V. Chernikov 
658d6164b77SAlexander V. Chernikov 	snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id);
659d6164b77SAlexander V. Chernikov 	ucfg->ip = ptr->ip;
660d6164b77SAlexander V. Chernikov 	ucfg->redir_cnt = ptr->redir_cnt;
661d6164b77SAlexander V. Chernikov 	ucfg->mode = ptr->mode;
662d6164b77SAlexander V. Chernikov 	strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name));
663d6164b77SAlexander V. Chernikov }
664d6164b77SAlexander V. Chernikov 
665d6164b77SAlexander V. Chernikov /*
666d6164b77SAlexander V. Chernikov  * Gets config for given nat instance
667d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
668d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
669d6164b77SAlexander V. Chernikov  *
670d6164b77SAlexander V. Chernikov  * Returns 0 on success
671d6164b77SAlexander V. Chernikov  */
672d6164b77SAlexander V. Chernikov static int
673d6164b77SAlexander V. Chernikov nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
674d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
675d6164b77SAlexander V. Chernikov {
676d6164b77SAlexander V. Chernikov 	ipfw_obj_header *oh;
677d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
678d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr;
679d6164b77SAlexander V. Chernikov 	struct cfg_redir *r;
680d6164b77SAlexander V. Chernikov 	struct cfg_spool *s;
681d6164b77SAlexander V. Chernikov 	struct nat44_cfg_redir *ser_r;
682d6164b77SAlexander V. Chernikov 	struct nat44_cfg_spool *ser_s;
683d6164b77SAlexander V. Chernikov 	size_t sz;
684d6164b77SAlexander V. Chernikov 
685d6164b77SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(*ucfg);
686d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
687d6164b77SAlexander V. Chernikov 	if (sd->valsize < sz)
688d6164b77SAlexander V. Chernikov 		return (EINVAL);
689d6164b77SAlexander V. Chernikov 
690d6164b77SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
691d6164b77SAlexander V. Chernikov 
692d6164b77SAlexander V. Chernikov 	/* Basic length checks for TLVs */
693d6164b77SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
694d6164b77SAlexander V. Chernikov 		return (EINVAL);
695d6164b77SAlexander V. Chernikov 
696d6164b77SAlexander V. Chernikov 	ucfg = (struct nat44_cfg_nat *)(oh + 1);
697d6164b77SAlexander V. Chernikov 
698d6164b77SAlexander V. Chernikov 	/* Check if name is properly terminated */
699d6164b77SAlexander V. Chernikov 	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
700d6164b77SAlexander V. Chernikov 		return (EINVAL);
701d6164b77SAlexander V. Chernikov 
702d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
703d6164b77SAlexander V. Chernikov 	ptr = lookup_nat_name(&chain->nat, ucfg->name);
704d6164b77SAlexander V. Chernikov 	if (ptr == NULL) {
705d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
706d6164b77SAlexander V. Chernikov 		return (ESRCH);
707d6164b77SAlexander V. Chernikov 	}
708d6164b77SAlexander V. Chernikov 
709d6164b77SAlexander V. Chernikov 	export_nat_cfg(ptr, ucfg);
710d6164b77SAlexander V. Chernikov 
711d6164b77SAlexander V. Chernikov 	/* Estimate memory amount */
7129f925e8aSAlexander V. Chernikov 	sz = sizeof(ipfw_obj_header) + sizeof(struct nat44_cfg_nat);
713d6164b77SAlexander V. Chernikov 	LIST_FOREACH(r, &ptr->redir_chain, _next) {
714d6164b77SAlexander V. Chernikov 		sz += sizeof(struct nat44_cfg_redir);
715d6164b77SAlexander V. Chernikov 		LIST_FOREACH(s, &r->spool_chain, _next)
716d6164b77SAlexander V. Chernikov 			sz += sizeof(struct nat44_cfg_spool);
717d6164b77SAlexander V. Chernikov 	}
718d6164b77SAlexander V. Chernikov 
719d6164b77SAlexander V. Chernikov 	ucfg->size = sz;
7209f925e8aSAlexander V. Chernikov 	if (sd->valsize < sz) {
721d6164b77SAlexander V. Chernikov 
722d6164b77SAlexander V. Chernikov 		/*
723d6164b77SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
724d6164b77SAlexander V. Chernikov 		 * WE've already filled in @ucfg structure with
725d6164b77SAlexander V. Chernikov 		 * relevant info including size, so we
726d6164b77SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
727d6164b77SAlexander V. Chernikov 		 */
728d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
729d6164b77SAlexander V. Chernikov 		return (ENOMEM);
730d6164b77SAlexander V. Chernikov 	}
731d6164b77SAlexander V. Chernikov 
732d6164b77SAlexander V. Chernikov 	/* Size OK, let's copy data */
733d6164b77SAlexander V. Chernikov 	LIST_FOREACH(r, &ptr->redir_chain, _next) {
734d6164b77SAlexander V. Chernikov 		ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd,
735d6164b77SAlexander V. Chernikov 		    sizeof(*ser_r));
736d6164b77SAlexander V. Chernikov 		ser_r->mode = r->mode;
737d6164b77SAlexander V. Chernikov 		ser_r->laddr = r->laddr;
738d6164b77SAlexander V. Chernikov 		ser_r->paddr = r->paddr;
739d6164b77SAlexander V. Chernikov 		ser_r->raddr = r->raddr;
740d6164b77SAlexander V. Chernikov 		ser_r->lport = r->lport;
741d6164b77SAlexander V. Chernikov 		ser_r->pport = r->pport;
742d6164b77SAlexander V. Chernikov 		ser_r->rport = r->rport;
743d6164b77SAlexander V. Chernikov 		ser_r->pport_cnt = r->pport_cnt;
744d6164b77SAlexander V. Chernikov 		ser_r->rport_cnt = r->rport_cnt;
745d6164b77SAlexander V. Chernikov 		ser_r->proto = r->proto;
746d6164b77SAlexander V. Chernikov 		ser_r->spool_cnt = r->spool_cnt;
747d6164b77SAlexander V. Chernikov 
748d6164b77SAlexander V. Chernikov 		LIST_FOREACH(s, &r->spool_chain, _next) {
749d6164b77SAlexander V. Chernikov 			ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space(
750d6164b77SAlexander V. Chernikov 			    sd, sizeof(*ser_s));
751d6164b77SAlexander V. Chernikov 
752d6164b77SAlexander V. Chernikov 			ser_s->addr = s->addr;
753d6164b77SAlexander V. Chernikov 			ser_s->port = s->port;
754d6164b77SAlexander V. Chernikov 		}
755d6164b77SAlexander V. Chernikov 	}
756d6164b77SAlexander V. Chernikov 
757d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
758d6164b77SAlexander V. Chernikov 
759d6164b77SAlexander V. Chernikov 	return (0);
760d6164b77SAlexander V. Chernikov }
761d6164b77SAlexander V. Chernikov 
762d6164b77SAlexander V. Chernikov /*
763d6164b77SAlexander V. Chernikov  * Lists all nat44 instances currently available in kernel.
764d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
765d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ]
766d6164b77SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ]
767d6164b77SAlexander V. Chernikov  *
768d6164b77SAlexander V. Chernikov  * Returns 0 on success
769d6164b77SAlexander V. Chernikov  */
770d6164b77SAlexander V. Chernikov static int
771d6164b77SAlexander V. Chernikov nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
772d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
773d6164b77SAlexander V. Chernikov {
774d6164b77SAlexander V. Chernikov 	ipfw_obj_lheader *olh;
775d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
776d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr;
777d6164b77SAlexander V. Chernikov 	int nat_count;
778d6164b77SAlexander V. Chernikov 
779d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
780d6164b77SAlexander V. Chernikov 	if (sd->valsize < sizeof(ipfw_obj_lheader))
781d6164b77SAlexander V. Chernikov 		return (EINVAL);
782d6164b77SAlexander V. Chernikov 
783d6164b77SAlexander V. Chernikov 	olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
784d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
785d6164b77SAlexander V. Chernikov 	nat_count = 0;
786d6164b77SAlexander V. Chernikov 	LIST_FOREACH(ptr, &chain->nat, _next)
787d6164b77SAlexander V. Chernikov 		nat_count++;
788d6164b77SAlexander V. Chernikov 
789d6164b77SAlexander V. Chernikov 	olh->count = nat_count;
790d6164b77SAlexander V. Chernikov 	olh->objsize = sizeof(struct nat44_cfg_nat);
791d6164b77SAlexander V. Chernikov 	olh->size = sizeof(*olh) + olh->count * olh->objsize;
792d6164b77SAlexander V. Chernikov 
793d6164b77SAlexander V. Chernikov 	if (sd->valsize < olh->size) {
794d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
795d6164b77SAlexander V. Chernikov 		return (ENOMEM);
796d6164b77SAlexander V. Chernikov 	}
797d6164b77SAlexander V. Chernikov 
798d6164b77SAlexander V. Chernikov 	LIST_FOREACH(ptr, &chain->nat, _next) {
799d6164b77SAlexander V. Chernikov 		ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd,
800d6164b77SAlexander V. Chernikov 		    sizeof(*ucfg));
801d6164b77SAlexander V. Chernikov 		export_nat_cfg(ptr, ucfg);
802d6164b77SAlexander V. Chernikov 	}
803d6164b77SAlexander V. Chernikov 
804d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
805d6164b77SAlexander V. Chernikov 
806d6164b77SAlexander V. Chernikov 	return (0);
807d6164b77SAlexander V. Chernikov }
808d6164b77SAlexander V. Chernikov 
809d6164b77SAlexander V. Chernikov /*
810d6164b77SAlexander V. Chernikov  * Gets log for given nat instance
811d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
812d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_header nat44_cfg_nat ]
813d6164b77SAlexander V. Chernikov  * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ]
814d6164b77SAlexander V. Chernikov  *
815d6164b77SAlexander V. Chernikov  * Returns 0 on success
816d6164b77SAlexander V. Chernikov  */
817d6164b77SAlexander V. Chernikov static int
818d6164b77SAlexander V. Chernikov nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
819d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
820d6164b77SAlexander V. Chernikov {
821d6164b77SAlexander V. Chernikov 	ipfw_obj_header *oh;
822d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
823d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr;
824d6164b77SAlexander V. Chernikov 	void *pbuf;
825d6164b77SAlexander V. Chernikov 	size_t sz;
826d6164b77SAlexander V. Chernikov 
827d6164b77SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(*ucfg);
828d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
829d6164b77SAlexander V. Chernikov 	if (sd->valsize < sz)
830d6164b77SAlexander V. Chernikov 		return (EINVAL);
831d6164b77SAlexander V. Chernikov 
832d6164b77SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
833d6164b77SAlexander V. Chernikov 
834d6164b77SAlexander V. Chernikov 	/* Basic length checks for TLVs */
835d6164b77SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
836d6164b77SAlexander V. Chernikov 		return (EINVAL);
837d6164b77SAlexander V. Chernikov 
838d6164b77SAlexander V. Chernikov 	ucfg = (struct nat44_cfg_nat *)(oh + 1);
839d6164b77SAlexander V. Chernikov 
840d6164b77SAlexander V. Chernikov 	/* Check if name is properly terminated */
841d6164b77SAlexander V. Chernikov 	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
842d6164b77SAlexander V. Chernikov 		return (EINVAL);
843d6164b77SAlexander V. Chernikov 
844d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
845d6164b77SAlexander V. Chernikov 	ptr = lookup_nat_name(&chain->nat, ucfg->name);
846d6164b77SAlexander V. Chernikov 	if (ptr == NULL) {
847d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
848d6164b77SAlexander V. Chernikov 		return (ESRCH);
849d6164b77SAlexander V. Chernikov 	}
850d6164b77SAlexander V. Chernikov 
851d6164b77SAlexander V. Chernikov 	if (ptr->lib->logDesc == NULL) {
852d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
853d6164b77SAlexander V. Chernikov 		return (ENOENT);
854d6164b77SAlexander V. Chernikov 	}
855d6164b77SAlexander V. Chernikov 
856d6164b77SAlexander V. Chernikov 	export_nat_cfg(ptr, ucfg);
857d6164b77SAlexander V. Chernikov 
858d6164b77SAlexander V. Chernikov 	/* Estimate memory amount */
859d6164b77SAlexander V. Chernikov 	ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE;
860d6164b77SAlexander V. Chernikov 	if (sd->valsize < sz + sizeof(*oh)) {
861d6164b77SAlexander V. Chernikov 
862d6164b77SAlexander V. Chernikov 		/*
863d6164b77SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
864d6164b77SAlexander V. Chernikov 		 * WE've already filled in @ucfg structure with
865d6164b77SAlexander V. Chernikov 		 * relevant info including size, so we
866d6164b77SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
867d6164b77SAlexander V. Chernikov 		 */
868d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
869d6164b77SAlexander V. Chernikov 		return (ENOMEM);
870d6164b77SAlexander V. Chernikov 	}
871d6164b77SAlexander V. Chernikov 
872d6164b77SAlexander V. Chernikov 	pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE);
873d6164b77SAlexander V. Chernikov 	memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE);
874d6164b77SAlexander V. Chernikov 
875d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
876d6164b77SAlexander V. Chernikov 
877d6164b77SAlexander V. Chernikov 	return (0);
878d6164b77SAlexander V. Chernikov }
879d6164b77SAlexander V. Chernikov 
880d6164b77SAlexander V. Chernikov static struct ipfw_sopt_handler	scodes[] = {
881d6164b77SAlexander V. Chernikov 	{ IP_FW_NAT44_XCONFIG,	0,	HDIR_SET,	nat44_cfg },
882d6164b77SAlexander V. Chernikov 	{ IP_FW_NAT44_DESTROY,	0,	HDIR_SET,	nat44_destroy },
883d6164b77SAlexander V. Chernikov 	{ IP_FW_NAT44_XGETCONFIG,	0,	HDIR_GET,	nat44_get_cfg },
884d6164b77SAlexander V. Chernikov 	{ IP_FW_NAT44_LIST_NAT,	0,	HDIR_GET,	nat44_list_nat },
885d6164b77SAlexander V. Chernikov 	{ IP_FW_NAT44_XGETLOG,	0,	HDIR_GET,	nat44_get_log },
886d6164b77SAlexander V. Chernikov };
887d6164b77SAlexander V. Chernikov 
888d6164b77SAlexander V. Chernikov 
889d6164b77SAlexander V. Chernikov /*
890d6164b77SAlexander V. Chernikov  * Legacy configuration routines
891d6164b77SAlexander V. Chernikov  */
892d6164b77SAlexander V. Chernikov 
893d6164b77SAlexander V. Chernikov struct cfg_spool_legacy {
894d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_spool_legacy)	_next;
895d6164b77SAlexander V. Chernikov 	struct in_addr			addr;
896d6164b77SAlexander V. Chernikov 	u_short				port;
897d6164b77SAlexander V. Chernikov };
898d6164b77SAlexander V. Chernikov 
899d6164b77SAlexander V. Chernikov struct cfg_redir_legacy {
900d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_redir)   _next;
901d6164b77SAlexander V. Chernikov 	u_int16_t               mode;
902d6164b77SAlexander V. Chernikov 	struct in_addr	        laddr;
903d6164b77SAlexander V. Chernikov 	struct in_addr	        paddr;
904d6164b77SAlexander V. Chernikov 	struct in_addr	        raddr;
905d6164b77SAlexander V. Chernikov 	u_short                 lport;
906d6164b77SAlexander V. Chernikov 	u_short                 pport;
907d6164b77SAlexander V. Chernikov 	u_short                 rport;
908d6164b77SAlexander V. Chernikov 	u_short                 pport_cnt;
909d6164b77SAlexander V. Chernikov 	u_short                 rport_cnt;
910d6164b77SAlexander V. Chernikov 	int                     proto;
911d6164b77SAlexander V. Chernikov 	struct alias_link       **alink;
912d6164b77SAlexander V. Chernikov 	u_int16_t               spool_cnt;
913d6164b77SAlexander V. Chernikov 	LIST_HEAD(, cfg_spool_legacy) spool_chain;
914d6164b77SAlexander V. Chernikov };
915d6164b77SAlexander V. Chernikov 
916d6164b77SAlexander V. Chernikov struct cfg_nat_legacy {
917d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_nat_legacy)	_next;
918d6164b77SAlexander V. Chernikov 	int				id;
919d6164b77SAlexander V. Chernikov 	struct in_addr			ip;
920d6164b77SAlexander V. Chernikov 	char				if_name[IF_NAMESIZE];
921d6164b77SAlexander V. Chernikov 	int				mode;
922d6164b77SAlexander V. Chernikov 	struct libalias			*lib;
923d6164b77SAlexander V. Chernikov 	int				redir_cnt;
924d6164b77SAlexander V. Chernikov 	LIST_HEAD(, cfg_redir_legacy)	redir_chain;
925d6164b77SAlexander V. Chernikov };
926d6164b77SAlexander V. Chernikov 
927d6164b77SAlexander V. Chernikov static int
928d6164b77SAlexander V. Chernikov ipfw_nat_cfg(struct sockopt *sopt)
929d6164b77SAlexander V. Chernikov {
930d6164b77SAlexander V. Chernikov 	struct cfg_nat_legacy *cfg;
931d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
932d6164b77SAlexander V. Chernikov 	struct cfg_redir_legacy *rdir;
933d6164b77SAlexander V. Chernikov 	struct nat44_cfg_redir *urdir;
934d6164b77SAlexander V. Chernikov 	char *buf;
935d6164b77SAlexander V. Chernikov 	size_t len, len2;
936d6164b77SAlexander V. Chernikov 	int error, i;
937d6164b77SAlexander V. Chernikov 
938d6164b77SAlexander V. Chernikov 	len = sopt->sopt_valsize;
939d6164b77SAlexander V. Chernikov 	len2 = len + 128;
940d6164b77SAlexander V. Chernikov 
941d6164b77SAlexander V. Chernikov 	/*
942d6164b77SAlexander V. Chernikov 	 * Allocate 2x buffer to store converted structures.
943a4641f4eSPedro F. Giffuni 	 * new redir_cfg has shrunk, so we're sure that
944d6164b77SAlexander V. Chernikov 	 * new buffer size is enough.
945d6164b77SAlexander V. Chernikov 	 */
946d6164b77SAlexander V. Chernikov 	buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO);
947d6164b77SAlexander V. Chernikov 	error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy));
948d6164b77SAlexander V. Chernikov 	if (error != 0)
949d6164b77SAlexander V. Chernikov 		goto out;
950d6164b77SAlexander V. Chernikov 
951d6164b77SAlexander V. Chernikov 	cfg = (struct cfg_nat_legacy *)buf;
952d6164b77SAlexander V. Chernikov 	if (cfg->id < 0) {
953d6164b77SAlexander V. Chernikov 		error = EINVAL;
954d6164b77SAlexander V. Chernikov 		goto out;
955d6164b77SAlexander V. Chernikov 	}
956d6164b77SAlexander V. Chernikov 
957d6164b77SAlexander V. Chernikov 	ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)];
958d6164b77SAlexander V. Chernikov 	snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id);
959d6164b77SAlexander V. Chernikov 	strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name));
960d6164b77SAlexander V. Chernikov 	ucfg->ip = cfg->ip;
961d6164b77SAlexander V. Chernikov 	ucfg->mode = cfg->mode;
962d6164b77SAlexander V. Chernikov 	ucfg->redir_cnt = cfg->redir_cnt;
963d6164b77SAlexander V. Chernikov 
964d6164b77SAlexander V. Chernikov 	if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) {
965d6164b77SAlexander V. Chernikov 		error = EINVAL;
966d6164b77SAlexander V. Chernikov 		goto out;
967d6164b77SAlexander V. Chernikov 	}
968d6164b77SAlexander V. Chernikov 
969d6164b77SAlexander V. Chernikov 	urdir = (struct nat44_cfg_redir *)(ucfg + 1);
970d6164b77SAlexander V. Chernikov 	rdir = (struct cfg_redir_legacy *)(cfg + 1);
971d6164b77SAlexander V. Chernikov 	for (i = 0; i < cfg->redir_cnt; i++) {
972d6164b77SAlexander V. Chernikov 		urdir->mode = rdir->mode;
973d6164b77SAlexander V. Chernikov 		urdir->laddr = rdir->laddr;
974d6164b77SAlexander V. Chernikov 		urdir->paddr = rdir->paddr;
975d6164b77SAlexander V. Chernikov 		urdir->raddr = rdir->raddr;
976d6164b77SAlexander V. Chernikov 		urdir->lport = rdir->lport;
977d6164b77SAlexander V. Chernikov 		urdir->pport = rdir->pport;
978d6164b77SAlexander V. Chernikov 		urdir->rport = rdir->rport;
979d6164b77SAlexander V. Chernikov 		urdir->pport_cnt = rdir->pport_cnt;
980d6164b77SAlexander V. Chernikov 		urdir->rport_cnt = rdir->rport_cnt;
981d6164b77SAlexander V. Chernikov 		urdir->proto = rdir->proto;
982d6164b77SAlexander V. Chernikov 		urdir->spool_cnt = rdir->spool_cnt;
983d6164b77SAlexander V. Chernikov 
984d6164b77SAlexander V. Chernikov 		urdir++;
985d6164b77SAlexander V. Chernikov 		rdir++;
986d6164b77SAlexander V. Chernikov 	}
987d6164b77SAlexander V. Chernikov 
988d6164b77SAlexander V. Chernikov 	nat44_config(&V_layer3_chain, ucfg);
9893b3a8eb9SGleb Smirnoff 
9903b3a8eb9SGleb Smirnoff out:
9913b3a8eb9SGleb Smirnoff 	free(buf, M_TEMP);
9923b3a8eb9SGleb Smirnoff 	return (error);
9933b3a8eb9SGleb Smirnoff }
9943b3a8eb9SGleb Smirnoff 
9953b3a8eb9SGleb Smirnoff static int
9963b3a8eb9SGleb Smirnoff ipfw_nat_del(struct sockopt *sopt)
9973b3a8eb9SGleb Smirnoff {
9983b3a8eb9SGleb Smirnoff 	struct cfg_nat *ptr;
9993b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain = &V_layer3_chain;
10003b3a8eb9SGleb Smirnoff 	int i;
10013b3a8eb9SGleb Smirnoff 
10023b3a8eb9SGleb Smirnoff 	sooptcopyin(sopt, &i, sizeof i, sizeof i);
10033b3a8eb9SGleb Smirnoff 	/* XXX validate i */
1004d6164b77SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
10053b3a8eb9SGleb Smirnoff 	ptr = lookup_nat(&chain->nat, i);
10063b3a8eb9SGleb Smirnoff 	if (ptr == NULL) {
1007d6164b77SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
10083b3a8eb9SGleb Smirnoff 		return (EINVAL);
10093b3a8eb9SGleb Smirnoff 	}
1010d6164b77SAlexander V. Chernikov 	IPFW_WLOCK(chain);
10113b3a8eb9SGleb Smirnoff 	LIST_REMOVE(ptr, _next);
10123b3a8eb9SGleb Smirnoff 	flush_nat_ptrs(chain, i);
10133b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(chain);
1014d6164b77SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
10151a458088SAlexander V. Chernikov 	free_nat_instance(ptr);
10163b3a8eb9SGleb Smirnoff 	return (0);
10173b3a8eb9SGleb Smirnoff }
10183b3a8eb9SGleb Smirnoff 
10193b3a8eb9SGleb Smirnoff static int
10203b3a8eb9SGleb Smirnoff ipfw_nat_get_cfg(struct sockopt *sopt)
10213b3a8eb9SGleb Smirnoff {
10223b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain = &V_layer3_chain;
10233b3a8eb9SGleb Smirnoff 	struct cfg_nat *n;
1024d6164b77SAlexander V. Chernikov 	struct cfg_nat_legacy *ucfg;
10253b3a8eb9SGleb Smirnoff 	struct cfg_redir *r;
10263b3a8eb9SGleb Smirnoff 	struct cfg_spool *s;
1027d6164b77SAlexander V. Chernikov 	struct cfg_redir_legacy *ser_r;
1028d6164b77SAlexander V. Chernikov 	struct cfg_spool_legacy *ser_s;
10293b3a8eb9SGleb Smirnoff 	char *data;
10303b3a8eb9SGleb Smirnoff 	int gencnt, nat_cnt, len, error;
10313b3a8eb9SGleb Smirnoff 
10323b3a8eb9SGleb Smirnoff 	nat_cnt = 0;
10333b3a8eb9SGleb Smirnoff 	len = sizeof(nat_cnt);
10343b3a8eb9SGleb Smirnoff 
1035d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
10363b3a8eb9SGleb Smirnoff retry:
10373b3a8eb9SGleb Smirnoff 	gencnt = chain->gencnt;
10383b3a8eb9SGleb Smirnoff 	/* Estimate memory amount */
10393b3a8eb9SGleb Smirnoff 	LIST_FOREACH(n, &chain->nat, _next) {
10403b3a8eb9SGleb Smirnoff 		nat_cnt++;
1041d6164b77SAlexander V. Chernikov 		len += sizeof(struct cfg_nat_legacy);
10423b3a8eb9SGleb Smirnoff 		LIST_FOREACH(r, &n->redir_chain, _next) {
1043d6164b77SAlexander V. Chernikov 			len += sizeof(struct cfg_redir_legacy);
10443b3a8eb9SGleb Smirnoff 			LIST_FOREACH(s, &r->spool_chain, _next)
1045d6164b77SAlexander V. Chernikov 				len += sizeof(struct cfg_spool_legacy);
10463b3a8eb9SGleb Smirnoff 		}
10473b3a8eb9SGleb Smirnoff 	}
1048d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
10493b3a8eb9SGleb Smirnoff 
10503b3a8eb9SGleb Smirnoff 	data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
10513b3a8eb9SGleb Smirnoff 	bcopy(&nat_cnt, data, sizeof(nat_cnt));
10523b3a8eb9SGleb Smirnoff 
10533b3a8eb9SGleb Smirnoff 	nat_cnt = 0;
10543b3a8eb9SGleb Smirnoff 	len = sizeof(nat_cnt);
10553b3a8eb9SGleb Smirnoff 
1056d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
10573b3a8eb9SGleb Smirnoff 	if (gencnt != chain->gencnt) {
10583b3a8eb9SGleb Smirnoff 		free(data, M_TEMP);
10593b3a8eb9SGleb Smirnoff 		goto retry;
10603b3a8eb9SGleb Smirnoff 	}
10613b3a8eb9SGleb Smirnoff 	/* Serialize all the data. */
10623b3a8eb9SGleb Smirnoff 	LIST_FOREACH(n, &chain->nat, _next) {
1063d6164b77SAlexander V. Chernikov 		ucfg = (struct cfg_nat_legacy *)&data[len];
1064d6164b77SAlexander V. Chernikov 		ucfg->id = n->id;
1065d6164b77SAlexander V. Chernikov 		ucfg->ip = n->ip;
1066d6164b77SAlexander V. Chernikov 		ucfg->redir_cnt = n->redir_cnt;
1067d6164b77SAlexander V. Chernikov 		ucfg->mode = n->mode;
1068d6164b77SAlexander V. Chernikov 		strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name));
1069d6164b77SAlexander V. Chernikov 		len += sizeof(struct cfg_nat_legacy);
10703b3a8eb9SGleb Smirnoff 		LIST_FOREACH(r, &n->redir_chain, _next) {
1071d6164b77SAlexander V. Chernikov 			ser_r = (struct cfg_redir_legacy *)&data[len];
1072d6164b77SAlexander V. Chernikov 			ser_r->mode = r->mode;
1073d6164b77SAlexander V. Chernikov 			ser_r->laddr = r->laddr;
1074d6164b77SAlexander V. Chernikov 			ser_r->paddr = r->paddr;
1075d6164b77SAlexander V. Chernikov 			ser_r->raddr = r->raddr;
1076d6164b77SAlexander V. Chernikov 			ser_r->lport = r->lport;
1077d6164b77SAlexander V. Chernikov 			ser_r->pport = r->pport;
1078d6164b77SAlexander V. Chernikov 			ser_r->rport = r->rport;
1079d6164b77SAlexander V. Chernikov 			ser_r->pport_cnt = r->pport_cnt;
1080d6164b77SAlexander V. Chernikov 			ser_r->rport_cnt = r->rport_cnt;
1081d6164b77SAlexander V. Chernikov 			ser_r->proto = r->proto;
1082d6164b77SAlexander V. Chernikov 			ser_r->spool_cnt = r->spool_cnt;
1083d6164b77SAlexander V. Chernikov 			len += sizeof(struct cfg_redir_legacy);
10843b3a8eb9SGleb Smirnoff 			LIST_FOREACH(s, &r->spool_chain, _next) {
1085d6164b77SAlexander V. Chernikov 				ser_s = (struct cfg_spool_legacy *)&data[len];
1086d6164b77SAlexander V. Chernikov 				ser_s->addr = s->addr;
1087d6164b77SAlexander V. Chernikov 				ser_s->port = s->port;
1088d6164b77SAlexander V. Chernikov 				len += sizeof(struct cfg_spool_legacy);
10893b3a8eb9SGleb Smirnoff 			}
10903b3a8eb9SGleb Smirnoff 		}
10913b3a8eb9SGleb Smirnoff 	}
1092d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
10933b3a8eb9SGleb Smirnoff 
10943b3a8eb9SGleb Smirnoff 	error = sooptcopyout(sopt, data, len);
10953b3a8eb9SGleb Smirnoff 	free(data, M_TEMP);
10963b3a8eb9SGleb Smirnoff 
10973b3a8eb9SGleb Smirnoff 	return (error);
10983b3a8eb9SGleb Smirnoff }
10993b3a8eb9SGleb Smirnoff 
11003b3a8eb9SGleb Smirnoff static int
11013b3a8eb9SGleb Smirnoff ipfw_nat_get_log(struct sockopt *sopt)
11023b3a8eb9SGleb Smirnoff {
11033b3a8eb9SGleb Smirnoff 	uint8_t *data;
11043b3a8eb9SGleb Smirnoff 	struct cfg_nat *ptr;
11053b3a8eb9SGleb Smirnoff 	int i, size;
11063b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain;
1107ccba94b8SAlexander V. Chernikov 	IPFW_RLOCK_TRACKER;
11083b3a8eb9SGleb Smirnoff 
11093b3a8eb9SGleb Smirnoff 	chain = &V_layer3_chain;
11103b3a8eb9SGleb Smirnoff 
11113b3a8eb9SGleb Smirnoff 	IPFW_RLOCK(chain);
11123b3a8eb9SGleb Smirnoff 	/* one pass to count, one to copy the data */
11133b3a8eb9SGleb Smirnoff 	i = 0;
11143b3a8eb9SGleb Smirnoff 	LIST_FOREACH(ptr, &chain->nat, _next) {
11153b3a8eb9SGleb Smirnoff 		if (ptr->lib->logDesc == NULL)
11163b3a8eb9SGleb Smirnoff 			continue;
11173b3a8eb9SGleb Smirnoff 		i++;
11183b3a8eb9SGleb Smirnoff 	}
11193b3a8eb9SGleb Smirnoff 	size = i * (LIBALIAS_BUF_SIZE + sizeof(int));
11203b3a8eb9SGleb Smirnoff 	data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO);
11213b3a8eb9SGleb Smirnoff 	if (data == NULL) {
11223b3a8eb9SGleb Smirnoff 		IPFW_RUNLOCK(chain);
11233b3a8eb9SGleb Smirnoff 		return (ENOSPC);
11243b3a8eb9SGleb Smirnoff 	}
11253b3a8eb9SGleb Smirnoff 	i = 0;
11263b3a8eb9SGleb Smirnoff 	LIST_FOREACH(ptr, &chain->nat, _next) {
11273b3a8eb9SGleb Smirnoff 		if (ptr->lib->logDesc == NULL)
11283b3a8eb9SGleb Smirnoff 			continue;
11293b3a8eb9SGleb Smirnoff 		bcopy(&ptr->id, &data[i], sizeof(int));
11303b3a8eb9SGleb Smirnoff 		i += sizeof(int);
11313b3a8eb9SGleb Smirnoff 		bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE);
11323b3a8eb9SGleb Smirnoff 		i += LIBALIAS_BUF_SIZE;
11333b3a8eb9SGleb Smirnoff 	}
11343b3a8eb9SGleb Smirnoff 	IPFW_RUNLOCK(chain);
11353b3a8eb9SGleb Smirnoff 	sooptcopyout(sopt, data, size);
11363b3a8eb9SGleb Smirnoff 	free(data, M_IPFW);
11373b3a8eb9SGleb Smirnoff 	return(0);
11383b3a8eb9SGleb Smirnoff }
11393b3a8eb9SGleb Smirnoff 
11408856400bSMikolaj Golub static int
11418856400bSMikolaj Golub vnet_ipfw_nat_init(const void *arg __unused)
11423b3a8eb9SGleb Smirnoff {
11433b3a8eb9SGleb Smirnoff 
11448856400bSMikolaj Golub 	V_ipfw_nat_ready = 1;
11458856400bSMikolaj Golub 	return (0);
11463b3a8eb9SGleb Smirnoff }
11473b3a8eb9SGleb Smirnoff 
11488856400bSMikolaj Golub static int
11498856400bSMikolaj Golub vnet_ipfw_nat_uninit(const void *arg __unused)
11503b3a8eb9SGleb Smirnoff {
11513b3a8eb9SGleb Smirnoff 	struct cfg_nat *ptr, *ptr_temp;
11523b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain;
11533b3a8eb9SGleb Smirnoff 
11543b3a8eb9SGleb Smirnoff 	chain = &V_layer3_chain;
11553b3a8eb9SGleb Smirnoff 	IPFW_WLOCK(chain);
11569ac51e79SBjoern A. Zeeb 	V_ipfw_nat_ready = 0;
11573b3a8eb9SGleb Smirnoff 	LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) {
11583b3a8eb9SGleb Smirnoff 		LIST_REMOVE(ptr, _next);
11591a458088SAlexander V. Chernikov 		free_nat_instance(ptr);
11603b3a8eb9SGleb Smirnoff 	}
11613b3a8eb9SGleb Smirnoff 	flush_nat_ptrs(chain, -1 /* flush all */);
11628856400bSMikolaj Golub 	IPFW_WUNLOCK(chain);
11638856400bSMikolaj Golub 	return (0);
11648856400bSMikolaj Golub }
11658856400bSMikolaj Golub 
11668856400bSMikolaj Golub static void
11678856400bSMikolaj Golub ipfw_nat_init(void)
11688856400bSMikolaj Golub {
11698856400bSMikolaj Golub 
11708856400bSMikolaj Golub 	/* init ipfw hooks */
11718856400bSMikolaj Golub 	ipfw_nat_ptr = ipfw_nat;
11728856400bSMikolaj Golub 	lookup_nat_ptr = lookup_nat;
11738856400bSMikolaj Golub 	ipfw_nat_cfg_ptr = ipfw_nat_cfg;
11748856400bSMikolaj Golub 	ipfw_nat_del_ptr = ipfw_nat_del;
11758856400bSMikolaj Golub 	ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
11768856400bSMikolaj Golub 	ipfw_nat_get_log_ptr = ipfw_nat_get_log;
1177d6164b77SAlexander V. Chernikov 	IPFW_ADD_SOPT_HANDLER(1, scodes);
11788856400bSMikolaj Golub 
11798856400bSMikolaj Golub 	ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
11808856400bSMikolaj Golub 	    NULL, EVENTHANDLER_PRI_ANY);
11818856400bSMikolaj Golub }
11828856400bSMikolaj Golub 
11838856400bSMikolaj Golub static void
11848856400bSMikolaj Golub ipfw_nat_destroy(void)
11858856400bSMikolaj Golub {
11868856400bSMikolaj Golub 
11878856400bSMikolaj Golub 	EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
11883b3a8eb9SGleb Smirnoff 	/* deregister ipfw_nat */
1189d6164b77SAlexander V. Chernikov 	IPFW_DEL_SOPT_HANDLER(1, scodes);
11903b3a8eb9SGleb Smirnoff 	ipfw_nat_ptr = NULL;
11913b3a8eb9SGleb Smirnoff 	lookup_nat_ptr = NULL;
11923b3a8eb9SGleb Smirnoff 	ipfw_nat_cfg_ptr = NULL;
11933b3a8eb9SGleb Smirnoff 	ipfw_nat_del_ptr = NULL;
11943b3a8eb9SGleb Smirnoff 	ipfw_nat_get_cfg_ptr = NULL;
11953b3a8eb9SGleb Smirnoff 	ipfw_nat_get_log_ptr = NULL;
11963b3a8eb9SGleb Smirnoff }
11973b3a8eb9SGleb Smirnoff 
11983b3a8eb9SGleb Smirnoff static int
11993b3a8eb9SGleb Smirnoff ipfw_nat_modevent(module_t mod, int type, void *unused)
12003b3a8eb9SGleb Smirnoff {
12013b3a8eb9SGleb Smirnoff 	int err = 0;
12023b3a8eb9SGleb Smirnoff 
12033b3a8eb9SGleb Smirnoff 	switch (type) {
12043b3a8eb9SGleb Smirnoff 	case MOD_LOAD:
12053b3a8eb9SGleb Smirnoff 		break;
12063b3a8eb9SGleb Smirnoff 
12073b3a8eb9SGleb Smirnoff 	case MOD_UNLOAD:
12083b3a8eb9SGleb Smirnoff 		break;
12093b3a8eb9SGleb Smirnoff 
12103b3a8eb9SGleb Smirnoff 	default:
12113b3a8eb9SGleb Smirnoff 		return EOPNOTSUPP;
12123b3a8eb9SGleb Smirnoff 		break;
12133b3a8eb9SGleb Smirnoff 	}
12143b3a8eb9SGleb Smirnoff 	return err;
12153b3a8eb9SGleb Smirnoff }
12163b3a8eb9SGleb Smirnoff 
12173b3a8eb9SGleb Smirnoff static moduledata_t ipfw_nat_mod = {
12183b3a8eb9SGleb Smirnoff 	"ipfw_nat",
12193b3a8eb9SGleb Smirnoff 	ipfw_nat_modevent,
12209823d527SKevin Lo 	0
12213b3a8eb9SGleb Smirnoff };
12223b3a8eb9SGleb Smirnoff 
12238856400bSMikolaj Golub /* Define startup order. */
122489856f7eSBjoern A. Zeeb #define	IPFW_NAT_SI_SUB_FIREWALL	SI_SUB_PROTO_FIREWALL
12258a477d48SMikolaj Golub #define	IPFW_NAT_MODEVENT_ORDER		(SI_ORDER_ANY - 128) /* after ipfw */
12268856400bSMikolaj Golub #define	IPFW_NAT_MODULE_ORDER		(IPFW_NAT_MODEVENT_ORDER + 1)
12278856400bSMikolaj Golub #define	IPFW_NAT_VNET_ORDER		(IPFW_NAT_MODEVENT_ORDER + 2)
12288856400bSMikolaj Golub 
12298856400bSMikolaj Golub DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY);
12303b3a8eb9SGleb Smirnoff MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1);
1231f9ab623bSAlexander V. Chernikov MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3);
12323b3a8eb9SGleb Smirnoff MODULE_VERSION(ipfw_nat, 1);
12338856400bSMikolaj Golub 
12348856400bSMikolaj Golub SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
12358856400bSMikolaj Golub     ipfw_nat_init, NULL);
12368856400bSMikolaj Golub VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER,
12378856400bSMikolaj Golub     vnet_ipfw_nat_init, NULL);
12388856400bSMikolaj Golub 
12398856400bSMikolaj Golub SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
12408856400bSMikolaj Golub     ipfw_nat_destroy, NULL);
12418856400bSMikolaj Golub VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL,
12428856400bSMikolaj Golub     IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL);
12438856400bSMikolaj Golub 
12443b3a8eb9SGleb Smirnoff /* end of file */
1245