124084f9bSBrian Somers /* 224084f9bSBrian Somers * natd - Network Address Translation Daemon for FreeBSD. 324084f9bSBrian Somers * 4f13f9fadSAlexander Langer * This software is provided free of charge, with no 524084f9bSBrian Somers * warranty of any kind, either expressed or implied. 624084f9bSBrian Somers * Use at your own risk. 724084f9bSBrian Somers * 824084f9bSBrian Somers * You may copy, modify and distribute this software (natd.c) freely. 924084f9bSBrian Somers * 10fb994b07SBrian Somers * Ari Suutari <suutari@iki.fi> 1124084f9bSBrian Somers * 127f3dea24SPeter Wemm * $FreeBSD$ 1324084f9bSBrian Somers */ 1424084f9bSBrian Somers 1559a7c613SBrian Somers #define SYSLOG_NAMES 1659a7c613SBrian Somers 1724084f9bSBrian Somers #include <sys/types.h> 1824084f9bSBrian Somers #include <sys/socket.h> 194c04fa4cSRuslan Ermilov #include <sys/sysctl.h> 2024084f9bSBrian Somers #include <sys/time.h> 2124084f9bSBrian Somers 2224084f9bSBrian Somers #include <netinet/in.h> 2324084f9bSBrian Somers #include <netinet/in_systm.h> 2424084f9bSBrian Somers #include <netinet/ip.h> 2524084f9bSBrian Somers #include <machine/in_cksum.h> 2624084f9bSBrian Somers #include <netinet/tcp.h> 2759a7c613SBrian Somers #include <netinet/udp.h> 2859a7c613SBrian Somers #include <netinet/ip_icmp.h> 2924084f9bSBrian Somers #include <net/if.h> 304c04fa4cSRuslan Ermilov #include <net/if_dl.h> 3124084f9bSBrian Somers #include <net/route.h> 3224084f9bSBrian Somers #include <arpa/inet.h> 3324084f9bSBrian Somers 3424084f9bSBrian Somers #include <alias.h> 350fc81af1SPhilippe Charnier #include <ctype.h> 360fc81af1SPhilippe Charnier #include <err.h> 370fc81af1SPhilippe Charnier #include <errno.h> 380fc81af1SPhilippe Charnier #include <netdb.h> 390fc81af1SPhilippe Charnier #include <signal.h> 400fc81af1SPhilippe Charnier #include <stdio.h> 410fc81af1SPhilippe Charnier #include <stdlib.h> 420fc81af1SPhilippe Charnier #include <string.h> 430fc81af1SPhilippe Charnier #include <syslog.h> 440fc81af1SPhilippe Charnier #include <unistd.h> 4567a886fbSBrian Somers 4624084f9bSBrian Somers #include "natd.h" 4724084f9bSBrian Somers 4824084f9bSBrian Somers /* 4924084f9bSBrian Somers * Default values for input and output 5024084f9bSBrian Somers * divert socket ports. 5124084f9bSBrian Somers */ 5224084f9bSBrian Somers 5324084f9bSBrian Somers #define DEFAULT_SERVICE "natd" 5424084f9bSBrian Somers 5524084f9bSBrian Somers /* 565d8ee958SBrian Somers * Definition of a port range, and macros to deal with values. 575d8ee958SBrian Somers * FORMAT: HI 16-bits == first port in range, 0 == all ports. 585d8ee958SBrian Somers * LO 16-bits == number of ports in range 595d8ee958SBrian Somers * NOTES: - Port values are not stored in network byte order. 605d8ee958SBrian Somers */ 615d8ee958SBrian Somers 625d8ee958SBrian Somers typedef u_long port_range; 635d8ee958SBrian Somers 645d8ee958SBrian Somers #define GETLOPORT(x) ((x) >> 0x10) 655d8ee958SBrian Somers #define GETNUMPORTS(x) ((x) & 0x0000ffff) 665d8ee958SBrian Somers #define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x))) 675d8ee958SBrian Somers 685d8ee958SBrian Somers /* Set y to be the low-port value in port_range variable x. */ 695d8ee958SBrian Somers #define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10)) 705d8ee958SBrian Somers 715d8ee958SBrian Somers /* Set y to be the number of ports in port_range variable x. */ 725d8ee958SBrian Somers #define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y)) 735d8ee958SBrian Somers 745d8ee958SBrian Somers /* 7524084f9bSBrian Somers * Function prototypes. 7624084f9bSBrian Somers */ 7724084f9bSBrian Somers 7859a7c613SBrian Somers static void DoAliasing (int fd, int direction); 79902cb50aSBrian Somers static void DaemonMode (void); 8024084f9bSBrian Somers static void HandleRoutingInfo (int fd); 81902cb50aSBrian Somers static void Usage (void); 8259a7c613SBrian Somers static char* FormatPacket (struct ip*); 8324084f9bSBrian Somers static void PrintPacket (struct ip*); 84902cb50aSBrian Somers static void SyslogPacket (struct ip*, int priority, const char *label); 854c04fa4cSRuslan Ermilov static void SetAliasAddressFromIfName (const char *ifName); 86902cb50aSBrian Somers static void InitiateShutdown (int); 87902cb50aSBrian Somers static void Shutdown (int); 88902cb50aSBrian Somers static void RefreshAddr (int); 89b0f55af6SRuslan Ermilov static void ParseOption (const char* option, const char* parms); 90902cb50aSBrian Somers static void ReadConfigFile (const char* fileName); 91902cb50aSBrian Somers static void SetupPortRedirect (const char* parms); 924330006dSRuslan Ermilov static void SetupProtoRedirect(const char* parms); 93902cb50aSBrian Somers static void SetupAddressRedirect (const char* parms); 94902cb50aSBrian Somers static void SetupPptpAlias (const char* parms); 95902cb50aSBrian Somers static void StrToAddr (const char* str, struct in_addr* addr); 96902cb50aSBrian Somers static u_short StrToPort (const char* str, const char* proto); 97902cb50aSBrian Somers static int StrToPortRange (const char* str, const char* proto, port_range *portRange); 98902cb50aSBrian Somers static int StrToProto (const char* str); 99902cb50aSBrian Somers static int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange); 10024084f9bSBrian Somers static void ParseArgs (int argc, char** argv); 101fb994b07SBrian Somers static void FlushPacketBuffer (int fd); 10224084f9bSBrian Somers 10324084f9bSBrian Somers /* 10424084f9bSBrian Somers * Globals. 10524084f9bSBrian Somers */ 10624084f9bSBrian Somers 10724084f9bSBrian Somers static int verbose; 10824084f9bSBrian Somers static int background; 10924084f9bSBrian Somers static int running; 11024084f9bSBrian Somers static int assignAliasAddr; 11124084f9bSBrian Somers static char* ifName; 11224084f9bSBrian Somers static int ifIndex; 11367a886fbSBrian Somers static u_short inPort; 11467a886fbSBrian Somers static u_short outPort; 11567a886fbSBrian Somers static u_short inOutPort; 11624084f9bSBrian Somers static struct in_addr aliasAddr; 11724084f9bSBrian Somers static int dynamicMode; 11824084f9bSBrian Somers static int ifMTU; 11924084f9bSBrian Somers static int aliasOverhead; 12024084f9bSBrian Somers static int icmpSock; 121fb994b07SBrian Somers static char packetBuf[IP_MAXPACKET]; 122fb994b07SBrian Somers static int packetLen; 123fb994b07SBrian Somers static struct sockaddr_in packetAddr; 124fb994b07SBrian Somers static int packetSock; 12559a7c613SBrian Somers static int packetDirection; 126f9b06d5cSBrian Somers static int dropIgnoredIncoming; 12759a7c613SBrian Somers static int logDropped; 12859a7c613SBrian Somers static int logFacility; 12924084f9bSBrian Somers 13024084f9bSBrian Somers int main (int argc, char** argv) 13124084f9bSBrian Somers { 13224084f9bSBrian Somers int divertIn; 13324084f9bSBrian Somers int divertOut; 13424084f9bSBrian Somers int divertInOut; 13524084f9bSBrian Somers int routeSock; 13624084f9bSBrian Somers struct sockaddr_in addr; 13724084f9bSBrian Somers fd_set readMask; 138fb994b07SBrian Somers fd_set writeMask; 13924084f9bSBrian Somers int fdMax; 14024084f9bSBrian Somers /* 14124084f9bSBrian Somers * Initialize packet aliasing software. 14224084f9bSBrian Somers * Done already here to be able to alter option bits 14324084f9bSBrian Somers * during command line and configuration file processing. 14424084f9bSBrian Somers */ 145fb994b07SBrian Somers PacketAliasInit (); 14624084f9bSBrian Somers /* 14724084f9bSBrian Somers * Parse options. 14824084f9bSBrian Somers */ 14924084f9bSBrian Somers inPort = 0; 15024084f9bSBrian Somers outPort = 0; 15124084f9bSBrian Somers verbose = 0; 15224084f9bSBrian Somers inOutPort = 0; 15324084f9bSBrian Somers ifName = NULL; 15424084f9bSBrian Somers ifMTU = -1; 15524084f9bSBrian Somers background = 0; 15624084f9bSBrian Somers running = 1; 15724084f9bSBrian Somers assignAliasAddr = 0; 15824084f9bSBrian Somers aliasAddr.s_addr = INADDR_NONE; 15924084f9bSBrian Somers aliasOverhead = 12; 16024084f9bSBrian Somers dynamicMode = 0; 16159a7c613SBrian Somers logDropped = 0; 16259a7c613SBrian Somers logFacility = LOG_DAEMON; 163fb994b07SBrian Somers /* 164fb994b07SBrian Somers * Mark packet buffer empty. 165fb994b07SBrian Somers */ 166fb994b07SBrian Somers packetSock = -1; 16759a7c613SBrian Somers packetDirection = DONT_KNOW; 16824084f9bSBrian Somers 16924084f9bSBrian Somers ParseArgs (argc, argv); 17024084f9bSBrian Somers /* 17159a7c613SBrian Somers * Open syslog channel. 17259a7c613SBrian Somers */ 1734c04fa4cSRuslan Ermilov openlog ("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0), 1744c04fa4cSRuslan Ermilov logFacility); 17559a7c613SBrian Somers /* 17624084f9bSBrian Somers * Check that valid aliasing address has been given. 17724084f9bSBrian Somers */ 1780fc81af1SPhilippe Charnier if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL) 1790fc81af1SPhilippe Charnier errx (1, "aliasing address not given"); 18024084f9bSBrian Somers 1810fc81af1SPhilippe Charnier if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL) 18267a886fbSBrian Somers errx (1, "both alias address and interface " 18367a886fbSBrian Somers "name are not allowed"); 18424084f9bSBrian Somers /* 18524084f9bSBrian Somers * Check that valid port number is known. 18624084f9bSBrian Somers */ 18724084f9bSBrian Somers if (inPort != 0 || outPort != 0) 1880fc81af1SPhilippe Charnier if (inPort == 0 || outPort == 0) 1890fc81af1SPhilippe Charnier errx (1, "both input and output ports are required"); 19024084f9bSBrian Somers 19124084f9bSBrian Somers if (inPort == 0 && outPort == 0 && inOutPort == 0) 192b0f55af6SRuslan Ermilov ParseOption ("port", DEFAULT_SERVICE); 19324084f9bSBrian Somers 19424084f9bSBrian Somers /* 195f9b06d5cSBrian Somers * Check if ignored packets should be dropped. 196f9b06d5cSBrian Somers */ 197f9b06d5cSBrian Somers dropIgnoredIncoming = PacketAliasSetMode (0, 0); 198f9b06d5cSBrian Somers dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING; 199f9b06d5cSBrian Somers /* 20024084f9bSBrian Somers * Create divert sockets. Use only one socket if -p was specified 20124084f9bSBrian Somers * on command line. Otherwise, create separate sockets for 20224084f9bSBrian Somers * outgoing and incoming connnections. 20324084f9bSBrian Somers */ 20424084f9bSBrian Somers if (inOutPort) { 20524084f9bSBrian Somers 20624084f9bSBrian Somers divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 20724084f9bSBrian Somers if (divertInOut == -1) 20824084f9bSBrian Somers Quit ("Unable to create divert socket."); 20924084f9bSBrian Somers 21024084f9bSBrian Somers divertIn = -1; 21124084f9bSBrian Somers divertOut = -1; 21224084f9bSBrian Somers /* 21324084f9bSBrian Somers * Bind socket. 21424084f9bSBrian Somers */ 21524084f9bSBrian Somers 21624084f9bSBrian Somers addr.sin_family = AF_INET; 21724084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 21824084f9bSBrian Somers addr.sin_port = inOutPort; 21924084f9bSBrian Somers 22024084f9bSBrian Somers if (bind (divertInOut, 22124084f9bSBrian Somers (struct sockaddr*) &addr, 22224084f9bSBrian Somers sizeof addr) == -1) 22324084f9bSBrian Somers Quit ("Unable to bind divert socket."); 22424084f9bSBrian Somers } 22524084f9bSBrian Somers else { 22624084f9bSBrian Somers 22724084f9bSBrian Somers divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 22824084f9bSBrian Somers if (divertIn == -1) 22924084f9bSBrian Somers Quit ("Unable to create incoming divert socket."); 23024084f9bSBrian Somers 23124084f9bSBrian Somers divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 23224084f9bSBrian Somers if (divertOut == -1) 23324084f9bSBrian Somers Quit ("Unable to create outgoing divert socket."); 23424084f9bSBrian Somers 23524084f9bSBrian Somers divertInOut = -1; 23624084f9bSBrian Somers 23724084f9bSBrian Somers /* 23824084f9bSBrian Somers * Bind divert sockets. 23924084f9bSBrian Somers */ 24024084f9bSBrian Somers 24124084f9bSBrian Somers addr.sin_family = AF_INET; 24224084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 24324084f9bSBrian Somers addr.sin_port = inPort; 24424084f9bSBrian Somers 24524084f9bSBrian Somers if (bind (divertIn, 24624084f9bSBrian Somers (struct sockaddr*) &addr, 24724084f9bSBrian Somers sizeof addr) == -1) 24824084f9bSBrian Somers Quit ("Unable to bind incoming divert socket."); 24924084f9bSBrian Somers 25024084f9bSBrian Somers addr.sin_family = AF_INET; 25124084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 25224084f9bSBrian Somers addr.sin_port = outPort; 25324084f9bSBrian Somers 25424084f9bSBrian Somers if (bind (divertOut, 25524084f9bSBrian Somers (struct sockaddr*) &addr, 25624084f9bSBrian Somers sizeof addr) == -1) 25724084f9bSBrian Somers Quit ("Unable to bind outgoing divert socket."); 25824084f9bSBrian Somers } 25924084f9bSBrian Somers /* 260f2da55a2SRuslan Ermilov * Create routing socket if interface name specified and in dynamic mode. 26124084f9bSBrian Somers */ 262f2da55a2SRuslan Ermilov routeSock = -1; 263f2da55a2SRuslan Ermilov if (ifName) { 264f2da55a2SRuslan Ermilov if (dynamicMode) { 26524084f9bSBrian Somers 26624084f9bSBrian Somers routeSock = socket (PF_ROUTE, SOCK_RAW, 0); 26724084f9bSBrian Somers if (routeSock == -1) 26824084f9bSBrian Somers Quit ("Unable to create routing info socket."); 269f2da55a2SRuslan Ermilov 270f2da55a2SRuslan Ermilov assignAliasAddr = 1; 27124084f9bSBrian Somers } 27224084f9bSBrian Somers else 273f2da55a2SRuslan Ermilov SetAliasAddressFromIfName (ifName); 274f2da55a2SRuslan Ermilov } 27524084f9bSBrian Somers /* 27624084f9bSBrian Somers * Create socket for sending ICMP messages. 27724084f9bSBrian Somers */ 27824084f9bSBrian Somers icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); 27924084f9bSBrian Somers if (icmpSock == -1) 28024084f9bSBrian Somers Quit ("Unable to create ICMP socket."); 281f3d64024SBrian Somers 282f3d64024SBrian Somers /* 283f3d64024SBrian Somers * And disable reads for the socket, otherwise it slowly fills 284f3d64024SBrian Somers * up with received icmps which we do not use. 285f3d64024SBrian Somers */ 286f3d64024SBrian Somers shutdown(icmpSock, SHUT_RD); 287f3d64024SBrian Somers 28824084f9bSBrian Somers /* 28924084f9bSBrian Somers * Become a daemon unless verbose mode was requested. 29024084f9bSBrian Somers */ 29124084f9bSBrian Somers if (!verbose) 29224084f9bSBrian Somers DaemonMode (); 29324084f9bSBrian Somers /* 29424084f9bSBrian Somers * Catch signals to manage shutdown and 29524084f9bSBrian Somers * refresh of interface address. 29624084f9bSBrian Somers */ 297cd45c931SRuslan Ermilov siginterrupt(SIGTERM, 1); 298cd45c931SRuslan Ermilov siginterrupt(SIGHUP, 1); 29924084f9bSBrian Somers signal (SIGTERM, InitiateShutdown); 30024084f9bSBrian Somers signal (SIGHUP, RefreshAddr); 30124084f9bSBrian Somers /* 30224084f9bSBrian Somers * Set alias address if it has been given. 30324084f9bSBrian Somers */ 30424084f9bSBrian Somers if (aliasAddr.s_addr != INADDR_NONE) 305fb994b07SBrian Somers PacketAliasSetAddress (aliasAddr); 30624084f9bSBrian Somers /* 30724084f9bSBrian Somers * We need largest descriptor number for select. 30824084f9bSBrian Somers */ 30924084f9bSBrian Somers 31024084f9bSBrian Somers fdMax = -1; 31124084f9bSBrian Somers 31224084f9bSBrian Somers if (divertIn > fdMax) 31324084f9bSBrian Somers fdMax = divertIn; 31424084f9bSBrian Somers 31524084f9bSBrian Somers if (divertOut > fdMax) 31624084f9bSBrian Somers fdMax = divertOut; 31724084f9bSBrian Somers 31824084f9bSBrian Somers if (divertInOut > fdMax) 31924084f9bSBrian Somers fdMax = divertInOut; 32024084f9bSBrian Somers 32124084f9bSBrian Somers if (routeSock > fdMax) 32224084f9bSBrian Somers fdMax = routeSock; 32324084f9bSBrian Somers 32424084f9bSBrian Somers while (running) { 325fb994b07SBrian Somers 326fb994b07SBrian Somers if (divertInOut != -1 && !ifName && packetSock == -1) { 327fb994b07SBrian Somers /* 328fb994b07SBrian Somers * When using only one socket, just call 329fb994b07SBrian Somers * DoAliasing repeatedly to process packets. 330fb994b07SBrian Somers */ 33159a7c613SBrian Somers DoAliasing (divertInOut, DONT_KNOW); 332fb994b07SBrian Somers continue; 333fb994b07SBrian Somers } 33424084f9bSBrian Somers /* 33524084f9bSBrian Somers * Build read mask from socket descriptors to select. 33624084f9bSBrian Somers */ 33724084f9bSBrian Somers FD_ZERO (&readMask); 338fb994b07SBrian Somers FD_ZERO (&writeMask); 33924084f9bSBrian Somers 340fb994b07SBrian Somers /* 341fb994b07SBrian Somers * If there is unsent packet in buffer, use select 342fb994b07SBrian Somers * to check when socket comes writable again. 343fb994b07SBrian Somers */ 344fb994b07SBrian Somers if (packetSock != -1) { 345fb994b07SBrian Somers 346fb994b07SBrian Somers FD_SET (packetSock, &writeMask); 347fb994b07SBrian Somers } 348fb994b07SBrian Somers else { 349fb994b07SBrian Somers /* 350fb994b07SBrian Somers * No unsent packet exists - safe to check if 351fb994b07SBrian Somers * new ones are available. 352fb994b07SBrian Somers */ 35324084f9bSBrian Somers if (divertIn != -1) 35424084f9bSBrian Somers FD_SET (divertIn, &readMask); 35524084f9bSBrian Somers 35624084f9bSBrian Somers if (divertOut != -1) 35724084f9bSBrian Somers FD_SET (divertOut, &readMask); 35824084f9bSBrian Somers 35924084f9bSBrian Somers if (divertInOut != -1) 36024084f9bSBrian Somers FD_SET (divertInOut, &readMask); 361fb994b07SBrian Somers } 362fb994b07SBrian Somers /* 363fb994b07SBrian Somers * Routing info is processed always. 364fb994b07SBrian Somers */ 36524084f9bSBrian Somers if (routeSock != -1) 36624084f9bSBrian Somers FD_SET (routeSock, &readMask); 36724084f9bSBrian Somers 36824084f9bSBrian Somers if (select (fdMax + 1, 36924084f9bSBrian Somers &readMask, 370fb994b07SBrian Somers &writeMask, 37124084f9bSBrian Somers NULL, 37224084f9bSBrian Somers NULL) == -1) { 37324084f9bSBrian Somers 37424084f9bSBrian Somers if (errno == EINTR) 37524084f9bSBrian Somers continue; 37624084f9bSBrian Somers 37724084f9bSBrian Somers Quit ("Select failed."); 37824084f9bSBrian Somers } 37924084f9bSBrian Somers 380fb994b07SBrian Somers if (packetSock != -1) 381fb994b07SBrian Somers if (FD_ISSET (packetSock, &writeMask)) 382fb994b07SBrian Somers FlushPacketBuffer (packetSock); 383fb994b07SBrian Somers 38424084f9bSBrian Somers if (divertIn != -1) 38524084f9bSBrian Somers if (FD_ISSET (divertIn, &readMask)) 38659a7c613SBrian Somers DoAliasing (divertIn, INPUT); 38724084f9bSBrian Somers 38824084f9bSBrian Somers if (divertOut != -1) 38924084f9bSBrian Somers if (FD_ISSET (divertOut, &readMask)) 39059a7c613SBrian Somers DoAliasing (divertOut, OUTPUT); 39124084f9bSBrian Somers 39224084f9bSBrian Somers if (divertInOut != -1) 39324084f9bSBrian Somers if (FD_ISSET (divertInOut, &readMask)) 39459a7c613SBrian Somers DoAliasing (divertInOut, DONT_KNOW); 39524084f9bSBrian Somers 39624084f9bSBrian Somers if (routeSock != -1) 39724084f9bSBrian Somers if (FD_ISSET (routeSock, &readMask)) 39824084f9bSBrian Somers HandleRoutingInfo (routeSock); 39924084f9bSBrian Somers } 40024084f9bSBrian Somers 40124084f9bSBrian Somers if (background) 40224084f9bSBrian Somers unlink (PIDFILE); 40324084f9bSBrian Somers 40424084f9bSBrian Somers return 0; 40524084f9bSBrian Somers } 40624084f9bSBrian Somers 40724084f9bSBrian Somers static void DaemonMode () 40824084f9bSBrian Somers { 40924084f9bSBrian Somers FILE* pidFile; 41024084f9bSBrian Somers 41124084f9bSBrian Somers daemon (0, 0); 41224084f9bSBrian Somers background = 1; 41324084f9bSBrian Somers 41424084f9bSBrian Somers pidFile = fopen (PIDFILE, "w"); 41524084f9bSBrian Somers if (pidFile) { 41624084f9bSBrian Somers 41724084f9bSBrian Somers fprintf (pidFile, "%d\n", getpid ()); 41824084f9bSBrian Somers fclose (pidFile); 41924084f9bSBrian Somers } 42024084f9bSBrian Somers } 42124084f9bSBrian Somers 42224084f9bSBrian Somers static void ParseArgs (int argc, char** argv) 42324084f9bSBrian Somers { 42424084f9bSBrian Somers int arg; 42524084f9bSBrian Somers char* opt; 42624084f9bSBrian Somers char parmBuf[256]; 42730395bb5SJosef Karthauser int len; /* bounds checking */ 42824084f9bSBrian Somers 42924084f9bSBrian Somers for (arg = 1; arg < argc; arg++) { 43024084f9bSBrian Somers 43124084f9bSBrian Somers opt = argv[arg]; 43224084f9bSBrian Somers if (*opt != '-') { 43324084f9bSBrian Somers 4340fc81af1SPhilippe Charnier warnx ("invalid option %s", opt); 43524084f9bSBrian Somers Usage (); 43624084f9bSBrian Somers } 43724084f9bSBrian Somers 43824084f9bSBrian Somers parmBuf[0] = '\0'; 43930395bb5SJosef Karthauser len = 0; 44024084f9bSBrian Somers 44124084f9bSBrian Somers while (arg < argc - 1) { 44224084f9bSBrian Somers 44324084f9bSBrian Somers if (argv[arg + 1][0] == '-') 44424084f9bSBrian Somers break; 44524084f9bSBrian Somers 44630395bb5SJosef Karthauser if (len) { 44730395bb5SJosef Karthauser strncat (parmBuf, " ", sizeof(parmBuf) - (len + 1)); 44830395bb5SJosef Karthauser len += strlen(parmBuf + len); 44924084f9bSBrian Somers } 45024084f9bSBrian Somers 45130395bb5SJosef Karthauser ++arg; 45230395bb5SJosef Karthauser strncat (parmBuf, argv[arg], sizeof(parmBuf) - (len + 1)); 45330395bb5SJosef Karthauser len += strlen(parmBuf + len); 45430395bb5SJosef Karthauser 45530395bb5SJosef Karthauser } 45630395bb5SJosef Karthauser 457b0f55af6SRuslan Ermilov ParseOption (opt + 1, (len ? parmBuf : NULL)); 45830395bb5SJosef Karthauser 45924084f9bSBrian Somers } 46024084f9bSBrian Somers } 46124084f9bSBrian Somers 46259a7c613SBrian Somers static void DoAliasing (int fd, int direction) 46324084f9bSBrian Somers { 46424084f9bSBrian Somers int bytes; 46524084f9bSBrian Somers int origBytes; 466f9b06d5cSBrian Somers int status; 46724084f9bSBrian Somers int addrSize; 46824084f9bSBrian Somers struct ip* ip; 46924084f9bSBrian Somers 47024084f9bSBrian Somers if (assignAliasAddr) { 47124084f9bSBrian Somers 47224084f9bSBrian Somers SetAliasAddressFromIfName (ifName); 47324084f9bSBrian Somers assignAliasAddr = 0; 47424084f9bSBrian Somers } 47524084f9bSBrian Somers /* 47624084f9bSBrian Somers * Get packet from socket. 47724084f9bSBrian Somers */ 478fb994b07SBrian Somers addrSize = sizeof packetAddr; 47924084f9bSBrian Somers origBytes = recvfrom (fd, 480fb994b07SBrian Somers packetBuf, 481fb994b07SBrian Somers sizeof packetBuf, 48224084f9bSBrian Somers 0, 483fb994b07SBrian Somers (struct sockaddr*) &packetAddr, 48424084f9bSBrian Somers &addrSize); 48524084f9bSBrian Somers 48624084f9bSBrian Somers if (origBytes == -1) { 48724084f9bSBrian Somers 48824084f9bSBrian Somers if (errno != EINTR) 4890fc81af1SPhilippe Charnier Warn ("read from divert socket failed"); 49024084f9bSBrian Somers 49124084f9bSBrian Somers return; 49224084f9bSBrian Somers } 49324084f9bSBrian Somers /* 49424084f9bSBrian Somers * This is a IP packet. 49524084f9bSBrian Somers */ 496fb994b07SBrian Somers ip = (struct ip*) packetBuf; 497ebe70c8fSWarner Losh if (direction == DONT_KNOW) { 49859a7c613SBrian Somers if (packetAddr.sin_addr.s_addr == INADDR_ANY) 49959a7c613SBrian Somers direction = OUTPUT; 50059a7c613SBrian Somers else 50159a7c613SBrian Somers direction = INPUT; 502ebe70c8fSWarner Losh } 50324084f9bSBrian Somers 50424084f9bSBrian Somers if (verbose) { 50524084f9bSBrian Somers /* 50624084f9bSBrian Somers * Print packet direction and protocol type. 50724084f9bSBrian Somers */ 50859a7c613SBrian Somers printf (direction == OUTPUT ? "Out " : "In "); 50924084f9bSBrian Somers 51024084f9bSBrian Somers switch (ip->ip_p) { 51124084f9bSBrian Somers case IPPROTO_TCP: 51224084f9bSBrian Somers printf ("[TCP] "); 51324084f9bSBrian Somers break; 51424084f9bSBrian Somers 51524084f9bSBrian Somers case IPPROTO_UDP: 51624084f9bSBrian Somers printf ("[UDP] "); 51724084f9bSBrian Somers break; 51824084f9bSBrian Somers 51924084f9bSBrian Somers case IPPROTO_ICMP: 52024084f9bSBrian Somers printf ("[ICMP] "); 52124084f9bSBrian Somers break; 52224084f9bSBrian Somers 52324084f9bSBrian Somers default: 52459a7c613SBrian Somers printf ("[%d] ", ip->ip_p); 52524084f9bSBrian Somers break; 52624084f9bSBrian Somers } 52724084f9bSBrian Somers /* 52824084f9bSBrian Somers * Print addresses. 52924084f9bSBrian Somers */ 53024084f9bSBrian Somers PrintPacket (ip); 53124084f9bSBrian Somers } 53224084f9bSBrian Somers 53359a7c613SBrian Somers if (direction == OUTPUT) { 53424084f9bSBrian Somers /* 53524084f9bSBrian Somers * Outgoing packets. Do aliasing. 53624084f9bSBrian Somers */ 537fb994b07SBrian Somers PacketAliasOut (packetBuf, IP_MAXPACKET); 53824084f9bSBrian Somers } 53924084f9bSBrian Somers else { 54059a7c613SBrian Somers 54124084f9bSBrian Somers /* 54224084f9bSBrian Somers * Do aliasing. 54324084f9bSBrian Somers */ 544f9b06d5cSBrian Somers status = PacketAliasIn (packetBuf, IP_MAXPACKET); 545f9b06d5cSBrian Somers if (status == PKT_ALIAS_IGNORED && 546f9b06d5cSBrian Somers dropIgnoredIncoming) { 547f9b06d5cSBrian Somers 54859a7c613SBrian Somers if (verbose) 549f9b06d5cSBrian Somers printf (" dropped.\n"); 55059a7c613SBrian Somers 55159a7c613SBrian Somers if (logDropped) 55259a7c613SBrian Somers SyslogPacket (ip, LOG_WARNING, "denied"); 55359a7c613SBrian Somers 554f9b06d5cSBrian Somers return; 555f9b06d5cSBrian Somers } 55624084f9bSBrian Somers } 55724084f9bSBrian Somers /* 55824084f9bSBrian Somers * Length might have changed during aliasing. 55924084f9bSBrian Somers */ 56024084f9bSBrian Somers bytes = ntohs (ip->ip_len); 56124084f9bSBrian Somers /* 56224084f9bSBrian Somers * Update alias overhead size for outgoing packets. 56324084f9bSBrian Somers */ 56459a7c613SBrian Somers if (direction == OUTPUT && 56524084f9bSBrian Somers bytes - origBytes > aliasOverhead) 56624084f9bSBrian Somers aliasOverhead = bytes - origBytes; 56724084f9bSBrian Somers 56824084f9bSBrian Somers if (verbose) { 56924084f9bSBrian Somers 57024084f9bSBrian Somers /* 57124084f9bSBrian Somers * Print addresses after aliasing. 57224084f9bSBrian Somers */ 57324084f9bSBrian Somers printf (" aliased to\n"); 57424084f9bSBrian Somers printf (" "); 57524084f9bSBrian Somers PrintPacket (ip); 57624084f9bSBrian Somers printf ("\n"); 57724084f9bSBrian Somers } 578fb994b07SBrian Somers 579fb994b07SBrian Somers packetLen = bytes; 580fb994b07SBrian Somers packetSock = fd; 58159a7c613SBrian Somers packetDirection = direction; 58259a7c613SBrian Somers 583fb994b07SBrian Somers FlushPacketBuffer (fd); 584fb994b07SBrian Somers } 585fb994b07SBrian Somers 586fb994b07SBrian Somers static void FlushPacketBuffer (int fd) 587fb994b07SBrian Somers { 588fb994b07SBrian Somers int wrote; 589fb994b07SBrian Somers char msgBuf[80]; 59024084f9bSBrian Somers /* 59124084f9bSBrian Somers * Put packet back for processing. 59224084f9bSBrian Somers */ 59324084f9bSBrian Somers wrote = sendto (fd, 594fb994b07SBrian Somers packetBuf, 595fb994b07SBrian Somers packetLen, 59624084f9bSBrian Somers 0, 597fb994b07SBrian Somers (struct sockaddr*) &packetAddr, 598fb994b07SBrian Somers sizeof packetAddr); 59924084f9bSBrian Somers 600fb994b07SBrian Somers if (wrote != packetLen) { 601fb994b07SBrian Somers /* 602fb994b07SBrian Somers * If buffer space is not available, 603fb994b07SBrian Somers * just return. Main loop will take care of 604fb994b07SBrian Somers * retrying send when space becomes available. 605fb994b07SBrian Somers */ 606fb994b07SBrian Somers if (errno == ENOBUFS) 607fb994b07SBrian Somers return; 60824084f9bSBrian Somers 60924084f9bSBrian Somers if (errno == EMSGSIZE) { 61024084f9bSBrian Somers 61159a7c613SBrian Somers if (packetDirection == OUTPUT && 61224084f9bSBrian Somers ifMTU != -1) 61324084f9bSBrian Somers SendNeedFragIcmp (icmpSock, 614fb994b07SBrian Somers (struct ip*) packetBuf, 61524084f9bSBrian Somers ifMTU - aliasOverhead); 61624084f9bSBrian Somers } 61724084f9bSBrian Somers else { 61824084f9bSBrian Somers 6190fc81af1SPhilippe Charnier sprintf (msgBuf, "failed to write packet back"); 62024084f9bSBrian Somers Warn (msgBuf); 62124084f9bSBrian Somers } 62224084f9bSBrian Somers } 623fb994b07SBrian Somers 624fb994b07SBrian Somers packetSock = -1; 62524084f9bSBrian Somers } 62624084f9bSBrian Somers 62724084f9bSBrian Somers static void HandleRoutingInfo (int fd) 62824084f9bSBrian Somers { 62924084f9bSBrian Somers int bytes; 63024084f9bSBrian Somers struct if_msghdr ifMsg; 63124084f9bSBrian Somers /* 63224084f9bSBrian Somers * Get packet from socket. 63324084f9bSBrian Somers */ 63424084f9bSBrian Somers bytes = read (fd, &ifMsg, sizeof ifMsg); 63524084f9bSBrian Somers if (bytes == -1) { 63624084f9bSBrian Somers 6370fc81af1SPhilippe Charnier Warn ("read from routing socket failed"); 63824084f9bSBrian Somers return; 63924084f9bSBrian Somers } 64024084f9bSBrian Somers 64124084f9bSBrian Somers if (ifMsg.ifm_version != RTM_VERSION) { 64224084f9bSBrian Somers 6430fc81af1SPhilippe Charnier Warn ("unexpected packet read from routing socket"); 64424084f9bSBrian Somers return; 64524084f9bSBrian Somers } 64624084f9bSBrian Somers 64724084f9bSBrian Somers if (verbose) 6486f3dbe5eSRuslan Ermilov printf ("Routing message %#x received.\n", ifMsg.ifm_type); 64924084f9bSBrian Somers 6506f3dbe5eSRuslan Ermilov if ((ifMsg.ifm_type == RTM_NEWADDR || ifMsg.ifm_type == RTM_IFINFO) && 6516f3dbe5eSRuslan Ermilov ifMsg.ifm_index == ifIndex) { 6526f3dbe5eSRuslan Ermilov if (verbose) 6536f3dbe5eSRuslan Ermilov printf("Interface address/MTU has probably changed.\n"); 65424084f9bSBrian Somers assignAliasAddr = 1; 65524084f9bSBrian Somers } 6566f3dbe5eSRuslan Ermilov } 65724084f9bSBrian Somers 65824084f9bSBrian Somers static void PrintPacket (struct ip* ip) 65924084f9bSBrian Somers { 66059a7c613SBrian Somers printf ("%s", FormatPacket (ip)); 66159a7c613SBrian Somers } 66259a7c613SBrian Somers 663902cb50aSBrian Somers static void SyslogPacket (struct ip* ip, int priority, const char *label) 66459a7c613SBrian Somers { 66559a7c613SBrian Somers syslog (priority, "%s %s", label, FormatPacket (ip)); 66659a7c613SBrian Somers } 66759a7c613SBrian Somers 66859a7c613SBrian Somers static char* FormatPacket (struct ip* ip) 66959a7c613SBrian Somers { 67059a7c613SBrian Somers static char buf[256]; 67124084f9bSBrian Somers struct tcphdr* tcphdr; 67259a7c613SBrian Somers struct udphdr* udphdr; 67359a7c613SBrian Somers struct icmp* icmphdr; 67459a7c613SBrian Somers char src[20]; 67559a7c613SBrian Somers char dst[20]; 67624084f9bSBrian Somers 67759a7c613SBrian Somers strcpy (src, inet_ntoa (ip->ip_src)); 67859a7c613SBrian Somers strcpy (dst, inet_ntoa (ip->ip_dst)); 67959a7c613SBrian Somers 68059a7c613SBrian Somers switch (ip->ip_p) { 68159a7c613SBrian Somers case IPPROTO_TCP: 68224084f9bSBrian Somers tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2)); 68359a7c613SBrian Somers sprintf (buf, "[TCP] %s:%d -> %s:%d", 68459a7c613SBrian Somers src, 68559a7c613SBrian Somers ntohs (tcphdr->th_sport), 68659a7c613SBrian Somers dst, 68759a7c613SBrian Somers ntohs (tcphdr->th_dport)); 68859a7c613SBrian Somers break; 68924084f9bSBrian Somers 69059a7c613SBrian Somers case IPPROTO_UDP: 69159a7c613SBrian Somers udphdr = (struct udphdr*) ((char*) ip + (ip->ip_hl << 2)); 69259a7c613SBrian Somers sprintf (buf, "[UDP] %s:%d -> %s:%d", 69359a7c613SBrian Somers src, 69459a7c613SBrian Somers ntohs (udphdr->uh_sport), 69559a7c613SBrian Somers dst, 69659a7c613SBrian Somers ntohs (udphdr->uh_dport)); 69759a7c613SBrian Somers break; 69824084f9bSBrian Somers 69959a7c613SBrian Somers case IPPROTO_ICMP: 70059a7c613SBrian Somers icmphdr = (struct icmp*) ((char*) ip + (ip->ip_hl << 2)); 701b71e869dSBrian Somers sprintf (buf, "[ICMP] %s -> %s %u(%u)", 70259a7c613SBrian Somers src, 70359a7c613SBrian Somers dst, 704b71e869dSBrian Somers icmphdr->icmp_type, 705b71e869dSBrian Somers icmphdr->icmp_code); 70659a7c613SBrian Somers break; 70759a7c613SBrian Somers 70859a7c613SBrian Somers default: 70959a7c613SBrian Somers sprintf (buf, "[%d] %s -> %s ", ip->ip_p, src, dst); 71059a7c613SBrian Somers break; 71159a7c613SBrian Somers } 71259a7c613SBrian Somers 71359a7c613SBrian Somers return buf; 71424084f9bSBrian Somers } 71524084f9bSBrian Somers 7164c04fa4cSRuslan Ermilov static void 7174c04fa4cSRuslan Ermilov SetAliasAddressFromIfName(const char *ifn) 71824084f9bSBrian Somers { 7194c04fa4cSRuslan Ermilov size_t needed; 7204c04fa4cSRuslan Ermilov int mib[6]; 7214c04fa4cSRuslan Ermilov char *buf, *lim, *next; 7224c04fa4cSRuslan Ermilov struct if_msghdr *ifm; 7234c04fa4cSRuslan Ermilov struct ifa_msghdr *ifam; 7244c04fa4cSRuslan Ermilov struct sockaddr_dl *sdl; 7254c04fa4cSRuslan Ermilov struct sockaddr_in *sin; 72624084f9bSBrian Somers 7274c04fa4cSRuslan Ermilov mib[0] = CTL_NET; 7284c04fa4cSRuslan Ermilov mib[1] = PF_ROUTE; 7294c04fa4cSRuslan Ermilov mib[2] = 0; 7304c04fa4cSRuslan Ermilov mib[3] = AF_INET; /* Only IP addresses please */ 7314c04fa4cSRuslan Ermilov mib[4] = NET_RT_IFLIST; 7324c04fa4cSRuslan Ermilov mib[5] = 0; /* ifIndex??? */ 73324084f9bSBrian Somers /* 73424084f9bSBrian Somers * Get interface data. 73524084f9bSBrian Somers */ 7364c04fa4cSRuslan Ermilov if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) 7374c04fa4cSRuslan Ermilov err(1, "iflist-sysctl-estimate"); 7384c04fa4cSRuslan Ermilov if ((buf = malloc(needed)) == NULL) 7394c04fa4cSRuslan Ermilov errx(1, "malloc failed"); 7404c04fa4cSRuslan Ermilov if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) 7414c04fa4cSRuslan Ermilov err(1, "iflist-sysctl-get"); 7424c04fa4cSRuslan Ermilov lim = buf + needed; 74324084f9bSBrian Somers /* 74424084f9bSBrian Somers * Loop through interfaces until one with 74524084f9bSBrian Somers * given name is found. This is done to 74624084f9bSBrian Somers * find correct interface index for routing 74724084f9bSBrian Somers * message processing. 74824084f9bSBrian Somers */ 7494c04fa4cSRuslan Ermilov ifIndex = 0; 7504c04fa4cSRuslan Ermilov next = buf; 7514c04fa4cSRuslan Ermilov while (next < lim) { 7524c04fa4cSRuslan Ermilov ifm = (struct if_msghdr *)next; 7534c04fa4cSRuslan Ermilov next += ifm->ifm_msglen; 7544c04fa4cSRuslan Ermilov if (ifm->ifm_version != RTM_VERSION) { 7554c04fa4cSRuslan Ermilov if (verbose) 7564c04fa4cSRuslan Ermilov warnx("routing message version %d " 7574c04fa4cSRuslan Ermilov "not understood", ifm->ifm_version); 7584c04fa4cSRuslan Ermilov continue; 7594c04fa4cSRuslan Ermilov } 7604c04fa4cSRuslan Ermilov if (ifm->ifm_type == RTM_IFINFO) { 7614c04fa4cSRuslan Ermilov sdl = (struct sockaddr_dl *)(ifm + 1); 7624c04fa4cSRuslan Ermilov if (strlen(ifn) == sdl->sdl_nlen && 7634c04fa4cSRuslan Ermilov strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) { 7644c04fa4cSRuslan Ermilov ifIndex = ifm->ifm_index; 7654c04fa4cSRuslan Ermilov ifMTU = ifm->ifm_data.ifi_mtu; 76624084f9bSBrian Somers break; 76724084f9bSBrian Somers } 76824084f9bSBrian Somers } 76924084f9bSBrian Somers } 7704c04fa4cSRuslan Ermilov if (!ifIndex) 7714c04fa4cSRuslan Ermilov errx(1, "unknown interface name %s", ifn); 77224084f9bSBrian Somers /* 77324084f9bSBrian Somers * Get interface address. 77424084f9bSBrian Somers */ 7754c04fa4cSRuslan Ermilov sin = NULL; 7764c04fa4cSRuslan Ermilov while (next < lim) { 7774c04fa4cSRuslan Ermilov ifam = (struct ifa_msghdr *)next; 7784c04fa4cSRuslan Ermilov next += ifam->ifam_msglen; 7794c04fa4cSRuslan Ermilov if (ifam->ifam_version != RTM_VERSION) { 7804c04fa4cSRuslan Ermilov if (verbose) 7814c04fa4cSRuslan Ermilov warnx("routing message version %d " 7824c04fa4cSRuslan Ermilov "not understood", ifam->ifam_version); 7834c04fa4cSRuslan Ermilov continue; 7844c04fa4cSRuslan Ermilov } 7854c04fa4cSRuslan Ermilov if (ifam->ifam_type != RTM_NEWADDR) 7864c04fa4cSRuslan Ermilov break; 7874c04fa4cSRuslan Ermilov if (ifam->ifam_addrs & RTA_IFA) { 7884c04fa4cSRuslan Ermilov int i; 7894c04fa4cSRuslan Ermilov char *cp = (char *)(ifam + 1); 79024084f9bSBrian Somers 7914c04fa4cSRuslan Ermilov #define ROUNDUP(a) \ 7924c04fa4cSRuslan Ermilov ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 7934c04fa4cSRuslan Ermilov #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 7944c04fa4cSRuslan Ermilov 7954c04fa4cSRuslan Ermilov for (i = 1; i < RTA_IFA; i <<= 1) 7964c04fa4cSRuslan Ermilov if (ifam->ifam_addrs & i) 7974c04fa4cSRuslan Ermilov ADVANCE(cp, (struct sockaddr *)cp); 7984c04fa4cSRuslan Ermilov if (((struct sockaddr *)cp)->sa_family == AF_INET) { 7994c04fa4cSRuslan Ermilov sin = (struct sockaddr_in *)cp; 8004c04fa4cSRuslan Ermilov break; 8014c04fa4cSRuslan Ermilov } 8024c04fa4cSRuslan Ermilov } 8034c04fa4cSRuslan Ermilov } 8044c04fa4cSRuslan Ermilov if (sin == NULL) 8054c04fa4cSRuslan Ermilov errx(1, "%s: cannot get interface address", ifn); 8064c04fa4cSRuslan Ermilov 8074c04fa4cSRuslan Ermilov PacketAliasSetAddress(sin->sin_addr); 80824084f9bSBrian Somers syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes", 8094c04fa4cSRuslan Ermilov inet_ntoa(sin->sin_addr), ifMTU); 81024084f9bSBrian Somers 8114c04fa4cSRuslan Ermilov free(buf); 81224084f9bSBrian Somers } 81324084f9bSBrian Somers 814902cb50aSBrian Somers void Quit (const char* msg) 81524084f9bSBrian Somers { 81624084f9bSBrian Somers Warn (msg); 81724084f9bSBrian Somers exit (1); 81824084f9bSBrian Somers } 81924084f9bSBrian Somers 820902cb50aSBrian Somers void Warn (const char* msg) 82124084f9bSBrian Somers { 82224084f9bSBrian Somers if (background) 82324084f9bSBrian Somers syslog (LOG_ALERT, "%s (%m)", msg); 82424084f9bSBrian Somers else 8250fc81af1SPhilippe Charnier warn (msg); 82624084f9bSBrian Somers } 82724084f9bSBrian Somers 828902cb50aSBrian Somers static void RefreshAddr (int sig) 82924084f9bSBrian Somers { 83024084f9bSBrian Somers if (ifName) 83124084f9bSBrian Somers assignAliasAddr = 1; 83224084f9bSBrian Somers } 83324084f9bSBrian Somers 834902cb50aSBrian Somers static void InitiateShutdown (int sig) 83524084f9bSBrian Somers { 83624084f9bSBrian Somers /* 83724084f9bSBrian Somers * Start timer to allow kernel gracefully 83824084f9bSBrian Somers * shutdown existing connections when system 83924084f9bSBrian Somers * is shut down. 84024084f9bSBrian Somers */ 841cd45c931SRuslan Ermilov siginterrupt(SIGALRM, 1); 84224084f9bSBrian Somers signal (SIGALRM, Shutdown); 84324084f9bSBrian Somers alarm (10); 84424084f9bSBrian Somers } 84524084f9bSBrian Somers 846902cb50aSBrian Somers static void Shutdown (int sig) 84724084f9bSBrian Somers { 84824084f9bSBrian Somers running = 0; 84924084f9bSBrian Somers } 85024084f9bSBrian Somers 85124084f9bSBrian Somers /* 85224084f9bSBrian Somers * Different options recognized by this program. 85324084f9bSBrian Somers */ 85424084f9bSBrian Somers 85524084f9bSBrian Somers enum Option { 85624084f9bSBrian Somers 85724084f9bSBrian Somers PacketAliasOption, 85824084f9bSBrian Somers Verbose, 85924084f9bSBrian Somers InPort, 86024084f9bSBrian Somers OutPort, 86124084f9bSBrian Somers Port, 86224084f9bSBrian Somers AliasAddress, 86311c2b3bfSRuslan Ermilov TargetAddress, 86424084f9bSBrian Somers InterfaceName, 86524084f9bSBrian Somers RedirectPort, 8664330006dSRuslan Ermilov RedirectProto, 86724084f9bSBrian Somers RedirectAddress, 86824084f9bSBrian Somers ConfigFile, 86959a7c613SBrian Somers DynamicMode, 87059a7c613SBrian Somers PptpAlias, 87159a7c613SBrian Somers ProxyRule, 87259a7c613SBrian Somers LogDenied, 87359a7c613SBrian Somers LogFacility 87424084f9bSBrian Somers }; 87524084f9bSBrian Somers 87624084f9bSBrian Somers enum Param { 87724084f9bSBrian Somers 87824084f9bSBrian Somers YesNo, 87924084f9bSBrian Somers Numeric, 88024084f9bSBrian Somers String, 88124084f9bSBrian Somers None, 88224084f9bSBrian Somers Address, 88324084f9bSBrian Somers Service 88424084f9bSBrian Somers }; 88524084f9bSBrian Somers 88624084f9bSBrian Somers /* 88724084f9bSBrian Somers * Option information structure (used by ParseOption). 88824084f9bSBrian Somers */ 88924084f9bSBrian Somers 89024084f9bSBrian Somers struct OptionInfo { 89124084f9bSBrian Somers 89224084f9bSBrian Somers enum Option type; 89324084f9bSBrian Somers int packetAliasOpt; 89424084f9bSBrian Somers enum Param parm; 895902cb50aSBrian Somers const char* parmDescription; 896902cb50aSBrian Somers const char* description; 897902cb50aSBrian Somers const char* name; 898902cb50aSBrian Somers const char* shortName; 89924084f9bSBrian Somers }; 90024084f9bSBrian Somers 90124084f9bSBrian Somers /* 90224084f9bSBrian Somers * Table of known options. 90324084f9bSBrian Somers */ 90424084f9bSBrian Somers 90524084f9bSBrian Somers static struct OptionInfo optionTable[] = { 90624084f9bSBrian Somers 90724084f9bSBrian Somers { PacketAliasOption, 90824084f9bSBrian Somers PKT_ALIAS_UNREGISTERED_ONLY, 90924084f9bSBrian Somers YesNo, 91024084f9bSBrian Somers "[yes|no]", 91124084f9bSBrian Somers "alias only unregistered addresses", 91224084f9bSBrian Somers "unregistered_only", 91324084f9bSBrian Somers "u" }, 91424084f9bSBrian Somers 91524084f9bSBrian Somers { PacketAliasOption, 91624084f9bSBrian Somers PKT_ALIAS_LOG, 91724084f9bSBrian Somers YesNo, 91824084f9bSBrian Somers "[yes|no]", 91924084f9bSBrian Somers "enable logging", 92024084f9bSBrian Somers "log", 92124084f9bSBrian Somers "l" }, 92224084f9bSBrian Somers 92324084f9bSBrian Somers { PacketAliasOption, 92459a7c613SBrian Somers PKT_ALIAS_PROXY_ONLY, 92559a7c613SBrian Somers YesNo, 92659a7c613SBrian Somers "[yes|no]", 92759a7c613SBrian Somers "proxy only", 92859a7c613SBrian Somers "proxy_only", 92959a7c613SBrian Somers NULL }, 93059a7c613SBrian Somers 93159a7c613SBrian Somers { PacketAliasOption, 93259a7c613SBrian Somers PKT_ALIAS_REVERSE, 93359a7c613SBrian Somers YesNo, 93459a7c613SBrian Somers "[yes|no]", 93559a7c613SBrian Somers "operate in reverse mode", 93659a7c613SBrian Somers "reverse", 93759a7c613SBrian Somers NULL }, 93859a7c613SBrian Somers 93959a7c613SBrian Somers { PacketAliasOption, 94024084f9bSBrian Somers PKT_ALIAS_DENY_INCOMING, 94124084f9bSBrian Somers YesNo, 94224084f9bSBrian Somers "[yes|no]", 94324084f9bSBrian Somers "allow incoming connections", 94424084f9bSBrian Somers "deny_incoming", 94524084f9bSBrian Somers "d" }, 94624084f9bSBrian Somers 94724084f9bSBrian Somers { PacketAliasOption, 94824084f9bSBrian Somers PKT_ALIAS_USE_SOCKETS, 94924084f9bSBrian Somers YesNo, 95024084f9bSBrian Somers "[yes|no]", 95124084f9bSBrian Somers "use sockets to inhibit port conflict", 95224084f9bSBrian Somers "use_sockets", 95324084f9bSBrian Somers "s" }, 95424084f9bSBrian Somers 95524084f9bSBrian Somers { PacketAliasOption, 95624084f9bSBrian Somers PKT_ALIAS_SAME_PORTS, 95724084f9bSBrian Somers YesNo, 95824084f9bSBrian Somers "[yes|no]", 95924084f9bSBrian Somers "try to keep original port numbers for connections", 96024084f9bSBrian Somers "same_ports", 96124084f9bSBrian Somers "m" }, 96224084f9bSBrian Somers 96324084f9bSBrian Somers { Verbose, 96424084f9bSBrian Somers 0, 96524084f9bSBrian Somers YesNo, 96624084f9bSBrian Somers "[yes|no]", 96724084f9bSBrian Somers "verbose mode, dump packet information", 96824084f9bSBrian Somers "verbose", 96924084f9bSBrian Somers "v" }, 97024084f9bSBrian Somers 97124084f9bSBrian Somers { DynamicMode, 97224084f9bSBrian Somers 0, 97324084f9bSBrian Somers YesNo, 97424084f9bSBrian Somers "[yes|no]", 97524084f9bSBrian Somers "dynamic mode, automatically detect interface address changes", 97624084f9bSBrian Somers "dynamic", 97724084f9bSBrian Somers NULL }, 97824084f9bSBrian Somers 97924084f9bSBrian Somers { InPort, 98024084f9bSBrian Somers 0, 98124084f9bSBrian Somers Service, 98224084f9bSBrian Somers "number|service_name", 98324084f9bSBrian Somers "set port for incoming packets", 98424084f9bSBrian Somers "in_port", 98524084f9bSBrian Somers "i" }, 98624084f9bSBrian Somers 98724084f9bSBrian Somers { OutPort, 98824084f9bSBrian Somers 0, 98924084f9bSBrian Somers Service, 99024084f9bSBrian Somers "number|service_name", 99124084f9bSBrian Somers "set port for outgoing packets", 99224084f9bSBrian Somers "out_port", 99324084f9bSBrian Somers "o" }, 99424084f9bSBrian Somers 99524084f9bSBrian Somers { Port, 99624084f9bSBrian Somers 0, 99724084f9bSBrian Somers Service, 99824084f9bSBrian Somers "number|service_name", 99924084f9bSBrian Somers "set port (defaults to natd/divert)", 100024084f9bSBrian Somers "port", 100124084f9bSBrian Somers "p" }, 100224084f9bSBrian Somers 100324084f9bSBrian Somers { AliasAddress, 100424084f9bSBrian Somers 0, 100524084f9bSBrian Somers Address, 100624084f9bSBrian Somers "x.x.x.x", 100724084f9bSBrian Somers "address to use for aliasing", 100824084f9bSBrian Somers "alias_address", 100924084f9bSBrian Somers "a" }, 101024084f9bSBrian Somers 101111c2b3bfSRuslan Ermilov { TargetAddress, 101211c2b3bfSRuslan Ermilov 0, 101311c2b3bfSRuslan Ermilov Address, 101411c2b3bfSRuslan Ermilov "x.x.x.x", 101511c2b3bfSRuslan Ermilov "address to use for incoming sessions", 101611c2b3bfSRuslan Ermilov "target_address", 101711c2b3bfSRuslan Ermilov "t" }, 101811c2b3bfSRuslan Ermilov 101924084f9bSBrian Somers { InterfaceName, 102024084f9bSBrian Somers 0, 102124084f9bSBrian Somers String, 102224084f9bSBrian Somers "network_if_name", 102324084f9bSBrian Somers "take aliasing address from interface", 102424084f9bSBrian Somers "interface", 102524084f9bSBrian Somers "n" }, 102624084f9bSBrian Somers 102759a7c613SBrian Somers { ProxyRule, 102824084f9bSBrian Somers 0, 102924084f9bSBrian Somers String, 103059a7c613SBrian Somers "[type encode_ip_hdr|encode_tcp_stream] port xxxx server " 103159a7c613SBrian Somers "a.b.c.d:yyyy", 103259a7c613SBrian Somers "add transparent proxying / destination NAT", 103359a7c613SBrian Somers "proxy_rule", 103424084f9bSBrian Somers NULL }, 103524084f9bSBrian Somers 103624084f9bSBrian Somers { RedirectPort, 103724084f9bSBrian Somers 0, 103824084f9bSBrian Somers String, 1039bd690510SRuslan Ermilov "tcp|udp local_addr:local_port_range[,...] [public_addr:]public_port_range" 10405d8ee958SBrian Somers " [remote_addr[:remote_port_range]]", 10415d8ee958SBrian Somers "redirect a port (or ports) for incoming traffic", 104224084f9bSBrian Somers "redirect_port", 104324084f9bSBrian Somers NULL }, 104424084f9bSBrian Somers 10454330006dSRuslan Ermilov { RedirectProto, 10464330006dSRuslan Ermilov 0, 10474330006dSRuslan Ermilov String, 10484330006dSRuslan Ermilov "proto local_addr [public_addr] [remote_addr]", 10494330006dSRuslan Ermilov "redirect packets of a given proto", 10504330006dSRuslan Ermilov "redirect_proto", 10514330006dSRuslan Ermilov NULL }, 10524330006dSRuslan Ermilov 105324084f9bSBrian Somers { RedirectAddress, 105424084f9bSBrian Somers 0, 105524084f9bSBrian Somers String, 1056bd690510SRuslan Ermilov "local_addr[,...] public_addr", 105724084f9bSBrian Somers "define mapping between local and public addresses", 105824084f9bSBrian Somers "redirect_address", 105924084f9bSBrian Somers NULL }, 106024084f9bSBrian Somers 106159a7c613SBrian Somers { PptpAlias, 106259a7c613SBrian Somers 0, 106359a7c613SBrian Somers String, 106459a7c613SBrian Somers "src", 106559a7c613SBrian Somers "define inside machine for PPTP traffic", 106659a7c613SBrian Somers "pptpalias", 106759a7c613SBrian Somers NULL }, 106859a7c613SBrian Somers 106924084f9bSBrian Somers { ConfigFile, 107024084f9bSBrian Somers 0, 107124084f9bSBrian Somers String, 107224084f9bSBrian Somers "file_name", 107324084f9bSBrian Somers "read options from configuration file", 107424084f9bSBrian Somers "config", 107559a7c613SBrian Somers "f" }, 107659a7c613SBrian Somers 107759a7c613SBrian Somers { LogDenied, 107859a7c613SBrian Somers 0, 107959a7c613SBrian Somers YesNo, 108059a7c613SBrian Somers "[yes|no]", 108159a7c613SBrian Somers "enable logging of denied incoming packets", 108259a7c613SBrian Somers "log_denied", 108359a7c613SBrian Somers NULL }, 108459a7c613SBrian Somers 108559a7c613SBrian Somers { LogFacility, 108659a7c613SBrian Somers 0, 108759a7c613SBrian Somers String, 108859a7c613SBrian Somers "facility", 108959a7c613SBrian Somers "name of syslog facility to use for logging", 109059a7c613SBrian Somers "log_facility", 109159a7c613SBrian Somers NULL } 109259a7c613SBrian Somers 109324084f9bSBrian Somers }; 109424084f9bSBrian Somers 1095b0f55af6SRuslan Ermilov static void ParseOption (const char* option, const char* parms) 109624084f9bSBrian Somers { 109724084f9bSBrian Somers int i; 109824084f9bSBrian Somers struct OptionInfo* info; 109924084f9bSBrian Somers int yesNoValue; 110024084f9bSBrian Somers int aliasValue; 110124084f9bSBrian Somers int numValue; 110267a886fbSBrian Somers u_short uNumValue; 1103902cb50aSBrian Somers const char* strValue; 110424084f9bSBrian Somers struct in_addr addrValue; 110524084f9bSBrian Somers int max; 110624084f9bSBrian Somers char* end; 110759a7c613SBrian Somers CODE* fac_record = NULL; 110824084f9bSBrian Somers /* 110924084f9bSBrian Somers * Find option from table. 111024084f9bSBrian Somers */ 111124084f9bSBrian Somers max = sizeof (optionTable) / sizeof (struct OptionInfo); 111224084f9bSBrian Somers for (i = 0, info = optionTable; i < max; i++, info++) { 111324084f9bSBrian Somers 111424084f9bSBrian Somers if (!strcmp (info->name, option)) 111524084f9bSBrian Somers break; 111624084f9bSBrian Somers 111724084f9bSBrian Somers if (info->shortName) 111824084f9bSBrian Somers if (!strcmp (info->shortName, option)) 111924084f9bSBrian Somers break; 112024084f9bSBrian Somers } 112124084f9bSBrian Somers 112224084f9bSBrian Somers if (i >= max) { 112324084f9bSBrian Somers 11240fc81af1SPhilippe Charnier warnx ("unknown option %s", option); 112524084f9bSBrian Somers Usage (); 112624084f9bSBrian Somers } 112724084f9bSBrian Somers 112867a886fbSBrian Somers uNumValue = 0; 112924084f9bSBrian Somers yesNoValue = 0; 113024084f9bSBrian Somers numValue = 0; 113124084f9bSBrian Somers strValue = NULL; 113224084f9bSBrian Somers /* 113324084f9bSBrian Somers * Check parameters. 113424084f9bSBrian Somers */ 113524084f9bSBrian Somers switch (info->parm) { 113624084f9bSBrian Somers case YesNo: 113724084f9bSBrian Somers if (!parms) 113824084f9bSBrian Somers parms = "yes"; 113924084f9bSBrian Somers 114024084f9bSBrian Somers if (!strcmp (parms, "yes")) 114124084f9bSBrian Somers yesNoValue = 1; 114224084f9bSBrian Somers else 114324084f9bSBrian Somers if (!strcmp (parms, "no")) 114424084f9bSBrian Somers yesNoValue = 0; 11450fc81af1SPhilippe Charnier else 11460fc81af1SPhilippe Charnier errx (1, "%s needs yes/no parameter", option); 114724084f9bSBrian Somers break; 114824084f9bSBrian Somers 114924084f9bSBrian Somers case Service: 11500fc81af1SPhilippe Charnier if (!parms) 115167a886fbSBrian Somers errx (1, "%s needs service name or " 115267a886fbSBrian Somers "port number parameter", 115367a886fbSBrian Somers option); 115424084f9bSBrian Somers 115567a886fbSBrian Somers uNumValue = StrToPort (parms, "divert"); 115624084f9bSBrian Somers break; 115724084f9bSBrian Somers 115824084f9bSBrian Somers case Numeric: 115924084f9bSBrian Somers if (parms) 116024084f9bSBrian Somers numValue = strtol (parms, &end, 10); 116124084f9bSBrian Somers else 1162902cb50aSBrian Somers end = NULL; 116324084f9bSBrian Somers 11640fc81af1SPhilippe Charnier if (end == parms) 11650fc81af1SPhilippe Charnier errx (1, "%s needs numeric parameter", option); 116624084f9bSBrian Somers break; 116724084f9bSBrian Somers 116824084f9bSBrian Somers case String: 116924084f9bSBrian Somers strValue = parms; 11700fc81af1SPhilippe Charnier if (!strValue) 11710fc81af1SPhilippe Charnier errx (1, "%s needs parameter", option); 117224084f9bSBrian Somers break; 117324084f9bSBrian Somers 117424084f9bSBrian Somers case None: 11750fc81af1SPhilippe Charnier if (parms) 11760fc81af1SPhilippe Charnier errx (1, "%s does not take parameters", option); 117724084f9bSBrian Somers break; 117824084f9bSBrian Somers 117924084f9bSBrian Somers case Address: 11800fc81af1SPhilippe Charnier if (!parms) 11810fc81af1SPhilippe Charnier errx (1, "%s needs address/host parameter", option); 118224084f9bSBrian Somers 118324084f9bSBrian Somers StrToAddr (parms, &addrValue); 118424084f9bSBrian Somers break; 118524084f9bSBrian Somers } 118624084f9bSBrian Somers 118724084f9bSBrian Somers switch (info->type) { 118824084f9bSBrian Somers case PacketAliasOption: 118924084f9bSBrian Somers 119024084f9bSBrian Somers aliasValue = yesNoValue ? info->packetAliasOpt : 0; 1191fb994b07SBrian Somers PacketAliasSetMode (aliasValue, info->packetAliasOpt); 119224084f9bSBrian Somers break; 119324084f9bSBrian Somers 119424084f9bSBrian Somers case Verbose: 119524084f9bSBrian Somers verbose = yesNoValue; 119624084f9bSBrian Somers break; 119724084f9bSBrian Somers 119824084f9bSBrian Somers case DynamicMode: 119924084f9bSBrian Somers dynamicMode = yesNoValue; 120024084f9bSBrian Somers break; 120124084f9bSBrian Somers 120224084f9bSBrian Somers case InPort: 120367a886fbSBrian Somers inPort = uNumValue; 120424084f9bSBrian Somers break; 120524084f9bSBrian Somers 120624084f9bSBrian Somers case OutPort: 120767a886fbSBrian Somers outPort = uNumValue; 120824084f9bSBrian Somers break; 120924084f9bSBrian Somers 121024084f9bSBrian Somers case Port: 121167a886fbSBrian Somers inOutPort = uNumValue; 121224084f9bSBrian Somers break; 121324084f9bSBrian Somers 121424084f9bSBrian Somers case AliasAddress: 121524084f9bSBrian Somers memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr)); 121624084f9bSBrian Somers break; 121724084f9bSBrian Somers 121811c2b3bfSRuslan Ermilov case TargetAddress: 121911c2b3bfSRuslan Ermilov PacketAliasSetTarget(addrValue); 122011c2b3bfSRuslan Ermilov break; 122111c2b3bfSRuslan Ermilov 122224084f9bSBrian Somers case RedirectPort: 122324084f9bSBrian Somers SetupPortRedirect (strValue); 122424084f9bSBrian Somers break; 122524084f9bSBrian Somers 12264330006dSRuslan Ermilov case RedirectProto: 12274330006dSRuslan Ermilov SetupProtoRedirect(strValue); 12284330006dSRuslan Ermilov break; 12294330006dSRuslan Ermilov 123024084f9bSBrian Somers case RedirectAddress: 123124084f9bSBrian Somers SetupAddressRedirect (strValue); 123224084f9bSBrian Somers break; 123324084f9bSBrian Somers 123459a7c613SBrian Somers case PptpAlias: 123559a7c613SBrian Somers SetupPptpAlias (strValue); 123659a7c613SBrian Somers break; 123759a7c613SBrian Somers 123859a7c613SBrian Somers case ProxyRule: 123959a7c613SBrian Somers PacketAliasProxyRule (strValue); 124059a7c613SBrian Somers break; 124159a7c613SBrian Somers 124224084f9bSBrian Somers case InterfaceName: 124324084f9bSBrian Somers if (ifName) 124424084f9bSBrian Somers free (ifName); 124524084f9bSBrian Somers 124624084f9bSBrian Somers ifName = strdup (strValue); 124724084f9bSBrian Somers break; 124824084f9bSBrian Somers 124924084f9bSBrian Somers case ConfigFile: 125024084f9bSBrian Somers ReadConfigFile (strValue); 125124084f9bSBrian Somers break; 125259a7c613SBrian Somers 125359a7c613SBrian Somers case LogDenied: 125459a7c613SBrian Somers logDropped = 1; 125559a7c613SBrian Somers break; 125659a7c613SBrian Somers 125759a7c613SBrian Somers case LogFacility: 125859a7c613SBrian Somers 125959a7c613SBrian Somers fac_record = facilitynames; 126059a7c613SBrian Somers while (fac_record->c_name != NULL) { 126159a7c613SBrian Somers 126259a7c613SBrian Somers if (!strcmp (fac_record->c_name, strValue)) { 126359a7c613SBrian Somers 126459a7c613SBrian Somers logFacility = fac_record->c_val; 126559a7c613SBrian Somers break; 126659a7c613SBrian Somers 126759a7c613SBrian Somers } 126859a7c613SBrian Somers else 126959a7c613SBrian Somers fac_record++; 127059a7c613SBrian Somers } 127159a7c613SBrian Somers 127259a7c613SBrian Somers if(fac_record->c_name == NULL) 127359a7c613SBrian Somers errx(1, "Unknown log facility name: %s", strValue); 127459a7c613SBrian Somers 127559a7c613SBrian Somers break; 127624084f9bSBrian Somers } 127724084f9bSBrian Somers } 127824084f9bSBrian Somers 1279902cb50aSBrian Somers void ReadConfigFile (const char* fileName) 128024084f9bSBrian Somers { 128124084f9bSBrian Somers FILE* file; 1282d99cc1daSRuslan Ermilov char *buf; 1283d99cc1daSRuslan Ermilov size_t len; 12842e7e7c71SRuslan Ermilov char *ptr, *p; 128524084f9bSBrian Somers char* option; 128624084f9bSBrian Somers 128724084f9bSBrian Somers file = fopen (fileName, "r"); 1288d99cc1daSRuslan Ermilov if (!file) 1289d99cc1daSRuslan Ermilov err(1, "cannot open config file %s", fileName); 129024084f9bSBrian Somers 1291d99cc1daSRuslan Ermilov while ((buf = fgetln(file, &len)) != NULL) { 1292d99cc1daSRuslan Ermilov if (buf[len - 1] == '\n') 1293d99cc1daSRuslan Ermilov buf[len - 1] = '\0'; 1294d99cc1daSRuslan Ermilov else 1295d99cc1daSRuslan Ermilov errx(1, "config file format error: " 1296d99cc1daSRuslan Ermilov "last line should end with newline"); 129724084f9bSBrian Somers 129824084f9bSBrian Somers /* 12992e7e7c71SRuslan Ermilov * Check for comments, strip off trailing spaces. 130024084f9bSBrian Somers */ 13012e7e7c71SRuslan Ermilov if ((ptr = strchr(buf, '#'))) 13022e7e7c71SRuslan Ermilov *ptr = '\0'; 13032e7e7c71SRuslan Ermilov for (ptr = buf; isspace(*ptr); ++ptr) 13042e7e7c71SRuslan Ermilov continue; 130524084f9bSBrian Somers if (*ptr == '\0') 130624084f9bSBrian Somers continue; 13072e7e7c71SRuslan Ermilov for (p = strchr(buf, '\0'); isspace(*--p);) 13082e7e7c71SRuslan Ermilov continue; 13092e7e7c71SRuslan Ermilov *++p = '\0'; 13102e7e7c71SRuslan Ermilov 131124084f9bSBrian Somers /* 131224084f9bSBrian Somers * Extract option name. 131324084f9bSBrian Somers */ 131424084f9bSBrian Somers option = ptr; 131524084f9bSBrian Somers while (*ptr && !isspace (*ptr)) 131624084f9bSBrian Somers ++ptr; 131724084f9bSBrian Somers 131824084f9bSBrian Somers if (*ptr != '\0') { 131924084f9bSBrian Somers 132024084f9bSBrian Somers *ptr = '\0'; 132124084f9bSBrian Somers ++ptr; 132224084f9bSBrian Somers } 132324084f9bSBrian Somers /* 132424084f9bSBrian Somers * Skip white space between name and parms. 132524084f9bSBrian Somers */ 132624084f9bSBrian Somers while (*ptr && isspace (*ptr)) 132724084f9bSBrian Somers ++ptr; 132824084f9bSBrian Somers 1329b0f55af6SRuslan Ermilov ParseOption (option, *ptr ? ptr : NULL); 133024084f9bSBrian Somers } 133124084f9bSBrian Somers 133224084f9bSBrian Somers fclose (file); 133324084f9bSBrian Somers } 133424084f9bSBrian Somers 133524084f9bSBrian Somers static void Usage () 133624084f9bSBrian Somers { 133724084f9bSBrian Somers int i; 133824084f9bSBrian Somers int max; 133924084f9bSBrian Somers struct OptionInfo* info; 134024084f9bSBrian Somers 134124084f9bSBrian Somers fprintf (stderr, "Recognized options:\n\n"); 134224084f9bSBrian Somers 134324084f9bSBrian Somers max = sizeof (optionTable) / sizeof (struct OptionInfo); 134424084f9bSBrian Somers for (i = 0, info = optionTable; i < max; i++, info++) { 134524084f9bSBrian Somers 134624084f9bSBrian Somers fprintf (stderr, "-%-20s %s\n", info->name, 134724084f9bSBrian Somers info->parmDescription); 134824084f9bSBrian Somers 134924084f9bSBrian Somers if (info->shortName) 135024084f9bSBrian Somers fprintf (stderr, "-%-20s %s\n", info->shortName, 135124084f9bSBrian Somers info->parmDescription); 135224084f9bSBrian Somers 135324084f9bSBrian Somers fprintf (stderr, " %s\n\n", info->description); 135424084f9bSBrian Somers } 135524084f9bSBrian Somers 135624084f9bSBrian Somers exit (1); 135724084f9bSBrian Somers } 135824084f9bSBrian Somers 1359902cb50aSBrian Somers void SetupPptpAlias (const char* parms) 136024084f9bSBrian Somers { 136124084f9bSBrian Somers char buf[128]; 136224084f9bSBrian Somers char* ptr; 136324084f9bSBrian Somers struct in_addr srcAddr; 136424084f9bSBrian Somers 136524084f9bSBrian Somers strcpy (buf, parms); 136624084f9bSBrian Somers 136724084f9bSBrian Somers /* 136824084f9bSBrian Somers * Extract source address. 136924084f9bSBrian Somers */ 137059a7c613SBrian Somers ptr = strtok (buf, " \t"); 13710fc81af1SPhilippe Charnier if (!ptr) 137259a7c613SBrian Somers errx(1, "pptpalias: missing src address"); 137324084f9bSBrian Somers 137459a7c613SBrian Somers StrToAddr (ptr, &srcAddr); 137559a7c613SBrian Somers PacketAliasPptp (srcAddr); 137624084f9bSBrian Somers } 137724084f9bSBrian Somers 1378902cb50aSBrian Somers void SetupPortRedirect (const char* parms) 137924084f9bSBrian Somers { 138024084f9bSBrian Somers char buf[128]; 138124084f9bSBrian Somers char* ptr; 1382bd690510SRuslan Ermilov char* serverPool; 138324084f9bSBrian Somers struct in_addr localAddr; 138424084f9bSBrian Somers struct in_addr publicAddr; 138524084f9bSBrian Somers struct in_addr remoteAddr; 13865d8ee958SBrian Somers port_range portRange; 13875d8ee958SBrian Somers u_short localPort = 0; 13885d8ee958SBrian Somers u_short publicPort = 0; 13895d8ee958SBrian Somers u_short remotePort = 0; 13905d8ee958SBrian Somers u_short numLocalPorts = 0; 13915d8ee958SBrian Somers u_short numPublicPorts = 0; 13925d8ee958SBrian Somers u_short numRemotePorts = 0; 139324084f9bSBrian Somers int proto; 139424084f9bSBrian Somers char* protoName; 139524084f9bSBrian Somers char* separator; 13965d8ee958SBrian Somers int i; 1397bd690510SRuslan Ermilov struct alias_link *link = NULL; 139824084f9bSBrian Somers 139924084f9bSBrian Somers strcpy (buf, parms); 140024084f9bSBrian Somers /* 140124084f9bSBrian Somers * Extract protocol. 140224084f9bSBrian Somers */ 140324084f9bSBrian Somers protoName = strtok (buf, " \t"); 14040fc81af1SPhilippe Charnier if (!protoName) 14050fc81af1SPhilippe Charnier errx (1, "redirect_port: missing protocol"); 140624084f9bSBrian Somers 140724084f9bSBrian Somers proto = StrToProto (protoName); 140824084f9bSBrian Somers /* 140924084f9bSBrian Somers * Extract local address. 141024084f9bSBrian Somers */ 141124084f9bSBrian Somers ptr = strtok (NULL, " \t"); 14120fc81af1SPhilippe Charnier if (!ptr) 14130fc81af1SPhilippe Charnier errx (1, "redirect_port: missing local address"); 141424084f9bSBrian Somers 1415bd690510SRuslan Ermilov separator = strchr(ptr, ','); 1416bd690510SRuslan Ermilov if (separator) { /* LSNAT redirection syntax. */ 1417bd690510SRuslan Ermilov localAddr.s_addr = INADDR_NONE; 1418bd690510SRuslan Ermilov localPort = ~0; 1419bd690510SRuslan Ermilov numLocalPorts = 1; 1420bd690510SRuslan Ermilov serverPool = ptr; 1421bd690510SRuslan Ermilov } else { 14225d8ee958SBrian Somers if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 ) 14235d8ee958SBrian Somers errx (1, "redirect_port: invalid local port range"); 14245d8ee958SBrian Somers 14255d8ee958SBrian Somers localPort = GETLOPORT(portRange); 14265d8ee958SBrian Somers numLocalPorts = GETNUMPORTS(portRange); 1427bd690510SRuslan Ermilov serverPool = NULL; 1428bd690510SRuslan Ermilov } 14295d8ee958SBrian Somers 143024084f9bSBrian Somers /* 14319c501140SBrian Somers * Extract public port and optionally address. 143224084f9bSBrian Somers */ 143324084f9bSBrian Somers ptr = strtok (NULL, " \t"); 14340fc81af1SPhilippe Charnier if (!ptr) 14350fc81af1SPhilippe Charnier errx (1, "redirect_port: missing public port"); 143624084f9bSBrian Somers 143724084f9bSBrian Somers separator = strchr (ptr, ':'); 14385d8ee958SBrian Somers if (separator) { 14395d8ee958SBrian Somers if (StrToAddrAndPortRange (ptr, &publicAddr, protoName, &portRange) != 0 ) 14405d8ee958SBrian Somers errx (1, "redirect_port: invalid public port range"); 144124084f9bSBrian Somers } 14425d8ee958SBrian Somers else { 14435d8ee958SBrian Somers publicAddr.s_addr = INADDR_ANY; 14445d8ee958SBrian Somers if (StrToPortRange (ptr, protoName, &portRange) != 0) 14455d8ee958SBrian Somers errx (1, "redirect_port: invalid public port range"); 14465d8ee958SBrian Somers } 14475d8ee958SBrian Somers 14485d8ee958SBrian Somers publicPort = GETLOPORT(portRange); 14495d8ee958SBrian Somers numPublicPorts = GETNUMPORTS(portRange); 145024084f9bSBrian Somers 145124084f9bSBrian Somers /* 145224084f9bSBrian Somers * Extract remote address and optionally port. 145324084f9bSBrian Somers */ 145424084f9bSBrian Somers ptr = strtok (NULL, " \t"); 145524084f9bSBrian Somers if (ptr) { 145624084f9bSBrian Somers separator = strchr (ptr, ':'); 1457ebe70c8fSWarner Losh if (separator) { 14585d8ee958SBrian Somers if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0) 14595d8ee958SBrian Somers errx (1, "redirect_port: invalid remote port range"); 1460ebe70c8fSWarner Losh } else { 14615d8ee958SBrian Somers SETLOPORT(portRange, 0); 14625d8ee958SBrian Somers SETNUMPORTS(portRange, 1); 146324084f9bSBrian Somers StrToAddr (ptr, &remoteAddr); 146424084f9bSBrian Somers } 146524084f9bSBrian Somers } 146624084f9bSBrian Somers else { 14675d8ee958SBrian Somers SETLOPORT(portRange, 0); 14685d8ee958SBrian Somers SETNUMPORTS(portRange, 1); 146924084f9bSBrian Somers remoteAddr.s_addr = INADDR_ANY; 147024084f9bSBrian Somers } 147124084f9bSBrian Somers 14725d8ee958SBrian Somers remotePort = GETLOPORT(portRange); 14735d8ee958SBrian Somers numRemotePorts = GETNUMPORTS(portRange); 14745d8ee958SBrian Somers 14755d8ee958SBrian Somers /* 14765d8ee958SBrian Somers * Make sure port ranges match up, then add the redirect ports. 14775d8ee958SBrian Somers */ 14785d8ee958SBrian Somers if (numLocalPorts != numPublicPorts) 14795d8ee958SBrian Somers errx (1, "redirect_port: port ranges must be equal in size"); 14805d8ee958SBrian Somers 14815d8ee958SBrian Somers /* Remote port range is allowed to be '0' which means all ports. */ 148229d97436SBrian Somers if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0)) 14835d8ee958SBrian Somers errx (1, "redirect_port: remote port must be 0 or equal to local port range in size"); 14845d8ee958SBrian Somers 14855d8ee958SBrian Somers for (i = 0 ; i < numPublicPorts ; ++i) { 14865d8ee958SBrian Somers /* If remotePort is all ports, set it to 0. */ 14875d8ee958SBrian Somers u_short remotePortCopy = remotePort + i; 14885d8ee958SBrian Somers if (numRemotePorts == 1 && remotePort == 0) 14895d8ee958SBrian Somers remotePortCopy = 0; 14905d8ee958SBrian Somers 1491bd690510SRuslan Ermilov link = PacketAliasRedirectPort (localAddr, 14925d8ee958SBrian Somers htons(localPort + i), 149324084f9bSBrian Somers remoteAddr, 14945d8ee958SBrian Somers htons(remotePortCopy), 149524084f9bSBrian Somers publicAddr, 14965d8ee958SBrian Somers htons(publicPort + i), 149724084f9bSBrian Somers proto); 149824084f9bSBrian Somers } 1499bd690510SRuslan Ermilov 1500bd690510SRuslan Ermilov /* 1501bd690510SRuslan Ermilov * Setup LSNAT server pool. 1502bd690510SRuslan Ermilov */ 1503bd690510SRuslan Ermilov if (serverPool != NULL && link != NULL) { 1504bd690510SRuslan Ermilov ptr = strtok(serverPool, ","); 1505bd690510SRuslan Ermilov while (ptr != NULL) { 1506bd690510SRuslan Ermilov if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0) 1507bd690510SRuslan Ermilov errx(1, "redirect_port: invalid local port range"); 1508bd690510SRuslan Ermilov 1509bd690510SRuslan Ermilov localPort = GETLOPORT(portRange); 1510bd690510SRuslan Ermilov if (GETNUMPORTS(portRange) != 1) 1511bd690510SRuslan Ermilov errx(1, "redirect_port: local port must be single in this context"); 1512bd690510SRuslan Ermilov PacketAliasAddServer(link, localAddr, htons(localPort)); 1513bd690510SRuslan Ermilov ptr = strtok(NULL, ","); 1514bd690510SRuslan Ermilov } 1515bd690510SRuslan Ermilov } 15165d8ee958SBrian Somers } 151724084f9bSBrian Somers 15184330006dSRuslan Ermilov void 15194330006dSRuslan Ermilov SetupProtoRedirect(const char* parms) 15204330006dSRuslan Ermilov { 15214330006dSRuslan Ermilov char buf[128]; 15224330006dSRuslan Ermilov char* ptr; 15234330006dSRuslan Ermilov struct in_addr localAddr; 15244330006dSRuslan Ermilov struct in_addr publicAddr; 15254330006dSRuslan Ermilov struct in_addr remoteAddr; 15264330006dSRuslan Ermilov int proto; 15274330006dSRuslan Ermilov char* protoName; 15284330006dSRuslan Ermilov struct protoent *protoent; 15294330006dSRuslan Ermilov 15304330006dSRuslan Ermilov strcpy (buf, parms); 15314330006dSRuslan Ermilov /* 15324330006dSRuslan Ermilov * Extract protocol. 15334330006dSRuslan Ermilov */ 15344330006dSRuslan Ermilov protoName = strtok(buf, " \t"); 15354330006dSRuslan Ermilov if (!protoName) 15364330006dSRuslan Ermilov errx(1, "redirect_proto: missing protocol"); 15374330006dSRuslan Ermilov 15384330006dSRuslan Ermilov protoent = getprotobyname(protoName); 15394330006dSRuslan Ermilov if (protoent == NULL) 15404330006dSRuslan Ermilov errx(1, "redirect_proto: unknown protocol %s", protoName); 15414330006dSRuslan Ermilov else 15424330006dSRuslan Ermilov proto = protoent->p_proto; 15434330006dSRuslan Ermilov /* 15444330006dSRuslan Ermilov * Extract local address. 15454330006dSRuslan Ermilov */ 15464330006dSRuslan Ermilov ptr = strtok(NULL, " \t"); 15474330006dSRuslan Ermilov if (!ptr) 15484330006dSRuslan Ermilov errx(1, "redirect_proto: missing local address"); 15494330006dSRuslan Ermilov else 15504330006dSRuslan Ermilov StrToAddr(ptr, &localAddr); 15514330006dSRuslan Ermilov /* 15524330006dSRuslan Ermilov * Extract optional public address. 15534330006dSRuslan Ermilov */ 15544330006dSRuslan Ermilov ptr = strtok(NULL, " \t"); 15554330006dSRuslan Ermilov if (ptr) 15564330006dSRuslan Ermilov StrToAddr(ptr, &publicAddr); 15574330006dSRuslan Ermilov else 15584330006dSRuslan Ermilov publicAddr.s_addr = INADDR_ANY; 15594330006dSRuslan Ermilov /* 15604330006dSRuslan Ermilov * Extract optional remote address. 15614330006dSRuslan Ermilov */ 15624330006dSRuslan Ermilov ptr = strtok(NULL, " \t"); 15634330006dSRuslan Ermilov if (ptr) 15644330006dSRuslan Ermilov StrToAddr(ptr, &remoteAddr); 15654330006dSRuslan Ermilov else 15664330006dSRuslan Ermilov remoteAddr.s_addr = INADDR_ANY; 15674330006dSRuslan Ermilov /* 15684330006dSRuslan Ermilov * Create aliasing link. 15694330006dSRuslan Ermilov */ 15704330006dSRuslan Ermilov (void)PacketAliasRedirectProto(localAddr, remoteAddr, publicAddr, 15714330006dSRuslan Ermilov proto); 15724330006dSRuslan Ermilov } 15734330006dSRuslan Ermilov 1574902cb50aSBrian Somers void SetupAddressRedirect (const char* parms) 157524084f9bSBrian Somers { 157624084f9bSBrian Somers char buf[128]; 157724084f9bSBrian Somers char* ptr; 1578bd690510SRuslan Ermilov char* separator; 157924084f9bSBrian Somers struct in_addr localAddr; 158024084f9bSBrian Somers struct in_addr publicAddr; 1581bd690510SRuslan Ermilov char* serverPool; 1582bd690510SRuslan Ermilov struct alias_link *link; 158324084f9bSBrian Somers 158424084f9bSBrian Somers strcpy (buf, parms); 158524084f9bSBrian Somers /* 158624084f9bSBrian Somers * Extract local address. 158724084f9bSBrian Somers */ 158824084f9bSBrian Somers ptr = strtok (buf, " \t"); 15890fc81af1SPhilippe Charnier if (!ptr) 15900fc81af1SPhilippe Charnier errx (1, "redirect_address: missing local address"); 159124084f9bSBrian Somers 1592bd690510SRuslan Ermilov separator = strchr(ptr, ','); 1593bd690510SRuslan Ermilov if (separator) { /* LSNAT redirection syntax. */ 1594bd690510SRuslan Ermilov localAddr.s_addr = INADDR_NONE; 1595bd690510SRuslan Ermilov serverPool = ptr; 1596bd690510SRuslan Ermilov } else { 159724084f9bSBrian Somers StrToAddr (ptr, &localAddr); 1598bd690510SRuslan Ermilov serverPool = NULL; 1599bd690510SRuslan Ermilov } 160024084f9bSBrian Somers /* 160124084f9bSBrian Somers * Extract public address. 160224084f9bSBrian Somers */ 160324084f9bSBrian Somers ptr = strtok (NULL, " \t"); 16040fc81af1SPhilippe Charnier if (!ptr) 16050fc81af1SPhilippe Charnier errx (1, "redirect_address: missing public address"); 160624084f9bSBrian Somers 160724084f9bSBrian Somers StrToAddr (ptr, &publicAddr); 1608bd690510SRuslan Ermilov link = PacketAliasRedirectAddr(localAddr, publicAddr); 1609bd690510SRuslan Ermilov 1610bd690510SRuslan Ermilov /* 1611bd690510SRuslan Ermilov * Setup LSNAT server pool. 1612bd690510SRuslan Ermilov */ 1613bd690510SRuslan Ermilov if (serverPool != NULL && link != NULL) { 1614bd690510SRuslan Ermilov ptr = strtok(serverPool, ","); 1615bd690510SRuslan Ermilov while (ptr != NULL) { 1616bd690510SRuslan Ermilov StrToAddr(ptr, &localAddr); 1617bd690510SRuslan Ermilov PacketAliasAddServer(link, localAddr, htons(~0)); 1618bd690510SRuslan Ermilov ptr = strtok(NULL, ","); 1619bd690510SRuslan Ermilov } 1620bd690510SRuslan Ermilov } 162124084f9bSBrian Somers } 162224084f9bSBrian Somers 1623902cb50aSBrian Somers void StrToAddr (const char* str, struct in_addr* addr) 162424084f9bSBrian Somers { 162524084f9bSBrian Somers struct hostent* hp; 162624084f9bSBrian Somers 162724084f9bSBrian Somers if (inet_aton (str, addr)) 162824084f9bSBrian Somers return; 162924084f9bSBrian Somers 163024084f9bSBrian Somers hp = gethostbyname (str); 16310fc81af1SPhilippe Charnier if (!hp) 16320fc81af1SPhilippe Charnier errx (1, "unknown host %s", str); 163324084f9bSBrian Somers 163424084f9bSBrian Somers memcpy (addr, hp->h_addr, sizeof (struct in_addr)); 163524084f9bSBrian Somers } 163624084f9bSBrian Somers 1637902cb50aSBrian Somers u_short StrToPort (const char* str, const char* proto) 163824084f9bSBrian Somers { 163967a886fbSBrian Somers u_short port; 164024084f9bSBrian Somers struct servent* sp; 164124084f9bSBrian Somers char* end; 164224084f9bSBrian Somers 164324084f9bSBrian Somers port = strtol (str, &end, 10); 164424084f9bSBrian Somers if (end != str) 164527c20503SBrian Somers return htons (port); 164624084f9bSBrian Somers 164724084f9bSBrian Somers sp = getservbyname (str, proto); 16480fc81af1SPhilippe Charnier if (!sp) 16490fc81af1SPhilippe Charnier errx (1, "unknown service %s/%s", str, proto); 165024084f9bSBrian Somers 165124084f9bSBrian Somers return sp->s_port; 165224084f9bSBrian Somers } 165324084f9bSBrian Somers 1654902cb50aSBrian Somers int StrToPortRange (const char* str, const char* proto, port_range *portRange) 16555d8ee958SBrian Somers { 16565d8ee958SBrian Somers char* sep; 16575d8ee958SBrian Somers struct servent* sp; 16585d8ee958SBrian Somers char* end; 16595d8ee958SBrian Somers u_short loPort; 16605d8ee958SBrian Somers u_short hiPort; 16615d8ee958SBrian Somers 16625d8ee958SBrian Somers /* First see if this is a service, return corresponding port if so. */ 16635d8ee958SBrian Somers sp = getservbyname (str,proto); 16645d8ee958SBrian Somers if (sp) { 16655d8ee958SBrian Somers SETLOPORT(*portRange, ntohs(sp->s_port)); 16665d8ee958SBrian Somers SETNUMPORTS(*portRange, 1); 16675d8ee958SBrian Somers return 0; 16685d8ee958SBrian Somers } 16695d8ee958SBrian Somers 16705d8ee958SBrian Somers /* Not a service, see if it's a single port or port range. */ 16715d8ee958SBrian Somers sep = strchr (str, '-'); 16725d8ee958SBrian Somers if (sep == NULL) { 16735d8ee958SBrian Somers SETLOPORT(*portRange, strtol(str, &end, 10)); 16745d8ee958SBrian Somers if (end != str) { 16755d8ee958SBrian Somers /* Single port. */ 16765d8ee958SBrian Somers SETNUMPORTS(*portRange, 1); 16775d8ee958SBrian Somers return 0; 16785d8ee958SBrian Somers } 16795d8ee958SBrian Somers 16805d8ee958SBrian Somers /* Error in port range field. */ 16815d8ee958SBrian Somers errx (1, "unknown service %s/%s", str, proto); 16825d8ee958SBrian Somers } 16835d8ee958SBrian Somers 16845d8ee958SBrian Somers /* Port range, get the values and sanity check. */ 16855d8ee958SBrian Somers sscanf (str, "%hu-%hu", &loPort, &hiPort); 16865d8ee958SBrian Somers SETLOPORT(*portRange, loPort); 16875d8ee958SBrian Somers SETNUMPORTS(*portRange, 0); /* Error by default */ 16885d8ee958SBrian Somers if (loPort <= hiPort) 16895d8ee958SBrian Somers SETNUMPORTS(*portRange, hiPort - loPort + 1); 16905d8ee958SBrian Somers 16915d8ee958SBrian Somers if (GETNUMPORTS(*portRange) == 0) 16925d8ee958SBrian Somers errx (1, "invalid port range %s", str); 16935d8ee958SBrian Somers 16945d8ee958SBrian Somers return 0; 16955d8ee958SBrian Somers } 16965d8ee958SBrian Somers 16975d8ee958SBrian Somers 1698902cb50aSBrian Somers int StrToProto (const char* str) 169924084f9bSBrian Somers { 170024084f9bSBrian Somers if (!strcmp (str, "tcp")) 170124084f9bSBrian Somers return IPPROTO_TCP; 170224084f9bSBrian Somers 170324084f9bSBrian Somers if (!strcmp (str, "udp")) 170424084f9bSBrian Somers return IPPROTO_UDP; 170524084f9bSBrian Somers 17060fc81af1SPhilippe Charnier errx (1, "unknown protocol %s. Expected tcp or udp", str); 170724084f9bSBrian Somers } 170824084f9bSBrian Somers 1709902cb50aSBrian Somers int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange) 171024084f9bSBrian Somers { 171124084f9bSBrian Somers char* ptr; 171224084f9bSBrian Somers 171324084f9bSBrian Somers ptr = strchr (str, ':'); 17140fc81af1SPhilippe Charnier if (!ptr) 17150fc81af1SPhilippe Charnier errx (1, "%s is missing port number", str); 171624084f9bSBrian Somers 171724084f9bSBrian Somers *ptr = '\0'; 171824084f9bSBrian Somers ++ptr; 171924084f9bSBrian Somers 172024084f9bSBrian Somers StrToAddr (str, addr); 17215d8ee958SBrian Somers return StrToPortRange (ptr, proto, portRange); 172224084f9bSBrian Somers } 1723