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