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