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/mbuf.h> 35 #include <sys/kernel.h> 36 #include <sys/lock.h> 37 #include <sys/module.h> 38 #include <sys/rwlock.h> 39 #include <sys/rmlock.h> 40 41 #include <netinet/libalias/alias.h> 42 #include <netinet/libalias/alias_local.h> 43 44 #include <net/if.h> 45 #include <net/if_var.h> 46 #include <net/pfil.h> 47 #include <netinet/in.h> 48 #include <netinet/ip.h> 49 #include <netinet/ip_var.h> 50 #include <netinet/ip_fw.h> 51 #include <netinet/tcp.h> 52 #include <netinet/udp.h> 53 54 #include <netpfil/ipfw/ip_fw_private.h> 55 56 #include <machine/in_cksum.h> /* XXX for in_cksum */ 57 58 struct cfg_spool { 59 LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */ 60 struct in_addr addr; 61 uint16_t port; 62 }; 63 64 /* Nat redirect configuration. */ 65 struct cfg_redir { 66 LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */ 67 uint16_t mode; /* type of redirect mode */ 68 uint16_t proto; /* protocol: tcp/udp */ 69 struct in_addr laddr; /* local ip address */ 70 struct in_addr paddr; /* public ip address */ 71 struct in_addr raddr; /* remote ip address */ 72 uint16_t lport; /* local port */ 73 uint16_t pport; /* public port */ 74 uint16_t rport; /* remote port */ 75 uint16_t pport_cnt; /* number of public ports */ 76 uint16_t rport_cnt; /* number of remote ports */ 77 struct alias_link **alink; 78 u_int16_t spool_cnt; /* num of entry in spool chain */ 79 /* chain of spool instances */ 80 LIST_HEAD(spool_chain, cfg_spool) spool_chain; 81 }; 82 83 /* Nat configuration data struct. */ 84 struct cfg_nat { 85 /* chain of nat instances */ 86 LIST_ENTRY(cfg_nat) _next; 87 int id; /* nat id */ 88 struct in_addr ip; /* nat ip address */ 89 struct libalias *lib; /* libalias instance */ 90 int mode; /* aliasing mode */ 91 int redir_cnt; /* number of entry in spool chain */ 92 /* chain of redir instances */ 93 LIST_HEAD(redir_chain, cfg_redir) redir_chain; 94 char if_name[IF_NAMESIZE]; /* interface name */ 95 }; 96 97 static eventhandler_tag ifaddr_event_tag; 98 99 static void 100 ifaddr_change(void *arg __unused, struct ifnet *ifp) 101 { 102 struct cfg_nat *ptr; 103 struct ifaddr *ifa; 104 struct ip_fw_chain *chain; 105 106 KASSERT(curvnet == ifp->if_vnet, 107 ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); 108 109 if (V_ipfw_vnet_ready == 0 || V_ipfw_nat_ready == 0) 110 return; 111 112 chain = &V_layer3_chain; 113 IPFW_UH_WLOCK(chain); 114 /* Check every nat entry... */ 115 LIST_FOREACH(ptr, &chain->nat, _next) { 116 /* ...using nic 'ifp->if_xname' as dynamic alias address. */ 117 if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) 118 continue; 119 if_addr_rlock(ifp); 120 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 121 if (ifa->ifa_addr == NULL) 122 continue; 123 if (ifa->ifa_addr->sa_family != AF_INET) 124 continue; 125 IPFW_WLOCK(chain); 126 ptr->ip = ((struct sockaddr_in *) 127 (ifa->ifa_addr))->sin_addr; 128 LibAliasSetAddress(ptr->lib, ptr->ip); 129 IPFW_WUNLOCK(chain); 130 } 131 if_addr_runlock(ifp); 132 } 133 IPFW_UH_WUNLOCK(chain); 134 } 135 136 /* 137 * delete the pointers for nat entry ix, or all of them if ix < 0 138 */ 139 static void 140 flush_nat_ptrs(struct ip_fw_chain *chain, const int ix) 141 { 142 int i; 143 ipfw_insn_nat *cmd; 144 145 IPFW_WLOCK_ASSERT(chain); 146 for (i = 0; i < chain->n_rules; i++) { 147 cmd = (ipfw_insn_nat *)ACTION_PTR(chain->map[i]); 148 /* XXX skip log and the like ? */ 149 if (cmd->o.opcode == O_NAT && cmd->nat != NULL && 150 (ix < 0 || cmd->nat->id == ix)) 151 cmd->nat = NULL; 152 } 153 } 154 155 static void 156 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) 157 { 158 struct cfg_redir *r, *tmp_r; 159 struct cfg_spool *s, *tmp_s; 160 int i, num; 161 162 LIST_FOREACH_SAFE(r, head, _next, tmp_r) { 163 num = 1; /* Number of alias_link to delete. */ 164 switch (r->mode) { 165 case NAT44_REDIR_PORT: 166 num = r->pport_cnt; 167 /* FALLTHROUGH */ 168 case NAT44_REDIR_ADDR: 169 case NAT44_REDIR_PROTO: 170 /* Delete all libalias redirect entry. */ 171 for (i = 0; i < num; i++) 172 LibAliasRedirectDelete(n->lib, r->alink[i]); 173 /* Del spool cfg if any. */ 174 LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) { 175 LIST_REMOVE(s, _next); 176 free(s, M_IPFW); 177 } 178 free(r->alink, M_IPFW); 179 LIST_REMOVE(r, _next); 180 free(r, M_IPFW); 181 break; 182 default: 183 printf("unknown redirect mode: %u\n", r->mode); 184 /* XXX - panic?!?!? */ 185 break; 186 } 187 } 188 } 189 190 static int 191 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) 192 { 193 struct cfg_redir *r; 194 struct cfg_spool *s; 195 struct nat44_cfg_redir *ser_r; 196 struct nat44_cfg_spool *ser_s; 197 198 int cnt, off, i; 199 200 for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { 201 ser_r = (struct nat44_cfg_redir *)&buf[off]; 202 r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO); 203 r->mode = ser_r->mode; 204 r->laddr = ser_r->laddr; 205 r->paddr = ser_r->paddr; 206 r->raddr = ser_r->raddr; 207 r->lport = ser_r->lport; 208 r->pport = ser_r->pport; 209 r->rport = ser_r->rport; 210 r->pport_cnt = ser_r->pport_cnt; 211 r->rport_cnt = ser_r->rport_cnt; 212 r->proto = ser_r->proto; 213 r->spool_cnt = ser_r->spool_cnt; 214 //memcpy(r, ser_r, SOF_REDIR); 215 LIST_INIT(&r->spool_chain); 216 off += sizeof(struct nat44_cfg_redir); 217 r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, 218 M_IPFW, M_WAITOK | M_ZERO); 219 switch (r->mode) { 220 case NAT44_REDIR_ADDR: 221 r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, 222 r->paddr); 223 break; 224 case NAT44_REDIR_PORT: 225 for (i = 0 ; i < r->pport_cnt; i++) { 226 /* If remotePort is all ports, set it to 0. */ 227 u_short remotePortCopy = r->rport + i; 228 if (r->rport_cnt == 1 && r->rport == 0) 229 remotePortCopy = 0; 230 r->alink[i] = LibAliasRedirectPort(ptr->lib, 231 r->laddr, htons(r->lport + i), r->raddr, 232 htons(remotePortCopy), r->paddr, 233 htons(r->pport + i), r->proto); 234 if (r->alink[i] == NULL) { 235 r->alink[0] = NULL; 236 break; 237 } 238 } 239 break; 240 case NAT44_REDIR_PROTO: 241 r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, 242 r->raddr, r->paddr, r->proto); 243 break; 244 default: 245 printf("unknown redirect mode: %u\n", r->mode); 246 break; 247 } 248 if (r->alink[0] == NULL) { 249 printf("LibAliasRedirect* returned NULL\n"); 250 free(r->alink, M_IPFW); 251 free(r, M_IPFW); 252 return (EINVAL); 253 } 254 /* LSNAT handling. */ 255 for (i = 0; i < r->spool_cnt; i++) { 256 ser_s = (struct nat44_cfg_spool *)&buf[off]; 257 s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO); 258 s->addr = ser_s->addr; 259 s->port = ser_s->port; 260 LibAliasAddServer(ptr->lib, r->alink[0], 261 s->addr, htons(s->port)); 262 off += sizeof(struct nat44_cfg_spool); 263 /* Hook spool entry. */ 264 LIST_INSERT_HEAD(&r->spool_chain, s, _next); 265 } 266 /* And finally hook this redir entry. */ 267 LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); 268 } 269 270 return (0); 271 } 272 273 static void 274 free_nat_instance(struct cfg_nat *ptr) 275 { 276 277 del_redir_spool_cfg(ptr, &ptr->redir_chain); 278 LibAliasUninit(ptr->lib); 279 free(ptr, M_IPFW); 280 } 281 282 283 /* 284 * ipfw_nat - perform mbuf header translation. 285 * 286 * Note V_layer3_chain has to be locked while calling ipfw_nat() in 287 * 'global' operation mode (t == NULL). 288 * 289 */ 290 static int 291 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) 292 { 293 struct mbuf *mcl; 294 struct ip *ip; 295 /* XXX - libalias duct tape */ 296 int ldt, retval, found; 297 struct ip_fw_chain *chain; 298 char *c; 299 300 ldt = 0; 301 retval = 0; 302 mcl = m_megapullup(m, m->m_pkthdr.len); 303 if (mcl == NULL) { 304 args->m = NULL; 305 return (IP_FW_DENY); 306 } 307 ip = mtod(mcl, struct ip *); 308 309 /* 310 * XXX - Libalias checksum offload 'duct tape': 311 * 312 * locally generated packets have only pseudo-header checksum 313 * calculated and libalias will break it[1], so mark them for 314 * later fix. Moreover there are cases when libalias modifies 315 * tcp packet data[2], mark them for later fix too. 316 * 317 * [1] libalias was never meant to run in kernel, so it does 318 * not have any knowledge about checksum offloading, and 319 * expects a packet with a full internet checksum. 320 * Unfortunately, packets generated locally will have just the 321 * pseudo header calculated, and when libalias tries to adjust 322 * the checksum it will actually compute a wrong value. 323 * 324 * [2] when libalias modifies tcp's data content, full TCP 325 * checksum has to be recomputed: the problem is that 326 * libalias does not have any idea about checksum offloading. 327 * To work around this, we do not do checksumming in LibAlias, 328 * but only mark the packets in th_x2 field. If we receive a 329 * marked packet, we calculate correct checksum for it 330 * aware of offloading. Why such a terrible hack instead of 331 * recalculating checksum for each packet? 332 * Because the previous checksum was not checked! 333 * Recalculating checksums for EVERY packet will hide ALL 334 * transmission errors. Yes, marked packets still suffer from 335 * this problem. But, sigh, natd(8) has this problem, too. 336 * 337 * TODO: -make libalias mbuf aware (so 338 * it can handle delayed checksum and tso) 339 */ 340 341 if (mcl->m_pkthdr.rcvif == NULL && 342 mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) 343 ldt = 1; 344 345 c = mtod(mcl, char *); 346 347 /* Check if this is 'global' instance */ 348 if (t == NULL) { 349 if (args->oif == NULL) { 350 /* Wrong direction, skip processing */ 351 args->m = mcl; 352 return (IP_FW_NAT); 353 } 354 355 found = 0; 356 chain = &V_layer3_chain; 357 IPFW_RLOCK_ASSERT(chain); 358 /* Check every nat entry... */ 359 LIST_FOREACH(t, &chain->nat, _next) { 360 if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) 361 continue; 362 retval = LibAliasOutTry(t->lib, c, 363 mcl->m_len + M_TRAILINGSPACE(mcl), 0); 364 if (retval == PKT_ALIAS_OK) { 365 /* Nat instance recognises state */ 366 found = 1; 367 break; 368 } 369 } 370 if (found != 1) { 371 /* No instance found, return ignore */ 372 args->m = mcl; 373 return (IP_FW_NAT); 374 } 375 } else { 376 if (args->oif == NULL) 377 retval = LibAliasIn(t->lib, c, 378 mcl->m_len + M_TRAILINGSPACE(mcl)); 379 else 380 retval = LibAliasOut(t->lib, c, 381 mcl->m_len + M_TRAILINGSPACE(mcl)); 382 } 383 384 /* 385 * We drop packet when: 386 * 1. libalias returns PKT_ALIAS_ERROR; 387 * 2. For incoming packets: 388 * a) for unresolved fragments; 389 * b) libalias returns PKT_ALIAS_IGNORED and 390 * PKT_ALIAS_DENY_INCOMING flag is set. 391 */ 392 if (retval == PKT_ALIAS_ERROR || 393 (args->oif == NULL && (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT || 394 (retval == PKT_ALIAS_IGNORED && 395 (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) { 396 /* XXX - should i add some logging? */ 397 m_free(mcl); 398 args->m = NULL; 399 return (IP_FW_DENY); 400 } 401 402 if (retval == PKT_ALIAS_RESPOND) 403 mcl->m_flags |= M_SKIP_FIREWALL; 404 mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); 405 406 /* 407 * XXX - libalias checksum offload 408 * 'duct tape' (see above) 409 */ 410 411 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 412 ip->ip_p == IPPROTO_TCP) { 413 struct tcphdr *th; 414 415 th = (struct tcphdr *)(ip + 1); 416 if (th->th_x2) 417 ldt = 1; 418 } 419 420 if (ldt) { 421 struct tcphdr *th; 422 struct udphdr *uh; 423 uint16_t ip_len, cksum; 424 425 ip_len = ntohs(ip->ip_len); 426 cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 427 htons(ip->ip_p + ip_len - (ip->ip_hl << 2))); 428 429 switch (ip->ip_p) { 430 case IPPROTO_TCP: 431 th = (struct tcphdr *)(ip + 1); 432 /* 433 * Maybe it was set in 434 * libalias... 435 */ 436 th->th_x2 = 0; 437 th->th_sum = cksum; 438 mcl->m_pkthdr.csum_data = 439 offsetof(struct tcphdr, th_sum); 440 break; 441 case IPPROTO_UDP: 442 uh = (struct udphdr *)(ip + 1); 443 uh->uh_sum = cksum; 444 mcl->m_pkthdr.csum_data = 445 offsetof(struct udphdr, uh_sum); 446 break; 447 } 448 /* No hw checksum offloading: do it ourselves */ 449 if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { 450 in_delayed_cksum(mcl); 451 mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 452 } 453 } 454 args->m = mcl; 455 return (IP_FW_NAT); 456 } 457 458 static struct cfg_nat * 459 lookup_nat(struct nat_list *l, int nat_id) 460 { 461 struct cfg_nat *res; 462 463 LIST_FOREACH(res, l, _next) { 464 if (res->id == nat_id) 465 break; 466 } 467 return res; 468 } 469 470 static struct cfg_nat * 471 lookup_nat_name(struct nat_list *l, char *name) 472 { 473 struct cfg_nat *res; 474 int id; 475 char *errptr; 476 477 id = strtol(name, &errptr, 10); 478 if (id == 0 || *errptr != '\0') 479 return (NULL); 480 481 LIST_FOREACH(res, l, _next) { 482 if (res->id == id) 483 break; 484 } 485 return (res); 486 } 487 488 /* IP_FW3 configuration routines */ 489 490 static void 491 nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg) 492 { 493 struct cfg_nat *ptr, *tcfg; 494 int gencnt; 495 496 /* 497 * Find/create nat rule. 498 */ 499 IPFW_UH_WLOCK(chain); 500 gencnt = chain->gencnt; 501 ptr = lookup_nat_name(&chain->nat, ucfg->name); 502 if (ptr == NULL) { 503 IPFW_UH_WUNLOCK(chain); 504 /* New rule: allocate and init new instance. */ 505 ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); 506 ptr->lib = LibAliasInit(NULL); 507 LIST_INIT(&ptr->redir_chain); 508 } else { 509 /* Entry already present: temporarily unhook it. */ 510 IPFW_WLOCK(chain); 511 LIST_REMOVE(ptr, _next); 512 flush_nat_ptrs(chain, ptr->id); 513 IPFW_WUNLOCK(chain); 514 IPFW_UH_WUNLOCK(chain); 515 } 516 517 /* 518 * Basic nat (re)configuration. 519 */ 520 ptr->id = strtol(ucfg->name, NULL, 10); 521 /* 522 * XXX - what if this rule doesn't nat any ip and just 523 * redirect? 524 * do we set aliasaddress to 0.0.0.0? 525 */ 526 ptr->ip = ucfg->ip; 527 ptr->redir_cnt = ucfg->redir_cnt; 528 ptr->mode = ucfg->mode; 529 strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name)); 530 LibAliasSetMode(ptr->lib, ptr->mode, ~0); 531 LibAliasSetAddress(ptr->lib, ptr->ip); 532 533 /* 534 * Redir and LSNAT configuration. 535 */ 536 /* Delete old cfgs. */ 537 del_redir_spool_cfg(ptr, &ptr->redir_chain); 538 /* Add new entries. */ 539 add_redir_spool_cfg((char *)(ucfg + 1), ptr); 540 IPFW_UH_WLOCK(chain); 541 542 /* Extra check to avoid race with another ipfw_nat_cfg() */ 543 tcfg = NULL; 544 if (gencnt != chain->gencnt) 545 tcfg = lookup_nat_name(&chain->nat, ucfg->name); 546 IPFW_WLOCK(chain); 547 if (tcfg != NULL) 548 LIST_REMOVE(tcfg, _next); 549 LIST_INSERT_HEAD(&chain->nat, ptr, _next); 550 IPFW_WUNLOCK(chain); 551 chain->gencnt++; 552 553 IPFW_UH_WUNLOCK(chain); 554 555 if (tcfg != NULL) 556 free_nat_instance(ptr); 557 } 558 559 /* 560 * Creates/configure nat44 instance 561 * Data layout (v0)(current): 562 * Request: [ ipfw_obj_header nat44_cfg_nat .. ] 563 * 564 * Returns 0 on success 565 */ 566 static int 567 nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 568 struct sockopt_data *sd) 569 { 570 ipfw_obj_header *oh; 571 struct nat44_cfg_nat *ucfg; 572 int id; 573 size_t read; 574 char *errptr; 575 576 /* Check minimum header size */ 577 if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg))) 578 return (EINVAL); 579 580 oh = (ipfw_obj_header *)sd->kbuf; 581 582 /* Basic length checks for TLVs */ 583 if (oh->ntlv.head.length != sizeof(oh->ntlv)) 584 return (EINVAL); 585 586 ucfg = (struct nat44_cfg_nat *)(oh + 1); 587 588 /* Check if name is properly terminated and looks like number */ 589 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 590 return (EINVAL); 591 id = strtol(ucfg->name, &errptr, 10); 592 if (id == 0 || *errptr != '\0') 593 return (EINVAL); 594 595 read = sizeof(*oh) + sizeof(*ucfg); 596 /* Check number of redirs */ 597 if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir)) 598 return (EINVAL); 599 600 nat44_config(chain, ucfg); 601 return (0); 602 } 603 604 /* 605 * Destroys given nat instances. 606 * Data layout (v0)(current): 607 * Request: [ ipfw_obj_header ] 608 * 609 * Returns 0 on success 610 */ 611 static int 612 nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 613 struct sockopt_data *sd) 614 { 615 ipfw_obj_header *oh; 616 struct cfg_nat *ptr; 617 ipfw_obj_ntlv *ntlv; 618 619 /* Check minimum header size */ 620 if (sd->valsize < sizeof(*oh)) 621 return (EINVAL); 622 623 oh = (ipfw_obj_header *)sd->kbuf; 624 625 /* Basic length checks for TLVs */ 626 if (oh->ntlv.head.length != sizeof(oh->ntlv)) 627 return (EINVAL); 628 629 ntlv = &oh->ntlv; 630 /* Check if name is properly terminated */ 631 if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name)) 632 return (EINVAL); 633 634 IPFW_UH_WLOCK(chain); 635 ptr = lookup_nat_name(&chain->nat, ntlv->name); 636 if (ptr == NULL) { 637 IPFW_UH_WUNLOCK(chain); 638 return (ESRCH); 639 } 640 IPFW_WLOCK(chain); 641 LIST_REMOVE(ptr, _next); 642 flush_nat_ptrs(chain, ptr->id); 643 IPFW_WUNLOCK(chain); 644 IPFW_UH_WUNLOCK(chain); 645 646 free_nat_instance(ptr); 647 648 return (0); 649 } 650 651 static void 652 export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg) 653 { 654 655 snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id); 656 ucfg->ip = ptr->ip; 657 ucfg->redir_cnt = ptr->redir_cnt; 658 ucfg->mode = ptr->mode; 659 strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name)); 660 } 661 662 /* 663 * Gets config for given nat instance 664 * Data layout (v0)(current): 665 * Request: [ ipfw_obj_header nat44_cfg_nat .. ] 666 * 667 * Returns 0 on success 668 */ 669 static int 670 nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 671 struct sockopt_data *sd) 672 { 673 ipfw_obj_header *oh; 674 struct nat44_cfg_nat *ucfg; 675 struct cfg_nat *ptr; 676 struct cfg_redir *r; 677 struct cfg_spool *s; 678 struct nat44_cfg_redir *ser_r; 679 struct nat44_cfg_spool *ser_s; 680 size_t sz; 681 682 sz = sizeof(*oh) + sizeof(*ucfg); 683 /* Check minimum header size */ 684 if (sd->valsize < sz) 685 return (EINVAL); 686 687 oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 688 689 /* Basic length checks for TLVs */ 690 if (oh->ntlv.head.length != sizeof(oh->ntlv)) 691 return (EINVAL); 692 693 ucfg = (struct nat44_cfg_nat *)(oh + 1); 694 695 /* Check if name is properly terminated */ 696 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 697 return (EINVAL); 698 699 IPFW_UH_RLOCK(chain); 700 ptr = lookup_nat_name(&chain->nat, ucfg->name); 701 if (ptr == NULL) { 702 IPFW_UH_RUNLOCK(chain); 703 return (ESRCH); 704 } 705 706 export_nat_cfg(ptr, ucfg); 707 708 /* Estimate memory amount */ 709 sz = sizeof(ipfw_obj_header) + sizeof(struct nat44_cfg_nat); 710 LIST_FOREACH(r, &ptr->redir_chain, _next) { 711 sz += sizeof(struct nat44_cfg_redir); 712 LIST_FOREACH(s, &r->spool_chain, _next) 713 sz += sizeof(struct nat44_cfg_spool); 714 } 715 716 ucfg->size = sz; 717 if (sd->valsize < sz) { 718 719 /* 720 * Submitted buffer size is not enough. 721 * WE've already filled in @ucfg structure with 722 * relevant info including size, so we 723 * can return. Buffer will be flushed automatically. 724 */ 725 IPFW_UH_RUNLOCK(chain); 726 return (ENOMEM); 727 } 728 729 /* Size OK, let's copy data */ 730 LIST_FOREACH(r, &ptr->redir_chain, _next) { 731 ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd, 732 sizeof(*ser_r)); 733 ser_r->mode = r->mode; 734 ser_r->laddr = r->laddr; 735 ser_r->paddr = r->paddr; 736 ser_r->raddr = r->raddr; 737 ser_r->lport = r->lport; 738 ser_r->pport = r->pport; 739 ser_r->rport = r->rport; 740 ser_r->pport_cnt = r->pport_cnt; 741 ser_r->rport_cnt = r->rport_cnt; 742 ser_r->proto = r->proto; 743 ser_r->spool_cnt = r->spool_cnt; 744 745 LIST_FOREACH(s, &r->spool_chain, _next) { 746 ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space( 747 sd, sizeof(*ser_s)); 748 749 ser_s->addr = s->addr; 750 ser_s->port = s->port; 751 } 752 } 753 754 IPFW_UH_RUNLOCK(chain); 755 756 return (0); 757 } 758 759 /* 760 * Lists all nat44 instances currently available in kernel. 761 * Data layout (v0)(current): 762 * Request: [ ipfw_obj_lheader ] 763 * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ] 764 * 765 * Returns 0 on success 766 */ 767 static int 768 nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 769 struct sockopt_data *sd) 770 { 771 ipfw_obj_lheader *olh; 772 struct nat44_cfg_nat *ucfg; 773 struct cfg_nat *ptr; 774 int nat_count; 775 776 /* Check minimum header size */ 777 if (sd->valsize < sizeof(ipfw_obj_lheader)) 778 return (EINVAL); 779 780 olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); 781 IPFW_UH_RLOCK(chain); 782 nat_count = 0; 783 LIST_FOREACH(ptr, &chain->nat, _next) 784 nat_count++; 785 786 olh->count = nat_count; 787 olh->objsize = sizeof(struct nat44_cfg_nat); 788 olh->size = sizeof(*olh) + olh->count * olh->objsize; 789 790 if (sd->valsize < olh->size) { 791 IPFW_UH_RUNLOCK(chain); 792 return (ENOMEM); 793 } 794 795 LIST_FOREACH(ptr, &chain->nat, _next) { 796 ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd, 797 sizeof(*ucfg)); 798 export_nat_cfg(ptr, ucfg); 799 } 800 801 IPFW_UH_RUNLOCK(chain); 802 803 return (0); 804 } 805 806 /* 807 * Gets log for given nat instance 808 * Data layout (v0)(current): 809 * Request: [ ipfw_obj_header nat44_cfg_nat ] 810 * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ] 811 * 812 * Returns 0 on success 813 */ 814 static int 815 nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 816 struct sockopt_data *sd) 817 { 818 ipfw_obj_header *oh; 819 struct nat44_cfg_nat *ucfg; 820 struct cfg_nat *ptr; 821 void *pbuf; 822 size_t sz; 823 824 sz = sizeof(*oh) + sizeof(*ucfg); 825 /* Check minimum header size */ 826 if (sd->valsize < sz) 827 return (EINVAL); 828 829 oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 830 831 /* Basic length checks for TLVs */ 832 if (oh->ntlv.head.length != sizeof(oh->ntlv)) 833 return (EINVAL); 834 835 ucfg = (struct nat44_cfg_nat *)(oh + 1); 836 837 /* Check if name is properly terminated */ 838 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 839 return (EINVAL); 840 841 IPFW_UH_RLOCK(chain); 842 ptr = lookup_nat_name(&chain->nat, ucfg->name); 843 if (ptr == NULL) { 844 IPFW_UH_RUNLOCK(chain); 845 return (ESRCH); 846 } 847 848 if (ptr->lib->logDesc == NULL) { 849 IPFW_UH_RUNLOCK(chain); 850 return (ENOENT); 851 } 852 853 export_nat_cfg(ptr, ucfg); 854 855 /* Estimate memory amount */ 856 ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE; 857 if (sd->valsize < sz + sizeof(*oh)) { 858 859 /* 860 * Submitted buffer size is not enough. 861 * WE've already filled in @ucfg structure with 862 * relevant info including size, so we 863 * can return. Buffer will be flushed automatically. 864 */ 865 IPFW_UH_RUNLOCK(chain); 866 return (ENOMEM); 867 } 868 869 pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE); 870 memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE); 871 872 IPFW_UH_RUNLOCK(chain); 873 874 return (0); 875 } 876 877 static struct ipfw_sopt_handler scodes[] = { 878 { IP_FW_NAT44_XCONFIG, 0, HDIR_SET, nat44_cfg }, 879 { IP_FW_NAT44_DESTROY, 0, HDIR_SET, nat44_destroy }, 880 { IP_FW_NAT44_XGETCONFIG, 0, HDIR_GET, nat44_get_cfg }, 881 { IP_FW_NAT44_LIST_NAT, 0, HDIR_GET, nat44_list_nat }, 882 { IP_FW_NAT44_XGETLOG, 0, HDIR_GET, nat44_get_log }, 883 }; 884 885 886 /* 887 * Legacy configuration routines 888 */ 889 890 struct cfg_spool_legacy { 891 LIST_ENTRY(cfg_spool_legacy) _next; 892 struct in_addr addr; 893 u_short port; 894 }; 895 896 struct cfg_redir_legacy { 897 LIST_ENTRY(cfg_redir) _next; 898 u_int16_t mode; 899 struct in_addr laddr; 900 struct in_addr paddr; 901 struct in_addr raddr; 902 u_short lport; 903 u_short pport; 904 u_short rport; 905 u_short pport_cnt; 906 u_short rport_cnt; 907 int proto; 908 struct alias_link **alink; 909 u_int16_t spool_cnt; 910 LIST_HEAD(, cfg_spool_legacy) spool_chain; 911 }; 912 913 struct cfg_nat_legacy { 914 LIST_ENTRY(cfg_nat_legacy) _next; 915 int id; 916 struct in_addr ip; 917 char if_name[IF_NAMESIZE]; 918 int mode; 919 struct libalias *lib; 920 int redir_cnt; 921 LIST_HEAD(, cfg_redir_legacy) redir_chain; 922 }; 923 924 static int 925 ipfw_nat_cfg(struct sockopt *sopt) 926 { 927 struct cfg_nat_legacy *cfg; 928 struct nat44_cfg_nat *ucfg; 929 struct cfg_redir_legacy *rdir; 930 struct nat44_cfg_redir *urdir; 931 char *buf; 932 size_t len, len2; 933 int error, i; 934 935 len = sopt->sopt_valsize; 936 len2 = len + 128; 937 938 /* 939 * Allocate 2x buffer to store converted structures. 940 * new redir_cfg has shrunk, so we're sure that 941 * new buffer size is enough. 942 */ 943 buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO); 944 error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy)); 945 if (error != 0) 946 goto out; 947 948 cfg = (struct cfg_nat_legacy *)buf; 949 if (cfg->id < 0) { 950 error = EINVAL; 951 goto out; 952 } 953 954 ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)]; 955 snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id); 956 strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name)); 957 ucfg->ip = cfg->ip; 958 ucfg->mode = cfg->mode; 959 ucfg->redir_cnt = cfg->redir_cnt; 960 961 if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) { 962 error = EINVAL; 963 goto out; 964 } 965 966 urdir = (struct nat44_cfg_redir *)(ucfg + 1); 967 rdir = (struct cfg_redir_legacy *)(cfg + 1); 968 for (i = 0; i < cfg->redir_cnt; i++) { 969 urdir->mode = rdir->mode; 970 urdir->laddr = rdir->laddr; 971 urdir->paddr = rdir->paddr; 972 urdir->raddr = rdir->raddr; 973 urdir->lport = rdir->lport; 974 urdir->pport = rdir->pport; 975 urdir->rport = rdir->rport; 976 urdir->pport_cnt = rdir->pport_cnt; 977 urdir->rport_cnt = rdir->rport_cnt; 978 urdir->proto = rdir->proto; 979 urdir->spool_cnt = rdir->spool_cnt; 980 981 urdir++; 982 rdir++; 983 } 984 985 nat44_config(&V_layer3_chain, ucfg); 986 987 out: 988 free(buf, M_TEMP); 989 return (error); 990 } 991 992 static int 993 ipfw_nat_del(struct sockopt *sopt) 994 { 995 struct cfg_nat *ptr; 996 struct ip_fw_chain *chain = &V_layer3_chain; 997 int i; 998 999 sooptcopyin(sopt, &i, sizeof i, sizeof i); 1000 /* XXX validate i */ 1001 IPFW_UH_WLOCK(chain); 1002 ptr = lookup_nat(&chain->nat, i); 1003 if (ptr == NULL) { 1004 IPFW_UH_WUNLOCK(chain); 1005 return (EINVAL); 1006 } 1007 IPFW_WLOCK(chain); 1008 LIST_REMOVE(ptr, _next); 1009 flush_nat_ptrs(chain, i); 1010 IPFW_WUNLOCK(chain); 1011 IPFW_UH_WUNLOCK(chain); 1012 free_nat_instance(ptr); 1013 return (0); 1014 } 1015 1016 static int 1017 ipfw_nat_get_cfg(struct sockopt *sopt) 1018 { 1019 struct ip_fw_chain *chain = &V_layer3_chain; 1020 struct cfg_nat *n; 1021 struct cfg_nat_legacy *ucfg; 1022 struct cfg_redir *r; 1023 struct cfg_spool *s; 1024 struct cfg_redir_legacy *ser_r; 1025 struct cfg_spool_legacy *ser_s; 1026 char *data; 1027 int gencnt, nat_cnt, len, error; 1028 1029 nat_cnt = 0; 1030 len = sizeof(nat_cnt); 1031 1032 IPFW_UH_RLOCK(chain); 1033 retry: 1034 gencnt = chain->gencnt; 1035 /* Estimate memory amount */ 1036 LIST_FOREACH(n, &chain->nat, _next) { 1037 nat_cnt++; 1038 len += sizeof(struct cfg_nat_legacy); 1039 LIST_FOREACH(r, &n->redir_chain, _next) { 1040 len += sizeof(struct cfg_redir_legacy); 1041 LIST_FOREACH(s, &r->spool_chain, _next) 1042 len += sizeof(struct cfg_spool_legacy); 1043 } 1044 } 1045 IPFW_UH_RUNLOCK(chain); 1046 1047 data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 1048 bcopy(&nat_cnt, data, sizeof(nat_cnt)); 1049 1050 nat_cnt = 0; 1051 len = sizeof(nat_cnt); 1052 1053 IPFW_UH_RLOCK(chain); 1054 if (gencnt != chain->gencnt) { 1055 free(data, M_TEMP); 1056 goto retry; 1057 } 1058 /* Serialize all the data. */ 1059 LIST_FOREACH(n, &chain->nat, _next) { 1060 ucfg = (struct cfg_nat_legacy *)&data[len]; 1061 ucfg->id = n->id; 1062 ucfg->ip = n->ip; 1063 ucfg->redir_cnt = n->redir_cnt; 1064 ucfg->mode = n->mode; 1065 strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name)); 1066 len += sizeof(struct cfg_nat_legacy); 1067 LIST_FOREACH(r, &n->redir_chain, _next) { 1068 ser_r = (struct cfg_redir_legacy *)&data[len]; 1069 ser_r->mode = r->mode; 1070 ser_r->laddr = r->laddr; 1071 ser_r->paddr = r->paddr; 1072 ser_r->raddr = r->raddr; 1073 ser_r->lport = r->lport; 1074 ser_r->pport = r->pport; 1075 ser_r->rport = r->rport; 1076 ser_r->pport_cnt = r->pport_cnt; 1077 ser_r->rport_cnt = r->rport_cnt; 1078 ser_r->proto = r->proto; 1079 ser_r->spool_cnt = r->spool_cnt; 1080 len += sizeof(struct cfg_redir_legacy); 1081 LIST_FOREACH(s, &r->spool_chain, _next) { 1082 ser_s = (struct cfg_spool_legacy *)&data[len]; 1083 ser_s->addr = s->addr; 1084 ser_s->port = s->port; 1085 len += sizeof(struct cfg_spool_legacy); 1086 } 1087 } 1088 } 1089 IPFW_UH_RUNLOCK(chain); 1090 1091 error = sooptcopyout(sopt, data, len); 1092 free(data, M_TEMP); 1093 1094 return (error); 1095 } 1096 1097 static int 1098 ipfw_nat_get_log(struct sockopt *sopt) 1099 { 1100 uint8_t *data; 1101 struct cfg_nat *ptr; 1102 int i, size; 1103 struct ip_fw_chain *chain; 1104 IPFW_RLOCK_TRACKER; 1105 1106 chain = &V_layer3_chain; 1107 1108 IPFW_RLOCK(chain); 1109 /* one pass to count, one to copy the data */ 1110 i = 0; 1111 LIST_FOREACH(ptr, &chain->nat, _next) { 1112 if (ptr->lib->logDesc == NULL) 1113 continue; 1114 i++; 1115 } 1116 size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); 1117 data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); 1118 if (data == NULL) { 1119 IPFW_RUNLOCK(chain); 1120 return (ENOSPC); 1121 } 1122 i = 0; 1123 LIST_FOREACH(ptr, &chain->nat, _next) { 1124 if (ptr->lib->logDesc == NULL) 1125 continue; 1126 bcopy(&ptr->id, &data[i], sizeof(int)); 1127 i += sizeof(int); 1128 bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); 1129 i += LIBALIAS_BUF_SIZE; 1130 } 1131 IPFW_RUNLOCK(chain); 1132 sooptcopyout(sopt, data, size); 1133 free(data, M_IPFW); 1134 return(0); 1135 } 1136 1137 static int 1138 vnet_ipfw_nat_init(const void *arg __unused) 1139 { 1140 1141 V_ipfw_nat_ready = 1; 1142 return (0); 1143 } 1144 1145 static int 1146 vnet_ipfw_nat_uninit(const void *arg __unused) 1147 { 1148 struct cfg_nat *ptr, *ptr_temp; 1149 struct ip_fw_chain *chain; 1150 1151 chain = &V_layer3_chain; 1152 IPFW_WLOCK(chain); 1153 V_ipfw_nat_ready = 0; 1154 LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { 1155 LIST_REMOVE(ptr, _next); 1156 free_nat_instance(ptr); 1157 } 1158 flush_nat_ptrs(chain, -1 /* flush all */); 1159 IPFW_WUNLOCK(chain); 1160 return (0); 1161 } 1162 1163 static void 1164 ipfw_nat_init(void) 1165 { 1166 1167 /* init ipfw hooks */ 1168 ipfw_nat_ptr = ipfw_nat; 1169 lookup_nat_ptr = lookup_nat; 1170 ipfw_nat_cfg_ptr = ipfw_nat_cfg; 1171 ipfw_nat_del_ptr = ipfw_nat_del; 1172 ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; 1173 ipfw_nat_get_log_ptr = ipfw_nat_get_log; 1174 IPFW_ADD_SOPT_HANDLER(1, scodes); 1175 1176 ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change, 1177 NULL, EVENTHANDLER_PRI_ANY); 1178 } 1179 1180 static void 1181 ipfw_nat_destroy(void) 1182 { 1183 1184 EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag); 1185 /* deregister ipfw_nat */ 1186 IPFW_DEL_SOPT_HANDLER(1, scodes); 1187 ipfw_nat_ptr = NULL; 1188 lookup_nat_ptr = NULL; 1189 ipfw_nat_cfg_ptr = NULL; 1190 ipfw_nat_del_ptr = NULL; 1191 ipfw_nat_get_cfg_ptr = NULL; 1192 ipfw_nat_get_log_ptr = NULL; 1193 } 1194 1195 static int 1196 ipfw_nat_modevent(module_t mod, int type, void *unused) 1197 { 1198 int err = 0; 1199 1200 switch (type) { 1201 case MOD_LOAD: 1202 break; 1203 1204 case MOD_UNLOAD: 1205 break; 1206 1207 default: 1208 return EOPNOTSUPP; 1209 break; 1210 } 1211 return err; 1212 } 1213 1214 static moduledata_t ipfw_nat_mod = { 1215 "ipfw_nat", 1216 ipfw_nat_modevent, 1217 0 1218 }; 1219 1220 /* Define startup order. */ 1221 #define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_FIREWALL 1222 #define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 128) /* after ipfw */ 1223 #define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1) 1224 #define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2) 1225 1226 DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY); 1227 MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); 1228 MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3); 1229 MODULE_VERSION(ipfw_nat, 1); 1230 1231 SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 1232 ipfw_nat_init, NULL); 1233 VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, 1234 vnet_ipfw_nat_init, NULL); 1235 1236 SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 1237 ipfw_nat_destroy, NULL); 1238 VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL, 1239 IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL); 1240 1241 /* end of file */ 1242