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