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