xref: /dragonfly/sys/net/ipfw3_nat/ip_fw3_nat.c (revision 68732d8f)
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 extern struct ipfw_context *ipfw_ctx[MAXCPU];
83 extern ip_fw_ctl_t *ipfw_ctl_nat_ptr;
84 
85 void check_nat(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
86 		struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len);
87 void add_alias_link_dispatch(netmsg_t nat_del_msg);
88 int ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m);
89 void nat_add_dispatch(netmsg_t msg);
90 int ipfw_ctl_nat_add(struct sockopt *sopt);
91 void nat_del_dispatch(netmsg_t msg);
92 int ipfw_ctl_nat_del(struct sockopt *sopt);
93 int ipfw_ctl_nat_flush(struct sockopt *sopt);
94 int ipfw_ctl_nat_sockopt(struct sockopt *sopt);
95 void nat_init_ctx_dispatch(netmsg_t msg);
96 int ipfw_ctl_nat_get_cfg(struct sockopt *sopt);
97 void del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head);
98 int add_redir_spool_cfg(char *buf, struct cfg_nat *ptr);
99 int ipfw_ctl_nat_get_record(struct sockopt *sopt);
100 
101 
102 void
103 check_nat(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
104 		struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len)
105 {
106 	if ((*args)->eh != NULL) {
107 		*cmd_ctl = IP_FW_CTL_NO;
108 		*cmd_val = IP_FW_NOT_MATCH;
109 		return;
110 	}
111 
112 	struct ipfw_nat_context *nat_ctx;
113 	struct cfg_nat *t;
114 	int nat_id;
115 
116 	nat_ctx = ipfw_nat_ctx[mycpuid];
117 	(*args)->rule = *f;
118 	t = ((ipfw_insn_nat *)cmd)->nat;
119 	if (t == NULL) {
120 		nat_id = cmd->arg1;
121 		LOOKUP_NAT((*nat_ctx), nat_id, t);
122 		if (t == NULL) {
123 			*cmd_val = IP_FW_DENY;
124 			*cmd_ctl = IP_FW_CTL_DONE;
125 			return;
126 		}
127 		((ipfw_insn_nat *)cmd)->nat = t;
128 	}
129 	*cmd_val = ipfw_nat(*args, t, (*args)->m);
130 	*cmd_ctl = IP_FW_CTL_NAT;
131 }
132 
133 /* Local prototypes */
134 u_int StartPointIn(struct in_addr, u_short, int);
135 
136 u_int StartPointOut(struct in_addr, struct in_addr,
137 		u_short, u_short, int);
138 
139 u_int
140 StartPointIn(struct in_addr alias_addr,
141 	u_short alias_port,
142 	int link_type)
143 {
144 	u_int n;
145 
146 	n = alias_addr.s_addr;
147 	if (link_type != LINK_PPTP)
148 		n += alias_port;
149 	n += link_type;
150 	return (n % LINK_TABLE_IN_SIZE);
151 }
152 
153 
154 u_int
155 StartPointOut(struct in_addr src_addr, struct in_addr dst_addr,
156 	u_short src_port, u_short dst_port, int link_type)
157 {
158 	u_int n;
159 
160 	n = src_addr.s_addr;
161 	n += dst_addr.s_addr;
162 	if (link_type != LINK_PPTP) {
163 		n += src_port;
164 		n += dst_port;
165 	}
166 	n += link_type;
167 
168 	return (n % LINK_TABLE_OUT_SIZE);
169 }
170 
171 
172 void
173 add_alias_link_dispatch(netmsg_t alias_link_add)
174 {
175 	struct ipfw_nat_context *nat_ctx;
176 	struct netmsg_alias_link_add *msg;
177 	struct libalias *la;
178 	struct alias_link *lnk;
179 	struct cfg_nat *t;
180 	struct tcp_dat *aux_tcp;
181 	u_int start_point;
182 
183 	msg = (struct netmsg_alias_link_add *)alias_link_add;
184 	nat_ctx = ipfw_nat_ctx[mycpuid];
185 	LOOKUP_NAT((*nat_ctx), msg->id, t);
186 	la = t->lib;
187 	lnk = kmalloc(sizeof(struct alias_link), M_ALIAS, M_WAITOK | M_ZERO);
188 	memcpy(lnk, msg->lnk, sizeof(struct alias_link));
189 	lnk->la = la;
190 	if (msg->is_tcp) {
191 		aux_tcp = kmalloc(sizeof(struct tcp_dat),
192 				M_ALIAS, M_WAITOK | M_ZERO);
193 		memcpy(aux_tcp, msg->lnk->data.tcp, sizeof(struct tcp_dat));
194 		lnk->data.tcp = aux_tcp;
195 	}
196 
197 	/* Set up pointers for output lookup table */
198 	start_point = StartPointOut(lnk->src_addr, lnk->dst_addr,
199 			lnk->src_port, lnk->dst_port, lnk->link_type);
200 	LIST_INSERT_HEAD(&la->linkTableOut[start_point], lnk, list_out);
201 
202 	/* Set up pointers for input lookup table */
203 	start_point = StartPointIn(lnk->alias_addr,
204 			lnk->alias_port, lnk->link_type);
205 	LIST_INSERT_HEAD(&la->linkTableIn[start_point], lnk, list_in);
206 	kfree(alias_link_add, M_LWKTMSG);
207 }
208 
209 int
210 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
211 {
212 	struct alias_link *new = NULL;
213 	struct mbuf *mcl;
214 	struct ip *ip;
215 	int ldt, retval, nextcpu;
216 	char *c;
217 
218 	ldt = 0;
219 	retval = 0;
220 	if ((mcl = m_megapullup(m, m->m_pkthdr.len)) ==NULL)
221 		goto badnat;
222 
223 	ip = mtod(mcl, struct ip *);
224 	if (args->eh == NULL) {
225 		ip->ip_len = htons(ip->ip_len);
226 		ip->ip_off = htons(ip->ip_off);
227 	}
228 
229 	if (mcl->m_pkthdr.rcvif == NULL &&
230 			mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
231 		ldt = 1;
232 	}
233 
234 	c = mtod(mcl, char *);
235 	if (args->oif == NULL) {
236 		retval = LibAliasIn(t->lib, c,
237 				mcl->m_len + M_TRAILINGSPACE(mcl), &new);
238 	} else {
239 		retval = LibAliasOut(t->lib, c,
240 				mcl->m_len + M_TRAILINGSPACE(mcl), &new);
241 	}
242 	if (retval != PKT_ALIAS_OK &&
243 			retval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
244 		/* XXX - should i add some logging? */
245 		m_free(mcl);
246 badnat:
247 		args->m = NULL;
248 		return IP_FW_DENY;
249 	}
250 	mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
251 
252 	if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
253 			ip->ip_p == IPPROTO_TCP) {
254 		struct tcphdr 	*th;
255 
256 		th = (struct tcphdr *)(ip + 1);
257 		if (th->th_x2){
258 			ldt = 1;
259 		}
260 	}
261 	if (new != NULL &&
262 			(ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP)) {
263 		ip_hashfn(&mcl, 0);
264 		nextcpu = netisr_hashcpu(m->m_pkthdr.hash);
265 		if (nextcpu != mycpuid) {
266 			struct netmsg_alias_link_add *msg;
267 			msg = kmalloc(sizeof(struct netmsg_alias_link_add),
268 					M_LWKTMSG, M_NOWAIT | M_ZERO);
269 
270 			netmsg_init(&msg->base, NULL,
271 					&curthread->td_msgport, 0,
272 					add_alias_link_dispatch);
273 			msg->lnk = new;
274 			msg->id = t->id;
275 			if (ip->ip_p == IPPROTO_TCP) {
276 				msg->is_tcp = 1;
277 			}
278 			if (args->oif == NULL) {
279 				msg->is_outgoing = 0;
280 			} else {
281 				msg->is_outgoing = 1;
282 			}
283 			netisr_sendmsg(&msg->base, nextcpu);
284 		}
285 	}
286 	if (ldt) {
287 		struct tcphdr 	*th;
288 		struct udphdr 	*uh;
289 		u_short cksum;
290 
291 		ip->ip_len = ntohs(ip->ip_len);
292 		cksum = in_pseudo(
293 				ip->ip_src.s_addr,
294 				ip->ip_dst.s_addr,
295 				htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2))
296 				);
297 
298 		switch (ip->ip_p) {
299 			case IPPROTO_TCP:
300 				th = (struct tcphdr *)(ip + 1);
301 				th->th_x2 = 0;
302 				th->th_sum = cksum;
303 				mcl->m_pkthdr.csum_data =
304 					offsetof(struct tcphdr, th_sum);
305 				break;
306 			case IPPROTO_UDP:
307 				uh = (struct udphdr *)(ip + 1);
308 				uh->uh_sum = cksum;
309 				mcl->m_pkthdr.csum_data =
310 					offsetof(struct udphdr, uh_sum);
311 				break;
312 		}
313 		/*
314 		 * No hw checksum offloading: do it
315 		 * by ourself.
316 		 */
317 		if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) {
318 			in_delayed_cksum(mcl);
319 			mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
320 		}
321 		ip->ip_len = htons(ip->ip_len);
322 	}
323 
324 	if (args->eh == NULL) {
325 		ip->ip_len = ntohs(ip->ip_len);
326 		ip->ip_off = ntohs(ip->ip_off);
327 	}
328 
329 	args->m = mcl;
330 	return IP_FW_NAT;
331 }
332 
333 void
334 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
335 {
336 	struct cfg_redir *r, *tmp_r;
337 	struct cfg_spool *s, *tmp_s;
338 	int i, num;
339 
340 	LIST_FOREACH_MUTABLE(r, head, _next, tmp_r) {
341 		num = 1; /* Number of alias_link to delete. */
342 		switch (r->mode) {
343 			case REDIR_PORT:
344 				num = r->pport_cnt;
345 				/* FALLTHROUGH */
346 			case REDIR_ADDR:
347 			case REDIR_PROTO:
348 				/* Delete all libalias redirect entry. */
349 				for (i = 0; i < num; i++)
350 					LibAliasRedirectDelete(n->lib,
351 							r->alink[i]);
352 
353 				/* Del spool cfg if any. */
354 				LIST_FOREACH_MUTABLE(s, &r->spool_chain,
355 						_next, tmp_s) {
356 					LIST_REMOVE(s, _next);
357 					kfree(s, M_IPFW_NAT);
358 				}
359 				kfree(r->alink, M_IPFW_NAT);
360 				LIST_REMOVE(r, _next);
361 				kfree(r, M_IPFW_NAT);
362 				break;
363 			default:
364 				kprintf("unknown redirect mode: %u\n", r->mode);
365 				/* XXX - panic?!?!? */
366 				break;
367 		}
368 	}
369 }
370 
371 int
372 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
373 {
374 	struct cfg_redir *r, *ser_r;
375 	struct cfg_spool *s, *ser_s;
376 	int cnt, off, i;
377 	char *panic_err;
378 
379 	for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
380 		ser_r = (struct cfg_redir *)&buf[off];
381 		r = kmalloc(SOF_REDIR, M_IPFW_NAT, M_WAITOK | M_ZERO);
382 		memcpy(r, ser_r, SOF_REDIR);
383 		LIST_INIT(&r->spool_chain);
384 		off += SOF_REDIR;
385 		r->alink = kmalloc(sizeof(struct alias_link *) * r->pport_cnt,
386 				M_IPFW_NAT, M_WAITOK | M_ZERO);
387 		switch (r->mode) {
388 			case REDIR_ADDR:
389 				r->alink[0] = LibAliasRedirectAddr(ptr->lib,
390 							r->laddr, r->paddr);
391 				break;
392 			case REDIR_PORT:
393 				for (i = 0 ; i < r->pport_cnt; i++) {
394 					/*
395 					 * If remotePort is all ports
396 					 * set it to 0.
397 					 */
398 					u_short remotePortCopy = r->rport + i;
399 					if (r->rport_cnt == 1 && r->rport == 0)
400 						remotePortCopy = 0;
401 						r->alink[i] =
402 
403 						LibAliasRedirectPort(ptr->lib,
404 						r->laddr,htons(r->lport + i),
405 						r->raddr,htons(remotePortCopy),
406 						r->paddr,htons(r->pport + i),
407 						r->proto);
408 
409 					if (r->alink[i] == NULL) {
410 						r->alink[0] = NULL;
411 						break;
412 					}
413 				}
414 				break;
415 			case REDIR_PROTO:
416 				r->alink[0] = LibAliasRedirectProto(ptr->lib,
417 					r->laddr, r->raddr, r->paddr, r->proto);
418 				break;
419 			default:
420 				kprintf("unknown redirect mode: %u\n", r->mode);
421 				break;
422 		}
423 		if (r->alink[0] == NULL) {
424 			panic_err = "LibAliasRedirect* returned NULL";
425 			goto bad;
426 		} else /* LSNAT handling. */
427 			for (i = 0; i < r->spool_cnt; i++) {
428 				ser_s = (struct cfg_spool *)&buf[off];
429 				s = kmalloc(SOF_REDIR, M_IPFW_NAT,
430 						M_WAITOK | M_ZERO);
431 				memcpy(s, ser_s, SOF_SPOOL);
432 				LibAliasAddServer(ptr->lib, r->alink[0],
433 						s->addr, htons(s->port));
434 				off += SOF_SPOOL;
435 				/* Hook spool entry. */
436 				HOOK_SPOOL(&r->spool_chain, s);
437 			}
438 		/* And finally hook this redir entry. */
439 		HOOK_REDIR(&ptr->redir_chain, r);
440 	}
441 	return 1;
442 bad:
443 	/* something really bad happened: panic! */
444 	panic("%s\n", panic_err);
445 }
446 
447 int
448 ipfw_ctl_nat_get_cfg(struct sockopt *sopt)
449 {
450 	struct ipfw_nat_context *nat_ctx;
451 	struct cfg_nat *n;
452 	struct cfg_redir *r;
453 	struct cfg_spool *s;
454 	int nat_cnt, off, nat_cfg_size;
455 	size_t size;
456 	uint8_t *data;
457 
458 	nat_cnt = 0;
459 	nat_cfg_size = 0;
460 	off = sizeof(nat_cnt);
461 
462 	nat_ctx = ipfw_nat_ctx[mycpuid];
463 	size = sopt->sopt_valsize;
464 
465 	data = sopt->sopt_val;
466 	/* count the size of nat cfg */
467 	LIST_FOREACH(n, &((*nat_ctx).nat), _next) {
468 		nat_cfg_size += SOF_NAT;
469 	}
470 
471 	LIST_FOREACH(n, &((*nat_ctx).nat), _next) {
472 		nat_cnt++;
473 		if (off + SOF_NAT < size) {
474 			bcopy(n, &data[off], SOF_NAT);
475 			off += SOF_NAT;
476 			LIST_FOREACH(r, &n->redir_chain, _next) {
477 				if (off + SOF_REDIR < size) {
478 					bcopy(r, &data[off], SOF_REDIR);
479 					off += SOF_REDIR;
480 					LIST_FOREACH(s, &r->spool_chain,
481 						_next) {
482 						if (off + SOF_SPOOL < size) {
483 							bcopy(s, &data[off],
484 								SOF_SPOOL);
485 							off += SOF_SPOOL;
486 						} else
487 							goto nospace;
488 					}
489 				} else
490 					goto nospace;
491 			}
492 		} else
493 			goto nospace;
494 	}
495 	bcopy(&nat_cnt, data, sizeof(nat_cnt));
496 	sopt->sopt_valsize = nat_cfg_size;
497 	return 0;
498 nospace:
499 	bzero(sopt->sopt_val, sopt->sopt_valsize);
500 	sopt->sopt_valsize = nat_cfg_size;
501 	return 0;
502 }
503 
504 int
505 ipfw_ctl_nat_get_record(struct sockopt *sopt)
506 {
507 	struct ipfw_nat_context *nat_ctx;
508 	struct cfg_nat *t;
509 	struct alias_link *lnk;
510 	struct libalias *la;
511 	size_t sopt_size, all_lnk_size = 0;
512 	int i, *nat_id, id, n;
513 	struct ipfw_ioc_nat_state *nat_state;
514 
515 	int cpu;
516 
517 	nat_id = (int *)(sopt->sopt_val);
518 	n = *nat_id;
519 	sopt_size = sopt->sopt_valsize;
520 	nat_state = (struct ipfw_ioc_nat_state *)sopt->sopt_val;
521 	for (cpu = 0; cpu < ncpus; cpu++) {
522 		nat_ctx = ipfw_nat_ctx[cpu];
523 		id = n;
524 		LOOKUP_NAT((*nat_ctx), id, t);
525 		if (t != NULL) {
526 			la = t->lib;
527 			LIBALIAS_LOCK_ASSERT(la);
528 			for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) {
529 				LIST_FOREACH(lnk, &la->linkTableOut[i],
530 						list_out) {
531 					all_lnk_size += sizeof(*nat_state);
532 					if (all_lnk_size > sopt_size)
533 						goto nospace;
534 					nat_state->src_addr = lnk->src_addr;
535 					nat_state->dst_addr = lnk->dst_addr;
536 					nat_state->alias_addr = lnk->alias_addr;
537 					nat_state->src_port = lnk->src_port;
538 					nat_state->dst_port = lnk->dst_port;
539 					nat_state->alias_port = lnk->alias_port;
540 					nat_state->link_type = lnk->link_type;
541 					nat_state->timestamp = lnk->timestamp;
542 					nat_state->cpuid = cpu;
543 					nat_state->is_outgoing = 1;
544 					nat_state++;
545 				}
546 				LIST_FOREACH(lnk, &la->linkTableIn[i],
547 						list_out) {
548 					all_lnk_size += sizeof(*nat_state);
549 					if (all_lnk_size > sopt_size)
550 						goto nospace;
551 					nat_state->src_addr = lnk->src_addr;
552 					nat_state->dst_addr = lnk->dst_addr;
553 					nat_state->alias_addr = lnk->alias_addr;
554 					nat_state->src_port = lnk->src_port;
555 					nat_state->dst_port = lnk->dst_port;
556 					nat_state->alias_port = lnk->alias_port;
557 					nat_state->link_type = lnk->link_type;
558 					nat_state->timestamp = lnk->timestamp;
559 					nat_state->cpuid = cpu;
560 					nat_state->is_outgoing = 0;
561 					nat_state++;
562 				}
563 			}
564 		}
565 	}
566 	sopt->sopt_valsize = all_lnk_size;
567 	return 0;
568 nospace:
569 	return 0;
570 }
571 
572 void
573 nat_add_dispatch(netmsg_t nat_add_msg)
574 {
575 	struct ipfw_nat_context *nat_ctx;
576 	struct cfg_nat *ptr, *ser_n;
577 	struct netmsg_nat_add *msg;
578 
579 	msg = (struct netmsg_nat_add *)nat_add_msg;
580 
581 	ser_n = (struct cfg_nat *)(msg->buf);
582 
583 	/* New rule: allocate and init new instance. */
584 	ptr = kmalloc(sizeof(struct cfg_nat), M_IPFW_NAT, M_WAITOK | M_ZERO);
585 
586 	ptr->lib = LibAliasInit(NULL);
587 	if (ptr->lib == NULL) {
588 		kfree(ptr, M_IPFW_NAT);
589 		kfree(msg->buf, M_IPFW_NAT);
590 	}
591 
592 	LIST_INIT(&ptr->redir_chain);
593 	/*
594 	 * Basic nat configuration.
595 	 */
596 	ptr->id = ser_n->id;
597 	/*
598 	 * XXX - what if this rule doesn't nat any ip and just
599 	 * redirect?
600 	 * do we set aliasaddress to 0.0.0.0?
601 	 */
602 	ptr->ip = ser_n->ip;
603 	ptr->redir_cnt = ser_n->redir_cnt;
604 	ptr->mode = ser_n->mode;
605 
606 	LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode);
607 	LibAliasSetAddress(ptr->lib, ptr->ip);
608 	memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE);
609 
610 	/* Add new entries. */
611 	add_redir_spool_cfg(&msg->buf[(sizeof(struct cfg_nat))], ptr);
612 
613 	nat_ctx = ipfw_nat_ctx[mycpuid];
614 	HOOK_NAT(&(nat_ctx->nat), ptr);
615 	netisr_forwardmsg(&msg->base, mycpuid + 1);
616 }
617 
618 int
619 ipfw_ctl_nat_add(struct sockopt *sopt)
620 {
621 	struct ipfw_nat_context *nat_ctx;
622 	struct cfg_nat *ptr, *ser_n;
623 	ser_n = (struct cfg_nat *)(sopt->sopt_val);
624 
625 	nat_ctx = ipfw_nat_ctx[mycpuid];
626 	/*
627 	 * Find/create nat rule.
628 	 */
629 	LOOKUP_NAT((*nat_ctx), ser_n->id, ptr);
630 
631 	if (ptr == NULL) {
632 		struct netmsg_nat_add nat_add_msg;
633 		struct netmsg_nat_add *msg;
634 
635 		msg = &nat_add_msg;
636 		msg->buf = kmalloc(sopt->sopt_valsize,
637 				M_IPFW_NAT, M_WAITOK | M_ZERO);
638 
639 		sooptcopyin(sopt, msg->buf, sopt->sopt_valsize,
640 				sizeof(struct cfg_nat));
641 
642 		netmsg_init(&msg->base, NULL, &curthread->td_msgport,
643 				0, nat_add_dispatch);
644 
645 
646 		netisr_domsg(&msg->base, 0);
647 		kfree(msg->buf, M_IPFW_NAT);
648 	} else {
649 		goto done;
650 	}
651 done:
652 	return 0;
653 }
654 
655 void
656 nat_del_dispatch(netmsg_t nat_del_msg)
657 {
658 	struct ipfw_nat_context *nat_ctx;
659 	struct ipfw_context *ctx;
660 	struct cfg_nat *n, *tmp;
661 	struct netmsg_nat_del *msg;
662 	struct ip_fw *f;
663 	ipfw_insn *cmd;
664 	int id;
665 
666 	msg = (struct netmsg_nat_del *)nat_del_msg;
667 	id = msg->id;
668 
669 	nat_ctx = ipfw_nat_ctx[mycpuid];
670 	LOOKUP_NAT((*nat_ctx), id, n);
671 	if (n == NULL) {
672 	}
673 
674 	/*
675 	 * stop deleting when this cfg_nat was in use in ipfw_ctx
676 	 */
677 	ctx = ipfw_ctx[mycpuid];
678 	for (f = ctx->ipfw_rule_chain; f; f = f->next) {
679 		cmd = ACTION_PTR(f);
680 		if ((int)cmd->module == MODULE_NAT_ID &&
681 				(int)cmd->opcode == O_NAT_NAT) {
682 			tmp = ((ipfw_insn_nat *)cmd)->nat;
683 			if (tmp != NULL && tmp->id == n->id) {
684 			}
685 		}
686 	}
687 
688 	UNHOOK_NAT(n);
689 	del_redir_spool_cfg(n, &n->redir_chain);
690 	LibAliasUninit(n->lib);
691 	kfree(n, M_IPFW_NAT);
692 }
693 
694 int
695 ipfw_ctl_nat_del(struct sockopt *sopt)
696 {
697 	struct netmsg_nat_del nat_del_msg;
698 	struct netmsg_nat_del *msg;
699 	int *id;
700 
701 	msg = &nat_del_msg;
702 	id = sopt->sopt_val;
703 	msg->id = *id;
704 
705 	netmsg_init(&msg->base, NULL, &curthread->td_msgport,
706 			0, nat_del_dispatch);
707 
708 	netisr_domsg(&msg->base, 0);
709 	return 0;
710 }
711 
712 int
713 ipfw_ctl_nat_flush(struct sockopt *sopt)
714 {
715 	struct ipfw_nat_context *nat_ctx;
716 	struct ipfw_context *ctx;
717 	struct cfg_nat *ptr, *tmp;
718 	struct ip_fw *f;
719 	ipfw_insn *cmd;
720 	int cpu;
721 
722 	/*
723 	 * stop flushing when any cfg_nat was in use in ipfw_ctx
724 	 */
725 	for (cpu = 0; cpu < ncpus; cpu++) {
726 		ctx = ipfw_ctx[cpu];
727 		for (f = ctx->ipfw_rule_chain; f; f = f->next) {
728 			cmd = ACTION_PTR(f);
729 			if ((int)cmd->module == MODULE_NAT_ID &&
730 				(int)cmd->opcode == O_NAT_NAT) {
731 				return EINVAL;
732 			}
733 		}
734 	}
735 
736 	nat_ctx = ipfw_nat_ctx[mycpuid];
737 
738 	LIST_FOREACH_MUTABLE(ptr, &(nat_ctx->nat), _next, tmp) {
739 		LIST_REMOVE(ptr, _next);
740 		del_redir_spool_cfg(ptr, &ptr->redir_chain);
741 		LibAliasUninit(ptr->lib);
742 		kfree(ptr, M_IPFW_NAT);
743 	}
744 	return 0;
745 }
746 
747 int
748 ipfw_ctl_nat_sockopt(struct sockopt *sopt)
749 {
750 	int error = 0;
751 	switch (sopt->sopt_name) {
752 		case IP_FW_NAT_ADD:
753 			error = ipfw_ctl_nat_add(sopt);
754 			break;
755 		case IP_FW_NAT_DEL:
756 			error = ipfw_ctl_nat_del(sopt);
757 			break;
758 		case IP_FW_NAT_FLUSH:
759 			error = ipfw_ctl_nat_flush(sopt);
760 			break;
761 		case IP_FW_NAT_GET:
762 			error = ipfw_ctl_nat_get_cfg(sopt);
763 			break;
764 		case IP_FW_NAT_GET_RECORD:
765 			error = ipfw_ctl_nat_get_record(sopt);
766 			break;
767 		default:
768 			kprintf("ipfw3 nat invalid socket option %d\n",
769 					sopt->sopt_name);
770 	}
771 	return error;
772 }
773 
774 void
775 nat_init_ctx_dispatch(netmsg_t msg)
776 {
777 	struct ipfw_nat_context *tmp;
778 	tmp = kmalloc(sizeof(struct ipfw_nat_context),
779 				M_IPFW_NAT, M_WAITOK | M_ZERO);
780 	ipfw_nat_ctx[mycpuid] = tmp;
781 	netisr_forwardmsg(&msg->base, mycpuid + 1);
782 }
783 
784 static
785 int ipfw_nat_init(void)
786 {
787 	struct netmsg_base msg;
788 	register_ipfw_module(MODULE_NAT_ID, MODULE_NAT_NAME);
789 	register_ipfw_filter_funcs(MODULE_NAT_ID, O_NAT_NAT,
790 			(filter_func)check_nat);
791 	ipfw_ctl_nat_ptr = ipfw_ctl_nat_sockopt;
792 	netmsg_init(&msg, NULL, &curthread->td_msgport,
793 			0, nat_init_ctx_dispatch);
794 	netisr_domsg(&msg, 0);
795 	return 0;
796 }
797 
798 static int
799 ipfw_nat_fini(void)
800 {
801 	struct cfg_nat *ptr, *tmp;
802 	struct ipfw_nat_context *ctx;
803 	int cpu;
804 
805 	for (cpu = 0; cpu < ncpus; cpu++) {
806 		ctx = ipfw_nat_ctx[cpu];
807 		if(ctx != NULL) {
808 			LIST_FOREACH_MUTABLE(ptr, &(ctx->nat), _next, tmp) {
809 				LIST_REMOVE(ptr, _next);
810 				del_redir_spool_cfg(ptr, &ptr->redir_chain);
811 				LibAliasUninit(ptr->lib);
812 				kfree(ptr, M_IPFW_NAT);
813 			}
814 
815 			kfree(ctx, M_IPFW_NAT);
816 			ipfw_nat_ctx[cpu] = NULL;
817 		}
818 	}
819 	ipfw_ctl_nat_ptr = NULL;
820 
821 	return unregister_ipfw_module(MODULE_NAT_ID);
822 }
823 
824 static int
825 ipfw_nat_modevent(module_t mod, int type, void *data)
826 {
827 	switch (type) {
828 		case MOD_LOAD:
829 			return ipfw_nat_init();
830 		case MOD_UNLOAD:
831 			return ipfw_nat_fini();
832 		default:
833 			break;
834 	}
835 	return 0;
836 }
837 
838 moduledata_t ipfw_nat_mod = {
839 	"ipfw3_nat",
840 	ipfw_nat_modevent,
841 	NULL
842 };
843 
844 DECLARE_MODULE(ipfw3_nat, ipfw_nat_mod,
845 		SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
846 MODULE_DEPEND(ipfw3_nat, libalias, 1, 1, 1);
847 MODULE_DEPEND(ipfw3_nat, ipfw3_basic, 1, 1, 1);
848 MODULE_VERSION(ipfw3_nat, 1);
849