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