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