xref: /freebsd/sys/netpfil/ipfw/ip_fw_nat.c (revision 3fc9e2c36555140de248a0b4def91bbfa44d7c2c)
1 /*-
2  * Copyright (c) 2008 Paolo Pisati
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/eventhandler.h>
33 #include <sys/malloc.h>
34 #include <sys/kernel.h>
35 #include <sys/lock.h>
36 #include <sys/module.h>
37 #include <sys/rwlock.h>
38 
39 #define        IPFW_INTERNAL   /* Access to protected data structures in ip_fw.h. */
40 
41 #include <netinet/libalias/alias.h>
42 #include <netinet/libalias/alias_local.h>
43 
44 #include <net/if.h>
45 #include <netinet/in.h>
46 #include <netinet/ip.h>
47 #include <netinet/ip_var.h>
48 #include <netinet/ip_fw.h>
49 #include <netinet/tcp.h>
50 #include <netinet/udp.h>
51 
52 #include <netpfil/ipfw/ip_fw_private.h>
53 
54 #include <machine/in_cksum.h>	/* XXX for in_cksum */
55 
56 static eventhandler_tag ifaddr_event_tag;
57 
58 static void
59 ifaddr_change(void *arg __unused, struct ifnet *ifp)
60 {
61 	struct cfg_nat *ptr;
62 	struct ifaddr *ifa;
63 	struct ip_fw_chain *chain;
64 
65 	KASSERT(curvnet == ifp->if_vnet,
66 	    ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet));
67 	chain = &V_layer3_chain;
68 	IPFW_WLOCK(chain);
69 	/* Check every nat entry... */
70 	LIST_FOREACH(ptr, &chain->nat, _next) {
71 		/* ...using nic 'ifp->if_xname' as dynamic alias address. */
72 		if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0)
73 			continue;
74 		if_addr_rlock(ifp);
75 		TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
76 			if (ifa->ifa_addr == NULL)
77 				continue;
78 			if (ifa->ifa_addr->sa_family != AF_INET)
79 				continue;
80 			ptr->ip = ((struct sockaddr_in *)
81 			    (ifa->ifa_addr))->sin_addr;
82 			LibAliasSetAddress(ptr->lib, ptr->ip);
83 		}
84 		if_addr_runlock(ifp);
85 	}
86 	IPFW_WUNLOCK(chain);
87 }
88 
89 /*
90  * delete the pointers for nat entry ix, or all of them if ix < 0
91  */
92 static void
93 flush_nat_ptrs(struct ip_fw_chain *chain, const int ix)
94 {
95 	int i;
96 	ipfw_insn_nat *cmd;
97 
98 	IPFW_WLOCK_ASSERT(chain);
99 	for (i = 0; i < chain->n_rules; i++) {
100 		cmd = (ipfw_insn_nat *)ACTION_PTR(chain->map[i]);
101 		/* XXX skip log and the like ? */
102 		if (cmd->o.opcode == O_NAT && cmd->nat != NULL &&
103 			    (ix < 0 || cmd->nat->id == ix))
104 			cmd->nat = NULL;
105 	}
106 }
107 
108 static void
109 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
110 {
111 	struct cfg_redir *r, *tmp_r;
112 	struct cfg_spool *s, *tmp_s;
113 	int i, num;
114 
115 	LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
116 		num = 1; /* Number of alias_link to delete. */
117 		switch (r->mode) {
118 		case REDIR_PORT:
119 			num = r->pport_cnt;
120 			/* FALLTHROUGH */
121 		case REDIR_ADDR:
122 		case REDIR_PROTO:
123 			/* Delete all libalias redirect entry. */
124 			for (i = 0; i < num; i++)
125 				LibAliasRedirectDelete(n->lib, r->alink[i]);
126 			/* Del spool cfg if any. */
127 			LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) {
128 				LIST_REMOVE(s, _next);
129 				free(s, M_IPFW);
130 			}
131 			free(r->alink, M_IPFW);
132 			LIST_REMOVE(r, _next);
133 			free(r, M_IPFW);
134 			break;
135 		default:
136 			printf("unknown redirect mode: %u\n", r->mode);
137 			/* XXX - panic?!?!? */
138 			break;
139 		}
140 	}
141 }
142 
143 static void
144 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
145 {
146 	struct cfg_redir *r, *ser_r;
147 	struct cfg_spool *s, *ser_s;
148 	int cnt, off, i;
149 
150 	for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
151 		ser_r = (struct cfg_redir *)&buf[off];
152 		r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO);
153 		memcpy(r, ser_r, SOF_REDIR);
154 		LIST_INIT(&r->spool_chain);
155 		off += SOF_REDIR;
156 		r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
157 		    M_IPFW, M_WAITOK | M_ZERO);
158 		switch (r->mode) {
159 		case REDIR_ADDR:
160 			r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
161 			    r->paddr);
162 			break;
163 		case REDIR_PORT:
164 			for (i = 0 ; i < r->pport_cnt; i++) {
165 				/* If remotePort is all ports, set it to 0. */
166 				u_short remotePortCopy = r->rport + i;
167 				if (r->rport_cnt == 1 && r->rport == 0)
168 					remotePortCopy = 0;
169 				r->alink[i] = LibAliasRedirectPort(ptr->lib,
170 				    r->laddr, htons(r->lport + i), r->raddr,
171 				    htons(remotePortCopy), r->paddr,
172 				    htons(r->pport + i), r->proto);
173 				if (r->alink[i] == NULL) {
174 					r->alink[0] = NULL;
175 					break;
176 				}
177 			}
178 			break;
179 		case REDIR_PROTO:
180 			r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
181 			    r->raddr, r->paddr, r->proto);
182 			break;
183 		default:
184 			printf("unknown redirect mode: %u\n", r->mode);
185 			break;
186 		}
187 		/* XXX perhaps return an error instead of panic ? */
188 		if (r->alink[0] == NULL)
189 			panic("LibAliasRedirect* returned NULL");
190 		/* LSNAT handling. */
191 		for (i = 0; i < r->spool_cnt; i++) {
192 			ser_s = (struct cfg_spool *)&buf[off];
193 			s = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO);
194 			memcpy(s, ser_s, SOF_SPOOL);
195 			LibAliasAddServer(ptr->lib, r->alink[0],
196 			    s->addr, htons(s->port));
197 			off += SOF_SPOOL;
198 			/* Hook spool entry. */
199 			LIST_INSERT_HEAD(&r->spool_chain, s, _next);
200 		}
201 		/* And finally hook this redir entry. */
202 		LIST_INSERT_HEAD(&ptr->redir_chain, r, _next);
203 	}
204 }
205 
206 /*
207  * ipfw_nat - perform mbuf header translation.
208  *
209  * Note V_layer3_chain has to be locked while calling ipfw_nat() in
210  * 'global' operation mode (t == NULL).
211  *
212  */
213 static int
214 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
215 {
216 	struct mbuf *mcl;
217 	struct ip *ip;
218 	/* XXX - libalias duct tape */
219 	int ldt, retval, found;
220 	struct ip_fw_chain *chain;
221 	char *c;
222 
223 	ldt = 0;
224 	retval = 0;
225 	mcl = m_megapullup(m, m->m_pkthdr.len);
226 	if (mcl == NULL) {
227 		args->m = NULL;
228 		return (IP_FW_DENY);
229 	}
230 	ip = mtod(mcl, struct ip *);
231 
232 	/*
233 	 * XXX - Libalias checksum offload 'duct tape':
234 	 *
235 	 * locally generated packets have only pseudo-header checksum
236 	 * calculated and libalias will break it[1], so mark them for
237 	 * later fix.  Moreover there are cases when libalias modifies
238 	 * tcp packet data[2], mark them for later fix too.
239 	 *
240 	 * [1] libalias was never meant to run in kernel, so it does
241 	 * not have any knowledge about checksum offloading, and
242 	 * expects a packet with a full internet checksum.
243 	 * Unfortunately, packets generated locally will have just the
244 	 * pseudo header calculated, and when libalias tries to adjust
245 	 * the checksum it will actually compute a wrong value.
246 	 *
247 	 * [2] when libalias modifies tcp's data content, full TCP
248 	 * checksum has to be recomputed: the problem is that
249 	 * libalias does not have any idea about checksum offloading.
250 	 * To work around this, we do not do checksumming in LibAlias,
251 	 * but only mark the packets in th_x2 field. If we receive a
252 	 * marked packet, we calculate correct checksum for it
253 	 * aware of offloading.  Why such a terrible hack instead of
254 	 * recalculating checksum for each packet?
255 	 * Because the previous checksum was not checked!
256 	 * Recalculating checksums for EVERY packet will hide ALL
257 	 * transmission errors. Yes, marked packets still suffer from
258 	 * this problem. But, sigh, natd(8) has this problem, too.
259 	 *
260 	 * TODO: -make libalias mbuf aware (so
261 	 * it can handle delayed checksum and tso)
262 	 */
263 
264 	if (mcl->m_pkthdr.rcvif == NULL &&
265 	    mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA)
266 		ldt = 1;
267 
268 	c = mtod(mcl, char *);
269 
270 	/* Check if this is 'global' instance */
271 	if (t == NULL) {
272 		if (args->oif == NULL) {
273 			/* Wrong direction, skip processing */
274 			args->m = mcl;
275 			return (IP_FW_NAT);
276 		}
277 
278 		found = 0;
279 		chain = &V_layer3_chain;
280 		IPFW_RLOCK_ASSERT(chain);
281 		/* Check every nat entry... */
282 		LIST_FOREACH(t, &chain->nat, _next) {
283 			if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0)
284 				continue;
285 			retval = LibAliasOutTry(t->lib, c,
286 			    mcl->m_len + M_TRAILINGSPACE(mcl), 0);
287 			if (retval == PKT_ALIAS_OK) {
288 				/* Nat instance recognises state */
289 				found = 1;
290 				break;
291 			}
292 		}
293 		if (found != 1) {
294 			/* No instance found, return ignore */
295 			args->m = mcl;
296 			return (IP_FW_NAT);
297 		}
298 	} else {
299 		if (args->oif == NULL)
300 			retval = LibAliasIn(t->lib, c,
301 				mcl->m_len + M_TRAILINGSPACE(mcl));
302 		else
303 			retval = LibAliasOut(t->lib, c,
304 				mcl->m_len + M_TRAILINGSPACE(mcl));
305 	}
306 
307 	/*
308 	 * We drop packet when:
309 	 * 1. libalias returns PKT_ALIAS_ERROR;
310 	 * 2. For incoming packets:
311 	 *	a) for unresolved fragments;
312 	 *	b) libalias returns PKT_ALIAS_IGNORED and
313 	 *		PKT_ALIAS_DENY_INCOMING flag is set.
314 	 */
315 	if (retval == PKT_ALIAS_ERROR ||
316 	    (args->oif == NULL && (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
317 	    (retval == PKT_ALIAS_IGNORED &&
318 	    (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) {
319 		/* XXX - should i add some logging? */
320 		m_free(mcl);
321 		args->m = NULL;
322 		return (IP_FW_DENY);
323 	}
324 
325 	if (retval == PKT_ALIAS_RESPOND)
326 		mcl->m_flags |= M_SKIP_FIREWALL;
327 	mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
328 
329 	/*
330 	 * XXX - libalias checksum offload
331 	 * 'duct tape' (see above)
332 	 */
333 
334 	if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
335 	    ip->ip_p == IPPROTO_TCP) {
336 		struct tcphdr 	*th;
337 
338 		th = (struct tcphdr *)(ip + 1);
339 		if (th->th_x2)
340 			ldt = 1;
341 	}
342 
343 	if (ldt) {
344 		struct tcphdr 	*th;
345 		struct udphdr 	*uh;
346 		uint16_t ip_len, cksum;
347 
348 		ip_len = ntohs(ip->ip_len);
349 		cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
350 		    htons(ip->ip_p + ip_len - (ip->ip_hl << 2)));
351 
352 		switch (ip->ip_p) {
353 		case IPPROTO_TCP:
354 			th = (struct tcphdr *)(ip + 1);
355 			/*
356 			 * Maybe it was set in
357 			 * libalias...
358 			 */
359 			th->th_x2 = 0;
360 			th->th_sum = cksum;
361 			mcl->m_pkthdr.csum_data =
362 			    offsetof(struct tcphdr, th_sum);
363 			break;
364 		case IPPROTO_UDP:
365 			uh = (struct udphdr *)(ip + 1);
366 			uh->uh_sum = cksum;
367 			mcl->m_pkthdr.csum_data =
368 			    offsetof(struct udphdr, uh_sum);
369 			break;
370 		}
371 		/* No hw checksum offloading: do it ourselves */
372 		if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) {
373 			in_delayed_cksum(mcl);
374 			mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
375 		}
376 	}
377 	args->m = mcl;
378 	return (IP_FW_NAT);
379 }
380 
381 static struct cfg_nat *
382 lookup_nat(struct nat_list *l, int nat_id)
383 {
384 	struct cfg_nat *res;
385 
386 	LIST_FOREACH(res, l, _next) {
387 		if (res->id == nat_id)
388 			break;
389 	}
390 	return res;
391 }
392 
393 static int
394 ipfw_nat_cfg(struct sockopt *sopt)
395 {
396 	struct cfg_nat *cfg, *ptr;
397 	char *buf;
398 	struct ip_fw_chain *chain = &V_layer3_chain;
399 	size_t len;
400 	int gencnt, error = 0;
401 
402 	len = sopt->sopt_valsize;
403 	buf = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
404 	if ((error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat))) != 0)
405 		goto out;
406 
407 	cfg = (struct cfg_nat *)buf;
408 	if (cfg->id < 0) {
409 		error = EINVAL;
410 		goto out;
411 	}
412 
413 	/*
414 	 * Find/create nat rule.
415 	 */
416 	IPFW_WLOCK(chain);
417 	gencnt = chain->gencnt;
418 	ptr = lookup_nat(&chain->nat, cfg->id);
419 	if (ptr == NULL) {
420 		IPFW_WUNLOCK(chain);
421 		/* New rule: allocate and init new instance. */
422 		ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO);
423 		ptr->lib = LibAliasInit(NULL);
424 		LIST_INIT(&ptr->redir_chain);
425 	} else {
426 		/* Entry already present: temporarily unhook it. */
427 		LIST_REMOVE(ptr, _next);
428 		flush_nat_ptrs(chain, cfg->id);
429 		IPFW_WUNLOCK(chain);
430 	}
431 
432 	/*
433 	 * Basic nat configuration.
434 	 */
435 	ptr->id = cfg->id;
436 	/*
437 	 * XXX - what if this rule doesn't nat any ip and just
438 	 * redirect?
439 	 * do we set aliasaddress to 0.0.0.0?
440 	 */
441 	ptr->ip = cfg->ip;
442 	ptr->redir_cnt = cfg->redir_cnt;
443 	ptr->mode = cfg->mode;
444 	LibAliasSetMode(ptr->lib, cfg->mode, cfg->mode);
445 	LibAliasSetAddress(ptr->lib, ptr->ip);
446 	memcpy(ptr->if_name, cfg->if_name, IF_NAMESIZE);
447 
448 	/*
449 	 * Redir and LSNAT configuration.
450 	 */
451 	/* Delete old cfgs. */
452 	del_redir_spool_cfg(ptr, &ptr->redir_chain);
453 	/* Add new entries. */
454 	add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr);
455 
456 	IPFW_WLOCK(chain);
457 	/* Extra check to avoid race with another ipfw_nat_cfg() */
458 	if (gencnt != chain->gencnt &&
459 	    ((cfg = lookup_nat(&chain->nat, ptr->id)) != NULL))
460 		LIST_REMOVE(cfg, _next);
461 	LIST_INSERT_HEAD(&chain->nat, ptr, _next);
462 	chain->gencnt++;
463 	IPFW_WUNLOCK(chain);
464 
465 out:
466 	free(buf, M_TEMP);
467 	return (error);
468 }
469 
470 static int
471 ipfw_nat_del(struct sockopt *sopt)
472 {
473 	struct cfg_nat *ptr;
474 	struct ip_fw_chain *chain = &V_layer3_chain;
475 	int i;
476 
477 	sooptcopyin(sopt, &i, sizeof i, sizeof i);
478 	/* XXX validate i */
479 	IPFW_WLOCK(chain);
480 	ptr = lookup_nat(&chain->nat, i);
481 	if (ptr == NULL) {
482 		IPFW_WUNLOCK(chain);
483 		return (EINVAL);
484 	}
485 	LIST_REMOVE(ptr, _next);
486 	flush_nat_ptrs(chain, i);
487 	IPFW_WUNLOCK(chain);
488 	del_redir_spool_cfg(ptr, &ptr->redir_chain);
489 	LibAliasUninit(ptr->lib);
490 	free(ptr, M_IPFW);
491 	return (0);
492 }
493 
494 static int
495 ipfw_nat_get_cfg(struct sockopt *sopt)
496 {
497 	struct ip_fw_chain *chain = &V_layer3_chain;
498 	struct cfg_nat *n;
499 	struct cfg_redir *r;
500 	struct cfg_spool *s;
501 	char *data;
502 	int gencnt, nat_cnt, len, error;
503 
504 	nat_cnt = 0;
505 	len = sizeof(nat_cnt);
506 
507 	IPFW_RLOCK(chain);
508 retry:
509 	gencnt = chain->gencnt;
510 	/* Estimate memory amount */
511 	LIST_FOREACH(n, &chain->nat, _next) {
512 		nat_cnt++;
513 		len += sizeof(struct cfg_nat);
514 		LIST_FOREACH(r, &n->redir_chain, _next) {
515 			len += sizeof(struct cfg_redir);
516 			LIST_FOREACH(s, &r->spool_chain, _next)
517 				len += sizeof(struct cfg_spool);
518 		}
519 	}
520 	IPFW_RUNLOCK(chain);
521 
522 	data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
523 	bcopy(&nat_cnt, data, sizeof(nat_cnt));
524 
525 	nat_cnt = 0;
526 	len = sizeof(nat_cnt);
527 
528 	IPFW_RLOCK(chain);
529 	if (gencnt != chain->gencnt) {
530 		free(data, M_TEMP);
531 		goto retry;
532 	}
533 	/* Serialize all the data. */
534 	LIST_FOREACH(n, &chain->nat, _next) {
535 		bcopy(n, &data[len], sizeof(struct cfg_nat));
536 		len += sizeof(struct cfg_nat);
537 		LIST_FOREACH(r, &n->redir_chain, _next) {
538 			bcopy(r, &data[len], sizeof(struct cfg_redir));
539 			len += sizeof(struct cfg_redir);
540 			LIST_FOREACH(s, &r->spool_chain, _next) {
541 				bcopy(s, &data[len], sizeof(struct cfg_spool));
542 				len += sizeof(struct cfg_spool);
543 			}
544 		}
545 	}
546 	IPFW_RUNLOCK(chain);
547 
548 	error = sooptcopyout(sopt, data, len);
549 	free(data, M_TEMP);
550 
551 	return (error);
552 }
553 
554 static int
555 ipfw_nat_get_log(struct sockopt *sopt)
556 {
557 	uint8_t *data;
558 	struct cfg_nat *ptr;
559 	int i, size;
560 	struct ip_fw_chain *chain;
561 
562 	chain = &V_layer3_chain;
563 
564 	IPFW_RLOCK(chain);
565 	/* one pass to count, one to copy the data */
566 	i = 0;
567 	LIST_FOREACH(ptr, &chain->nat, _next) {
568 		if (ptr->lib->logDesc == NULL)
569 			continue;
570 		i++;
571 	}
572 	size = i * (LIBALIAS_BUF_SIZE + sizeof(int));
573 	data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO);
574 	if (data == NULL) {
575 		IPFW_RUNLOCK(chain);
576 		return (ENOSPC);
577 	}
578 	i = 0;
579 	LIST_FOREACH(ptr, &chain->nat, _next) {
580 		if (ptr->lib->logDesc == NULL)
581 			continue;
582 		bcopy(&ptr->id, &data[i], sizeof(int));
583 		i += sizeof(int);
584 		bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE);
585 		i += LIBALIAS_BUF_SIZE;
586 	}
587 	IPFW_RUNLOCK(chain);
588 	sooptcopyout(sopt, data, size);
589 	free(data, M_IPFW);
590 	return(0);
591 }
592 
593 static int
594 vnet_ipfw_nat_init(const void *arg __unused)
595 {
596 
597 	V_ipfw_nat_ready = 1;
598 	return (0);
599 }
600 
601 static int
602 vnet_ipfw_nat_uninit(const void *arg __unused)
603 {
604 	struct cfg_nat *ptr, *ptr_temp;
605 	struct ip_fw_chain *chain;
606 
607 	chain = &V_layer3_chain;
608 	IPFW_WLOCK(chain);
609 	LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) {
610 		LIST_REMOVE(ptr, _next);
611 		del_redir_spool_cfg(ptr, &ptr->redir_chain);
612 		LibAliasUninit(ptr->lib);
613 		free(ptr, M_IPFW);
614 	}
615 	flush_nat_ptrs(chain, -1 /* flush all */);
616 	V_ipfw_nat_ready = 0;
617 	IPFW_WUNLOCK(chain);
618 	return (0);
619 }
620 
621 static void
622 ipfw_nat_init(void)
623 {
624 
625 	/* init ipfw hooks */
626 	ipfw_nat_ptr = ipfw_nat;
627 	lookup_nat_ptr = lookup_nat;
628 	ipfw_nat_cfg_ptr = ipfw_nat_cfg;
629 	ipfw_nat_del_ptr = ipfw_nat_del;
630 	ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
631 	ipfw_nat_get_log_ptr = ipfw_nat_get_log;
632 
633 	ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
634 	    NULL, EVENTHANDLER_PRI_ANY);
635 }
636 
637 static void
638 ipfw_nat_destroy(void)
639 {
640 
641 	EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
642 	/* deregister ipfw_nat */
643 	ipfw_nat_ptr = NULL;
644 	lookup_nat_ptr = NULL;
645 	ipfw_nat_cfg_ptr = NULL;
646 	ipfw_nat_del_ptr = NULL;
647 	ipfw_nat_get_cfg_ptr = NULL;
648 	ipfw_nat_get_log_ptr = NULL;
649 }
650 
651 static int
652 ipfw_nat_modevent(module_t mod, int type, void *unused)
653 {
654 	int err = 0;
655 
656 	switch (type) {
657 	case MOD_LOAD:
658 		break;
659 
660 	case MOD_UNLOAD:
661 		break;
662 
663 	default:
664 		return EOPNOTSUPP;
665 		break;
666 	}
667 	return err;
668 }
669 
670 static moduledata_t ipfw_nat_mod = {
671 	"ipfw_nat",
672 	ipfw_nat_modevent,
673 	0
674 };
675 
676 /* Define startup order. */
677 #define	IPFW_NAT_SI_SUB_FIREWALL	SI_SUB_PROTO_IFATTACHDOMAIN
678 #define	IPFW_NAT_MODEVENT_ORDER		(SI_ORDER_ANY - 255)
679 #define	IPFW_NAT_MODULE_ORDER		(IPFW_NAT_MODEVENT_ORDER + 1)
680 #define	IPFW_NAT_VNET_ORDER		(IPFW_NAT_MODEVENT_ORDER + 2)
681 
682 DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY);
683 MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1);
684 MODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2);
685 MODULE_VERSION(ipfw_nat, 1);
686 
687 SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
688     ipfw_nat_init, NULL);
689 VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER,
690     vnet_ipfw_nat_init, NULL);
691 
692 SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
693     ipfw_nat_destroy, NULL);
694 VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL,
695     IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL);
696 
697 /* end of file */
698