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