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