xref: /freebsd/sys/netpfil/ipfw/ip_fw_nat.c (revision 4a77657cbc011ea657ccb079fff6b58b295eccb0)
13b3a8eb9SGleb Smirnoff /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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/param.h>
303b3a8eb9SGleb Smirnoff #include <sys/systm.h>
313b3a8eb9SGleb Smirnoff #include <sys/eventhandler.h>
323b3a8eb9SGleb Smirnoff #include <sys/malloc.h>
3376039bc8SGleb Smirnoff #include <sys/mbuf.h>
343b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
353b3a8eb9SGleb Smirnoff #include <sys/lock.h>
363b3a8eb9SGleb Smirnoff #include <sys/module.h>
373b3a8eb9SGleb Smirnoff #include <sys/rwlock.h>
38ccba94b8SAlexander V. Chernikov #include <sys/rmlock.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>
453d0d5b21SJustin Hibbits #include <net/if_private.h>
463b3a8eb9SGleb Smirnoff #include <netinet/in.h>
473b3a8eb9SGleb Smirnoff #include <netinet/ip.h>
483b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h>
493b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h>
503b3a8eb9SGleb Smirnoff #include <netinet/tcp.h>
513b3a8eb9SGleb Smirnoff #include <netinet/udp.h>
523b3a8eb9SGleb Smirnoff 
533b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h>
543b3a8eb9SGleb Smirnoff 
553b3a8eb9SGleb Smirnoff #include <machine/in_cksum.h>	/* XXX for in_cksum */
563b3a8eb9SGleb Smirnoff 
57d6164b77SAlexander V. Chernikov struct cfg_spool {
58d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_spool)   _next;          /* chain of spool instances */
59d6164b77SAlexander V. Chernikov 	struct in_addr          addr;
60d6164b77SAlexander V. Chernikov 	uint16_t		port;
61d6164b77SAlexander V. Chernikov };
62d6164b77SAlexander V. Chernikov 
63d6164b77SAlexander V. Chernikov /* Nat redirect configuration. */
64d6164b77SAlexander V. Chernikov struct cfg_redir {
65d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_redir)	_next;	/* chain of redir instances */
66d6164b77SAlexander V. Chernikov 	uint16_t		mode;	/* type of redirect mode */
67d6164b77SAlexander V. Chernikov 	uint16_t		proto;	/* protocol: tcp/udp */
68d6164b77SAlexander V. Chernikov 	struct in_addr		laddr;	/* local ip address */
69d6164b77SAlexander V. Chernikov 	struct in_addr		paddr;	/* public ip address */
70d6164b77SAlexander V. Chernikov 	struct in_addr		raddr;	/* remote ip address */
71d6164b77SAlexander V. Chernikov 	uint16_t		lport;	/* local port */
72d6164b77SAlexander V. Chernikov 	uint16_t		pport;	/* public port */
73d6164b77SAlexander V. Chernikov 	uint16_t		rport;	/* remote port	*/
74d6164b77SAlexander V. Chernikov 	uint16_t		pport_cnt;	/* number of public ports */
75d6164b77SAlexander V. Chernikov 	uint16_t		rport_cnt;	/* number of remote ports */
76d6164b77SAlexander V. Chernikov 	struct alias_link	**alink;
77d6164b77SAlexander V. Chernikov 	u_int16_t		spool_cnt; /* num of entry in spool chain */
78d6164b77SAlexander V. Chernikov 	/* chain of spool instances */
79d6164b77SAlexander V. Chernikov 	LIST_HEAD(spool_chain, cfg_spool) spool_chain;
80d6164b77SAlexander V. Chernikov };
81d6164b77SAlexander V. Chernikov 
82d6164b77SAlexander V. Chernikov /* Nat configuration data struct. */
83d6164b77SAlexander V. Chernikov struct cfg_nat {
84d6164b77SAlexander V. Chernikov 	/* chain of nat instances */
85d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_nat)	_next;
86d6164b77SAlexander V. Chernikov 	int			id;		/* nat id  */
87d6164b77SAlexander V. Chernikov 	struct in_addr		ip;		/* nat ip address */
88d6164b77SAlexander V. Chernikov 	struct libalias		*lib;		/* libalias instance */
89d6164b77SAlexander V. Chernikov 	int			mode;		/* aliasing mode */
90d6164b77SAlexander V. Chernikov 	int			redir_cnt; /* number of entry in spool chain */
91d6164b77SAlexander V. Chernikov 	/* chain of redir instances */
92d6164b77SAlexander V. Chernikov 	LIST_HEAD(redir_chain, cfg_redir) redir_chain;
93d6164b77SAlexander V. Chernikov 	char			if_name[IF_NAMESIZE];	/* interface name */
94a08cdb6cSNeel Chauhan 	u_short			alias_port_lo;	/* low range for port aliasing */
95a08cdb6cSNeel Chauhan 	u_short			alias_port_hi;	/* high range for port aliasing */
96d6164b77SAlexander V. Chernikov };
97d6164b77SAlexander V. Chernikov 
988856400bSMikolaj Golub static eventhandler_tag ifaddr_event_tag;
993b3a8eb9SGleb Smirnoff 
1003b3a8eb9SGleb Smirnoff static void
ifaddr_change(void * arg __unused,struct ifnet * ifp)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) {
11716a72f53SGleb Smirnoff 		struct epoch_tracker et;
11816a72f53SGleb 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;
12216a72f53SGleb 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 		}
13416a72f53SGleb 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
flush_nat_ptrs(struct ip_fw_chain * chain,const int ix)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
del_redir_spool_cfg(struct cfg_nat * n,struct redir_chain * head)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
add_redir_spool_cfg(char * buf,struct cfg_nat * ptr)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
free_nat_instance(struct cfg_nat * ptr)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 
28410ab2de0SAlexander V. Chernikov /*
28510ab2de0SAlexander V. Chernikov  * ipfw_nat - perform mbuf header translation.
28610ab2de0SAlexander V. Chernikov  *
28710ab2de0SAlexander V. Chernikov  * Note V_layer3_chain has to be locked while calling ipfw_nat() in
28810ab2de0SAlexander V. Chernikov  * 'global' operation mode (t == NULL).
28910ab2de0SAlexander V. Chernikov  *
29010ab2de0SAlexander V. Chernikov  */
2913b3a8eb9SGleb Smirnoff static int
ipfw_nat(struct ip_fw_args * args,struct cfg_nat * t,struct mbuf * m)2923b3a8eb9SGleb Smirnoff ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
2933b3a8eb9SGleb Smirnoff {
2943b3a8eb9SGleb Smirnoff 	struct mbuf *mcl;
2953b3a8eb9SGleb Smirnoff 	struct ip *ip;
2963b3a8eb9SGleb Smirnoff 	/* XXX - libalias duct tape */
2973b3a8eb9SGleb Smirnoff 	int ldt, retval, found;
2983b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain;
2993b3a8eb9SGleb Smirnoff 	char *c;
3003b3a8eb9SGleb Smirnoff 
3013b3a8eb9SGleb Smirnoff 	ldt = 0;
3023b3a8eb9SGleb Smirnoff 	retval = 0;
3033b3a8eb9SGleb Smirnoff 	mcl = m_megapullup(m, m->m_pkthdr.len);
3043b3a8eb9SGleb Smirnoff 	if (mcl == NULL) {
3053b3a8eb9SGleb Smirnoff 		args->m = NULL;
3063b3a8eb9SGleb Smirnoff 		return (IP_FW_DENY);
3073b3a8eb9SGleb Smirnoff 	}
30865290859SMark Johnston 	M_ASSERTMAPPED(mcl);
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);
4190fc7bdc9SRichard Scheffenegger 		if (tcp_get_flags(th) & TH_RES1)
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 			 */
4390fc7bdc9SRichard Scheffenegger 			tcp_set_flags(th, tcp_get_flags(th) & ~TH_RES1);
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 *
lookup_nat(struct nat_list * l,int nat_id)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 *
lookup_nat_name(struct nat_list * l,char * name)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
nat44_config(struct ip_fw_chain * chain,struct nat44_cfg_nat * ucfg)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;
532a08cdb6cSNeel Chauhan 	ptr->alias_port_lo = ucfg->alias_port_lo;
533a08cdb6cSNeel Chauhan 	ptr->alias_port_hi = ucfg->alias_port_hi;
534d6164b77SAlexander V. Chernikov 	strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name));
535d6164b77SAlexander V. Chernikov 	LibAliasSetMode(ptr->lib, ptr->mode, ~0);
5363b3a8eb9SGleb Smirnoff 	LibAliasSetAddress(ptr->lib, ptr->ip);
537a08cdb6cSNeel Chauhan 	LibAliasSetAliasPortRange(ptr->lib, ptr->alias_port_lo, ptr->alias_port_hi);
5383b3a8eb9SGleb Smirnoff 
5393b3a8eb9SGleb Smirnoff 	/*
5403b3a8eb9SGleb Smirnoff 	 * Redir and LSNAT configuration.
5413b3a8eb9SGleb Smirnoff 	 */
5423b3a8eb9SGleb Smirnoff 	/* Delete old cfgs. */
5433b3a8eb9SGleb Smirnoff 	del_redir_spool_cfg(ptr, &ptr->redir_chain);
5443b3a8eb9SGleb Smirnoff 	/* Add new entries. */
545d6164b77SAlexander V. Chernikov 	add_redir_spool_cfg((char *)(ucfg + 1), ptr);
546d6164b77SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
5473b3a8eb9SGleb Smirnoff 
5483b3a8eb9SGleb Smirnoff 	/* Extra check to avoid race with another ipfw_nat_cfg() */
549d6164b77SAlexander V. Chernikov 	tcfg = NULL;
550d6164b77SAlexander V. Chernikov 	if (gencnt != chain->gencnt)
551d6164b77SAlexander V. Chernikov 	    tcfg = lookup_nat_name(&chain->nat, ucfg->name);
552d6164b77SAlexander V. Chernikov 	IPFW_WLOCK(chain);
553d6164b77SAlexander V. Chernikov 	if (tcfg != NULL)
554d6164b77SAlexander V. Chernikov 		LIST_REMOVE(tcfg, _next);
5553b3a8eb9SGleb Smirnoff 	LIST_INSERT_HEAD(&chain->nat, ptr, _next);
5563b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(chain);
557d6164b77SAlexander V. Chernikov 	chain->gencnt++;
558d6164b77SAlexander V. Chernikov 
559d6164b77SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
560d6164b77SAlexander V. Chernikov 
561d6164b77SAlexander V. Chernikov 	if (tcfg != NULL)
5621a458088SAlexander V. Chernikov 		free_nat_instance(ptr);
563d6164b77SAlexander V. Chernikov }
564d6164b77SAlexander V. Chernikov 
565d6164b77SAlexander V. Chernikov /*
566d6164b77SAlexander V. Chernikov  * Creates/configure nat44 instance
567d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
568d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
569d6164b77SAlexander V. Chernikov  *
570d6164b77SAlexander V. Chernikov  * Returns 0 on success
571d6164b77SAlexander V. Chernikov  */
572d6164b77SAlexander V. Chernikov static int
nat44_cfg(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)573d6164b77SAlexander V. Chernikov nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
574d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
575d6164b77SAlexander V. Chernikov {
576d6164b77SAlexander V. Chernikov 	ipfw_obj_header *oh;
577d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
578d6164b77SAlexander V. Chernikov 	int id;
579d6164b77SAlexander V. Chernikov 	size_t read;
580d6164b77SAlexander V. Chernikov 	char *errptr;
581d6164b77SAlexander V. Chernikov 
582d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
583d6164b77SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg)))
584d6164b77SAlexander V. Chernikov 		return (EINVAL);
585d6164b77SAlexander V. Chernikov 
586d6164b77SAlexander V. Chernikov 	oh = (ipfw_obj_header *)sd->kbuf;
587d6164b77SAlexander V. Chernikov 
588d6164b77SAlexander V. Chernikov 	/* Basic length checks for TLVs */
589d6164b77SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
590d6164b77SAlexander V. Chernikov 		return (EINVAL);
591d6164b77SAlexander V. Chernikov 
592d6164b77SAlexander V. Chernikov 	ucfg = (struct nat44_cfg_nat *)(oh + 1);
593d6164b77SAlexander V. Chernikov 
594d6164b77SAlexander V. Chernikov 	/* Check if name is properly terminated and looks like number */
595d6164b77SAlexander V. Chernikov 	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
596d6164b77SAlexander V. Chernikov 		return (EINVAL);
597d6164b77SAlexander V. Chernikov 	id = strtol(ucfg->name, &errptr, 10);
598d6164b77SAlexander V. Chernikov 	if (id == 0 || *errptr != '\0')
599d6164b77SAlexander V. Chernikov 		return (EINVAL);
600d6164b77SAlexander V. Chernikov 
601d6164b77SAlexander V. Chernikov 	read = sizeof(*oh) + sizeof(*ucfg);
602d6164b77SAlexander V. Chernikov 	/* Check number of redirs */
603d6164b77SAlexander V. Chernikov 	if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir))
604d6164b77SAlexander V. Chernikov 		return (EINVAL);
605d6164b77SAlexander V. Chernikov 
606d6164b77SAlexander V. Chernikov 	nat44_config(chain, ucfg);
607d6164b77SAlexander V. Chernikov 	return (0);
608d6164b77SAlexander V. Chernikov }
609d6164b77SAlexander V. Chernikov 
610d6164b77SAlexander V. Chernikov /*
611d6164b77SAlexander V. Chernikov  * Destroys given nat instances.
612d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
613d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_header ]
614d6164b77SAlexander V. Chernikov  *
615d6164b77SAlexander V. Chernikov  * Returns 0 on success
616d6164b77SAlexander V. Chernikov  */
617d6164b77SAlexander V. Chernikov static int
nat44_destroy(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)618d6164b77SAlexander V. Chernikov nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
619d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
620d6164b77SAlexander V. Chernikov {
621d6164b77SAlexander V. Chernikov 	ipfw_obj_header *oh;
622d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr;
623d6164b77SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
624d6164b77SAlexander V. Chernikov 
625d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
626d6164b77SAlexander V. Chernikov 	if (sd->valsize < sizeof(*oh))
627d6164b77SAlexander V. Chernikov 		return (EINVAL);
628d6164b77SAlexander V. Chernikov 
629d6164b77SAlexander V. Chernikov 	oh = (ipfw_obj_header *)sd->kbuf;
630d6164b77SAlexander V. Chernikov 
631d6164b77SAlexander V. Chernikov 	/* Basic length checks for TLVs */
632d6164b77SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
633d6164b77SAlexander V. Chernikov 		return (EINVAL);
634d6164b77SAlexander V. Chernikov 
635d6164b77SAlexander V. Chernikov 	ntlv = &oh->ntlv;
636d6164b77SAlexander V. Chernikov 	/* Check if name is properly terminated */
637d6164b77SAlexander V. Chernikov 	if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name))
638d6164b77SAlexander V. Chernikov 		return (EINVAL);
639d6164b77SAlexander V. Chernikov 
640d6164b77SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
641d6164b77SAlexander V. Chernikov 	ptr = lookup_nat_name(&chain->nat, ntlv->name);
642d6164b77SAlexander V. Chernikov 	if (ptr == NULL) {
643d6164b77SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
644d6164b77SAlexander V. Chernikov 		return (ESRCH);
645d6164b77SAlexander V. Chernikov 	}
646d6164b77SAlexander V. Chernikov 	IPFW_WLOCK(chain);
647d6164b77SAlexander V. Chernikov 	LIST_REMOVE(ptr, _next);
648d6164b77SAlexander V. Chernikov 	flush_nat_ptrs(chain, ptr->id);
649d6164b77SAlexander V. Chernikov 	IPFW_WUNLOCK(chain);
650d6164b77SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
651d6164b77SAlexander V. Chernikov 
6521a458088SAlexander V. Chernikov 	free_nat_instance(ptr);
653d6164b77SAlexander V. Chernikov 
654d6164b77SAlexander V. Chernikov 	return (0);
655d6164b77SAlexander V. Chernikov }
656d6164b77SAlexander V. Chernikov 
657d6164b77SAlexander V. Chernikov static void
export_nat_cfg(struct cfg_nat * ptr,struct nat44_cfg_nat * ucfg)658d6164b77SAlexander V. Chernikov export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg)
659d6164b77SAlexander V. Chernikov {
660d6164b77SAlexander V. Chernikov 
661d6164b77SAlexander V. Chernikov 	snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id);
662d6164b77SAlexander V. Chernikov 	ucfg->ip = ptr->ip;
663d6164b77SAlexander V. Chernikov 	ucfg->redir_cnt = ptr->redir_cnt;
664d6164b77SAlexander V. Chernikov 	ucfg->mode = ptr->mode;
665a08cdb6cSNeel Chauhan 	ucfg->alias_port_lo = ptr->alias_port_lo;
666a08cdb6cSNeel Chauhan 	ucfg->alias_port_hi = ptr->alias_port_hi;
667d6164b77SAlexander V. Chernikov 	strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name));
668d6164b77SAlexander V. Chernikov }
669d6164b77SAlexander V. Chernikov 
670d6164b77SAlexander V. Chernikov /*
671d6164b77SAlexander V. Chernikov  * Gets config for given nat instance
672d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
673d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
674d6164b77SAlexander V. Chernikov  *
675d6164b77SAlexander V. Chernikov  * Returns 0 on success
676d6164b77SAlexander V. Chernikov  */
677d6164b77SAlexander V. Chernikov static int
nat44_get_cfg(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)678d6164b77SAlexander V. Chernikov nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
679d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
680d6164b77SAlexander V. Chernikov {
681d6164b77SAlexander V. Chernikov 	ipfw_obj_header *oh;
682d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
683d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr;
684d6164b77SAlexander V. Chernikov 	struct cfg_redir *r;
685d6164b77SAlexander V. Chernikov 	struct cfg_spool *s;
686d6164b77SAlexander V. Chernikov 	struct nat44_cfg_redir *ser_r;
687d6164b77SAlexander V. Chernikov 	struct nat44_cfg_spool *ser_s;
688d6164b77SAlexander V. Chernikov 	size_t sz;
689d6164b77SAlexander V. Chernikov 
690d6164b77SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(*ucfg);
691d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
692d6164b77SAlexander V. Chernikov 	if (sd->valsize < sz)
693d6164b77SAlexander V. Chernikov 		return (EINVAL);
694d6164b77SAlexander V. Chernikov 
695d6164b77SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
696d6164b77SAlexander V. Chernikov 
697d6164b77SAlexander V. Chernikov 	/* Basic length checks for TLVs */
698d6164b77SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
699d6164b77SAlexander V. Chernikov 		return (EINVAL);
700d6164b77SAlexander V. Chernikov 
701d6164b77SAlexander V. Chernikov 	ucfg = (struct nat44_cfg_nat *)(oh + 1);
702d6164b77SAlexander V. Chernikov 
703d6164b77SAlexander V. Chernikov 	/* Check if name is properly terminated */
704d6164b77SAlexander V. Chernikov 	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
705d6164b77SAlexander V. Chernikov 		return (EINVAL);
706d6164b77SAlexander V. Chernikov 
707d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
708d6164b77SAlexander V. Chernikov 	ptr = lookup_nat_name(&chain->nat, ucfg->name);
709d6164b77SAlexander V. Chernikov 	if (ptr == NULL) {
710d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
711d6164b77SAlexander V. Chernikov 		return (ESRCH);
712d6164b77SAlexander V. Chernikov 	}
713d6164b77SAlexander V. Chernikov 
714d6164b77SAlexander V. Chernikov 	export_nat_cfg(ptr, ucfg);
715d6164b77SAlexander V. Chernikov 
716d6164b77SAlexander V. Chernikov 	/* Estimate memory amount */
7179f925e8aSAlexander V. Chernikov 	sz = sizeof(ipfw_obj_header) + sizeof(struct nat44_cfg_nat);
718d6164b77SAlexander V. Chernikov 	LIST_FOREACH(r, &ptr->redir_chain, _next) {
719d6164b77SAlexander V. Chernikov 		sz += sizeof(struct nat44_cfg_redir);
720d6164b77SAlexander V. Chernikov 		LIST_FOREACH(s, &r->spool_chain, _next)
721d6164b77SAlexander V. Chernikov 			sz += sizeof(struct nat44_cfg_spool);
722d6164b77SAlexander V. Chernikov 	}
723d6164b77SAlexander V. Chernikov 
724d6164b77SAlexander V. Chernikov 	ucfg->size = sz;
7259f925e8aSAlexander V. Chernikov 	if (sd->valsize < sz) {
726d6164b77SAlexander V. Chernikov 		/*
727d6164b77SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
728d6164b77SAlexander V. Chernikov 		 * WE've already filled in @ucfg structure with
729d6164b77SAlexander V. Chernikov 		 * relevant info including size, so we
730d6164b77SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
731d6164b77SAlexander V. Chernikov 		 */
732d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
733d6164b77SAlexander V. Chernikov 		return (ENOMEM);
734d6164b77SAlexander V. Chernikov 	}
735d6164b77SAlexander V. Chernikov 
736d6164b77SAlexander V. Chernikov 	/* Size OK, let's copy data */
737d6164b77SAlexander V. Chernikov 	LIST_FOREACH(r, &ptr->redir_chain, _next) {
738d6164b77SAlexander V. Chernikov 		ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd,
739d6164b77SAlexander V. Chernikov 		    sizeof(*ser_r));
740d6164b77SAlexander V. Chernikov 		ser_r->mode = r->mode;
741d6164b77SAlexander V. Chernikov 		ser_r->laddr = r->laddr;
742d6164b77SAlexander V. Chernikov 		ser_r->paddr = r->paddr;
743d6164b77SAlexander V. Chernikov 		ser_r->raddr = r->raddr;
744d6164b77SAlexander V. Chernikov 		ser_r->lport = r->lport;
745d6164b77SAlexander V. Chernikov 		ser_r->pport = r->pport;
746d6164b77SAlexander V. Chernikov 		ser_r->rport = r->rport;
747d6164b77SAlexander V. Chernikov 		ser_r->pport_cnt = r->pport_cnt;
748d6164b77SAlexander V. Chernikov 		ser_r->rport_cnt = r->rport_cnt;
749d6164b77SAlexander V. Chernikov 		ser_r->proto = r->proto;
750d6164b77SAlexander V. Chernikov 		ser_r->spool_cnt = r->spool_cnt;
751d6164b77SAlexander V. Chernikov 
752d6164b77SAlexander V. Chernikov 		LIST_FOREACH(s, &r->spool_chain, _next) {
753d6164b77SAlexander V. Chernikov 			ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space(
754d6164b77SAlexander V. Chernikov 			    sd, sizeof(*ser_s));
755d6164b77SAlexander V. Chernikov 
756d6164b77SAlexander V. Chernikov 			ser_s->addr = s->addr;
757d6164b77SAlexander V. Chernikov 			ser_s->port = s->port;
758d6164b77SAlexander V. Chernikov 		}
759d6164b77SAlexander V. Chernikov 	}
760d6164b77SAlexander V. Chernikov 
761d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
762d6164b77SAlexander V. Chernikov 
763d6164b77SAlexander V. Chernikov 	return (0);
764d6164b77SAlexander V. Chernikov }
765d6164b77SAlexander V. Chernikov 
766d6164b77SAlexander V. Chernikov /*
767d6164b77SAlexander V. Chernikov  * Lists all nat44 instances currently available in kernel.
768d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
769d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ]
770d6164b77SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ]
771d6164b77SAlexander V. Chernikov  *
772d6164b77SAlexander V. Chernikov  * Returns 0 on success
773d6164b77SAlexander V. Chernikov  */
774d6164b77SAlexander V. Chernikov static int
nat44_list_nat(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)775d6164b77SAlexander V. Chernikov nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
776d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
777d6164b77SAlexander V. Chernikov {
778d6164b77SAlexander V. Chernikov 	ipfw_obj_lheader *olh;
779d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
780d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr;
781d6164b77SAlexander V. Chernikov 	int nat_count;
782d6164b77SAlexander V. Chernikov 
783d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
784d6164b77SAlexander V. Chernikov 	if (sd->valsize < sizeof(ipfw_obj_lheader))
785d6164b77SAlexander V. Chernikov 		return (EINVAL);
786d6164b77SAlexander V. Chernikov 
787d6164b77SAlexander V. Chernikov 	olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
788d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
789d6164b77SAlexander V. Chernikov 	nat_count = 0;
790d6164b77SAlexander V. Chernikov 	LIST_FOREACH(ptr, &chain->nat, _next)
791d6164b77SAlexander V. Chernikov 		nat_count++;
792d6164b77SAlexander V. Chernikov 
793d6164b77SAlexander V. Chernikov 	olh->count = nat_count;
794d6164b77SAlexander V. Chernikov 	olh->objsize = sizeof(struct nat44_cfg_nat);
795d6164b77SAlexander V. Chernikov 	olh->size = sizeof(*olh) + olh->count * olh->objsize;
796d6164b77SAlexander V. Chernikov 
797d6164b77SAlexander V. Chernikov 	if (sd->valsize < olh->size) {
798d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
799d6164b77SAlexander V. Chernikov 		return (ENOMEM);
800d6164b77SAlexander V. Chernikov 	}
801d6164b77SAlexander V. Chernikov 
802d6164b77SAlexander V. Chernikov 	LIST_FOREACH(ptr, &chain->nat, _next) {
803d6164b77SAlexander V. Chernikov 		ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd,
804d6164b77SAlexander V. Chernikov 		    sizeof(*ucfg));
805d6164b77SAlexander V. Chernikov 		export_nat_cfg(ptr, ucfg);
806d6164b77SAlexander V. Chernikov 	}
807d6164b77SAlexander V. Chernikov 
808d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
809d6164b77SAlexander V. Chernikov 
810d6164b77SAlexander V. Chernikov 	return (0);
811d6164b77SAlexander V. Chernikov }
812d6164b77SAlexander V. Chernikov 
813d6164b77SAlexander V. Chernikov /*
814d6164b77SAlexander V. Chernikov  * Gets log for given nat instance
815d6164b77SAlexander V. Chernikov  * Data layout (v0)(current):
816d6164b77SAlexander V. Chernikov  * Request: [ ipfw_obj_header nat44_cfg_nat ]
817d6164b77SAlexander V. Chernikov  * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ]
818d6164b77SAlexander V. Chernikov  *
819d6164b77SAlexander V. Chernikov  * Returns 0 on success
820d6164b77SAlexander V. Chernikov  */
821d6164b77SAlexander V. Chernikov static int
nat44_get_log(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)822d6164b77SAlexander V. Chernikov nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
823d6164b77SAlexander V. Chernikov     struct sockopt_data *sd)
824d6164b77SAlexander V. Chernikov {
825d6164b77SAlexander V. Chernikov 	ipfw_obj_header *oh;
826d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
827d6164b77SAlexander V. Chernikov 	struct cfg_nat *ptr;
828d6164b77SAlexander V. Chernikov 	void *pbuf;
829d6164b77SAlexander V. Chernikov 	size_t sz;
830d6164b77SAlexander V. Chernikov 
831d6164b77SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(*ucfg);
832d6164b77SAlexander V. Chernikov 	/* Check minimum header size */
833d6164b77SAlexander V. Chernikov 	if (sd->valsize < sz)
834d6164b77SAlexander V. Chernikov 		return (EINVAL);
835d6164b77SAlexander V. Chernikov 
836d6164b77SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
837d6164b77SAlexander V. Chernikov 
838d6164b77SAlexander V. Chernikov 	/* Basic length checks for TLVs */
839d6164b77SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
840d6164b77SAlexander V. Chernikov 		return (EINVAL);
841d6164b77SAlexander V. Chernikov 
842d6164b77SAlexander V. Chernikov 	ucfg = (struct nat44_cfg_nat *)(oh + 1);
843d6164b77SAlexander V. Chernikov 
844d6164b77SAlexander V. Chernikov 	/* Check if name is properly terminated */
845d6164b77SAlexander V. Chernikov 	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
846d6164b77SAlexander V. Chernikov 		return (EINVAL);
847d6164b77SAlexander V. Chernikov 
848d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
849d6164b77SAlexander V. Chernikov 	ptr = lookup_nat_name(&chain->nat, ucfg->name);
850d6164b77SAlexander V. Chernikov 	if (ptr == NULL) {
851d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
852d6164b77SAlexander V. Chernikov 		return (ESRCH);
853d6164b77SAlexander V. Chernikov 	}
854d6164b77SAlexander V. Chernikov 
855d6164b77SAlexander V. Chernikov 	if (ptr->lib->logDesc == NULL) {
856d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
857d6164b77SAlexander V. Chernikov 		return (ENOENT);
858d6164b77SAlexander V. Chernikov 	}
859d6164b77SAlexander V. Chernikov 
860d6164b77SAlexander V. Chernikov 	export_nat_cfg(ptr, ucfg);
861d6164b77SAlexander V. Chernikov 
862d6164b77SAlexander V. Chernikov 	/* Estimate memory amount */
863d6164b77SAlexander V. Chernikov 	ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE;
864d6164b77SAlexander V. Chernikov 	if (sd->valsize < sz + sizeof(*oh)) {
865d6164b77SAlexander V. Chernikov 		/*
866d6164b77SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
867d6164b77SAlexander V. Chernikov 		 * WE've already filled in @ucfg structure with
868d6164b77SAlexander V. Chernikov 		 * relevant info including size, so we
869d6164b77SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
870d6164b77SAlexander V. Chernikov 		 */
871d6164b77SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(chain);
872d6164b77SAlexander V. Chernikov 		return (ENOMEM);
873d6164b77SAlexander V. Chernikov 	}
874d6164b77SAlexander V. Chernikov 
875d6164b77SAlexander V. Chernikov 	pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE);
876d6164b77SAlexander V. Chernikov 	memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE);
877d6164b77SAlexander V. Chernikov 
878d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
879d6164b77SAlexander V. Chernikov 
880d6164b77SAlexander V. Chernikov 	return (0);
881d6164b77SAlexander V. Chernikov }
882d6164b77SAlexander V. Chernikov 
883d6164b77SAlexander V. Chernikov static struct ipfw_sopt_handler	scodes[] = {
884*4a77657cSAndrey V. Elsukov     { IP_FW_NAT44_XCONFIG,	IP_FW3_OPVER, HDIR_SET,	nat44_cfg },
885*4a77657cSAndrey V. Elsukov     { IP_FW_NAT44_DESTROY,	IP_FW3_OPVER, HDIR_SET,	nat44_destroy },
886*4a77657cSAndrey V. Elsukov     { IP_FW_NAT44_XGETCONFIG,	IP_FW3_OPVER, HDIR_GET,	nat44_get_cfg },
887*4a77657cSAndrey V. Elsukov     { IP_FW_NAT44_LIST_NAT,	IP_FW3_OPVER, HDIR_GET,	nat44_list_nat },
888*4a77657cSAndrey V. Elsukov     { IP_FW_NAT44_XGETLOG,	IP_FW3_OPVER, HDIR_GET,	nat44_get_log },
889d6164b77SAlexander V. Chernikov };
890d6164b77SAlexander V. Chernikov 
891d6164b77SAlexander V. Chernikov /*
892d6164b77SAlexander V. Chernikov  * Legacy configuration routines
893d6164b77SAlexander V. Chernikov  */
894d6164b77SAlexander V. Chernikov 
895d6164b77SAlexander V. Chernikov struct cfg_spool_legacy {
896d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_spool_legacy)	_next;
897d6164b77SAlexander V. Chernikov 	struct in_addr			addr;
898d6164b77SAlexander V. Chernikov 	u_short				port;
899d6164b77SAlexander V. Chernikov };
900d6164b77SAlexander V. Chernikov 
901d6164b77SAlexander V. Chernikov struct cfg_redir_legacy {
902d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_redir)   _next;
903d6164b77SAlexander V. Chernikov 	u_int16_t               mode;
904d6164b77SAlexander V. Chernikov 	struct in_addr	        laddr;
905d6164b77SAlexander V. Chernikov 	struct in_addr	        paddr;
906d6164b77SAlexander V. Chernikov 	struct in_addr	        raddr;
907d6164b77SAlexander V. Chernikov 	u_short                 lport;
908d6164b77SAlexander V. Chernikov 	u_short                 pport;
909d6164b77SAlexander V. Chernikov 	u_short                 rport;
910d6164b77SAlexander V. Chernikov 	u_short                 pport_cnt;
911d6164b77SAlexander V. Chernikov 	u_short                 rport_cnt;
912d6164b77SAlexander V. Chernikov 	int                     proto;
913d6164b77SAlexander V. Chernikov 	struct alias_link       **alink;
914d6164b77SAlexander V. Chernikov 	u_int16_t               spool_cnt;
915d6164b77SAlexander V. Chernikov 	LIST_HEAD(, cfg_spool_legacy) spool_chain;
916d6164b77SAlexander V. Chernikov };
917d6164b77SAlexander V. Chernikov 
918d6164b77SAlexander V. Chernikov struct cfg_nat_legacy {
919d6164b77SAlexander V. Chernikov 	LIST_ENTRY(cfg_nat_legacy)	_next;
920d6164b77SAlexander V. Chernikov 	int				id;
921d6164b77SAlexander V. Chernikov 	struct in_addr			ip;
922d6164b77SAlexander V. Chernikov 	char				if_name[IF_NAMESIZE];
923d6164b77SAlexander V. Chernikov 	int				mode;
924d6164b77SAlexander V. Chernikov 	struct libalias			*lib;
925d6164b77SAlexander V. Chernikov 	int				redir_cnt;
926d6164b77SAlexander V. Chernikov 	LIST_HEAD(, cfg_redir_legacy)	redir_chain;
927d6164b77SAlexander V. Chernikov };
928d6164b77SAlexander V. Chernikov 
929d6164b77SAlexander V. Chernikov static int
ipfw_nat_cfg(struct sockopt * sopt)930d6164b77SAlexander V. Chernikov ipfw_nat_cfg(struct sockopt *sopt)
931d6164b77SAlexander V. Chernikov {
932d6164b77SAlexander V. Chernikov 	struct cfg_nat_legacy *cfg;
933d6164b77SAlexander V. Chernikov 	struct nat44_cfg_nat *ucfg;
934d6164b77SAlexander V. Chernikov 	struct cfg_redir_legacy *rdir;
935d6164b77SAlexander V. Chernikov 	struct nat44_cfg_redir *urdir;
936d6164b77SAlexander V. Chernikov 	char *buf;
937d6164b77SAlexander V. Chernikov 	size_t len, len2;
938d6164b77SAlexander V. Chernikov 	int error, i;
939d6164b77SAlexander V. Chernikov 
940d6164b77SAlexander V. Chernikov 	len = sopt->sopt_valsize;
941d6164b77SAlexander V. Chernikov 	len2 = len + 128;
942d6164b77SAlexander V. Chernikov 
943d6164b77SAlexander V. Chernikov 	/*
944d6164b77SAlexander V. Chernikov 	 * Allocate 2x buffer to store converted structures.
945a4641f4eSPedro F. Giffuni 	 * new redir_cfg has shrunk, so we're sure that
946d6164b77SAlexander V. Chernikov 	 * new buffer size is enough.
947d6164b77SAlexander V. Chernikov 	 */
948d6164b77SAlexander V. Chernikov 	buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO);
949d6164b77SAlexander V. Chernikov 	error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy));
950d6164b77SAlexander V. Chernikov 	if (error != 0)
951d6164b77SAlexander V. Chernikov 		goto out;
952d6164b77SAlexander V. Chernikov 
953d6164b77SAlexander V. Chernikov 	cfg = (struct cfg_nat_legacy *)buf;
954d6164b77SAlexander V. Chernikov 	if (cfg->id < 0) {
955d6164b77SAlexander V. Chernikov 		error = EINVAL;
956d6164b77SAlexander V. Chernikov 		goto out;
957d6164b77SAlexander V. Chernikov 	}
958d6164b77SAlexander V. Chernikov 
959d6164b77SAlexander V. Chernikov 	ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)];
960d6164b77SAlexander V. Chernikov 	snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id);
961d6164b77SAlexander V. Chernikov 	strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name));
962d6164b77SAlexander V. Chernikov 	ucfg->ip = cfg->ip;
963d6164b77SAlexander V. Chernikov 	ucfg->mode = cfg->mode;
964d6164b77SAlexander V. Chernikov 	ucfg->redir_cnt = cfg->redir_cnt;
965d6164b77SAlexander V. Chernikov 
966d6164b77SAlexander V. Chernikov 	if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) {
967d6164b77SAlexander V. Chernikov 		error = EINVAL;
968d6164b77SAlexander V. Chernikov 		goto out;
969d6164b77SAlexander V. Chernikov 	}
970d6164b77SAlexander V. Chernikov 
971d6164b77SAlexander V. Chernikov 	urdir = (struct nat44_cfg_redir *)(ucfg + 1);
972d6164b77SAlexander V. Chernikov 	rdir = (struct cfg_redir_legacy *)(cfg + 1);
973d6164b77SAlexander V. Chernikov 	for (i = 0; i < cfg->redir_cnt; i++) {
974d6164b77SAlexander V. Chernikov 		urdir->mode = rdir->mode;
975d6164b77SAlexander V. Chernikov 		urdir->laddr = rdir->laddr;
976d6164b77SAlexander V. Chernikov 		urdir->paddr = rdir->paddr;
977d6164b77SAlexander V. Chernikov 		urdir->raddr = rdir->raddr;
978d6164b77SAlexander V. Chernikov 		urdir->lport = rdir->lport;
979d6164b77SAlexander V. Chernikov 		urdir->pport = rdir->pport;
980d6164b77SAlexander V. Chernikov 		urdir->rport = rdir->rport;
981d6164b77SAlexander V. Chernikov 		urdir->pport_cnt = rdir->pport_cnt;
982d6164b77SAlexander V. Chernikov 		urdir->rport_cnt = rdir->rport_cnt;
983d6164b77SAlexander V. Chernikov 		urdir->proto = rdir->proto;
984d6164b77SAlexander V. Chernikov 		urdir->spool_cnt = rdir->spool_cnt;
985d6164b77SAlexander V. Chernikov 
986d6164b77SAlexander V. Chernikov 		urdir++;
987d6164b77SAlexander V. Chernikov 		rdir++;
988d6164b77SAlexander V. Chernikov 	}
989d6164b77SAlexander V. Chernikov 
990d6164b77SAlexander V. Chernikov 	nat44_config(&V_layer3_chain, ucfg);
9913b3a8eb9SGleb Smirnoff 
9923b3a8eb9SGleb Smirnoff out:
9933b3a8eb9SGleb Smirnoff 	free(buf, M_TEMP);
9943b3a8eb9SGleb Smirnoff 	return (error);
9953b3a8eb9SGleb Smirnoff }
9963b3a8eb9SGleb Smirnoff 
9973b3a8eb9SGleb Smirnoff static int
ipfw_nat_del(struct sockopt * sopt)9983b3a8eb9SGleb Smirnoff ipfw_nat_del(struct sockopt *sopt)
9993b3a8eb9SGleb Smirnoff {
10003b3a8eb9SGleb Smirnoff 	struct cfg_nat *ptr;
10013b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain = &V_layer3_chain;
10023b3a8eb9SGleb Smirnoff 	int i;
10033b3a8eb9SGleb Smirnoff 
10043b3a8eb9SGleb Smirnoff 	sooptcopyin(sopt, &i, sizeof i, sizeof i);
10053b3a8eb9SGleb Smirnoff 	/* XXX validate i */
1006d6164b77SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
10073b3a8eb9SGleb Smirnoff 	ptr = lookup_nat(&chain->nat, i);
10083b3a8eb9SGleb Smirnoff 	if (ptr == NULL) {
1009d6164b77SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
10103b3a8eb9SGleb Smirnoff 		return (EINVAL);
10113b3a8eb9SGleb Smirnoff 	}
1012d6164b77SAlexander V. Chernikov 	IPFW_WLOCK(chain);
10133b3a8eb9SGleb Smirnoff 	LIST_REMOVE(ptr, _next);
10143b3a8eb9SGleb Smirnoff 	flush_nat_ptrs(chain, i);
10153b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(chain);
1016d6164b77SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
10171a458088SAlexander V. Chernikov 	free_nat_instance(ptr);
10183b3a8eb9SGleb Smirnoff 	return (0);
10193b3a8eb9SGleb Smirnoff }
10203b3a8eb9SGleb Smirnoff 
10213b3a8eb9SGleb Smirnoff static int
ipfw_nat_get_cfg(struct sockopt * sopt)10223b3a8eb9SGleb Smirnoff ipfw_nat_get_cfg(struct sockopt *sopt)
10233b3a8eb9SGleb Smirnoff {
10243b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain = &V_layer3_chain;
10253b3a8eb9SGleb Smirnoff 	struct cfg_nat *n;
1026d6164b77SAlexander V. Chernikov 	struct cfg_nat_legacy *ucfg;
10273b3a8eb9SGleb Smirnoff 	struct cfg_redir *r;
10283b3a8eb9SGleb Smirnoff 	struct cfg_spool *s;
1029d6164b77SAlexander V. Chernikov 	struct cfg_redir_legacy *ser_r;
1030d6164b77SAlexander V. Chernikov 	struct cfg_spool_legacy *ser_s;
10313b3a8eb9SGleb Smirnoff 	char *data;
10323b3a8eb9SGleb Smirnoff 	int gencnt, nat_cnt, len, error;
10333b3a8eb9SGleb Smirnoff 
10343b3a8eb9SGleb Smirnoff 	nat_cnt = 0;
10353b3a8eb9SGleb Smirnoff 	len = sizeof(nat_cnt);
10363b3a8eb9SGleb Smirnoff 
1037d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
10383b3a8eb9SGleb Smirnoff retry:
10393b3a8eb9SGleb Smirnoff 	gencnt = chain->gencnt;
10403b3a8eb9SGleb Smirnoff 	/* Estimate memory amount */
10413b3a8eb9SGleb Smirnoff 	LIST_FOREACH(n, &chain->nat, _next) {
10423b3a8eb9SGleb Smirnoff 		nat_cnt++;
1043d6164b77SAlexander V. Chernikov 		len += sizeof(struct cfg_nat_legacy);
10443b3a8eb9SGleb Smirnoff 		LIST_FOREACH(r, &n->redir_chain, _next) {
1045d6164b77SAlexander V. Chernikov 			len += sizeof(struct cfg_redir_legacy);
10463b3a8eb9SGleb Smirnoff 			LIST_FOREACH(s, &r->spool_chain, _next)
1047d6164b77SAlexander V. Chernikov 				len += sizeof(struct cfg_spool_legacy);
10483b3a8eb9SGleb Smirnoff 		}
10493b3a8eb9SGleb Smirnoff 	}
1050d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
10513b3a8eb9SGleb Smirnoff 
10523b3a8eb9SGleb Smirnoff 	data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
10533b3a8eb9SGleb Smirnoff 	bcopy(&nat_cnt, data, sizeof(nat_cnt));
10543b3a8eb9SGleb Smirnoff 
10553b3a8eb9SGleb Smirnoff 	nat_cnt = 0;
10563b3a8eb9SGleb Smirnoff 	len = sizeof(nat_cnt);
10573b3a8eb9SGleb Smirnoff 
1058d6164b77SAlexander V. Chernikov 	IPFW_UH_RLOCK(chain);
10593b3a8eb9SGleb Smirnoff 	if (gencnt != chain->gencnt) {
10603b3a8eb9SGleb Smirnoff 		free(data, M_TEMP);
10613b3a8eb9SGleb Smirnoff 		goto retry;
10623b3a8eb9SGleb Smirnoff 	}
10633b3a8eb9SGleb Smirnoff 	/* Serialize all the data. */
10643b3a8eb9SGleb Smirnoff 	LIST_FOREACH(n, &chain->nat, _next) {
1065d6164b77SAlexander V. Chernikov 		ucfg = (struct cfg_nat_legacy *)&data[len];
1066d6164b77SAlexander V. Chernikov 		ucfg->id = n->id;
1067d6164b77SAlexander V. Chernikov 		ucfg->ip = n->ip;
1068d6164b77SAlexander V. Chernikov 		ucfg->redir_cnt = n->redir_cnt;
1069d6164b77SAlexander V. Chernikov 		ucfg->mode = n->mode;
1070d6164b77SAlexander V. Chernikov 		strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name));
1071d6164b77SAlexander V. Chernikov 		len += sizeof(struct cfg_nat_legacy);
10723b3a8eb9SGleb Smirnoff 		LIST_FOREACH(r, &n->redir_chain, _next) {
1073d6164b77SAlexander V. Chernikov 			ser_r = (struct cfg_redir_legacy *)&data[len];
1074d6164b77SAlexander V. Chernikov 			ser_r->mode = r->mode;
1075d6164b77SAlexander V. Chernikov 			ser_r->laddr = r->laddr;
1076d6164b77SAlexander V. Chernikov 			ser_r->paddr = r->paddr;
1077d6164b77SAlexander V. Chernikov 			ser_r->raddr = r->raddr;
1078d6164b77SAlexander V. Chernikov 			ser_r->lport = r->lport;
1079d6164b77SAlexander V. Chernikov 			ser_r->pport = r->pport;
1080d6164b77SAlexander V. Chernikov 			ser_r->rport = r->rport;
1081d6164b77SAlexander V. Chernikov 			ser_r->pport_cnt = r->pport_cnt;
1082d6164b77SAlexander V. Chernikov 			ser_r->rport_cnt = r->rport_cnt;
1083d6164b77SAlexander V. Chernikov 			ser_r->proto = r->proto;
1084d6164b77SAlexander V. Chernikov 			ser_r->spool_cnt = r->spool_cnt;
1085d6164b77SAlexander V. Chernikov 			len += sizeof(struct cfg_redir_legacy);
10863b3a8eb9SGleb Smirnoff 			LIST_FOREACH(s, &r->spool_chain, _next) {
1087d6164b77SAlexander V. Chernikov 				ser_s = (struct cfg_spool_legacy *)&data[len];
1088d6164b77SAlexander V. Chernikov 				ser_s->addr = s->addr;
1089d6164b77SAlexander V. Chernikov 				ser_s->port = s->port;
1090d6164b77SAlexander V. Chernikov 				len += sizeof(struct cfg_spool_legacy);
10913b3a8eb9SGleb Smirnoff 			}
10923b3a8eb9SGleb Smirnoff 		}
10933b3a8eb9SGleb Smirnoff 	}
1094d6164b77SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(chain);
10953b3a8eb9SGleb Smirnoff 
10963b3a8eb9SGleb Smirnoff 	error = sooptcopyout(sopt, data, len);
10973b3a8eb9SGleb Smirnoff 	free(data, M_TEMP);
10983b3a8eb9SGleb Smirnoff 
10993b3a8eb9SGleb Smirnoff 	return (error);
11003b3a8eb9SGleb Smirnoff }
11013b3a8eb9SGleb Smirnoff 
11023b3a8eb9SGleb Smirnoff static int
ipfw_nat_get_log(struct sockopt * sopt)11033b3a8eb9SGleb Smirnoff ipfw_nat_get_log(struct sockopt *sopt)
11043b3a8eb9SGleb Smirnoff {
11053b3a8eb9SGleb Smirnoff 	uint8_t *data;
11063b3a8eb9SGleb Smirnoff 	struct cfg_nat *ptr;
11073b3a8eb9SGleb Smirnoff 	int i, size;
11083b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain;
1109ccba94b8SAlexander V. Chernikov 	IPFW_RLOCK_TRACKER;
11103b3a8eb9SGleb Smirnoff 
11113b3a8eb9SGleb Smirnoff 	chain = &V_layer3_chain;
11123b3a8eb9SGleb Smirnoff 
11133b3a8eb9SGleb Smirnoff 	IPFW_RLOCK(chain);
11143b3a8eb9SGleb Smirnoff 	/* one pass to count, one to copy the data */
11153b3a8eb9SGleb Smirnoff 	i = 0;
11163b3a8eb9SGleb Smirnoff 	LIST_FOREACH(ptr, &chain->nat, _next) {
11173b3a8eb9SGleb Smirnoff 		if (ptr->lib->logDesc == NULL)
11183b3a8eb9SGleb Smirnoff 			continue;
11193b3a8eb9SGleb Smirnoff 		i++;
11203b3a8eb9SGleb Smirnoff 	}
11213b3a8eb9SGleb Smirnoff 	size = i * (LIBALIAS_BUF_SIZE + sizeof(int));
11223b3a8eb9SGleb Smirnoff 	data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO);
11233b3a8eb9SGleb Smirnoff 	if (data == NULL) {
11243b3a8eb9SGleb Smirnoff 		IPFW_RUNLOCK(chain);
11253b3a8eb9SGleb Smirnoff 		return (ENOSPC);
11263b3a8eb9SGleb Smirnoff 	}
11273b3a8eb9SGleb Smirnoff 	i = 0;
11283b3a8eb9SGleb Smirnoff 	LIST_FOREACH(ptr, &chain->nat, _next) {
11293b3a8eb9SGleb Smirnoff 		if (ptr->lib->logDesc == NULL)
11303b3a8eb9SGleb Smirnoff 			continue;
11313b3a8eb9SGleb Smirnoff 		bcopy(&ptr->id, &data[i], sizeof(int));
11323b3a8eb9SGleb Smirnoff 		i += sizeof(int);
11333b3a8eb9SGleb Smirnoff 		bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE);
11343b3a8eb9SGleb Smirnoff 		i += LIBALIAS_BUF_SIZE;
11353b3a8eb9SGleb Smirnoff 	}
11363b3a8eb9SGleb Smirnoff 	IPFW_RUNLOCK(chain);
11373b3a8eb9SGleb Smirnoff 	sooptcopyout(sopt, data, size);
11383b3a8eb9SGleb Smirnoff 	free(data, M_IPFW);
11393b3a8eb9SGleb Smirnoff 	return(0);
11403b3a8eb9SGleb Smirnoff }
11413b3a8eb9SGleb Smirnoff 
11428856400bSMikolaj Golub static int
vnet_ipfw_nat_init(const void * arg __unused)11438856400bSMikolaj Golub vnet_ipfw_nat_init(const void *arg __unused)
11443b3a8eb9SGleb Smirnoff {
11453b3a8eb9SGleb Smirnoff 
11468856400bSMikolaj Golub 	V_ipfw_nat_ready = 1;
11478856400bSMikolaj Golub 	return (0);
11483b3a8eb9SGleb Smirnoff }
11493b3a8eb9SGleb Smirnoff 
11508856400bSMikolaj Golub static int
vnet_ipfw_nat_uninit(const void * arg __unused)11518856400bSMikolaj Golub vnet_ipfw_nat_uninit(const void *arg __unused)
11523b3a8eb9SGleb Smirnoff {
11533b3a8eb9SGleb Smirnoff 	struct cfg_nat *ptr, *ptr_temp;
11543b3a8eb9SGleb Smirnoff 	struct ip_fw_chain *chain;
11553b3a8eb9SGleb Smirnoff 
11563b3a8eb9SGleb Smirnoff 	chain = &V_layer3_chain;
11573b3a8eb9SGleb Smirnoff 	IPFW_WLOCK(chain);
11589ac51e79SBjoern A. Zeeb 	V_ipfw_nat_ready = 0;
11593b3a8eb9SGleb Smirnoff 	LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) {
11603b3a8eb9SGleb Smirnoff 		LIST_REMOVE(ptr, _next);
11611a458088SAlexander V. Chernikov 		free_nat_instance(ptr);
11623b3a8eb9SGleb Smirnoff 	}
11633b3a8eb9SGleb Smirnoff 	flush_nat_ptrs(chain, -1 /* flush all */);
11648856400bSMikolaj Golub 	IPFW_WUNLOCK(chain);
11658856400bSMikolaj Golub 	return (0);
11668856400bSMikolaj Golub }
11678856400bSMikolaj Golub 
11688856400bSMikolaj Golub static void
ipfw_nat_init(void)11698856400bSMikolaj Golub ipfw_nat_init(void)
11708856400bSMikolaj Golub {
11718856400bSMikolaj Golub 
11728856400bSMikolaj Golub 	/* init ipfw hooks */
11738856400bSMikolaj Golub 	ipfw_nat_ptr = ipfw_nat;
11748856400bSMikolaj Golub 	lookup_nat_ptr = lookup_nat;
11758856400bSMikolaj Golub 	ipfw_nat_cfg_ptr = ipfw_nat_cfg;
11768856400bSMikolaj Golub 	ipfw_nat_del_ptr = ipfw_nat_del;
11778856400bSMikolaj Golub 	ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
11788856400bSMikolaj Golub 	ipfw_nat_get_log_ptr = ipfw_nat_get_log;
1179d6164b77SAlexander V. Chernikov 	IPFW_ADD_SOPT_HANDLER(1, scodes);
11808856400bSMikolaj Golub 
11818856400bSMikolaj Golub 	ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
11828856400bSMikolaj Golub 	    NULL, EVENTHANDLER_PRI_ANY);
11838856400bSMikolaj Golub }
11848856400bSMikolaj Golub 
11858856400bSMikolaj Golub static void
ipfw_nat_destroy(void)11868856400bSMikolaj Golub ipfw_nat_destroy(void)
11878856400bSMikolaj Golub {
11888856400bSMikolaj Golub 
11898856400bSMikolaj Golub 	EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
11903b3a8eb9SGleb Smirnoff 	/* deregister ipfw_nat */
1191d6164b77SAlexander V. Chernikov 	IPFW_DEL_SOPT_HANDLER(1, scodes);
11923b3a8eb9SGleb Smirnoff 	ipfw_nat_ptr = NULL;
11933b3a8eb9SGleb Smirnoff 	lookup_nat_ptr = NULL;
11943b3a8eb9SGleb Smirnoff 	ipfw_nat_cfg_ptr = NULL;
11953b3a8eb9SGleb Smirnoff 	ipfw_nat_del_ptr = NULL;
11963b3a8eb9SGleb Smirnoff 	ipfw_nat_get_cfg_ptr = NULL;
11973b3a8eb9SGleb Smirnoff 	ipfw_nat_get_log_ptr = NULL;
11983b3a8eb9SGleb Smirnoff }
11993b3a8eb9SGleb Smirnoff 
12003b3a8eb9SGleb Smirnoff static int
ipfw_nat_modevent(module_t mod,int type,void * unused)12013b3a8eb9SGleb Smirnoff ipfw_nat_modevent(module_t mod, int type, void *unused)
12023b3a8eb9SGleb Smirnoff {
12033b3a8eb9SGleb Smirnoff 	int err = 0;
12043b3a8eb9SGleb Smirnoff 
12053b3a8eb9SGleb Smirnoff 	switch (type) {
12063b3a8eb9SGleb Smirnoff 	case MOD_LOAD:
12073b3a8eb9SGleb Smirnoff 		break;
12083b3a8eb9SGleb Smirnoff 
12093b3a8eb9SGleb Smirnoff 	case MOD_UNLOAD:
12103b3a8eb9SGleb Smirnoff 		break;
12113b3a8eb9SGleb Smirnoff 
12123b3a8eb9SGleb Smirnoff 	default:
12133b3a8eb9SGleb Smirnoff 		return EOPNOTSUPP;
12143b3a8eb9SGleb Smirnoff 		break;
12153b3a8eb9SGleb Smirnoff 	}
12163b3a8eb9SGleb Smirnoff 	return err;
12173b3a8eb9SGleb Smirnoff }
12183b3a8eb9SGleb Smirnoff 
12193b3a8eb9SGleb Smirnoff static moduledata_t ipfw_nat_mod = {
12203b3a8eb9SGleb Smirnoff 	"ipfw_nat",
12213b3a8eb9SGleb Smirnoff 	ipfw_nat_modevent,
12229823d527SKevin Lo 	0
12233b3a8eb9SGleb Smirnoff };
12243b3a8eb9SGleb Smirnoff 
12258856400bSMikolaj Golub /* Define startup order. */
122689856f7eSBjoern A. Zeeb #define	IPFW_NAT_SI_SUB_FIREWALL	SI_SUB_PROTO_FIREWALL
12278a477d48SMikolaj Golub #define	IPFW_NAT_MODEVENT_ORDER		(SI_ORDER_ANY - 128) /* after ipfw */
12288856400bSMikolaj Golub #define	IPFW_NAT_MODULE_ORDER		(IPFW_NAT_MODEVENT_ORDER + 1)
12298856400bSMikolaj Golub #define	IPFW_NAT_VNET_ORDER		(IPFW_NAT_MODEVENT_ORDER + 2)
12308856400bSMikolaj Golub 
12318856400bSMikolaj Golub DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY);
12323b3a8eb9SGleb Smirnoff MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1);
1233f9ab623bSAlexander V. Chernikov MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3);
12343b3a8eb9SGleb Smirnoff MODULE_VERSION(ipfw_nat, 1);
12358856400bSMikolaj Golub 
12368856400bSMikolaj Golub SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
12378856400bSMikolaj Golub     ipfw_nat_init, NULL);
12388856400bSMikolaj Golub VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER,
12398856400bSMikolaj Golub     vnet_ipfw_nat_init, NULL);
12408856400bSMikolaj Golub 
12418856400bSMikolaj Golub SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
12428856400bSMikolaj Golub     ipfw_nat_destroy, NULL);
12438856400bSMikolaj Golub VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL,
12448856400bSMikolaj Golub     IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL);
12458856400bSMikolaj Golub 
12463b3a8eb9SGleb Smirnoff /* end of file */
1247