124084f9bSBrian Somers /* 224084f9bSBrian Somers * natd - Network Address Translation Daemon for FreeBSD. 324084f9bSBrian Somers * 424084f9bSBrian Somers * This software ois 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 * 1224084f9bSBrian Somers */ 1324084f9bSBrian Somers 1424084f9bSBrian Somers #include <sys/types.h> 1524084f9bSBrian Somers #include <sys/socket.h> 1624084f9bSBrian Somers #include <sys/time.h> 1724084f9bSBrian Somers 1824084f9bSBrian Somers #include <netinet/in.h> 1924084f9bSBrian Somers #include <netinet/in_systm.h> 2024084f9bSBrian Somers #include <netinet/ip.h> 2124084f9bSBrian Somers #include <machine/in_cksum.h> 2224084f9bSBrian Somers #include <netinet/tcp.h> 2324084f9bSBrian Somers #include <sys/ioctl.h> 2424084f9bSBrian Somers #include <net/if.h> 2524084f9bSBrian Somers #include <net/route.h> 2624084f9bSBrian Somers #include <arpa/inet.h> 2724084f9bSBrian Somers 2824084f9bSBrian Somers #include <alias.h> 290fc81af1SPhilippe Charnier #include <ctype.h> 300fc81af1SPhilippe Charnier #include <err.h> 310fc81af1SPhilippe Charnier #include <errno.h> 320fc81af1SPhilippe Charnier #include <netdb.h> 330fc81af1SPhilippe Charnier #include <signal.h> 340fc81af1SPhilippe Charnier #include <stdio.h> 350fc81af1SPhilippe Charnier #include <stdlib.h> 360fc81af1SPhilippe Charnier #include <string.h> 370fc81af1SPhilippe Charnier #include <syslog.h> 380fc81af1SPhilippe Charnier #include <unistd.h> 3924084f9bSBrian Somers #include "natd.h" 4024084f9bSBrian Somers 4124084f9bSBrian Somers /* 4224084f9bSBrian Somers * Default values for input and output 4324084f9bSBrian Somers * divert socket ports. 4424084f9bSBrian Somers */ 4524084f9bSBrian Somers 4624084f9bSBrian Somers #define DEFAULT_SERVICE "natd" 4724084f9bSBrian Somers 4824084f9bSBrian Somers /* 4924084f9bSBrian Somers * Function prototypes. 5024084f9bSBrian Somers */ 5124084f9bSBrian Somers 5224084f9bSBrian Somers static void DoAliasing (int fd); 5324084f9bSBrian Somers static void DaemonMode (); 5424084f9bSBrian Somers static void HandleRoutingInfo (int fd); 5524084f9bSBrian Somers static void Usage (); 5624084f9bSBrian Somers static void PrintPacket (struct ip*); 5724084f9bSBrian Somers static void SetAliasAddressFromIfName (char* ifName); 5824084f9bSBrian Somers static void InitiateShutdown (); 5924084f9bSBrian Somers static void Shutdown (); 6024084f9bSBrian Somers static void RefreshAddr (); 6124084f9bSBrian Somers static void ParseOption (char* option, char* parms, int cmdLine); 6224084f9bSBrian Somers static void ReadConfigFile (char* fileName); 6324084f9bSBrian Somers static void SetupPermanentLink (char* parms); 6424084f9bSBrian Somers static void SetupPortRedirect (char* parms); 6524084f9bSBrian Somers static void SetupAddressRedirect (char* parms); 6624084f9bSBrian Somers static void StrToAddr (char* str, struct in_addr* addr); 6724084f9bSBrian Somers static int StrToPort (char* str, char* proto); 6824084f9bSBrian Somers static int StrToProto (char* str); 6924084f9bSBrian Somers static int StrToAddrAndPort (char* str, struct in_addr* addr, char* proto); 7024084f9bSBrian Somers static void ParseArgs (int argc, char** argv); 71fb994b07SBrian Somers static void FlushPacketBuffer (int fd); 7224084f9bSBrian Somers 7324084f9bSBrian Somers /* 7424084f9bSBrian Somers * Globals. 7524084f9bSBrian Somers */ 7624084f9bSBrian Somers 7724084f9bSBrian Somers static int verbose; 7824084f9bSBrian Somers static int background; 7924084f9bSBrian Somers static int running; 8024084f9bSBrian Somers static int assignAliasAddr; 8124084f9bSBrian Somers static char* ifName; 8224084f9bSBrian Somers static int ifIndex; 8324084f9bSBrian Somers static int inPort; 8424084f9bSBrian Somers static int outPort; 8524084f9bSBrian Somers static int inOutPort; 8624084f9bSBrian Somers static struct in_addr aliasAddr; 8724084f9bSBrian Somers static int dynamicMode; 8824084f9bSBrian Somers static int ifMTU; 8924084f9bSBrian Somers static int aliasOverhead; 9024084f9bSBrian Somers static int icmpSock; 91fb994b07SBrian Somers static char packetBuf[IP_MAXPACKET]; 92fb994b07SBrian Somers static int packetLen; 93fb994b07SBrian Somers static struct sockaddr_in packetAddr; 94fb994b07SBrian Somers static int packetSock; 95f9b06d5cSBrian Somers static int dropIgnoredIncoming; 9624084f9bSBrian Somers 9724084f9bSBrian Somers int main (int argc, char** argv) 9824084f9bSBrian Somers { 9924084f9bSBrian Somers int divertIn; 10024084f9bSBrian Somers int divertOut; 10124084f9bSBrian Somers int divertInOut; 10224084f9bSBrian Somers int routeSock; 10324084f9bSBrian Somers struct sockaddr_in addr; 10424084f9bSBrian Somers fd_set readMask; 105fb994b07SBrian Somers fd_set writeMask; 10624084f9bSBrian Somers int fdMax; 10724084f9bSBrian Somers /* 10824084f9bSBrian Somers * Initialize packet aliasing software. 10924084f9bSBrian Somers * Done already here to be able to alter option bits 11024084f9bSBrian Somers * during command line and configuration file processing. 11124084f9bSBrian Somers */ 112fb994b07SBrian Somers PacketAliasInit (); 11324084f9bSBrian Somers /* 11424084f9bSBrian Somers * Parse options. 11524084f9bSBrian Somers */ 11624084f9bSBrian Somers inPort = 0; 11724084f9bSBrian Somers outPort = 0; 11824084f9bSBrian Somers verbose = 0; 11924084f9bSBrian Somers inOutPort = 0; 12024084f9bSBrian Somers ifName = NULL; 12124084f9bSBrian Somers ifMTU = -1; 12224084f9bSBrian Somers background = 0; 12324084f9bSBrian Somers running = 1; 12424084f9bSBrian Somers assignAliasAddr = 0; 12524084f9bSBrian Somers aliasAddr.s_addr = INADDR_NONE; 12624084f9bSBrian Somers aliasOverhead = 12; 12724084f9bSBrian Somers dynamicMode = 0; 128fb994b07SBrian Somers /* 129fb994b07SBrian Somers * Mark packet buffer empty. 130fb994b07SBrian Somers */ 131fb994b07SBrian Somers packetSock = -1; 13224084f9bSBrian Somers 13324084f9bSBrian Somers ParseArgs (argc, argv); 13424084f9bSBrian Somers /* 13524084f9bSBrian Somers * Check that valid aliasing address has been given. 13624084f9bSBrian Somers */ 1370fc81af1SPhilippe Charnier if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL) 1380fc81af1SPhilippe Charnier errx(1, "aliasing address not given"); 13924084f9bSBrian Somers 1400fc81af1SPhilippe Charnier if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL) 1410fc81af1SPhilippe Charnier errx(1, 1420fc81af1SPhilippe Charnier "both alias address and interface name are not allowed"); 14324084f9bSBrian Somers /* 14424084f9bSBrian Somers * Check that valid port number is known. 14524084f9bSBrian Somers */ 14624084f9bSBrian Somers if (inPort != 0 || outPort != 0) 1470fc81af1SPhilippe Charnier if (inPort == 0 || outPort == 0) 1480fc81af1SPhilippe Charnier errx(1, "both input and output ports are required"); 14924084f9bSBrian Somers 15024084f9bSBrian Somers if (inPort == 0 && outPort == 0 && inOutPort == 0) 15124084f9bSBrian Somers ParseOption ("port", DEFAULT_SERVICE, 0); 15224084f9bSBrian Somers 15324084f9bSBrian Somers /* 154f9b06d5cSBrian Somers * Check if ignored packets should be dropped. 155f9b06d5cSBrian Somers */ 156f9b06d5cSBrian Somers dropIgnoredIncoming = PacketAliasSetMode (0, 0); 157f9b06d5cSBrian Somers dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING; 158f9b06d5cSBrian Somers /* 15924084f9bSBrian Somers * Create divert sockets. Use only one socket if -p was specified 16024084f9bSBrian Somers * on command line. Otherwise, create separate sockets for 16124084f9bSBrian Somers * outgoing and incoming connnections. 16224084f9bSBrian Somers */ 16324084f9bSBrian Somers if (inOutPort) { 16424084f9bSBrian Somers 16524084f9bSBrian Somers divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 16624084f9bSBrian Somers if (divertInOut == -1) 16724084f9bSBrian Somers Quit ("Unable to create divert socket."); 16824084f9bSBrian Somers 16924084f9bSBrian Somers divertIn = -1; 17024084f9bSBrian Somers divertOut = -1; 17124084f9bSBrian Somers /* 17224084f9bSBrian Somers * Bind socket. 17324084f9bSBrian Somers */ 17424084f9bSBrian Somers 17524084f9bSBrian Somers addr.sin_family = AF_INET; 17624084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 17724084f9bSBrian Somers addr.sin_port = inOutPort; 17824084f9bSBrian Somers 17924084f9bSBrian Somers if (bind (divertInOut, 18024084f9bSBrian Somers (struct sockaddr*) &addr, 18124084f9bSBrian Somers sizeof addr) == -1) 18224084f9bSBrian Somers Quit ("Unable to bind divert socket."); 18324084f9bSBrian Somers } 18424084f9bSBrian Somers else { 18524084f9bSBrian Somers 18624084f9bSBrian Somers divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 18724084f9bSBrian Somers if (divertIn == -1) 18824084f9bSBrian Somers Quit ("Unable to create incoming divert socket."); 18924084f9bSBrian Somers 19024084f9bSBrian Somers divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 19124084f9bSBrian Somers if (divertOut == -1) 19224084f9bSBrian Somers Quit ("Unable to create outgoing divert socket."); 19324084f9bSBrian Somers 19424084f9bSBrian Somers divertInOut = -1; 19524084f9bSBrian Somers 19624084f9bSBrian Somers /* 19724084f9bSBrian Somers * Bind divert sockets. 19824084f9bSBrian Somers */ 19924084f9bSBrian Somers 20024084f9bSBrian Somers addr.sin_family = AF_INET; 20124084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 20224084f9bSBrian Somers addr.sin_port = inPort; 20324084f9bSBrian Somers 20424084f9bSBrian Somers if (bind (divertIn, 20524084f9bSBrian Somers (struct sockaddr*) &addr, 20624084f9bSBrian Somers sizeof addr) == -1) 20724084f9bSBrian Somers Quit ("Unable to bind incoming divert socket."); 20824084f9bSBrian Somers 20924084f9bSBrian Somers addr.sin_family = AF_INET; 21024084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 21124084f9bSBrian Somers addr.sin_port = outPort; 21224084f9bSBrian Somers 21324084f9bSBrian Somers if (bind (divertOut, 21424084f9bSBrian Somers (struct sockaddr*) &addr, 21524084f9bSBrian Somers sizeof addr) == -1) 21624084f9bSBrian Somers Quit ("Unable to bind outgoing divert socket."); 21724084f9bSBrian Somers } 21824084f9bSBrian Somers /* 21924084f9bSBrian Somers * Create routing socket if interface name specified. 22024084f9bSBrian Somers */ 22124084f9bSBrian Somers if (ifName && dynamicMode) { 22224084f9bSBrian Somers 22324084f9bSBrian Somers routeSock = socket (PF_ROUTE, SOCK_RAW, 0); 22424084f9bSBrian Somers if (routeSock == -1) 22524084f9bSBrian Somers Quit ("Unable to create routing info socket."); 22624084f9bSBrian Somers } 22724084f9bSBrian Somers else 22824084f9bSBrian Somers routeSock = -1; 22924084f9bSBrian Somers /* 23024084f9bSBrian Somers * Create socket for sending ICMP messages. 23124084f9bSBrian Somers */ 23224084f9bSBrian Somers icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); 23324084f9bSBrian Somers if (icmpSock == -1) 23424084f9bSBrian Somers Quit ("Unable to create ICMP socket."); 23524084f9bSBrian Somers /* 23624084f9bSBrian Somers * Become a daemon unless verbose mode was requested. 23724084f9bSBrian Somers */ 23824084f9bSBrian Somers if (!verbose) 23924084f9bSBrian Somers DaemonMode (); 24024084f9bSBrian Somers /* 24124084f9bSBrian Somers * Catch signals to manage shutdown and 24224084f9bSBrian Somers * refresh of interface address. 24324084f9bSBrian Somers */ 24424084f9bSBrian Somers signal (SIGTERM, InitiateShutdown); 24524084f9bSBrian Somers signal (SIGHUP, RefreshAddr); 24624084f9bSBrian Somers /* 24724084f9bSBrian Somers * Set alias address if it has been given. 24824084f9bSBrian Somers */ 24924084f9bSBrian Somers if (aliasAddr.s_addr != INADDR_NONE) 250fb994b07SBrian Somers PacketAliasSetAddress (aliasAddr); 25124084f9bSBrian Somers /* 25224084f9bSBrian Somers * We need largest descriptor number for select. 25324084f9bSBrian Somers */ 25424084f9bSBrian Somers 25524084f9bSBrian Somers fdMax = -1; 25624084f9bSBrian Somers 25724084f9bSBrian Somers if (divertIn > fdMax) 25824084f9bSBrian Somers fdMax = divertIn; 25924084f9bSBrian Somers 26024084f9bSBrian Somers if (divertOut > fdMax) 26124084f9bSBrian Somers fdMax = divertOut; 26224084f9bSBrian Somers 26324084f9bSBrian Somers if (divertInOut > fdMax) 26424084f9bSBrian Somers fdMax = divertInOut; 26524084f9bSBrian Somers 26624084f9bSBrian Somers if (routeSock > fdMax) 26724084f9bSBrian Somers fdMax = routeSock; 26824084f9bSBrian Somers 26924084f9bSBrian Somers while (running) { 270fb994b07SBrian Somers 271fb994b07SBrian Somers if (divertInOut != -1 && !ifName && packetSock == -1) { 272fb994b07SBrian Somers /* 273fb994b07SBrian Somers * When using only one socket, just call 274fb994b07SBrian Somers * DoAliasing repeatedly to process packets. 275fb994b07SBrian Somers */ 276fb994b07SBrian Somers DoAliasing (divertInOut); 277fb994b07SBrian Somers continue; 278fb994b07SBrian Somers } 27924084f9bSBrian Somers /* 28024084f9bSBrian Somers * Build read mask from socket descriptors to select. 28124084f9bSBrian Somers */ 28224084f9bSBrian Somers FD_ZERO (&readMask); 283fb994b07SBrian Somers FD_ZERO (&writeMask); 28424084f9bSBrian Somers 285fb994b07SBrian Somers /* 286fb994b07SBrian Somers * If there is unsent packet in buffer, use select 287fb994b07SBrian Somers * to check when socket comes writable again. 288fb994b07SBrian Somers */ 289fb994b07SBrian Somers if (packetSock != -1) { 290fb994b07SBrian Somers 291fb994b07SBrian Somers FD_SET (packetSock, &writeMask); 292fb994b07SBrian Somers } 293fb994b07SBrian Somers else { 294fb994b07SBrian Somers /* 295fb994b07SBrian Somers * No unsent packet exists - safe to check if 296fb994b07SBrian Somers * new ones are available. 297fb994b07SBrian Somers */ 29824084f9bSBrian Somers if (divertIn != -1) 29924084f9bSBrian Somers FD_SET (divertIn, &readMask); 30024084f9bSBrian Somers 30124084f9bSBrian Somers if (divertOut != -1) 30224084f9bSBrian Somers FD_SET (divertOut, &readMask); 30324084f9bSBrian Somers 30424084f9bSBrian Somers if (divertInOut != -1) 30524084f9bSBrian Somers FD_SET (divertInOut, &readMask); 306fb994b07SBrian Somers } 307fb994b07SBrian Somers /* 308fb994b07SBrian Somers * Routing info is processed always. 309fb994b07SBrian Somers */ 31024084f9bSBrian Somers if (routeSock != -1) 31124084f9bSBrian Somers FD_SET (routeSock, &readMask); 31224084f9bSBrian Somers 31324084f9bSBrian Somers if (select (fdMax + 1, 31424084f9bSBrian Somers &readMask, 315fb994b07SBrian Somers &writeMask, 31624084f9bSBrian Somers NULL, 31724084f9bSBrian Somers NULL) == -1) { 31824084f9bSBrian Somers 31924084f9bSBrian Somers if (errno == EINTR) 32024084f9bSBrian Somers continue; 32124084f9bSBrian Somers 32224084f9bSBrian Somers Quit ("Select failed."); 32324084f9bSBrian Somers } 32424084f9bSBrian Somers 325fb994b07SBrian Somers if (packetSock != -1) 326fb994b07SBrian Somers if (FD_ISSET (packetSock, &writeMask)) 327fb994b07SBrian Somers FlushPacketBuffer (packetSock); 328fb994b07SBrian Somers 32924084f9bSBrian Somers if (divertIn != -1) 33024084f9bSBrian Somers if (FD_ISSET (divertIn, &readMask)) 33124084f9bSBrian Somers DoAliasing (divertIn); 33224084f9bSBrian Somers 33324084f9bSBrian Somers if (divertOut != -1) 33424084f9bSBrian Somers if (FD_ISSET (divertOut, &readMask)) 33524084f9bSBrian Somers DoAliasing (divertOut); 33624084f9bSBrian Somers 33724084f9bSBrian Somers if (divertInOut != -1) 33824084f9bSBrian Somers if (FD_ISSET (divertInOut, &readMask)) 33924084f9bSBrian Somers DoAliasing (divertInOut); 34024084f9bSBrian Somers 34124084f9bSBrian Somers if (routeSock != -1) 34224084f9bSBrian Somers if (FD_ISSET (routeSock, &readMask)) 34324084f9bSBrian Somers HandleRoutingInfo (routeSock); 34424084f9bSBrian Somers } 34524084f9bSBrian Somers 34624084f9bSBrian Somers if (background) 34724084f9bSBrian Somers unlink (PIDFILE); 34824084f9bSBrian Somers 34924084f9bSBrian Somers return 0; 35024084f9bSBrian Somers } 35124084f9bSBrian Somers 35224084f9bSBrian Somers static void DaemonMode () 35324084f9bSBrian Somers { 35424084f9bSBrian Somers FILE* pidFile; 35524084f9bSBrian Somers 35624084f9bSBrian Somers daemon (0, 0); 35724084f9bSBrian Somers background = 1; 35824084f9bSBrian Somers 35924084f9bSBrian Somers pidFile = fopen (PIDFILE, "w"); 36024084f9bSBrian Somers if (pidFile) { 36124084f9bSBrian Somers 36224084f9bSBrian Somers fprintf (pidFile, "%d\n", getpid ()); 36324084f9bSBrian Somers fclose (pidFile); 36424084f9bSBrian Somers } 36524084f9bSBrian Somers } 36624084f9bSBrian Somers 36724084f9bSBrian Somers static void ParseArgs (int argc, char** argv) 36824084f9bSBrian Somers { 36924084f9bSBrian Somers int arg; 37024084f9bSBrian Somers char* parm; 37124084f9bSBrian Somers char* opt; 37224084f9bSBrian Somers char parmBuf[256]; 37324084f9bSBrian Somers 37424084f9bSBrian Somers for (arg = 1; arg < argc; arg++) { 37524084f9bSBrian Somers 37624084f9bSBrian Somers opt = argv[arg]; 37724084f9bSBrian Somers if (*opt != '-') { 37824084f9bSBrian Somers 3790fc81af1SPhilippe Charnier warnx ("invalid option %s", opt); 38024084f9bSBrian Somers Usage (); 38124084f9bSBrian Somers } 38224084f9bSBrian Somers 38324084f9bSBrian Somers parm = NULL; 38424084f9bSBrian Somers parmBuf[0] = '\0'; 38524084f9bSBrian Somers 38624084f9bSBrian Somers while (arg < argc - 1) { 38724084f9bSBrian Somers 38824084f9bSBrian Somers if (argv[arg + 1][0] == '-') 38924084f9bSBrian Somers break; 39024084f9bSBrian Somers 39124084f9bSBrian Somers if (parm) 39224084f9bSBrian Somers strcat (parmBuf, " "); 39324084f9bSBrian Somers 39424084f9bSBrian Somers ++arg; 39524084f9bSBrian Somers parm = parmBuf; 39624084f9bSBrian Somers strcat (parmBuf, argv[arg]); 39724084f9bSBrian Somers } 39824084f9bSBrian Somers 39924084f9bSBrian Somers ParseOption (opt + 1, parm, 1); 40024084f9bSBrian Somers } 40124084f9bSBrian Somers } 40224084f9bSBrian Somers 40324084f9bSBrian Somers static void DoAliasing (int fd) 40424084f9bSBrian Somers { 40524084f9bSBrian Somers int bytes; 40624084f9bSBrian Somers int origBytes; 407f9b06d5cSBrian Somers int status; 40824084f9bSBrian Somers int addrSize; 40924084f9bSBrian Somers struct ip* ip; 41024084f9bSBrian Somers 41124084f9bSBrian Somers if (assignAliasAddr) { 41224084f9bSBrian Somers 41324084f9bSBrian Somers SetAliasAddressFromIfName (ifName); 41424084f9bSBrian Somers assignAliasAddr = 0; 41524084f9bSBrian Somers } 41624084f9bSBrian Somers /* 41724084f9bSBrian Somers * Get packet from socket. 41824084f9bSBrian Somers */ 419fb994b07SBrian Somers addrSize = sizeof packetAddr; 42024084f9bSBrian Somers origBytes = recvfrom (fd, 421fb994b07SBrian Somers packetBuf, 422fb994b07SBrian Somers sizeof packetBuf, 42324084f9bSBrian Somers 0, 424fb994b07SBrian Somers (struct sockaddr*) &packetAddr, 42524084f9bSBrian Somers &addrSize); 42624084f9bSBrian Somers 42724084f9bSBrian Somers if (origBytes == -1) { 42824084f9bSBrian Somers 42924084f9bSBrian Somers if (errno != EINTR) 4300fc81af1SPhilippe Charnier Warn ("read from divert socket failed"); 43124084f9bSBrian Somers 43224084f9bSBrian Somers return; 43324084f9bSBrian Somers } 43424084f9bSBrian Somers /* 43524084f9bSBrian Somers * This is a IP packet. 43624084f9bSBrian Somers */ 437fb994b07SBrian Somers ip = (struct ip*) packetBuf; 43824084f9bSBrian Somers 43924084f9bSBrian Somers if (verbose) { 44024084f9bSBrian Somers 44124084f9bSBrian Somers /* 44224084f9bSBrian Somers * Print packet direction and protocol type. 44324084f9bSBrian Somers */ 44424084f9bSBrian Somers 445fb994b07SBrian Somers if (packetAddr.sin_addr.s_addr == INADDR_ANY) 44624084f9bSBrian Somers printf ("Out "); 44724084f9bSBrian Somers else 44824084f9bSBrian Somers printf ("In "); 44924084f9bSBrian Somers 45024084f9bSBrian Somers switch (ip->ip_p) { 45124084f9bSBrian Somers case IPPROTO_TCP: 45224084f9bSBrian Somers printf ("[TCP] "); 45324084f9bSBrian Somers break; 45424084f9bSBrian Somers 45524084f9bSBrian Somers case IPPROTO_UDP: 45624084f9bSBrian Somers printf ("[UDP] "); 45724084f9bSBrian Somers break; 45824084f9bSBrian Somers 45924084f9bSBrian Somers case IPPROTO_ICMP: 46024084f9bSBrian Somers printf ("[ICMP] "); 46124084f9bSBrian Somers break; 46224084f9bSBrian Somers 46324084f9bSBrian Somers default: 46424084f9bSBrian Somers printf ("[?] "); 46524084f9bSBrian Somers break; 46624084f9bSBrian Somers } 46724084f9bSBrian Somers /* 46824084f9bSBrian Somers * Print addresses. 46924084f9bSBrian Somers */ 47024084f9bSBrian Somers PrintPacket (ip); 47124084f9bSBrian Somers } 47224084f9bSBrian Somers 473fb994b07SBrian Somers if (packetAddr.sin_addr.s_addr == INADDR_ANY) { 47424084f9bSBrian Somers /* 47524084f9bSBrian Somers * Outgoing packets. Do aliasing. 47624084f9bSBrian Somers */ 477fb994b07SBrian Somers PacketAliasOut (packetBuf, IP_MAXPACKET); 47824084f9bSBrian Somers } 47924084f9bSBrian Somers else { 48024084f9bSBrian Somers /* 48124084f9bSBrian Somers * Do aliasing. 48224084f9bSBrian Somers */ 483f9b06d5cSBrian Somers status = PacketAliasIn (packetBuf, IP_MAXPACKET); 484f9b06d5cSBrian Somers if (status == PKT_ALIAS_IGNORED && 485f9b06d5cSBrian Somers dropIgnoredIncoming) { 486f9b06d5cSBrian Somers 487f9b06d5cSBrian Somers printf (" dropped.\n"); 488f9b06d5cSBrian Somers return; 489f9b06d5cSBrian Somers } 49024084f9bSBrian Somers } 49124084f9bSBrian Somers /* 49224084f9bSBrian Somers * Length might have changed during aliasing. 49324084f9bSBrian Somers */ 49424084f9bSBrian Somers bytes = ntohs (ip->ip_len); 49524084f9bSBrian Somers /* 49624084f9bSBrian Somers * Update alias overhead size for outgoing packets. 49724084f9bSBrian Somers */ 498fb994b07SBrian Somers if (packetAddr.sin_addr.s_addr == INADDR_ANY && 49924084f9bSBrian Somers bytes - origBytes > aliasOverhead) 50024084f9bSBrian Somers aliasOverhead = bytes - origBytes; 50124084f9bSBrian Somers 50224084f9bSBrian Somers if (verbose) { 50324084f9bSBrian Somers 50424084f9bSBrian Somers /* 50524084f9bSBrian Somers * Print addresses after aliasing. 50624084f9bSBrian Somers */ 50724084f9bSBrian Somers printf (" aliased to\n"); 50824084f9bSBrian Somers printf (" "); 50924084f9bSBrian Somers PrintPacket (ip); 51024084f9bSBrian Somers printf ("\n"); 51124084f9bSBrian Somers } 512fb994b07SBrian Somers 513fb994b07SBrian Somers packetLen = bytes; 514fb994b07SBrian Somers packetSock = fd; 515fb994b07SBrian Somers FlushPacketBuffer (fd); 516fb994b07SBrian Somers } 517fb994b07SBrian Somers 518fb994b07SBrian Somers static void FlushPacketBuffer (int fd) 519fb994b07SBrian Somers { 520fb994b07SBrian Somers int wrote; 521fb994b07SBrian Somers char msgBuf[80]; 52224084f9bSBrian Somers /* 52324084f9bSBrian Somers * Put packet back for processing. 52424084f9bSBrian Somers */ 52524084f9bSBrian Somers wrote = sendto (fd, 526fb994b07SBrian Somers packetBuf, 527fb994b07SBrian Somers packetLen, 52824084f9bSBrian Somers 0, 529fb994b07SBrian Somers (struct sockaddr*) &packetAddr, 530fb994b07SBrian Somers sizeof packetAddr); 53124084f9bSBrian Somers 532fb994b07SBrian Somers if (wrote != packetLen) { 533fb994b07SBrian Somers /* 534fb994b07SBrian Somers * If buffer space is not available, 535fb994b07SBrian Somers * just return. Main loop will take care of 536fb994b07SBrian Somers * retrying send when space becomes available. 537fb994b07SBrian Somers */ 538fb994b07SBrian Somers if (errno == ENOBUFS) 539fb994b07SBrian Somers return; 54024084f9bSBrian Somers 54124084f9bSBrian Somers if (errno == EMSGSIZE) { 54224084f9bSBrian Somers 543fb994b07SBrian Somers if (packetAddr.sin_addr.s_addr == INADDR_ANY && 54424084f9bSBrian Somers ifMTU != -1) 54524084f9bSBrian Somers SendNeedFragIcmp (icmpSock, 546fb994b07SBrian Somers (struct ip*) packetBuf, 54724084f9bSBrian Somers ifMTU - aliasOverhead); 54824084f9bSBrian Somers } 54924084f9bSBrian Somers else { 55024084f9bSBrian Somers 5510fc81af1SPhilippe Charnier sprintf (msgBuf, "failed to write packet back"); 55224084f9bSBrian Somers Warn (msgBuf); 55324084f9bSBrian Somers } 55424084f9bSBrian Somers } 555fb994b07SBrian Somers 556fb994b07SBrian Somers packetSock = -1; 55724084f9bSBrian Somers } 55824084f9bSBrian Somers 55924084f9bSBrian Somers static void HandleRoutingInfo (int fd) 56024084f9bSBrian Somers { 56124084f9bSBrian Somers int bytes; 56224084f9bSBrian Somers struct if_msghdr ifMsg; 56324084f9bSBrian Somers /* 56424084f9bSBrian Somers * Get packet from socket. 56524084f9bSBrian Somers */ 56624084f9bSBrian Somers bytes = read (fd, &ifMsg, sizeof ifMsg); 56724084f9bSBrian Somers if (bytes == -1) { 56824084f9bSBrian Somers 5690fc81af1SPhilippe Charnier Warn ("read from routing socket failed"); 57024084f9bSBrian Somers return; 57124084f9bSBrian Somers } 57224084f9bSBrian Somers 57324084f9bSBrian Somers if (ifMsg.ifm_version != RTM_VERSION) { 57424084f9bSBrian Somers 5750fc81af1SPhilippe Charnier Warn ("unexpected packet read from routing socket"); 57624084f9bSBrian Somers return; 57724084f9bSBrian Somers } 57824084f9bSBrian Somers 57924084f9bSBrian Somers if (verbose) 58024084f9bSBrian Somers printf ("Routing message %X received.\n", ifMsg.ifm_type); 58124084f9bSBrian Somers 58224084f9bSBrian Somers if (ifMsg.ifm_type != RTM_NEWADDR) 58324084f9bSBrian Somers return; 58424084f9bSBrian Somers 58524084f9bSBrian Somers if (verbose && ifMsg.ifm_index == ifIndex) 58624084f9bSBrian Somers printf ("Interface address has changed.\n"); 58724084f9bSBrian Somers 58824084f9bSBrian Somers if (ifMsg.ifm_index == ifIndex) 58924084f9bSBrian Somers assignAliasAddr = 1; 59024084f9bSBrian Somers } 59124084f9bSBrian Somers 59224084f9bSBrian Somers static void PrintPacket (struct ip* ip) 59324084f9bSBrian Somers { 59424084f9bSBrian Somers struct tcphdr* tcphdr; 59524084f9bSBrian Somers 59624084f9bSBrian Somers if (ip->ip_p == IPPROTO_TCP) 59724084f9bSBrian Somers tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2)); 59824084f9bSBrian Somers else 59924084f9bSBrian Somers tcphdr = NULL; 60024084f9bSBrian Somers 60124084f9bSBrian Somers printf ("%s", inet_ntoa (ip->ip_src)); 60224084f9bSBrian Somers if (tcphdr) 60324084f9bSBrian Somers printf (":%d", ntohs (tcphdr->th_sport)); 60424084f9bSBrian Somers 60524084f9bSBrian Somers printf (" -> "); 60624084f9bSBrian Somers printf ("%s", inet_ntoa (ip->ip_dst)); 60724084f9bSBrian Somers if (tcphdr) 60824084f9bSBrian Somers printf (":%d", ntohs (tcphdr->th_dport)); 60924084f9bSBrian Somers } 61024084f9bSBrian Somers 61124084f9bSBrian Somers static void SetAliasAddressFromIfName (char* ifName) 61224084f9bSBrian Somers { 61324084f9bSBrian Somers struct ifconf cf; 61424084f9bSBrian Somers struct ifreq buf[32]; 61524084f9bSBrian Somers char msg[80]; 61624084f9bSBrian Somers struct ifreq* ifPtr; 61724084f9bSBrian Somers int extra; 61824084f9bSBrian Somers int helperSock; 61924084f9bSBrian Somers int bytes; 62024084f9bSBrian Somers struct sockaddr_in* addr; 62124084f9bSBrian Somers int found; 62224084f9bSBrian Somers struct ifreq req; 62324084f9bSBrian Somers char last[10]; 62424084f9bSBrian Somers /* 62524084f9bSBrian Somers * Create a dummy socket to access interface information. 62624084f9bSBrian Somers */ 62724084f9bSBrian Somers helperSock = socket (AF_INET, SOCK_DGRAM, 0); 62824084f9bSBrian Somers if (helperSock == -1) { 62924084f9bSBrian Somers 63024084f9bSBrian Somers Quit ("Failed to create helper socket."); 63124084f9bSBrian Somers exit (1); 63224084f9bSBrian Somers } 63324084f9bSBrian Somers 63424084f9bSBrian Somers cf.ifc_len = sizeof (buf); 63524084f9bSBrian Somers cf.ifc_req = buf; 63624084f9bSBrian Somers /* 63724084f9bSBrian Somers * Get interface data. 63824084f9bSBrian Somers */ 63924084f9bSBrian Somers if (ioctl (helperSock, SIOCGIFCONF, &cf) == -1) { 64024084f9bSBrian Somers 64124084f9bSBrian Somers Quit ("Ioctl SIOCGIFCONF failed."); 64224084f9bSBrian Somers exit (1); 64324084f9bSBrian Somers } 64424084f9bSBrian Somers 64524084f9bSBrian Somers ifIndex = 0; 64624084f9bSBrian Somers ifPtr = buf; 64724084f9bSBrian Somers bytes = cf.ifc_len; 64824084f9bSBrian Somers found = 0; 64924084f9bSBrian Somers last[0] = '\0'; 65024084f9bSBrian Somers /* 65124084f9bSBrian Somers * Loop through interfaces until one with 65224084f9bSBrian Somers * given name is found. This is done to 65324084f9bSBrian Somers * find correct interface index for routing 65424084f9bSBrian Somers * message processing. 65524084f9bSBrian Somers */ 65624084f9bSBrian Somers while (bytes) { 65724084f9bSBrian Somers 65824084f9bSBrian Somers if (ifPtr->ifr_addr.sa_family == AF_INET && 65924084f9bSBrian Somers !strcmp (ifPtr->ifr_name, ifName)) { 66024084f9bSBrian Somers 66124084f9bSBrian Somers found = 1; 66224084f9bSBrian Somers break; 66324084f9bSBrian Somers } 66424084f9bSBrian Somers 66524084f9bSBrian Somers if (strcmp (last, ifPtr->ifr_name)) { 66624084f9bSBrian Somers 66724084f9bSBrian Somers strcpy (last, ifPtr->ifr_name); 66824084f9bSBrian Somers ++ifIndex; 66924084f9bSBrian Somers } 67024084f9bSBrian Somers 67124084f9bSBrian Somers extra = ifPtr->ifr_addr.sa_len - sizeof (struct sockaddr); 67224084f9bSBrian Somers 67324084f9bSBrian Somers ifPtr++; 67424084f9bSBrian Somers ifPtr = (struct ifreq*) ((char*) ifPtr + extra); 67524084f9bSBrian Somers bytes -= sizeof (struct ifreq) + extra; 67624084f9bSBrian Somers } 67724084f9bSBrian Somers 67824084f9bSBrian Somers if (!found) { 67924084f9bSBrian Somers 68024084f9bSBrian Somers close (helperSock); 68124084f9bSBrian Somers sprintf (msg, "Unknown interface name %s.\n", ifName); 68224084f9bSBrian Somers Quit (msg); 68324084f9bSBrian Somers } 68424084f9bSBrian Somers /* 68524084f9bSBrian Somers * Get MTU size. 68624084f9bSBrian Somers */ 68724084f9bSBrian Somers strcpy (req.ifr_name, ifName); 68824084f9bSBrian Somers 68924084f9bSBrian Somers if (ioctl (helperSock, SIOCGIFMTU, &req) == -1) 69024084f9bSBrian Somers Quit ("Cannot get interface mtu size."); 69124084f9bSBrian Somers 69224084f9bSBrian Somers ifMTU = req.ifr_mtu; 69324084f9bSBrian Somers /* 69424084f9bSBrian Somers * Get interface address. 69524084f9bSBrian Somers */ 69624084f9bSBrian Somers if (ioctl (helperSock, SIOCGIFADDR, &req) == -1) 69724084f9bSBrian Somers Quit ("Cannot get interface address."); 69824084f9bSBrian Somers 69924084f9bSBrian Somers addr = (struct sockaddr_in*) &req.ifr_addr; 70024084f9bSBrian Somers SetPacketAliasAddress (addr->sin_addr); 70124084f9bSBrian Somers syslog (LOG_INFO, "Aliasing to %s, mtu %d bytes", 70224084f9bSBrian Somers inet_ntoa (addr->sin_addr), 70324084f9bSBrian Somers ifMTU); 70424084f9bSBrian Somers 70524084f9bSBrian Somers close (helperSock); 70624084f9bSBrian Somers } 70724084f9bSBrian Somers 70824084f9bSBrian Somers void Quit (char* msg) 70924084f9bSBrian Somers { 71024084f9bSBrian Somers Warn (msg); 71124084f9bSBrian Somers exit (1); 71224084f9bSBrian Somers } 71324084f9bSBrian Somers 71424084f9bSBrian Somers void Warn (char* msg) 71524084f9bSBrian Somers { 71624084f9bSBrian Somers if (background) 71724084f9bSBrian Somers syslog (LOG_ALERT, "%s (%m)", msg); 71824084f9bSBrian Somers else 7190fc81af1SPhilippe Charnier warn (msg); 72024084f9bSBrian Somers } 72124084f9bSBrian Somers 72224084f9bSBrian Somers static void RefreshAddr () 72324084f9bSBrian Somers { 72424084f9bSBrian Somers signal (SIGHUP, RefreshAddr); 72524084f9bSBrian Somers if (ifName) 72624084f9bSBrian Somers assignAliasAddr = 1; 72724084f9bSBrian Somers } 72824084f9bSBrian Somers 72924084f9bSBrian Somers static void InitiateShutdown () 73024084f9bSBrian Somers { 73124084f9bSBrian Somers /* 73224084f9bSBrian Somers * Start timer to allow kernel gracefully 73324084f9bSBrian Somers * shutdown existing connections when system 73424084f9bSBrian Somers * is shut down. 73524084f9bSBrian Somers */ 73624084f9bSBrian Somers signal (SIGALRM, Shutdown); 73724084f9bSBrian Somers alarm (10); 73824084f9bSBrian Somers } 73924084f9bSBrian Somers 74024084f9bSBrian Somers static void Shutdown () 74124084f9bSBrian Somers { 74224084f9bSBrian Somers running = 0; 74324084f9bSBrian Somers } 74424084f9bSBrian Somers 74524084f9bSBrian Somers /* 74624084f9bSBrian Somers * Different options recognized by this program. 74724084f9bSBrian Somers */ 74824084f9bSBrian Somers 74924084f9bSBrian Somers enum Option { 75024084f9bSBrian Somers 75124084f9bSBrian Somers PacketAliasOption, 75224084f9bSBrian Somers Verbose, 75324084f9bSBrian Somers InPort, 75424084f9bSBrian Somers OutPort, 75524084f9bSBrian Somers Port, 75624084f9bSBrian Somers AliasAddress, 75724084f9bSBrian Somers InterfaceName, 75824084f9bSBrian Somers PermanentLink, 75924084f9bSBrian Somers RedirectPort, 76024084f9bSBrian Somers RedirectAddress, 76124084f9bSBrian Somers ConfigFile, 76224084f9bSBrian Somers DynamicMode 76324084f9bSBrian Somers }; 76424084f9bSBrian Somers 76524084f9bSBrian Somers enum Param { 76624084f9bSBrian Somers 76724084f9bSBrian Somers YesNo, 76824084f9bSBrian Somers Numeric, 76924084f9bSBrian Somers String, 77024084f9bSBrian Somers None, 77124084f9bSBrian Somers Address, 77224084f9bSBrian Somers Service 77324084f9bSBrian Somers }; 77424084f9bSBrian Somers 77524084f9bSBrian Somers /* 77624084f9bSBrian Somers * Option information structure (used by ParseOption). 77724084f9bSBrian Somers */ 77824084f9bSBrian Somers 77924084f9bSBrian Somers struct OptionInfo { 78024084f9bSBrian Somers 78124084f9bSBrian Somers enum Option type; 78224084f9bSBrian Somers int packetAliasOpt; 78324084f9bSBrian Somers enum Param parm; 78424084f9bSBrian Somers char* parmDescription; 78524084f9bSBrian Somers char* description; 78624084f9bSBrian Somers char* name; 78724084f9bSBrian Somers char* shortName; 78824084f9bSBrian Somers }; 78924084f9bSBrian Somers 79024084f9bSBrian Somers /* 79124084f9bSBrian Somers * Table of known options. 79224084f9bSBrian Somers */ 79324084f9bSBrian Somers 79424084f9bSBrian Somers static struct OptionInfo optionTable[] = { 79524084f9bSBrian Somers 79624084f9bSBrian Somers { PacketAliasOption, 79724084f9bSBrian Somers PKT_ALIAS_UNREGISTERED_ONLY, 79824084f9bSBrian Somers YesNo, 79924084f9bSBrian Somers "[yes|no]", 80024084f9bSBrian Somers "alias only unregistered addresses", 80124084f9bSBrian Somers "unregistered_only", 80224084f9bSBrian Somers "u" }, 80324084f9bSBrian Somers 80424084f9bSBrian Somers { PacketAliasOption, 80524084f9bSBrian Somers PKT_ALIAS_LOG, 80624084f9bSBrian Somers YesNo, 80724084f9bSBrian Somers "[yes|no]", 80824084f9bSBrian Somers "enable logging", 80924084f9bSBrian Somers "log", 81024084f9bSBrian Somers "l" }, 81124084f9bSBrian Somers 81224084f9bSBrian Somers { PacketAliasOption, 81324084f9bSBrian Somers PKT_ALIAS_DENY_INCOMING, 81424084f9bSBrian Somers YesNo, 81524084f9bSBrian Somers "[yes|no]", 81624084f9bSBrian Somers "allow incoming connections", 81724084f9bSBrian Somers "deny_incoming", 81824084f9bSBrian Somers "d" }, 81924084f9bSBrian Somers 82024084f9bSBrian Somers { PacketAliasOption, 82124084f9bSBrian Somers PKT_ALIAS_USE_SOCKETS, 82224084f9bSBrian Somers YesNo, 82324084f9bSBrian Somers "[yes|no]", 82424084f9bSBrian Somers "use sockets to inhibit port conflict", 82524084f9bSBrian Somers "use_sockets", 82624084f9bSBrian Somers "s" }, 82724084f9bSBrian Somers 82824084f9bSBrian Somers { PacketAliasOption, 82924084f9bSBrian Somers PKT_ALIAS_SAME_PORTS, 83024084f9bSBrian Somers YesNo, 83124084f9bSBrian Somers "[yes|no]", 83224084f9bSBrian Somers "try to keep original port numbers for connections", 83324084f9bSBrian Somers "same_ports", 83424084f9bSBrian Somers "m" }, 83524084f9bSBrian Somers 83624084f9bSBrian Somers { Verbose, 83724084f9bSBrian Somers 0, 83824084f9bSBrian Somers YesNo, 83924084f9bSBrian Somers "[yes|no]", 84024084f9bSBrian Somers "verbose mode, dump packet information", 84124084f9bSBrian Somers "verbose", 84224084f9bSBrian Somers "v" }, 84324084f9bSBrian Somers 84424084f9bSBrian Somers { DynamicMode, 84524084f9bSBrian Somers 0, 84624084f9bSBrian Somers YesNo, 84724084f9bSBrian Somers "[yes|no]", 84824084f9bSBrian Somers "dynamic mode, automatically detect interface address changes", 84924084f9bSBrian Somers "dynamic", 85024084f9bSBrian Somers NULL }, 85124084f9bSBrian Somers 85224084f9bSBrian Somers { InPort, 85324084f9bSBrian Somers 0, 85424084f9bSBrian Somers Service, 85524084f9bSBrian Somers "number|service_name", 85624084f9bSBrian Somers "set port for incoming packets", 85724084f9bSBrian Somers "in_port", 85824084f9bSBrian Somers "i" }, 85924084f9bSBrian Somers 86024084f9bSBrian Somers { OutPort, 86124084f9bSBrian Somers 0, 86224084f9bSBrian Somers Service, 86324084f9bSBrian Somers "number|service_name", 86424084f9bSBrian Somers "set port for outgoing packets", 86524084f9bSBrian Somers "out_port", 86624084f9bSBrian Somers "o" }, 86724084f9bSBrian Somers 86824084f9bSBrian Somers { Port, 86924084f9bSBrian Somers 0, 87024084f9bSBrian Somers Service, 87124084f9bSBrian Somers "number|service_name", 87224084f9bSBrian Somers "set port (defaults to natd/divert)", 87324084f9bSBrian Somers "port", 87424084f9bSBrian Somers "p" }, 87524084f9bSBrian Somers 87624084f9bSBrian Somers { AliasAddress, 87724084f9bSBrian Somers 0, 87824084f9bSBrian Somers Address, 87924084f9bSBrian Somers "x.x.x.x", 88024084f9bSBrian Somers "address to use for aliasing", 88124084f9bSBrian Somers "alias_address", 88224084f9bSBrian Somers "a" }, 88324084f9bSBrian Somers 88424084f9bSBrian Somers { InterfaceName, 88524084f9bSBrian Somers 0, 88624084f9bSBrian Somers String, 88724084f9bSBrian Somers "network_if_name", 88824084f9bSBrian Somers "take aliasing address from interface", 88924084f9bSBrian Somers "interface", 89024084f9bSBrian Somers "n" }, 89124084f9bSBrian Somers 89224084f9bSBrian Somers { PermanentLink, 89324084f9bSBrian Somers 0, 89424084f9bSBrian Somers String, 89524084f9bSBrian Somers "tcp|udp src:port dst:port alias", 89624084f9bSBrian Somers "define permanent link for incoming connection", 89724084f9bSBrian Somers "permanent_link", 89824084f9bSBrian Somers NULL }, 89924084f9bSBrian Somers 90024084f9bSBrian Somers { RedirectPort, 90124084f9bSBrian Somers 0, 90224084f9bSBrian Somers String, 90324084f9bSBrian Somers "tcp|udp local_addr:local_port [public_addr:]public_port" 90424084f9bSBrian Somers " [remote_addr[:remote_port]]", 90524084f9bSBrian Somers "redirect a port for incoming traffic", 90624084f9bSBrian Somers "redirect_port", 90724084f9bSBrian Somers NULL }, 90824084f9bSBrian Somers 90924084f9bSBrian Somers { RedirectAddress, 91024084f9bSBrian Somers 0, 91124084f9bSBrian Somers String, 91224084f9bSBrian Somers "local_addr public_addr", 91324084f9bSBrian Somers "define mapping between local and public addresses", 91424084f9bSBrian Somers "redirect_address", 91524084f9bSBrian Somers NULL }, 91624084f9bSBrian Somers 91724084f9bSBrian Somers { ConfigFile, 91824084f9bSBrian Somers 0, 91924084f9bSBrian Somers String, 92024084f9bSBrian Somers "file_name", 92124084f9bSBrian Somers "read options from configuration file", 92224084f9bSBrian Somers "config", 92324084f9bSBrian Somers "f" } 92424084f9bSBrian Somers }; 92524084f9bSBrian Somers 92624084f9bSBrian Somers static void ParseOption (char* option, char* parms, int cmdLine) 92724084f9bSBrian Somers { 92824084f9bSBrian Somers int i; 92924084f9bSBrian Somers struct OptionInfo* info; 93024084f9bSBrian Somers int yesNoValue; 93124084f9bSBrian Somers int aliasValue; 93224084f9bSBrian Somers int numValue; 93324084f9bSBrian Somers char* strValue; 93424084f9bSBrian Somers struct in_addr addrValue; 93524084f9bSBrian Somers int max; 93624084f9bSBrian Somers char* end; 93724084f9bSBrian Somers /* 93824084f9bSBrian Somers * Find option from table. 93924084f9bSBrian Somers */ 94024084f9bSBrian Somers max = sizeof (optionTable) / sizeof (struct OptionInfo); 94124084f9bSBrian Somers for (i = 0, info = optionTable; i < max; i++, info++) { 94224084f9bSBrian Somers 94324084f9bSBrian Somers if (!strcmp (info->name, option)) 94424084f9bSBrian Somers break; 94524084f9bSBrian Somers 94624084f9bSBrian Somers if (info->shortName) 94724084f9bSBrian Somers if (!strcmp (info->shortName, option)) 94824084f9bSBrian Somers break; 94924084f9bSBrian Somers } 95024084f9bSBrian Somers 95124084f9bSBrian Somers if (i >= max) { 95224084f9bSBrian Somers 9530fc81af1SPhilippe Charnier warnx ("unknown option %s", option); 95424084f9bSBrian Somers Usage (); 95524084f9bSBrian Somers } 95624084f9bSBrian Somers 95724084f9bSBrian Somers yesNoValue = 0; 95824084f9bSBrian Somers numValue = 0; 95924084f9bSBrian Somers strValue = NULL; 96024084f9bSBrian Somers /* 96124084f9bSBrian Somers * Check parameters. 96224084f9bSBrian Somers */ 96324084f9bSBrian Somers switch (info->parm) { 96424084f9bSBrian Somers case YesNo: 96524084f9bSBrian Somers if (!parms) 96624084f9bSBrian Somers parms = "yes"; 96724084f9bSBrian Somers 96824084f9bSBrian Somers if (!strcmp (parms, "yes")) 96924084f9bSBrian Somers yesNoValue = 1; 97024084f9bSBrian Somers else 97124084f9bSBrian Somers if (!strcmp (parms, "no")) 97224084f9bSBrian Somers yesNoValue = 0; 9730fc81af1SPhilippe Charnier else 9740fc81af1SPhilippe Charnier errx(1, "%s needs yes/no parameter", option); 97524084f9bSBrian Somers break; 97624084f9bSBrian Somers 97724084f9bSBrian Somers case Service: 9780fc81af1SPhilippe Charnier if (!parms) 9790fc81af1SPhilippe Charnier errx(1, 9800fc81af1SPhilippe Charnier "%s needs service name or port number parameter", option); 98124084f9bSBrian Somers 98224084f9bSBrian Somers numValue = StrToPort (parms, "divert"); 98324084f9bSBrian Somers break; 98424084f9bSBrian Somers 98524084f9bSBrian Somers case Numeric: 98624084f9bSBrian Somers if (parms) 98724084f9bSBrian Somers numValue = strtol (parms, &end, 10); 98824084f9bSBrian Somers else 98924084f9bSBrian Somers end = parms; 99024084f9bSBrian Somers 9910fc81af1SPhilippe Charnier if (end == parms) 9920fc81af1SPhilippe Charnier errx(1, "%s needs numeric parameter", option); 99324084f9bSBrian Somers break; 99424084f9bSBrian Somers 99524084f9bSBrian Somers case String: 99624084f9bSBrian Somers strValue = parms; 9970fc81af1SPhilippe Charnier if (!strValue) 9980fc81af1SPhilippe Charnier errx(1, "%s needs parameter", option); 99924084f9bSBrian Somers break; 100024084f9bSBrian Somers 100124084f9bSBrian Somers case None: 10020fc81af1SPhilippe Charnier if (parms) 10030fc81af1SPhilippe Charnier errx(1, "%s does not take parameters", option); 100424084f9bSBrian Somers break; 100524084f9bSBrian Somers 100624084f9bSBrian Somers case Address: 10070fc81af1SPhilippe Charnier if (!parms) 10080fc81af1SPhilippe Charnier errx(1, "%s needs address/host parameter", option); 100924084f9bSBrian Somers 101024084f9bSBrian Somers StrToAddr (parms, &addrValue); 101124084f9bSBrian Somers break; 101224084f9bSBrian Somers } 101324084f9bSBrian Somers 101424084f9bSBrian Somers switch (info->type) { 101524084f9bSBrian Somers case PacketAliasOption: 101624084f9bSBrian Somers 101724084f9bSBrian Somers aliasValue = yesNoValue ? info->packetAliasOpt : 0; 1018fb994b07SBrian Somers PacketAliasSetMode (aliasValue, info->packetAliasOpt); 101924084f9bSBrian Somers break; 102024084f9bSBrian Somers 102124084f9bSBrian Somers case Verbose: 102224084f9bSBrian Somers verbose = yesNoValue; 102324084f9bSBrian Somers break; 102424084f9bSBrian Somers 102524084f9bSBrian Somers case DynamicMode: 102624084f9bSBrian Somers dynamicMode = yesNoValue; 102724084f9bSBrian Somers break; 102824084f9bSBrian Somers 102924084f9bSBrian Somers case InPort: 103024084f9bSBrian Somers inPort = numValue; 103124084f9bSBrian Somers break; 103224084f9bSBrian Somers 103324084f9bSBrian Somers case OutPort: 103424084f9bSBrian Somers outPort = numValue; 103524084f9bSBrian Somers break; 103624084f9bSBrian Somers 103724084f9bSBrian Somers case Port: 103824084f9bSBrian Somers inOutPort = numValue; 103924084f9bSBrian Somers break; 104024084f9bSBrian Somers 104124084f9bSBrian Somers case AliasAddress: 104224084f9bSBrian Somers memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr)); 104324084f9bSBrian Somers break; 104424084f9bSBrian Somers 104524084f9bSBrian Somers case PermanentLink: 104624084f9bSBrian Somers SetupPermanentLink (strValue); 104724084f9bSBrian Somers break; 104824084f9bSBrian Somers 104924084f9bSBrian Somers case RedirectPort: 105024084f9bSBrian Somers SetupPortRedirect (strValue); 105124084f9bSBrian Somers break; 105224084f9bSBrian Somers 105324084f9bSBrian Somers case RedirectAddress: 105424084f9bSBrian Somers SetupAddressRedirect (strValue); 105524084f9bSBrian Somers break; 105624084f9bSBrian Somers 105724084f9bSBrian Somers case InterfaceName: 105824084f9bSBrian Somers if (ifName) 105924084f9bSBrian Somers free (ifName); 106024084f9bSBrian Somers 106124084f9bSBrian Somers ifName = strdup (strValue); 106224084f9bSBrian Somers assignAliasAddr = 1; 106324084f9bSBrian Somers break; 106424084f9bSBrian Somers 106524084f9bSBrian Somers case ConfigFile: 106624084f9bSBrian Somers ReadConfigFile (strValue); 106724084f9bSBrian Somers break; 106824084f9bSBrian Somers } 106924084f9bSBrian Somers } 107024084f9bSBrian Somers 107124084f9bSBrian Somers void ReadConfigFile (char* fileName) 107224084f9bSBrian Somers { 107324084f9bSBrian Somers FILE* file; 107424084f9bSBrian Somers char buf[128]; 107524084f9bSBrian Somers char* ptr; 107624084f9bSBrian Somers char* option; 107724084f9bSBrian Somers 107824084f9bSBrian Somers file = fopen (fileName, "r"); 107924084f9bSBrian Somers if (!file) { 108024084f9bSBrian Somers 108124084f9bSBrian Somers sprintf (buf, "Cannot open config file %s.\n", fileName); 108224084f9bSBrian Somers Quit (buf); 108324084f9bSBrian Somers } 108424084f9bSBrian Somers 108524084f9bSBrian Somers while (fgets (buf, sizeof (buf), file)) { 108624084f9bSBrian Somers 108724084f9bSBrian Somers ptr = strchr (buf, '\n'); 10880fc81af1SPhilippe Charnier if (!ptr) 10890fc81af1SPhilippe Charnier errx(1, "config line too link: %s", buf); 109024084f9bSBrian Somers 109124084f9bSBrian Somers *ptr = '\0'; 109224084f9bSBrian Somers if (buf[0] == '#') 109324084f9bSBrian Somers continue; 109424084f9bSBrian Somers 109524084f9bSBrian Somers ptr = buf; 109624084f9bSBrian Somers /* 109724084f9bSBrian Somers * Skip white space at beginning of line. 109824084f9bSBrian Somers */ 109924084f9bSBrian Somers while (*ptr && isspace (*ptr)) 110024084f9bSBrian Somers ++ptr; 110124084f9bSBrian Somers 110224084f9bSBrian Somers if (*ptr == '\0') 110324084f9bSBrian Somers continue; 110424084f9bSBrian Somers /* 110524084f9bSBrian Somers * Extract option name. 110624084f9bSBrian Somers */ 110724084f9bSBrian Somers option = ptr; 110824084f9bSBrian Somers while (*ptr && !isspace (*ptr)) 110924084f9bSBrian Somers ++ptr; 111024084f9bSBrian Somers 111124084f9bSBrian Somers if (*ptr != '\0') { 111224084f9bSBrian Somers 111324084f9bSBrian Somers *ptr = '\0'; 111424084f9bSBrian Somers ++ptr; 111524084f9bSBrian Somers } 111624084f9bSBrian Somers /* 111724084f9bSBrian Somers * Skip white space between name and parms. 111824084f9bSBrian Somers */ 111924084f9bSBrian Somers while (*ptr && isspace (*ptr)) 112024084f9bSBrian Somers ++ptr; 112124084f9bSBrian Somers 112224084f9bSBrian Somers ParseOption (option, *ptr ? ptr : NULL, 0); 112324084f9bSBrian Somers } 112424084f9bSBrian Somers 112524084f9bSBrian Somers fclose (file); 112624084f9bSBrian Somers } 112724084f9bSBrian Somers 112824084f9bSBrian Somers static void Usage () 112924084f9bSBrian Somers { 113024084f9bSBrian Somers int i; 113124084f9bSBrian Somers int max; 113224084f9bSBrian Somers struct OptionInfo* info; 113324084f9bSBrian Somers 113424084f9bSBrian Somers fprintf (stderr, "Recognized options:\n\n"); 113524084f9bSBrian Somers 113624084f9bSBrian Somers max = sizeof (optionTable) / sizeof (struct OptionInfo); 113724084f9bSBrian Somers for (i = 0, info = optionTable; i < max; i++, info++) { 113824084f9bSBrian Somers 113924084f9bSBrian Somers fprintf (stderr, "-%-20s %s\n", info->name, 114024084f9bSBrian Somers info->parmDescription); 114124084f9bSBrian Somers 114224084f9bSBrian Somers if (info->shortName) 114324084f9bSBrian Somers fprintf (stderr, "-%-20s %s\n", info->shortName, 114424084f9bSBrian Somers info->parmDescription); 114524084f9bSBrian Somers 114624084f9bSBrian Somers fprintf (stderr, " %s\n\n", info->description); 114724084f9bSBrian Somers } 114824084f9bSBrian Somers 114924084f9bSBrian Somers exit (1); 115024084f9bSBrian Somers } 115124084f9bSBrian Somers 115224084f9bSBrian Somers void SetupPermanentLink (char* parms) 115324084f9bSBrian Somers { 115424084f9bSBrian Somers char buf[128]; 115524084f9bSBrian Somers char* ptr; 115624084f9bSBrian Somers struct in_addr srcAddr; 115724084f9bSBrian Somers struct in_addr dstAddr; 115824084f9bSBrian Somers int srcPort; 115924084f9bSBrian Somers int dstPort; 116024084f9bSBrian Somers int aliasPort; 116124084f9bSBrian Somers int proto; 116224084f9bSBrian Somers char* protoName; 116324084f9bSBrian Somers 116424084f9bSBrian Somers strcpy (buf, parms); 116524084f9bSBrian Somers /* 116624084f9bSBrian Somers * Extract protocol. 116724084f9bSBrian Somers */ 116824084f9bSBrian Somers protoName = strtok (buf, " \t"); 11690fc81af1SPhilippe Charnier if (!protoName) 11700fc81af1SPhilippe Charnier errx(1, "permanent_link: missing protocol"); 117124084f9bSBrian Somers 117224084f9bSBrian Somers proto = StrToProto (protoName); 117324084f9bSBrian Somers /* 117424084f9bSBrian Somers * Extract source address. 117524084f9bSBrian Somers */ 117624084f9bSBrian Somers ptr = strtok (NULL, " \t"); 11770fc81af1SPhilippe Charnier if (!ptr) 11780fc81af1SPhilippe Charnier errx(1, "permanent_link: missing src address"); 117924084f9bSBrian Somers 118024084f9bSBrian Somers srcPort = StrToAddrAndPort (ptr, &srcAddr, protoName); 118124084f9bSBrian Somers /* 118224084f9bSBrian Somers * Extract destination address. 118324084f9bSBrian Somers */ 118424084f9bSBrian Somers ptr = strtok (NULL, " \t"); 11850fc81af1SPhilippe Charnier if (!ptr) 11860fc81af1SPhilippe Charnier errx(1, "permanent_link: missing dst address"); 118724084f9bSBrian Somers 118824084f9bSBrian Somers dstPort = StrToAddrAndPort (ptr, &dstAddr, protoName); 118924084f9bSBrian Somers /* 119024084f9bSBrian Somers * Export alias port. 119124084f9bSBrian Somers */ 119224084f9bSBrian Somers ptr = strtok (NULL, " \t"); 11930fc81af1SPhilippe Charnier if (!ptr) 11940fc81af1SPhilippe Charnier errx(1, "permanent_link: missing alias port"); 119524084f9bSBrian Somers 119624084f9bSBrian Somers aliasPort = StrToPort (ptr, protoName); 119724084f9bSBrian Somers 119824084f9bSBrian Somers PacketAliasPermanentLink (srcAddr, 119924084f9bSBrian Somers srcPort, 120024084f9bSBrian Somers dstAddr, 120124084f9bSBrian Somers dstPort, 120224084f9bSBrian Somers aliasPort, 120324084f9bSBrian Somers proto); 120424084f9bSBrian Somers } 120524084f9bSBrian Somers 120624084f9bSBrian Somers void SetupPortRedirect (char* parms) 120724084f9bSBrian Somers { 120824084f9bSBrian Somers char buf[128]; 120924084f9bSBrian Somers char* ptr; 121024084f9bSBrian Somers struct in_addr localAddr; 121124084f9bSBrian Somers struct in_addr publicAddr; 121224084f9bSBrian Somers struct in_addr remoteAddr; 121324084f9bSBrian Somers int localPort; 121424084f9bSBrian Somers int publicPort; 121524084f9bSBrian Somers int remotePort; 121624084f9bSBrian Somers int proto; 121724084f9bSBrian Somers char* protoName; 121824084f9bSBrian Somers char* separator; 121924084f9bSBrian Somers 122024084f9bSBrian Somers strcpy (buf, parms); 122124084f9bSBrian Somers /* 122224084f9bSBrian Somers * Extract protocol. 122324084f9bSBrian Somers */ 122424084f9bSBrian Somers protoName = strtok (buf, " \t"); 12250fc81af1SPhilippe Charnier if (!protoName) 12260fc81af1SPhilippe Charnier errx(1, "redirect_port: missing protocol"); 122724084f9bSBrian Somers 122824084f9bSBrian Somers proto = StrToProto (protoName); 122924084f9bSBrian Somers /* 123024084f9bSBrian Somers * Extract local address. 123124084f9bSBrian Somers */ 123224084f9bSBrian Somers ptr = strtok (NULL, " \t"); 12330fc81af1SPhilippe Charnier if (!ptr) 12340fc81af1SPhilippe Charnier errx(1, "redirect_port: missing local address"); 123524084f9bSBrian Somers 123624084f9bSBrian Somers localPort = StrToAddrAndPort (ptr, &localAddr, protoName); 123724084f9bSBrian Somers /* 123824084f9bSBrian Somers * Extract public port and optinally address. 123924084f9bSBrian Somers */ 124024084f9bSBrian Somers ptr = strtok (NULL, " \t"); 12410fc81af1SPhilippe Charnier if (!ptr) 12420fc81af1SPhilippe Charnier errx(1, "redirect_port: missing public port"); 124324084f9bSBrian Somers 124424084f9bSBrian Somers separator = strchr (ptr, ':'); 124524084f9bSBrian Somers if (separator) 124624084f9bSBrian Somers publicPort = StrToAddrAndPort (ptr, &publicAddr, protoName); 124724084f9bSBrian Somers else { 124824084f9bSBrian Somers 124924084f9bSBrian Somers publicAddr.s_addr = INADDR_ANY; 125024084f9bSBrian Somers publicPort = StrToPort (ptr, protoName); 125124084f9bSBrian Somers } 125224084f9bSBrian Somers 125324084f9bSBrian Somers /* 125424084f9bSBrian Somers * Extract remote address and optionally port. 125524084f9bSBrian Somers */ 125624084f9bSBrian Somers ptr = strtok (NULL, " \t"); 125724084f9bSBrian Somers if (ptr) { 125824084f9bSBrian Somers 125924084f9bSBrian Somers 126024084f9bSBrian Somers separator = strchr (ptr, ':'); 126124084f9bSBrian Somers if (separator) 126224084f9bSBrian Somers remotePort = StrToAddrAndPort (ptr, 126324084f9bSBrian Somers &remoteAddr, 126424084f9bSBrian Somers protoName); 126524084f9bSBrian Somers else { 126624084f9bSBrian Somers 126724084f9bSBrian Somers remotePort = 0; 126824084f9bSBrian Somers StrToAddr (ptr, &remoteAddr); 126924084f9bSBrian Somers } 127024084f9bSBrian Somers } 127124084f9bSBrian Somers else { 127224084f9bSBrian Somers 127324084f9bSBrian Somers remotePort = 0; 127424084f9bSBrian Somers remoteAddr.s_addr = INADDR_ANY; 127524084f9bSBrian Somers } 127624084f9bSBrian Somers 127724084f9bSBrian Somers PacketAliasRedirectPort (localAddr, 127824084f9bSBrian Somers localPort, 127924084f9bSBrian Somers remoteAddr, 128024084f9bSBrian Somers remotePort, 128124084f9bSBrian Somers publicAddr, 128224084f9bSBrian Somers publicPort, 128324084f9bSBrian Somers proto); 128424084f9bSBrian Somers } 128524084f9bSBrian Somers 128624084f9bSBrian Somers void SetupAddressRedirect (char* parms) 128724084f9bSBrian Somers { 128824084f9bSBrian Somers char buf[128]; 128924084f9bSBrian Somers char* ptr; 129024084f9bSBrian Somers struct in_addr localAddr; 129124084f9bSBrian Somers struct in_addr publicAddr; 129224084f9bSBrian Somers 129324084f9bSBrian Somers strcpy (buf, parms); 129424084f9bSBrian Somers /* 129524084f9bSBrian Somers * Extract local address. 129624084f9bSBrian Somers */ 129724084f9bSBrian Somers ptr = strtok (buf, " \t"); 12980fc81af1SPhilippe Charnier if (!ptr) 12990fc81af1SPhilippe Charnier errx(1, "redirect_address: missing local address"); 130024084f9bSBrian Somers 130124084f9bSBrian Somers StrToAddr (ptr, &localAddr); 130224084f9bSBrian Somers /* 130324084f9bSBrian Somers * Extract public address. 130424084f9bSBrian Somers */ 130524084f9bSBrian Somers ptr = strtok (NULL, " \t"); 13060fc81af1SPhilippe Charnier if (!ptr) 13070fc81af1SPhilippe Charnier errx(1, "redirect_address: missing public address"); 130824084f9bSBrian Somers 130924084f9bSBrian Somers StrToAddr (ptr, &publicAddr); 131024084f9bSBrian Somers PacketAliasRedirectAddr (localAddr, publicAddr); 131124084f9bSBrian Somers } 131224084f9bSBrian Somers 131324084f9bSBrian Somers void StrToAddr (char* str, struct in_addr* addr) 131424084f9bSBrian Somers { 131524084f9bSBrian Somers struct hostent* hp; 131624084f9bSBrian Somers 131724084f9bSBrian Somers if (inet_aton (str, addr)) 131824084f9bSBrian Somers return; 131924084f9bSBrian Somers 132024084f9bSBrian Somers hp = gethostbyname (str); 13210fc81af1SPhilippe Charnier if (!hp) 13220fc81af1SPhilippe Charnier errx(1, "unknown host %s", str); 132324084f9bSBrian Somers 132424084f9bSBrian Somers memcpy (addr, hp->h_addr, sizeof (struct in_addr)); 132524084f9bSBrian Somers } 132624084f9bSBrian Somers 132724084f9bSBrian Somers int StrToPort (char* str, char* proto) 132824084f9bSBrian Somers { 132924084f9bSBrian Somers int port; 133024084f9bSBrian Somers struct servent* sp; 133124084f9bSBrian Somers char* end; 133224084f9bSBrian Somers 133324084f9bSBrian Somers port = strtol (str, &end, 10); 133424084f9bSBrian Somers if (end != str) 133527c20503SBrian Somers return htons (port); 133624084f9bSBrian Somers 133724084f9bSBrian Somers sp = getservbyname (str, proto); 13380fc81af1SPhilippe Charnier if (!sp) 13390fc81af1SPhilippe Charnier errx(1, "unknown service %s/%s", str, proto); 134024084f9bSBrian Somers 134124084f9bSBrian Somers return sp->s_port; 134224084f9bSBrian Somers } 134324084f9bSBrian Somers 134424084f9bSBrian Somers int StrToProto (char* str) 134524084f9bSBrian Somers { 134624084f9bSBrian Somers if (!strcmp (str, "tcp")) 134724084f9bSBrian Somers return IPPROTO_TCP; 134824084f9bSBrian Somers 134924084f9bSBrian Somers if (!strcmp (str, "udp")) 135024084f9bSBrian Somers return IPPROTO_UDP; 135124084f9bSBrian Somers 13520fc81af1SPhilippe Charnier errx(1, "unknown protocol %s. Expected tcp or udp", str); 135324084f9bSBrian Somers } 135424084f9bSBrian Somers 135524084f9bSBrian Somers int StrToAddrAndPort (char* str, struct in_addr* addr, char* proto) 135624084f9bSBrian Somers { 135724084f9bSBrian Somers char* ptr; 135824084f9bSBrian Somers 135924084f9bSBrian Somers ptr = strchr (str, ':'); 13600fc81af1SPhilippe Charnier if (!ptr) 13610fc81af1SPhilippe Charnier errx(1, "%s is missing port number", str); 136224084f9bSBrian Somers 136324084f9bSBrian Somers *ptr = '\0'; 136424084f9bSBrian Somers ++ptr; 136524084f9bSBrian Somers 136624084f9bSBrian Somers StrToAddr (str, addr); 136724084f9bSBrian Somers return StrToPort (ptr, proto); 136824084f9bSBrian Somers } 136924084f9bSBrian Somers 1370