Merge branch 'vendor/ELFTOOLCHAIN'
[dragonfly.git] / sys / net / ipfw3_nat / ip_fw3_nat.c
1 /*
2  * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Bill Yuan <bycn82@gmail.com>
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 static struct lock nat_lock;
82
83 extern struct ipfw_nat_context  *ipfw_nat_ctx;
84 extern struct ipfw_context      *ipfw_ctx[MAXCPU];
85 extern ipfw_nat_cfg_t *ipfw_nat_cfg_ptr;
86 extern ipfw_nat_cfg_t *ipfw_nat_del_ptr;
87 extern ipfw_nat_cfg_t *ipfw_nat_flush_ptr;
88 extern ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr;
89 extern ipfw_nat_cfg_t *ipfw_nat_get_log_ptr;
90
91 typedef int ipfw_nat_t(struct ip_fw_args *, struct cfg_nat *, struct mbuf *);
92
93 int ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m);
94 int ipfw_nat_cfg(struct sockopt *sopt);
95 int ipfw_nat_del(struct sockopt *sopt);
96 int ipfw_nat_flush(struct sockopt *sopt);
97 void check_nat(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
98                 struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len);
99
100
101 void
102 check_nat(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
103                 struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len)
104 {
105         if ((*args)->eh != NULL) {
106                 *cmd_ctl = IP_FW_CTL_NO;
107                 *cmd_val = IP_FW_NOT_MATCH;
108                 return;
109         }
110         struct cfg_nat *t;
111         int nat_id;
112         (*args)->rule = *f;
113         lockmgr(&nat_lock, LK_SHARED);
114         t = ((ipfw_insn_nat *)cmd)->nat;
115         if (t == NULL) {
116                 nat_id = cmd->arg1;
117                 LOOKUP_NAT((*ipfw_nat_ctx), nat_id, t);
118                 if (t == NULL) {
119                         lockmgr(&nat_lock, LK_RELEASE);
120                         *cmd_val = IP_FW_DENY;
121                         *cmd_ctl = IP_FW_CTL_DONE;
122                         return;
123                 }
124                 ((ipfw_insn_nat *)cmd)->nat = t;
125         }
126         *cmd_val = ipfw_nat(*args, t, (*args)->m);
127         lockmgr(&nat_lock, LK_RELEASE);
128         *cmd_ctl = IP_FW_CTL_NAT;
129 }
130
131 static void
132 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
133 {
134         struct cfg_redir *r, *tmp_r;
135         struct cfg_spool *s, *tmp_s;
136         int i, num;
137
138         LIST_FOREACH_MUTABLE(r, head, _next, tmp_r) {
139                 num = 1; /* Number of alias_link to delete. */
140                 switch (r->mode) {
141                         case REDIR_PORT:
142                                 num = r->pport_cnt;
143                                 /* FALLTHROUGH */
144                         case REDIR_ADDR:
145                         case REDIR_PROTO:
146                                 /* Delete all libalias redirect entry. */
147                                 for (i = 0; i < num; i++)
148                                         LibAliasRedirectDelete(n->lib,
149                                                         r->alink[i]);
150
151                                 /* Del spool cfg if any. */
152                                 LIST_FOREACH_MUTABLE(s, &r->spool_chain,
153                                                 _next, tmp_s) {
154                                         LIST_REMOVE(s, _next);
155                                         kfree(s, M_IPFW_NAT);
156                                 }
157                                 kfree(r->alink, M_IPFW_NAT);
158                                 LIST_REMOVE(r, _next);
159                                 kfree(r, M_IPFW_NAT);
160                                 break;
161                         default:
162                                 kprintf("unknown redirect mode: %u\n", r->mode);
163                                 /* XXX - panic?!?!? */
164                                 break;
165                 }
166         }
167 }
168
169 static int
170 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
171 {
172         struct cfg_redir *r, *ser_r;
173         struct cfg_spool *s, *ser_s;
174         int cnt, off, i;
175         char *panic_err;
176
177         for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
178                 ser_r = (struct cfg_redir *)&buf[off];
179                 r = kmalloc(SOF_REDIR, M_IPFW_NAT, M_WAITOK | M_ZERO);
180                 memcpy(r, ser_r, SOF_REDIR);
181                 LIST_INIT(&r->spool_chain);
182                 off += SOF_REDIR;
183                 r->alink = kmalloc(sizeof(struct alias_link *) * r->pport_cnt,
184                                 M_IPFW_NAT, M_WAITOK | M_ZERO);
185                 switch (r->mode) {
186                         case REDIR_ADDR:
187                                 r->alink[0] = LibAliasRedirectAddr(ptr->lib,
188                                                         r->laddr, r->paddr);
189                                 break;
190                         case REDIR_PORT:
191                                 for (i = 0 ; i < r->pport_cnt; i++) {
192                                         /*
193                                          * If remotePort is all ports
194                                          * set it to 0.
195                                          */
196                                         u_short remotePortCopy = r->rport + i;
197                                         if (r->rport_cnt == 1 && r->rport == 0)
198                                                 remotePortCopy = 0;
199                                                 r->alink[i] =
200
201                                                 LibAliasRedirectPort(ptr->lib,
202                                                 r->laddr,htons(r->lport + i),
203                                                 r->raddr,htons(remotePortCopy),
204                                                 r->paddr,htons(r->pport + i),
205                                                 r->proto);
206
207                                         if (r->alink[i] == NULL) {
208                                                 r->alink[0] = NULL;
209                                                 break;
210                                         }
211                                 }
212                                 break;
213                         case REDIR_PROTO:
214                                 r->alink[0] = LibAliasRedirectProto(ptr->lib,
215                                         r->laddr, r->raddr, r->paddr, r->proto);
216                                 break;
217                         default:
218                                 kprintf("unknown redirect mode: %u\n", r->mode);
219                                 break;
220                 }
221                 if (r->alink[0] == NULL) {
222                         panic_err = "LibAliasRedirect* returned NULL";
223                         goto bad;
224                 } else /* LSNAT handling. */
225                         for (i = 0; i < r->spool_cnt; i++) {
226                                 ser_s = (struct cfg_spool *)&buf[off];
227                                 s = kmalloc(SOF_REDIR, M_IPFW_NAT,
228                                                 M_WAITOK | M_ZERO);
229                                 memcpy(s, ser_s, SOF_SPOOL);
230                                 LibAliasAddServer(ptr->lib, r->alink[0],
231                                                 s->addr, htons(s->port));
232                                 off += SOF_SPOOL;
233                                 /* Hook spool entry. */
234                                 HOOK_SPOOL(&r->spool_chain, s);
235                         }
236                 /* And finally hook this redir entry. */
237                 HOOK_REDIR(&ptr->redir_chain, r);
238         }
239         return 1;
240 bad:
241         /* something really bad happened: panic! */
242         panic("%s\n", panic_err);
243 }
244
245 static int
246 ipfw_nat_get_cfg(struct sockopt *sopt)
247 {
248         uint8_t *data;
249         struct cfg_nat *n;
250         struct cfg_redir *r;
251         struct cfg_spool *s;
252         int nat_cnt, off, nat_cfg_size;
253         size_t size;
254
255         nat_cnt = 0;
256         nat_cfg_size = 0;
257         off = sizeof(nat_cnt);
258
259         size = sopt->sopt_valsize;
260
261         data = sopt->sopt_val;
262         lockmgr(&nat_lock, LK_SHARED);
263         /* count the size of nat cfg */
264         LIST_FOREACH(n, &((*ipfw_nat_ctx).nat), _next) {
265                 nat_cfg_size += SOF_NAT;
266         }
267
268         LIST_FOREACH(n, &((*ipfw_nat_ctx).nat), _next) {
269                 nat_cnt++;
270                 if (off + SOF_NAT < size) {
271                         bcopy(n, &data[off], SOF_NAT);
272                         off += SOF_NAT;
273                         LIST_FOREACH(r, &n->redir_chain, _next) {
274                                 if (off + SOF_REDIR < size) {
275                                         bcopy(r, &data[off], SOF_REDIR);
276                                         off += SOF_REDIR;
277                                         LIST_FOREACH(s, &r->spool_chain,
278                                                 _next) {
279                                                 if (off + SOF_SPOOL < size) {
280                                                         bcopy(s, &data[off],
281                                                                 SOF_SPOOL);
282                                                         off += SOF_SPOOL;
283                                                 } else
284                                                         goto nospace;
285                                         }
286                                 } else
287                                         goto nospace;
288                         }
289                 } else
290                         goto nospace;
291         }
292         bcopy(&nat_cnt, data, sizeof(nat_cnt));
293         sopt->sopt_valsize = nat_cfg_size;
294         lockmgr(&nat_lock, LK_RELEASE);
295         return 0;
296 nospace:
297         lockmgr(&nat_lock, LK_RELEASE);
298         bzero(sopt->sopt_val, sopt->sopt_valsize);
299         sopt->sopt_valsize = nat_cfg_size;
300         return 0;
301 }
302
303 static int
304 ipfw_nat_get_log(struct sockopt *sopt)
305 {
306         struct cfg_nat *t;
307         struct alias_link *lnk;
308         struct libalias *la;
309         size_t sopt_size, all_lnk_size = 0;
310         int i, *nat_id;
311         struct ipfw_ioc_nat_state *nat_state;
312
313         nat_id = (int *)(sopt->sopt_val);
314         sopt_size = sopt->sopt_valsize;
315
316         lockmgr(&nat_lock, LK_SHARED);
317         LOOKUP_NAT((*ipfw_nat_ctx), *nat_id, t);
318         if (t != NULL) {
319                 nat_state = (struct ipfw_ioc_nat_state *)sopt->sopt_val;
320                 la = t->lib;
321                 LIBALIAS_LOCK_ASSERT(la);
322                 for (i = 0; i < LINK_TABLE_OUT_SIZE; i ++) {
323                         LIST_FOREACH(lnk, &la->linkTableOut[i], list_out) {
324                                 all_lnk_size += sizeof(*nat_state);
325                                 if (all_lnk_size > sopt_size)
326                                         goto nospace;
327                                 nat_state->src_addr = lnk->src_addr;
328                                 nat_state->dst_addr = lnk->dst_addr;
329                                 nat_state->alias_addr = lnk->alias_addr;
330                                 nat_state->src_port = lnk->src_port;
331                                 nat_state->dst_port = lnk->dst_port;
332                                 nat_state->alias_port = lnk->alias_port;
333                                 nat_state->link_type = lnk->link_type;
334                                 nat_state->timestamp = lnk->timestamp;
335                                 nat_state++;
336                         }
337                 }
338         }
339         sopt->sopt_valsize = all_lnk_size;
340         lockmgr(&nat_lock, LK_RELEASE);
341         return 0;
342 nospace:
343         lockmgr(&nat_lock, LK_RELEASE);
344         return 0;
345 }
346
347 int
348 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
349 {
350         struct mbuf *mcl;
351         struct ip *ip;
352         int ldt, retval;
353         char *c;
354         ldt = 0;
355         retval = 0;
356         if ((mcl = m_megapullup(m, m->m_pkthdr.len)) ==NULL)
357                 goto badnat;
358         ip = mtod(mcl, struct ip *);
359         if (args->eh == NULL) {
360                 ip->ip_len = htons(ip->ip_len);
361                 ip->ip_off = htons(ip->ip_off);
362         }
363
364         if (mcl->m_pkthdr.rcvif == NULL &&
365                         mcl->m_pkthdr.csum_flags &
366                         CSUM_DELAY_DATA)
367                 ldt = 1;
368
369         c = mtod(mcl, char *);
370         if (args->oif == NULL)
371                 retval = LibAliasIn(t->lib, c,
372                                 mcl->m_len + M_TRAILINGSPACE(mcl));
373         else
374                 retval = LibAliasOut(t->lib, c,
375                                 mcl->m_len + M_TRAILINGSPACE(mcl));
376         if (retval != PKT_ALIAS_OK &&
377                         retval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
378                 /* XXX - should i add some logging? */
379                 m_free(mcl);
380 badnat:
381                 args->m = NULL;
382                 return IP_FW_DENY;
383         }
384         mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
385
386         if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
387                         ip->ip_p == IPPROTO_TCP) {
388                 struct tcphdr   *th;
389
390                 th = (struct tcphdr *)(ip + 1);
391                 if (th->th_x2){
392                         ldt = 1;
393                 }
394         }
395
396         if (ldt) {
397                 struct tcphdr   *th;
398                 struct udphdr   *uh;
399                 u_short cksum;
400
401                 ip->ip_len = ntohs(ip->ip_len);
402                 cksum = in_pseudo(
403                                 ip->ip_src.s_addr,
404                                 ip->ip_dst.s_addr,
405                                 htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2))
406                                 );
407
408                 switch (ip->ip_p) {
409                         case IPPROTO_TCP:
410                                 th = (struct tcphdr *)(ip + 1);
411                                 th->th_x2 = 0;
412                                 th->th_sum = cksum;
413                                 mcl->m_pkthdr.csum_data =
414                                         offsetof(struct tcphdr, th_sum);
415                                 break;
416                         case IPPROTO_UDP:
417                                 uh = (struct udphdr *)(ip + 1);
418                                 uh->uh_sum = cksum;
419                                 mcl->m_pkthdr.csum_data =
420                                         offsetof(struct udphdr, uh_sum);
421                                 break;
422                 }
423                 /*
424                  * No hw checksum offloading: do it
425                  * by ourself.
426                  */
427                 if ((mcl->m_pkthdr.csum_flags &
428                                         CSUM_DELAY_DATA) == 0) {
429                         in_delayed_cksum(mcl);
430                         mcl->m_pkthdr.csum_flags &=
431                                 ~CSUM_DELAY_DATA;
432                 }
433                 ip->ip_len = htons(ip->ip_len);
434         }
435
436         if (args->eh == NULL) {
437                 ip->ip_len = ntohs(ip->ip_len);
438                 ip->ip_off = ntohs(ip->ip_off);
439         }
440
441         args->m = mcl;
442         return IP_FW_NAT;
443 }
444
445 int ipfw_nat_cfg(struct sockopt *sopt)
446 {
447         struct cfg_nat *ptr, *ser_n;
448         char *buf;
449
450         buf = kmalloc(sopt->sopt_valsize, M_IPFW_NAT, M_WAITOK | M_ZERO);
451         sooptcopyin(sopt, buf, sopt->sopt_valsize, sizeof(struct cfg_nat));
452         ser_n = (struct cfg_nat *)(sopt->sopt_val);
453
454         /*
455          * Find/create nat rule.
456          */
457         lockmgr(&nat_lock, LK_EXCLUSIVE);
458         LOOKUP_NAT((*ipfw_nat_ctx), ser_n->id, ptr);
459
460         if (ptr == NULL) {
461                 /* New rule: allocate and init new instance. */
462                 ptr = kmalloc(sizeof(struct cfg_nat), M_IPFW_NAT,
463                                 M_WAITOK | M_ZERO);
464
465                 ptr->lib = LibAliasInit(NULL);
466                 if (ptr->lib == NULL) {
467                         kfree(ptr, M_IPFW_NAT);
468                         kfree(buf, M_IPFW_NAT);
469                         lockmgr(&nat_lock, LK_RELEASE);
470                         return EINVAL;
471                 }
472
473                 LIST_INIT(&ptr->redir_chain);
474         } else {
475                 /* XXX TODO Entry already exists */
476                 goto done;
477         }
478
479         /*
480          * Basic nat configuration.
481          */
482         ptr->id = ser_n->id;
483         /*
484          * XXX - what if this rule doesn't nat any ip and just
485          * redirect?
486          * do we set aliasaddress to 0.0.0.0?
487          */
488         ptr->ip = ser_n->ip;
489         ptr->redir_cnt = ser_n->redir_cnt;
490         ptr->mode = ser_n->mode;
491
492         LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode);
493         LibAliasSetAddress(ptr->lib, ptr->ip);
494         memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE);
495
496         /* Add new entries. */
497         add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr);
498         HOOK_NAT(&(ipfw_nat_ctx->nat), ptr);
499 done:
500         lockmgr(&nat_lock, LK_RELEASE);
501         kfree(buf, M_IPFW_NAT);
502         return 0;
503 }
504
505 int
506 ipfw_nat_del(struct sockopt *sopt)
507 {
508         struct ipfw_context *ctx;
509         struct cfg_nat *n, *tmp;
510         struct ip_fw *f;
511         ipfw_insn *cmd;
512         int *i, cpu;
513
514         i = sopt->sopt_val;
515         lockmgr(&nat_lock, LK_EXCLUSIVE);
516         LOOKUP_NAT((*ipfw_nat_ctx), *i, n);
517         if (n == NULL) {
518                 lockmgr(&nat_lock, LK_RELEASE);
519                 return EINVAL;
520         }
521
522         /*
523          * stop deleting when this cfg_nat was in use in ipfw_ctx
524          */
525         for (cpu = 0; cpu < ncpus; cpu++) {
526                 ctx = ipfw_ctx[cpu];
527                 for (f = ctx->ipfw_rule_chain; f; f = f->next) {
528                         cmd = ACTION_PTR(f);
529                         if ((int)cmd->module == MODULE_NAT_ID &&
530                                         (int)cmd->opcode == O_NAT_NAT) {
531                                 tmp = ((ipfw_insn_nat *)cmd)->nat;
532                                 if (tmp != NULL && tmp->id == n->id) {
533                                         lockmgr(&nat_lock, LK_RELEASE);
534                                         return EINVAL;
535                                 }
536                         }
537                 }
538         }
539
540         UNHOOK_NAT(n);
541         del_redir_spool_cfg(n, &n->redir_chain);
542         LibAliasUninit(n->lib);
543         kfree(n, M_IPFW_NAT);
544         lockmgr(&nat_lock, LK_RELEASE);
545         return 0;
546 }
547
548 int
549 ipfw_nat_flush(struct sockopt *sopt)
550 {
551         struct ipfw_context *ctx;
552         struct cfg_nat *ptr, *tmp;
553         struct ip_fw *f;
554         ipfw_insn *cmd;
555         int cpu;
556
557         /*
558          * stop flushing when any cfg_nat was in use in ipfw_ctx
559          */
560         for (cpu = 0; cpu < ncpus; cpu++) {
561                 ctx = ipfw_ctx[cpu];
562                 for (f = ctx->ipfw_rule_chain; f; f = f->next) {
563                         cmd = ACTION_PTR(f);
564                         if ((int)cmd->module == MODULE_NAT_ID &&
565                                 (int)cmd->opcode == O_NAT_NAT) {
566                                 return EINVAL;
567                         }
568                 }
569         }
570
571         lockmgr(&nat_lock, LK_EXCLUSIVE);
572         LIST_FOREACH_MUTABLE(ptr, &(ipfw_nat_ctx->nat), _next, tmp) {
573                 LIST_REMOVE(ptr, _next);
574                 del_redir_spool_cfg(ptr, &ptr->redir_chain);
575                 LibAliasUninit(ptr->lib);
576                 kfree(ptr, M_IPFW_NAT);
577         }
578         lockmgr(&nat_lock, LK_RELEASE);
579         return 0;
580 }
581
582 static
583 int ipfw_nat_init(void)
584 {
585         lockinit(&nat_lock, "ipfw3 nat lock", 0, 0);
586         register_ipfw_module(MODULE_NAT_ID, MODULE_NAT_NAME);
587         register_ipfw_filter_funcs(MODULE_NAT_ID, O_NAT_NAT,
588                         (filter_func)check_nat);
589         ipfw_nat_cfg_ptr = ipfw_nat_cfg;
590         ipfw_nat_del_ptr = ipfw_nat_del;
591         ipfw_nat_flush_ptr = ipfw_nat_flush;
592         ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
593         ipfw_nat_get_log_ptr = ipfw_nat_get_log;
594         return 0;
595 }
596
597 static int
598 ipfw_nat_stop(void)
599 {
600         struct cfg_nat *ptr, *ptr_temp;
601         lockuninit(&nat_lock);
602         LIST_FOREACH_MUTABLE(ptr, &(ipfw_nat_ctx->nat), _next, ptr_temp) {
603                 LIST_REMOVE(ptr, _next);
604                 del_redir_spool_cfg(ptr, &ptr->redir_chain);
605                 LibAliasUninit(ptr->lib);
606                 kfree(ptr, M_IPFW_NAT);
607         }
608
609         ipfw_nat_cfg_ptr = NULL;
610         ipfw_nat_del_ptr = NULL;
611         ipfw_nat_flush_ptr = NULL;
612         ipfw_nat_get_cfg_ptr = NULL;
613         ipfw_nat_get_log_ptr = NULL;
614
615         return unregister_ipfw_module(MODULE_NAT_ID);
616 }
617
618 static int ipfw_nat_modevent(module_t mod, int type, void *data)
619 {
620         switch (type) {
621                 case MOD_LOAD:
622                         return ipfw_nat_init();
623                 case MOD_UNLOAD:
624                         return ipfw_nat_stop();
625                 default:
626                         break;
627         }
628         return 0;
629 }
630
631 static moduledata_t ipfw_nat_mod = {
632         "ipfw3_nat",
633         ipfw_nat_modevent,
634         NULL
635 };
636
637 DECLARE_MODULE(ipfw3_nat, ipfw_nat_mod,
638                 SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
639 MODULE_DEPEND(ipfw3_nat, libalias, 1, 1, 1);
640 MODULE_DEPEND(ipfw3_nat, ipfw3_basic, 1, 1, 1);
641 MODULE_VERSION(ipfw3_nat, 1);