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 <net/pfil.h> 49#include <netinet/in.h> 50#include <netinet/ip.h> 51#include <netinet/ip_var.h> 52#include <netinet/ip_fw.h> 53#include <netinet/tcp.h> 54#include <netinet/udp.h> 55 56#include <netpfil/ipfw/ip_fw_private.h> 57 58#include <machine/in_cksum.h> /* XXX for in_cksum */ 59 60struct cfg_spool { 61 LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */ 62 struct in_addr addr; 63 uint16_t port; 64}; 65 66/* Nat redirect configuration. */ 67struct cfg_redir { 68 LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */ 69 uint16_t mode; /* type of redirect mode */ 70 uint16_t proto; /* protocol: tcp/udp */ 71 struct in_addr laddr; /* local ip address */ 72 struct in_addr paddr; /* public ip address */ 73 struct in_addr raddr; /* remote ip address */ 74 uint16_t lport; /* local port */ 75 uint16_t pport; /* public port */ 76 uint16_t rport; /* remote port */ 77 uint16_t pport_cnt; /* number of public ports */ 78 uint16_t rport_cnt; /* number of remote ports */ 79 struct alias_link **alink; 80 u_int16_t spool_cnt; /* num of entry in spool chain */ 81 /* chain of spool instances */ 82 LIST_HEAD(spool_chain, cfg_spool) spool_chain; 83}; 84 85/* Nat configuration data struct. */ 86struct cfg_nat { 87 /* chain of nat instances */ 88 LIST_ENTRY(cfg_nat) _next; 89 int id; /* nat id */ 90 struct in_addr ip; /* nat ip address */ 91 struct libalias *lib; /* libalias instance */ 92 int mode; /* aliasing mode */ 93 int redir_cnt; /* number of entry in spool chain */ 94 /* chain of redir instances */ 95 LIST_HEAD(redir_chain, cfg_redir) redir_chain; 96 char if_name[IF_NAMESIZE]; /* interface name */ 97 u_short alias_port_lo; /* low range for port aliasing */ 98 u_short alias_port_hi; /* high range for port aliasing */ 99}; 100 101static eventhandler_tag ifaddr_event_tag; 102 103static void 104ifaddr_change(void *arg __unused, struct ifnet *ifp) 105{ 106 struct cfg_nat *ptr; 107 struct ifaddr *ifa; 108 struct ip_fw_chain *chain; 109 110 KASSERT(curvnet == ifp->if_vnet, 111 ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); 112 113 if (V_ipfw_vnet_ready == 0 || V_ipfw_nat_ready == 0) 114 return; 115 116 chain = &V_layer3_chain; 117 IPFW_UH_WLOCK(chain); 118 /* Check every nat entry... */ 119 LIST_FOREACH(ptr, &chain->nat, _next) { 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 if_addr_rlock(ifp); 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 if_addr_runlock(ifp); 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 */ 143static void 144flush_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 158static void 159del_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 193static int 194add_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 276static void 277free_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/* 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 */ 293static int 294ipfw_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 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->oif == NULL) { 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->oif == NULL) 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->oif == NULL && (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT || 397 (retval == PKT_ALIAS_IGNORED && 398 (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) { 399 /* XXX - should i add some logging? */ 400 m_free(mcl); 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 (th->th_x2) 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 th->th_x2 = 0; 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 461static struct cfg_nat * 462lookup_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 473static struct cfg_nat * 474lookup_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 493static void 494nat44_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 IPFW_UH_WUNLOCK(chain); 507 /* New rule: allocate and init new instance. */ 508 ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); 509 ptr->lib = LibAliasInit(NULL); 510 LIST_INIT(&ptr->redir_chain); 511 } else { 512 /* Entry already present: temporarily unhook it. */ 513 IPFW_WLOCK(chain); 514 LIST_REMOVE(ptr, _next); 515 flush_nat_ptrs(chain, ptr->id); 516 IPFW_WUNLOCK(chain); 517 IPFW_UH_WUNLOCK(chain); 518 } 519 520 /* 521 * Basic nat (re)configuration. 522 */ 523 ptr->id = strtol(ucfg->name, NULL, 10); 524 /* 525 * XXX - what if this rule doesn't nat any ip and just 526 * redirect? 527 * do we set aliasaddress to 0.0.0.0? 528 */ 529 ptr->ip = ucfg->ip; 530 ptr->redir_cnt = ucfg->redir_cnt; 531 ptr->mode = ucfg->mode; 532 ptr->alias_port_lo = ucfg->alias_port_lo; 533 ptr->alias_port_hi = ucfg->alias_port_hi; 534 strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name)); 535 LibAliasSetMode(ptr->lib, ptr->mode, ~0); 536 LibAliasSetAddress(ptr->lib, ptr->ip); 537 LibAliasSetAliasPortRange(ptr->lib, ptr->alias_port_lo, ptr->alias_port_hi); 538 539 /* 540 * Redir and LSNAT configuration. 541 */ 542 /* Delete old cfgs. */ 543 del_redir_spool_cfg(ptr, &ptr->redir_chain); 544 /* Add new entries. */ 545 add_redir_spool_cfg((char *)(ucfg + 1), ptr); 546 IPFW_UH_WLOCK(chain); 547 548 /* Extra check to avoid race with another ipfw_nat_cfg() */ 549 tcfg = NULL; 550 if (gencnt != chain->gencnt) 551 tcfg = lookup_nat_name(&chain->nat, ucfg->name); 552 IPFW_WLOCK(chain); 553 if (tcfg != NULL) 554 LIST_REMOVE(tcfg, _next); 555 LIST_INSERT_HEAD(&chain->nat, ptr, _next); 556 IPFW_WUNLOCK(chain); 557 chain->gencnt++; 558 559 IPFW_UH_WUNLOCK(chain); 560 561 if (tcfg != NULL) 562 free_nat_instance(ptr); 563} 564 565/* 566 * Creates/configure nat44 instance 567 * Data layout (v0)(current): 568 * Request: [ ipfw_obj_header nat44_cfg_nat .. ] 569 * 570 * Returns 0 on success 571 */ 572static int 573nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 574 struct sockopt_data *sd) 575{ 576 ipfw_obj_header *oh; 577 struct nat44_cfg_nat *ucfg; 578 int id; 579 size_t read; 580 char *errptr; 581 582 /* Check minimum header size */ 583 if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg))) 584 return (EINVAL); 585 586 oh = (ipfw_obj_header *)sd->kbuf; 587 588 /* Basic length checks for TLVs */ 589 if (oh->ntlv.head.length != sizeof(oh->ntlv)) 590 return (EINVAL); 591 592 ucfg = (struct nat44_cfg_nat *)(oh + 1); 593 594 /* Check if name is properly terminated and looks like number */ 595 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 596 return (EINVAL); 597 id = strtol(ucfg->name, &errptr, 10); 598 if (id == 0 || *errptr != '\0') 599 return (EINVAL); 600 601 read = sizeof(*oh) + sizeof(*ucfg); 602 /* Check number of redirs */ 603 if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir)) 604 return (EINVAL); 605 606 nat44_config(chain, ucfg); 607 return (0); 608} 609 610/* 611 * Destroys given nat instances. 612 * Data layout (v0)(current): 613 * Request: [ ipfw_obj_header ] 614 * 615 * Returns 0 on success 616 */ 617static int 618nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 619 struct sockopt_data *sd) 620{ 621 ipfw_obj_header *oh; 622 struct cfg_nat *ptr; 623 ipfw_obj_ntlv *ntlv; 624 625 /* Check minimum header size */ 626 if (sd->valsize < sizeof(*oh)) 627 return (EINVAL); 628 629 oh = (ipfw_obj_header *)sd->kbuf; 630 631 /* Basic length checks for TLVs */ 632 if (oh->ntlv.head.length != sizeof(oh->ntlv)) 633 return (EINVAL); 634 635 ntlv = &oh->ntlv; 636 /* Check if name is properly terminated */ 637 if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name)) 638 return (EINVAL); 639 640 IPFW_UH_WLOCK(chain); 641 ptr = lookup_nat_name(&chain->nat, ntlv->name); 642 if (ptr == NULL) { 643 IPFW_UH_WUNLOCK(chain); 644 return (ESRCH); 645 } 646 IPFW_WLOCK(chain); 647 LIST_REMOVE(ptr, _next); 648 flush_nat_ptrs(chain, ptr->id); 649 IPFW_WUNLOCK(chain); 650 IPFW_UH_WUNLOCK(chain); 651 652 free_nat_instance(ptr); 653 654 return (0); 655} 656 657static void 658export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg) 659{ 660 661 snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id); 662 ucfg->ip = ptr->ip; 663 ucfg->redir_cnt = ptr->redir_cnt; 664 ucfg->mode = ptr->mode; 665 ucfg->alias_port_lo = ptr->alias_port_lo; 666 ucfg->alias_port_hi = ptr->alias_port_hi; 667 strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name)); 668} 669 670/* 671 * Gets config for given nat instance 672 * Data layout (v0)(current): 673 * Request: [ ipfw_obj_header nat44_cfg_nat .. ] 674 * 675 * Returns 0 on success 676 */ 677static int 678nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 679 struct sockopt_data *sd) 680{ 681 ipfw_obj_header *oh; 682 struct nat44_cfg_nat *ucfg; 683 struct cfg_nat *ptr; 684 struct cfg_redir *r; 685 struct cfg_spool *s; 686 struct nat44_cfg_redir *ser_r; 687 struct nat44_cfg_spool *ser_s; 688 size_t sz; 689 690 sz = sizeof(*oh) + sizeof(*ucfg); 691 /* Check minimum header size */ 692 if (sd->valsize < sz) 693 return (EINVAL); 694 695 oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 696 697 /* Basic length checks for TLVs */ 698 if (oh->ntlv.head.length != sizeof(oh->ntlv)) 699 return (EINVAL); 700 701 ucfg = (struct nat44_cfg_nat *)(oh + 1); 702 703 /* Check if name is properly terminated */ 704 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 705 return (EINVAL); 706 707 IPFW_UH_RLOCK(chain); 708 ptr = lookup_nat_name(&chain->nat, ucfg->name); 709 if (ptr == NULL) { 710 IPFW_UH_RUNLOCK(chain); 711 return (ESRCH); 712 } 713 714 export_nat_cfg(ptr, ucfg); 715 716 /* Estimate memory amount */ 717 sz = sizeof(ipfw_obj_header) + sizeof(struct nat44_cfg_nat); 718 LIST_FOREACH(r, &ptr->redir_chain, _next) { 719 sz += sizeof(struct nat44_cfg_redir); 720 LIST_FOREACH(s, &r->spool_chain, _next) 721 sz += sizeof(struct nat44_cfg_spool); 722 } 723 724 ucfg->size = sz; 725 if (sd->valsize < sz) { 726 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 */ 775static int 776nat44_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 */ 822static int 823nat44_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 /* 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 885static 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/* 895 * Legacy configuration routines 896 */ 897 898struct cfg_spool_legacy { 899 LIST_ENTRY(cfg_spool_legacy) _next; 900 struct in_addr addr; 901 u_short port; 902}; 903 904struct cfg_redir_legacy { 905 LIST_ENTRY(cfg_redir) _next; 906 u_int16_t mode; 907 struct in_addr laddr; 908 struct in_addr paddr; 909 struct in_addr raddr; 910 u_short lport; 911 u_short pport; 912 u_short rport; 913 u_short pport_cnt; 914 u_short rport_cnt; 915 int proto; 916 struct alias_link **alink; 917 u_int16_t spool_cnt; 918 LIST_HEAD(, cfg_spool_legacy) spool_chain; 919}; 920 921struct cfg_nat_legacy { 922 LIST_ENTRY(cfg_nat_legacy) _next; 923 int id; 924 struct in_addr ip; 925 char if_name[IF_NAMESIZE]; 926 int mode; 927 struct libalias *lib; 928 int redir_cnt; 929 LIST_HEAD(, cfg_redir_legacy) redir_chain; 930}; 931 932static int 933ipfw_nat_cfg(struct sockopt *sopt) 934{ 935 struct cfg_nat_legacy *cfg; 936 struct nat44_cfg_nat *ucfg; 937 struct cfg_redir_legacy *rdir; 938 struct nat44_cfg_redir *urdir; 939 char *buf; 940 size_t len, len2; 941 int error, i; 942 943 len = sopt->sopt_valsize; 944 len2 = len + 128; 945 946 /* 947 * Allocate 2x buffer to store converted structures. 948 * new redir_cfg has shrunk, so we're sure that 949 * new buffer size is enough. 950 */ 951 buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO); 952 error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy)); 953 if (error != 0) 954 goto out; 955 956 cfg = (struct cfg_nat_legacy *)buf; 957 if (cfg->id < 0) { 958 error = EINVAL; 959 goto out; 960 } 961 962 ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)]; 963 snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id); 964 strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name)); 965 ucfg->ip = cfg->ip; 966 ucfg->mode = cfg->mode; 967 ucfg->redir_cnt = cfg->redir_cnt; 968 969 if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) { 970 error = EINVAL; 971 goto out; 972 } 973 974 urdir = (struct nat44_cfg_redir *)(ucfg + 1); 975 rdir = (struct cfg_redir_legacy *)(cfg + 1); 976 for (i = 0; i < cfg->redir_cnt; i++) { 977 urdir->mode = rdir->mode; 978 urdir->laddr = rdir->laddr; 979 urdir->paddr = rdir->paddr; 980 urdir->raddr = rdir->raddr; 981 urdir->lport = rdir->lport; 982 urdir->pport = rdir->pport; 983 urdir->rport = rdir->rport; 984 urdir->pport_cnt = rdir->pport_cnt; 985 urdir->rport_cnt = rdir->rport_cnt; 986 urdir->proto = rdir->proto; 987 urdir->spool_cnt = rdir->spool_cnt; 988 989 urdir++; 990 rdir++; 991 } 992 993 nat44_config(&V_layer3_chain, ucfg); 994 995out: 996 free(buf, M_TEMP); 997 return (error); 998} 999 1000static int 1001ipfw_nat_del(struct sockopt *sopt) 1002{ 1003 struct cfg_nat *ptr; 1004 struct ip_fw_chain *chain = &V_layer3_chain; 1005 int i; 1006 1007 sooptcopyin(sopt, &i, sizeof i, sizeof i); 1008 /* XXX validate i */ 1009 IPFW_UH_WLOCK(chain); 1010 ptr = lookup_nat(&chain->nat, i); 1011 if (ptr == NULL) { 1012 IPFW_UH_WUNLOCK(chain); 1013 return (EINVAL); 1014 } 1015 IPFW_WLOCK(chain); 1016 LIST_REMOVE(ptr, _next); 1017 flush_nat_ptrs(chain, i); 1018 IPFW_WUNLOCK(chain); 1019 IPFW_UH_WUNLOCK(chain); 1020 free_nat_instance(ptr); 1021 return (0); 1022} 1023 1024static int 1025ipfw_nat_get_cfg(struct sockopt *sopt) 1026{ 1027 struct ip_fw_chain *chain = &V_layer3_chain; 1028 struct cfg_nat *n; 1029 struct cfg_nat_legacy *ucfg; 1030 struct cfg_redir *r; 1031 struct cfg_spool *s; 1032 struct cfg_redir_legacy *ser_r; 1033 struct cfg_spool_legacy *ser_s; 1034 char *data; 1035 int gencnt, nat_cnt, len, error; 1036 1037 nat_cnt = 0; 1038 len = sizeof(nat_cnt); 1039 1040 IPFW_UH_RLOCK(chain); 1041retry: 1042 gencnt = chain->gencnt; 1043 /* Estimate memory amount */ 1044 LIST_FOREACH(n, &chain->nat, _next) { 1045 nat_cnt++; 1046 len += sizeof(struct cfg_nat_legacy); 1047 LIST_FOREACH(r, &n->redir_chain, _next) { 1048 len += sizeof(struct cfg_redir_legacy); 1049 LIST_FOREACH(s, &r->spool_chain, _next) 1050 len += sizeof(struct cfg_spool_legacy); 1051 } 1052 } 1053 IPFW_UH_RUNLOCK(chain); 1054 1055 data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 1056 bcopy(&nat_cnt, data, sizeof(nat_cnt)); 1057 1058 nat_cnt = 0; 1059 len = sizeof(nat_cnt); 1060 1061 IPFW_UH_RLOCK(chain); 1062 if (gencnt != chain->gencnt) { 1063 free(data, M_TEMP); 1064 goto retry; 1065 } 1066 /* Serialize all the data. */ 1067 LIST_FOREACH(n, &chain->nat, _next) { 1068 ucfg = (struct cfg_nat_legacy *)&data[len]; 1069 ucfg->id = n->id; 1070 ucfg->ip = n->ip; 1071 ucfg->redir_cnt = n->redir_cnt; 1072 ucfg->mode = n->mode; 1073 strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name)); 1074 len += sizeof(struct cfg_nat_legacy); 1075 LIST_FOREACH(r, &n->redir_chain, _next) { 1076 ser_r = (struct cfg_redir_legacy *)&data[len]; 1077 ser_r->mode = r->mode; 1078 ser_r->laddr = r->laddr; 1079 ser_r->paddr = r->paddr; 1080 ser_r->raddr = r->raddr; 1081 ser_r->lport = r->lport; 1082 ser_r->pport = r->pport; 1083 ser_r->rport = r->rport; 1084 ser_r->pport_cnt = r->pport_cnt; 1085 ser_r->rport_cnt = r->rport_cnt; 1086 ser_r->proto = r->proto; 1087 ser_r->spool_cnt = r->spool_cnt; 1088 len += sizeof(struct cfg_redir_legacy); 1089 LIST_FOREACH(s, &r->spool_chain, _next) { 1090 ser_s = (struct cfg_spool_legacy *)&data[len]; 1091 ser_s->addr = s->addr; 1092 ser_s->port = s->port; 1093 len += sizeof(struct cfg_spool_legacy); 1094 } 1095 } 1096 } 1097 IPFW_UH_RUNLOCK(chain); 1098 1099 error = sooptcopyout(sopt, data, len); 1100 free(data, M_TEMP); 1101 1102 return (error); 1103} 1104 1105static int 1106ipfw_nat_get_log(struct sockopt *sopt) 1107{ 1108 uint8_t *data; 1109 struct cfg_nat *ptr; 1110 int i, size; 1111 struct ip_fw_chain *chain; 1112 IPFW_RLOCK_TRACKER; 1113 1114 chain = &V_layer3_chain; 1115 1116 IPFW_RLOCK(chain); 1117 /* one pass to count, one to copy the data */ 1118 i = 0; 1119 LIST_FOREACH(ptr, &chain->nat, _next) { 1120 if (ptr->lib->logDesc == NULL) 1121 continue; 1122 i++; 1123 } 1124 size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); 1125 data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); 1126 if (data == NULL) { 1127 IPFW_RUNLOCK(chain); 1128 return (ENOSPC); 1129 } 1130 i = 0; 1131 LIST_FOREACH(ptr, &chain->nat, _next) { 1132 if (ptr->lib->logDesc == NULL) 1133 continue; 1134 bcopy(&ptr->id, &data[i], sizeof(int)); 1135 i += sizeof(int); 1136 bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); 1137 i += LIBALIAS_BUF_SIZE; 1138 } 1139 IPFW_RUNLOCK(chain); 1140 sooptcopyout(sopt, data, size); 1141 free(data, M_IPFW); 1142 return(0); 1143} 1144 1145static int 1146vnet_ipfw_nat_init(const void *arg __unused) 1147{ 1148 1149 V_ipfw_nat_ready = 1; 1150 return (0); 1151} 1152 1153static int 1154vnet_ipfw_nat_uninit(const void *arg __unused) 1155{ 1156 struct cfg_nat *ptr, *ptr_temp; 1157 struct ip_fw_chain *chain; 1158 1159 chain = &V_layer3_chain; 1160 IPFW_WLOCK(chain); 1161 V_ipfw_nat_ready = 0; 1162 LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { 1163 LIST_REMOVE(ptr, _next); 1164 free_nat_instance(ptr); 1165 } 1166 flush_nat_ptrs(chain, -1 /* flush all */); 1167 IPFW_WUNLOCK(chain); 1168 return (0); 1169} 1170 1171static void 1172ipfw_nat_init(void) 1173{ 1174 1175 /* init ipfw hooks */ 1176 ipfw_nat_ptr = ipfw_nat; 1177 lookup_nat_ptr = lookup_nat; 1178 ipfw_nat_cfg_ptr = ipfw_nat_cfg; 1179 ipfw_nat_del_ptr = ipfw_nat_del; 1180 ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; 1181 ipfw_nat_get_log_ptr = ipfw_nat_get_log; 1182 IPFW_ADD_SOPT_HANDLER(1, scodes); 1183 1184 ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change, 1185 NULL, EVENTHANDLER_PRI_ANY); 1186} 1187 1188static void 1189ipfw_nat_destroy(void) 1190{ 1191 1192 EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag); 1193 /* deregister ipfw_nat */ 1194 IPFW_DEL_SOPT_HANDLER(1, scodes); 1195 ipfw_nat_ptr = NULL; 1196 lookup_nat_ptr = NULL; 1197 ipfw_nat_cfg_ptr = NULL; 1198 ipfw_nat_del_ptr = NULL; 1199 ipfw_nat_get_cfg_ptr = NULL; 1200 ipfw_nat_get_log_ptr = NULL; 1201} 1202 1203static int 1204ipfw_nat_modevent(module_t mod, int type, void *unused) 1205{ 1206 int err = 0; 1207 1208 switch (type) { 1209 case MOD_LOAD: 1210 break; 1211 1212 case MOD_UNLOAD: 1213 break; 1214 1215 default: 1216 return EOPNOTSUPP; 1217 break; 1218 } 1219 return err; 1220} 1221 1222static moduledata_t ipfw_nat_mod = { 1223 "ipfw_nat", 1224 ipfw_nat_modevent, 1225 0 1226}; 1227 1228/* Define startup order. */ 1229#define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_FIREWALL 1230#define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 128) /* after ipfw */ 1231#define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1) 1232#define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2) 1233 1234DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY); 1235MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); 1236MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3); 1237MODULE_VERSION(ipfw_nat, 1); 1238 1239SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 1240 ipfw_nat_init, NULL); 1241VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, 1242 vnet_ipfw_nat_init, NULL); 1243 1244SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 1245 ipfw_nat_destroy, NULL); 1246VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL, 1247 IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL); 1248 1249/* end of file */ 1250