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