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, 276 M_NOWAIT | M_ZERO); 277 if (priv == NULL) 278 return (ENOMEM); 279 280 /* Init aliasing engine. */ 281 priv->lib = LibAliasInit(NULL); 282 if (priv->lib == NULL) { 283 free(priv, M_NETGRAPH); 284 return (ENOMEM); 285 } 286 287 /* Set same ports on. */ 288 (void )LibAliasSetMode(priv->lib, PKT_ALIAS_SAME_PORTS, 289 PKT_ALIAS_SAME_PORTS); 290 291 /* Init redirects housekeeping. */ 292 priv->rdrcount = 0; 293 priv->nextid = 1; 294 STAILQ_INIT(&priv->redirhead); 295 296 /* Link structs together. */ 297 NG_NODE_SET_PRIVATE(node, priv); 298 priv->node = node; 299 300 /* 301 * libalias is not thread safe, so our node 302 * must be single threaded. 303 */ 304 NG_NODE_FORCE_WRITER(node); 305 306 return (0); 307 } 308 309 static int 310 ng_nat_newhook(node_p node, hook_p hook, const char *name) 311 { 312 const priv_p priv = NG_NODE_PRIVATE(node); 313 314 if (strcmp(name, NG_NAT_HOOK_IN) == 0) { 315 priv->in = hook; 316 } else if (strcmp(name, NG_NAT_HOOK_OUT) == 0) { 317 priv->out = hook; 318 } else 319 return (EINVAL); 320 321 if (priv->out != NULL && 322 priv->in != NULL) 323 priv->flags |= NGNAT_CONNECTED; 324 325 return(0); 326 } 327 328 static int 329 ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook) 330 { 331 const priv_p priv = NG_NODE_PRIVATE(node); 332 struct ng_mesg *resp = NULL; 333 struct ng_mesg *msg; 334 int error = 0; 335 336 NGI_GET_MSG(item, msg); 337 338 switch (msg->header.typecookie) { 339 case NGM_NAT_COOKIE: 340 switch (msg->header.cmd) { 341 case NGM_NAT_SET_IPADDR: 342 { 343 struct in_addr *const ia = (struct in_addr *)msg->data; 344 345 if (msg->header.arglen < sizeof(*ia)) { 346 error = EINVAL; 347 break; 348 } 349 350 LibAliasSetAddress(priv->lib, *ia); 351 352 priv->flags |= NGNAT_ADDR_DEFINED; 353 } 354 break; 355 case NGM_NAT_SET_MODE: 356 { 357 struct ng_nat_mode *const mode = 358 (struct ng_nat_mode *)msg->data; 359 360 if (msg->header.arglen < sizeof(*mode)) { 361 error = EINVAL; 362 break; 363 } 364 365 if (LibAliasSetMode(priv->lib, 366 ng_nat_translate_flags(mode->flags), 367 ng_nat_translate_flags(mode->mask)) < 0) { 368 error = ENOMEM; 369 break; 370 } 371 } 372 break; 373 case NGM_NAT_SET_TARGET: 374 { 375 struct in_addr *const ia = (struct in_addr *)msg->data; 376 377 if (msg->header.arglen < sizeof(*ia)) { 378 error = EINVAL; 379 break; 380 } 381 382 LibAliasSetTarget(priv->lib, *ia); 383 } 384 break; 385 case NGM_NAT_REDIRECT_PORT: 386 { 387 struct ng_nat_rdr_lst *entry; 388 struct ng_nat_redirect_port *const rp = 389 (struct ng_nat_redirect_port *)msg->data; 390 391 if (msg->header.arglen < sizeof(*rp)) { 392 error = EINVAL; 393 break; 394 } 395 396 if ((entry = malloc(sizeof(struct ng_nat_rdr_lst), 397 M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) { 398 error = ENOMEM; 399 break; 400 } 401 402 /* Try actual redirect. */ 403 entry->lnk = LibAliasRedirectPort(priv->lib, 404 rp->local_addr, htons(rp->local_port), 405 rp->remote_addr, htons(rp->remote_port), 406 rp->alias_addr, htons(rp->alias_port), 407 rp->proto); 408 409 if (entry->lnk == NULL) { 410 error = ENOMEM; 411 free(entry, M_NETGRAPH); 412 break; 413 } 414 415 /* Successful, save info in our internal list. */ 416 entry->rdr.local_addr = rp->local_addr; 417 entry->rdr.alias_addr = rp->alias_addr; 418 entry->rdr.remote_addr = rp->remote_addr; 419 entry->rdr.local_port = rp->local_port; 420 entry->rdr.alias_port = rp->alias_port; 421 entry->rdr.remote_port = rp->remote_port; 422 entry->rdr.proto = rp->proto; 423 bcopy(rp->description, entry->rdr.description, 424 NG_NAT_DESC_LENGTH); 425 426 /* Safety precaution. */ 427 entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0'; 428 429 entry->rdr.id = priv->nextid++; 430 priv->rdrcount++; 431 432 /* Link to list of redirects. */ 433 STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries); 434 435 /* Response with id of newly added entry. */ 436 NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT); 437 if (resp == NULL) { 438 error = ENOMEM; 439 break; 440 } 441 bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id)); 442 } 443 break; 444 case NGM_NAT_REDIRECT_ADDR: 445 { 446 struct ng_nat_rdr_lst *entry; 447 struct ng_nat_redirect_addr *const ra = 448 (struct ng_nat_redirect_addr *)msg->data; 449 450 if (msg->header.arglen < sizeof(*ra)) { 451 error = EINVAL; 452 break; 453 } 454 455 if ((entry = malloc(sizeof(struct ng_nat_rdr_lst), 456 M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) { 457 error = ENOMEM; 458 break; 459 } 460 461 /* Try actual redirect. */ 462 entry->lnk = LibAliasRedirectAddr(priv->lib, 463 ra->local_addr, ra->alias_addr); 464 465 if (entry->lnk == NULL) { 466 error = ENOMEM; 467 free(entry, M_NETGRAPH); 468 break; 469 } 470 471 /* Successful, save info in our internal list. */ 472 entry->rdr.local_addr = ra->local_addr; 473 entry->rdr.alias_addr = ra->alias_addr; 474 entry->rdr.proto = NG_NAT_REDIRPROTO_ADDR; 475 bcopy(ra->description, entry->rdr.description, 476 NG_NAT_DESC_LENGTH); 477 478 /* Safety precaution. */ 479 entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0'; 480 481 entry->rdr.id = priv->nextid++; 482 priv->rdrcount++; 483 484 /* Link to list of redirects. */ 485 STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries); 486 487 /* Response with id of newly added entry. */ 488 NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT); 489 if (resp == NULL) { 490 error = ENOMEM; 491 break; 492 } 493 bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id)); 494 } 495 break; 496 case NGM_NAT_REDIRECT_PROTO: 497 { 498 struct ng_nat_rdr_lst *entry; 499 struct ng_nat_redirect_proto *const rp = 500 (struct ng_nat_redirect_proto *)msg->data; 501 502 if (msg->header.arglen < sizeof(*rp)) { 503 error = EINVAL; 504 break; 505 } 506 507 if ((entry = malloc(sizeof(struct ng_nat_rdr_lst), 508 M_NETGRAPH, M_NOWAIT | M_ZERO)) == NULL) { 509 error = ENOMEM; 510 break; 511 } 512 513 /* Try actual redirect. */ 514 entry->lnk = LibAliasRedirectProto(priv->lib, 515 rp->local_addr, rp->remote_addr, 516 rp->alias_addr, rp->proto); 517 518 if (entry->lnk == NULL) { 519 error = ENOMEM; 520 free(entry, M_NETGRAPH); 521 break; 522 } 523 524 /* Successful, save info in our internal list. */ 525 entry->rdr.local_addr = rp->local_addr; 526 entry->rdr.alias_addr = rp->alias_addr; 527 entry->rdr.remote_addr = rp->remote_addr; 528 entry->rdr.proto = rp->proto; 529 bcopy(rp->description, entry->rdr.description, 530 NG_NAT_DESC_LENGTH); 531 532 /* Safety precaution. */ 533 entry->rdr.description[NG_NAT_DESC_LENGTH-1] = '\0'; 534 535 entry->rdr.id = priv->nextid++; 536 priv->rdrcount++; 537 538 /* Link to list of redirects. */ 539 STAILQ_INSERT_TAIL(&priv->redirhead, entry, entries); 540 541 /* Response with id of newly added entry. */ 542 NG_MKRESPONSE(resp, msg, sizeof(entry->rdr.id), M_NOWAIT); 543 if (resp == NULL) { 544 error = ENOMEM; 545 break; 546 } 547 bcopy(&entry->rdr.id, resp->data, sizeof(entry->rdr.id)); 548 } 549 break; 550 case NGM_NAT_REDIRECT_DYNAMIC: 551 case NGM_NAT_REDIRECT_DELETE: 552 { 553 struct ng_nat_rdr_lst *entry; 554 uint32_t *const id = (uint32_t *)msg->data; 555 556 if (msg->header.arglen < sizeof(*id)) { 557 error = EINVAL; 558 break; 559 } 560 561 /* Find entry with supplied id. */ 562 STAILQ_FOREACH(entry, &priv->redirhead, entries) { 563 if (entry->rdr.id == *id) 564 break; 565 } 566 567 /* Not found. */ 568 if (entry == NULL) { 569 error = ENOENT; 570 break; 571 } 572 573 if (msg->header.cmd == NGM_NAT_REDIRECT_DYNAMIC) { 574 if (LibAliasRedirectDynamic(priv->lib, 575 entry->lnk) == -1) { 576 error = ENOTTY; /* XXX Something better? */ 577 break; 578 } 579 } else { /* NGM_NAT_REDIRECT_DELETE */ 580 LibAliasRedirectDelete(priv->lib, entry->lnk); 581 } 582 583 /* Delete entry from our internal list. */ 584 priv->rdrcount--; 585 STAILQ_REMOVE(&priv->redirhead, entry, ng_nat_rdr_lst, entries); 586 free(entry, M_NETGRAPH); 587 } 588 break; 589 case NGM_NAT_ADD_SERVER: 590 { 591 struct ng_nat_rdr_lst *entry; 592 struct ng_nat_add_server *const as = 593 (struct ng_nat_add_server *)msg->data; 594 595 if (msg->header.arglen < sizeof(*as)) { 596 error = EINVAL; 597 break; 598 } 599 600 /* Find entry with supplied id. */ 601 STAILQ_FOREACH(entry, &priv->redirhead, entries) { 602 if (entry->rdr.id == as->id) 603 break; 604 } 605 606 /* Not found. */ 607 if (entry == NULL) { 608 error = ENOENT; 609 break; 610 } 611 612 if (LibAliasAddServer(priv->lib, entry->lnk, 613 as->addr, htons(as->port)) == -1) { 614 error = ENOMEM; 615 break; 616 } 617 618 entry->rdr.lsnat++; 619 } 620 break; 621 case NGM_NAT_LIST_REDIRECTS: 622 { 623 struct ng_nat_rdr_lst *entry; 624 struct ng_nat_list_redirects *ary; 625 int i = 0; 626 627 NG_MKRESPONSE(resp, msg, sizeof(*ary) + 628 (priv->rdrcount) * sizeof(*entry), M_NOWAIT); 629 if (resp == NULL) { 630 error = ENOMEM; 631 break; 632 } 633 634 ary = (struct ng_nat_list_redirects *)resp->data; 635 ary->total_count = priv->rdrcount; 636 637 STAILQ_FOREACH(entry, &priv->redirhead, entries) { 638 bcopy(&entry->rdr, &ary->redirects[i++], 639 sizeof(struct ng_nat_listrdrs_entry)); 640 } 641 } 642 break; 643 case NGM_NAT_PROXY_RULE: 644 { 645 char *cmd = (char *)msg->data; 646 647 if (msg->header.arglen < 6) { 648 error = EINVAL; 649 break; 650 } 651 652 if (LibAliasProxyRule(priv->lib, cmd) != 0) 653 error = ENOMEM; 654 } 655 break; 656 default: 657 error = EINVAL; /* unknown command */ 658 break; 659 } 660 break; 661 default: 662 error = EINVAL; /* unknown cookie type */ 663 break; 664 } 665 666 NG_RESPOND_MSG(error, node, item, resp); 667 NG_FREE_MSG(msg); 668 return (error); 669 } 670 671 static int 672 ng_nat_rcvdata(hook_p hook, item_p item ) 673 { 674 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 675 struct mbuf *m; 676 struct ip *ip; 677 int rval, error = 0; 678 char *c; 679 680 /* We have no required hooks. */ 681 if (!(priv->flags & NGNAT_CONNECTED)) { 682 NG_FREE_ITEM(item); 683 return (ENXIO); 684 } 685 686 /* We have no alias address yet to do anything. */ 687 if (!(priv->flags & NGNAT_ADDR_DEFINED)) 688 goto send; 689 690 m = NGI_M(item); 691 692 if ((m = m_megapullup(m, m->m_pkthdr.len)) == NULL) { 693 NGI_M(item) = NULL; /* avoid double free */ 694 NG_FREE_ITEM(item); 695 return (ENOBUFS); 696 } 697 698 NGI_M(item) = m; 699 700 c = mtod(m, char *); 701 ip = mtod(m, struct ip *); 702 703 KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len), 704 ("ng_nat: ip_len != m_pkthdr.len")); 705 706 if (hook == priv->in) { 707 rval = LibAliasIn(priv->lib, c, m->m_len + M_TRAILINGSPACE(m)); 708 if (rval != PKT_ALIAS_OK && 709 rval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) { 710 NG_FREE_ITEM(item); 711 return (EINVAL); 712 } 713 } else if (hook == priv->out) { 714 rval = LibAliasOut(priv->lib, c, m->m_len + M_TRAILINGSPACE(m)); 715 if (rval != PKT_ALIAS_OK) { 716 NG_FREE_ITEM(item); 717 return (EINVAL); 718 } 719 } else 720 panic("ng_nat: unknown hook!\n"); 721 722 m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len); 723 724 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 725 ip->ip_p == IPPROTO_TCP) { 726 struct tcphdr *th = (struct tcphdr *)((caddr_t)ip + 727 (ip->ip_hl << 2)); 728 729 /* 730 * Here is our terrible HACK. 731 * 732 * Sometimes LibAlias edits contents of TCP packet. 733 * In this case it needs to recompute full TCP 734 * checksum. However, the problem is that LibAlias 735 * doesn't have any idea about checksum offloading 736 * in kernel. To workaround this, we do not do 737 * checksumming in LibAlias, but only mark the 738 * packets in th_x2 field. If we receive a marked 739 * packet, we calculate correct checksum for it 740 * aware of offloading. 741 * 742 * Why do I do such a terrible hack instead of 743 * recalculating checksum for each packet? 744 * Because the previous checksum was not checked! 745 * Recalculating checksums for EVERY packet will 746 * hide ALL transmission errors. Yes, marked packets 747 * still suffer from this problem. But, sigh, natd(8) 748 * has this problem, too. 749 */ 750 751 if (th->th_x2) { 752 th->th_x2 = 0; 753 ip->ip_len = ntohs(ip->ip_len); 754 th->th_sum = in_pseudo(ip->ip_src.s_addr, 755 ip->ip_dst.s_addr, htons(IPPROTO_TCP + 756 ip->ip_len - (ip->ip_hl << 2))); 757 758 if ((m->m_pkthdr.csum_flags & CSUM_TCP) == 0) { 759 m->m_pkthdr.csum_data = offsetof(struct tcphdr, 760 th_sum); 761 in_delayed_cksum(m); 762 } 763 ip->ip_len = htons(ip->ip_len); 764 } 765 } 766 767 send: 768 if (hook == priv->in) 769 NG_FWD_ITEM_HOOK(error, item, priv->out); 770 else 771 NG_FWD_ITEM_HOOK(error, item, priv->in); 772 773 return (error); 774 } 775 776 static int 777 ng_nat_shutdown(node_p node) 778 { 779 const priv_p priv = NG_NODE_PRIVATE(node); 780 781 NG_NODE_SET_PRIVATE(node, NULL); 782 NG_NODE_UNREF(node); 783 784 /* Free redirects list. */ 785 while (!STAILQ_EMPTY(&priv->redirhead)) { 786 struct ng_nat_rdr_lst *entry = STAILQ_FIRST(&priv->redirhead); 787 STAILQ_REMOVE_HEAD(&priv->redirhead, entries); 788 free(entry, M_NETGRAPH); 789 }; 790 791 /* Final free. */ 792 LibAliasUninit(priv->lib); 793 free(priv, M_NETGRAPH); 794 795 return (0); 796 } 797 798 static int 799 ng_nat_disconnect(hook_p hook) 800 { 801 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 802 803 priv->flags &= ~NGNAT_CONNECTED; 804 805 if (hook == priv->out) 806 priv->out = NULL; 807 if (hook == priv->in) 808 priv->in = NULL; 809 810 if (priv->out == NULL && priv->in == NULL) 811 ng_rmnode_self(NG_HOOK_NODE(hook)); 812 813 return (0); 814 } 815 816 static unsigned int 817 ng_nat_translate_flags(unsigned int x) 818 { 819 unsigned int res = 0; 820 821 if (x & NG_NAT_LOG) 822 res |= PKT_ALIAS_LOG; 823 if (x & NG_NAT_DENY_INCOMING) 824 res |= PKT_ALIAS_DENY_INCOMING; 825 if (x & NG_NAT_SAME_PORTS) 826 res |= PKT_ALIAS_SAME_PORTS; 827 if (x & NG_NAT_UNREGISTERED_ONLY) 828 res |= PKT_ALIAS_UNREGISTERED_ONLY; 829 if (x & NG_NAT_RESET_ON_ADDR_CHANGE) 830 res |= PKT_ALIAS_RESET_ON_ADDR_CHANGE; 831 if (x & NG_NAT_PROXY_ONLY) 832 res |= PKT_ALIAS_PROXY_ONLY; 833 if (x & NG_NAT_REVERSE) 834 res |= PKT_ALIAS_REVERSE; 835 836 return (res); 837 } 838