]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/netinet/ip_fw_nat.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / netinet / ip_fw_nat.c
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/condvar.h>
33 #include <sys/eventhandler.h>
34 #include <sys/malloc.h>
35 #include <sys/mbuf.h>
36 #include <sys/kernel.h>
37 #include <sys/lock.h>
38 #include <sys/jail.h>
39 #include <sys/module.h>
40 #include <sys/priv.h>
41 #include <sys/proc.h>
42 #include <sys/rwlock.h>
43 #include <sys/socket.h>
44 #include <sys/socketvar.h>
45 #include <sys/sysctl.h>
46 #include <sys/syslog.h>
47 #include <sys/ucred.h>
48
49 #include <netinet/libalias/alias.h>
50 #include <netinet/libalias/alias_local.h>
51
52 #define IPFW_INTERNAL   /* Access to protected data structures in ip_fw.h. */
53
54 #include <net/if.h>
55 #include <netinet/in.h>
56 #include <netinet/ip.h>
57 #include <netinet/ip_var.h>
58 #include <netinet/ip_icmp.h>
59 #include <netinet/ip_fw.h>
60 #include <netinet/tcp.h>
61 #include <netinet/tcp_timer.h>
62 #include <netinet/tcp_var.h>
63 #include <netinet/tcpip.h>
64 #include <netinet/udp.h>
65 #include <netinet/udp_var.h>
66
67 #include <machine/in_cksum.h>   /* XXX for in_cksum */
68
69 MALLOC_DECLARE(M_IPFW);
70
71 extern struct ip_fw_chain layer3_chain;
72
73 static eventhandler_tag ifaddr_event_tag;
74
75 extern ipfw_nat_t *ipfw_nat_ptr;
76 extern ipfw_nat_cfg_t *ipfw_nat_cfg_ptr;
77 extern ipfw_nat_cfg_t *ipfw_nat_del_ptr;
78 extern ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr;
79 extern ipfw_nat_cfg_t *ipfw_nat_get_log_ptr;
80
81 static void 
82 ifaddr_change(void *arg __unused, struct ifnet *ifp)
83 {
84         struct cfg_nat *ptr;
85         struct ifaddr *ifa;
86
87         IPFW_WLOCK(&layer3_chain);                      
88         /* Check every nat entry... */
89         LIST_FOREACH(ptr, &layer3_chain.nat, _next) {
90                 /* ...using nic 'ifp->if_xname' as dynamic alias address. */
91                 if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) == 0) {
92                         mtx_lock(&ifp->if_addr_mtx);
93                         TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
94                                 if (ifa->ifa_addr == NULL)
95                                         continue;
96                                 if (ifa->ifa_addr->sa_family != AF_INET)
97                                         continue;
98                                 ptr->ip = ((struct sockaddr_in *) 
99                                     (ifa->ifa_addr))->sin_addr;
100                                 LibAliasSetAddress(ptr->lib, ptr->ip);
101                         }
102                         mtx_unlock(&ifp->if_addr_mtx);
103                 }
104         }
105         IPFW_WUNLOCK(&layer3_chain);    
106 }
107
108 static void
109 flush_nat_ptrs(const int i)
110 {
111         struct ip_fw *rule;
112
113         IPFW_WLOCK_ASSERT(&layer3_chain);
114         for (rule = layer3_chain.rules; rule; rule = rule->next) {
115                 ipfw_insn_nat *cmd = (ipfw_insn_nat *)ACTION_PTR(rule);
116                 if (cmd->o.opcode != O_NAT)
117                         continue;
118                 if (cmd->nat != NULL && cmd->nat->id == i)
119                         cmd->nat = NULL;
120         }
121 }
122
123 #define HOOK_NAT(b, p) do {                             \
124                 IPFW_WLOCK_ASSERT(&layer3_chain);       \
125                 LIST_INSERT_HEAD(b, p, _next);          \
126         } while (0)
127
128 #define UNHOOK_NAT(p) do {                              \
129                 IPFW_WLOCK_ASSERT(&layer3_chain);       \
130                 LIST_REMOVE(p, _next);                  \
131         } while (0)
132
133 #define HOOK_REDIR(b, p) do {                   \
134                 LIST_INSERT_HEAD(b, p, _next);  \
135         } while (0)
136
137 #define HOOK_SPOOL(b, p) do {                   \
138                 LIST_INSERT_HEAD(b, p, _next);  \
139         } while (0)
140
141 static void
142 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
143 {
144         struct cfg_redir *r, *tmp_r;
145         struct cfg_spool *s, *tmp_s;
146         int i, num;
147
148         LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
149                 num = 1; /* Number of alias_link to delete. */
150                 switch (r->mode) {
151                 case REDIR_PORT:
152                         num = r->pport_cnt;
153                         /* FALLTHROUGH */
154                 case REDIR_ADDR:
155                 case REDIR_PROTO:
156                         /* Delete all libalias redirect entry. */
157                         for (i = 0; i < num; i++)
158                                 LibAliasRedirectDelete(n->lib, r->alink[i]);
159                         /* Del spool cfg if any. */
160                         LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) {
161                                 LIST_REMOVE(s, _next);
162                                 free(s, M_IPFW);
163                         }
164                         free(r->alink, M_IPFW);
165                         LIST_REMOVE(r, _next);
166                         free(r, M_IPFW);
167                         break;
168                 default:
169                         printf("unknown redirect mode: %u\n", r->mode);                         
170                         /* XXX - panic?!?!? */
171                         break; 
172                 }
173         }
174 }
175
176 static int
177 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
178 {
179         struct cfg_redir *r, *ser_r;
180         struct cfg_spool *s, *ser_s;
181         int cnt, off, i;
182         char *panic_err;
183
184         for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
185                 ser_r = (struct cfg_redir *)&buf[off];
186                 r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO);
187                 memcpy(r, ser_r, SOF_REDIR);
188                 LIST_INIT(&r->spool_chain);
189                 off += SOF_REDIR;
190                 r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
191                     M_IPFW, M_WAITOK | M_ZERO);
192                 switch (r->mode) {
193                 case REDIR_ADDR:
194                         r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
195                             r->paddr);
196                         break;
197                 case REDIR_PORT:
198                         for (i = 0 ; i < r->pport_cnt; i++) {
199                                 /* If remotePort is all ports, set it to 0. */
200                                 u_short remotePortCopy = r->rport + i;
201                                 if (r->rport_cnt == 1 && r->rport == 0)
202                                         remotePortCopy = 0;
203                                 r->alink[i] = LibAliasRedirectPort(ptr->lib,
204                                     r->laddr, htons(r->lport + i), r->raddr,
205                                     htons(remotePortCopy), r->paddr, 
206                                     htons(r->pport + i), r->proto);
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 ,r->laddr,
215                             r->raddr, r->paddr, r->proto);
216                         break;
217                 default:
218                         printf("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 = malloc(SOF_REDIR, M_IPFW, 
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(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
247 {
248         struct mbuf *mcl;
249         struct ip *ip;
250         /* XXX - libalias duct tape */
251         int ldt, retval;
252         char *c;
253
254         ldt = 0;
255         retval = 0;
256         if ((mcl = m_megapullup(m, m->m_pkthdr.len)) ==
257             NULL)
258                 goto badnat;
259         ip = mtod(mcl, struct ip *);
260         if (args->eh == NULL) {
261                 ip->ip_len = htons(ip->ip_len);
262                 ip->ip_off = htons(ip->ip_off);
263         }
264
265         /* 
266          * XXX - Libalias checksum offload 'duct tape':
267          * 
268          * locally generated packets have only
269          * pseudo-header checksum calculated
270          * and libalias will screw it[1], so
271          * mark them for later fix.  Moreover
272          * there are cases when libalias
273          * modify tcp packet data[2], mark it
274          * for later fix too.
275          *
276          * [1] libalias was never meant to run
277          * in kernel, so it doesn't have any
278          * knowledge about checksum
279          * offloading, and it expects a packet
280          * with a full internet
281          * checksum. Unfortunately, packets
282          * generated locally will have just the
283          * pseudo header calculated, and when
284          * libalias tries to adjust the
285          * checksum it will actually screw it.
286          *
287          * [2] when libalias modify tcp's data
288          * content, full TCP checksum has to
289          * be recomputed: the problem is that
290          * libalias doesn't have any idea
291          * about checksum offloading To
292          * workaround this, we do not do
293          * checksumming in LibAlias, but only
294          * mark the packets in th_x2 field. If
295          * we receive a marked packet, we
296          * calculate correct checksum for it
297          * aware of offloading.  Why such a
298          * terrible hack instead of
299          * recalculating checksum for each
300          * packet?  Because the previous
301          * checksum was not checked!
302          * Recalculating checksums for EVERY
303          * packet will hide ALL transmission
304          * errors. Yes, marked packets still
305          * suffer from this problem. But,
306          * sigh, natd(8) has this problem,
307          * too.
308          *
309          * TODO: -make libalias mbuf aware (so
310          * it can handle delayed checksum and tso)
311          */
312
313         if (mcl->m_pkthdr.rcvif == NULL && 
314             mcl->m_pkthdr.csum_flags & 
315             CSUM_DELAY_DATA)
316                 ldt = 1;
317
318         c = mtod(mcl, char *);
319         if (args->oif == NULL)
320                 retval = LibAliasIn(t->lib, c, 
321                         mcl->m_len + M_TRAILINGSPACE(mcl));
322         else
323                 retval = LibAliasOut(t->lib, c, 
324                         mcl->m_len + M_TRAILINGSPACE(mcl));
325         if (retval != PKT_ALIAS_OK &&
326             retval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
327                 /* XXX - should i add some logging? */
328                 m_free(mcl);
329         badnat:
330                 args->m = NULL;
331                 return (IP_FW_DENY);
332         }
333         mcl->m_pkthdr.len = mcl->m_len = 
334             ntohs(ip->ip_len);
335
336         /* 
337          * XXX - libalias checksum offload 
338          * 'duct tape' (see above) 
339          */
340
341         if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 
342             ip->ip_p == IPPROTO_TCP) {
343                 struct tcphdr   *th; 
344
345                 th = (struct tcphdr *)(ip + 1);
346                 if (th->th_x2) 
347                         ldt = 1;
348         }
349
350         if (ldt) {
351                 struct tcphdr   *th;
352                 struct udphdr   *uh;
353                 u_short cksum;
354
355                 ip->ip_len = ntohs(ip->ip_len);
356                 cksum = in_pseudo(
357                     ip->ip_src.s_addr,
358                     ip->ip_dst.s_addr, 
359                     htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2))
360                 );
361                                         
362                 switch (ip->ip_p) {
363                 case IPPROTO_TCP:
364                         th = (struct tcphdr *)(ip + 1);
365                         /* 
366                          * Maybe it was set in 
367                          * libalias... 
368                          */
369                         th->th_x2 = 0;
370                         th->th_sum = cksum;
371                         mcl->m_pkthdr.csum_data = 
372                             offsetof(struct tcphdr, th_sum);
373                         break;
374                 case IPPROTO_UDP:
375                         uh = (struct udphdr *)(ip + 1);
376                         uh->uh_sum = cksum;
377                         mcl->m_pkthdr.csum_data = 
378                             offsetof(struct udphdr, uh_sum);
379                         break;                                          
380                 }
381                 /* 
382                  * No hw checksum offloading: do it 
383                  * by ourself. 
384                  */
385                 if ((mcl->m_pkthdr.csum_flags & 
386                      CSUM_DELAY_DATA) == 0) {
387                         in_delayed_cksum(mcl);
388                         mcl->m_pkthdr.csum_flags &= 
389                             ~CSUM_DELAY_DATA;
390                 }
391                 ip->ip_len = htons(ip->ip_len);
392         }
393
394         if (args->eh == NULL) {
395                 ip->ip_len = ntohs(ip->ip_len);
396                 ip->ip_off = ntohs(ip->ip_off);
397         }
398
399         args->m = mcl;
400         return (IP_FW_NAT);
401 }
402
403 static int 
404 ipfw_nat_cfg(struct sockopt *sopt)
405 {
406         struct cfg_nat *ptr, *ser_n;
407         char *buf;
408
409         buf = malloc(NAT_BUF_LEN, M_IPFW, M_WAITOK | M_ZERO);
410         sooptcopyin(sopt, buf, NAT_BUF_LEN, 
411             sizeof(struct cfg_nat));
412         ser_n = (struct cfg_nat *)buf;
413
414         /* 
415          * Find/create nat rule.
416          */
417         IPFW_WLOCK(&layer3_chain);
418         LOOKUP_NAT(layer3_chain, ser_n->id, ptr);
419         if (ptr == NULL) {
420                 /* New rule: allocate and init new instance. */
421                 ptr = malloc(sizeof(struct cfg_nat), 
422                     M_IPFW, M_NOWAIT | M_ZERO);
423                 if (ptr == NULL) {
424                         IPFW_WUNLOCK(&layer3_chain);                            
425                         free(buf, M_IPFW);
426                         return (ENOSPC);
427                 }
428                 ptr->lib = LibAliasInit(NULL);
429                 if (ptr->lib == NULL) {
430                         IPFW_WUNLOCK(&layer3_chain);
431                         free(ptr, M_IPFW);
432                         free(buf, M_IPFW);
433                         return (EINVAL);
434                 }
435                 LIST_INIT(&ptr->redir_chain);
436         } else {
437                 /* Entry already present: temporarly unhook it. */
438                 UNHOOK_NAT(ptr);
439                 flush_nat_ptrs(ser_n->id);
440         }
441         IPFW_WUNLOCK(&layer3_chain);
442
443         /* 
444          * Basic nat configuration.
445          */
446         ptr->id = ser_n->id;
447         /* 
448          * XXX - what if this rule doesn't nat any ip and just 
449          * redirect? 
450          * do we set aliasaddress to 0.0.0.0?
451          */
452         ptr->ip = ser_n->ip;
453         ptr->redir_cnt = ser_n->redir_cnt;
454         ptr->mode = ser_n->mode;
455         LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode);
456         LibAliasSetAddress(ptr->lib, ptr->ip);
457         memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE);
458
459         /* 
460          * Redir and LSNAT configuration.
461          */
462         /* Delete old cfgs. */
463         del_redir_spool_cfg(ptr, &ptr->redir_chain);
464         /* Add new entries. */
465         add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr);
466         free(buf, M_IPFW);
467         IPFW_WLOCK(&layer3_chain);
468         HOOK_NAT(&layer3_chain.nat, ptr);
469         IPFW_WUNLOCK(&layer3_chain);
470         return (0);
471 }
472
473 static int
474 ipfw_nat_del(struct sockopt *sopt)
475 {
476         struct cfg_nat *ptr;
477         int i;
478                 
479         sooptcopyin(sopt, &i, sizeof i, sizeof i);
480         IPFW_WLOCK(&layer3_chain);
481         LOOKUP_NAT(layer3_chain, i, ptr);
482         if (ptr == NULL) {
483                 IPFW_WUNLOCK(&layer3_chain);
484                 return (EINVAL);
485         }
486         UNHOOK_NAT(ptr);
487         flush_nat_ptrs(i);
488         IPFW_WUNLOCK(&layer3_chain);
489         del_redir_spool_cfg(ptr, &ptr->redir_chain);
490         LibAliasUninit(ptr->lib);
491         free(ptr, M_IPFW);
492         return (0);
493 }
494
495 static int
496 ipfw_nat_get_cfg(struct sockopt *sopt)
497 {       
498         uint8_t *data;
499         struct cfg_nat *n;
500         struct cfg_redir *r;
501         struct cfg_spool *s;
502         int nat_cnt, off;
503                 
504         nat_cnt = 0;
505         off = sizeof(nat_cnt);
506
507         data = malloc(NAT_BUF_LEN, M_IPFW, M_WAITOK | M_ZERO);
508         IPFW_RLOCK(&layer3_chain);
509         /* Serialize all the data. */
510         LIST_FOREACH(n, &layer3_chain.nat, _next) {
511                 nat_cnt++;
512                 if (off + SOF_NAT < NAT_BUF_LEN) {
513                         bcopy(n, &data[off], SOF_NAT);
514                         off += SOF_NAT;
515                         LIST_FOREACH(r, &n->redir_chain, _next) {
516                                 if (off + SOF_REDIR < NAT_BUF_LEN) {
517                                         bcopy(r, &data[off], 
518                                             SOF_REDIR);
519                                         off += SOF_REDIR;
520                                         LIST_FOREACH(s, &r->spool_chain, 
521                                             _next) {
522                                                 if (off + SOF_SPOOL < 
523                                                     NAT_BUF_LEN) {
524                                                         bcopy(s, &data[off],
525                                                             SOF_SPOOL);
526                                                         off += SOF_SPOOL;
527                                                 } else
528                                                         goto nospace;
529                                         }
530                                 } else
531                                         goto nospace;
532                         }
533                 } else
534                         goto nospace;
535         }
536         bcopy(&nat_cnt, data, sizeof(nat_cnt));
537         IPFW_RUNLOCK(&layer3_chain);
538         sooptcopyout(sopt, data, NAT_BUF_LEN);
539         free(data, M_IPFW);
540         return (0);
541 nospace:
542         IPFW_RUNLOCK(&layer3_chain);
543         printf("serialized data buffer not big enough:"
544             "please increase NAT_BUF_LEN\n");
545         free(data, M_IPFW);
546         return (ENOSPC);
547 }
548
549 static int
550 ipfw_nat_get_log(struct sockopt *sopt)
551 {
552         uint8_t *data;
553         struct cfg_nat *ptr;
554         int i, size, cnt, sof;
555
556         data = NULL;
557         sof = LIBALIAS_BUF_SIZE;
558         cnt = 0;
559
560         IPFW_RLOCK(&layer3_chain);
561         size = i = 0;
562         LIST_FOREACH(ptr, &layer3_chain.nat, _next) {
563                 if (ptr->lib->logDesc == NULL) 
564                         continue;
565                 cnt++;
566                 size = cnt * (sof + sizeof(int));
567                 data = realloc(data, size, M_IPFW, M_NOWAIT | M_ZERO);
568                 if (data == NULL) {
569                         IPFW_RUNLOCK(&layer3_chain);
570                         return (ENOSPC);
571                 }
572                 bcopy(&ptr->id, &data[i], sizeof(int));
573                 i += sizeof(int);
574                 bcopy(ptr->lib->logDesc, &data[i], sof);
575                 i += sof;
576         }
577         IPFW_RUNLOCK(&layer3_chain);
578         sooptcopyout(sopt, data, size);
579         free(data, M_IPFW);
580         return(0);
581 }
582
583 static void
584 ipfw_nat_init(void)
585 {
586
587         IPFW_WLOCK(&layer3_chain);
588         /* init ipfw hooks */
589         ipfw_nat_ptr = ipfw_nat;
590         ipfw_nat_cfg_ptr = ipfw_nat_cfg;
591         ipfw_nat_del_ptr = ipfw_nat_del;
592         ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
593         ipfw_nat_get_log_ptr = ipfw_nat_get_log;
594         IPFW_WUNLOCK(&layer3_chain);
595         ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change, 
596             NULL, EVENTHANDLER_PRI_ANY);
597 }
598
599 static void
600 ipfw_nat_destroy(void)
601 {
602         struct ip_fw *rule;
603         struct cfg_nat *ptr, *ptr_temp;
604         
605         IPFW_WLOCK(&layer3_chain);
606         LIST_FOREACH_SAFE(ptr, &layer3_chain.nat, _next, ptr_temp) {
607                 LIST_REMOVE(ptr, _next);
608                 del_redir_spool_cfg(ptr, &ptr->redir_chain);
609                 LibAliasUninit(ptr->lib);
610                 free(ptr, M_IPFW);
611         }
612         EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
613         /* flush all nat ptrs */
614         for (rule = layer3_chain.rules; rule; rule = rule->next) {
615                 ipfw_insn_nat *cmd = (ipfw_insn_nat *)ACTION_PTR(rule);
616                 if (cmd->o.opcode == O_NAT)
617                         cmd->nat = NULL;
618         }
619         /* deregister ipfw_nat */
620         ipfw_nat_ptr = NULL;
621         IPFW_WUNLOCK(&layer3_chain);
622 }
623
624 static int
625 ipfw_nat_modevent(module_t mod, int type, void *unused)
626 {
627         int err = 0;
628
629         switch (type) {
630         case MOD_LOAD:
631                 ipfw_nat_init();
632                 break;
633
634         case MOD_UNLOAD:
635                 ipfw_nat_destroy();
636                 break;
637
638         default:
639                 return EOPNOTSUPP;
640                 break;
641         }
642         return err;
643 }
644
645 static moduledata_t ipfw_nat_mod = {
646         "ipfw_nat",
647         ipfw_nat_modevent,
648         0
649 };
650
651 DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
652 MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1);
653 MODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2);
654 MODULE_VERSION(ipfw_nat, 1);