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