1 /* 2 * natd - Network Address Translation Daemon for FreeBSD. 3 * 4 * This software is provided free of charge, with no 5 * warranty of any kind, either expressed or implied. 6 * Use at your own risk. 7 * 8 * You may copy, modify and distribute this software (natd.c) freely. 9 * 10 * Ari Suutari <suutari@iki.fi> 11 */ 12 13 #include <sys/cdefs.h> 14 __FBSDID("$FreeBSD$"); 15 16 #define SYSLOG_NAMES 17 18 #include <sys/types.h> 19 #include <sys/socket.h> 20 #include <sys/sysctl.h> 21 #include <sys/time.h> 22 23 #include <netinet/in.h> 24 #include <netinet/in_systm.h> 25 #include <netinet/ip.h> 26 #include <machine/in_cksum.h> 27 #include <netinet/tcp.h> 28 #include <netinet/udp.h> 29 #include <netinet/ip_icmp.h> 30 #include <net/if.h> 31 #include <net/if_dl.h> 32 #include <net/route.h> 33 #include <arpa/inet.h> 34 35 #include <alias.h> 36 #include <ctype.h> 37 #include <err.h> 38 #include <errno.h> 39 #include <netdb.h> 40 #include <signal.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <syslog.h> 45 #include <unistd.h> 46 47 #include "natd.h" 48 49 /* 50 * Default values for input and output 51 * divert socket ports. 52 */ 53 54 #define DEFAULT_SERVICE "natd" 55 56 /* 57 * Definition of a port range, and macros to deal with values. 58 * FORMAT: HI 16-bits == first port in range, 0 == all ports. 59 * LO 16-bits == number of ports in range 60 * NOTES: - Port values are not stored in network byte order. 61 */ 62 63 typedef u_long port_range; 64 65 #define GETLOPORT(x) ((x) >> 0x10) 66 #define GETNUMPORTS(x) ((x) & 0x0000ffff) 67 #define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x))) 68 69 /* Set y to be the low-port value in port_range variable x. */ 70 #define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10)) 71 72 /* Set y to be the number of ports in port_range variable x. */ 73 #define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y)) 74 75 /* 76 * Function prototypes. 77 */ 78 79 static void DoAliasing (int fd, int direction); 80 static void DaemonMode (void); 81 static void HandleRoutingInfo (int fd); 82 static void Usage (void); 83 static char* FormatPacket (struct ip*); 84 static void PrintPacket (struct ip*); 85 static void SyslogPacket (struct ip*, int priority, const char *label); 86 static void SetAliasAddressFromIfName (const char *ifName); 87 static void InitiateShutdown (int); 88 static void Shutdown (int); 89 static void RefreshAddr (int); 90 static void ParseOption (const char* option, const char* parms); 91 static void ReadConfigFile (const char* fileName); 92 static void SetupPortRedirect (const char* parms); 93 static void SetupProtoRedirect(const char* parms); 94 static void SetupAddressRedirect (const char* parms); 95 static void StrToAddr (const char* str, struct in_addr* addr); 96 static u_short StrToPort (const char* str, const char* proto); 97 static int StrToPortRange (const char* str, const char* proto, port_range *portRange); 98 static int StrToProto (const char* str); 99 static int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange); 100 static void ParseArgs (int argc, char** argv); 101 static void SetupPunchFW(const char *strValue); 102 static void SetupSkinnyPort(const char *strValue); 103 104 /* 105 * Globals. 106 */ 107 108 static int verbose; 109 static int background; 110 static int running; 111 static int assignAliasAddr; 112 static char* ifName; 113 static int ifIndex; 114 static u_short inPort; 115 static u_short outPort; 116 static u_short inOutPort; 117 static struct in_addr aliasAddr; 118 static int dynamicMode; 119 static int ifMTU; 120 static int aliasOverhead; 121 static int icmpSock; 122 static int dropIgnoredIncoming; 123 static int logDropped; 124 static int logFacility; 125 static int logIpfwDenied; 126 static char* pidName; 127 128 int main (int argc, char** argv) 129 { 130 int divertIn; 131 int divertOut; 132 int divertInOut; 133 int routeSock; 134 struct sockaddr_in addr; 135 fd_set readMask; 136 int fdMax; 137 /* 138 * Initialize packet aliasing software. 139 * Done already here to be able to alter option bits 140 * during command line and configuration file processing. 141 */ 142 PacketAliasInit (); 143 /* 144 * Parse options. 145 */ 146 inPort = 0; 147 outPort = 0; 148 verbose = 0; 149 inOutPort = 0; 150 ifName = NULL; 151 ifMTU = -1; 152 background = 0; 153 running = 1; 154 assignAliasAddr = 0; 155 aliasAddr.s_addr = INADDR_NONE; 156 aliasOverhead = 12; 157 dynamicMode = 0; 158 logDropped = 0; 159 logFacility = LOG_DAEMON; 160 logIpfwDenied = -1; 161 pidName = PIDFILE; 162 163 ParseArgs (argc, argv); 164 /* 165 * Log ipfw(8) denied packets by default in verbose mode. 166 */ 167 if (logIpfwDenied == -1) 168 logIpfwDenied = verbose; 169 /* 170 * Open syslog channel. 171 */ 172 openlog ("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0), 173 logFacility); 174 /* 175 * If not doing the transparent proxying only, 176 * check that valid aliasing address has been given. 177 */ 178 if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL && 179 !(PacketAliasSetMode(0,0) & PKT_ALIAS_PROXY_ONLY)) 180 errx (1, "aliasing address not given"); 181 182 if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL) 183 errx (1, "both alias address and interface " 184 "name are not allowed"); 185 /* 186 * Check that valid port number is known. 187 */ 188 if (inPort != 0 || outPort != 0) 189 if (inPort == 0 || outPort == 0) 190 errx (1, "both input and output ports are required"); 191 192 if (inPort == 0 && outPort == 0 && inOutPort == 0) 193 ParseOption ("port", DEFAULT_SERVICE); 194 195 /* 196 * Check if ignored packets should be dropped. 197 */ 198 dropIgnoredIncoming = PacketAliasSetMode (0, 0); 199 dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING; 200 /* 201 * Create divert sockets. Use only one socket if -p was specified 202 * on command line. Otherwise, create separate sockets for 203 * outgoing and incoming connnections. 204 */ 205 if (inOutPort) { 206 207 divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 208 if (divertInOut == -1) 209 Quit ("Unable to create divert socket."); 210 211 divertIn = -1; 212 divertOut = -1; 213 /* 214 * Bind socket. 215 */ 216 217 addr.sin_family = AF_INET; 218 addr.sin_addr.s_addr = INADDR_ANY; 219 addr.sin_port = inOutPort; 220 221 if (bind (divertInOut, 222 (struct sockaddr*) &addr, 223 sizeof addr) == -1) 224 Quit ("Unable to bind divert socket."); 225 } 226 else { 227 228 divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 229 if (divertIn == -1) 230 Quit ("Unable to create incoming divert socket."); 231 232 divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 233 if (divertOut == -1) 234 Quit ("Unable to create outgoing divert socket."); 235 236 divertInOut = -1; 237 238 /* 239 * Bind divert sockets. 240 */ 241 242 addr.sin_family = AF_INET; 243 addr.sin_addr.s_addr = INADDR_ANY; 244 addr.sin_port = inPort; 245 246 if (bind (divertIn, 247 (struct sockaddr*) &addr, 248 sizeof addr) == -1) 249 Quit ("Unable to bind incoming divert socket."); 250 251 addr.sin_family = AF_INET; 252 addr.sin_addr.s_addr = INADDR_ANY; 253 addr.sin_port = outPort; 254 255 if (bind (divertOut, 256 (struct sockaddr*) &addr, 257 sizeof addr) == -1) 258 Quit ("Unable to bind outgoing divert socket."); 259 } 260 /* 261 * Create routing socket if interface name specified and in dynamic mode. 262 */ 263 routeSock = -1; 264 if (ifName) { 265 if (dynamicMode) { 266 267 routeSock = socket (PF_ROUTE, SOCK_RAW, 0); 268 if (routeSock == -1) 269 Quit ("Unable to create routing info socket."); 270 271 assignAliasAddr = 1; 272 } 273 else 274 SetAliasAddressFromIfName (ifName); 275 } 276 /* 277 * Create socket for sending ICMP messages. 278 */ 279 icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); 280 if (icmpSock == -1) 281 Quit ("Unable to create ICMP socket."); 282 283 /* 284 * And disable reads for the socket, otherwise it slowly fills 285 * up with received icmps which we do not use. 286 */ 287 shutdown(icmpSock, SHUT_RD); 288 289 /* 290 * Become a daemon unless verbose mode was requested. 291 */ 292 if (!verbose) 293 DaemonMode (); 294 /* 295 * Catch signals to manage shutdown and 296 * refresh of interface address. 297 */ 298 siginterrupt(SIGTERM, 1); 299 siginterrupt(SIGHUP, 1); 300 signal (SIGTERM, InitiateShutdown); 301 signal (SIGHUP, RefreshAddr); 302 /* 303 * Set alias address if it has been given. 304 */ 305 if (aliasAddr.s_addr != INADDR_NONE) 306 PacketAliasSetAddress (aliasAddr); 307 /* 308 * We need largest descriptor number for select. 309 */ 310 311 fdMax = -1; 312 313 if (divertIn > fdMax) 314 fdMax = divertIn; 315 316 if (divertOut > fdMax) 317 fdMax = divertOut; 318 319 if (divertInOut > fdMax) 320 fdMax = divertInOut; 321 322 if (routeSock > fdMax) 323 fdMax = routeSock; 324 325 while (running) { 326 327 if (divertInOut != -1 && !ifName) { 328 /* 329 * When using only one socket, just call 330 * DoAliasing repeatedly to process packets. 331 */ 332 DoAliasing (divertInOut, DONT_KNOW); 333 continue; 334 } 335 /* 336 * Build read mask from socket descriptors to select. 337 */ 338 FD_ZERO (&readMask); 339 /* 340 * Check if new packets are available. 341 */ 342 if (divertIn != -1) 343 FD_SET (divertIn, &readMask); 344 345 if (divertOut != -1) 346 FD_SET (divertOut, &readMask); 347 348 if (divertInOut != -1) 349 FD_SET (divertInOut, &readMask); 350 /* 351 * Routing info is processed always. 352 */ 353 if (routeSock != -1) 354 FD_SET (routeSock, &readMask); 355 356 if (select (fdMax + 1, 357 &readMask, 358 NULL, 359 NULL, 360 NULL) == -1) { 361 362 if (errno == EINTR) 363 continue; 364 365 Quit ("Select failed."); 366 } 367 368 if (divertIn != -1) 369 if (FD_ISSET (divertIn, &readMask)) 370 DoAliasing (divertIn, INPUT); 371 372 if (divertOut != -1) 373 if (FD_ISSET (divertOut, &readMask)) 374 DoAliasing (divertOut, OUTPUT); 375 376 if (divertInOut != -1) 377 if (FD_ISSET (divertInOut, &readMask)) 378 DoAliasing (divertInOut, DONT_KNOW); 379 380 if (routeSock != -1) 381 if (FD_ISSET (routeSock, &readMask)) 382 HandleRoutingInfo (routeSock); 383 } 384 385 if (background) 386 unlink (pidName); 387 388 return 0; 389 } 390 391 static void DaemonMode () 392 { 393 FILE* pidFile; 394 395 daemon (0, 0); 396 background = 1; 397 398 pidFile = fopen (pidName, "w"); 399 if (pidFile) { 400 401 fprintf (pidFile, "%d\n", getpid ()); 402 fclose (pidFile); 403 } 404 } 405 406 static void ParseArgs (int argc, char** argv) 407 { 408 int arg; 409 char* opt; 410 char parmBuf[256]; 411 int len; /* bounds checking */ 412 413 for (arg = 1; arg < argc; arg++) { 414 415 opt = argv[arg]; 416 if (*opt != '-') { 417 418 warnx ("invalid option %s", opt); 419 Usage (); 420 } 421 422 parmBuf[0] = '\0'; 423 len = 0; 424 425 while (arg < argc - 1) { 426 427 if (argv[arg + 1][0] == '-') 428 break; 429 430 if (len) { 431 strncat (parmBuf, " ", sizeof(parmBuf) - (len + 1)); 432 len += strlen(parmBuf + len); 433 } 434 435 ++arg; 436 strncat (parmBuf, argv[arg], sizeof(parmBuf) - (len + 1)); 437 len += strlen(parmBuf + len); 438 439 } 440 441 ParseOption (opt + 1, (len ? parmBuf : NULL)); 442 443 } 444 } 445 446 static void DoAliasing (int fd, int direction) 447 { 448 int bytes; 449 int origBytes; 450 char buf[IP_MAXPACKET]; 451 struct sockaddr_in addr; 452 int wrote; 453 int status; 454 int addrSize; 455 struct ip* ip; 456 char msgBuf[80]; 457 458 if (assignAliasAddr) { 459 460 SetAliasAddressFromIfName (ifName); 461 assignAliasAddr = 0; 462 } 463 /* 464 * Get packet from socket. 465 */ 466 addrSize = sizeof addr; 467 origBytes = recvfrom (fd, 468 buf, 469 sizeof buf, 470 0, 471 (struct sockaddr*) &addr, 472 &addrSize); 473 474 if (origBytes == -1) { 475 476 if (errno != EINTR) 477 Warn ("read from divert socket failed"); 478 479 return; 480 } 481 /* 482 * This is an IP packet. 483 */ 484 ip = (struct ip*) buf; 485 if (direction == DONT_KNOW) { 486 if (addr.sin_addr.s_addr == INADDR_ANY) 487 direction = OUTPUT; 488 else 489 direction = INPUT; 490 } 491 492 if (verbose) { 493 /* 494 * Print packet direction and protocol type. 495 */ 496 printf (direction == OUTPUT ? "Out " : "In "); 497 498 switch (ip->ip_p) { 499 case IPPROTO_TCP: 500 printf ("[TCP] "); 501 break; 502 503 case IPPROTO_UDP: 504 printf ("[UDP] "); 505 break; 506 507 case IPPROTO_ICMP: 508 printf ("[ICMP] "); 509 break; 510 511 default: 512 printf ("[%d] ", ip->ip_p); 513 break; 514 } 515 /* 516 * Print addresses. 517 */ 518 PrintPacket (ip); 519 } 520 521 if (direction == OUTPUT) { 522 /* 523 * Outgoing packets. Do aliasing. 524 */ 525 PacketAliasOut (buf, IP_MAXPACKET); 526 } 527 else { 528 529 /* 530 * Do aliasing. 531 */ 532 status = PacketAliasIn (buf, IP_MAXPACKET); 533 if (status == PKT_ALIAS_IGNORED && 534 dropIgnoredIncoming) { 535 536 if (verbose) 537 printf (" dropped.\n"); 538 539 if (logDropped) 540 SyslogPacket (ip, LOG_WARNING, "denied"); 541 542 return; 543 } 544 } 545 /* 546 * Length might have changed during aliasing. 547 */ 548 bytes = ntohs (ip->ip_len); 549 /* 550 * Update alias overhead size for outgoing packets. 551 */ 552 if (direction == OUTPUT && 553 bytes - origBytes > aliasOverhead) 554 aliasOverhead = bytes - origBytes; 555 556 if (verbose) { 557 558 /* 559 * Print addresses after aliasing. 560 */ 561 printf (" aliased to\n"); 562 printf (" "); 563 PrintPacket (ip); 564 printf ("\n"); 565 } 566 567 /* 568 * Put packet back for processing. 569 */ 570 wrote = sendto (fd, 571 buf, 572 bytes, 573 0, 574 (struct sockaddr*) &addr, 575 sizeof addr); 576 577 if (wrote != bytes) { 578 579 if (errno == EMSGSIZE) { 580 581 if (direction == OUTPUT && 582 ifMTU != -1) 583 SendNeedFragIcmp (icmpSock, 584 (struct ip*) buf, 585 ifMTU - aliasOverhead); 586 } 587 else if (errno == EACCES && logIpfwDenied) { 588 589 sprintf (msgBuf, "failed to write packet back"); 590 Warn (msgBuf); 591 } 592 } 593 } 594 595 static void HandleRoutingInfo (int fd) 596 { 597 int bytes; 598 struct if_msghdr ifMsg; 599 /* 600 * Get packet from socket. 601 */ 602 bytes = read (fd, &ifMsg, sizeof ifMsg); 603 if (bytes == -1) { 604 605 Warn ("read from routing socket failed"); 606 return; 607 } 608 609 if (ifMsg.ifm_version != RTM_VERSION) { 610 611 Warn ("unexpected packet read from routing socket"); 612 return; 613 } 614 615 if (verbose) 616 printf ("Routing message %#x received.\n", ifMsg.ifm_type); 617 618 if ((ifMsg.ifm_type == RTM_NEWADDR || ifMsg.ifm_type == RTM_IFINFO) && 619 ifMsg.ifm_index == ifIndex) { 620 if (verbose) 621 printf("Interface address/MTU has probably changed.\n"); 622 assignAliasAddr = 1; 623 } 624 } 625 626 static void PrintPacket (struct ip* ip) 627 { 628 printf ("%s", FormatPacket (ip)); 629 } 630 631 static void SyslogPacket (struct ip* ip, int priority, const char *label) 632 { 633 syslog (priority, "%s %s", label, FormatPacket (ip)); 634 } 635 636 static char* FormatPacket (struct ip* ip) 637 { 638 static char buf[256]; 639 struct tcphdr* tcphdr; 640 struct udphdr* udphdr; 641 struct icmp* icmphdr; 642 char src[20]; 643 char dst[20]; 644 645 strcpy (src, inet_ntoa (ip->ip_src)); 646 strcpy (dst, inet_ntoa (ip->ip_dst)); 647 648 switch (ip->ip_p) { 649 case IPPROTO_TCP: 650 tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2)); 651 sprintf (buf, "[TCP] %s:%d -> %s:%d", 652 src, 653 ntohs (tcphdr->th_sport), 654 dst, 655 ntohs (tcphdr->th_dport)); 656 break; 657 658 case IPPROTO_UDP: 659 udphdr = (struct udphdr*) ((char*) ip + (ip->ip_hl << 2)); 660 sprintf (buf, "[UDP] %s:%d -> %s:%d", 661 src, 662 ntohs (udphdr->uh_sport), 663 dst, 664 ntohs (udphdr->uh_dport)); 665 break; 666 667 case IPPROTO_ICMP: 668 icmphdr = (struct icmp*) ((char*) ip + (ip->ip_hl << 2)); 669 sprintf (buf, "[ICMP] %s -> %s %u(%u)", 670 src, 671 dst, 672 icmphdr->icmp_type, 673 icmphdr->icmp_code); 674 break; 675 676 default: 677 sprintf (buf, "[%d] %s -> %s ", ip->ip_p, src, dst); 678 break; 679 } 680 681 return buf; 682 } 683 684 static void 685 SetAliasAddressFromIfName(const char *ifn) 686 { 687 size_t needed; 688 int mib[6]; 689 char *buf, *lim, *next; 690 struct if_msghdr *ifm; 691 struct ifa_msghdr *ifam; 692 struct sockaddr_dl *sdl; 693 struct sockaddr_in *sin; 694 695 mib[0] = CTL_NET; 696 mib[1] = PF_ROUTE; 697 mib[2] = 0; 698 mib[3] = AF_INET; /* Only IP addresses please */ 699 mib[4] = NET_RT_IFLIST; 700 mib[5] = 0; /* ifIndex??? */ 701 /* 702 * Get interface data. 703 */ 704 if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) 705 err(1, "iflist-sysctl-estimate"); 706 if ((buf = malloc(needed)) == NULL) 707 errx(1, "malloc failed"); 708 if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) 709 err(1, "iflist-sysctl-get"); 710 lim = buf + needed; 711 /* 712 * Loop through interfaces until one with 713 * given name is found. This is done to 714 * find correct interface index for routing 715 * message processing. 716 */ 717 ifIndex = 0; 718 next = buf; 719 while (next < lim) { 720 ifm = (struct if_msghdr *)next; 721 next += ifm->ifm_msglen; 722 if (ifm->ifm_version != RTM_VERSION) { 723 if (verbose) 724 warnx("routing message version %d " 725 "not understood", ifm->ifm_version); 726 continue; 727 } 728 if (ifm->ifm_type == RTM_IFINFO) { 729 sdl = (struct sockaddr_dl *)(ifm + 1); 730 if (strlen(ifn) == sdl->sdl_nlen && 731 strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) { 732 ifIndex = ifm->ifm_index; 733 ifMTU = ifm->ifm_data.ifi_mtu; 734 break; 735 } 736 } 737 } 738 if (!ifIndex) 739 errx(1, "unknown interface name %s", ifn); 740 /* 741 * Get interface address. 742 */ 743 sin = NULL; 744 while (next < lim) { 745 ifam = (struct ifa_msghdr *)next; 746 next += ifam->ifam_msglen; 747 if (ifam->ifam_version != RTM_VERSION) { 748 if (verbose) 749 warnx("routing message version %d " 750 "not understood", ifam->ifam_version); 751 continue; 752 } 753 if (ifam->ifam_type != RTM_NEWADDR) 754 break; 755 if (ifam->ifam_addrs & RTA_IFA) { 756 int i; 757 char *cp = (char *)(ifam + 1); 758 759 #define ROUNDUP(a) \ 760 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 761 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 762 763 for (i = 1; i < RTA_IFA; i <<= 1) 764 if (ifam->ifam_addrs & i) 765 ADVANCE(cp, (struct sockaddr *)cp); 766 if (((struct sockaddr *)cp)->sa_family == AF_INET) { 767 sin = (struct sockaddr_in *)cp; 768 break; 769 } 770 } 771 } 772 if (sin == NULL) 773 errx(1, "%s: cannot get interface address", ifn); 774 775 PacketAliasSetAddress(sin->sin_addr); 776 syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes", 777 inet_ntoa(sin->sin_addr), ifMTU); 778 779 free(buf); 780 } 781 782 void Quit (const char* msg) 783 { 784 Warn (msg); 785 exit (1); 786 } 787 788 void Warn (const char* msg) 789 { 790 if (background) 791 syslog (LOG_ALERT, "%s (%m)", msg); 792 else 793 warn ("%s", msg); 794 } 795 796 static void RefreshAddr (int sig) 797 { 798 if (ifName) 799 assignAliasAddr = 1; 800 } 801 802 static void InitiateShutdown (int sig) 803 { 804 /* 805 * Start timer to allow kernel gracefully 806 * shutdown existing connections when system 807 * is shut down. 808 */ 809 siginterrupt(SIGALRM, 1); 810 signal (SIGALRM, Shutdown); 811 alarm (10); 812 } 813 814 static void Shutdown (int sig) 815 { 816 running = 0; 817 } 818 819 /* 820 * Different options recognized by this program. 821 */ 822 823 enum Option { 824 825 PacketAliasOption, 826 Verbose, 827 InPort, 828 OutPort, 829 Port, 830 AliasAddress, 831 TargetAddress, 832 InterfaceName, 833 RedirectPort, 834 RedirectProto, 835 RedirectAddress, 836 ConfigFile, 837 DynamicMode, 838 ProxyRule, 839 LogDenied, 840 LogFacility, 841 PunchFW, 842 SkinnyPort, 843 LogIpfwDenied, 844 PidFile 845 }; 846 847 enum Param { 848 849 YesNo, 850 Numeric, 851 String, 852 None, 853 Address, 854 Service 855 }; 856 857 /* 858 * Option information structure (used by ParseOption). 859 */ 860 861 struct OptionInfo { 862 863 enum Option type; 864 int packetAliasOpt; 865 enum Param parm; 866 const char* parmDescription; 867 const char* description; 868 const char* name; 869 const char* shortName; 870 }; 871 872 /* 873 * Table of known options. 874 */ 875 876 static struct OptionInfo optionTable[] = { 877 878 { PacketAliasOption, 879 PKT_ALIAS_UNREGISTERED_ONLY, 880 YesNo, 881 "[yes|no]", 882 "alias only unregistered addresses", 883 "unregistered_only", 884 "u" }, 885 886 { PacketAliasOption, 887 PKT_ALIAS_LOG, 888 YesNo, 889 "[yes|no]", 890 "enable logging", 891 "log", 892 "l" }, 893 894 { PacketAliasOption, 895 PKT_ALIAS_PROXY_ONLY, 896 YesNo, 897 "[yes|no]", 898 "proxy only", 899 "proxy_only", 900 NULL }, 901 902 { PacketAliasOption, 903 PKT_ALIAS_REVERSE, 904 YesNo, 905 "[yes|no]", 906 "operate in reverse mode", 907 "reverse", 908 NULL }, 909 910 { PacketAliasOption, 911 PKT_ALIAS_DENY_INCOMING, 912 YesNo, 913 "[yes|no]", 914 "allow incoming connections", 915 "deny_incoming", 916 "d" }, 917 918 { PacketAliasOption, 919 PKT_ALIAS_USE_SOCKETS, 920 YesNo, 921 "[yes|no]", 922 "use sockets to inhibit port conflict", 923 "use_sockets", 924 "s" }, 925 926 { PacketAliasOption, 927 PKT_ALIAS_SAME_PORTS, 928 YesNo, 929 "[yes|no]", 930 "try to keep original port numbers for connections", 931 "same_ports", 932 "m" }, 933 934 { Verbose, 935 0, 936 YesNo, 937 "[yes|no]", 938 "verbose mode, dump packet information", 939 "verbose", 940 "v" }, 941 942 { DynamicMode, 943 0, 944 YesNo, 945 "[yes|no]", 946 "dynamic mode, automatically detect interface address changes", 947 "dynamic", 948 NULL }, 949 950 { InPort, 951 0, 952 Service, 953 "number|service_name", 954 "set port for incoming packets", 955 "in_port", 956 "i" }, 957 958 { OutPort, 959 0, 960 Service, 961 "number|service_name", 962 "set port for outgoing packets", 963 "out_port", 964 "o" }, 965 966 { Port, 967 0, 968 Service, 969 "number|service_name", 970 "set port (defaults to natd/divert)", 971 "port", 972 "p" }, 973 974 { AliasAddress, 975 0, 976 Address, 977 "x.x.x.x", 978 "address to use for aliasing", 979 "alias_address", 980 "a" }, 981 982 { TargetAddress, 983 0, 984 Address, 985 "x.x.x.x", 986 "address to use for incoming sessions", 987 "target_address", 988 "t" }, 989 990 { InterfaceName, 991 0, 992 String, 993 "network_if_name", 994 "take aliasing address from interface", 995 "interface", 996 "n" }, 997 998 { ProxyRule, 999 0, 1000 String, 1001 "[type encode_ip_hdr|encode_tcp_stream] port xxxx server " 1002 "a.b.c.d:yyyy", 1003 "add transparent proxying / destination NAT", 1004 "proxy_rule", 1005 NULL }, 1006 1007 { RedirectPort, 1008 0, 1009 String, 1010 "tcp|udp local_addr:local_port_range[,...] [public_addr:]public_port_range" 1011 " [remote_addr[:remote_port_range]]", 1012 "redirect a port (or ports) for incoming traffic", 1013 "redirect_port", 1014 NULL }, 1015 1016 { RedirectProto, 1017 0, 1018 String, 1019 "proto local_addr [public_addr] [remote_addr]", 1020 "redirect packets of a given proto", 1021 "redirect_proto", 1022 NULL }, 1023 1024 { RedirectAddress, 1025 0, 1026 String, 1027 "local_addr[,...] public_addr", 1028 "define mapping between local and public addresses", 1029 "redirect_address", 1030 NULL }, 1031 1032 { ConfigFile, 1033 0, 1034 String, 1035 "file_name", 1036 "read options from configuration file", 1037 "config", 1038 "f" }, 1039 1040 { LogDenied, 1041 0, 1042 YesNo, 1043 "[yes|no]", 1044 "enable logging of denied incoming packets", 1045 "log_denied", 1046 NULL }, 1047 1048 { LogFacility, 1049 0, 1050 String, 1051 "facility", 1052 "name of syslog facility to use for logging", 1053 "log_facility", 1054 NULL }, 1055 1056 { PunchFW, 1057 0, 1058 String, 1059 "basenumber:count", 1060 "punch holes in the firewall for incoming FTP/IRC DCC connections", 1061 "punch_fw", 1062 NULL }, 1063 1064 { SkinnyPort, 1065 0, 1066 String, 1067 "port", 1068 "set the TCP port for use with the Skinny Station protocol", 1069 "skinny_port", 1070 NULL }, 1071 1072 { LogIpfwDenied, 1073 0, 1074 YesNo, 1075 "[yes|no]", 1076 "log packets converted by natd, but denied by ipfw", 1077 "log_ipfw_denied", 1078 NULL }, 1079 1080 { PidFile, 1081 0, 1082 String, 1083 "file_name", 1084 "store PID in an alternate file", 1085 "pid_file", 1086 "P" }, 1087 }; 1088 1089 static void ParseOption (const char* option, const char* parms) 1090 { 1091 int i; 1092 struct OptionInfo* info; 1093 int yesNoValue; 1094 int aliasValue; 1095 int numValue; 1096 u_short uNumValue; 1097 const char* strValue; 1098 struct in_addr addrValue; 1099 int max; 1100 char* end; 1101 CODE* fac_record = NULL; 1102 /* 1103 * Find option from table. 1104 */ 1105 max = sizeof (optionTable) / sizeof (struct OptionInfo); 1106 for (i = 0, info = optionTable; i < max; i++, info++) { 1107 1108 if (!strcmp (info->name, option)) 1109 break; 1110 1111 if (info->shortName) 1112 if (!strcmp (info->shortName, option)) 1113 break; 1114 } 1115 1116 if (i >= max) { 1117 1118 warnx ("unknown option %s", option); 1119 Usage (); 1120 } 1121 1122 uNumValue = 0; 1123 yesNoValue = 0; 1124 numValue = 0; 1125 strValue = NULL; 1126 /* 1127 * Check parameters. 1128 */ 1129 switch (info->parm) { 1130 case YesNo: 1131 if (!parms) 1132 parms = "yes"; 1133 1134 if (!strcmp (parms, "yes")) 1135 yesNoValue = 1; 1136 else 1137 if (!strcmp (parms, "no")) 1138 yesNoValue = 0; 1139 else 1140 errx (1, "%s needs yes/no parameter", option); 1141 break; 1142 1143 case Service: 1144 if (!parms) 1145 errx (1, "%s needs service name or " 1146 "port number parameter", 1147 option); 1148 1149 uNumValue = StrToPort (parms, "divert"); 1150 break; 1151 1152 case Numeric: 1153 if (parms) 1154 numValue = strtol (parms, &end, 10); 1155 else 1156 end = NULL; 1157 1158 if (end == parms) 1159 errx (1, "%s needs numeric parameter", option); 1160 break; 1161 1162 case String: 1163 strValue = parms; 1164 if (!strValue) 1165 errx (1, "%s needs parameter", option); 1166 break; 1167 1168 case None: 1169 if (parms) 1170 errx (1, "%s does not take parameters", option); 1171 break; 1172 1173 case Address: 1174 if (!parms) 1175 errx (1, "%s needs address/host parameter", option); 1176 1177 StrToAddr (parms, &addrValue); 1178 break; 1179 } 1180 1181 switch (info->type) { 1182 case PacketAliasOption: 1183 1184 aliasValue = yesNoValue ? info->packetAliasOpt : 0; 1185 PacketAliasSetMode (aliasValue, info->packetAliasOpt); 1186 break; 1187 1188 case Verbose: 1189 verbose = yesNoValue; 1190 break; 1191 1192 case DynamicMode: 1193 dynamicMode = yesNoValue; 1194 break; 1195 1196 case InPort: 1197 inPort = uNumValue; 1198 break; 1199 1200 case OutPort: 1201 outPort = uNumValue; 1202 break; 1203 1204 case Port: 1205 inOutPort = uNumValue; 1206 break; 1207 1208 case AliasAddress: 1209 memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr)); 1210 break; 1211 1212 case TargetAddress: 1213 PacketAliasSetTarget(addrValue); 1214 break; 1215 1216 case RedirectPort: 1217 SetupPortRedirect (strValue); 1218 break; 1219 1220 case RedirectProto: 1221 SetupProtoRedirect(strValue); 1222 break; 1223 1224 case RedirectAddress: 1225 SetupAddressRedirect (strValue); 1226 break; 1227 1228 case ProxyRule: 1229 PacketAliasProxyRule (strValue); 1230 break; 1231 1232 case InterfaceName: 1233 if (ifName) 1234 free (ifName); 1235 1236 ifName = strdup (strValue); 1237 break; 1238 1239 case ConfigFile: 1240 ReadConfigFile (strValue); 1241 break; 1242 1243 case LogDenied: 1244 logDropped = yesNoValue; 1245 break; 1246 1247 case LogFacility: 1248 1249 fac_record = facilitynames; 1250 while (fac_record->c_name != NULL) { 1251 1252 if (!strcmp (fac_record->c_name, strValue)) { 1253 1254 logFacility = fac_record->c_val; 1255 break; 1256 1257 } 1258 else 1259 fac_record++; 1260 } 1261 1262 if(fac_record->c_name == NULL) 1263 errx(1, "Unknown log facility name: %s", strValue); 1264 1265 break; 1266 1267 case PunchFW: 1268 SetupPunchFW(strValue); 1269 break; 1270 1271 case SkinnyPort: 1272 SetupSkinnyPort(strValue); 1273 break; 1274 1275 case LogIpfwDenied: 1276 logIpfwDenied = yesNoValue;; 1277 break; 1278 1279 case PidFile: 1280 pidName = strdup (strValue); 1281 break; 1282 } 1283 } 1284 1285 void ReadConfigFile (const char* fileName) 1286 { 1287 FILE* file; 1288 char *buf; 1289 size_t len; 1290 char *ptr, *p; 1291 char* option; 1292 1293 file = fopen (fileName, "r"); 1294 if (!file) 1295 err(1, "cannot open config file %s", fileName); 1296 1297 while ((buf = fgetln(file, &len)) != NULL) { 1298 if (buf[len - 1] == '\n') 1299 buf[len - 1] = '\0'; 1300 else 1301 errx(1, "config file format error: " 1302 "last line should end with newline"); 1303 1304 /* 1305 * Check for comments, strip off trailing spaces. 1306 */ 1307 if ((ptr = strchr(buf, '#'))) 1308 *ptr = '\0'; 1309 for (ptr = buf; isspace(*ptr); ++ptr) 1310 continue; 1311 if (*ptr == '\0') 1312 continue; 1313 for (p = strchr(buf, '\0'); isspace(*--p);) 1314 continue; 1315 *++p = '\0'; 1316 1317 /* 1318 * Extract option name. 1319 */ 1320 option = ptr; 1321 while (*ptr && !isspace (*ptr)) 1322 ++ptr; 1323 1324 if (*ptr != '\0') { 1325 1326 *ptr = '\0'; 1327 ++ptr; 1328 } 1329 /* 1330 * Skip white space between name and parms. 1331 */ 1332 while (*ptr && isspace (*ptr)) 1333 ++ptr; 1334 1335 ParseOption (option, *ptr ? ptr : NULL); 1336 } 1337 1338 fclose (file); 1339 } 1340 1341 static void Usage () 1342 { 1343 int i; 1344 int max; 1345 struct OptionInfo* info; 1346 1347 fprintf (stderr, "Recognized options:\n\n"); 1348 1349 max = sizeof (optionTable) / sizeof (struct OptionInfo); 1350 for (i = 0, info = optionTable; i < max; i++, info++) { 1351 1352 fprintf (stderr, "-%-20s %s\n", info->name, 1353 info->parmDescription); 1354 1355 if (info->shortName) 1356 fprintf (stderr, "-%-20s %s\n", info->shortName, 1357 info->parmDescription); 1358 1359 fprintf (stderr, " %s\n\n", info->description); 1360 } 1361 1362 exit (1); 1363 } 1364 1365 void SetupPortRedirect (const char* parms) 1366 { 1367 char buf[128]; 1368 char* ptr; 1369 char* serverPool; 1370 struct in_addr localAddr; 1371 struct in_addr publicAddr; 1372 struct in_addr remoteAddr; 1373 port_range portRange; 1374 u_short localPort = 0; 1375 u_short publicPort = 0; 1376 u_short remotePort = 0; 1377 u_short numLocalPorts = 0; 1378 u_short numPublicPorts = 0; 1379 u_short numRemotePorts = 0; 1380 int proto; 1381 char* protoName; 1382 char* separator; 1383 int i; 1384 struct alias_link *link = NULL; 1385 1386 strcpy (buf, parms); 1387 /* 1388 * Extract protocol. 1389 */ 1390 protoName = strtok (buf, " \t"); 1391 if (!protoName) 1392 errx (1, "redirect_port: missing protocol"); 1393 1394 proto = StrToProto (protoName); 1395 /* 1396 * Extract local address. 1397 */ 1398 ptr = strtok (NULL, " \t"); 1399 if (!ptr) 1400 errx (1, "redirect_port: missing local address"); 1401 1402 separator = strchr(ptr, ','); 1403 if (separator) { /* LSNAT redirection syntax. */ 1404 localAddr.s_addr = INADDR_NONE; 1405 localPort = ~0; 1406 numLocalPorts = 1; 1407 serverPool = ptr; 1408 } else { 1409 if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 ) 1410 errx (1, "redirect_port: invalid local port range"); 1411 1412 localPort = GETLOPORT(portRange); 1413 numLocalPorts = GETNUMPORTS(portRange); 1414 serverPool = NULL; 1415 } 1416 1417 /* 1418 * Extract public port and optionally address. 1419 */ 1420 ptr = strtok (NULL, " \t"); 1421 if (!ptr) 1422 errx (1, "redirect_port: missing public port"); 1423 1424 separator = strchr (ptr, ':'); 1425 if (separator) { 1426 if (StrToAddrAndPortRange (ptr, &publicAddr, protoName, &portRange) != 0 ) 1427 errx (1, "redirect_port: invalid public port range"); 1428 } 1429 else { 1430 publicAddr.s_addr = INADDR_ANY; 1431 if (StrToPortRange (ptr, protoName, &portRange) != 0) 1432 errx (1, "redirect_port: invalid public port range"); 1433 } 1434 1435 publicPort = GETLOPORT(portRange); 1436 numPublicPorts = GETNUMPORTS(portRange); 1437 1438 /* 1439 * Extract remote address and optionally port. 1440 */ 1441 ptr = strtok (NULL, " \t"); 1442 if (ptr) { 1443 separator = strchr (ptr, ':'); 1444 if (separator) { 1445 if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0) 1446 errx (1, "redirect_port: invalid remote port range"); 1447 } else { 1448 SETLOPORT(portRange, 0); 1449 SETNUMPORTS(portRange, 1); 1450 StrToAddr (ptr, &remoteAddr); 1451 } 1452 } 1453 else { 1454 SETLOPORT(portRange, 0); 1455 SETNUMPORTS(portRange, 1); 1456 remoteAddr.s_addr = INADDR_ANY; 1457 } 1458 1459 remotePort = GETLOPORT(portRange); 1460 numRemotePorts = GETNUMPORTS(portRange); 1461 1462 /* 1463 * Make sure port ranges match up, then add the redirect ports. 1464 */ 1465 if (numLocalPorts != numPublicPorts) 1466 errx (1, "redirect_port: port ranges must be equal in size"); 1467 1468 /* Remote port range is allowed to be '0' which means all ports. */ 1469 if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0)) 1470 errx (1, "redirect_port: remote port must be 0 or equal to local port range in size"); 1471 1472 for (i = 0 ; i < numPublicPorts ; ++i) { 1473 /* If remotePort is all ports, set it to 0. */ 1474 u_short remotePortCopy = remotePort + i; 1475 if (numRemotePorts == 1 && remotePort == 0) 1476 remotePortCopy = 0; 1477 1478 link = PacketAliasRedirectPort (localAddr, 1479 htons(localPort + i), 1480 remoteAddr, 1481 htons(remotePortCopy), 1482 publicAddr, 1483 htons(publicPort + i), 1484 proto); 1485 } 1486 1487 /* 1488 * Setup LSNAT server pool. 1489 */ 1490 if (serverPool != NULL && link != NULL) { 1491 ptr = strtok(serverPool, ","); 1492 while (ptr != NULL) { 1493 if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0) 1494 errx(1, "redirect_port: invalid local port range"); 1495 1496 localPort = GETLOPORT(portRange); 1497 if (GETNUMPORTS(portRange) != 1) 1498 errx(1, "redirect_port: local port must be single in this context"); 1499 PacketAliasAddServer(link, localAddr, htons(localPort)); 1500 ptr = strtok(NULL, ","); 1501 } 1502 } 1503 } 1504 1505 void 1506 SetupProtoRedirect(const char* parms) 1507 { 1508 char buf[128]; 1509 char* ptr; 1510 struct in_addr localAddr; 1511 struct in_addr publicAddr; 1512 struct in_addr remoteAddr; 1513 int proto; 1514 char* protoName; 1515 struct protoent *protoent; 1516 1517 strcpy (buf, parms); 1518 /* 1519 * Extract protocol. 1520 */ 1521 protoName = strtok(buf, " \t"); 1522 if (!protoName) 1523 errx(1, "redirect_proto: missing protocol"); 1524 1525 protoent = getprotobyname(protoName); 1526 if (protoent == NULL) 1527 errx(1, "redirect_proto: unknown protocol %s", protoName); 1528 else 1529 proto = protoent->p_proto; 1530 /* 1531 * Extract local address. 1532 */ 1533 ptr = strtok(NULL, " \t"); 1534 if (!ptr) 1535 errx(1, "redirect_proto: missing local address"); 1536 else 1537 StrToAddr(ptr, &localAddr); 1538 /* 1539 * Extract optional public address. 1540 */ 1541 ptr = strtok(NULL, " \t"); 1542 if (ptr) 1543 StrToAddr(ptr, &publicAddr); 1544 else 1545 publicAddr.s_addr = INADDR_ANY; 1546 /* 1547 * Extract optional remote address. 1548 */ 1549 ptr = strtok(NULL, " \t"); 1550 if (ptr) 1551 StrToAddr(ptr, &remoteAddr); 1552 else 1553 remoteAddr.s_addr = INADDR_ANY; 1554 /* 1555 * Create aliasing link. 1556 */ 1557 (void)PacketAliasRedirectProto(localAddr, remoteAddr, publicAddr, 1558 proto); 1559 } 1560 1561 void SetupAddressRedirect (const char* parms) 1562 { 1563 char buf[128]; 1564 char* ptr; 1565 char* separator; 1566 struct in_addr localAddr; 1567 struct in_addr publicAddr; 1568 char* serverPool; 1569 struct alias_link *link; 1570 1571 strcpy (buf, parms); 1572 /* 1573 * Extract local address. 1574 */ 1575 ptr = strtok (buf, " \t"); 1576 if (!ptr) 1577 errx (1, "redirect_address: missing local address"); 1578 1579 separator = strchr(ptr, ','); 1580 if (separator) { /* LSNAT redirection syntax. */ 1581 localAddr.s_addr = INADDR_NONE; 1582 serverPool = ptr; 1583 } else { 1584 StrToAddr (ptr, &localAddr); 1585 serverPool = NULL; 1586 } 1587 /* 1588 * Extract public address. 1589 */ 1590 ptr = strtok (NULL, " \t"); 1591 if (!ptr) 1592 errx (1, "redirect_address: missing public address"); 1593 1594 StrToAddr (ptr, &publicAddr); 1595 link = PacketAliasRedirectAddr(localAddr, publicAddr); 1596 1597 /* 1598 * Setup LSNAT server pool. 1599 */ 1600 if (serverPool != NULL && link != NULL) { 1601 ptr = strtok(serverPool, ","); 1602 while (ptr != NULL) { 1603 StrToAddr(ptr, &localAddr); 1604 PacketAliasAddServer(link, localAddr, htons(~0)); 1605 ptr = strtok(NULL, ","); 1606 } 1607 } 1608 } 1609 1610 void StrToAddr (const char* str, struct in_addr* addr) 1611 { 1612 struct hostent* hp; 1613 1614 if (inet_aton (str, addr)) 1615 return; 1616 1617 hp = gethostbyname (str); 1618 if (!hp) 1619 errx (1, "unknown host %s", str); 1620 1621 memcpy (addr, hp->h_addr, sizeof (struct in_addr)); 1622 } 1623 1624 u_short StrToPort (const char* str, const char* proto) 1625 { 1626 u_short port; 1627 struct servent* sp; 1628 char* end; 1629 1630 port = strtol (str, &end, 10); 1631 if (end != str) 1632 return htons (port); 1633 1634 sp = getservbyname (str, proto); 1635 if (!sp) 1636 errx (1, "%s/%s: unknown service", str, proto); 1637 1638 return sp->s_port; 1639 } 1640 1641 int StrToPortRange (const char* str, const char* proto, port_range *portRange) 1642 { 1643 char* sep; 1644 struct servent* sp; 1645 char* end; 1646 u_short loPort; 1647 u_short hiPort; 1648 1649 /* First see if this is a service, return corresponding port if so. */ 1650 sp = getservbyname (str,proto); 1651 if (sp) { 1652 SETLOPORT(*portRange, ntohs(sp->s_port)); 1653 SETNUMPORTS(*portRange, 1); 1654 return 0; 1655 } 1656 1657 /* Not a service, see if it's a single port or port range. */ 1658 sep = strchr (str, '-'); 1659 if (sep == NULL) { 1660 SETLOPORT(*portRange, strtol(str, &end, 10)); 1661 if (end != str) { 1662 /* Single port. */ 1663 SETNUMPORTS(*portRange, 1); 1664 return 0; 1665 } 1666 1667 /* Error in port range field. */ 1668 errx (1, "%s/%s: unknown service", str, proto); 1669 } 1670 1671 /* Port range, get the values and sanity check. */ 1672 sscanf (str, "%hu-%hu", &loPort, &hiPort); 1673 SETLOPORT(*portRange, loPort); 1674 SETNUMPORTS(*portRange, 0); /* Error by default */ 1675 if (loPort <= hiPort) 1676 SETNUMPORTS(*portRange, hiPort - loPort + 1); 1677 1678 if (GETNUMPORTS(*portRange) == 0) 1679 errx (1, "invalid port range %s", str); 1680 1681 return 0; 1682 } 1683 1684 1685 int StrToProto (const char* str) 1686 { 1687 if (!strcmp (str, "tcp")) 1688 return IPPROTO_TCP; 1689 1690 if (!strcmp (str, "udp")) 1691 return IPPROTO_UDP; 1692 1693 errx (1, "unknown protocol %s. Expected tcp or udp", str); 1694 } 1695 1696 int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange) 1697 { 1698 char* ptr; 1699 1700 ptr = strchr (str, ':'); 1701 if (!ptr) 1702 errx (1, "%s is missing port number", str); 1703 1704 *ptr = '\0'; 1705 ++ptr; 1706 1707 StrToAddr (str, addr); 1708 return StrToPortRange (ptr, proto, portRange); 1709 } 1710 1711 static void 1712 SetupPunchFW(const char *strValue) 1713 { 1714 unsigned int base, num; 1715 1716 if (sscanf(strValue, "%u:%u", &base, &num) != 2) 1717 errx(1, "punch_fw: basenumber:count parameter required"); 1718 1719 PacketAliasSetFWBase(base, num); 1720 (void)PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW); 1721 } 1722 1723 static void 1724 SetupSkinnyPort(const char *strValue) 1725 { 1726 unsigned int port; 1727 1728 if (sscanf(strValue, "%u", &port) != 1) 1729 errx(1, "skinny_port: port parameter required"); 1730 1731 PacketAliasSetSkinnyPort(port); 1732 } 1733