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