xref: /dragonfly/sys/net/ipfw3_nat/ip_fw3_nat.c (revision 8a93af2a)
16a03354eSMatthew Dillon /*
2aad10cc6SBill Yuan  * Copyright (c) 2014 - 2018 The DragonFly Project.  All rights reserved.
36a03354eSMatthew Dillon  *
46a03354eSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
59187b359SBill Yuan  * by Bill Yuan <bycn82@dragonflybsd.org>
66a03354eSMatthew Dillon  *
76a03354eSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
86a03354eSMatthew Dillon  * modification, are permitted provided that the following conditions
96a03354eSMatthew Dillon  * are met:
106a03354eSMatthew Dillon  *
116a03354eSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
126a03354eSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
136a03354eSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
146a03354eSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
156a03354eSMatthew Dillon  *    the documentation and/or other materials provided with the
166a03354eSMatthew Dillon  *    distribution.
176a03354eSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
186a03354eSMatthew Dillon  *    contributors may be used to endorse or promote products derived
196a03354eSMatthew Dillon  *    from this software without specific, prior written permission.
206a03354eSMatthew Dillon  *
216a03354eSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
226a03354eSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
236a03354eSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
246a03354eSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
256a03354eSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
266a03354eSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
276a03354eSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
286a03354eSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
296a03354eSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
306a03354eSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
316a03354eSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
326a03354eSMatthew Dillon  * SUCH DAMAGE.
336a03354eSMatthew Dillon  */
346a03354eSMatthew Dillon 
354408d548SBill Yuan #include "opt_ipfw.h"
364408d548SBill Yuan #include "opt_inet.h"
374408d548SBill Yuan #ifndef INET
384408d548SBill Yuan #error IPFIREWALL3 requires INET.
394408d548SBill Yuan #endif /* INET */
404408d548SBill Yuan 
416a03354eSMatthew Dillon #include <sys/param.h>
426a03354eSMatthew Dillon #include <sys/kernel.h>
436a03354eSMatthew Dillon #include <sys/malloc.h>
446a03354eSMatthew Dillon #include <sys/mbuf.h>
456a03354eSMatthew Dillon #include <sys/socketvar.h>
466a03354eSMatthew Dillon #include <sys/sysctl.h>
476a03354eSMatthew Dillon #include <sys/systimer.h>
486a03354eSMatthew Dillon #include <sys/in_cksum.h>
496a03354eSMatthew Dillon #include <sys/systm.h>
506a03354eSMatthew Dillon #include <sys/proc.h>
516a03354eSMatthew Dillon #include <sys/socket.h>
526a03354eSMatthew Dillon #include <sys/syslog.h>
536a03354eSMatthew Dillon #include <sys/ucred.h>
546a03354eSMatthew Dillon #include <sys/lock.h>
556a03354eSMatthew Dillon 
566a03354eSMatthew Dillon #include <net/ethernet.h>
576a03354eSMatthew Dillon #include <net/netmsg2.h>
586a03354eSMatthew Dillon #include <net/netisr2.h>
596a03354eSMatthew Dillon #include <net/route.h>
606a03354eSMatthew Dillon #include <net/if.h>
616a03354eSMatthew Dillon 
626a03354eSMatthew Dillon #include <netinet/in.h>
636a03354eSMatthew Dillon #include <netinet/ip.h>
646a03354eSMatthew Dillon #include <netinet/ip_icmp.h>
656a03354eSMatthew Dillon #include <netinet/tcp.h>
666a03354eSMatthew Dillon #include <netinet/tcp_timer.h>
676a03354eSMatthew Dillon #include <netinet/tcp_var.h>
686a03354eSMatthew Dillon #include <netinet/tcpip.h>
696a03354eSMatthew Dillon #include <netinet/udp.h>
706a03354eSMatthew Dillon #include <netinet/udp_var.h>
716a03354eSMatthew Dillon #include <netinet/in_systm.h>
726a03354eSMatthew Dillon #include <netinet/in_var.h>
736a03354eSMatthew Dillon #include <netinet/in_pcb.h>
746a03354eSMatthew Dillon #include <netinet/ip_var.h>
756a03354eSMatthew Dillon #include <netinet/ip_divert.h>
766a03354eSMatthew Dillon #include <net/ipfw3/ip_fw.h>
77936e3df8SBill Yuan 
786a03354eSMatthew Dillon #include "ip_fw3_nat.h"
796a03354eSMatthew Dillon 
804408d548SBill Yuan MALLOC_DEFINE(M_IPFW3_NAT, "IP_FW3_NAT", "ipfw3_nat module");
814408d548SBill Yuan 
8229f13cb6SBill Yuan /*
834408d548SBill Yuan  * Highspeed Lockless Kernel NAT
8429f13cb6SBill Yuan  *
854408d548SBill Yuan  * Kernel NAT
864408d548SBill Yuan  * The network address translation (NAT) will replace the `src` of the packet
874408d548SBill Yuan  * with an `alias` (alias_addr & alias_port). Accordingt to the configuration,
884408d548SBill Yuan  * The alias will be randomly picked from the configured range.
8929f13cb6SBill Yuan  *
904408d548SBill Yuan  * Highspeed
914408d548SBill Yuan  * The first outgoing packet should trigger the creation of the `net_state`,
924408d548SBill Yuan  * and the `net_state` will keep in a RB-Tree for the subsequent outgoing
934408d548SBill Yuan  * packets.
944408d548SBill Yuan  * The first returning packet will trigger the creation of the `net_state2`,
954408d548SBill Yuan  * which will be stored in a multidimensional array of points ( of net_state2 ).
964408d548SBill Yuan  *
974408d548SBill Yuan  * Lockless
984408d548SBill Yuan  * The `net_state` for outgoing packet will be stored in the nat_context of
994408d548SBill Yuan  * current CPU. But due to the nature of the NAT, the returning packet may be
1004408d548SBill Yuan  * handled by another CPU. Hence, The `net_state2` for the returning packet
1014408d548SBill Yuan  * will be prepared and stored into the nat_context of the right CPU.
10229f13cb6SBill Yuan  */
10329f13cb6SBill Yuan 
104aad10cc6SBill Yuan struct ip_fw3_nat_context	*ip_fw3_nat_ctx[MAXCPU];
105aad10cc6SBill Yuan static struct callout 		ip_fw3_nat_cleanup_callout;
1064408d548SBill Yuan extern struct ipfw3_context 	*fw3_ctx[MAXCPU];
1074408d548SBill Yuan extern ip_fw_ctl_t 		*ip_fw3_ctl_nat_ptr;
108936e3df8SBill Yuan 
109f1b0a2e2SBill Yuan static int 			sysctl_var_cleanup_interval = 1;
110f1b0a2e2SBill Yuan static int 			sysctl_var_icmp_timeout = 10;
111f1b0a2e2SBill Yuan static int 			sysctl_var_tcp_timeout = 60;
112f1b0a2e2SBill Yuan static int 			sysctl_var_udp_timeout = 30;
113452fe3bbSBill Yuan 
114452fe3bbSBill Yuan SYSCTL_NODE(_net_inet_ip, OID_AUTO, fw3_nat, CTLFLAG_RW, 0, "ipfw3 NAT");
115452fe3bbSBill Yuan SYSCTL_INT(_net_inet_ip_fw3_nat, OID_AUTO, cleanup_interval, CTLFLAG_RW,
116f1b0a2e2SBill Yuan 		&sysctl_var_cleanup_interval, 0, "default life time");
117f1b0a2e2SBill Yuan SYSCTL_INT(_net_inet_ip_fw3_nat, OID_AUTO, icmp_timeout, CTLFLAG_RW,
118f1b0a2e2SBill Yuan 		&sysctl_var_icmp_timeout, 0, "default icmp state life time");
119f1b0a2e2SBill Yuan SYSCTL_INT(_net_inet_ip_fw3_nat, OID_AUTO, tcp_timeout, CTLFLAG_RW,
120f1b0a2e2SBill Yuan 		&sysctl_var_tcp_timeout, 0, "default tcp state life time");
121f1b0a2e2SBill Yuan SYSCTL_INT(_net_inet_ip_fw3_nat, OID_AUTO, udp_timeout, CTLFLAG_RW,
122f1b0a2e2SBill Yuan 		&sysctl_var_udp_timeout, 0, "default udp state life time");
1236a03354eSMatthew Dillon 
1244408d548SBill Yuan RB_PROTOTYPE(state_tree, nat_state, entries, ip_fw3_nat_state_cmp);
1254408d548SBill Yuan RB_GENERATE(state_tree, nat_state, entries, ip_fw3_nat_state_cmp);
126ca9d3a28SBill Yuan 
12729f13cb6SBill Yuan static __inline uint16_t
fix_cksum(uint16_t cksum,uint16_t old_info,uint16_t new_info,uint8_t is_udp)12829f13cb6SBill Yuan fix_cksum(uint16_t cksum, uint16_t old_info, uint16_t new_info, uint8_t is_udp)
12929f13cb6SBill Yuan {
13029f13cb6SBill Yuan 	uint32_t tmp;
13129f13cb6SBill Yuan 
13229f13cb6SBill Yuan 	if (is_udp && !cksum)
13329f13cb6SBill Yuan 		return (0x0000);
13429f13cb6SBill Yuan 	tmp = cksum + old_info - new_info;
13529f13cb6SBill Yuan 	tmp = (tmp >> 16) + (tmp & 65535);
13629f13cb6SBill Yuan 	tmp = tmp & 65535;
13729f13cb6SBill Yuan 	if (is_udp && !tmp)
13829f13cb6SBill Yuan 		return (0xFFFF);
13929f13cb6SBill Yuan 	return tmp;
14029f13cb6SBill Yuan }
14129f13cb6SBill Yuan 
1426a03354eSMatthew Dillon void
check_nat(int * cmd_ctl,int * cmd_val,struct ip_fw_args ** args,struct ip_fw ** f,ipfw_insn * cmd,uint16_t ip_len)1436a03354eSMatthew Dillon check_nat(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
1446a03354eSMatthew Dillon 		struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len)
1456a03354eSMatthew Dillon {
146a9e52100SBill Yuan 	if ((*args)->eh != NULL) {
147a9e52100SBill Yuan 		*cmd_ctl = IP_FW_CTL_NO;
148a9e52100SBill Yuan 		*cmd_val = IP_FW_NOT_MATCH;
149a9e52100SBill Yuan 		return;
150a9e52100SBill Yuan 	}
151a9e52100SBill Yuan 
152a9e52100SBill Yuan 	struct ip_fw3_nat_context *nat_ctx;
153a9e52100SBill Yuan 	struct cfg_nat *nat;
154a9e52100SBill Yuan 	int nat_id;
155a9e52100SBill Yuan 
156a9e52100SBill Yuan 	nat_ctx = ip_fw3_nat_ctx[mycpuid];
157a9e52100SBill Yuan 	(*args)->rule = *f;
158a9e52100SBill Yuan 	nat = ((ipfw_insn_nat *)cmd)->nat;
159a9e52100SBill Yuan 	if (nat == NULL) {
160a9e52100SBill Yuan 		nat_id = cmd->arg1;
161a9e52100SBill Yuan 		nat = nat_ctx->nats[nat_id - 1];
162a9e52100SBill Yuan 		if (nat == NULL) {
1636a03354eSMatthew Dillon 			*cmd_val = IP_FW_DENY;
164a9e52100SBill Yuan 			*cmd_ctl = IP_FW_CTL_DONE;
165a9e52100SBill Yuan 			return;
166a9e52100SBill Yuan 		}
167a9e52100SBill Yuan 		((ipfw_insn_nat *)cmd)->nat = nat;
168a9e52100SBill Yuan 	}
169a9e52100SBill Yuan 	*cmd_val = ip_fw3_nat(*args, nat, (*args)->m);
1706a03354eSMatthew Dillon 	*cmd_ctl = IP_FW_CTL_NAT;
1716a03354eSMatthew Dillon }
1726a03354eSMatthew Dillon 
1739187b359SBill Yuan int
ip_fw3_nat(struct ip_fw_args * args,struct cfg_nat * nat,struct mbuf * m)17429f13cb6SBill Yuan ip_fw3_nat(struct ip_fw_args *args, struct cfg_nat *nat, struct mbuf *m)
1759187b359SBill Yuan {
1764408d548SBill Yuan 	struct state_tree *tree_out = NULL;
1774408d548SBill Yuan 	struct nat_state *s = NULL, *dup, *k, key;
1784408d548SBill Yuan 	struct nat_state2 *s2 = NULL;
179936e3df8SBill Yuan 	struct ip *ip = mtod(m, struct ip *);
180936e3df8SBill Yuan 	struct in_addr *old_addr = NULL, new_addr;
181936e3df8SBill Yuan 	uint16_t *old_port = NULL, new_port;
182936e3df8SBill Yuan 	uint16_t *csum = NULL, dlen = 0;
183936e3df8SBill Yuan 	uint8_t udp = 0;
184936e3df8SBill Yuan 	boolean_t pseudo = FALSE, need_return_state = FALSE;
185936e3df8SBill Yuan 	struct cfg_alias *alias;
186936e3df8SBill Yuan 	int i = 0, rand_n = 0;
18729f13cb6SBill Yuan 
188936e3df8SBill Yuan 	k = &key;
189936e3df8SBill Yuan 	memset(k, 0, LEN_NAT_STATE);
19029f13cb6SBill Yuan 	if (args->oif == NULL) {
191936e3df8SBill Yuan 		old_addr = &ip->ip_dst;
192936e3df8SBill Yuan 		k->dst_addr = ntohl(args->f_id.dst_ip);
1934408d548SBill Yuan 		LIST_FOREACH(alias, &nat->alias, next) {
1944408d548SBill Yuan 			if (alias->ip.s_addr == ntohl(args->f_id.dst_ip)) {
1954408d548SBill Yuan 				break;
1964408d548SBill Yuan 			}
1974408d548SBill Yuan 		}
1984408d548SBill Yuan 		if (alias == NULL) {
1994408d548SBill Yuan 			goto oops;
2004408d548SBill Yuan 		}
20129f13cb6SBill Yuan 		switch (ip->ip_p) {
20229f13cb6SBill Yuan 		case IPPROTO_TCP:
203936e3df8SBill Yuan 			old_port = &L3HDR(struct tcphdr, ip)->th_dport;
2044408d548SBill Yuan 			s2 = alias->tcp_in[*old_port - ALIAS_BEGIN];
20529f13cb6SBill Yuan 			csum = &L3HDR(struct tcphdr, ip)->th_sum;
20629f13cb6SBill Yuan 			break;
20729f13cb6SBill Yuan 		case IPPROTO_UDP:
208936e3df8SBill Yuan 			old_port = &L3HDR(struct udphdr, ip)->uh_dport;
2094408d548SBill Yuan 			s2 = alias->udp_in[*old_port - ALIAS_BEGIN];
21029f13cb6SBill Yuan 			csum = &L3HDR(struct udphdr, ip)->uh_sum;
21129f13cb6SBill Yuan 			udp = 1;
21229f13cb6SBill Yuan 			break;
21329f13cb6SBill Yuan 		case IPPROTO_ICMP:
214936e3df8SBill Yuan 			old_port = &L3HDR(struct icmp, ip)->icmp_id;
2154408d548SBill Yuan 			s2 = alias->icmp_in[*old_port];
21629f13cb6SBill Yuan 			csum = &L3HDR(struct icmp, ip)->icmp_cksum;
21729f13cb6SBill Yuan 			break;
21829f13cb6SBill Yuan 		default:
21929f13cb6SBill Yuan 			panic("ipfw3: unsupported proto %u", ip->ip_p);
22029f13cb6SBill Yuan 		}
2214408d548SBill Yuan 		if (s2 == NULL) {
22229f13cb6SBill Yuan 			goto oops;
22329f13cb6SBill Yuan 		}
22429f13cb6SBill Yuan 	} else {
225936e3df8SBill Yuan 		old_addr = &ip->ip_src;
226936e3df8SBill Yuan 		k->src_addr = args->f_id.src_ip;
227936e3df8SBill Yuan 		k->dst_addr = args->f_id.dst_ip;
22829f13cb6SBill Yuan 		switch (ip->ip_p) {
22929f13cb6SBill Yuan 		case IPPROTO_TCP:
230936e3df8SBill Yuan 			k->src_port = args->f_id.src_port;
231936e3df8SBill Yuan 			k->dst_port = args->f_id.dst_port;
23229f13cb6SBill Yuan 			m->m_pkthdr.csum_flags = CSUM_TCP;
233936e3df8SBill Yuan 			tree_out = &nat->rb_tcp_out;
234936e3df8SBill Yuan 			old_port = &L3HDR(struct tcphdr, ip)->th_sport;
23529f13cb6SBill Yuan 			csum = &L3HDR(struct tcphdr, ip)->th_sum;
23629f13cb6SBill Yuan 			break;
23729f13cb6SBill Yuan 		case IPPROTO_UDP:
238936e3df8SBill Yuan 			k->src_port = args->f_id.src_port;
239936e3df8SBill Yuan 			k->dst_port = args->f_id.dst_port;
24029f13cb6SBill Yuan 			m->m_pkthdr.csum_flags = CSUM_UDP;
241936e3df8SBill Yuan 			tree_out = &nat->rb_udp_out;
242936e3df8SBill Yuan 			old_port = &L3HDR(struct udphdr, ip)->uh_sport;
24329f13cb6SBill Yuan 			csum = &L3HDR(struct udphdr, ip)->uh_sum;
24429f13cb6SBill Yuan 			udp = 1;
24529f13cb6SBill Yuan 			break;
24629f13cb6SBill Yuan 		case IPPROTO_ICMP:
247936e3df8SBill Yuan 			k->src_port = L3HDR(struct icmp, ip)->icmp_id;
248936e3df8SBill Yuan 			k->dst_port = k->src_port;
249936e3df8SBill Yuan 			tree_out = &nat->rb_icmp_out;
250936e3df8SBill Yuan 			old_port = &L3HDR(struct icmp, ip)->icmp_id;
25129f13cb6SBill Yuan 			csum = &L3HDR(struct icmp, ip)->icmp_cksum;
25229f13cb6SBill Yuan 			break;
25329f13cb6SBill Yuan 		default:
25429f13cb6SBill Yuan 			panic("ipfw3: unsupported proto %u", ip->ip_p);
25529f13cb6SBill Yuan 		}
25629f13cb6SBill Yuan 		s = RB_FIND(state_tree, tree_out, k);
25729f13cb6SBill Yuan 		if (s == NULL) {
258936e3df8SBill Yuan 			/* pick an alias ip randomly when there are multiple */
259936e3df8SBill Yuan 			if (nat->count > 1) {
260936e3df8SBill Yuan 				rand_n = krandom() % nat->count;
261936e3df8SBill Yuan 			}
262936e3df8SBill Yuan 			LIST_FOREACH(alias, &nat->alias, next) {
263936e3df8SBill Yuan 				if (i++ == rand_n) {
264936e3df8SBill Yuan 					break;
265936e3df8SBill Yuan 				}
266936e3df8SBill Yuan 			}
26729f13cb6SBill Yuan 			switch  (ip->ip_p) {
26829f13cb6SBill Yuan 			case IPPROTO_TCP:
26929f13cb6SBill Yuan 				m->m_pkthdr.csum_flags = CSUM_TCP;
2704408d548SBill Yuan 				s = kmalloc(LEN_NAT_STATE, M_IPFW3_NAT,
27129f13cb6SBill Yuan 						M_INTWAIT | M_NULLOK | M_ZERO);
27229f13cb6SBill Yuan 
273936e3df8SBill Yuan 				s->src_addr = args->f_id.src_ip;
274936e3df8SBill Yuan 				s->src_port = args->f_id.src_port;
27529f13cb6SBill Yuan 
276936e3df8SBill Yuan 				s->dst_addr = args->f_id.dst_ip;
277936e3df8SBill Yuan 				s->dst_port = args->f_id.dst_port;
27829f13cb6SBill Yuan 
279936e3df8SBill Yuan 				s->alias_addr = alias->ip.s_addr;
280936e3df8SBill Yuan 				pick_alias_port(s, tree_out);
281936e3df8SBill Yuan 				dup = RB_INSERT(state_tree, tree_out, s);
282936e3df8SBill Yuan 				need_return_state = TRUE;
28329f13cb6SBill Yuan 				break;
28429f13cb6SBill Yuan 			case IPPROTO_UDP:
28529f13cb6SBill Yuan 				m->m_pkthdr.csum_flags = CSUM_UDP;
2864408d548SBill Yuan 				s = kmalloc(LEN_NAT_STATE, M_IPFW3_NAT,
28729f13cb6SBill Yuan 						M_INTWAIT | M_NULLOK | M_ZERO);
28829f13cb6SBill Yuan 
289936e3df8SBill Yuan 				s->src_addr = args->f_id.src_ip;
290936e3df8SBill Yuan 				s->src_port = args->f_id.src_port;
29129f13cb6SBill Yuan 
292936e3df8SBill Yuan 				s->dst_addr = args->f_id.dst_ip;
293936e3df8SBill Yuan 				s->dst_port = args->f_id.dst_port;
29429f13cb6SBill Yuan 
295936e3df8SBill Yuan 				s->alias_addr = alias->ip.s_addr;
296936e3df8SBill Yuan 				pick_alias_port(s, tree_out);
297936e3df8SBill Yuan 				dup = RB_INSERT(state_tree, tree_out, s);
298936e3df8SBill Yuan 				need_return_state = TRUE;
29929f13cb6SBill Yuan 				break;
30029f13cb6SBill Yuan 			case IPPROTO_ICMP:
3014408d548SBill Yuan 				s = kmalloc(LEN_NAT_STATE, M_IPFW3_NAT,
30229f13cb6SBill Yuan 						M_INTWAIT | M_NULLOK | M_ZERO);
303936e3df8SBill Yuan 				s->src_addr = args->f_id.src_ip;
304936e3df8SBill Yuan 				s->dst_addr = args->f_id.dst_ip;
30529f13cb6SBill Yuan 
306936e3df8SBill Yuan 				s->src_port = *old_port;
307936e3df8SBill Yuan 				s->dst_port = *old_port;
30829f13cb6SBill Yuan 
309936e3df8SBill Yuan 				s->alias_addr = alias->ip.s_addr;
310eb4c9e6dSBill Yuan 				s->alias_port = htons(s->src_addr *
311eb4c9e6dSBill Yuan 						s->dst_addr % ALIAS_RANGE);
312936e3df8SBill Yuan 				dup = RB_INSERT(state_tree, tree_out, s);
31329f13cb6SBill Yuan 
3144408d548SBill Yuan 				s2 = kmalloc(LEN_NAT_STATE2, M_IPFW3_NAT,
31529f13cb6SBill Yuan 						M_INTWAIT | M_NULLOK | M_ZERO);
31629f13cb6SBill Yuan 
317936e3df8SBill Yuan 				s2->src_addr = args->f_id.dst_ip;
318936e3df8SBill Yuan 				s2->dst_addr = alias->ip.s_addr;
319936e3df8SBill Yuan 
320936e3df8SBill Yuan 				s2->src_port = s->alias_port;
321936e3df8SBill Yuan 				s2->dst_port = s->alias_port;
32229f13cb6SBill Yuan 
32329f13cb6SBill Yuan 				s2->alias_addr = htonl(args->f_id.src_ip);
324936e3df8SBill Yuan 				s2->alias_port = *old_port;
3254408d548SBill Yuan 
3264408d548SBill Yuan 				alias->icmp_in[s->alias_port] = s2;
32729f13cb6SBill Yuan 				break;
32829f13cb6SBill Yuan 			default :
32929f13cb6SBill Yuan 				goto oops;
33029f13cb6SBill Yuan 			}
33129f13cb6SBill Yuan 		}
33229f13cb6SBill Yuan 	}
3334408d548SBill Yuan 	if (args->oif == NULL) {
334eb4c9e6dSBill Yuan 		if (ip->ip_p == IPPROTO_ICMP) {
335eb4c9e6dSBill Yuan 			new_addr.s_addr = s2->alias_addr;
336eb4c9e6dSBill Yuan 			new_port = s2->alias_port;
337eb4c9e6dSBill Yuan 		} else {
3384408d548SBill Yuan 			new_addr.s_addr = s2->src_addr;
3394408d548SBill Yuan 			new_port = s2->src_port;
340eb4c9e6dSBill Yuan 		}
3414408d548SBill Yuan 		s2->timestamp = time_uptime;
3424408d548SBill Yuan 	} else {
343936e3df8SBill Yuan 		new_addr.s_addr = s->alias_addr;
344936e3df8SBill Yuan 		new_port = s->alias_port;
345936e3df8SBill Yuan 		s->timestamp = time_uptime;
3464408d548SBill Yuan 	}
347936e3df8SBill Yuan 
348936e3df8SBill Yuan 	/* replace src/dst and fix the checksum */
34929f13cb6SBill Yuan 	if (m->m_pkthdr.csum_flags & (CSUM_UDP | CSUM_TCP | CSUM_TSO)) {
35029f13cb6SBill Yuan 		if ((m->m_pkthdr.csum_flags & CSUM_TSO) == 0) {
351*8a93af2aSMatthew Dillon 			dlen = ntohs(ip->ip_len) - (ip->ip_hl << 2);
35229f13cb6SBill Yuan 		}
35329f13cb6SBill Yuan 		pseudo = TRUE;
35429f13cb6SBill Yuan 	}
35529f13cb6SBill Yuan 	if (!pseudo) {
35629f13cb6SBill Yuan 		const uint16_t *oaddr, *naddr;
35729f13cb6SBill Yuan 		oaddr = (const uint16_t *)&old_addr->s_addr;
358936e3df8SBill Yuan 		naddr = (const uint16_t *)&new_addr.s_addr;
35929f13cb6SBill Yuan 		ip->ip_sum = fix_cksum(ip->ip_sum, oaddr[0], naddr[0], 0);
36029f13cb6SBill Yuan 		ip->ip_sum = fix_cksum(ip->ip_sum, oaddr[1], naddr[1], 0);
36129f13cb6SBill Yuan 		if (ip->ip_p != IPPROTO_ICMP) {
36229f13cb6SBill Yuan 			*csum = fix_cksum(*csum, oaddr[0], naddr[0], udp);
36329f13cb6SBill Yuan 			*csum = fix_cksum(*csum, oaddr[1], naddr[1], udp);
36429f13cb6SBill Yuan 		}
36529f13cb6SBill Yuan 	}
366936e3df8SBill Yuan 	old_addr->s_addr = new_addr.s_addr;
36729f13cb6SBill Yuan 	if (!pseudo) {
368936e3df8SBill Yuan 		*csum = fix_cksum(*csum, *old_port, new_port, udp);
36929f13cb6SBill Yuan 	}
370936e3df8SBill Yuan 	*old_port = new_port;
37129f13cb6SBill Yuan 
37229f13cb6SBill Yuan 	if (pseudo) {
373936e3df8SBill Yuan 		*csum = in_pseudo(ip->ip_src.s_addr,
374936e3df8SBill Yuan 				ip->ip_dst.s_addr, htons(dlen + ip->ip_p));
375936e3df8SBill Yuan 	}
376936e3df8SBill Yuan 
377936e3df8SBill Yuan 	/* prepare the state for return traffic */
378936e3df8SBill Yuan 	if (need_return_state) {
379936e3df8SBill Yuan 		m->m_flags &= ~M_HASH;
380936e3df8SBill Yuan 		ip_hashfn(&m, 0);
381936e3df8SBill Yuan 
382936e3df8SBill Yuan 		int nextcpu = netisr_hashcpu(m->m_pkthdr.hash);
383936e3df8SBill Yuan 		if (nextcpu != mycpuid) {
384936e3df8SBill Yuan 			struct netmsg_nat_state_add *msg;
385936e3df8SBill Yuan 			msg = kmalloc(LEN_NMSG_NAT_STATE_ADD,
386936e3df8SBill Yuan 					M_LWKTMSG, M_NOWAIT | M_ZERO);
387936e3df8SBill Yuan 			netmsg_init(&msg->base, NULL, &curthread->td_msgport,
388936e3df8SBill Yuan 					0, nat_state_add_dispatch);
3894408d548SBill Yuan 			s2 = kmalloc(LEN_NAT_STATE2, M_IPFW3_NAT,
390936e3df8SBill Yuan 					M_INTWAIT | M_NULLOK | M_ZERO);
391936e3df8SBill Yuan 
392936e3df8SBill Yuan 			s2->src_addr = args->f_id.dst_ip;
3934408d548SBill Yuan 			s2->src_port = args->f_id.dst_port;
394936e3df8SBill Yuan 
395936e3df8SBill Yuan 			s2->dst_addr = alias->ip.s_addr;
396936e3df8SBill Yuan 			s2->dst_port = s->alias_port;
397936e3df8SBill Yuan 
3984408d548SBill Yuan 			s2->src_addr = htonl(args->f_id.src_ip);
3994408d548SBill Yuan 			s2->src_port = htons(args->f_id.src_port);
400936e3df8SBill Yuan 
401936e3df8SBill Yuan 			s2->timestamp = s->timestamp;
4024408d548SBill Yuan 			msg->alias_addr.s_addr = alias->ip.s_addr;
4034408d548SBill Yuan 			msg->alias_port = s->alias_port;
404936e3df8SBill Yuan 			msg->state = s2;
405936e3df8SBill Yuan 			msg->nat_id = nat->id;
406936e3df8SBill Yuan 			msg->proto = ip->ip_p;
407936e3df8SBill Yuan 			netisr_sendmsg(&msg->base, nextcpu);
408936e3df8SBill Yuan 		} else {
4094408d548SBill Yuan 			s2 = kmalloc(LEN_NAT_STATE2, M_IPFW3_NAT,
410936e3df8SBill Yuan 					M_INTWAIT | M_NULLOK | M_ZERO);
411936e3df8SBill Yuan 
412936e3df8SBill Yuan 			s2->src_addr = args->f_id.dst_ip;
413936e3df8SBill Yuan 			s2->dst_addr = alias->ip.s_addr;
4144408d548SBill Yuan 
4154408d548SBill Yuan 			s2->src_port = s->alias_port;
416936e3df8SBill Yuan 			s2->dst_port = s->alias_port;
417936e3df8SBill Yuan 
4184408d548SBill Yuan 			s2->src_addr = htonl(args->f_id.src_ip);
4194408d548SBill Yuan 			s2->src_port = htons(args->f_id.src_port);
420936e3df8SBill Yuan 
421936e3df8SBill Yuan 			s2->timestamp = s->timestamp;
422936e3df8SBill Yuan 			if (ip->ip_p == IPPROTO_TCP) {
4234408d548SBill Yuan 				alias->tcp_in[s->alias_port - ALIAS_BEGIN] = s2;
424936e3df8SBill Yuan 			} else {
4254408d548SBill Yuan 				alias->udp_in[s->alias_port - ALIAS_BEGIN] = s2;
426936e3df8SBill Yuan 			}
427936e3df8SBill Yuan 		}
42829f13cb6SBill Yuan 	}
4299187b359SBill Yuan 	return IP_FW_NAT;
43029f13cb6SBill Yuan oops:
431db88c4b9SSascha Wildner 	IPFW3_DEBUG1("oops\n");
43229f13cb6SBill Yuan 	return IP_FW_DENY;
4339187b359SBill Yuan }
4349187b359SBill Yuan 
435936e3df8SBill Yuan void
pick_alias_port(struct nat_state * s,struct state_tree * tree)436936e3df8SBill Yuan pick_alias_port(struct nat_state *s, struct state_tree *tree)
4376e433a95SBill Yuan {
4386e433a95SBill Yuan 	do {
439936e3df8SBill Yuan 		s->alias_port = htons(krandom() % ALIAS_RANGE + ALIAS_BEGIN);
440936e3df8SBill Yuan 	} while (RB_FIND(state_tree, tree, s) != NULL);
4416e433a95SBill Yuan }
4426e433a95SBill Yuan 
4436e433a95SBill Yuan int
ip_fw3_nat_state_cmp(struct nat_state * s1,struct nat_state * s2)4444408d548SBill Yuan ip_fw3_nat_state_cmp(struct nat_state *s1, struct nat_state *s2)
445c8ac7327SBill Yuan {
446936e3df8SBill Yuan 	if (s1->src_addr > s2->src_addr)
447c8ac7327SBill Yuan 		return 1;
448936e3df8SBill Yuan 	if (s1->src_addr < s2->src_addr)
449c8ac7327SBill Yuan 		return -1;
450c8ac7327SBill Yuan 
451936e3df8SBill Yuan 	if (s1->dst_addr > s2->dst_addr)
452c8ac7327SBill Yuan 		return 1;
453936e3df8SBill Yuan 	if (s1->dst_addr < s2->dst_addr)
454c8ac7327SBill Yuan 		return -1;
455c8ac7327SBill Yuan 
456936e3df8SBill Yuan 	if (s1->src_port > s2->src_port)
457c8ac7327SBill Yuan 		return 1;
458936e3df8SBill Yuan 	if (s1->src_port < s2->src_port)
459c8ac7327SBill Yuan 		return -1;
460c8ac7327SBill Yuan 
461936e3df8SBill Yuan 	if (s1->dst_port > s2->dst_port)
462c8ac7327SBill Yuan 		return 1;
463936e3df8SBill Yuan 	if (s1->dst_port < s2->dst_port)
464c8ac7327SBill Yuan 		return -1;
465c8ac7327SBill Yuan 
466c8ac7327SBill Yuan 	return 0;
467c8ac7327SBill Yuan }
468c8ac7327SBill Yuan 
469c8ac7327SBill Yuan int
ip_fw3_ctl_nat_get_cfg(struct sockopt * sopt)47071194d7aSBill Yuan ip_fw3_ctl_nat_get_cfg(struct sockopt *sopt)
4716a03354eSMatthew Dillon {
472ca8ec5ceSBill Yuan 	struct ip_fw3_nat_context *nat_ctx;
473ca8ec5ceSBill Yuan 	struct ioc_nat *ioc;
474ca8ec5ceSBill Yuan 	struct cfg_nat *nat;
475936e3df8SBill Yuan 	struct cfg_alias *alias;
476936e3df8SBill Yuan 	struct in_addr *ip;
477ca8ec5ceSBill Yuan 	size_t valsize;
478ca8ec5ceSBill Yuan 	int i, len;
479ca8ec5ceSBill Yuan 
480ca8ec5ceSBill Yuan 	len = 0;
481ca8ec5ceSBill Yuan 	nat_ctx = ip_fw3_nat_ctx[mycpuid];
482ca8ec5ceSBill Yuan 	valsize = sopt->sopt_valsize;
483936e3df8SBill Yuan 	ioc = (struct ioc_nat *)sopt->sopt_val;
484ca8ec5ceSBill Yuan 
485ca8ec5ceSBill Yuan 	for (i = 0; i < NAT_ID_MAX; i++) {
486ca8ec5ceSBill Yuan 		nat = nat_ctx->nats[i];
487ca8ec5ceSBill Yuan 		if (nat != NULL) {
488ca8ec5ceSBill Yuan 			len += LEN_IOC_NAT;
489936e3df8SBill Yuan 			if (len >= valsize) {
490ca8ec5ceSBill Yuan 				goto nospace;
491ca8ec5ceSBill Yuan 			}
492936e3df8SBill Yuan 			ioc->id = nat->id;
493936e3df8SBill Yuan 			ioc->count = nat->count;
494936e3df8SBill Yuan 			ip = &ioc->ip;
495936e3df8SBill Yuan 			LIST_FOREACH(alias, &nat->alias, next) {
496936e3df8SBill Yuan 				len += LEN_IN_ADDR;
497936e3df8SBill Yuan 				if (len > valsize) {
498936e3df8SBill Yuan 					goto nospace;
499936e3df8SBill Yuan 				}
500936e3df8SBill Yuan 				bcopy(&alias->ip, ip, LEN_IN_ADDR);
501936e3df8SBill Yuan 				ip++;
502936e3df8SBill Yuan 			}
503ca8ec5ceSBill Yuan 		}
504ca8ec5ceSBill Yuan 	}
505ca8ec5ceSBill Yuan 	sopt->sopt_valsize = len;
506ca8ec5ceSBill Yuan 	return 0;
507ca8ec5ceSBill Yuan nospace:
508ca8ec5ceSBill Yuan 	bzero(sopt->sopt_val, sopt->sopt_valsize);
509ca8ec5ceSBill Yuan 	sopt->sopt_valsize = 0;
5106a03354eSMatthew Dillon 	return 0;
5116a03354eSMatthew Dillon }
5126a03354eSMatthew Dillon 
5139187b359SBill Yuan int
ip_fw3_ctl_nat_get_record(struct sockopt * sopt)51471194d7aSBill Yuan ip_fw3_ctl_nat_get_record(struct sockopt *sopt)
5156a03354eSMatthew Dillon {
5168e040facSBill Yuan 	struct ip_fw3_nat_context *nat_ctx;
5178e040facSBill Yuan 	struct cfg_nat *the;
5188e040facSBill Yuan 	size_t sopt_size, total_len = 0;
5198e040facSBill Yuan 	struct ioc_nat_state *ioc;
5204408d548SBill Yuan 	int ioc_nat_id, i, n, cpu;
521936e3df8SBill Yuan 	struct nat_state 	*s;
5224408d548SBill Yuan 	struct nat_state2 	*s2;
5234408d548SBill Yuan 	struct cfg_alias	*a1;
5248e040facSBill Yuan 
5258e040facSBill Yuan 	ioc_nat_id = *((int *)(sopt->sopt_val));
5268e040facSBill Yuan 	sopt_size = sopt->sopt_valsize;
5278e040facSBill Yuan 	ioc = (struct ioc_nat_state *)sopt->sopt_val;
5288e040facSBill Yuan 	/* icmp states only in CPU 0 */
5298e040facSBill Yuan 	cpu = 0;
5308e040facSBill Yuan 	nat_ctx = ip_fw3_nat_ctx[cpu];
5318e040facSBill Yuan 	for (n = 0; n < NAT_ID_MAX; n++) {
5328e040facSBill Yuan 		if (ioc_nat_id == 0 || ioc_nat_id == n + 1) {
5338e040facSBill Yuan 			if (nat_ctx->nats[n] == NULL)
5348e040facSBill Yuan 				break;
5358e040facSBill Yuan 			the = nat_ctx->nats[n];
536936e3df8SBill Yuan 			RB_FOREACH(s, state_tree, &the->rb_icmp_out) {
5378e040facSBill Yuan 				total_len += LEN_IOC_NAT_STATE;
5388e040facSBill Yuan 				if (total_len > sopt_size)
5398e040facSBill Yuan 					goto nospace;
540936e3df8SBill Yuan 				ioc->src_addr.s_addr = ntohl(s->src_addr);
541936e3df8SBill Yuan 				ioc->dst_addr.s_addr = s->dst_addr;
5428e040facSBill Yuan 				ioc->alias_addr.s_addr = s->alias_addr;
543936e3df8SBill Yuan 				ioc->src_port = s->src_port;
544936e3df8SBill Yuan 				ioc->dst_port = s->dst_port;
5458e040facSBill Yuan 				ioc->alias_port = s->alias_port;
5468e040facSBill Yuan 				ioc->nat_id = n + 1;
5478e040facSBill Yuan 				ioc->cpu_id = cpu;
5488e040facSBill Yuan 				ioc->proto = IPPROTO_ICMP;
5498e040facSBill Yuan 				ioc->direction = 1;
5508e040facSBill Yuan 				ioc->life = s->timestamp +
5518e040facSBill Yuan 					sysctl_var_icmp_timeout - time_uptime;
5528e040facSBill Yuan 				ioc++;
5538e040facSBill Yuan 			}
5544408d548SBill Yuan 
5554408d548SBill Yuan 			LIST_FOREACH(a1, &the->alias, next) {
5564408d548SBill Yuan 			for (i = 0; i < ALIAS_RANGE; i++) {
5574408d548SBill Yuan 				s2 = a1->icmp_in[i];
5584408d548SBill Yuan 				if (s2 == NULL) {
5594408d548SBill Yuan 					continue;
5604408d548SBill Yuan 				}
5614408d548SBill Yuan 
5628e040facSBill Yuan 				total_len += LEN_IOC_NAT_STATE;
5638e040facSBill Yuan 				if (total_len > sopt_size)
5648e040facSBill Yuan 					goto nospace;
5654408d548SBill Yuan 
5664408d548SBill Yuan 				ioc->src_addr.s_addr = ntohl(s2->src_addr);
5674408d548SBill Yuan 				ioc->dst_addr.s_addr = s2->dst_addr;
5684408d548SBill Yuan 				ioc->alias_addr.s_addr = s2->alias_addr;
5694408d548SBill Yuan 				ioc->src_port = s2->src_port;
5704408d548SBill Yuan 				ioc->dst_port = s2->dst_port;
5714408d548SBill Yuan 				ioc->alias_port = s2->alias_port;
5728e040facSBill Yuan 				ioc->nat_id = n + 1;
5738e040facSBill Yuan 				ioc->cpu_id = cpu;
5748e040facSBill Yuan 				ioc->proto = IPPROTO_ICMP;
5758e040facSBill Yuan 				ioc->direction = 0;
5764408d548SBill Yuan 				ioc->life = s2->timestamp +
5778e040facSBill Yuan 					sysctl_var_icmp_timeout - time_uptime;
5788e040facSBill Yuan 				ioc++;
5798e040facSBill Yuan 			}
5808e040facSBill Yuan 			}
5818e040facSBill Yuan 		}
5824408d548SBill Yuan 	}
5838e040facSBill Yuan 
5848e040facSBill Yuan 	/* tcp states */
5858e040facSBill Yuan 	for (cpu = 0; cpu < ncpus; cpu++) {
5868e040facSBill Yuan 		nat_ctx = ip_fw3_nat_ctx[cpu];
5878e040facSBill Yuan 		for (n = 0; n < NAT_ID_MAX; n++) {
5888e040facSBill Yuan 			if (ioc_nat_id == 0 || ioc_nat_id == n + 1) {
5898e040facSBill Yuan 				if (nat_ctx->nats[n] == NULL)
5908e040facSBill Yuan 					break;
5918e040facSBill Yuan 				the = nat_ctx->nats[n];
592936e3df8SBill Yuan 				RB_FOREACH(s, state_tree, &the->rb_tcp_out) {
5938e040facSBill Yuan 					total_len += LEN_IOC_NAT_STATE;
5948e040facSBill Yuan 					if (total_len > sopt_size)
5958e040facSBill Yuan 						goto nospace;
596936e3df8SBill Yuan 					ioc->src_addr.s_addr = ntohl(s->src_addr);
597936e3df8SBill Yuan 					ioc->dst_addr.s_addr = ntohl(s->dst_addr);
5988e040facSBill Yuan 					ioc->alias_addr.s_addr = s->alias_addr;
599936e3df8SBill Yuan 					ioc->src_port = ntohs(s->src_port);
600936e3df8SBill Yuan 					ioc->dst_port = ntohs(s->dst_port);
6018e040facSBill Yuan 					ioc->alias_port = s->alias_port;
6028e040facSBill Yuan 					ioc->nat_id = n + 1;
6038e040facSBill Yuan 					ioc->cpu_id = cpu;
6048e040facSBill Yuan 					ioc->proto = IPPROTO_TCP;
6058e040facSBill Yuan 					ioc->direction = 1;
6068e040facSBill Yuan 					ioc->life = s->timestamp +
6078e040facSBill Yuan 						sysctl_var_tcp_timeout - time_uptime;
6088e040facSBill Yuan 					ioc++;
6098e040facSBill Yuan 				}
6104408d548SBill Yuan 				LIST_FOREACH(a1, &the->alias, next) {
6114408d548SBill Yuan 					for (i = 0; i < ALIAS_RANGE; i++) {
6124408d548SBill Yuan 						s2 = a1->tcp_in[i];
6134408d548SBill Yuan 						if (s2 == NULL) {
6144408d548SBill Yuan 							continue;
6154408d548SBill Yuan 						}
6164408d548SBill Yuan 
6178e040facSBill Yuan 						total_len += LEN_IOC_NAT_STATE;
6188e040facSBill Yuan 						if (total_len > sopt_size)
6198e040facSBill Yuan 							goto nospace;
6204408d548SBill Yuan 
6214408d548SBill Yuan 						ioc->src_addr.s_addr = ntohl(s2->src_addr);
6224408d548SBill Yuan 						ioc->dst_addr.s_addr = s2->dst_addr;
6234408d548SBill Yuan 						ioc->alias_addr.s_addr = s2->alias_addr;
6244408d548SBill Yuan 						ioc->src_port = s2->src_port;
6254408d548SBill Yuan 						ioc->dst_port = s2->dst_port;
6264408d548SBill Yuan 						ioc->alias_port = s2->alias_port;
6278e040facSBill Yuan 						ioc->nat_id = n + 1;
6288e040facSBill Yuan 						ioc->cpu_id = cpu;
6298e040facSBill Yuan 						ioc->proto = IPPROTO_TCP;
6308e040facSBill Yuan 						ioc->direction = 0;
6314408d548SBill Yuan 						ioc->life = s2->timestamp +
6324408d548SBill Yuan 							sysctl_var_icmp_timeout - time_uptime;
6338e040facSBill Yuan 						ioc++;
6348e040facSBill Yuan 					}
6358e040facSBill Yuan 				}
6368e040facSBill Yuan 			}
6378e040facSBill Yuan 		}
6384408d548SBill Yuan 	}
6398e040facSBill Yuan 
6408e040facSBill Yuan 	/* udp states */
6418e040facSBill Yuan 	for (cpu = 0; cpu < ncpus; cpu++) {
6428e040facSBill Yuan 		nat_ctx = ip_fw3_nat_ctx[cpu];
6438e040facSBill Yuan 		for (n = 0; n < NAT_ID_MAX; n++) {
6448e040facSBill Yuan 			if (ioc_nat_id == 0 || ioc_nat_id == n + 1) {
6458e040facSBill Yuan 				if (nat_ctx->nats[n] == NULL)
6468e040facSBill Yuan 					break;
6478e040facSBill Yuan 				the = nat_ctx->nats[n];
648936e3df8SBill Yuan 				RB_FOREACH(s, state_tree, &the->rb_udp_out) {
6498e040facSBill Yuan 					total_len += LEN_IOC_NAT_STATE;
6508e040facSBill Yuan 					if (total_len > sopt_size)
6518e040facSBill Yuan 						goto nospace;
652936e3df8SBill Yuan 					ioc->src_addr.s_addr = ntohl(s->src_addr);
653936e3df8SBill Yuan 					ioc->dst_addr.s_addr = s->dst_addr;
6548e040facSBill Yuan 					ioc->alias_addr.s_addr = s->alias_addr;
655936e3df8SBill Yuan 					ioc->src_port = s->src_port;
656936e3df8SBill Yuan 					ioc->dst_port = s->dst_port;
6578e040facSBill Yuan 					ioc->alias_port = s->alias_port;
6588e040facSBill Yuan 					ioc->nat_id = n + 1;
6598e040facSBill Yuan 					ioc->cpu_id = cpu;
6608e040facSBill Yuan 					ioc->proto = IPPROTO_UDP;
6618e040facSBill Yuan 					ioc->direction = 1;
6628e040facSBill Yuan 					ioc->life = s->timestamp +
6638e040facSBill Yuan 						sysctl_var_udp_timeout - time_uptime;
6648e040facSBill Yuan 					ioc++;
6658e040facSBill Yuan 				}
6664408d548SBill Yuan 				LIST_FOREACH(a1, &the->alias, next) {
6674408d548SBill Yuan 					for (i = 0; i < ALIAS_RANGE; i++) {
6684408d548SBill Yuan 						s2 = a1->udp_in[i];
6694408d548SBill Yuan 						if (s2 == NULL) {
6704408d548SBill Yuan 							continue;
6714408d548SBill Yuan 						}
6724408d548SBill Yuan 
6738e040facSBill Yuan 						total_len += LEN_IOC_NAT_STATE;
6748e040facSBill Yuan 						if (total_len > sopt_size)
6758e040facSBill Yuan 							goto nospace;
6764408d548SBill Yuan 
6774408d548SBill Yuan 						ioc->src_addr.s_addr = ntohl(s2->src_addr);
6784408d548SBill Yuan 						ioc->dst_addr.s_addr = s2->dst_addr;
6794408d548SBill Yuan 						ioc->alias_addr.s_addr = s2->alias_addr;
6804408d548SBill Yuan 						ioc->src_port = s2->src_port;
6814408d548SBill Yuan 						ioc->dst_port = s2->dst_port;
6824408d548SBill Yuan 						ioc->alias_port = s2->alias_port;
6838e040facSBill Yuan 						ioc->nat_id = n + 1;
6848e040facSBill Yuan 						ioc->cpu_id = cpu;
6858e040facSBill Yuan 						ioc->proto = IPPROTO_UDP;
6868e040facSBill Yuan 						ioc->direction = 0;
6874408d548SBill Yuan 						ioc->life = s2->timestamp +
6884408d548SBill Yuan 							sysctl_var_icmp_timeout - time_uptime;
6898e040facSBill Yuan 						ioc++;
6908e040facSBill Yuan 					}
6918e040facSBill Yuan 				}
6928e040facSBill Yuan 			}
6938e040facSBill Yuan 		}
6944408d548SBill Yuan 	}
6958e040facSBill Yuan 	sopt->sopt_valsize = total_len;
6968e040facSBill Yuan 	return 0;
6978e040facSBill Yuan nospace:
6986a03354eSMatthew Dillon 	return 0;
6996a03354eSMatthew Dillon }
7006a03354eSMatthew Dillon 
701936e3df8SBill Yuan void
nat_state_add_dispatch(netmsg_t add_msg)702936e3df8SBill Yuan nat_state_add_dispatch(netmsg_t add_msg)
703936e3df8SBill Yuan {
704936e3df8SBill Yuan 	struct ip_fw3_nat_context *nat_ctx;
705936e3df8SBill Yuan 	struct netmsg_nat_state_add *msg;
706936e3df8SBill Yuan 	struct cfg_nat *nat;
7074408d548SBill Yuan 	struct nat_state2 *s2;
7084408d548SBill Yuan 	struct cfg_alias *alias;
709936e3df8SBill Yuan 
710936e3df8SBill Yuan 	nat_ctx = ip_fw3_nat_ctx[mycpuid];
711936e3df8SBill Yuan 	msg = (struct netmsg_nat_state_add *)add_msg;
712936e3df8SBill Yuan 	nat = nat_ctx->nats[msg->nat_id - 1];
7134408d548SBill Yuan 
7144408d548SBill Yuan 	LIST_FOREACH(alias, &nat->alias, next) {
7154408d548SBill Yuan 		if (alias->ip.s_addr == msg->alias_addr.s_addr) {
7164408d548SBill Yuan 			break;
7174408d548SBill Yuan 		}
718936e3df8SBill Yuan 	}
719936e3df8SBill Yuan 	s2 = msg->state;
7204408d548SBill Yuan 	if (msg->proto == IPPROTO_TCP) {
7214408d548SBill Yuan 		alias->tcp_in[msg->alias_port - ALIAS_BEGIN] = s2;
7224408d548SBill Yuan 	} else {
7234408d548SBill Yuan 		alias->udp_in[msg->alias_port - ALIAS_BEGIN] = s2;
7244408d548SBill Yuan 	}
725936e3df8SBill Yuan }
726936e3df8SBill Yuan 
727df0c223eSBill Yuan /*
728df0c223eSBill Yuan  * Init the RB trees only when the NAT is configured.
729df0c223eSBill Yuan  */
7309187b359SBill Yuan void
nat_add_dispatch(netmsg_t nat_add_msg)7319187b359SBill Yuan nat_add_dispatch(netmsg_t nat_add_msg)
7326a03354eSMatthew Dillon {
733df0c223eSBill Yuan 	struct ip_fw3_nat_context *nat_ctx;
7349187b359SBill Yuan 	struct netmsg_nat_add *msg;
735df0c223eSBill Yuan 	struct ioc_nat *ioc;
736df0c223eSBill Yuan 	struct cfg_nat *nat;
737936e3df8SBill Yuan 	struct cfg_alias *alias;
738936e3df8SBill Yuan 	struct in_addr *ip;
739936e3df8SBill Yuan 	int n;
7406a03354eSMatthew Dillon 
7419187b359SBill Yuan 	msg = (struct netmsg_nat_add *)nat_add_msg;
742df0c223eSBill Yuan 	ioc = &msg->ioc_nat;
743df0c223eSBill Yuan 	nat_ctx = ip_fw3_nat_ctx[mycpuid];
744df0c223eSBill Yuan 
745df0c223eSBill Yuan 	if (nat_ctx->nats[ioc->id - 1] == NULL) {
746936e3df8SBill Yuan 		/* op = set, and nat not exists */
7474408d548SBill Yuan 		nat = kmalloc(LEN_CFG_NAT, M_IPFW3_NAT, M_WAITOK | M_ZERO);
748936e3df8SBill Yuan 		LIST_INIT(&nat->alias);
749936e3df8SBill Yuan 		RB_INIT(&nat->rb_tcp_out);
750936e3df8SBill Yuan 		RB_INIT(&nat->rb_udp_out);
751936e3df8SBill Yuan 		if (mycpuid == 0) {
752936e3df8SBill Yuan 			RB_INIT(&nat->rb_icmp_out);
753936e3df8SBill Yuan 		}
754df0c223eSBill Yuan 		nat->id = ioc->id;
755936e3df8SBill Yuan 		nat->count = ioc->count;
756936e3df8SBill Yuan 		ip = &ioc->ip;
757936e3df8SBill Yuan 		for (n = 0; n < ioc->count; n++) {
758936e3df8SBill Yuan 			alias = kmalloc(LEN_CFG_ALIAS,
7594408d548SBill Yuan 					M_IPFW3_NAT, M_WAITOK | M_ZERO);
760936e3df8SBill Yuan 			memcpy(&alias->ip, ip, LEN_IN_ADDR);
761936e3df8SBill Yuan 			LIST_INSERT_HEAD((&nat->alias), alias, next);
762936e3df8SBill Yuan 			ip++;
763936e3df8SBill Yuan 		}
764df0c223eSBill Yuan 		nat_ctx->nats[ioc->id - 1] = nat;
765df0c223eSBill Yuan 	}
766c3b4f1bfSSepherosa Ziehau 	netisr_forwardmsg_all(&msg->base, mycpuid + 1);
7676a03354eSMatthew Dillon }
7686a03354eSMatthew Dillon 
7696a03354eSMatthew Dillon int
ip_fw3_ctl_nat_add(struct sockopt * sopt)77071194d7aSBill Yuan ip_fw3_ctl_nat_add(struct sockopt *sopt)
7716a03354eSMatthew Dillon {
772df0c223eSBill Yuan 	struct netmsg_nat_add nat_add_msg, *msg;
773df0c223eSBill Yuan 	struct ioc_nat *ioc;
774df0c223eSBill Yuan 	msg = &nat_add_msg;
775936e3df8SBill Yuan 
776df0c223eSBill Yuan 	ioc = (struct ioc_nat *)(sopt->sopt_val);
777df0c223eSBill Yuan 	sooptcopyin(sopt, &msg->ioc_nat, sopt->sopt_valsize,
778df0c223eSBill Yuan 			sizeof(struct ioc_nat));
779df0c223eSBill Yuan 	netmsg_init(&msg->base, NULL, &curthread->td_msgport, 0,
780df0c223eSBill Yuan 			nat_add_dispatch);
781df0c223eSBill Yuan 	netisr_domsg(&msg->base, 0);
7829187b359SBill Yuan 	return 0;
7839187b359SBill Yuan }
7849187b359SBill Yuan 
7859187b359SBill Yuan void
nat_del_dispatch(netmsg_t nat_del_msg)7869187b359SBill Yuan nat_del_dispatch(netmsg_t nat_del_msg)
7879187b359SBill Yuan {
788e930ddabSBill Yuan 	struct ip_fw3_nat_context *nat_ctx;
789e930ddabSBill Yuan 	struct netmsg_nat_del *msg;
790e930ddabSBill Yuan 	struct cfg_nat *nat;
791e930ddabSBill Yuan 	struct nat_state *s, *tmp;
7924408d548SBill Yuan 	struct cfg_alias *alias, *tmp3;
793e930ddabSBill Yuan 
794e930ddabSBill Yuan 	msg = (struct netmsg_nat_del *)nat_del_msg;
795e930ddabSBill Yuan 
796e930ddabSBill Yuan 	nat_ctx = ip_fw3_nat_ctx[mycpuid];
797e930ddabSBill Yuan 	nat = nat_ctx->nats[msg->id - 1];
798e930ddabSBill Yuan 	if (nat != NULL) {
799936e3df8SBill Yuan 		/* the icmp states will only stored in cpu 0 */
800936e3df8SBill Yuan 		RB_FOREACH_SAFE(s, state_tree, &nat->rb_icmp_out, tmp) {
801936e3df8SBill Yuan 			RB_REMOVE(state_tree, &nat->rb_icmp_out, s);
802e930ddabSBill Yuan 			if (s != NULL) {
8034408d548SBill Yuan 				kfree(s, M_IPFW3_NAT);
804e930ddabSBill Yuan 			}
805e930ddabSBill Yuan 		}
8064408d548SBill Yuan 		/*
8074408d548SBill Yuan 		LIST_FOREACH_MUTABLE(s2, &nat->alias->icmp_in, next, tmp2) {
8084408d548SBill Yuan 			LIST_REMOVE(s2, next);
809e930ddabSBill Yuan 			if (s != NULL) {
8104408d548SBill Yuan 				kfree(s, M_IPFW3_NAT);
811e930ddabSBill Yuan 			}
812e930ddabSBill Yuan 		}
8134408d548SBill Yuan 		*/
8144408d548SBill Yuan 
815936e3df8SBill Yuan 		RB_FOREACH_SAFE(s, state_tree, &nat->rb_tcp_out, tmp) {
816936e3df8SBill Yuan 			RB_REMOVE(state_tree, &nat->rb_tcp_out, s);
817e930ddabSBill Yuan 			if (s != NULL) {
8184408d548SBill Yuan 				kfree(s, M_IPFW3_NAT);
819e930ddabSBill Yuan 			}
820e930ddabSBill Yuan 		}
8214408d548SBill Yuan 		/*
8224408d548SBill Yuan 		LIST_FOREACH_MUTABLE(s2, &nat->alias->tcp_in, next, tmp2) {
8234408d548SBill Yuan 			LIST_REMOVE(s2, next);
824e930ddabSBill Yuan 			if (s != NULL) {
8254408d548SBill Yuan 				kfree(s, M_IPFW3_NAT);
826e930ddabSBill Yuan 			}
827e930ddabSBill Yuan 		}
8284408d548SBill Yuan 		*/
829936e3df8SBill Yuan 		RB_FOREACH_SAFE(s, state_tree, &nat->rb_udp_out, tmp) {
830aa7fb6bfSBill Yuan 			RB_REMOVE(state_tree, &nat->rb_udp_out, s);
831e930ddabSBill Yuan 			if (s != NULL) {
8324408d548SBill Yuan 				kfree(s, M_IPFW3_NAT);
833e930ddabSBill Yuan 			}
834e930ddabSBill Yuan 		}
8354408d548SBill Yuan 		/*
8364408d548SBill Yuan 		LIST_FOREACH_MUTABLE(s2, &nat->alias->udp_in, next, tmp2) {
8374408d548SBill Yuan 			LIST_REMOVE(s2, next);
8384408d548SBill Yuan 			if (s != NULL) {
8394408d548SBill Yuan 				kfree(s, M_IPFW3_NAT);
840936e3df8SBill Yuan 			}
8414408d548SBill Yuan 		}
8424408d548SBill Yuan 		*/
8434408d548SBill Yuan 		LIST_FOREACH_MUTABLE(alias, &nat->alias, next, tmp3) {
8444408d548SBill Yuan 			kfree(alias, M_IPFW3_NAT);
8454408d548SBill Yuan 		}
8464408d548SBill Yuan 		kfree(nat, M_IPFW3_NAT);
847e930ddabSBill Yuan 		nat_ctx->nats[msg->id - 1] = NULL;
848e930ddabSBill Yuan 	}
849e930ddabSBill Yuan 	netisr_forwardmsg_all(&nat_del_msg->base, mycpuid + 1);
8509187b359SBill Yuan }
8519187b359SBill Yuan int
ip_fw3_ctl_nat_del(struct sockopt * sopt)85271194d7aSBill Yuan ip_fw3_ctl_nat_del(struct sockopt *sopt)
8539187b359SBill Yuan {
854e930ddabSBill Yuan 	struct netmsg_nat_del nat_del_msg, *msg;
8559187b359SBill Yuan 
8569187b359SBill Yuan 	msg = &nat_del_msg;
857e930ddabSBill Yuan 	msg->id = *((int *)sopt->sopt_val);
8589187b359SBill Yuan 	netmsg_init(&msg->base, NULL, &curthread->td_msgport,
8599187b359SBill Yuan 			0, nat_del_dispatch);
8609187b359SBill Yuan 
861e97b1989SSepherosa Ziehau 	netisr_domsg(&msg->base, 0);
8626a03354eSMatthew Dillon 	return 0;
8636a03354eSMatthew Dillon }
8646a03354eSMatthew Dillon int
ip_fw3_ctl_nat_flush(struct sockopt * sopt)86571194d7aSBill Yuan ip_fw3_ctl_nat_flush(struct sockopt *sopt)
8666a03354eSMatthew Dillon {
8672c8dcae0SBill Yuan 	struct netmsg_nat_del nat_del_msg, *msg;
8682c8dcae0SBill Yuan 	int i;
8692c8dcae0SBill Yuan 	msg = &nat_del_msg;
8702c8dcae0SBill Yuan 	for (i = 0; i < NAT_ID_MAX; i++) {
8712c8dcae0SBill Yuan 		msg->id = i + 1;
8722c8dcae0SBill Yuan 		netmsg_init(&msg->base, NULL, &curthread->td_msgport,
8732c8dcae0SBill Yuan 				0, nat_del_dispatch);
8742c8dcae0SBill Yuan 
8752c8dcae0SBill Yuan 		netisr_domsg(&msg->base, 0);
8762c8dcae0SBill Yuan 	}
8776a03354eSMatthew Dillon 	return 0;
8786a03354eSMatthew Dillon }
8794408d548SBill Yuan 
8809187b359SBill Yuan int
ip_fw3_ctl_nat_sockopt(struct sockopt * sopt)88171194d7aSBill Yuan ip_fw3_ctl_nat_sockopt(struct sockopt *sopt)
8829187b359SBill Yuan {
8839187b359SBill Yuan 	int error = 0;
8849187b359SBill Yuan 	switch (sopt->sopt_name) {
8859187b359SBill Yuan 	case IP_FW_NAT_ADD:
88671194d7aSBill Yuan 		error = ip_fw3_ctl_nat_add(sopt);
8879187b359SBill Yuan 		break;
8889187b359SBill Yuan 	case IP_FW_NAT_DEL:
88971194d7aSBill Yuan 		error = ip_fw3_ctl_nat_del(sopt);
8909187b359SBill Yuan 		break;
8919187b359SBill Yuan 	case IP_FW_NAT_FLUSH:
89271194d7aSBill Yuan 		error = ip_fw3_ctl_nat_flush(sopt);
8939187b359SBill Yuan 		break;
8949187b359SBill Yuan 	case IP_FW_NAT_GET:
89571194d7aSBill Yuan 		error = ip_fw3_ctl_nat_get_cfg(sopt);
8969187b359SBill Yuan 		break;
8979187b359SBill Yuan 	case IP_FW_NAT_GET_RECORD:
89871194d7aSBill Yuan 		error = ip_fw3_ctl_nat_get_record(sopt);
8999187b359SBill Yuan 		break;
9009187b359SBill Yuan 	default:
9019187b359SBill Yuan 		kprintf("ipfw3 nat invalid socket option %d\n",
9029187b359SBill Yuan 				sopt->sopt_name);
9039187b359SBill Yuan 	}
9049187b359SBill Yuan 	return error;
9059187b359SBill Yuan }
9069187b359SBill Yuan 
9079187b359SBill Yuan void
nat_init_ctx_dispatch(netmsg_t msg)9089187b359SBill Yuan nat_init_ctx_dispatch(netmsg_t msg)
9099187b359SBill Yuan {
91071194d7aSBill Yuan 	struct ip_fw3_nat_context *tmp;
91171194d7aSBill Yuan 	tmp = kmalloc(sizeof(struct ip_fw3_nat_context),
9124408d548SBill Yuan 				M_IPFW3_NAT, M_WAITOK | M_ZERO);
913cea1826cSBill Yuan 
914aad10cc6SBill Yuan 	ip_fw3_nat_ctx[mycpuid] = tmp;
915c3b4f1bfSSepherosa Ziehau 	netisr_forwardmsg_all(&msg->base, mycpuid + 1);
9169187b359SBill Yuan }
9179187b359SBill Yuan 
918cea1826cSBill Yuan void
nat_fnit_ctx_dispatch(netmsg_t msg)919cea1826cSBill Yuan nat_fnit_ctx_dispatch(netmsg_t msg)
920cea1826cSBill Yuan {
9214408d548SBill Yuan 	kfree(ip_fw3_nat_ctx[mycpuid], M_IPFW3_NAT);
922cea1826cSBill Yuan 	netisr_forwardmsg_all(&msg->base, mycpuid + 1);
923cea1826cSBill Yuan }
924cea1826cSBill Yuan 
925cfac6146SBill Yuan static void
nat_cleanup_func_dispatch(netmsg_t nmsg)9264408d548SBill Yuan nat_cleanup_func_dispatch(netmsg_t nmsg)
927cfac6146SBill Yuan {
928e4bab615SBill Yuan 	struct nat_state *s, *tmp;
929e4bab615SBill Yuan 	struct ip_fw3_nat_context *nat_ctx;
930e4bab615SBill Yuan 	struct cfg_nat *nat;
9314408d548SBill Yuan 	struct cfg_alias *a1, *tmp2;
9324408d548SBill Yuan 	struct nat_state2 *s2;
9334408d548SBill Yuan 	int i, j;
934e4bab615SBill Yuan 
935e4bab615SBill Yuan 	nat_ctx = ip_fw3_nat_ctx[mycpuid];
9364408d548SBill Yuan 	for (j = 0; j < NAT_ID_MAX; j++) {
9374408d548SBill Yuan 		nat = nat_ctx->nats[j];
938e4bab615SBill Yuan 		if (nat == NULL)
939e4bab615SBill Yuan 			continue;
940e4bab615SBill Yuan 		/* check the nat_states, remove the expired state */
941936e3df8SBill Yuan 		/* the icmp states will only stored in cpu 0 */
942936e3df8SBill Yuan 		RB_FOREACH_SAFE(s, state_tree, &nat->rb_icmp_out, tmp) {
943e4bab615SBill Yuan 			if (time_uptime - s->timestamp > sysctl_var_icmp_timeout) {
944936e3df8SBill Yuan 				RB_REMOVE(state_tree, &nat->rb_icmp_out, s);
9454408d548SBill Yuan 				kfree(s, M_IPFW3_NAT);
946e4bab615SBill Yuan 			}
947e4bab615SBill Yuan 		}
9484408d548SBill Yuan 		LIST_FOREACH_MUTABLE(a1, &nat->alias, next, tmp2) {
9494408d548SBill Yuan 			for (i = 0; i < ALIAS_RANGE; i++) {
9504408d548SBill Yuan 				s2 = a1->icmp_in[i];
9514408d548SBill Yuan 				if (s2 != NULL) {
9524408d548SBill Yuan 					if (time_uptime - s2->timestamp > sysctl_var_icmp_timeout) {
9534408d548SBill Yuan 						a1->icmp_in[i] = NULL;
9544408d548SBill Yuan 						kfree(s2, M_IPFW3_NAT);
955e4bab615SBill Yuan 					}
956e4bab615SBill Yuan 				}
9574408d548SBill Yuan 
9584408d548SBill Yuan 			}
9594408d548SBill Yuan 		}
9604408d548SBill Yuan 
961936e3df8SBill Yuan 		RB_FOREACH_SAFE(s, state_tree, &nat->rb_tcp_out, tmp) {
962e4bab615SBill Yuan 			if (time_uptime - s->timestamp > sysctl_var_tcp_timeout) {
963936e3df8SBill Yuan 				RB_REMOVE(state_tree, &nat->rb_tcp_out, s);
9644408d548SBill Yuan 				kfree(s, M_IPFW3_NAT);
965e4bab615SBill Yuan 			}
966e4bab615SBill Yuan 		}
9674408d548SBill Yuan 		LIST_FOREACH_MUTABLE(a1, &nat->alias, next, tmp2) {
9684408d548SBill Yuan 			for (i = 0; i < ALIAS_RANGE; i++) {
9694408d548SBill Yuan 				s2 = a1->tcp_in[i];
9704408d548SBill Yuan 				if (s2 != NULL) {
9714408d548SBill Yuan 					if (time_uptime - s2->timestamp > sysctl_var_icmp_timeout) {
9724408d548SBill Yuan 						a1->tcp_in[i] = NULL;
9734408d548SBill Yuan 						kfree(s2, M_IPFW3_NAT);
9744408d548SBill Yuan 					}
9754408d548SBill Yuan 				}
9764408d548SBill Yuan 
977e4bab615SBill Yuan 			}
978e4bab615SBill Yuan 		}
979936e3df8SBill Yuan 		RB_FOREACH_SAFE(s, state_tree, &nat->rb_udp_out, tmp) {
980e4bab615SBill Yuan 			if (time_uptime - s->timestamp > sysctl_var_udp_timeout) {
981936e3df8SBill Yuan 				RB_REMOVE(state_tree, &nat->rb_udp_out, s);
9824408d548SBill Yuan 				kfree(s, M_IPFW3_NAT);
9834408d548SBill Yuan 			}
9844408d548SBill Yuan 		}
9854408d548SBill Yuan 		LIST_FOREACH_MUTABLE(a1, &nat->alias, next, tmp2) {
9864408d548SBill Yuan 			for (i = 0; i < ALIAS_RANGE; i++) {
9874408d548SBill Yuan 				s2 = a1->udp_in[i];
9884408d548SBill Yuan 				if (s2 != NULL) {
9894408d548SBill Yuan 					if (time_uptime - s2->timestamp > sysctl_var_icmp_timeout) {
9904408d548SBill Yuan 						a1->udp_in[i] = NULL;
9914408d548SBill Yuan 						kfree(s2, M_IPFW3_NAT);
9924408d548SBill Yuan 					}
9934408d548SBill Yuan 				}
9944408d548SBill Yuan 
995e4bab615SBill Yuan 			}
996e4bab615SBill Yuan 		}
997e4bab615SBill Yuan 	}
998c3b4f1bfSSepherosa Ziehau 	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
999cfac6146SBill Yuan }
1000936e3df8SBill Yuan 
1001cfac6146SBill Yuan static void
ip_fw3_nat_cleanup_func(void * dummy __unused)1002936e3df8SBill Yuan ip_fw3_nat_cleanup_func(void *dummy __unused)
1003cfac6146SBill Yuan {
1004cfac6146SBill Yuan 	struct netmsg_base msg;
1005cfac6146SBill Yuan 	netmsg_init(&msg, NULL, &curthread->td_msgport, 0,
10064408d548SBill Yuan 			nat_cleanup_func_dispatch);
1007cfac6146SBill Yuan 	netisr_domsg(&msg, 0);
1008cfac6146SBill Yuan 
1009aad10cc6SBill Yuan 	callout_reset(&ip_fw3_nat_cleanup_callout,
1010f1b0a2e2SBill Yuan 			sysctl_var_cleanup_interval * hz,
1011936e3df8SBill Yuan 			ip_fw3_nat_cleanup_func, NULL);
1012cfac6146SBill Yuan }
1013cfac6146SBill Yuan 
1014936e3df8SBill Yuan static
ip_fw3_nat_init(void)1015936e3df8SBill Yuan int ip_fw3_nat_init(void)
10166a03354eSMatthew Dillon {
10179187b359SBill Yuan 	struct netmsg_base msg;
10184408d548SBill Yuan 	ip_fw3_register_module(MODULE_NAT_ID, MODULE_NAT_NAME);
10194408d548SBill Yuan 	ip_fw3_register_filter_funcs(MODULE_NAT_ID, O_NAT_NAT,
10206a03354eSMatthew Dillon 			(filter_func)check_nat);
10214408d548SBill Yuan 	ip_fw3_ctl_nat_ptr = ip_fw3_ctl_nat_sockopt;
10229187b359SBill Yuan 	netmsg_init(&msg, NULL, &curthread->td_msgport,
10239187b359SBill Yuan 			0, nat_init_ctx_dispatch);
1024e97b1989SSepherosa Ziehau 	netisr_domsg(&msg, 0);
1025cfac6146SBill Yuan 
1026aad10cc6SBill Yuan 	callout_init_mp(&ip_fw3_nat_cleanup_callout);
1027aad10cc6SBill Yuan 	callout_reset(&ip_fw3_nat_cleanup_callout,
1028f1b0a2e2SBill Yuan 			sysctl_var_cleanup_interval * hz,
1029936e3df8SBill Yuan 			ip_fw3_nat_cleanup_func,
1030cfac6146SBill Yuan 			NULL);
10316a03354eSMatthew Dillon 	return 0;
10326a03354eSMatthew Dillon }
10336a03354eSMatthew Dillon 
10346a03354eSMatthew Dillon static int
ip_fw3_nat_fini(void)1035aad10cc6SBill Yuan ip_fw3_nat_fini(void)
10366a03354eSMatthew Dillon {
1037cea1826cSBill Yuan 	struct netmsg_base msg;
1038cea1826cSBill Yuan 	struct netmsg_nat_del nat_del_msg, *msg1;
1039cea1826cSBill Yuan 	int i;
1040cea1826cSBill Yuan 
1041aad10cc6SBill Yuan 	callout_stop(&ip_fw3_nat_cleanup_callout);
1042cea1826cSBill Yuan 
1043cea1826cSBill Yuan 	msg1 = &nat_del_msg;
1044cea1826cSBill Yuan 	for (i = 0; i < NAT_ID_MAX; i++) {
1045cea1826cSBill Yuan 		msg1->id = i + 1;
1046cea1826cSBill Yuan 		netmsg_init(&msg1->base, NULL, &curthread->td_msgport,
1047cea1826cSBill Yuan 				0, nat_del_dispatch);
1048cea1826cSBill Yuan 
1049cea1826cSBill Yuan 		netisr_domsg(&msg1->base, 0);
1050cea1826cSBill Yuan 	}
1051cea1826cSBill Yuan 
1052cea1826cSBill Yuan 	netmsg_init(&msg, NULL, &curthread->td_msgport,
1053cea1826cSBill Yuan 			0, nat_fnit_ctx_dispatch);
1054cea1826cSBill Yuan 	netisr_domsg(&msg, 0);
1055cea1826cSBill Yuan 
10564408d548SBill Yuan 	return ip_fw3_unregister_module(MODULE_NAT_ID);
10576a03354eSMatthew Dillon }
10586a03354eSMatthew Dillon 
10599187b359SBill Yuan static int
ip_fw3_nat_modevent(module_t mod,int type,void * data)1060aad10cc6SBill Yuan ip_fw3_nat_modevent(module_t mod, int type, void *data)
10616a03354eSMatthew Dillon {
10626a03354eSMatthew Dillon 	switch (type) {
10636a03354eSMatthew Dillon 	case MOD_LOAD:
1064aad10cc6SBill Yuan 		return ip_fw3_nat_init();
10656a03354eSMatthew Dillon 	case MOD_UNLOAD:
1066aad10cc6SBill Yuan 		return ip_fw3_nat_fini();
10676a03354eSMatthew Dillon 	default:
10686a03354eSMatthew Dillon 		break;
10696a03354eSMatthew Dillon 	}
10706a03354eSMatthew Dillon 	return 0;
10716a03354eSMatthew Dillon }
10726a03354eSMatthew Dillon 
1073aad10cc6SBill Yuan moduledata_t ip_fw3_nat_mod = {
10746a03354eSMatthew Dillon 	"ipfw3_nat",
1075aad10cc6SBill Yuan 	ip_fw3_nat_modevent,
10766a03354eSMatthew Dillon 	NULL
10776a03354eSMatthew Dillon };
10786a03354eSMatthew Dillon 
1079aad10cc6SBill Yuan DECLARE_MODULE(ipfw3_nat, ip_fw3_nat_mod,
10806a03354eSMatthew Dillon 		SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
10816a03354eSMatthew Dillon MODULE_DEPEND(ipfw3_nat, ipfw3_basic, 1, 1, 1);
10826a03354eSMatthew Dillon MODULE_VERSION(ipfw3_nat, 1);
1083