ip_fw_nat.c revision 244569
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: stable/9/sys/netpfil/ipfw/ip_fw_nat.c 244569 2012-12-21 22:48:12Z melifaro $"); 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 56static VNET_DEFINE(eventhandler_tag, ifaddr_event_tag); 57#define V_ifaddr_event_tag VNET(ifaddr_event_tag) 58 59static void 60ifaddr_change(void *arg __unused, struct ifnet *ifp) 61{ 62 struct cfg_nat *ptr; 63 struct ifaddr *ifa; 64 struct ip_fw_chain *chain; 65 66 chain = &V_layer3_chain; 67 IPFW_WLOCK(chain); 68 /* Check every nat entry... */ 69 LIST_FOREACH(ptr, &chain->nat, _next) { 70 /* ...using nic 'ifp->if_xname' as dynamic alias address. */ 71 if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) 72 continue; 73 if_addr_rlock(ifp); 74 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 75 if (ifa->ifa_addr == NULL) 76 continue; 77 if (ifa->ifa_addr->sa_family != AF_INET) 78 continue; 79 ptr->ip = ((struct sockaddr_in *) 80 (ifa->ifa_addr))->sin_addr; 81 LibAliasSetAddress(ptr->lib, ptr->ip); 82 } 83 if_addr_runlock(ifp); 84 } 85 IPFW_WUNLOCK(chain); 86} 87 88/* 89 * delete the pointers for nat entry ix, or all of them if ix < 0 90 */ 91static void 92flush_nat_ptrs(struct ip_fw_chain *chain, const int ix) 93{ 94 int i; 95 ipfw_insn_nat *cmd; 96 97 IPFW_WLOCK_ASSERT(chain); 98 for (i = 0; i < chain->n_rules; i++) { 99 cmd = (ipfw_insn_nat *)ACTION_PTR(chain->map[i]); 100 /* XXX skip log and the like ? */ 101 if (cmd->o.opcode == O_NAT && cmd->nat != NULL && 102 (ix < 0 || cmd->nat->id == ix)) 103 cmd->nat = NULL; 104 } 105} 106 107static void 108del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) 109{ 110 struct cfg_redir *r, *tmp_r; 111 struct cfg_spool *s, *tmp_s; 112 int i, num; 113 114 LIST_FOREACH_SAFE(r, head, _next, tmp_r) { 115 num = 1; /* Number of alias_link to delete. */ 116 switch (r->mode) { 117 case REDIR_PORT: 118 num = r->pport_cnt; 119 /* FALLTHROUGH */ 120 case REDIR_ADDR: 121 case REDIR_PROTO: 122 /* Delete all libalias redirect entry. */ 123 for (i = 0; i < num; i++) 124 LibAliasRedirectDelete(n->lib, r->alink[i]); 125 /* Del spool cfg if any. */ 126 LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) { 127 LIST_REMOVE(s, _next); 128 free(s, M_IPFW); 129 } 130 free(r->alink, M_IPFW); 131 LIST_REMOVE(r, _next); 132 free(r, M_IPFW); 133 break; 134 default: 135 printf("unknown redirect mode: %u\n", r->mode); 136 /* XXX - panic?!?!? */ 137 break; 138 } 139 } 140} 141 142static void 143add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) 144{ 145 struct cfg_redir *r, *ser_r; 146 struct cfg_spool *s, *ser_s; 147 int cnt, off, i; 148 149 for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { 150 ser_r = (struct cfg_redir *)&buf[off]; 151 r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); 152 memcpy(r, ser_r, SOF_REDIR); 153 LIST_INIT(&r->spool_chain); 154 off += SOF_REDIR; 155 r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, 156 M_IPFW, M_WAITOK | M_ZERO); 157 switch (r->mode) { 158 case REDIR_ADDR: 159 r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, 160 r->paddr); 161 break; 162 case REDIR_PORT: 163 for (i = 0 ; i < r->pport_cnt; i++) { 164 /* If remotePort is all ports, set it to 0. */ 165 u_short remotePortCopy = r->rport + i; 166 if (r->rport_cnt == 1 && r->rport == 0) 167 remotePortCopy = 0; 168 r->alink[i] = LibAliasRedirectPort(ptr->lib, 169 r->laddr, htons(r->lport + i), r->raddr, 170 htons(remotePortCopy), r->paddr, 171 htons(r->pport + i), r->proto); 172 if (r->alink[i] == NULL) { 173 r->alink[0] = NULL; 174 break; 175 } 176 } 177 break; 178 case REDIR_PROTO: 179 r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, 180 r->raddr, r->paddr, r->proto); 181 break; 182 default: 183 printf("unknown redirect mode: %u\n", r->mode); 184 break; 185 } 186 /* XXX perhaps return an error instead of panic ? */ 187 if (r->alink[0] == NULL) 188 panic("LibAliasRedirect* returned NULL"); 189 /* LSNAT handling. */ 190 for (i = 0; i < r->spool_cnt; i++) { 191 ser_s = (struct cfg_spool *)&buf[off]; 192 s = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); 193 memcpy(s, ser_s, SOF_SPOOL); 194 LibAliasAddServer(ptr->lib, r->alink[0], 195 s->addr, htons(s->port)); 196 off += SOF_SPOOL; 197 /* Hook spool entry. */ 198 LIST_INSERT_HEAD(&r->spool_chain, s, _next); 199 } 200 /* And finally hook this redir entry. */ 201 LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); 202 } 203} 204 205/* 206 * ipfw_nat - perform mbuf header translation. 207 * 208 * Note V_layer3_chain has to be locked while calling ipfw_nat() in 209 * 'global' operation mode (t == NULL). 210 * 211 */ 212static int 213ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) 214{ 215 struct mbuf *mcl; 216 struct ip *ip; 217 /* XXX - libalias duct tape */ 218 int ldt, retval, found; 219 struct ip_fw_chain *chain; 220 char *c; 221 222 ldt = 0; 223 retval = 0; 224 mcl = m_megapullup(m, m->m_pkthdr.len); 225 if (mcl == NULL) { 226 args->m = NULL; 227 return (IP_FW_DENY); 228 } 229 ip = mtod(mcl, struct ip *); 230 231 /* 232 * XXX - Libalias checksum offload 'duct tape': 233 * 234 * locally generated packets have only pseudo-header checksum 235 * calculated and libalias will break it[1], so mark them for 236 * later fix. Moreover there are cases when libalias modifies 237 * tcp packet data[2], mark them for later fix too. 238 * 239 * [1] libalias was never meant to run in kernel, so it does 240 * not have any knowledge about checksum offloading, and 241 * expects a packet with a full internet checksum. 242 * Unfortunately, packets generated locally will have just the 243 * pseudo header calculated, and when libalias tries to adjust 244 * the checksum it will actually compute a wrong value. 245 * 246 * [2] when libalias modifies tcp's data content, full TCP 247 * checksum has to be recomputed: the problem is that 248 * libalias does not have any idea about checksum offloading. 249 * To work around this, we do not do checksumming in LibAlias, 250 * but only mark the packets in th_x2 field. If we receive a 251 * marked packet, we calculate correct checksum for it 252 * aware of offloading. Why such a terrible hack instead of 253 * recalculating checksum for each packet? 254 * Because the previous checksum was not checked! 255 * Recalculating checksums for EVERY packet will hide ALL 256 * transmission errors. Yes, marked packets still suffer from 257 * this problem. But, sigh, natd(8) has this problem, too. 258 * 259 * TODO: -make libalias mbuf aware (so 260 * it can handle delayed checksum and tso) 261 */ 262 263 if (mcl->m_pkthdr.rcvif == NULL && 264 mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) 265 ldt = 1; 266 267 c = mtod(mcl, char *); 268 269 /* Check if this is 'global' instance */ 270 if (t == NULL) { 271 if (args->oif == NULL) { 272 /* Wrong direction, skip processing */ 273 args->m = mcl; 274 return (IP_FW_NAT); 275 } 276 277 found = 0; 278 chain = &V_layer3_chain; 279 IPFW_RLOCK_ASSERT(chain); 280 /* Check every nat entry... */ 281 LIST_FOREACH(t, &chain->nat, _next) { 282 if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) 283 continue; 284 retval = LibAliasOutTry(t->lib, c, 285 mcl->m_len + M_TRAILINGSPACE(mcl), 0); 286 if (retval == PKT_ALIAS_OK) { 287 /* Nat instance recognises state */ 288 found = 1; 289 break; 290 } 291 } 292 if (found != 1) { 293 /* No instance found, return ignore */ 294 args->m = mcl; 295 return (IP_FW_NAT); 296 } 297 } else { 298 if (args->oif == NULL) 299 retval = LibAliasIn(t->lib, c, 300 mcl->m_len + M_TRAILINGSPACE(mcl)); 301 else 302 retval = LibAliasOut(t->lib, c, 303 mcl->m_len + M_TRAILINGSPACE(mcl)); 304 } 305 306 /* 307 * We drop packet when: 308 * 1. libalias returns PKT_ALIAS_ERROR; 309 * 2. For incoming packets: 310 * a) for unresolved fragments; 311 * b) libalias returns PKT_ALIAS_IGNORED and 312 * PKT_ALIAS_DENY_INCOMING flag is set. 313 */ 314 if (retval == PKT_ALIAS_ERROR || 315 (args->oif == NULL && (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT || 316 (retval == PKT_ALIAS_IGNORED && 317 (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) { 318 /* XXX - should i add some logging? */ 319 m_free(mcl); 320 args->m = NULL; 321 return (IP_FW_DENY); 322 } 323 324 if (retval == PKT_ALIAS_RESPOND) 325 mcl->m_flags |= M_SKIP_FIREWALL; 326 mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); 327 328 /* 329 * XXX - libalias checksum offload 330 * 'duct tape' (see above) 331 */ 332 333 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 334 ip->ip_p == IPPROTO_TCP) { 335 struct tcphdr *th; 336 337 th = (struct tcphdr *)(ip + 1); 338 if (th->th_x2) 339 ldt = 1; 340 } 341 342 if (ldt) { 343 struct tcphdr *th; 344 struct udphdr *uh; 345 u_short cksum; 346 347 ip->ip_len = ntohs(ip->ip_len); 348 cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 349 htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2))); 350 351 switch (ip->ip_p) { 352 case IPPROTO_TCP: 353 th = (struct tcphdr *)(ip + 1); 354 /* 355 * Maybe it was set in 356 * libalias... 357 */ 358 th->th_x2 = 0; 359 th->th_sum = cksum; 360 mcl->m_pkthdr.csum_data = 361 offsetof(struct tcphdr, th_sum); 362 break; 363 case IPPROTO_UDP: 364 uh = (struct udphdr *)(ip + 1); 365 uh->uh_sum = cksum; 366 mcl->m_pkthdr.csum_data = 367 offsetof(struct udphdr, uh_sum); 368 break; 369 } 370 /* No hw checksum offloading: do it ourselves */ 371 if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { 372 in_delayed_cksum(mcl); 373 mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 374 } 375 ip->ip_len = htons(ip->ip_len); 376 } 377 args->m = mcl; 378 return (IP_FW_NAT); 379} 380 381static struct cfg_nat * 382lookup_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 393static int 394ipfw_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 465out: 466 free(buf, M_TEMP); 467 return (error); 468} 469 470static int 471ipfw_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 494static int 495ipfw_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); 508retry: 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 554static int 555ipfw_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 593static void 594ipfw_nat_init(void) 595{ 596 597 IPFW_WLOCK(&V_layer3_chain); 598 /* init ipfw hooks */ 599 ipfw_nat_ptr = ipfw_nat; 600 lookup_nat_ptr = lookup_nat; 601 ipfw_nat_cfg_ptr = ipfw_nat_cfg; 602 ipfw_nat_del_ptr = ipfw_nat_del; 603 ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; 604 ipfw_nat_get_log_ptr = ipfw_nat_get_log; 605 IPFW_WUNLOCK(&V_layer3_chain); 606 V_ifaddr_event_tag = EVENTHANDLER_REGISTER( 607 ifaddr_event, ifaddr_change, 608 NULL, EVENTHANDLER_PRI_ANY); 609} 610 611static void 612ipfw_nat_destroy(void) 613{ 614 struct cfg_nat *ptr, *ptr_temp; 615 struct ip_fw_chain *chain; 616 617 chain = &V_layer3_chain; 618 IPFW_WLOCK(chain); 619 LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { 620 LIST_REMOVE(ptr, _next); 621 del_redir_spool_cfg(ptr, &ptr->redir_chain); 622 LibAliasUninit(ptr->lib); 623 free(ptr, M_IPFW); 624 } 625 EVENTHANDLER_DEREGISTER(ifaddr_event, V_ifaddr_event_tag); 626 flush_nat_ptrs(chain, -1 /* flush all */); 627 /* deregister ipfw_nat */ 628 ipfw_nat_ptr = NULL; 629 lookup_nat_ptr = NULL; 630 ipfw_nat_cfg_ptr = NULL; 631 ipfw_nat_del_ptr = NULL; 632 ipfw_nat_get_cfg_ptr = NULL; 633 ipfw_nat_get_log_ptr = NULL; 634 IPFW_WUNLOCK(chain); 635} 636 637static int 638ipfw_nat_modevent(module_t mod, int type, void *unused) 639{ 640 int err = 0; 641 642 switch (type) { 643 case MOD_LOAD: 644 ipfw_nat_init(); 645 break; 646 647 case MOD_UNLOAD: 648 ipfw_nat_destroy(); 649 break; 650 651 default: 652 return EOPNOTSUPP; 653 break; 654 } 655 return err; 656} 657 658static moduledata_t ipfw_nat_mod = { 659 "ipfw_nat", 660 ipfw_nat_modevent, 661 0 662}; 663 664DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); 665MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); 666MODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2); 667MODULE_VERSION(ipfw_nat, 1); 668/* end of file */ 669