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