1 /* 2 * Copyright (c) 2014 - 2016 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Bill Yuan <bycn82@dragonflybsd.org> 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 struct ipfw_nat_context *ipfw_nat_ctx[MAXCPU]; 82 extern struct ipfw_context *ipfw_ctx[MAXCPU]; 83 extern ip_fw_ctl_t *ipfw_ctl_nat_ptr; 84 85 void check_nat(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args, 86 struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len); 87 void add_alias_link_dispatch(netmsg_t nat_del_msg); 88 int ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m); 89 void nat_add_dispatch(netmsg_t msg); 90 int ipfw_ctl_nat_add(struct sockopt *sopt); 91 void nat_del_dispatch(netmsg_t msg); 92 int ipfw_ctl_nat_del(struct sockopt *sopt); 93 int ipfw_ctl_nat_flush(struct sockopt *sopt); 94 int ipfw_ctl_nat_sockopt(struct sockopt *sopt); 95 void nat_init_ctx_dispatch(netmsg_t msg); 96 int ipfw_ctl_nat_get_cfg(struct sockopt *sopt); 97 void del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head); 98 int add_redir_spool_cfg(char *buf, struct cfg_nat *ptr); 99 int ipfw_ctl_nat_get_record(struct sockopt *sopt); 100 101 102 void 103 check_nat(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args, 104 struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len) 105 { 106 if ((*args)->eh != NULL) { 107 *cmd_ctl = IP_FW_CTL_NO; 108 *cmd_val = IP_FW_NOT_MATCH; 109 return; 110 } 111 112 struct ipfw_nat_context *nat_ctx; 113 struct cfg_nat *t; 114 int nat_id; 115 116 nat_ctx = ipfw_nat_ctx[mycpuid]; 117 (*args)->rule = *f; 118 t = ((ipfw_insn_nat *)cmd)->nat; 119 if (t == NULL) { 120 nat_id = cmd->arg1; 121 LOOKUP_NAT((*nat_ctx), nat_id, t); 122 if (t == NULL) { 123 *cmd_val = IP_FW_DENY; 124 *cmd_ctl = IP_FW_CTL_DONE; 125 return; 126 } 127 ((ipfw_insn_nat *)cmd)->nat = t; 128 } 129 *cmd_val = ipfw_nat(*args, t, (*args)->m); 130 *cmd_ctl = IP_FW_CTL_NAT; 131 } 132 133 /* Local prototypes */ 134 u_int StartPointIn(struct in_addr, u_short, int); 135 136 u_int StartPointOut(struct in_addr, struct in_addr, 137 u_short, u_short, int); 138 139 u_int 140 StartPointIn(struct in_addr alias_addr, 141 u_short alias_port, 142 int link_type) 143 { 144 u_int n; 145 146 n = alias_addr.s_addr; 147 if (link_type != LINK_PPTP) 148 n += alias_port; 149 n += link_type; 150 return (n % LINK_TABLE_IN_SIZE); 151 } 152 153 154 u_int 155 StartPointOut(struct in_addr src_addr, struct in_addr dst_addr, 156 u_short src_port, u_short dst_port, int link_type) 157 { 158 u_int n; 159 160 n = src_addr.s_addr; 161 n += dst_addr.s_addr; 162 if (link_type != LINK_PPTP) { 163 n += src_port; 164 n += dst_port; 165 } 166 n += link_type; 167 168 return (n % LINK_TABLE_OUT_SIZE); 169 } 170 171 172 void 173 add_alias_link_dispatch(netmsg_t alias_link_add) 174 { 175 struct ipfw_nat_context *nat_ctx; 176 struct netmsg_alias_link_add *msg; 177 struct libalias *la; 178 struct alias_link *lnk; 179 struct cfg_nat *t; 180 struct tcp_dat *aux_tcp; 181 u_int start_point; 182 183 msg = (struct netmsg_alias_link_add *)alias_link_add; 184 nat_ctx = ipfw_nat_ctx[mycpuid]; 185 LOOKUP_NAT((*nat_ctx), msg->id, t); 186 la = t->lib; 187 lnk = kmalloc(sizeof(struct alias_link), M_ALIAS, M_WAITOK | M_ZERO); 188 memcpy(lnk, msg->lnk, sizeof(struct alias_link)); 189 lnk->la = la; 190 if (msg->is_tcp) { 191 aux_tcp = kmalloc(sizeof(struct tcp_dat), 192 M_ALIAS, M_WAITOK | M_ZERO); 193 memcpy(aux_tcp, msg->lnk->data.tcp, sizeof(struct tcp_dat)); 194 lnk->data.tcp = aux_tcp; 195 } 196 197 /* Set up pointers for output lookup table */ 198 start_point = StartPointOut(lnk->src_addr, lnk->dst_addr, 199 lnk->src_port, lnk->dst_port, lnk->link_type); 200 LIST_INSERT_HEAD(&la->linkTableOut[start_point], lnk, list_out); 201 202 /* Set up pointers for input lookup table */ 203 start_point = StartPointIn(lnk->alias_addr, 204 lnk->alias_port, lnk->link_type); 205 LIST_INSERT_HEAD(&la->linkTableIn[start_point], lnk, list_in); 206 kfree(alias_link_add, M_LWKTMSG); 207 } 208 209 int 210 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) 211 { 212 struct alias_link *new = NULL; 213 struct mbuf *mcl; 214 struct ip *ip; 215 int ldt, retval, nextcpu; 216 char *c; 217 218 ldt = 0; 219 retval = 0; 220 if ((mcl = m_megapullup(m, m->m_pkthdr.len)) ==NULL) 221 goto badnat; 222 223 ip = mtod(mcl, struct ip *); 224 if (args->eh == NULL) { 225 ip->ip_len = htons(ip->ip_len); 226 ip->ip_off = htons(ip->ip_off); 227 } 228 229 if (mcl->m_pkthdr.rcvif == NULL && 230 mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { 231 ldt = 1; 232 } 233 234 c = mtod(mcl, char *); 235 if (args->oif == NULL) { 236 retval = LibAliasIn(t->lib, c, 237 mcl->m_len + M_TRAILINGSPACE(mcl), &new); 238 } else { 239 retval = LibAliasOut(t->lib, c, 240 mcl->m_len + M_TRAILINGSPACE(mcl), &new); 241 } 242 if (retval != PKT_ALIAS_OK && 243 retval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) { 244 /* XXX - should i add some logging? */ 245 m_free(mcl); 246 badnat: 247 args->m = NULL; 248 return IP_FW_DENY; 249 } 250 mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); 251 252 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 253 ip->ip_p == IPPROTO_TCP) { 254 struct tcphdr *th; 255 256 th = (struct tcphdr *)(ip + 1); 257 if (th->th_x2){ 258 ldt = 1; 259 } 260 } 261 if (new != NULL && 262 (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP)) { 263 ip_hashfn(&mcl, 0); 264 nextcpu = netisr_hashcpu(m->m_pkthdr.hash); 265 if (nextcpu != mycpuid) { 266 struct netmsg_alias_link_add *msg; 267 msg = kmalloc(sizeof(struct netmsg_alias_link_add), 268 M_LWKTMSG, M_NOWAIT | M_ZERO); 269 270 netmsg_init(&msg->base, NULL, 271 &curthread->td_msgport, 0, 272 add_alias_link_dispatch); 273 msg->lnk = new; 274 msg->id = t->id; 275 if (ip->ip_p == IPPROTO_TCP) { 276 msg->is_tcp = 1; 277 } 278 if (args->oif == NULL) { 279 msg->is_outgoing = 0; 280 } else { 281 msg->is_outgoing = 1; 282 } 283 ifnet_sendmsg(&msg->base.lmsg, nextcpu); 284 } 285 } 286 if (ldt) { 287 struct tcphdr *th; 288 struct udphdr *uh; 289 u_short cksum; 290 291 ip->ip_len = ntohs(ip->ip_len); 292 cksum = in_pseudo( 293 ip->ip_src.s_addr, 294 ip->ip_dst.s_addr, 295 htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2)) 296 ); 297 298 switch (ip->ip_p) { 299 case IPPROTO_TCP: 300 th = (struct tcphdr *)(ip + 1); 301 th->th_x2 = 0; 302 th->th_sum = cksum; 303 mcl->m_pkthdr.csum_data = 304 offsetof(struct tcphdr, th_sum); 305 break; 306 case IPPROTO_UDP: 307 uh = (struct udphdr *)(ip + 1); 308 uh->uh_sum = cksum; 309 mcl->m_pkthdr.csum_data = 310 offsetof(struct udphdr, uh_sum); 311 break; 312 } 313 /* 314 * No hw checksum offloading: do it 315 * by ourself. 316 */ 317 if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { 318 in_delayed_cksum(mcl); 319 mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 320 } 321 ip->ip_len = htons(ip->ip_len); 322 } 323 324 if (args->eh == NULL) { 325 ip->ip_len = ntohs(ip->ip_len); 326 ip->ip_off = ntohs(ip->ip_off); 327 } 328 329 args->m = mcl; 330 return IP_FW_NAT; 331 } 332 333 void 334 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) 335 { 336 struct cfg_redir *r, *tmp_r; 337 struct cfg_spool *s, *tmp_s; 338 int i, num; 339 340 LIST_FOREACH_MUTABLE(r, head, _next, tmp_r) { 341 num = 1; /* Number of alias_link to delete. */ 342 switch (r->mode) { 343 case REDIR_PORT: 344 num = r->pport_cnt; 345 /* FALLTHROUGH */ 346 case REDIR_ADDR: 347 case REDIR_PROTO: 348 /* Delete all libalias redirect entry. */ 349 for (i = 0; i < num; i++) 350 LibAliasRedirectDelete(n->lib, 351 r->alink[i]); 352 353 /* Del spool cfg if any. */ 354 LIST_FOREACH_MUTABLE(s, &r->spool_chain, 355 _next, tmp_s) { 356 LIST_REMOVE(s, _next); 357 kfree(s, M_IPFW_NAT); 358 } 359 kfree(r->alink, M_IPFW_NAT); 360 LIST_REMOVE(r, _next); 361 kfree(r, M_IPFW_NAT); 362 break; 363 default: 364 kprintf("unknown redirect mode: %u\n", r->mode); 365 /* XXX - panic?!?!? */ 366 break; 367 } 368 } 369 } 370 371 int 372 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) 373 { 374 struct cfg_redir *r, *ser_r; 375 struct cfg_spool *s, *ser_s; 376 int cnt, off, i; 377 char *panic_err; 378 379 for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { 380 ser_r = (struct cfg_redir *)&buf[off]; 381 r = kmalloc(SOF_REDIR, M_IPFW_NAT, M_WAITOK | M_ZERO); 382 memcpy(r, ser_r, SOF_REDIR); 383 LIST_INIT(&r->spool_chain); 384 off += SOF_REDIR; 385 r->alink = kmalloc(sizeof(struct alias_link *) * r->pport_cnt, 386 M_IPFW_NAT, M_WAITOK | M_ZERO); 387 switch (r->mode) { 388 case REDIR_ADDR: 389 r->alink[0] = LibAliasRedirectAddr(ptr->lib, 390 r->laddr, r->paddr); 391 break; 392 case REDIR_PORT: 393 for (i = 0 ; i < r->pport_cnt; i++) { 394 /* 395 * If remotePort is all ports 396 * set it to 0. 397 */ 398 u_short remotePortCopy = r->rport + i; 399 if (r->rport_cnt == 1 && r->rport == 0) 400 remotePortCopy = 0; 401 r->alink[i] = 402 403 LibAliasRedirectPort(ptr->lib, 404 r->laddr,htons(r->lport + i), 405 r->raddr,htons(remotePortCopy), 406 r->paddr,htons(r->pport + i), 407 r->proto); 408 409 if (r->alink[i] == NULL) { 410 r->alink[0] = NULL; 411 break; 412 } 413 } 414 break; 415 case REDIR_PROTO: 416 r->alink[0] = LibAliasRedirectProto(ptr->lib, 417 r->laddr, r->raddr, r->paddr, r->proto); 418 break; 419 default: 420 kprintf("unknown redirect mode: %u\n", r->mode); 421 break; 422 } 423 if (r->alink[0] == NULL) { 424 panic_err = "LibAliasRedirect* returned NULL"; 425 goto bad; 426 } else /* LSNAT handling. */ 427 for (i = 0; i < r->spool_cnt; i++) { 428 ser_s = (struct cfg_spool *)&buf[off]; 429 s = kmalloc(SOF_REDIR, M_IPFW_NAT, 430 M_WAITOK | M_ZERO); 431 memcpy(s, ser_s, SOF_SPOOL); 432 LibAliasAddServer(ptr->lib, r->alink[0], 433 s->addr, htons(s->port)); 434 off += SOF_SPOOL; 435 /* Hook spool entry. */ 436 HOOK_SPOOL(&r->spool_chain, s); 437 } 438 /* And finally hook this redir entry. */ 439 HOOK_REDIR(&ptr->redir_chain, r); 440 } 441 return 1; 442 bad: 443 /* something really bad happened: panic! */ 444 panic("%s\n", panic_err); 445 } 446 447 int 448 ipfw_ctl_nat_get_cfg(struct sockopt *sopt) 449 { 450 struct ipfw_nat_context *nat_ctx; 451 struct cfg_nat *n; 452 struct cfg_redir *r; 453 struct cfg_spool *s; 454 int nat_cnt, off, nat_cfg_size; 455 size_t size; 456 uint8_t *data; 457 458 nat_cnt = 0; 459 nat_cfg_size = 0; 460 off = sizeof(nat_cnt); 461 462 nat_ctx = ipfw_nat_ctx[mycpuid]; 463 size = sopt->sopt_valsize; 464 465 data = sopt->sopt_val; 466 /* count the size of nat cfg */ 467 LIST_FOREACH(n, &((*nat_ctx).nat), _next) { 468 nat_cfg_size += SOF_NAT; 469 } 470 471 LIST_FOREACH(n, &((*nat_ctx).nat), _next) { 472 nat_cnt++; 473 if (off + SOF_NAT < size) { 474 bcopy(n, &data[off], SOF_NAT); 475 off += SOF_NAT; 476 LIST_FOREACH(r, &n->redir_chain, _next) { 477 if (off + SOF_REDIR < size) { 478 bcopy(r, &data[off], SOF_REDIR); 479 off += SOF_REDIR; 480 LIST_FOREACH(s, &r->spool_chain, 481 _next) { 482 if (off + SOF_SPOOL < size) { 483 bcopy(s, &data[off], 484 SOF_SPOOL); 485 off += SOF_SPOOL; 486 } else 487 goto nospace; 488 } 489 } else 490 goto nospace; 491 } 492 } else 493 goto nospace; 494 } 495 bcopy(&nat_cnt, data, sizeof(nat_cnt)); 496 sopt->sopt_valsize = nat_cfg_size; 497 return 0; 498 nospace: 499 bzero(sopt->sopt_val, sopt->sopt_valsize); 500 sopt->sopt_valsize = nat_cfg_size; 501 return 0; 502 } 503 504 int 505 ipfw_ctl_nat_get_record(struct sockopt *sopt) 506 { 507 struct ipfw_nat_context *nat_ctx; 508 struct cfg_nat *t; 509 struct alias_link *lnk; 510 struct libalias *la; 511 size_t sopt_size, all_lnk_size = 0; 512 int i, *nat_id, id, n; 513 struct ipfw_ioc_nat_state *nat_state; 514 515 int cpu; 516 517 nat_id = (int *)(sopt->sopt_val); 518 n = *nat_id; 519 sopt_size = sopt->sopt_valsize; 520 nat_state = (struct ipfw_ioc_nat_state *)sopt->sopt_val; 521 for (cpu = 0; cpu < ncpus; cpu++) { 522 nat_ctx = ipfw_nat_ctx[cpu]; 523 id = n; 524 LOOKUP_NAT((*nat_ctx), id, t); 525 if (t != NULL) { 526 la = t->lib; 527 LIBALIAS_LOCK_ASSERT(la); 528 for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) { 529 LIST_FOREACH(lnk, &la->linkTableOut[i], 530 list_out) { 531 all_lnk_size += sizeof(*nat_state); 532 if (all_lnk_size > sopt_size) 533 goto nospace; 534 nat_state->src_addr = lnk->src_addr; 535 nat_state->dst_addr = lnk->dst_addr; 536 nat_state->alias_addr = lnk->alias_addr; 537 nat_state->src_port = lnk->src_port; 538 nat_state->dst_port = lnk->dst_port; 539 nat_state->alias_port = lnk->alias_port; 540 nat_state->link_type = lnk->link_type; 541 nat_state->timestamp = lnk->timestamp; 542 nat_state->cpuid = cpu; 543 nat_state->is_outgoing = 1; 544 nat_state++; 545 } 546 LIST_FOREACH(lnk, &la->linkTableIn[i], 547 list_out) { 548 all_lnk_size += sizeof(*nat_state); 549 if (all_lnk_size > sopt_size) 550 goto nospace; 551 nat_state->src_addr = lnk->src_addr; 552 nat_state->dst_addr = lnk->dst_addr; 553 nat_state->alias_addr = lnk->alias_addr; 554 nat_state->src_port = lnk->src_port; 555 nat_state->dst_port = lnk->dst_port; 556 nat_state->alias_port = lnk->alias_port; 557 nat_state->link_type = lnk->link_type; 558 nat_state->timestamp = lnk->timestamp; 559 nat_state->cpuid = cpu; 560 nat_state->is_outgoing = 0; 561 nat_state++; 562 } 563 } 564 } 565 } 566 sopt->sopt_valsize = all_lnk_size; 567 return 0; 568 nospace: 569 return 0; 570 } 571 572 void 573 nat_add_dispatch(netmsg_t nat_add_msg) 574 { 575 struct ipfw_nat_context *nat_ctx; 576 struct cfg_nat *ptr, *ser_n; 577 struct netmsg_nat_add *msg; 578 579 msg = (struct netmsg_nat_add *)nat_add_msg; 580 581 ser_n = (struct cfg_nat *)(msg->buf); 582 583 /* New rule: allocate and init new instance. */ 584 ptr = kmalloc(sizeof(struct cfg_nat), M_IPFW_NAT, M_WAITOK | M_ZERO); 585 586 ptr->lib = LibAliasInit(NULL); 587 if (ptr->lib == NULL) { 588 kfree(ptr, M_IPFW_NAT); 589 kfree(msg->buf, M_IPFW_NAT); 590 } 591 592 LIST_INIT(&ptr->redir_chain); 593 /* 594 * Basic nat configuration. 595 */ 596 ptr->id = ser_n->id; 597 /* 598 * XXX - what if this rule doesn't nat any ip and just 599 * redirect? 600 * do we set aliasaddress to 0.0.0.0? 601 */ 602 ptr->ip = ser_n->ip; 603 ptr->redir_cnt = ser_n->redir_cnt; 604 ptr->mode = ser_n->mode; 605 606 LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode); 607 LibAliasSetAddress(ptr->lib, ptr->ip); 608 memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE); 609 610 /* Add new entries. */ 611 add_redir_spool_cfg(&msg->buf[(sizeof(struct cfg_nat))], ptr); 612 613 nat_ctx = ipfw_nat_ctx[mycpuid]; 614 HOOK_NAT(&(nat_ctx->nat), ptr); 615 ifnet_forwardmsg(&msg->base.lmsg, mycpuid + 1); 616 } 617 618 int 619 ipfw_ctl_nat_add(struct sockopt *sopt) 620 { 621 struct ipfw_nat_context *nat_ctx; 622 struct cfg_nat *ptr, *ser_n; 623 ser_n = (struct cfg_nat *)(sopt->sopt_val); 624 625 nat_ctx = ipfw_nat_ctx[mycpuid]; 626 /* 627 * Find/create nat rule. 628 */ 629 LOOKUP_NAT((*nat_ctx), ser_n->id, ptr); 630 631 if (ptr == NULL) { 632 struct netmsg_nat_add nat_add_msg; 633 struct netmsg_nat_add *msg; 634 635 msg = &nat_add_msg; 636 msg->buf = kmalloc(sopt->sopt_valsize, 637 M_IPFW_NAT, M_WAITOK | M_ZERO); 638 639 sooptcopyin(sopt, msg->buf, sopt->sopt_valsize, 640 sizeof(struct cfg_nat)); 641 642 netmsg_init(&msg->base, NULL, &curthread->td_msgport, 643 0, nat_add_dispatch); 644 645 646 ifnet_domsg(&msg->base.lmsg, 0); 647 kfree(msg->buf, M_IPFW_NAT); 648 } else { 649 goto done; 650 } 651 done: 652 return 0; 653 } 654 655 void 656 nat_del_dispatch(netmsg_t nat_del_msg) 657 { 658 struct ipfw_nat_context *nat_ctx; 659 struct ipfw_context *ctx; 660 struct cfg_nat *n, *tmp; 661 struct netmsg_nat_del *msg; 662 struct ip_fw *f; 663 ipfw_insn *cmd; 664 int id; 665 666 msg = (struct netmsg_nat_del *)nat_del_msg; 667 id = msg->id; 668 669 nat_ctx = ipfw_nat_ctx[mycpuid]; 670 LOOKUP_NAT((*nat_ctx), id, n); 671 if (n == NULL) { 672 } 673 674 /* 675 * stop deleting when this cfg_nat was in use in ipfw_ctx 676 */ 677 ctx = ipfw_ctx[mycpuid]; 678 for (f = ctx->ipfw_rule_chain; f; f = f->next) { 679 cmd = ACTION_PTR(f); 680 if ((int)cmd->module == MODULE_NAT_ID && 681 (int)cmd->opcode == O_NAT_NAT) { 682 tmp = ((ipfw_insn_nat *)cmd)->nat; 683 if (tmp != NULL && tmp->id == n->id) { 684 } 685 } 686 } 687 688 UNHOOK_NAT(n); 689 del_redir_spool_cfg(n, &n->redir_chain); 690 LibAliasUninit(n->lib); 691 kfree(n, M_IPFW_NAT); 692 } 693 694 int 695 ipfw_ctl_nat_del(struct sockopt *sopt) 696 { 697 struct netmsg_nat_del nat_del_msg; 698 struct netmsg_nat_del *msg; 699 int *id; 700 701 msg = &nat_del_msg; 702 id = sopt->sopt_val; 703 msg->id = *id; 704 705 netmsg_init(&msg->base, NULL, &curthread->td_msgport, 706 0, nat_del_dispatch); 707 708 ifnet_domsg(&msg->base.lmsg, 0); 709 return 0; 710 } 711 712 int 713 ipfw_ctl_nat_flush(struct sockopt *sopt) 714 { 715 struct ipfw_nat_context *nat_ctx; 716 struct ipfw_context *ctx; 717 struct cfg_nat *ptr, *tmp; 718 struct ip_fw *f; 719 ipfw_insn *cmd; 720 int cpu; 721 722 /* 723 * stop flushing when any cfg_nat was in use in ipfw_ctx 724 */ 725 for (cpu = 0; cpu < ncpus; cpu++) { 726 ctx = ipfw_ctx[cpu]; 727 for (f = ctx->ipfw_rule_chain; f; f = f->next) { 728 cmd = ACTION_PTR(f); 729 if ((int)cmd->module == MODULE_NAT_ID && 730 (int)cmd->opcode == O_NAT_NAT) { 731 return EINVAL; 732 } 733 } 734 } 735 736 nat_ctx = ipfw_nat_ctx[mycpuid]; 737 738 LIST_FOREACH_MUTABLE(ptr, &(nat_ctx->nat), _next, tmp) { 739 LIST_REMOVE(ptr, _next); 740 del_redir_spool_cfg(ptr, &ptr->redir_chain); 741 LibAliasUninit(ptr->lib); 742 kfree(ptr, M_IPFW_NAT); 743 } 744 return 0; 745 } 746 747 int 748 ipfw_ctl_nat_sockopt(struct sockopt *sopt) 749 { 750 int error = 0; 751 switch (sopt->sopt_name) { 752 case IP_FW_NAT_ADD: 753 error = ipfw_ctl_nat_add(sopt); 754 break; 755 case IP_FW_NAT_DEL: 756 error = ipfw_ctl_nat_del(sopt); 757 break; 758 case IP_FW_NAT_FLUSH: 759 error = ipfw_ctl_nat_flush(sopt); 760 break; 761 case IP_FW_NAT_GET: 762 error = ipfw_ctl_nat_get_cfg(sopt); 763 break; 764 case IP_FW_NAT_GET_RECORD: 765 error = ipfw_ctl_nat_get_record(sopt); 766 break; 767 default: 768 kprintf("ipfw3 nat invalid socket option %d\n", 769 sopt->sopt_name); 770 } 771 return error; 772 } 773 774 void 775 nat_init_ctx_dispatch(netmsg_t msg) 776 { 777 struct ipfw_nat_context *tmp; 778 tmp = kmalloc(sizeof(struct ipfw_nat_context), 779 M_IPFW_NAT, M_WAITOK | M_ZERO); 780 ipfw_nat_ctx[mycpuid] = tmp; 781 ifnet_forwardmsg(&msg->lmsg, mycpuid + 1); 782 } 783 784 static 785 int ipfw_nat_init(void) 786 { 787 struct netmsg_base msg; 788 register_ipfw_module(MODULE_NAT_ID, MODULE_NAT_NAME); 789 register_ipfw_filter_funcs(MODULE_NAT_ID, O_NAT_NAT, 790 (filter_func)check_nat); 791 ipfw_ctl_nat_ptr = ipfw_ctl_nat_sockopt; 792 netmsg_init(&msg, NULL, &curthread->td_msgport, 793 0, nat_init_ctx_dispatch); 794 ifnet_domsg(&msg.lmsg, 0); 795 return 0; 796 } 797 798 static int 799 ipfw_nat_fini(void) 800 { 801 struct cfg_nat *ptr, *tmp; 802 struct ipfw_nat_context *ctx; 803 int cpu; 804 805 for (cpu = 0; cpu < ncpus; cpu++) { 806 ctx = ipfw_nat_ctx[cpu]; 807 if(ctx != NULL) { 808 LIST_FOREACH_MUTABLE(ptr, &(ctx->nat), _next, tmp) { 809 LIST_REMOVE(ptr, _next); 810 del_redir_spool_cfg(ptr, &ptr->redir_chain); 811 LibAliasUninit(ptr->lib); 812 kfree(ptr, M_IPFW_NAT); 813 } 814 815 kfree(ctx, M_IPFW_NAT); 816 ipfw_nat_ctx[cpu] = NULL; 817 } 818 } 819 ipfw_ctl_nat_ptr = NULL; 820 821 return unregister_ipfw_module(MODULE_NAT_ID); 822 } 823 824 static int 825 ipfw_nat_modevent(module_t mod, int type, void *data) 826 { 827 switch (type) { 828 case MOD_LOAD: 829 return ipfw_nat_init(); 830 case MOD_UNLOAD: 831 return ipfw_nat_fini(); 832 default: 833 break; 834 } 835 return 0; 836 } 837 838 moduledata_t ipfw_nat_mod = { 839 "ipfw3_nat", 840 ipfw_nat_modevent, 841 NULL 842 }; 843 844 DECLARE_MODULE(ipfw3_nat, ipfw_nat_mod, 845 SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); 846 MODULE_DEPEND(ipfw3_nat, libalias, 1, 1, 1); 847 MODULE_DEPEND(ipfw3_nat, ipfw3_basic, 1, 1, 1); 848 MODULE_VERSION(ipfw3_nat, 1); 849