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 VNET_DEFINE(eventhandler_tag, ifaddr_event_tag); 57 #define V_ifaddr_event_tag VNET(ifaddr_event_tag) 58 59 static void 60 ifaddr_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 */ 91 static void 92 flush_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 107 static void 108 del_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 142 static void 143 add_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 */ 212 static int 213 ipfw_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 uint16_t ip_len, cksum; 346 347 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_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 } 376 args->m = mcl; 377 return (IP_FW_NAT); 378 } 379 380 static struct cfg_nat * 381 lookup_nat(struct nat_list *l, int nat_id) 382 { 383 struct cfg_nat *res; 384 385 LIST_FOREACH(res, l, _next) { 386 if (res->id == nat_id) 387 break; 388 } 389 return res; 390 } 391 392 static int 393 ipfw_nat_cfg(struct sockopt *sopt) 394 { 395 struct cfg_nat *cfg, *ptr; 396 char *buf; 397 struct ip_fw_chain *chain = &V_layer3_chain; 398 size_t len; 399 int gencnt, error = 0; 400 401 len = sopt->sopt_valsize; 402 buf = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 403 if ((error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat))) != 0) 404 goto out; 405 406 cfg = (struct cfg_nat *)buf; 407 if (cfg->id < 0) { 408 error = EINVAL; 409 goto out; 410 } 411 412 /* 413 * Find/create nat rule. 414 */ 415 IPFW_WLOCK(chain); 416 gencnt = chain->gencnt; 417 ptr = lookup_nat(&chain->nat, cfg->id); 418 if (ptr == NULL) { 419 IPFW_WUNLOCK(chain); 420 /* New rule: allocate and init new instance. */ 421 ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); 422 ptr->lib = LibAliasInit(NULL); 423 LIST_INIT(&ptr->redir_chain); 424 } else { 425 /* Entry already present: temporarily unhook it. */ 426 LIST_REMOVE(ptr, _next); 427 flush_nat_ptrs(chain, cfg->id); 428 IPFW_WUNLOCK(chain); 429 } 430 431 /* 432 * Basic nat configuration. 433 */ 434 ptr->id = cfg->id; 435 /* 436 * XXX - what if this rule doesn't nat any ip and just 437 * redirect? 438 * do we set aliasaddress to 0.0.0.0? 439 */ 440 ptr->ip = cfg->ip; 441 ptr->redir_cnt = cfg->redir_cnt; 442 ptr->mode = cfg->mode; 443 LibAliasSetMode(ptr->lib, cfg->mode, cfg->mode); 444 LibAliasSetAddress(ptr->lib, ptr->ip); 445 memcpy(ptr->if_name, cfg->if_name, IF_NAMESIZE); 446 447 /* 448 * Redir and LSNAT configuration. 449 */ 450 /* Delete old cfgs. */ 451 del_redir_spool_cfg(ptr, &ptr->redir_chain); 452 /* Add new entries. */ 453 add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr); 454 455 IPFW_WLOCK(chain); 456 /* Extra check to avoid race with another ipfw_nat_cfg() */ 457 if (gencnt != chain->gencnt && 458 ((cfg = lookup_nat(&chain->nat, ptr->id)) != NULL)) 459 LIST_REMOVE(cfg, _next); 460 LIST_INSERT_HEAD(&chain->nat, ptr, _next); 461 chain->gencnt++; 462 IPFW_WUNLOCK(chain); 463 464 out: 465 free(buf, M_TEMP); 466 return (error); 467 } 468 469 static int 470 ipfw_nat_del(struct sockopt *sopt) 471 { 472 struct cfg_nat *ptr; 473 struct ip_fw_chain *chain = &V_layer3_chain; 474 int i; 475 476 sooptcopyin(sopt, &i, sizeof i, sizeof i); 477 /* XXX validate i */ 478 IPFW_WLOCK(chain); 479 ptr = lookup_nat(&chain->nat, i); 480 if (ptr == NULL) { 481 IPFW_WUNLOCK(chain); 482 return (EINVAL); 483 } 484 LIST_REMOVE(ptr, _next); 485 flush_nat_ptrs(chain, i); 486 IPFW_WUNLOCK(chain); 487 del_redir_spool_cfg(ptr, &ptr->redir_chain); 488 LibAliasUninit(ptr->lib); 489 free(ptr, M_IPFW); 490 return (0); 491 } 492 493 static int 494 ipfw_nat_get_cfg(struct sockopt *sopt) 495 { 496 struct ip_fw_chain *chain = &V_layer3_chain; 497 struct cfg_nat *n; 498 struct cfg_redir *r; 499 struct cfg_spool *s; 500 char *data; 501 int gencnt, nat_cnt, len, error; 502 503 nat_cnt = 0; 504 len = sizeof(nat_cnt); 505 506 IPFW_RLOCK(chain); 507 retry: 508 gencnt = chain->gencnt; 509 /* Estimate memory amount */ 510 LIST_FOREACH(n, &chain->nat, _next) { 511 nat_cnt++; 512 len += sizeof(struct cfg_nat); 513 LIST_FOREACH(r, &n->redir_chain, _next) { 514 len += sizeof(struct cfg_redir); 515 LIST_FOREACH(s, &r->spool_chain, _next) 516 len += sizeof(struct cfg_spool); 517 } 518 } 519 IPFW_RUNLOCK(chain); 520 521 data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 522 bcopy(&nat_cnt, data, sizeof(nat_cnt)); 523 524 nat_cnt = 0; 525 len = sizeof(nat_cnt); 526 527 IPFW_RLOCK(chain); 528 if (gencnt != chain->gencnt) { 529 free(data, M_TEMP); 530 goto retry; 531 } 532 /* Serialize all the data. */ 533 LIST_FOREACH(n, &chain->nat, _next) { 534 bcopy(n, &data[len], sizeof(struct cfg_nat)); 535 len += sizeof(struct cfg_nat); 536 LIST_FOREACH(r, &n->redir_chain, _next) { 537 bcopy(r, &data[len], sizeof(struct cfg_redir)); 538 len += sizeof(struct cfg_redir); 539 LIST_FOREACH(s, &r->spool_chain, _next) { 540 bcopy(s, &data[len], sizeof(struct cfg_spool)); 541 len += sizeof(struct cfg_spool); 542 } 543 } 544 } 545 IPFW_RUNLOCK(chain); 546 547 error = sooptcopyout(sopt, data, len); 548 free(data, M_TEMP); 549 550 return (error); 551 } 552 553 static int 554 ipfw_nat_get_log(struct sockopt *sopt) 555 { 556 uint8_t *data; 557 struct cfg_nat *ptr; 558 int i, size; 559 struct ip_fw_chain *chain; 560 561 chain = &V_layer3_chain; 562 563 IPFW_RLOCK(chain); 564 /* one pass to count, one to copy the data */ 565 i = 0; 566 LIST_FOREACH(ptr, &chain->nat, _next) { 567 if (ptr->lib->logDesc == NULL) 568 continue; 569 i++; 570 } 571 size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); 572 data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); 573 if (data == NULL) { 574 IPFW_RUNLOCK(chain); 575 return (ENOSPC); 576 } 577 i = 0; 578 LIST_FOREACH(ptr, &chain->nat, _next) { 579 if (ptr->lib->logDesc == NULL) 580 continue; 581 bcopy(&ptr->id, &data[i], sizeof(int)); 582 i += sizeof(int); 583 bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); 584 i += LIBALIAS_BUF_SIZE; 585 } 586 IPFW_RUNLOCK(chain); 587 sooptcopyout(sopt, data, size); 588 free(data, M_IPFW); 589 return(0); 590 } 591 592 static void 593 ipfw_nat_init(void) 594 { 595 596 IPFW_WLOCK(&V_layer3_chain); 597 /* init ipfw hooks */ 598 ipfw_nat_ptr = ipfw_nat; 599 lookup_nat_ptr = lookup_nat; 600 ipfw_nat_cfg_ptr = ipfw_nat_cfg; 601 ipfw_nat_del_ptr = ipfw_nat_del; 602 ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; 603 ipfw_nat_get_log_ptr = ipfw_nat_get_log; 604 IPFW_WUNLOCK(&V_layer3_chain); 605 V_ifaddr_event_tag = EVENTHANDLER_REGISTER( 606 ifaddr_event, ifaddr_change, 607 NULL, EVENTHANDLER_PRI_ANY); 608 } 609 610 static void 611 ipfw_nat_destroy(void) 612 { 613 struct cfg_nat *ptr, *ptr_temp; 614 struct ip_fw_chain *chain; 615 616 chain = &V_layer3_chain; 617 IPFW_WLOCK(chain); 618 LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { 619 LIST_REMOVE(ptr, _next); 620 del_redir_spool_cfg(ptr, &ptr->redir_chain); 621 LibAliasUninit(ptr->lib); 622 free(ptr, M_IPFW); 623 } 624 EVENTHANDLER_DEREGISTER(ifaddr_event, V_ifaddr_event_tag); 625 flush_nat_ptrs(chain, -1 /* flush all */); 626 /* deregister ipfw_nat */ 627 ipfw_nat_ptr = NULL; 628 lookup_nat_ptr = NULL; 629 ipfw_nat_cfg_ptr = NULL; 630 ipfw_nat_del_ptr = NULL; 631 ipfw_nat_get_cfg_ptr = NULL; 632 ipfw_nat_get_log_ptr = NULL; 633 IPFW_WUNLOCK(chain); 634 } 635 636 static int 637 ipfw_nat_modevent(module_t mod, int type, void *unused) 638 { 639 int err = 0; 640 641 switch (type) { 642 case MOD_LOAD: 643 ipfw_nat_init(); 644 break; 645 646 case MOD_UNLOAD: 647 ipfw_nat_destroy(); 648 break; 649 650 default: 651 return EOPNOTSUPP; 652 break; 653 } 654 return err; 655 } 656 657 static moduledata_t ipfw_nat_mod = { 658 "ipfw_nat", 659 ipfw_nat_modevent, 660 0 661 }; 662 663 DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); 664 MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); 665 MODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2); 666 MODULE_VERSION(ipfw_nat, 1); 667 /* end of file */ 668