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 __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/if_private.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 60 struct 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. */ 67 struct 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. */ 86 struct 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 101 static eventhandler_tag ifaddr_event_tag; 102 103 static void 104 ifaddr_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 struct epoch_tracker et; 121 122 /* ...using nic 'ifp->if_xname' as dynamic alias address. */ 123 if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) 124 continue; 125 NET_EPOCH_ENTER(et); 126 CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 127 if (ifa->ifa_addr == NULL) 128 continue; 129 if (ifa->ifa_addr->sa_family != AF_INET) 130 continue; 131 IPFW_WLOCK(chain); 132 ptr->ip = ((struct sockaddr_in *) 133 (ifa->ifa_addr))->sin_addr; 134 LibAliasSetAddress(ptr->lib, ptr->ip); 135 IPFW_WUNLOCK(chain); 136 } 137 NET_EPOCH_EXIT(et); 138 } 139 IPFW_UH_WUNLOCK(chain); 140 } 141 142 /* 143 * delete the pointers for nat entry ix, or all of them if ix < 0 144 */ 145 static void 146 flush_nat_ptrs(struct ip_fw_chain *chain, const int ix) 147 { 148 ipfw_insn_nat *cmd; 149 int i; 150 151 IPFW_WLOCK_ASSERT(chain); 152 for (i = 0; i < chain->n_rules; i++) { 153 cmd = (ipfw_insn_nat *)ipfw_get_action(chain->map[i]); 154 if (cmd->o.opcode == O_NAT && cmd->nat != NULL && 155 (ix < 0 || cmd->nat->id == ix)) 156 cmd->nat = NULL; 157 } 158 } 159 160 static void 161 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) 162 { 163 struct cfg_redir *r, *tmp_r; 164 struct cfg_spool *s, *tmp_s; 165 int i, num; 166 167 LIST_FOREACH_SAFE(r, head, _next, tmp_r) { 168 num = 1; /* Number of alias_link to delete. */ 169 switch (r->mode) { 170 case NAT44_REDIR_PORT: 171 num = r->pport_cnt; 172 /* FALLTHROUGH */ 173 case NAT44_REDIR_ADDR: 174 case NAT44_REDIR_PROTO: 175 /* Delete all libalias redirect entry. */ 176 for (i = 0; i < num; i++) 177 LibAliasRedirectDelete(n->lib, r->alink[i]); 178 /* Del spool cfg if any. */ 179 LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) { 180 LIST_REMOVE(s, _next); 181 free(s, M_IPFW); 182 } 183 free(r->alink, M_IPFW); 184 LIST_REMOVE(r, _next); 185 free(r, M_IPFW); 186 break; 187 default: 188 printf("unknown redirect mode: %u\n", r->mode); 189 /* XXX - panic?!?!? */ 190 break; 191 } 192 } 193 } 194 195 static int 196 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) 197 { 198 struct cfg_redir *r; 199 struct cfg_spool *s; 200 struct nat44_cfg_redir *ser_r; 201 struct nat44_cfg_spool *ser_s; 202 203 int cnt, off, i; 204 205 for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { 206 ser_r = (struct nat44_cfg_redir *)&buf[off]; 207 r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO); 208 r->mode = ser_r->mode; 209 r->laddr = ser_r->laddr; 210 r->paddr = ser_r->paddr; 211 r->raddr = ser_r->raddr; 212 r->lport = ser_r->lport; 213 r->pport = ser_r->pport; 214 r->rport = ser_r->rport; 215 r->pport_cnt = ser_r->pport_cnt; 216 r->rport_cnt = ser_r->rport_cnt; 217 r->proto = ser_r->proto; 218 r->spool_cnt = ser_r->spool_cnt; 219 //memcpy(r, ser_r, SOF_REDIR); 220 LIST_INIT(&r->spool_chain); 221 off += sizeof(struct nat44_cfg_redir); 222 r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, 223 M_IPFW, M_WAITOK | M_ZERO); 224 switch (r->mode) { 225 case NAT44_REDIR_ADDR: 226 r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, 227 r->paddr); 228 break; 229 case NAT44_REDIR_PORT: 230 for (i = 0 ; i < r->pport_cnt; i++) { 231 /* If remotePort is all ports, set it to 0. */ 232 u_short remotePortCopy = r->rport + i; 233 if (r->rport_cnt == 1 && r->rport == 0) 234 remotePortCopy = 0; 235 r->alink[i] = LibAliasRedirectPort(ptr->lib, 236 r->laddr, htons(r->lport + i), r->raddr, 237 htons(remotePortCopy), r->paddr, 238 htons(r->pport + i), r->proto); 239 if (r->alink[i] == NULL) { 240 r->alink[0] = NULL; 241 break; 242 } 243 } 244 break; 245 case NAT44_REDIR_PROTO: 246 r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, 247 r->raddr, r->paddr, r->proto); 248 break; 249 default: 250 printf("unknown redirect mode: %u\n", r->mode); 251 break; 252 } 253 if (r->alink[0] == NULL) { 254 printf("LibAliasRedirect* returned NULL\n"); 255 free(r->alink, M_IPFW); 256 free(r, M_IPFW); 257 return (EINVAL); 258 } 259 /* LSNAT handling. */ 260 for (i = 0; i < r->spool_cnt; i++) { 261 ser_s = (struct nat44_cfg_spool *)&buf[off]; 262 s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO); 263 s->addr = ser_s->addr; 264 s->port = ser_s->port; 265 LibAliasAddServer(ptr->lib, r->alink[0], 266 s->addr, htons(s->port)); 267 off += sizeof(struct nat44_cfg_spool); 268 /* Hook spool entry. */ 269 LIST_INSERT_HEAD(&r->spool_chain, s, _next); 270 } 271 /* And finally hook this redir entry. */ 272 LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); 273 } 274 275 return (0); 276 } 277 278 static void 279 free_nat_instance(struct cfg_nat *ptr) 280 { 281 282 del_redir_spool_cfg(ptr, &ptr->redir_chain); 283 LibAliasUninit(ptr->lib); 284 free(ptr, M_IPFW); 285 } 286 287 /* 288 * ipfw_nat - perform mbuf header translation. 289 * 290 * Note V_layer3_chain has to be locked while calling ipfw_nat() in 291 * 'global' operation mode (t == NULL). 292 * 293 */ 294 static int 295 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) 296 { 297 struct mbuf *mcl; 298 struct ip *ip; 299 /* XXX - libalias duct tape */ 300 int ldt, retval, found; 301 struct ip_fw_chain *chain; 302 char *c; 303 304 ldt = 0; 305 retval = 0; 306 mcl = m_megapullup(m, m->m_pkthdr.len); 307 if (mcl == NULL) { 308 args->m = NULL; 309 return (IP_FW_DENY); 310 } 311 M_ASSERTMAPPED(mcl); 312 ip = mtod(mcl, struct ip *); 313 314 /* 315 * XXX - Libalias checksum offload 'duct tape': 316 * 317 * locally generated packets have only pseudo-header checksum 318 * calculated and libalias will break it[1], so mark them for 319 * later fix. Moreover there are cases when libalias modifies 320 * tcp packet data[2], mark them for later fix too. 321 * 322 * [1] libalias was never meant to run in kernel, so it does 323 * not have any knowledge about checksum offloading, and 324 * expects a packet with a full internet checksum. 325 * Unfortunately, packets generated locally will have just the 326 * pseudo header calculated, and when libalias tries to adjust 327 * the checksum it will actually compute a wrong value. 328 * 329 * [2] when libalias modifies tcp's data content, full TCP 330 * checksum has to be recomputed: the problem is that 331 * libalias does not have any idea about checksum offloading. 332 * To work around this, we do not do checksumming in LibAlias, 333 * but only mark the packets in th_x2 field. If we receive a 334 * marked packet, we calculate correct checksum for it 335 * aware of offloading. Why such a terrible hack instead of 336 * recalculating checksum for each packet? 337 * Because the previous checksum was not checked! 338 * Recalculating checksums for EVERY packet will hide ALL 339 * transmission errors. Yes, marked packets still suffer from 340 * this problem. But, sigh, natd(8) has this problem, too. 341 * 342 * TODO: -make libalias mbuf aware (so 343 * it can handle delayed checksum and tso) 344 */ 345 346 if (mcl->m_pkthdr.rcvif == NULL && 347 mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) 348 ldt = 1; 349 350 c = mtod(mcl, char *); 351 352 /* Check if this is 'global' instance */ 353 if (t == NULL) { 354 if (args->flags & IPFW_ARGS_IN) { 355 /* Wrong direction, skip processing */ 356 args->m = mcl; 357 return (IP_FW_NAT); 358 } 359 360 found = 0; 361 chain = &V_layer3_chain; 362 IPFW_RLOCK_ASSERT(chain); 363 /* Check every nat entry... */ 364 LIST_FOREACH(t, &chain->nat, _next) { 365 if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) 366 continue; 367 retval = LibAliasOutTry(t->lib, c, 368 mcl->m_len + M_TRAILINGSPACE(mcl), 0); 369 if (retval == PKT_ALIAS_OK) { 370 /* Nat instance recognises state */ 371 found = 1; 372 break; 373 } 374 } 375 if (found != 1) { 376 /* No instance found, return ignore */ 377 args->m = mcl; 378 return (IP_FW_NAT); 379 } 380 } else { 381 if (args->flags & IPFW_ARGS_IN) 382 retval = LibAliasIn(t->lib, c, 383 mcl->m_len + M_TRAILINGSPACE(mcl)); 384 else 385 retval = LibAliasOut(t->lib, c, 386 mcl->m_len + M_TRAILINGSPACE(mcl)); 387 } 388 389 /* 390 * We drop packet when: 391 * 1. libalias returns PKT_ALIAS_ERROR; 392 * 2. For incoming packets: 393 * a) for unresolved fragments; 394 * b) libalias returns PKT_ALIAS_IGNORED and 395 * PKT_ALIAS_DENY_INCOMING flag is set. 396 */ 397 if (retval == PKT_ALIAS_ERROR || 398 ((args->flags & IPFW_ARGS_IN) && 399 (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT || 400 (retval == PKT_ALIAS_IGNORED && 401 (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) { 402 /* XXX - should i add some logging? */ 403 m_free(mcl); 404 args->m = NULL; 405 return (IP_FW_DENY); 406 } 407 408 if (retval == PKT_ALIAS_RESPOND) 409 mcl->m_flags |= M_SKIP_FIREWALL; 410 mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); 411 412 /* 413 * XXX - libalias checksum offload 414 * 'duct tape' (see above) 415 */ 416 417 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 418 ip->ip_p == IPPROTO_TCP) { 419 struct tcphdr *th; 420 421 th = (struct tcphdr *)(ip + 1); 422 if (th->th_x2 & (TH_RES1 >> 8)) 423 ldt = 1; 424 } 425 426 if (ldt) { 427 struct tcphdr *th; 428 struct udphdr *uh; 429 uint16_t ip_len, cksum; 430 431 ip_len = ntohs(ip->ip_len); 432 cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 433 htons(ip->ip_p + ip_len - (ip->ip_hl << 2))); 434 435 switch (ip->ip_p) { 436 case IPPROTO_TCP: 437 th = (struct tcphdr *)(ip + 1); 438 /* 439 * Maybe it was set in 440 * libalias... 441 */ 442 th->th_x2 &= ~(TH_RES1 >> 8); 443 th->th_sum = cksum; 444 mcl->m_pkthdr.csum_data = 445 offsetof(struct tcphdr, th_sum); 446 break; 447 case IPPROTO_UDP: 448 uh = (struct udphdr *)(ip + 1); 449 uh->uh_sum = cksum; 450 mcl->m_pkthdr.csum_data = 451 offsetof(struct udphdr, uh_sum); 452 break; 453 } 454 /* No hw checksum offloading: do it ourselves */ 455 if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { 456 in_delayed_cksum(mcl); 457 mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 458 } 459 } 460 args->m = mcl; 461 return (IP_FW_NAT); 462 } 463 464 static struct cfg_nat * 465 lookup_nat(struct nat_list *l, int nat_id) 466 { 467 struct cfg_nat *res; 468 469 LIST_FOREACH(res, l, _next) { 470 if (res->id == nat_id) 471 break; 472 } 473 return res; 474 } 475 476 static struct cfg_nat * 477 lookup_nat_name(struct nat_list *l, char *name) 478 { 479 struct cfg_nat *res; 480 int id; 481 char *errptr; 482 483 id = strtol(name, &errptr, 10); 484 if (id == 0 || *errptr != '\0') 485 return (NULL); 486 487 LIST_FOREACH(res, l, _next) { 488 if (res->id == id) 489 break; 490 } 491 return (res); 492 } 493 494 /* IP_FW3 configuration routines */ 495 496 static void 497 nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg) 498 { 499 struct cfg_nat *ptr, *tcfg; 500 int gencnt; 501 502 /* 503 * Find/create nat rule. 504 */ 505 IPFW_UH_WLOCK(chain); 506 gencnt = chain->gencnt; 507 ptr = lookup_nat_name(&chain->nat, ucfg->name); 508 if (ptr == NULL) { 509 IPFW_UH_WUNLOCK(chain); 510 /* New rule: allocate and init new instance. */ 511 ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); 512 ptr->lib = LibAliasInit(NULL); 513 LIST_INIT(&ptr->redir_chain); 514 } else { 515 /* Entry already present: temporarily unhook it. */ 516 IPFW_WLOCK(chain); 517 LIST_REMOVE(ptr, _next); 518 flush_nat_ptrs(chain, ptr->id); 519 IPFW_WUNLOCK(chain); 520 IPFW_UH_WUNLOCK(chain); 521 } 522 523 /* 524 * Basic nat (re)configuration. 525 */ 526 ptr->id = strtol(ucfg->name, NULL, 10); 527 /* 528 * XXX - what if this rule doesn't nat any ip and just 529 * redirect? 530 * do we set aliasaddress to 0.0.0.0? 531 */ 532 ptr->ip = ucfg->ip; 533 ptr->redir_cnt = ucfg->redir_cnt; 534 ptr->mode = ucfg->mode; 535 ptr->alias_port_lo = ucfg->alias_port_lo; 536 ptr->alias_port_hi = ucfg->alias_port_hi; 537 strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name)); 538 LibAliasSetMode(ptr->lib, ptr->mode, ~0); 539 LibAliasSetAddress(ptr->lib, ptr->ip); 540 LibAliasSetAliasPortRange(ptr->lib, ptr->alias_port_lo, ptr->alias_port_hi); 541 542 /* 543 * Redir and LSNAT configuration. 544 */ 545 /* Delete old cfgs. */ 546 del_redir_spool_cfg(ptr, &ptr->redir_chain); 547 /* Add new entries. */ 548 add_redir_spool_cfg((char *)(ucfg + 1), ptr); 549 IPFW_UH_WLOCK(chain); 550 551 /* Extra check to avoid race with another ipfw_nat_cfg() */ 552 tcfg = NULL; 553 if (gencnt != chain->gencnt) 554 tcfg = lookup_nat_name(&chain->nat, ucfg->name); 555 IPFW_WLOCK(chain); 556 if (tcfg != NULL) 557 LIST_REMOVE(tcfg, _next); 558 LIST_INSERT_HEAD(&chain->nat, ptr, _next); 559 IPFW_WUNLOCK(chain); 560 chain->gencnt++; 561 562 IPFW_UH_WUNLOCK(chain); 563 564 if (tcfg != NULL) 565 free_nat_instance(ptr); 566 } 567 568 /* 569 * Creates/configure nat44 instance 570 * Data layout (v0)(current): 571 * Request: [ ipfw_obj_header nat44_cfg_nat .. ] 572 * 573 * Returns 0 on success 574 */ 575 static int 576 nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 577 struct sockopt_data *sd) 578 { 579 ipfw_obj_header *oh; 580 struct nat44_cfg_nat *ucfg; 581 int id; 582 size_t read; 583 char *errptr; 584 585 /* Check minimum header size */ 586 if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg))) 587 return (EINVAL); 588 589 oh = (ipfw_obj_header *)sd->kbuf; 590 591 /* Basic length checks for TLVs */ 592 if (oh->ntlv.head.length != sizeof(oh->ntlv)) 593 return (EINVAL); 594 595 ucfg = (struct nat44_cfg_nat *)(oh + 1); 596 597 /* Check if name is properly terminated and looks like number */ 598 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 599 return (EINVAL); 600 id = strtol(ucfg->name, &errptr, 10); 601 if (id == 0 || *errptr != '\0') 602 return (EINVAL); 603 604 read = sizeof(*oh) + sizeof(*ucfg); 605 /* Check number of redirs */ 606 if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir)) 607 return (EINVAL); 608 609 nat44_config(chain, ucfg); 610 return (0); 611 } 612 613 /* 614 * Destroys given nat instances. 615 * Data layout (v0)(current): 616 * Request: [ ipfw_obj_header ] 617 * 618 * Returns 0 on success 619 */ 620 static int 621 nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 622 struct sockopt_data *sd) 623 { 624 ipfw_obj_header *oh; 625 struct cfg_nat *ptr; 626 ipfw_obj_ntlv *ntlv; 627 628 /* Check minimum header size */ 629 if (sd->valsize < sizeof(*oh)) 630 return (EINVAL); 631 632 oh = (ipfw_obj_header *)sd->kbuf; 633 634 /* Basic length checks for TLVs */ 635 if (oh->ntlv.head.length != sizeof(oh->ntlv)) 636 return (EINVAL); 637 638 ntlv = &oh->ntlv; 639 /* Check if name is properly terminated */ 640 if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name)) 641 return (EINVAL); 642 643 IPFW_UH_WLOCK(chain); 644 ptr = lookup_nat_name(&chain->nat, ntlv->name); 645 if (ptr == NULL) { 646 IPFW_UH_WUNLOCK(chain); 647 return (ESRCH); 648 } 649 IPFW_WLOCK(chain); 650 LIST_REMOVE(ptr, _next); 651 flush_nat_ptrs(chain, ptr->id); 652 IPFW_WUNLOCK(chain); 653 IPFW_UH_WUNLOCK(chain); 654 655 free_nat_instance(ptr); 656 657 return (0); 658 } 659 660 static void 661 export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg) 662 { 663 664 snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id); 665 ucfg->ip = ptr->ip; 666 ucfg->redir_cnt = ptr->redir_cnt; 667 ucfg->mode = ptr->mode; 668 ucfg->alias_port_lo = ptr->alias_port_lo; 669 ucfg->alias_port_hi = ptr->alias_port_hi; 670 strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name)); 671 } 672 673 /* 674 * Gets config for given nat instance 675 * Data layout (v0)(current): 676 * Request: [ ipfw_obj_header nat44_cfg_nat .. ] 677 * 678 * Returns 0 on success 679 */ 680 static int 681 nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 682 struct sockopt_data *sd) 683 { 684 ipfw_obj_header *oh; 685 struct nat44_cfg_nat *ucfg; 686 struct cfg_nat *ptr; 687 struct cfg_redir *r; 688 struct cfg_spool *s; 689 struct nat44_cfg_redir *ser_r; 690 struct nat44_cfg_spool *ser_s; 691 size_t sz; 692 693 sz = sizeof(*oh) + sizeof(*ucfg); 694 /* Check minimum header size */ 695 if (sd->valsize < sz) 696 return (EINVAL); 697 698 oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 699 700 /* Basic length checks for TLVs */ 701 if (oh->ntlv.head.length != sizeof(oh->ntlv)) 702 return (EINVAL); 703 704 ucfg = (struct nat44_cfg_nat *)(oh + 1); 705 706 /* Check if name is properly terminated */ 707 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 708 return (EINVAL); 709 710 IPFW_UH_RLOCK(chain); 711 ptr = lookup_nat_name(&chain->nat, ucfg->name); 712 if (ptr == NULL) { 713 IPFW_UH_RUNLOCK(chain); 714 return (ESRCH); 715 } 716 717 export_nat_cfg(ptr, ucfg); 718 719 /* Estimate memory amount */ 720 sz = sizeof(ipfw_obj_header) + sizeof(struct nat44_cfg_nat); 721 LIST_FOREACH(r, &ptr->redir_chain, _next) { 722 sz += sizeof(struct nat44_cfg_redir); 723 LIST_FOREACH(s, &r->spool_chain, _next) 724 sz += sizeof(struct nat44_cfg_spool); 725 } 726 727 ucfg->size = sz; 728 if (sd->valsize < sz) { 729 /* 730 * Submitted buffer size is not enough. 731 * WE've already filled in @ucfg structure with 732 * relevant info including size, so we 733 * can return. Buffer will be flushed automatically. 734 */ 735 IPFW_UH_RUNLOCK(chain); 736 return (ENOMEM); 737 } 738 739 /* Size OK, let's copy data */ 740 LIST_FOREACH(r, &ptr->redir_chain, _next) { 741 ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd, 742 sizeof(*ser_r)); 743 ser_r->mode = r->mode; 744 ser_r->laddr = r->laddr; 745 ser_r->paddr = r->paddr; 746 ser_r->raddr = r->raddr; 747 ser_r->lport = r->lport; 748 ser_r->pport = r->pport; 749 ser_r->rport = r->rport; 750 ser_r->pport_cnt = r->pport_cnt; 751 ser_r->rport_cnt = r->rport_cnt; 752 ser_r->proto = r->proto; 753 ser_r->spool_cnt = r->spool_cnt; 754 755 LIST_FOREACH(s, &r->spool_chain, _next) { 756 ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space( 757 sd, sizeof(*ser_s)); 758 759 ser_s->addr = s->addr; 760 ser_s->port = s->port; 761 } 762 } 763 764 IPFW_UH_RUNLOCK(chain); 765 766 return (0); 767 } 768 769 /* 770 * Lists all nat44 instances currently available in kernel. 771 * Data layout (v0)(current): 772 * Request: [ ipfw_obj_lheader ] 773 * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ] 774 * 775 * Returns 0 on success 776 */ 777 static int 778 nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 779 struct sockopt_data *sd) 780 { 781 ipfw_obj_lheader *olh; 782 struct nat44_cfg_nat *ucfg; 783 struct cfg_nat *ptr; 784 int nat_count; 785 786 /* Check minimum header size */ 787 if (sd->valsize < sizeof(ipfw_obj_lheader)) 788 return (EINVAL); 789 790 olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); 791 IPFW_UH_RLOCK(chain); 792 nat_count = 0; 793 LIST_FOREACH(ptr, &chain->nat, _next) 794 nat_count++; 795 796 olh->count = nat_count; 797 olh->objsize = sizeof(struct nat44_cfg_nat); 798 olh->size = sizeof(*olh) + olh->count * olh->objsize; 799 800 if (sd->valsize < olh->size) { 801 IPFW_UH_RUNLOCK(chain); 802 return (ENOMEM); 803 } 804 805 LIST_FOREACH(ptr, &chain->nat, _next) { 806 ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd, 807 sizeof(*ucfg)); 808 export_nat_cfg(ptr, ucfg); 809 } 810 811 IPFW_UH_RUNLOCK(chain); 812 813 return (0); 814 } 815 816 /* 817 * Gets log for given nat instance 818 * Data layout (v0)(current): 819 * Request: [ ipfw_obj_header nat44_cfg_nat ] 820 * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ] 821 * 822 * Returns 0 on success 823 */ 824 static int 825 nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 826 struct sockopt_data *sd) 827 { 828 ipfw_obj_header *oh; 829 struct nat44_cfg_nat *ucfg; 830 struct cfg_nat *ptr; 831 void *pbuf; 832 size_t sz; 833 834 sz = sizeof(*oh) + sizeof(*ucfg); 835 /* Check minimum header size */ 836 if (sd->valsize < sz) 837 return (EINVAL); 838 839 oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 840 841 /* Basic length checks for TLVs */ 842 if (oh->ntlv.head.length != sizeof(oh->ntlv)) 843 return (EINVAL); 844 845 ucfg = (struct nat44_cfg_nat *)(oh + 1); 846 847 /* Check if name is properly terminated */ 848 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 849 return (EINVAL); 850 851 IPFW_UH_RLOCK(chain); 852 ptr = lookup_nat_name(&chain->nat, ucfg->name); 853 if (ptr == NULL) { 854 IPFW_UH_RUNLOCK(chain); 855 return (ESRCH); 856 } 857 858 if (ptr->lib->logDesc == NULL) { 859 IPFW_UH_RUNLOCK(chain); 860 return (ENOENT); 861 } 862 863 export_nat_cfg(ptr, ucfg); 864 865 /* Estimate memory amount */ 866 ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE; 867 if (sd->valsize < sz + sizeof(*oh)) { 868 /* 869 * Submitted buffer size is not enough. 870 * WE've already filled in @ucfg structure with 871 * relevant info including size, so we 872 * can return. Buffer will be flushed automatically. 873 */ 874 IPFW_UH_RUNLOCK(chain); 875 return (ENOMEM); 876 } 877 878 pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE); 879 memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE); 880 881 IPFW_UH_RUNLOCK(chain); 882 883 return (0); 884 } 885 886 static struct ipfw_sopt_handler scodes[] = { 887 { IP_FW_NAT44_XCONFIG, 0, HDIR_SET, nat44_cfg }, 888 { IP_FW_NAT44_DESTROY, 0, HDIR_SET, nat44_destroy }, 889 { IP_FW_NAT44_XGETCONFIG, 0, HDIR_GET, nat44_get_cfg }, 890 { IP_FW_NAT44_LIST_NAT, 0, HDIR_GET, nat44_list_nat }, 891 { IP_FW_NAT44_XGETLOG, 0, HDIR_GET, nat44_get_log }, 892 }; 893 894 /* 895 * Legacy configuration routines 896 */ 897 898 struct cfg_spool_legacy { 899 LIST_ENTRY(cfg_spool_legacy) _next; 900 struct in_addr addr; 901 u_short port; 902 }; 903 904 struct 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 921 struct 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 932 static int 933 ipfw_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 995 out: 996 free(buf, M_TEMP); 997 return (error); 998 } 999 1000 static int 1001 ipfw_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 1024 static int 1025 ipfw_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); 1041 retry: 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 1105 static int 1106 ipfw_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 1145 static int 1146 vnet_ipfw_nat_init(const void *arg __unused) 1147 { 1148 1149 V_ipfw_nat_ready = 1; 1150 return (0); 1151 } 1152 1153 static int 1154 vnet_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 1171 static void 1172 ipfw_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 1188 static void 1189 ipfw_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 1203 static int 1204 ipfw_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 1222 static 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 1234 DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY); 1235 MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); 1236 MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3); 1237 MODULE_VERSION(ipfw_nat, 1); 1238 1239 SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 1240 ipfw_nat_init, NULL); 1241 VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, 1242 vnet_ipfw_nat_init, NULL); 1243 1244 SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 1245 ipfw_nat_destroy, NULL); 1246 VNET_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