xref: /dragonfly/sys/net/ipfw3_nat/ip_fw3_nat.c (revision 6700dd34)
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 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,
342 							r->alink[i]);
343 
344 				/* Del spool cfg if any. */
345 				LIST_FOREACH_MUTABLE(s, &r->spool_chain,
346 						_next, tmp_s) {
347 					LIST_REMOVE(s, _next);
348 					kfree(s, M_IPFW_NAT);
349 				}
350 				kfree(r->alink, M_IPFW_NAT);
351 				LIST_REMOVE(r, _next);
352 				kfree(r, M_IPFW_NAT);
353 				break;
354 			default:
355 				kprintf("unknown redirect mode: %u\n", r->mode);
356 				/* XXX - panic?!?!? */
357 				break;
358 		}
359 	}
360 }
361 
362 int
363 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
364 {
365 	struct cfg_redir *r, *ser_r;
366 	struct cfg_spool *s, *ser_s;
367 	int cnt, off, i;
368 	char *panic_err;
369 
370 	for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
371 		ser_r = (struct cfg_redir *)&buf[off];
372 		r = kmalloc(SOF_REDIR, M_IPFW_NAT, M_WAITOK | M_ZERO);
373 		memcpy(r, ser_r, SOF_REDIR);
374 		LIST_INIT(&r->spool_chain);
375 		off += SOF_REDIR;
376 		r->alink = kmalloc(sizeof(struct alias_link *) * r->pport_cnt,
377 				M_IPFW_NAT, M_WAITOK | M_ZERO);
378 		switch (r->mode) {
379 			case REDIR_ADDR:
380 				r->alink[0] = LibAliasRedirectAddr(ptr->lib,
381 							r->laddr, r->paddr);
382 				break;
383 			case REDIR_PORT:
384 				for (i = 0 ; i < r->pport_cnt; i++) {
385 					/*
386 					 * If remotePort is all ports
387 					 * set it to 0.
388 					 */
389 					u_short remotePortCopy = r->rport + i;
390 					if (r->rport_cnt == 1 && r->rport == 0)
391 						remotePortCopy = 0;
392 						r->alink[i] =
393 
394 						LibAliasRedirectPort(ptr->lib,
395 						r->laddr,htons(r->lport + i),
396 						r->raddr,htons(remotePortCopy),
397 						r->paddr,htons(r->pport + i),
398 						r->proto);
399 
400 					if (r->alink[i] == NULL) {
401 						r->alink[0] = NULL;
402 						break;
403 					}
404 				}
405 				break;
406 			case REDIR_PROTO:
407 				r->alink[0] = LibAliasRedirectProto(ptr->lib,
408 					r->laddr, r->raddr, r->paddr, r->proto);
409 				break;
410 			default:
411 				kprintf("unknown redirect mode: %u\n", r->mode);
412 				break;
413 		}
414 		if (r->alink[0] == NULL) {
415 			panic_err = "LibAliasRedirect* returned NULL";
416 			goto bad;
417 		} else /* LSNAT handling. */
418 			for (i = 0; i < r->spool_cnt; i++) {
419 				ser_s = (struct cfg_spool *)&buf[off];
420 				s = kmalloc(SOF_REDIR, M_IPFW_NAT,
421 						M_WAITOK | M_ZERO);
422 				memcpy(s, ser_s, SOF_SPOOL);
423 				LibAliasAddServer(ptr->lib, r->alink[0],
424 						s->addr, htons(s->port));
425 				off += SOF_SPOOL;
426 				/* Hook spool entry. */
427 				HOOK_SPOOL(&r->spool_chain, s);
428 			}
429 		/* And finally hook this redir entry. */
430 		HOOK_REDIR(&ptr->redir_chain, r);
431 	}
432 	return 1;
433 bad:
434 	/* something really bad happened: panic! */
435 	panic("%s\n", panic_err);
436 }
437 
438 int
439 ipfw_ctl_nat_get_cfg(struct sockopt *sopt)
440 {
441 	struct ipfw_nat_context *nat_ctx;
442 	struct cfg_nat *n;
443 	struct cfg_redir *r;
444 	struct cfg_spool *s;
445 	int nat_cnt, off, nat_cfg_size;
446 	size_t size;
447 	uint8_t *data;
448 
449 	nat_cnt = 0;
450 	nat_cfg_size = 0;
451 	off = sizeof(nat_cnt);
452 
453 	nat_ctx = ipfw_nat_ctx[mycpuid];
454 	size = sopt->sopt_valsize;
455 
456 	data = sopt->sopt_val;
457 	/* count the size of nat cfg */
458 	LIST_FOREACH(n, &((*nat_ctx).nat), _next) {
459 		nat_cfg_size += SOF_NAT;
460 	}
461 
462 	LIST_FOREACH(n, &((*nat_ctx).nat), _next) {
463 		nat_cnt++;
464 		if (off + SOF_NAT < size) {
465 			bcopy(n, &data[off], SOF_NAT);
466 			off += SOF_NAT;
467 			LIST_FOREACH(r, &n->redir_chain, _next) {
468 				if (off + SOF_REDIR < size) {
469 					bcopy(r, &data[off], SOF_REDIR);
470 					off += SOF_REDIR;
471 					LIST_FOREACH(s, &r->spool_chain,
472 						_next) {
473 						if (off + SOF_SPOOL < size) {
474 							bcopy(s, &data[off],
475 								SOF_SPOOL);
476 							off += SOF_SPOOL;
477 						} else
478 							goto nospace;
479 					}
480 				} else
481 					goto nospace;
482 			}
483 		} else
484 			goto nospace;
485 	}
486 	bcopy(&nat_cnt, data, sizeof(nat_cnt));
487 	sopt->sopt_valsize = nat_cfg_size;
488 	return 0;
489 nospace:
490 	bzero(sopt->sopt_val, sopt->sopt_valsize);
491 	sopt->sopt_valsize = nat_cfg_size;
492 	return 0;
493 }
494 
495 int
496 ipfw_ctl_nat_get_record(struct sockopt *sopt)
497 {
498 	struct ipfw_nat_context *nat_ctx;
499 	struct cfg_nat *t;
500 	struct alias_link *lnk;
501 	struct libalias *la;
502 	size_t sopt_size, all_lnk_size = 0;
503 	int i, *nat_id, id, n;
504 	struct ipfw_ioc_nat_state *nat_state;
505 
506 	int cpu;
507 
508 	nat_id = (int *)(sopt->sopt_val);
509 	n = *nat_id;
510 	sopt_size = sopt->sopt_valsize;
511 	nat_state = (struct ipfw_ioc_nat_state *)sopt->sopt_val;
512 	for (cpu = 0; cpu < ncpus; cpu++) {
513 		nat_ctx = ipfw_nat_ctx[cpu];
514 		id = n;
515 		LOOKUP_NAT((*nat_ctx), id, t);
516 		if (t != NULL) {
517 			la = t->lib;
518 			LIBALIAS_LOCK_ASSERT(la);
519 			for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) {
520 				LIST_FOREACH(lnk, &la->linkTableOut[i],
521 						list_out) {
522 					all_lnk_size += sizeof(*nat_state);
523 					if (all_lnk_size > sopt_size)
524 						goto nospace;
525 					nat_state->src_addr = lnk->src_addr;
526 					nat_state->dst_addr = lnk->dst_addr;
527 					nat_state->alias_addr = lnk->alias_addr;
528 					nat_state->src_port = lnk->src_port;
529 					nat_state->dst_port = lnk->dst_port;
530 					nat_state->alias_port = lnk->alias_port;
531 					nat_state->link_type = lnk->link_type;
532 					nat_state->timestamp = lnk->timestamp;
533 					nat_state->cpuid = cpu;
534 					nat_state->is_outgoing = 1;
535 					nat_state++;
536 				}
537 				LIST_FOREACH(lnk, &la->linkTableIn[i],
538 						list_out) {
539 					all_lnk_size += sizeof(*nat_state);
540 					if (all_lnk_size > sopt_size)
541 						goto nospace;
542 					nat_state->src_addr = lnk->src_addr;
543 					nat_state->dst_addr = lnk->dst_addr;
544 					nat_state->alias_addr = lnk->alias_addr;
545 					nat_state->src_port = lnk->src_port;
546 					nat_state->dst_port = lnk->dst_port;
547 					nat_state->alias_port = lnk->alias_port;
548 					nat_state->link_type = lnk->link_type;
549 					nat_state->timestamp = lnk->timestamp;
550 					nat_state->cpuid = cpu;
551 					nat_state->is_outgoing = 0;
552 					nat_state++;
553 				}
554 			}
555 		}
556 	}
557 	sopt->sopt_valsize = all_lnk_size;
558 	return 0;
559 nospace:
560 	return 0;
561 }
562 
563 void
564 nat_add_dispatch(netmsg_t nat_add_msg)
565 {
566 	struct ipfw_nat_context *nat_ctx;
567 	struct cfg_nat *ptr, *ser_n;
568 	struct netmsg_nat_add *msg;
569 
570 	msg = (struct netmsg_nat_add *)nat_add_msg;
571 
572 	ser_n = (struct cfg_nat *)(msg->buf);
573 
574 	/* New rule: allocate and init new instance. */
575 	ptr = kmalloc(sizeof(struct cfg_nat), M_IPFW_NAT, M_WAITOK | M_ZERO);
576 
577 	ptr->lib = LibAliasInit(NULL);
578 	if (ptr->lib == NULL) {
579 		kfree(ptr, M_IPFW_NAT);
580 		kfree(msg->buf, M_IPFW_NAT);
581 	}
582 
583 	LIST_INIT(&ptr->redir_chain);
584 	/*
585 	 * Basic nat configuration.
586 	 */
587 	ptr->id = ser_n->id;
588 	/*
589 	 * XXX - what if this rule doesn't nat any ip and just
590 	 * redirect?
591 	 * do we set aliasaddress to 0.0.0.0?
592 	 */
593 	ptr->ip = ser_n->ip;
594 	ptr->redir_cnt = ser_n->redir_cnt;
595 	ptr->mode = ser_n->mode;
596 
597 	LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode);
598 	LibAliasSetAddress(ptr->lib, ptr->ip);
599 	memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE);
600 
601 	/* Add new entries. */
602 	add_redir_spool_cfg(&msg->buf[(sizeof(struct cfg_nat))], ptr);
603 
604 	nat_ctx = ipfw_nat_ctx[mycpuid];
605 	HOOK_NAT(&(nat_ctx->nat), ptr);
606 	netisr_forwardmsg_all(&msg->base, mycpuid + 1);
607 }
608 
609 int
610 ipfw_ctl_nat_add(struct sockopt *sopt)
611 {
612 	struct ipfw_nat_context *nat_ctx;
613 	struct cfg_nat *ptr, *ser_n;
614 	ser_n = (struct cfg_nat *)(sopt->sopt_val);
615 
616 	nat_ctx = ipfw_nat_ctx[mycpuid];
617 	/*
618 	 * Find/create nat rule.
619 	 */
620 	LOOKUP_NAT((*nat_ctx), ser_n->id, ptr);
621 
622 	if (ptr == NULL) {
623 		struct netmsg_nat_add nat_add_msg;
624 		struct netmsg_nat_add *msg;
625 
626 		msg = &nat_add_msg;
627 		msg->buf = kmalloc(sopt->sopt_valsize,
628 				M_IPFW_NAT, M_WAITOK | M_ZERO);
629 
630 		sooptcopyin(sopt, msg->buf, sopt->sopt_valsize,
631 				sizeof(struct cfg_nat));
632 
633 		netmsg_init(&msg->base, NULL, &curthread->td_msgport,
634 				0, nat_add_dispatch);
635 
636 
637 		netisr_domsg(&msg->base, 0);
638 		kfree(msg->buf, M_IPFW_NAT);
639 	} else {
640 		goto done;
641 	}
642 done:
643 	return 0;
644 }
645 
646 void
647 nat_del_dispatch(netmsg_t nat_del_msg)
648 {
649 	struct ipfw_nat_context *nat_ctx;
650 	struct ipfw_context *ctx;
651 	struct cfg_nat *n, *tmp;
652 	struct netmsg_nat_del *msg;
653 	struct ip_fw *f;
654 	ipfw_insn *cmd;
655 	int id;
656 
657 	msg = (struct netmsg_nat_del *)nat_del_msg;
658 	id = msg->id;
659 
660 	nat_ctx = ipfw_nat_ctx[mycpuid];
661 	LOOKUP_NAT((*nat_ctx), id, n);
662 	if (n == NULL) {
663 	}
664 
665 	/*
666 	 * stop deleting when this cfg_nat was in use in ipfw_ctx
667 	 */
668 	ctx = ipfw_ctx[mycpuid];
669 	for (f = ctx->ipfw_rule_chain; f; f = f->next) {
670 		cmd = ACTION_PTR(f);
671 		if ((int)cmd->module == MODULE_NAT_ID &&
672 				(int)cmd->opcode == O_NAT_NAT) {
673 			tmp = ((ipfw_insn_nat *)cmd)->nat;
674 			if (tmp != NULL && tmp->id == n->id) {
675 			}
676 		}
677 	}
678 
679 	UNHOOK_NAT(n);
680 	del_redir_spool_cfg(n, &n->redir_chain);
681 	LibAliasUninit(n->lib);
682 	kfree(n, M_IPFW_NAT);
683 }
684 
685 int
686 ipfw_ctl_nat_del(struct sockopt *sopt)
687 {
688 	struct netmsg_nat_del nat_del_msg;
689 	struct netmsg_nat_del *msg;
690 	int *id;
691 
692 	msg = &nat_del_msg;
693 	id = sopt->sopt_val;
694 	msg->id = *id;
695 
696 	netmsg_init(&msg->base, NULL, &curthread->td_msgport,
697 			0, nat_del_dispatch);
698 
699 	netisr_domsg(&msg->base, 0);
700 	return 0;
701 }
702 
703 int
704 ipfw_ctl_nat_flush(struct sockopt *sopt)
705 {
706 	struct ipfw_nat_context *nat_ctx;
707 	struct ipfw_context *ctx;
708 	struct cfg_nat *ptr, *tmp;
709 	struct ip_fw *f;
710 	ipfw_insn *cmd;
711 	int cpu;
712 
713 	/*
714 	 * stop flushing when any cfg_nat was in use in ipfw_ctx
715 	 */
716 	for (cpu = 0; cpu < ncpus; cpu++) {
717 		ctx = ipfw_ctx[cpu];
718 		for (f = ctx->ipfw_rule_chain; f; f = f->next) {
719 			cmd = ACTION_PTR(f);
720 			if ((int)cmd->module == MODULE_NAT_ID &&
721 				(int)cmd->opcode == O_NAT_NAT) {
722 				return EINVAL;
723 			}
724 		}
725 	}
726 
727 	nat_ctx = ipfw_nat_ctx[mycpuid];
728 
729 	LIST_FOREACH_MUTABLE(ptr, &(nat_ctx->nat), _next, tmp) {
730 		LIST_REMOVE(ptr, _next);
731 		del_redir_spool_cfg(ptr, &ptr->redir_chain);
732 		LibAliasUninit(ptr->lib);
733 		kfree(ptr, M_IPFW_NAT);
734 	}
735 	return 0;
736 }
737 
738 int
739 ipfw_ctl_nat_sockopt(struct sockopt *sopt)
740 {
741 	int error = 0;
742 	switch (sopt->sopt_name) {
743 		case IP_FW_NAT_ADD:
744 			error = ipfw_ctl_nat_add(sopt);
745 			break;
746 		case IP_FW_NAT_DEL:
747 			error = ipfw_ctl_nat_del(sopt);
748 			break;
749 		case IP_FW_NAT_FLUSH:
750 			error = ipfw_ctl_nat_flush(sopt);
751 			break;
752 		case IP_FW_NAT_GET:
753 			error = ipfw_ctl_nat_get_cfg(sopt);
754 			break;
755 		case IP_FW_NAT_GET_RECORD:
756 			error = ipfw_ctl_nat_get_record(sopt);
757 			break;
758 		default:
759 			kprintf("ipfw3 nat invalid socket option %d\n",
760 					sopt->sopt_name);
761 	}
762 	return error;
763 }
764 
765 void
766 nat_init_ctx_dispatch(netmsg_t msg)
767 {
768 	struct ipfw_nat_context *tmp;
769 	tmp = kmalloc(sizeof(struct ipfw_nat_context),
770 				M_IPFW_NAT, M_WAITOK | M_ZERO);
771 	ipfw_nat_ctx[mycpuid] = tmp;
772 	netisr_forwardmsg_all(&msg->base, mycpuid + 1);
773 }
774 
775 static void
776 ipfw3_nat_cleanup_func_dispatch(netmsg_t nmsg)
777 {
778 	struct ipfw_nat_context *nctx;
779 	struct cfg_nat *ptr, *tmp;
780 
781 	nctx = ipfw_nat_ctx[mycpuid];
782 	LIST_FOREACH_MUTABLE(ptr, &(nctx->nat), _next, tmp) {
783 		if (libalias_housekeeping_prt != NULL) {
784 			(*libalias_housekeeping_prt)(ptr->lib);
785 		}
786 	}
787 	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
788 }
789 
790 static void
791 ipfw3_nat_cleanup_func(void *dummy __unused)
792 {
793 	struct netmsg_base msg;
794 	netmsg_init(&msg, NULL, &curthread->td_msgport, 0,
795 			ipfw3_nat_cleanup_func_dispatch);
796 	netisr_domsg(&msg, 0);
797 
798 	callout_reset(&ipfw3_nat_cleanup_callout,
799 			fw3_nat_cleanup_interval * hz,
800 			ipfw3_nat_cleanup_func,
801 			NULL);
802 }
803 
804 static
805 int ipfw_nat_init(void)
806 {
807 	struct netmsg_base msg;
808 	register_ipfw_module(MODULE_NAT_ID, MODULE_NAT_NAME);
809 	register_ipfw_filter_funcs(MODULE_NAT_ID, O_NAT_NAT,
810 			(filter_func)check_nat);
811 	ipfw_ctl_nat_ptr = ipfw_ctl_nat_sockopt;
812 	netmsg_init(&msg, NULL, &curthread->td_msgport,
813 			0, nat_init_ctx_dispatch);
814 	netisr_domsg(&msg, 0);
815 
816 	callout_init_mp(&ipfw3_nat_cleanup_callout);
817 	callout_reset(&ipfw3_nat_cleanup_callout,
818 			fw3_nat_cleanup_interval * hz,
819 			ipfw3_nat_cleanup_func,
820 			NULL);
821 	return 0;
822 }
823 
824 static int
825 ipfw_nat_fini(void)
826 {
827 	struct cfg_nat *ptr, *tmp;
828 	struct ipfw_nat_context *ctx;
829 	int cpu;
830 
831 	callout_stop(&ipfw3_nat_cleanup_callout);
832 
833 	for (cpu = 0; cpu < ncpus; cpu++) {
834 		ctx = ipfw_nat_ctx[cpu];
835 		if(ctx != NULL) {
836 			LIST_FOREACH_MUTABLE(ptr, &(ctx->nat), _next, tmp) {
837 				LIST_REMOVE(ptr, _next);
838 				del_redir_spool_cfg(ptr, &ptr->redir_chain);
839 				LibAliasUninit(ptr->lib);
840 				kfree(ptr, M_IPFW_NAT);
841 			}
842 
843 			kfree(ctx, M_IPFW_NAT);
844 			ipfw_nat_ctx[cpu] = NULL;
845 		}
846 	}
847 	ipfw_ctl_nat_ptr = NULL;
848 
849 	return unregister_ipfw_module(MODULE_NAT_ID);
850 }
851 
852 static int
853 ipfw_nat_modevent(module_t mod, int type, void *data)
854 {
855 	switch (type) {
856 		case MOD_LOAD:
857 			return ipfw_nat_init();
858 		case MOD_UNLOAD:
859 			return ipfw_nat_fini();
860 		default:
861 			break;
862 	}
863 	return 0;
864 }
865 
866 moduledata_t ipfw_nat_mod = {
867 	"ipfw3_nat",
868 	ipfw_nat_modevent,
869 	NULL
870 };
871 
872 DECLARE_MODULE(ipfw3_nat, ipfw_nat_mod,
873 		SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
874 MODULE_DEPEND(ipfw3_nat, libalias, 1, 1, 1);
875 MODULE_DEPEND(ipfw3_nat, ipfw3_basic, 1, 1, 1);
876 MODULE_VERSION(ipfw3_nat, 1);
877