1 /*- 2 * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org> 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 * $FreeBSD$ 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/kernel.h> 32 #include <sys/mbuf.h> 33 #include <sys/malloc.h> 34 #include <sys/ctype.h> 35 #include <sys/errno.h> 36 #include <sys/syslog.h> 37 38 #include <netinet/in_systm.h> 39 #include <netinet/in.h> 40 #include <netinet/ip.h> 41 #include <netinet/ip_var.h> 42 #include <netinet/tcp.h> 43 #include <machine/in_cksum.h> 44 45 #include <netinet/libalias/alias.h> 46 47 #include <netgraph/ng_message.h> 48 #include <netgraph/ng_parse.h> 49 #include <netgraph/ng_nat.h> 50 #include <netgraph/netgraph.h> 51 52 static ng_constructor_t ng_nat_constructor; 53 static ng_rcvmsg_t ng_nat_rcvmsg; 54 static ng_shutdown_t ng_nat_shutdown; 55 static ng_newhook_t ng_nat_newhook; 56 static ng_rcvdata_t ng_nat_rcvdata; 57 static ng_disconnect_t ng_nat_disconnect; 58 59 static unsigned int ng_nat_translate_flags(unsigned int x); 60 61 /* Parse type for struct ng_nat_mode. */ 62 static const struct ng_parse_struct_field ng_nat_mode_fields[] 63 = NG_NAT_MODE_INFO; 64 static const struct ng_parse_type ng_nat_mode_type = { 65 &ng_parse_struct_type, 66 &ng_nat_mode_fields 67 }; 68 69 /* Parse type for 'description' field in structs. */ 70 static const struct ng_parse_fixedstring_info ng_nat_description_info 71 = { NG_NAT_DESC_LENGTH }; 72 static const struct ng_parse_type ng_nat_description_type = { 73 &ng_parse_fixedstring_type, 74 &ng_nat_description_info 75 }; 76 77 /* Parse type for struct ng_nat_redirect_port. */ 78 static const struct ng_parse_struct_field ng_nat_redirect_port_fields[] 79 = NG_NAT_REDIRECT_PORT_TYPE_INFO(&ng_nat_description_type); 80 static const struct ng_parse_type ng_nat_redirect_port_type = { 81 &ng_parse_struct_type, 82 &ng_nat_redirect_port_fields 83 }; 84 85 /* Parse type for struct ng_nat_redirect_addr. */ 86 static const struct ng_parse_struct_field ng_nat_redirect_addr_fields[] 87 = NG_NAT_REDIRECT_ADDR_TYPE_INFO(&ng_nat_description_type); 88 static const struct ng_parse_type ng_nat_redirect_addr_type = { 89 &ng_parse_struct_type, 90 &ng_nat_redirect_addr_fields 91 }; 92 93 /* Parse type for struct ng_nat_redirect_proto. */ 94 static const struct ng_parse_struct_field ng_nat_redirect_proto_fields[] 95 = NG_NAT_REDIRECT_PROTO_TYPE_INFO(&ng_nat_description_type); 96 static const struct ng_parse_type ng_nat_redirect_proto_type = { 97 &ng_parse_struct_type, 98 &ng_nat_redirect_proto_fields 99 }; 100 101 /* Parse type for struct ng_nat_add_server. */ 102 static const struct ng_parse_struct_field ng_nat_add_server_fields[] 103 = NG_NAT_ADD_SERVER_TYPE_INFO; 104 static const struct ng_parse_type ng_nat_add_server_type = { 105 &ng_parse_struct_type, 106 &ng_nat_add_server_fields 107 }; 108 109 /* Parse type for one struct ng_nat_listrdrs_entry. */ 110 static const struct ng_parse_struct_field ng_nat_listrdrs_entry_fields[] 111 = NG_NAT_LISTRDRS_ENTRY_TYPE_INFO(&ng_nat_description_type); 112 static const struct ng_parse_type ng_nat_listrdrs_entry_type = { 113 &ng_parse_struct_type, 114 &ng_nat_listrdrs_entry_fields 115 }; 116 117 /* Parse type for 'redirects' array in struct ng_nat_list_redirects. */ 118 static int 119 ng_nat_listrdrs_ary_getLength(const struct ng_parse_type *type, 120 const u_char *start, const u_char *buf) 121 { 122 const struct ng_nat_list_redirects *lr; 123 124 lr = (const struct ng_nat_list_redirects *) 125 (buf - offsetof(struct ng_nat_list_redirects, redirects)); 126 return lr->total_count; 127 } 128 129 static const struct ng_parse_array_info ng_nat_listrdrs_ary_info = { 130 &ng_nat_listrdrs_entry_type, 131 &ng_nat_listrdrs_ary_getLength, 132 NULL 133 }; 134 static const struct ng_parse_type ng_nat_listrdrs_ary_type = { 135 &ng_parse_array_type, 136 &ng_nat_listrdrs_ary_info 137 }; 138 139 /* Parse type for struct ng_nat_list_redirects. */ 140 static const struct ng_parse_struct_field ng_nat_list_redirects_fields[] 141 = NG_NAT_LIST_REDIRECTS_TYPE_INFO(&ng_nat_listrdrs_ary_type); 142 static const struct ng_parse_type ng_nat_list_redirects_type = { 143 &ng_parse_struct_type, 144 &ng_nat_list_redirects_fields 145 }; 146 147 /* List of commands and how to convert arguments to/from ASCII. */ 148 static const struct ng_cmdlist ng_nat_cmdlist[] = { 149 { 150 NGM_NAT_COOKIE, 151 NGM_NAT_SET_IPADDR, 152 "setaliasaddr", 153 &ng_parse_ipaddr_type, 154 NULL 155 }, 156 { 157 NGM_NAT_COOKIE, 158 NGM_NAT_SET_MODE, 159 "setmode", 160 &ng_nat_mode_type, 161 NULL 162 }, 163 { 164 NGM_NAT_COOKIE, 165 NGM_NAT_SET_TARGET, 166 "settarget", 167 &ng_parse_ipaddr_type, 168 NULL 169 }, 170 { 171 NGM_NAT_COOKIE, 172 NGM_NAT_REDIRECT_PORT, 173 "redirectport", 174 &ng_nat_redirect_port_type, 175 &ng_parse_uint32_type 176 }, 177 { 178 NGM_NAT_COOKIE, 179 NGM_NAT_REDIRECT_ADDR, 180 "redirectaddr", 181 &ng_nat_redirect_addr_type, 182 &ng_parse_uint32_type 183 }, 184 { 185 NGM_NAT_COOKIE, 186 NGM_NAT_REDIRECT_PROTO, 187 "redirectproto", 188 &ng_nat_redirect_proto_type, 189 &ng_parse_uint32_type 190 }, 191 { 192 NGM_NAT_COOKIE, 193 NGM_NAT_REDIRECT_DYNAMIC, 194 "redirectdynamic", 195 &ng_parse_uint32_type, 196 NULL 197 }, 198 { 199 NGM_NAT_COOKIE, 200 NGM_NAT_REDIRECT_DELETE, 201 "redirectdelete", 202 &ng_parse_uint32_type, 203 NULL 204 }, 205 { 206 NGM_NAT_COOKIE, 207 NGM_NAT_ADD_SERVER, 208 "addserver", 209 &ng_nat_add_server_type, 210 NULL 211 }, 212 { 213 NGM_NAT_COOKIE, 214 NGM_NAT_LIST_REDIRECTS, 215 "listredirects", 216 NULL, 217 &ng_nat_list_redirects_type 218 }, 219 { 220 NGM_NAT_COOKIE, 221 NGM_NAT_PROXY_RULE, 222 "proxyrule", 223 &ng_parse_string_type, 224 NULL 225 }, 226 { 0 } 227 }; 228 229 /* Netgraph node type descriptor. */ 230 static struct ng_type typestruct = { 231 .version = NG_ABI_VERSION, 232 .name = NG_NAT_NODE_TYPE, 233 .constructor = ng_nat_constructor, 234 .rcvmsg = ng_nat_rcvmsg, 235 .shutdown = ng_nat_shutdown, 236 .newhook = ng_nat_newhook, 237 .rcvdata = ng_nat_rcvdata, 238 .disconnect = ng_nat_disconnect, 239 .cmdlist = ng_nat_cmdlist, 240 }; 241 NETGRAPH_INIT(nat, &typestruct); 242 MODULE_DEPEND(ng_nat, libalias, 1, 1, 1); 243 244 /* Element for list of redirects. */ 245 struct ng_nat_rdr_lst { 246 STAILQ_ENTRY(ng_nat_rdr_lst) entries; 247 struct alias_link *lnk; 248 struct ng_nat_listrdrs_entry rdr; 249 }; 250 STAILQ_HEAD(rdrhead, ng_nat_rdr_lst); 251 252 /* Information we store for each node. */ 253 struct ng_nat_priv { 254 node_p node; /* back pointer to node */ 255 hook_p in; /* hook for demasquerading */ 256 hook_p out; /* hook for masquerading */ 257 struct libalias *lib; /* libalias handler */ 258 uint32_t flags; /* status flags */ 259 uint32_t rdrcount; /* number or redirects in list */ 260 uint32_t nextid; /* for next in turn in list */ 261 struct rdrhead redirhead; /* redirect list header */ 262 }; 263 typedef struct ng_nat_priv *priv_p; 264 265 /* Values of flags */ 266 #define NGNAT_CONNECTED 0x1 /* We have both hooks connected */ 267 #define NGNAT_ADDR_DEFINED 0x2 /* NGM_NAT_SET_IPADDR happened */ 268 269 static int 270 ng_nat_constructor(node_p node) 271 { 272 priv_p priv; 273 274 /* Initialize private descriptor. */ 275 priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 276 277 /* Init aliasing engine. */ 278 priv->lib = LibAliasInit(NULL); 279 280 /* Set same ports on. */ 281 (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS, 282 PKT_ALIAS_SAME_PORTS); 283 284 /* Init redirects housekeeping. */ 285 priv->rdrcount = 0; 286 priv->nextid = 1; 287 STAILQ_INIT(&priv->redirhead); 288 289 /* Link structs together. */ 290 NG_NODE_SET_PRIVATE(node, priv); 291 priv->node = node; 292 293 /* 294 * libalias is not thread safe, so our node 295 * must be single threaded. 296 */ 297 NG_NODE_FORCE_WRITER(node); 298 299 return (0); 300 } 301 302 static int 303 ng_nat_newhook(node_p node, hook_p hook, const char *name) 304 { 305 const priv_p priv = NG_NODE_PRIVATE(node); 306 307 if (strcmp(name, NG_NAT_HOOK_IN) == 0) { 308 priv->in = hook; 309 } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) { 310 priv->out = hook; 311 } else 312 return (EINVAL); 313 314 if (priv->out != NULL && 315 priv->in != NULL) 316 priv->flags |= NGNAT_CONNECTED; 317 318 return(0); 319 } 320 321 static int 322 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook) 323 { 324 const priv_p priv = NG_NODE_PRIVATE(node); 325 struct ng_mesg *resp = NULL; 326 struct ng_mesg *msg; 327 int error = 0; 328 329 NGI_GET_MSG(item, msg); 330 331 switch (msg->header.typecookie) { 332 case NGM_NAT_COOKIE: 333 switch (msg->header.cmd) { 334 case NGM_NAT_SET_IPADDR: 335 { 336 struct in_addr *const ia = (struct in_addr *)msg->data; 337 338 if (msg->header.arglen < sizeof(*ia)) { 339 error = EINVAL; 340 break; 341 } 342 343 LibAliasSetAddress(priv->lib, *ia); 344 345 priv->flags |= NGNAT_ADDR_DEFINED; 346 } 347 break; 348 case NGM_NAT_SET_MODE: 349 { 350 struct ng_nat_mode *const mode = 351 (struct ng_nat_mode *)msg->data; 352 353 if (msg->header.arglen < sizeof(*mode)) { 354 error = EINVAL; 355 break; 356 } 357 358 if (LibAliasSetMode(priv->lib, 359 ng_nat_translate_flags(mode->flags), 360 ng_nat_translate_flags(mode->mask)) < 0) { 361 error = ENOMEM; 362 break; 363 } 364 } 365 break; 366 case NGM_NAT_SET_TARGET: 367 { 368 struct in_addr *const ia = (struct in_addr *)msg->data; 369 370 if (msg->header.arglen < sizeof(*ia)) { 371 error = EINVAL; 372 break; 373 } 374 375 LibAliasSetTarget(priv->lib, *ia); 376 } 377 break; 378 case NGM_NAT_REDIRECT_PORT: 379 { 380 struct ng_nat_rdr_lst *entry; 381 struct ng_nat_redirect_port *const rp = 382 (struct ng_nat_redirect_port *)msg->data; 383 384 if (msg->header.arglen < sizeof(*rp)) { 385 error = EINVAL; 386 break; 387 } 388 389 if ((entry = malloc(sizeof(struct ng_nat_rdr_lst), 390 M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) { 391 error = ENOMEM; 392 break; 393 } 394 395 /* Try actual redirect. */ 396 entry->lnk = LibAliasRedirectPort(priv->lib, 397 rp->local_addr, htons(rp->local_port), 398 rp->remote_addr, htons(rp->remote_port), 399 rp->alias_addr, htons(rp->alias_port), 400 rp->proto); 401 402 if (entry->lnk == NULL) { 403 error = ENOMEM; 404 free(entry, M_NETGRAPH); 405 break; 406 } 407 408 /* Successful, save info in our internal list. */ 409 entry->rdr.local_addr = rp->local_addr; 410 entry->rdr.alias_addr = rp->alias_addr; 411 entry->rdr.remote_addr = rp->remote_addr; 412 entry->rdr.local_port = rp->local_port; 413 entry->rdr.alias_port = rp->alias_port; 414 entry->rdr.remote_port = rp->remote_port; 415 entry->rdr.proto = rp->proto; 416 bcopy(rp->description, entry->rdr.description, 417 NG_NAT_DESC_LENGTH); 418 419 /* Safety precaution. */ 420 entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0'; 421 422 entry->rdr.id = priv->nextid++; 423 priv->rdrcount++; 424 425 /* Link to list of redirects. */ 426 STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries); 427 428 /* Response with id of newly added entry. */ 429 NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT); 430 if (resp == NULL) { 431 error = ENOMEM; 432 break; 433 } 434 bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id)); 435 } 436 break; 437 case NGM_NAT_REDIRECT_ADDR: 438 { 439 struct ng_nat_rdr_lst *entry; 440 struct ng_nat_redirect_addr *const ra = 441 (struct ng_nat_redirect_addr *)msg->data; 442 443 if (msg->header.arglen < sizeof(*ra)) { 444 error = EINVAL; 445 break; 446 } 447 448 if ((entry = malloc(sizeof(struct ng_nat_rdr_lst), 449 M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) { 450 error = ENOMEM; 451 break; 452 } 453 454 /* Try actual redirect. */ 455 entry->lnk = LibAliasRedirectAddr(priv->lib, 456 ra->local_addr, ra->alias_addr); 457 458 if (entry->lnk == NULL) { 459 error = ENOMEM; 460 free(entry, M_NETGRAPH); 461 break; 462 } 463 464 /* Successful, save info in our internal list. */ 465 entry->rdr.local_addr = ra->local_addr; 466 entry->rdr.alias_addr = ra->alias_addr; 467 entry->rdr.proto = NG_NAT_REDIRPROTO_ADDR; 468 bcopy(ra->description, entry->rdr.description, 469 NG_NAT_DESC_LENGTH); 470 471 /* Safety precaution. */ 472 entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0'; 473 474 entry->rdr.id = priv->nextid++; 475 priv->rdrcount++; 476 477 /* Link to list of redirects. */ 478 STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries); 479 480 /* Response with id of newly added entry. */ 481 NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT); 482 if (resp == NULL) { 483 error = ENOMEM; 484 break; 485 } 486 bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id)); 487 } 488 break; 489 case NGM_NAT_REDIRECT_PROTO: 490 { 491 struct ng_nat_rdr_lst *entry; 492 struct ng_nat_redirect_proto *const rp = 493 (struct ng_nat_redirect_proto *)msg->data; 494 495 if (msg->header.arglen < sizeof(*rp)) { 496 error = EINVAL; 497 break; 498 } 499 500 if ((entry = malloc(sizeof(struct ng_nat_rdr_lst), 501 M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) { 502 error = ENOMEM; 503 break; 504 } 505 506 /* Try actual redirect. */ 507 entry->lnk = LibAliasRedirectProto(priv->lib, 508 rp->local_addr, rp->remote_addr, 509 rp->alias_addr, rp->proto); 510 511 if (entry->lnk == NULL) { 512 error = ENOMEM; 513 free(entry, M_NETGRAPH); 514 break; 515 } 516 517 /* Successful, save info in our internal list. */ 518 entry->rdr.local_addr = rp->local_addr; 519 entry->rdr.alias_addr = rp->alias_addr; 520 entry->rdr.remote_addr = rp->remote_addr; 521 entry->rdr.proto = rp->proto; 522 bcopy(rp->description, entry->rdr.description, 523 NG_NAT_DESC_LENGTH); 524 525 /* Safety precaution. */ 526 entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0'; 527 528 entry->rdr.id = priv->nextid++; 529 priv->rdrcount++; 530 531 /* Link to list of redirects. */ 532 STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries); 533 534 /* Response with id of newly added entry. */ 535 NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT); 536 if (resp == NULL) { 537 error = ENOMEM; 538 break; 539 } 540 bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id)); 541 } 542 break; 543 case NGM_NAT_REDIRECT_DYNAMIC: 544 case NGM_NAT_REDIRECT_DELETE: 545 { 546 struct ng_nat_rdr_lst *entry; 547 uint32_t *const id = (uint32_t *)msg->data; 548 549 if (msg->header.arglen < sizeof(*id)) { 550 error = EINVAL; 551 break; 552 } 553 554 /* Find entry with supplied id. */ 555 STAILQ_FOREACH(entry, &priv->redirhead, entries) { 556 if (entry->rdr.id == *id) 557 break; 558 } 559 560 /* Not found. */ 561 if (entry == NULL) { 562 error = ENOENT; 563 break; 564 } 565 566 if (msg->header.cmd == NGM_NAT_REDIRECT_DYNAMIC) { 567 if (LibAliasRedirectDynamic(priv->lib, 568 entry->lnk) == -1) { 569 error = ENOTTY; /* XXX Something better? */ 570 break; 571 } 572 } else { /* NGM_NAT_REDIRECT_DELETE */ 573 LibAliasRedirectDelete(priv->lib, entry->lnk); 574 } 575 576 /* Delete entry from our internal list. */ 577 priv->rdrcount--; 578 STAILQ_REMOVE(&priv->redirhead, entry, ng_nat_rdr_lst, entries); 579 free(entry, M_NETGRAPH); 580 } 581 break; 582 case NGM_NAT_ADD_SERVER: 583 { 584 struct ng_nat_rdr_lst *entry; 585 struct ng_nat_add_server *const as = 586 (struct ng_nat_add_server *)msg->data; 587 588 if (msg->header.arglen < sizeof(*as)) { 589 error = EINVAL; 590 break; 591 } 592 593 /* Find entry with supplied id. */ 594 STAILQ_FOREACH(entry, &priv->redirhead, entries) { 595 if (entry->rdr.id == as->id) 596 break; 597 } 598 599 /* Not found. */ 600 if (entry == NULL) { 601 error = ENOENT; 602 break; 603 } 604 605 if (LibAliasAddServer(priv->lib, entry->lnk, 606 as->addr, htons(as->port)) == -1) { 607 error = ENOMEM; 608 break; 609 } 610 611 entry->rdr.lsnat++; 612 } 613 break; 614 case NGM_NAT_LIST_REDIRECTS: 615 { 616 struct ng_nat_rdr_lst *entry; 617 struct ng_nat_list_redirects *ary; 618 int i = 0; 619 620 NG_MKRESPONSE(resp, msg, sizeof(*ary) + 621 (priv->rdrcount) * sizeof(*entry), M_NOWAIT); 622 if (resp == NULL) { 623 error = ENOMEM; 624 break; 625 } 626 627 ary = (struct ng_nat_list_redirects *)resp->data; 628 ary->total_count = priv->rdrcount; 629 630 STAILQ_FOREACH(entry, &priv->redirhead, entries) { 631 bcopy(&entry->rdr, &ary->redirects[i++], 632 sizeof(struct ng_nat_listrdrs_entry)); 633 } 634 } 635 break; 636 case NGM_NAT_PROXY_RULE: 637 { 638 char *cmd = (char *)msg->data; 639 640 if (msg->header.arglen < 6) { 641 error = EINVAL; 642 break; 643 } 644 645 if (LibAliasProxyRule(priv->lib, cmd) != 0) 646 error = ENOMEM; 647 } 648 break; 649 default: 650 error = EINVAL; /* unknown command */ 651 break; 652 } 653 break; 654 default: 655 error = EINVAL; /* unknown cookie type */ 656 break; 657 } 658 659 NG_RESPOND_MSG(error, node, item, resp); 660 NG_FREE_MSG(msg); 661 return (error); 662 } 663 664 static int 665 ng_nat_rcvdata(hook_p hook, item_p item ) 666 { 667 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 668 struct mbuf *m; 669 struct ip *ip; 670 int rval, error = 0; 671 char *c; 672 673 /* We have no required hooks. */ 674 if (!(priv->flags & NGNAT_CONNECTED)) { 675 NG_FREE_ITEM(item); 676 return (ENXIO); 677 } 678 679 /* We have no alias address yet to do anything. */ 680 if (!(priv->flags & NGNAT_ADDR_DEFINED)) 681 goto send; 682 683 m = NGI_M(item); 684 685 if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) { 686 NGI_M(item) = NULL; /* avoid double free */ 687 NG_FREE_ITEM(item); 688 return (ENOBUFS); 689 } 690 691 NGI_M(item) = m; 692 693 c = mtod(m, char *); 694 ip = mtod(m, struct ip *); 695 696 KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len), 697 ("ng_nat: ip_len != m_pkthdr.len")); 698 699 if (hook == priv->in) { 700 rval = LibAliasIn(priv->lib, c, m->m_len + M_TRAILINGSPACE(m)); 701 if (rval != PKT_ALIAS_OK && 702 rval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) { 703 NG_FREE_ITEM(item); 704 return (EINVAL); 705 } 706 } else if (hook == priv->out) { 707 rval = LibAliasOut(priv->lib, c, m->m_len + M_TRAILINGSPACE(m)); 708 if (rval != PKT_ALIAS_OK) { 709 NG_FREE_ITEM(item); 710 return (EINVAL); 711 } 712 } else 713 panic("ng_nat: unknown hook!\n"); 714 715 m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len); 716 717 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 718 ip->ip_p == IPPROTO_TCP) { 719 struct tcphdr *th = (struct tcphdr *)((caddr_t)ip + 720 (ip->ip_hl << 2)); 721 722 /* 723 * Here is our terrible HACK. 724 * 725 * Sometimes LibAlias edits contents of TCP packet. 726 * In this case it needs to recompute full TCP 727 * checksum. However, the problem is that LibAlias 728 * doesn't have any idea about checksum offloading 729 * in kernel. To workaround this, we do not do 730 * checksumming in LibAlias, but only mark the 731 * packets in th_x2 field. If we receive a marked 732 * packet, we calculate correct checksum for it 733 * aware of offloading. 734 * 735 * Why do I do such a terrible hack instead of 736 * recalculating checksum for each packet? 737 * Because the previous checksum was not checked! 738 * Recalculating checksums for EVERY packet will 739 * hide ALL transmission errors. Yes, marked packets 740 * still suffer from this problem. But, sigh, natd(8) 741 * has this problem, too. 742 */ 743 744 if (th->th_x2) { 745 th->th_x2 = 0; 746 ip->ip_len = ntohs(ip->ip_len); 747 th->th_sum = in_pseudo(ip->ip_src.s_addr, 748 ip->ip_dst.s_addr, htons(IPPROTO_TCP + 749 ip->ip_len - (ip->ip_hl << 2))); 750 751 if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) { 752 m->m_pkthdr.csum_data = offsetof(struct tcphdr, 753 th_sum); 754 in_delayed_cksum(m); 755 } 756 ip->ip_len = htons(ip->ip_len); 757 } 758 } 759 760 send: 761 if (hook == priv->in) 762 NG_FWD_ITEM_HOOK(error, item, priv->out); 763 else 764 NG_FWD_ITEM_HOOK(error, item, priv->in); 765 766 return (error); 767 } 768 769 static int 770 ng_nat_shutdown(node_p node) 771 { 772 const priv_p priv = NG_NODE_PRIVATE(node); 773 774 NG_NODE_SET_PRIVATE(node, NULL); 775 NG_NODE_UNREF(node); 776 777 /* Free redirects list. */ 778 while (!STAILQ_EMPTY(&priv->redirhead)) { 779 struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead); 780 STAILQ_REMOVE_HEAD(&priv->redirhead, entries); 781 free(entry, M_NETGRAPH); 782 }; 783 784 /* Final free. */ 785 LibAliasUninit(priv->lib); 786 free(priv, M_NETGRAPH); 787 788 return (0); 789 } 790 791 static int 792 ng_nat_disconnect(hook_p hook) 793 { 794 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 795 796 priv->flags &= ~NGNAT_CONNECTED; 797 798 if (hook == priv->out) 799 priv->out = NULL; 800 if (hook == priv->in) 801 priv->in = NULL; 802 803 if (priv->out == NULL && priv->in == NULL) 804 ng_rmnode_self(NG_HOOK_NODE(hook)); 805 806 return (0); 807 } 808 809 static unsigned int 810 ng_nat_translate_flags(unsigned int x) 811 { 812 unsigned int res = 0; 813 814 if (x & NG_NAT_LOG) 815 res |= PKT_ALIAS_LOG; 816 if (x & NG_NAT_DENY_INCOMING) 817 res |= PKT_ALIAS_DENY_INCOMING; 818 if (x & NG_NAT_SAME_PORTS) 819 res |= PKT_ALIAS_SAME_PORTS; 820 if (x & NG_NAT_UNREGISTERED_ONLY) 821 res |= PKT_ALIAS_UNREGISTERED_ONLY; 822 if (x & NG_NAT_RESET_ON_ADDR_CHANGE) 823 res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE; 824 if (x & NG_NAT_PROXY_ONLY) 825 res |= PKT_ALIAS_PROXY_ONLY; 826 if (x & NG_NAT_REVERSE) 827 res |= PKT_ALIAS_REVERSE; 828 829 return (res); 830 } 831