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