xref: /dragonfly/sys/net/ipfw3_nat/ip_fw3_nat.c (revision eca362d0)
1 /*
2  * Copyright (c) 2014 - 2016 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Bill Yuan <bycn82@dragonflybsd.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/mbuf.h>
39 #include <sys/socketvar.h>
40 #include <sys/sysctl.h>
41 #include <sys/systimer.h>
42 #include <sys/thread2.h>
43 #include <sys/in_cksum.h>
44 #include <sys/systm.h>
45 #include <sys/proc.h>
46 #include <sys/socket.h>
47 #include <sys/syslog.h>
48 #include <sys/ucred.h>
49 #include <sys/lock.h>
50 #include <sys/mplock2.h>
51 
52 #include <net/ethernet.h>
53 #include <net/netmsg2.h>
54 #include <net/netisr2.h>
55 #include <net/route.h>
56 #include <net/if.h>
57 
58 #include <netinet/in.h>
59 #include <netinet/ip.h>
60 #include <netinet/ip_icmp.h>
61 #include <netinet/tcp.h>
62 #include <netinet/tcp_timer.h>
63 #include <netinet/tcp_var.h>
64 #include <netinet/tcpip.h>
65 #include <netinet/udp.h>
66 #include <netinet/udp_var.h>
67 #include <netinet/in_systm.h>
68 #include <netinet/in_var.h>
69 #include <netinet/in_pcb.h>
70 #include <netinet/ip_var.h>
71 #include <netinet/ip_divert.h>
72 
73 #include <net/libalias/alias.h>
74 #include <net/libalias/alias_local.h>
75 
76 #include <net/ipfw3/ip_fw.h>
77 
78 #include "ip_fw3_nat.h"
79 
80 
81 struct ipfw_nat_context	*ipfw_nat_ctx[MAXCPU];
82 static struct callout ipfw3_nat_cleanup_callout;
83 extern struct ipfw_context *ipfw_ctx[MAXCPU];
84 extern ip_fw_ctl_t *ipfw_ctl_nat_ptr;
85 extern libalias_housekeeping_t *libalias_housekeeping_prt;
86 
87 static int fw3_nat_cleanup_interval = 5;
88 
89 SYSCTL_NODE(_net_inet_ip, OID_AUTO, fw3_nat, CTLFLAG_RW, 0, "ipfw3 NAT");
90 SYSCTL_INT(_net_inet_ip_fw3_nat, OID_AUTO, cleanup_interval, CTLFLAG_RW,
91 		&fw3_nat_cleanup_interval, 0, "default life time");
92 
93 void
94 check_nat(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
95 		struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len)
96 {
97 	if ((*args)->eh != NULL) {
98 		*cmd_ctl = IP_FW_CTL_NO;
99 		*cmd_val = IP_FW_NOT_MATCH;
100 		return;
101 	}
102 
103 	struct ipfw_nat_context *nat_ctx;
104 	struct cfg_nat *t;
105 	int nat_id;
106 
107 	nat_ctx = ipfw_nat_ctx[mycpuid];
108 	(*args)->rule = *f;
109 	t = ((ipfw_insn_nat *)cmd)->nat;
110 	if (t == NULL) {
111 		nat_id = cmd->arg1;
112 		LOOKUP_NAT((*nat_ctx), nat_id, t);
113 		if (t == NULL) {
114 			*cmd_val = IP_FW_DENY;
115 			*cmd_ctl = IP_FW_CTL_DONE;
116 			return;
117 		}
118 		((ipfw_insn_nat *)cmd)->nat = t;
119 	}
120 	*cmd_val = ipfw_nat(*args, t, (*args)->m);
121 	*cmd_ctl = IP_FW_CTL_NAT;
122 }
123 
124 /* Local prototypes */
125 u_int StartPointIn(struct in_addr, u_short, int);
126 
127 u_int StartPointOut(struct in_addr, struct in_addr,
128 		u_short, u_short, int);
129 
130 u_int
131 StartPointIn(struct in_addr alias_addr,
132 	u_short alias_port,
133 	int link_type)
134 {
135 	u_int n;
136 
137 	n = alias_addr.s_addr;
138 	if (link_type != LINK_PPTP)
139 		n += alias_port;
140 	n += link_type;
141 	return (n % LINK_TABLE_IN_SIZE);
142 }
143 
144 
145 u_int
146 StartPointOut(struct in_addr src_addr, struct in_addr dst_addr,
147 	u_short src_port, u_short dst_port, int link_type)
148 {
149 	u_int n;
150 
151 	n = src_addr.s_addr;
152 	n += dst_addr.s_addr;
153 	if (link_type != LINK_PPTP) {
154 		n += src_port;
155 		n += dst_port;
156 	}
157 	n += link_type;
158 
159 	return (n % LINK_TABLE_OUT_SIZE);
160 }
161 
162 
163 void
164 add_alias_link_dispatch(netmsg_t alias_link_add)
165 {
166 	struct ipfw_nat_context *nat_ctx;
167 	struct netmsg_alias_link_add *msg;
168 	struct libalias *la;
169 	struct alias_link *lnk;
170 	struct cfg_nat *t;
171 	struct tcp_dat *aux_tcp;
172 	u_int start_point;
173 
174 	msg = (struct netmsg_alias_link_add *)alias_link_add;
175 	nat_ctx = ipfw_nat_ctx[mycpuid];
176 	LOOKUP_NAT((*nat_ctx), msg->id, t);
177 	la = t->lib;
178 	lnk = kmalloc(sizeof(struct alias_link), M_ALIAS, M_WAITOK | M_ZERO);
179 	memcpy(lnk, msg->lnk, sizeof(struct alias_link));
180 	lnk->la = la;
181 	if (msg->is_tcp) {
182 		aux_tcp = kmalloc(sizeof(struct tcp_dat),
183 				M_ALIAS, M_WAITOK | M_ZERO);
184 		memcpy(aux_tcp, msg->lnk->data.tcp, sizeof(struct tcp_dat));
185 		lnk->data.tcp = aux_tcp;
186 	}
187 
188 	/* Set up pointers for output lookup table */
189 	start_point = StartPointOut(lnk->src_addr, lnk->dst_addr,
190 			lnk->src_port, lnk->dst_port, lnk->link_type);
191 	LIST_INSERT_HEAD(&la->linkTableOut[start_point], lnk, list_out);
192 
193 	/* Set up pointers for input lookup table */
194 	start_point = StartPointIn(lnk->alias_addr,
195 			lnk->alias_port, lnk->link_type);
196 	LIST_INSERT_HEAD(&la->linkTableIn[start_point], lnk, list_in);
197 	kfree(alias_link_add, M_LWKTMSG);
198 }
199 
200 int
201 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
202 {
203 	struct alias_link *new = NULL;
204 	struct mbuf *mcl;
205 	struct ip *ip;
206 	int ldt, retval, nextcpu;
207 	char *c;
208 
209 	ldt = 0;
210 	retval = 0;
211 	if ((mcl = m_megapullup(m, m->m_pkthdr.len)) ==NULL)
212 		goto badnat;
213 
214 	ip = mtod(mcl, struct ip *);
215 	if (args->eh == NULL) {
216 		ip->ip_len = htons(ip->ip_len);
217 		ip->ip_off = htons(ip->ip_off);
218 	}
219 
220 	if (mcl->m_pkthdr.rcvif == NULL &&
221 			mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
222 		ldt = 1;
223 	}
224 
225 	c = mtod(mcl, char *);
226 	if (args->oif == NULL) {
227 		retval = LibAliasIn(t->lib, c,
228 				mcl->m_len + M_TRAILINGSPACE(mcl), &new);
229 	} else {
230 		retval = LibAliasOut(t->lib, c,
231 				mcl->m_len + M_TRAILINGSPACE(mcl), &new);
232 	}
233 	if (retval != PKT_ALIAS_OK &&
234 			retval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
235 		/* XXX - should i add some logging? */
236 		m_free(mcl);
237 badnat:
238 		args->m = NULL;
239 		return IP_FW_DENY;
240 	}
241 	mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
242 
243 	if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
244 			ip->ip_p == IPPROTO_TCP) {
245 		struct tcphdr 	*th;
246 
247 		th = (struct tcphdr *)(ip + 1);
248 		if (th->th_x2){
249 			ldt = 1;
250 		}
251 	}
252 	if (new != NULL &&
253 			(ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP)) {
254 		ip_hashfn(&mcl, 0);
255 		nextcpu = netisr_hashcpu(m->m_pkthdr.hash);
256 		if (nextcpu != mycpuid) {
257 			struct netmsg_alias_link_add *msg;
258 			msg = kmalloc(sizeof(struct netmsg_alias_link_add),
259 					M_LWKTMSG, M_NOWAIT | M_ZERO);
260 
261 			netmsg_init(&msg->base, NULL,
262 					&curthread->td_msgport, 0,
263 					add_alias_link_dispatch);
264 			msg->lnk = new;
265 			msg->id = t->id;
266 			if (ip->ip_p == IPPROTO_TCP) {
267 				msg->is_tcp = 1;
268 			}
269 			if (args->oif == NULL) {
270 				msg->is_outgoing = 0;
271 			} else {
272 				msg->is_outgoing = 1;
273 			}
274 			netisr_sendmsg(&msg->base, nextcpu);
275 		}
276 	}
277 	if (ldt) {
278 		struct tcphdr 	*th;
279 		struct udphdr 	*uh;
280 		u_short cksum;
281 
282 		ip->ip_len = ntohs(ip->ip_len);
283 		cksum = in_pseudo(
284 				ip->ip_src.s_addr,
285 				ip->ip_dst.s_addr,
286 				htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2))
287 				);
288 
289 		switch (ip->ip_p) {
290 		case IPPROTO_TCP:
291 			th = (struct tcphdr *)(ip + 1);
292 			th->th_x2 = 0;
293 			th->th_sum = cksum;
294 			mcl->m_pkthdr.csum_data =
295 				offsetof(struct tcphdr, th_sum);
296 			break;
297 		case IPPROTO_UDP:
298 			uh = (struct udphdr *)(ip + 1);
299 			uh->uh_sum = cksum;
300 			mcl->m_pkthdr.csum_data =
301 				offsetof(struct udphdr, uh_sum);
302 			break;
303 		}
304 		/*
305 		 * No hw checksum offloading: do it
306 		 * by ourself.
307 		 */
308 		if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) {
309 			in_delayed_cksum(mcl);
310 			mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
311 		}
312 		ip->ip_len = htons(ip->ip_len);
313 	}
314 
315 	if (args->eh == NULL) {
316 		ip->ip_len = ntohs(ip->ip_len);
317 		ip->ip_off = ntohs(ip->ip_off);
318 	}
319 
320 	args->m = mcl;
321 	return IP_FW_NAT;
322 }
323 
324 static void
325 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
326 {
327 	struct cfg_redir *r, *tmp_r;
328 	struct cfg_spool *s, *tmp_s;
329 	int i, num;
330 
331 	LIST_FOREACH_MUTABLE(r, head, _next, tmp_r) {
332 		num = 1; /* Number of alias_link to delete. */
333 		switch (r->mode) {
334 		case REDIR_PORT:
335 			num = r->pport_cnt;
336 			/* FALLTHROUGH */
337 		case REDIR_ADDR:
338 		case REDIR_PROTO:
339 			/* Delete all libalias redirect entry. */
340 			for (i = 0; i < num; i++)
341 				LibAliasRedirectDelete(n->lib, r->alink[i]);
342 			/* Del spool cfg if any. */
343 			LIST_FOREACH_MUTABLE(s, &r->spool_chain, _next, tmp_s) {
344 				LIST_REMOVE(s, _next);
345 				kfree(s, M_IPFW_NAT);
346 			}
347 			kfree(r->alink, M_IPFW_NAT);
348 			LIST_REMOVE(r, _next);
349 			kfree(r, M_IPFW_NAT);
350 			break;
351 		default:
352 			kprintf("unknown redirect mode: %u\n", r->mode);
353 			/* XXX - panic?!?!? */
354 			break;
355 		}
356 	}
357 }
358 
359 static int
360 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
361 {
362 	struct cfg_redir *r, *ser_r;
363 	struct cfg_spool *s, *ser_s;
364 	int cnt, off, i;
365 	char *panic_err;
366 
367 	for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
368 		ser_r = (struct cfg_redir *)&buf[off];
369 		r = kmalloc(SOF_REDIR, M_IPFW_NAT, M_WAITOK | M_ZERO);
370 		memcpy(r, ser_r, SOF_REDIR);
371 		LIST_INIT(&r->spool_chain);
372 		off += SOF_REDIR;
373 		r->alink = kmalloc(sizeof(struct alias_link *) * r->pport_cnt,
374 		    M_IPFW_NAT, M_WAITOK | M_ZERO);
375 		switch (r->mode) {
376 		case REDIR_ADDR:
377 			r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
378 			    r->paddr);
379 			break;
380 		case REDIR_PORT:
381 			for (i = 0 ; i < r->pport_cnt; i++) {
382 				/* If remotePort is all ports, set it to 0. */
383 				u_short remotePortCopy = r->rport + i;
384 				if (r->rport_cnt == 1 && r->rport == 0)
385 					remotePortCopy = 0;
386 				r->alink[i] = LibAliasRedirectPort(ptr->lib,
387 				    r->laddr, htons(r->lport + i), r->raddr,
388 				    htons(remotePortCopy), r->paddr,
389 				    htons(r->pport + i), r->proto);
390 				if (r->alink[i] == NULL) {
391 					r->alink[0] = NULL;
392 					break;
393 				}
394 			}
395 			break;
396 		case REDIR_PROTO:
397 			r->alink[0] = LibAliasRedirectProto(ptr->lib, r->laddr,
398 			    r->raddr, r->paddr, r->proto);
399 			break;
400 		default:
401 			kprintf("unknown redirect mode: %u\n", r->mode);
402 			break;
403 		}
404 		if (r->alink[0] == NULL) {
405 			panic_err = "LibAliasRedirect* returned NULL";
406 			goto bad;
407 		}
408 		/* LSNAT handling. */
409 		for (i = 0; i < r->spool_cnt; i++) {
410 			ser_s = (struct cfg_spool *)&buf[off];
411 			s = kmalloc(SOF_REDIR, M_IPFW_NAT, M_WAITOK | M_ZERO);
412 			memcpy(s, ser_s, SOF_SPOOL);
413 			LibAliasAddServer(ptr->lib, r->alink[0],
414 			    s->addr, htons(s->port));
415 			off += SOF_SPOOL;
416 			/* Hook spool entry. */
417 			HOOK_SPOOL(&r->spool_chain, s);
418 		}
419 		/* And finally hook this redir entry. */
420 		HOOK_REDIR(&ptr->redir_chain, r);
421 	}
422 	return 1;
423 bad:
424 	/* something really bad happened: panic! */
425 	panic("%s\n", panic_err);
426 }
427 
428 int
429 ipfw_ctl_nat_get_cfg(struct sockopt *sopt)
430 {
431 	struct ipfw_nat_context *nat_ctx;
432 	struct cfg_nat *n;
433 	struct cfg_redir *r;
434 	struct cfg_spool *s;
435 	int nat_cnt, off, nat_cfg_size;
436 	size_t size;
437 	uint8_t *data;
438 
439 	nat_cnt = 0;
440 	nat_cfg_size = 0;
441 	off = sizeof(nat_cnt);
442 
443 	nat_ctx = ipfw_nat_ctx[mycpuid];
444 	size = sopt->sopt_valsize;
445 
446 	data = sopt->sopt_val;
447 	/* count the size of nat cfg */
448 	LIST_FOREACH(n, &((*nat_ctx).nat), _next) {
449 		nat_cfg_size += SOF_NAT;
450 	}
451 
452 	LIST_FOREACH(n, &((*nat_ctx).nat), _next) {
453 		nat_cnt++;
454 		if (off + SOF_NAT < size) {
455 			bcopy(n, &data[off], SOF_NAT);
456 			off += SOF_NAT;
457 			LIST_FOREACH(r, &n->redir_chain, _next) {
458 				if (off + SOF_REDIR < size) {
459 					bcopy(r, &data[off], SOF_REDIR);
460 					off += SOF_REDIR;
461 					LIST_FOREACH(s, &r->spool_chain,
462 						_next) {
463 						if (off + SOF_SPOOL < size) {
464 							bcopy(s, &data[off],
465 								SOF_SPOOL);
466 							off += SOF_SPOOL;
467 						} else
468 							goto nospace;
469 					}
470 				} else
471 					goto nospace;
472 			}
473 		} else
474 			goto nospace;
475 	}
476 	bcopy(&nat_cnt, data, sizeof(nat_cnt));
477 	sopt->sopt_valsize = nat_cfg_size;
478 	return 0;
479 nospace:
480 	bzero(sopt->sopt_val, sopt->sopt_valsize);
481 	sopt->sopt_valsize = nat_cfg_size;
482 	return 0;
483 }
484 
485 int
486 ipfw_ctl_nat_get_record(struct sockopt *sopt)
487 {
488 	struct ipfw_nat_context *nat_ctx;
489 	struct cfg_nat *t;
490 	struct alias_link *lnk;
491 	struct libalias *la;
492 	size_t sopt_size, all_lnk_size = 0;
493 	int i, *nat_id, id, n;
494 	struct ipfw_ioc_nat_state *nat_state;
495 
496 	int cpu;
497 
498 	nat_id = (int *)(sopt->sopt_val);
499 	n = *nat_id;
500 	sopt_size = sopt->sopt_valsize;
501 	nat_state = (struct ipfw_ioc_nat_state *)sopt->sopt_val;
502 	for (cpu = 0; cpu < ncpus; cpu++) {
503 		nat_ctx = ipfw_nat_ctx[cpu];
504 		id = n;
505 		LOOKUP_NAT((*nat_ctx), id, t);
506 		if (t != NULL) {
507 			la = t->lib;
508 			LIBALIAS_LOCK_ASSERT(la);
509 			for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) {
510 				LIST_FOREACH(lnk, &la->linkTableOut[i],
511 						list_out) {
512 					all_lnk_size += sizeof(*nat_state);
513 					if (all_lnk_size > sopt_size)
514 						goto nospace;
515 					nat_state->src_addr = lnk->src_addr;
516 					nat_state->dst_addr = lnk->dst_addr;
517 					nat_state->alias_addr = lnk->alias_addr;
518 					nat_state->src_port = lnk->src_port;
519 					nat_state->dst_port = lnk->dst_port;
520 					nat_state->alias_port = lnk->alias_port;
521 					nat_state->link_type = lnk->link_type;
522 					nat_state->timestamp = lnk->timestamp;
523 					nat_state->cpuid = cpu;
524 					nat_state->is_outgoing = 1;
525 					nat_state++;
526 				}
527 				LIST_FOREACH(lnk, &la->linkTableIn[i],
528 						list_out) {
529 					all_lnk_size += sizeof(*nat_state);
530 					if (all_lnk_size > sopt_size)
531 						goto nospace;
532 					nat_state->src_addr = lnk->src_addr;
533 					nat_state->dst_addr = lnk->dst_addr;
534 					nat_state->alias_addr = lnk->alias_addr;
535 					nat_state->src_port = lnk->src_port;
536 					nat_state->dst_port = lnk->dst_port;
537 					nat_state->alias_port = lnk->alias_port;
538 					nat_state->link_type = lnk->link_type;
539 					nat_state->timestamp = lnk->timestamp;
540 					nat_state->cpuid = cpu;
541 					nat_state->is_outgoing = 0;
542 					nat_state++;
543 				}
544 			}
545 		}
546 	}
547 	sopt->sopt_valsize = all_lnk_size;
548 	return 0;
549 nospace:
550 	return 0;
551 }
552 
553 void
554 nat_add_dispatch(netmsg_t nat_add_msg)
555 {
556 	struct ipfw_nat_context *nat_ctx;
557 	struct cfg_nat *ptr, *ser_n;
558 	struct netmsg_nat_add *msg;
559 
560 	msg = (struct netmsg_nat_add *)nat_add_msg;
561 
562 	ser_n = (struct cfg_nat *)(msg->buf);
563 
564 	/* New rule: allocate and init new instance. */
565 	ptr = kmalloc(sizeof(struct cfg_nat), M_IPFW_NAT, M_WAITOK | M_ZERO);
566 
567 	ptr->lib = LibAliasInit(NULL);
568 	if (ptr->lib == NULL) {
569 		kfree(ptr, M_IPFW_NAT);
570 		kfree(msg->buf, M_IPFW_NAT);
571 	}
572 
573 	LIST_INIT(&ptr->redir_chain);
574 	/*
575 	 * Basic nat configuration.
576 	 */
577 	ptr->id = ser_n->id;
578 	/*
579 	 * XXX - what if this rule doesn't nat any ip and just
580 	 * redirect?
581 	 * do we set aliasaddress to 0.0.0.0?
582 	 */
583 	ptr->ip = ser_n->ip;
584 	ptr->redir_cnt = ser_n->redir_cnt;
585 	ptr->mode = ser_n->mode;
586 
587 	LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode);
588 	LibAliasSetAddress(ptr->lib, ptr->ip);
589 	memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE);
590 
591 	/* Add new entries. */
592 	add_redir_spool_cfg(&msg->buf[(sizeof(struct cfg_nat))], ptr);
593 
594 	nat_ctx = ipfw_nat_ctx[mycpuid];
595 	HOOK_NAT(&(nat_ctx->nat), ptr);
596 	netisr_forwardmsg_all(&msg->base, mycpuid + 1);
597 }
598 
599 int
600 ipfw_ctl_nat_add(struct sockopt *sopt)
601 {
602 	struct ipfw_nat_context *nat_ctx;
603 	struct cfg_nat *ptr, *ser_n;
604 	ser_n = (struct cfg_nat *)(sopt->sopt_val);
605 
606 	nat_ctx = ipfw_nat_ctx[mycpuid];
607 	/*
608 	 * Find/create nat rule.
609 	 */
610 	LOOKUP_NAT((*nat_ctx), ser_n->id, ptr);
611 
612 	if (ptr == NULL) {
613 		struct netmsg_nat_add nat_add_msg;
614 		struct netmsg_nat_add *msg;
615 
616 		msg = &nat_add_msg;
617 		msg->buf = kmalloc(sopt->sopt_valsize,
618 				M_IPFW_NAT, M_WAITOK | M_ZERO);
619 
620 		sooptcopyin(sopt, msg->buf, sopt->sopt_valsize,
621 				sizeof(struct cfg_nat));
622 
623 		netmsg_init(&msg->base, NULL, &curthread->td_msgport,
624 				0, nat_add_dispatch);
625 
626 
627 		netisr_domsg(&msg->base, 0);
628 		kfree(msg->buf, M_IPFW_NAT);
629 	} else {
630 		goto done;
631 	}
632 done:
633 	return 0;
634 }
635 
636 void
637 nat_del_dispatch(netmsg_t nat_del_msg)
638 {
639 	struct ipfw_nat_context *nat_ctx;
640 	struct ipfw_context *ctx;
641 	struct cfg_nat *n, *tmp;
642 	struct netmsg_nat_del *msg;
643 	struct ip_fw *f;
644 	ipfw_insn *cmd;
645 	int id;
646 
647 	msg = (struct netmsg_nat_del *)nat_del_msg;
648 	id = msg->id;
649 
650 	nat_ctx = ipfw_nat_ctx[mycpuid];
651 	LOOKUP_NAT((*nat_ctx), id, n);
652 	if (n == NULL) {
653 	}
654 
655 	/*
656 	 * stop deleting when this cfg_nat was in use in ipfw_ctx
657 	 */
658 	ctx = ipfw_ctx[mycpuid];
659 	for (f = ctx->ipfw_rule_chain; f; f = f->next) {
660 		cmd = ACTION_PTR(f);
661 		if ((int)cmd->module == MODULE_NAT_ID &&
662 				(int)cmd->opcode == O_NAT_NAT) {
663 			tmp = ((ipfw_insn_nat *)cmd)->nat;
664 			if (tmp != NULL && tmp->id == n->id) {
665 			}
666 		}
667 	}
668 
669 	UNHOOK_NAT(n);
670 	del_redir_spool_cfg(n, &n->redir_chain);
671 	LibAliasUninit(n->lib);
672 	kfree(n, M_IPFW_NAT);
673 }
674 
675 int
676 ipfw_ctl_nat_del(struct sockopt *sopt)
677 {
678 	struct netmsg_nat_del nat_del_msg;
679 	struct netmsg_nat_del *msg;
680 	int *id;
681 
682 	msg = &nat_del_msg;
683 	id = sopt->sopt_val;
684 	msg->id = *id;
685 
686 	netmsg_init(&msg->base, NULL, &curthread->td_msgport,
687 			0, nat_del_dispatch);
688 
689 	netisr_domsg(&msg->base, 0);
690 	return 0;
691 }
692 
693 int
694 ipfw_ctl_nat_flush(struct sockopt *sopt)
695 {
696 	struct ipfw_nat_context *nat_ctx;
697 	struct ipfw_context *ctx;
698 	struct cfg_nat *ptr, *tmp;
699 	struct ip_fw *f;
700 	ipfw_insn *cmd;
701 	int cpu;
702 
703 	/*
704 	 * stop flushing when any cfg_nat was in use in ipfw_ctx
705 	 */
706 	for (cpu = 0; cpu < ncpus; cpu++) {
707 		ctx = ipfw_ctx[cpu];
708 		for (f = ctx->ipfw_rule_chain; f; f = f->next) {
709 			cmd = ACTION_PTR(f);
710 			if ((int)cmd->module == MODULE_NAT_ID &&
711 				(int)cmd->opcode == O_NAT_NAT) {
712 				return EINVAL;
713 			}
714 		}
715 	}
716 
717 	nat_ctx = ipfw_nat_ctx[mycpuid];
718 
719 	LIST_FOREACH_MUTABLE(ptr, &(nat_ctx->nat), _next, tmp) {
720 		LIST_REMOVE(ptr, _next);
721 		del_redir_spool_cfg(ptr, &ptr->redir_chain);
722 		LibAliasUninit(ptr->lib);
723 		kfree(ptr, M_IPFW_NAT);
724 	}
725 	return 0;
726 }
727 
728 int
729 ipfw_ctl_nat_sockopt(struct sockopt *sopt)
730 {
731 	int error = 0;
732 	switch (sopt->sopt_name) {
733 	case IP_FW_NAT_ADD:
734 		error = ipfw_ctl_nat_add(sopt);
735 		break;
736 	case IP_FW_NAT_DEL:
737 		error = ipfw_ctl_nat_del(sopt);
738 		break;
739 	case IP_FW_NAT_FLUSH:
740 		error = ipfw_ctl_nat_flush(sopt);
741 		break;
742 	case IP_FW_NAT_GET:
743 		error = ipfw_ctl_nat_get_cfg(sopt);
744 		break;
745 	case IP_FW_NAT_GET_RECORD:
746 		error = ipfw_ctl_nat_get_record(sopt);
747 		break;
748 	default:
749 		kprintf("ipfw3 nat invalid socket option %d\n",
750 				sopt->sopt_name);
751 	}
752 	return error;
753 }
754 
755 void
756 nat_init_ctx_dispatch(netmsg_t msg)
757 {
758 	struct ipfw_nat_context *tmp;
759 	tmp = kmalloc(sizeof(struct ipfw_nat_context),
760 				M_IPFW_NAT, M_WAITOK | M_ZERO);
761 	ipfw_nat_ctx[mycpuid] = tmp;
762 	netisr_forwardmsg_all(&msg->base, mycpuid + 1);
763 }
764 
765 static void
766 ipfw3_nat_cleanup_func_dispatch(netmsg_t nmsg)
767 {
768 	struct ipfw_nat_context *nctx;
769 	struct cfg_nat *ptr, *tmp;
770 
771 	nctx = ipfw_nat_ctx[mycpuid];
772 	LIST_FOREACH_MUTABLE(ptr, &(nctx->nat), _next, tmp) {
773 		if (libalias_housekeeping_prt != NULL) {
774 			(*libalias_housekeeping_prt)(ptr->lib);
775 		}
776 	}
777 	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
778 }
779 
780 static void
781 ipfw3_nat_cleanup_func(void *dummy __unused)
782 {
783 	struct netmsg_base msg;
784 	netmsg_init(&msg, NULL, &curthread->td_msgport, 0,
785 			ipfw3_nat_cleanup_func_dispatch);
786 	netisr_domsg(&msg, 0);
787 
788 	callout_reset(&ipfw3_nat_cleanup_callout,
789 			fw3_nat_cleanup_interval * hz,
790 			ipfw3_nat_cleanup_func,
791 			NULL);
792 }
793 
794 static
795 int ipfw_nat_init(void)
796 {
797 	struct netmsg_base msg;
798 	register_ipfw_module(MODULE_NAT_ID, MODULE_NAT_NAME);
799 	register_ipfw_filter_funcs(MODULE_NAT_ID, O_NAT_NAT,
800 			(filter_func)check_nat);
801 	ipfw_ctl_nat_ptr = ipfw_ctl_nat_sockopt;
802 	netmsg_init(&msg, NULL, &curthread->td_msgport,
803 			0, nat_init_ctx_dispatch);
804 	netisr_domsg(&msg, 0);
805 
806 	callout_init_mp(&ipfw3_nat_cleanup_callout);
807 	callout_reset(&ipfw3_nat_cleanup_callout,
808 			fw3_nat_cleanup_interval * hz,
809 			ipfw3_nat_cleanup_func,
810 			NULL);
811 	return 0;
812 }
813 
814 static int
815 ipfw_nat_fini(void)
816 {
817 	struct cfg_nat *ptr, *tmp;
818 	struct ipfw_nat_context *ctx;
819 	int cpu;
820 
821 	callout_stop(&ipfw3_nat_cleanup_callout);
822 
823 	for (cpu = 0; cpu < ncpus; cpu++) {
824 		ctx = ipfw_nat_ctx[cpu];
825 		if(ctx != NULL) {
826 			LIST_FOREACH_MUTABLE(ptr, &(ctx->nat), _next, tmp) {
827 				LIST_REMOVE(ptr, _next);
828 				del_redir_spool_cfg(ptr, &ptr->redir_chain);
829 				LibAliasUninit(ptr->lib);
830 				kfree(ptr, M_IPFW_NAT);
831 			}
832 
833 			kfree(ctx, M_IPFW_NAT);
834 			ipfw_nat_ctx[cpu] = NULL;
835 		}
836 	}
837 	ipfw_ctl_nat_ptr = NULL;
838 
839 	return unregister_ipfw_module(MODULE_NAT_ID);
840 }
841 
842 static int
843 ipfw_nat_modevent(module_t mod, int type, void *data)
844 {
845 	switch (type) {
846 	case MOD_LOAD:
847 		return ipfw_nat_init();
848 	case MOD_UNLOAD:
849 		return ipfw_nat_fini();
850 	default:
851 		break;
852 	}
853 	return 0;
854 }
855 
856 moduledata_t ipfw_nat_mod = {
857 	"ipfw3_nat",
858 	ipfw_nat_modevent,
859 	NULL
860 };
861 
862 DECLARE_MODULE(ipfw3_nat, ipfw_nat_mod,
863 		SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
864 MODULE_DEPEND(ipfw3_nat, libalias, 1, 1, 1);
865 MODULE_DEPEND(ipfw3_nat, ipfw3_basic, 1, 1, 1);
866 MODULE_VERSION(ipfw3_nat, 1);
867