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