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); 89902cb50aSBrian Somers static void ParseOption (const char* option, const char* parms, int cmdLine); 90902cb50aSBrian Somers static void ReadConfigFile (const char* fileName); 91902cb50aSBrian Somers static void SetupPortRedirect (const char* parms); 92902cb50aSBrian Somers static void SetupAddressRedirect (const char* parms); 93902cb50aSBrian Somers static void SetupPptpAlias (const char* parms); 94902cb50aSBrian Somers static void StrToAddr (const char* str, struct in_addr* addr); 95902cb50aSBrian Somers static u_short StrToPort (const char* str, const char* proto); 96902cb50aSBrian Somers static int StrToPortRange (const char* str, const char* proto, port_range *portRange); 97902cb50aSBrian Somers static int StrToProto (const char* str); 98902cb50aSBrian Somers static int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange); 9924084f9bSBrian Somers static void ParseArgs (int argc, char** argv); 100fb994b07SBrian Somers static void FlushPacketBuffer (int fd); 10124084f9bSBrian Somers 10224084f9bSBrian Somers /* 10324084f9bSBrian Somers * Globals. 10424084f9bSBrian Somers */ 10524084f9bSBrian Somers 10624084f9bSBrian Somers static int verbose; 10724084f9bSBrian Somers static int background; 10824084f9bSBrian Somers static int running; 10924084f9bSBrian Somers static int assignAliasAddr; 11024084f9bSBrian Somers static char* ifName; 11124084f9bSBrian Somers static int ifIndex; 11267a886fbSBrian Somers static u_short inPort; 11367a886fbSBrian Somers static u_short outPort; 11467a886fbSBrian Somers static u_short inOutPort; 11524084f9bSBrian Somers static struct in_addr aliasAddr; 11624084f9bSBrian Somers static int dynamicMode; 11724084f9bSBrian Somers static int ifMTU; 11824084f9bSBrian Somers static int aliasOverhead; 11924084f9bSBrian Somers static int icmpSock; 120fb994b07SBrian Somers static char packetBuf[IP_MAXPACKET]; 121fb994b07SBrian Somers static int packetLen; 122fb994b07SBrian Somers static struct sockaddr_in packetAddr; 123fb994b07SBrian Somers static int packetSock; 12459a7c613SBrian Somers static int packetDirection; 125f9b06d5cSBrian Somers static int dropIgnoredIncoming; 12659a7c613SBrian Somers static int logDropped; 12759a7c613SBrian Somers static int logFacility; 12824084f9bSBrian Somers 12924084f9bSBrian Somers int main (int argc, char** argv) 13024084f9bSBrian Somers { 13124084f9bSBrian Somers int divertIn; 13224084f9bSBrian Somers int divertOut; 13324084f9bSBrian Somers int divertInOut; 13424084f9bSBrian Somers int routeSock; 13524084f9bSBrian Somers struct sockaddr_in addr; 13624084f9bSBrian Somers fd_set readMask; 137fb994b07SBrian Somers fd_set writeMask; 13824084f9bSBrian Somers int fdMax; 13924084f9bSBrian Somers /* 14024084f9bSBrian Somers * Initialize packet aliasing software. 14124084f9bSBrian Somers * Done already here to be able to alter option bits 14224084f9bSBrian Somers * during command line and configuration file processing. 14324084f9bSBrian Somers */ 144fb994b07SBrian Somers PacketAliasInit (); 14524084f9bSBrian Somers /* 14624084f9bSBrian Somers * Parse options. 14724084f9bSBrian Somers */ 14824084f9bSBrian Somers inPort = 0; 14924084f9bSBrian Somers outPort = 0; 15024084f9bSBrian Somers verbose = 0; 15124084f9bSBrian Somers inOutPort = 0; 15224084f9bSBrian Somers ifName = NULL; 15324084f9bSBrian Somers ifMTU = -1; 15424084f9bSBrian Somers background = 0; 15524084f9bSBrian Somers running = 1; 15624084f9bSBrian Somers assignAliasAddr = 0; 15724084f9bSBrian Somers aliasAddr.s_addr = INADDR_NONE; 15824084f9bSBrian Somers aliasOverhead = 12; 15924084f9bSBrian Somers dynamicMode = 0; 16059a7c613SBrian Somers logDropped = 0; 16159a7c613SBrian Somers logFacility = LOG_DAEMON; 162fb994b07SBrian Somers /* 163fb994b07SBrian Somers * Mark packet buffer empty. 164fb994b07SBrian Somers */ 165fb994b07SBrian Somers packetSock = -1; 16659a7c613SBrian Somers packetDirection = DONT_KNOW; 16724084f9bSBrian Somers 16824084f9bSBrian Somers ParseArgs (argc, argv); 16924084f9bSBrian Somers /* 17059a7c613SBrian Somers * Open syslog channel. 17159a7c613SBrian Somers */ 1724c04fa4cSRuslan Ermilov openlog ("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0), 1734c04fa4cSRuslan Ermilov logFacility); 17459a7c613SBrian Somers /* 17524084f9bSBrian Somers * Check that valid aliasing address has been given. 17624084f9bSBrian Somers */ 1770fc81af1SPhilippe Charnier if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL) 1780fc81af1SPhilippe Charnier errx (1, "aliasing address not given"); 17924084f9bSBrian Somers 1800fc81af1SPhilippe Charnier if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL) 18167a886fbSBrian Somers errx (1, "both alias address and interface " 18267a886fbSBrian Somers "name are not allowed"); 18324084f9bSBrian Somers /* 18424084f9bSBrian Somers * Check that valid port number is known. 18524084f9bSBrian Somers */ 18624084f9bSBrian Somers if (inPort != 0 || outPort != 0) 1870fc81af1SPhilippe Charnier if (inPort == 0 || outPort == 0) 1880fc81af1SPhilippe Charnier errx (1, "both input and output ports are required"); 18924084f9bSBrian Somers 19024084f9bSBrian Somers if (inPort == 0 && outPort == 0 && inOutPort == 0) 19124084f9bSBrian Somers ParseOption ("port", DEFAULT_SERVICE, 0); 19224084f9bSBrian Somers 19324084f9bSBrian Somers /* 194f9b06d5cSBrian Somers * Check if ignored packets should be dropped. 195f9b06d5cSBrian Somers */ 196f9b06d5cSBrian Somers dropIgnoredIncoming = PacketAliasSetMode (0, 0); 197f9b06d5cSBrian Somers dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING; 198f9b06d5cSBrian Somers /* 19924084f9bSBrian Somers * Create divert sockets. Use only one socket if -p was specified 20024084f9bSBrian Somers * on command line. Otherwise, create separate sockets for 20124084f9bSBrian Somers * outgoing and incoming connnections. 20224084f9bSBrian Somers */ 20324084f9bSBrian Somers if (inOutPort) { 20424084f9bSBrian Somers 20524084f9bSBrian Somers divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 20624084f9bSBrian Somers if (divertInOut == -1) 20724084f9bSBrian Somers Quit ("Unable to create divert socket."); 20824084f9bSBrian Somers 20924084f9bSBrian Somers divertIn = -1; 21024084f9bSBrian Somers divertOut = -1; 21124084f9bSBrian Somers /* 21224084f9bSBrian Somers * Bind socket. 21324084f9bSBrian Somers */ 21424084f9bSBrian Somers 21524084f9bSBrian Somers addr.sin_family = AF_INET; 21624084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 21724084f9bSBrian Somers addr.sin_port = inOutPort; 21824084f9bSBrian Somers 21924084f9bSBrian Somers if (bind (divertInOut, 22024084f9bSBrian Somers (struct sockaddr*) &addr, 22124084f9bSBrian Somers sizeof addr) == -1) 22224084f9bSBrian Somers Quit ("Unable to bind divert socket."); 22324084f9bSBrian Somers } 22424084f9bSBrian Somers else { 22524084f9bSBrian Somers 22624084f9bSBrian Somers divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 22724084f9bSBrian Somers if (divertIn == -1) 22824084f9bSBrian Somers Quit ("Unable to create incoming divert socket."); 22924084f9bSBrian Somers 23024084f9bSBrian Somers divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 23124084f9bSBrian Somers if (divertOut == -1) 23224084f9bSBrian Somers Quit ("Unable to create outgoing divert socket."); 23324084f9bSBrian Somers 23424084f9bSBrian Somers divertInOut = -1; 23524084f9bSBrian Somers 23624084f9bSBrian Somers /* 23724084f9bSBrian Somers * Bind divert sockets. 23824084f9bSBrian Somers */ 23924084f9bSBrian Somers 24024084f9bSBrian Somers addr.sin_family = AF_INET; 24124084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 24224084f9bSBrian Somers addr.sin_port = inPort; 24324084f9bSBrian Somers 24424084f9bSBrian Somers if (bind (divertIn, 24524084f9bSBrian Somers (struct sockaddr*) &addr, 24624084f9bSBrian Somers sizeof addr) == -1) 24724084f9bSBrian Somers Quit ("Unable to bind incoming divert socket."); 24824084f9bSBrian Somers 24924084f9bSBrian Somers addr.sin_family = AF_INET; 25024084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 25124084f9bSBrian Somers addr.sin_port = outPort; 25224084f9bSBrian Somers 25324084f9bSBrian Somers if (bind (divertOut, 25424084f9bSBrian Somers (struct sockaddr*) &addr, 25524084f9bSBrian Somers sizeof addr) == -1) 25624084f9bSBrian Somers Quit ("Unable to bind outgoing divert socket."); 25724084f9bSBrian Somers } 25824084f9bSBrian Somers /* 259f2da55a2SRuslan Ermilov * Create routing socket if interface name specified and in dynamic mode. 26024084f9bSBrian Somers */ 261f2da55a2SRuslan Ermilov routeSock = -1; 262f2da55a2SRuslan Ermilov if (ifName) { 263f2da55a2SRuslan Ermilov if (dynamicMode) { 26424084f9bSBrian Somers 26524084f9bSBrian Somers routeSock = socket (PF_ROUTE, SOCK_RAW, 0); 26624084f9bSBrian Somers if (routeSock == -1) 26724084f9bSBrian Somers Quit ("Unable to create routing info socket."); 268f2da55a2SRuslan Ermilov 269f2da55a2SRuslan Ermilov assignAliasAddr = 1; 27024084f9bSBrian Somers } 27124084f9bSBrian Somers else 272f2da55a2SRuslan Ermilov SetAliasAddressFromIfName (ifName); 273f2da55a2SRuslan Ermilov } 27424084f9bSBrian Somers /* 27524084f9bSBrian Somers * Create socket for sending ICMP messages. 27624084f9bSBrian Somers */ 27724084f9bSBrian Somers icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); 27824084f9bSBrian Somers if (icmpSock == -1) 27924084f9bSBrian Somers Quit ("Unable to create ICMP socket."); 280f3d64024SBrian Somers 281f3d64024SBrian Somers /* 282f3d64024SBrian Somers * And disable reads for the socket, otherwise it slowly fills 283f3d64024SBrian Somers * up with received icmps which we do not use. 284f3d64024SBrian Somers */ 285f3d64024SBrian Somers shutdown(icmpSock, SHUT_RD); 286f3d64024SBrian Somers 28724084f9bSBrian Somers /* 28824084f9bSBrian Somers * Become a daemon unless verbose mode was requested. 28924084f9bSBrian Somers */ 29024084f9bSBrian Somers if (!verbose) 29124084f9bSBrian Somers DaemonMode (); 29224084f9bSBrian Somers /* 29324084f9bSBrian Somers * Catch signals to manage shutdown and 29424084f9bSBrian Somers * refresh of interface address. 29524084f9bSBrian Somers */ 296cd45c931SRuslan Ermilov siginterrupt(SIGTERM, 1); 297cd45c931SRuslan Ermilov siginterrupt(SIGHUP, 1); 29824084f9bSBrian Somers signal (SIGTERM, InitiateShutdown); 29924084f9bSBrian Somers signal (SIGHUP, RefreshAddr); 30024084f9bSBrian Somers /* 30124084f9bSBrian Somers * Set alias address if it has been given. 30224084f9bSBrian Somers */ 30324084f9bSBrian Somers if (aliasAddr.s_addr != INADDR_NONE) 304fb994b07SBrian Somers PacketAliasSetAddress (aliasAddr); 30524084f9bSBrian Somers /* 30624084f9bSBrian Somers * We need largest descriptor number for select. 30724084f9bSBrian Somers */ 30824084f9bSBrian Somers 30924084f9bSBrian Somers fdMax = -1; 31024084f9bSBrian Somers 31124084f9bSBrian Somers if (divertIn > fdMax) 31224084f9bSBrian Somers fdMax = divertIn; 31324084f9bSBrian Somers 31424084f9bSBrian Somers if (divertOut > fdMax) 31524084f9bSBrian Somers fdMax = divertOut; 31624084f9bSBrian Somers 31724084f9bSBrian Somers if (divertInOut > fdMax) 31824084f9bSBrian Somers fdMax = divertInOut; 31924084f9bSBrian Somers 32024084f9bSBrian Somers if (routeSock > fdMax) 32124084f9bSBrian Somers fdMax = routeSock; 32224084f9bSBrian Somers 32324084f9bSBrian Somers while (running) { 324fb994b07SBrian Somers 325fb994b07SBrian Somers if (divertInOut != -1 && !ifName && packetSock == -1) { 326fb994b07SBrian Somers /* 327fb994b07SBrian Somers * When using only one socket, just call 328fb994b07SBrian Somers * DoAliasing repeatedly to process packets. 329fb994b07SBrian Somers */ 33059a7c613SBrian Somers DoAliasing (divertInOut, DONT_KNOW); 331fb994b07SBrian Somers continue; 332fb994b07SBrian Somers } 33324084f9bSBrian Somers /* 33424084f9bSBrian Somers * Build read mask from socket descriptors to select. 33524084f9bSBrian Somers */ 33624084f9bSBrian Somers FD_ZERO (&readMask); 337fb994b07SBrian Somers FD_ZERO (&writeMask); 33824084f9bSBrian Somers 339fb994b07SBrian Somers /* 340fb994b07SBrian Somers * If there is unsent packet in buffer, use select 341fb994b07SBrian Somers * to check when socket comes writable again. 342fb994b07SBrian Somers */ 343fb994b07SBrian Somers if (packetSock != -1) { 344fb994b07SBrian Somers 345fb994b07SBrian Somers FD_SET (packetSock, &writeMask); 346fb994b07SBrian Somers } 347fb994b07SBrian Somers else { 348fb994b07SBrian Somers /* 349fb994b07SBrian Somers * No unsent packet exists - safe to check if 350fb994b07SBrian Somers * new ones are available. 351fb994b07SBrian Somers */ 35224084f9bSBrian Somers if (divertIn != -1) 35324084f9bSBrian Somers FD_SET (divertIn, &readMask); 35424084f9bSBrian Somers 35524084f9bSBrian Somers if (divertOut != -1) 35624084f9bSBrian Somers FD_SET (divertOut, &readMask); 35724084f9bSBrian Somers 35824084f9bSBrian Somers if (divertInOut != -1) 35924084f9bSBrian Somers FD_SET (divertInOut, &readMask); 360fb994b07SBrian Somers } 361fb994b07SBrian Somers /* 362fb994b07SBrian Somers * Routing info is processed always. 363fb994b07SBrian Somers */ 36424084f9bSBrian Somers if (routeSock != -1) 36524084f9bSBrian Somers FD_SET (routeSock, &readMask); 36624084f9bSBrian Somers 36724084f9bSBrian Somers if (select (fdMax + 1, 36824084f9bSBrian Somers &readMask, 369fb994b07SBrian Somers &writeMask, 37024084f9bSBrian Somers NULL, 37124084f9bSBrian Somers NULL) == -1) { 37224084f9bSBrian Somers 37324084f9bSBrian Somers if (errno == EINTR) 37424084f9bSBrian Somers continue; 37524084f9bSBrian Somers 37624084f9bSBrian Somers Quit ("Select failed."); 37724084f9bSBrian Somers } 37824084f9bSBrian Somers 379fb994b07SBrian Somers if (packetSock != -1) 380fb994b07SBrian Somers if (FD_ISSET (packetSock, &writeMask)) 381fb994b07SBrian Somers FlushPacketBuffer (packetSock); 382fb994b07SBrian Somers 38324084f9bSBrian Somers if (divertIn != -1) 38424084f9bSBrian Somers if (FD_ISSET (divertIn, &readMask)) 38559a7c613SBrian Somers DoAliasing (divertIn, INPUT); 38624084f9bSBrian Somers 38724084f9bSBrian Somers if (divertOut != -1) 38824084f9bSBrian Somers if (FD_ISSET (divertOut, &readMask)) 38959a7c613SBrian Somers DoAliasing (divertOut, OUTPUT); 39024084f9bSBrian Somers 39124084f9bSBrian Somers if (divertInOut != -1) 39224084f9bSBrian Somers if (FD_ISSET (divertInOut, &readMask)) 39359a7c613SBrian Somers DoAliasing (divertInOut, DONT_KNOW); 39424084f9bSBrian Somers 39524084f9bSBrian Somers if (routeSock != -1) 39624084f9bSBrian Somers if (FD_ISSET (routeSock, &readMask)) 39724084f9bSBrian Somers HandleRoutingInfo (routeSock); 39824084f9bSBrian Somers } 39924084f9bSBrian Somers 40024084f9bSBrian Somers if (background) 40124084f9bSBrian Somers unlink (PIDFILE); 40224084f9bSBrian Somers 40324084f9bSBrian Somers return 0; 40424084f9bSBrian Somers } 40524084f9bSBrian Somers 40624084f9bSBrian Somers static void DaemonMode () 40724084f9bSBrian Somers { 40824084f9bSBrian Somers FILE* pidFile; 40924084f9bSBrian Somers 41024084f9bSBrian Somers daemon (0, 0); 41124084f9bSBrian Somers background = 1; 41224084f9bSBrian Somers 41324084f9bSBrian Somers pidFile = fopen (PIDFILE, "w"); 41424084f9bSBrian Somers if (pidFile) { 41524084f9bSBrian Somers 41624084f9bSBrian Somers fprintf (pidFile, "%d\n", getpid ()); 41724084f9bSBrian Somers fclose (pidFile); 41824084f9bSBrian Somers } 41924084f9bSBrian Somers } 42024084f9bSBrian Somers 42124084f9bSBrian Somers static void ParseArgs (int argc, char** argv) 42224084f9bSBrian Somers { 42324084f9bSBrian Somers int arg; 42424084f9bSBrian Somers char* parm; 42524084f9bSBrian Somers char* opt; 42624084f9bSBrian Somers char parmBuf[256]; 42724084f9bSBrian Somers 42824084f9bSBrian Somers for (arg = 1; arg < argc; arg++) { 42924084f9bSBrian Somers 43024084f9bSBrian Somers opt = argv[arg]; 43124084f9bSBrian Somers if (*opt != '-') { 43224084f9bSBrian Somers 4330fc81af1SPhilippe Charnier warnx ("invalid option %s", opt); 43424084f9bSBrian Somers Usage (); 43524084f9bSBrian Somers } 43624084f9bSBrian Somers 43724084f9bSBrian Somers parm = NULL; 43824084f9bSBrian Somers parmBuf[0] = '\0'; 43924084f9bSBrian Somers 44024084f9bSBrian Somers while (arg < argc - 1) { 44124084f9bSBrian Somers 44224084f9bSBrian Somers if (argv[arg + 1][0] == '-') 44324084f9bSBrian Somers break; 44424084f9bSBrian Somers 44524084f9bSBrian Somers if (parm) 44624084f9bSBrian Somers strcat (parmBuf, " "); 44724084f9bSBrian Somers 44824084f9bSBrian Somers ++arg; 44924084f9bSBrian Somers parm = parmBuf; 45024084f9bSBrian Somers strcat (parmBuf, argv[arg]); 45124084f9bSBrian Somers } 45224084f9bSBrian Somers 45324084f9bSBrian Somers ParseOption (opt + 1, parm, 1); 45424084f9bSBrian Somers } 45524084f9bSBrian Somers } 45624084f9bSBrian Somers 45759a7c613SBrian Somers static void DoAliasing (int fd, int direction) 45824084f9bSBrian Somers { 45924084f9bSBrian Somers int bytes; 46024084f9bSBrian Somers int origBytes; 461f9b06d5cSBrian Somers int status; 46224084f9bSBrian Somers int addrSize; 46324084f9bSBrian Somers struct ip* ip; 46424084f9bSBrian Somers 46524084f9bSBrian Somers if (assignAliasAddr) { 46624084f9bSBrian Somers 46724084f9bSBrian Somers SetAliasAddressFromIfName (ifName); 46824084f9bSBrian Somers assignAliasAddr = 0; 46924084f9bSBrian Somers } 47024084f9bSBrian Somers /* 47124084f9bSBrian Somers * Get packet from socket. 47224084f9bSBrian Somers */ 473fb994b07SBrian Somers addrSize = sizeof packetAddr; 47424084f9bSBrian Somers origBytes = recvfrom (fd, 475fb994b07SBrian Somers packetBuf, 476fb994b07SBrian Somers sizeof packetBuf, 47724084f9bSBrian Somers 0, 478fb994b07SBrian Somers (struct sockaddr*) &packetAddr, 47924084f9bSBrian Somers &addrSize); 48024084f9bSBrian Somers 48124084f9bSBrian Somers if (origBytes == -1) { 48224084f9bSBrian Somers 48324084f9bSBrian Somers if (errno != EINTR) 4840fc81af1SPhilippe Charnier Warn ("read from divert socket failed"); 48524084f9bSBrian Somers 48624084f9bSBrian Somers return; 48724084f9bSBrian Somers } 48824084f9bSBrian Somers /* 48924084f9bSBrian Somers * This is a IP packet. 49024084f9bSBrian Somers */ 491fb994b07SBrian Somers ip = (struct ip*) packetBuf; 492ebe70c8fSWarner Losh if (direction == DONT_KNOW) { 49359a7c613SBrian Somers if (packetAddr.sin_addr.s_addr == INADDR_ANY) 49459a7c613SBrian Somers direction = OUTPUT; 49559a7c613SBrian Somers else 49659a7c613SBrian Somers direction = INPUT; 497ebe70c8fSWarner Losh } 49824084f9bSBrian Somers 49924084f9bSBrian Somers if (verbose) { 50024084f9bSBrian Somers /* 50124084f9bSBrian Somers * Print packet direction and protocol type. 50224084f9bSBrian Somers */ 50359a7c613SBrian Somers printf (direction == OUTPUT ? "Out " : "In "); 50424084f9bSBrian Somers 50524084f9bSBrian Somers switch (ip->ip_p) { 50624084f9bSBrian Somers case IPPROTO_TCP: 50724084f9bSBrian Somers printf ("[TCP] "); 50824084f9bSBrian Somers break; 50924084f9bSBrian Somers 51024084f9bSBrian Somers case IPPROTO_UDP: 51124084f9bSBrian Somers printf ("[UDP] "); 51224084f9bSBrian Somers break; 51324084f9bSBrian Somers 51424084f9bSBrian Somers case IPPROTO_ICMP: 51524084f9bSBrian Somers printf ("[ICMP] "); 51624084f9bSBrian Somers break; 51724084f9bSBrian Somers 51824084f9bSBrian Somers default: 51959a7c613SBrian Somers printf ("[%d] ", ip->ip_p); 52024084f9bSBrian Somers break; 52124084f9bSBrian Somers } 52224084f9bSBrian Somers /* 52324084f9bSBrian Somers * Print addresses. 52424084f9bSBrian Somers */ 52524084f9bSBrian Somers PrintPacket (ip); 52624084f9bSBrian Somers } 52724084f9bSBrian Somers 52859a7c613SBrian Somers if (direction == OUTPUT) { 52924084f9bSBrian Somers /* 53024084f9bSBrian Somers * Outgoing packets. Do aliasing. 53124084f9bSBrian Somers */ 532fb994b07SBrian Somers PacketAliasOut (packetBuf, IP_MAXPACKET); 53324084f9bSBrian Somers } 53424084f9bSBrian Somers else { 53559a7c613SBrian Somers 53624084f9bSBrian Somers /* 53724084f9bSBrian Somers * Do aliasing. 53824084f9bSBrian Somers */ 539f9b06d5cSBrian Somers status = PacketAliasIn (packetBuf, IP_MAXPACKET); 540f9b06d5cSBrian Somers if (status == PKT_ALIAS_IGNORED && 541f9b06d5cSBrian Somers dropIgnoredIncoming) { 542f9b06d5cSBrian Somers 54359a7c613SBrian Somers if (verbose) 544f9b06d5cSBrian Somers printf (" dropped.\n"); 54559a7c613SBrian Somers 54659a7c613SBrian Somers if (logDropped) 54759a7c613SBrian Somers SyslogPacket (ip, LOG_WARNING, "denied"); 54859a7c613SBrian Somers 549f9b06d5cSBrian Somers return; 550f9b06d5cSBrian Somers } 55124084f9bSBrian Somers } 55224084f9bSBrian Somers /* 55324084f9bSBrian Somers * Length might have changed during aliasing. 55424084f9bSBrian Somers */ 55524084f9bSBrian Somers bytes = ntohs (ip->ip_len); 55624084f9bSBrian Somers /* 55724084f9bSBrian Somers * Update alias overhead size for outgoing packets. 55824084f9bSBrian Somers */ 55959a7c613SBrian Somers if (direction == OUTPUT && 56024084f9bSBrian Somers bytes - origBytes > aliasOverhead) 56124084f9bSBrian Somers aliasOverhead = bytes - origBytes; 56224084f9bSBrian Somers 56324084f9bSBrian Somers if (verbose) { 56424084f9bSBrian Somers 56524084f9bSBrian Somers /* 56624084f9bSBrian Somers * Print addresses after aliasing. 56724084f9bSBrian Somers */ 56824084f9bSBrian Somers printf (" aliased to\n"); 56924084f9bSBrian Somers printf (" "); 57024084f9bSBrian Somers PrintPacket (ip); 57124084f9bSBrian Somers printf ("\n"); 57224084f9bSBrian Somers } 573fb994b07SBrian Somers 574fb994b07SBrian Somers packetLen = bytes; 575fb994b07SBrian Somers packetSock = fd; 57659a7c613SBrian Somers packetDirection = direction; 57759a7c613SBrian Somers 578fb994b07SBrian Somers FlushPacketBuffer (fd); 579fb994b07SBrian Somers } 580fb994b07SBrian Somers 581fb994b07SBrian Somers static void FlushPacketBuffer (int fd) 582fb994b07SBrian Somers { 583fb994b07SBrian Somers int wrote; 584fb994b07SBrian Somers char msgBuf[80]; 58524084f9bSBrian Somers /* 58624084f9bSBrian Somers * Put packet back for processing. 58724084f9bSBrian Somers */ 58824084f9bSBrian Somers wrote = sendto (fd, 589fb994b07SBrian Somers packetBuf, 590fb994b07SBrian Somers packetLen, 59124084f9bSBrian Somers 0, 592fb994b07SBrian Somers (struct sockaddr*) &packetAddr, 593fb994b07SBrian Somers sizeof packetAddr); 59424084f9bSBrian Somers 595fb994b07SBrian Somers if (wrote != packetLen) { 596fb994b07SBrian Somers /* 597fb994b07SBrian Somers * If buffer space is not available, 598fb994b07SBrian Somers * just return. Main loop will take care of 599fb994b07SBrian Somers * retrying send when space becomes available. 600fb994b07SBrian Somers */ 601fb994b07SBrian Somers if (errno == ENOBUFS) 602fb994b07SBrian Somers return; 60324084f9bSBrian Somers 60424084f9bSBrian Somers if (errno == EMSGSIZE) { 60524084f9bSBrian Somers 60659a7c613SBrian Somers if (packetDirection == OUTPUT && 60724084f9bSBrian Somers ifMTU != -1) 60824084f9bSBrian Somers SendNeedFragIcmp (icmpSock, 609fb994b07SBrian Somers (struct ip*) packetBuf, 61024084f9bSBrian Somers ifMTU - aliasOverhead); 61124084f9bSBrian Somers } 61224084f9bSBrian Somers else { 61324084f9bSBrian Somers 6140fc81af1SPhilippe Charnier sprintf (msgBuf, "failed to write packet back"); 61524084f9bSBrian Somers Warn (msgBuf); 61624084f9bSBrian Somers } 61724084f9bSBrian Somers } 618fb994b07SBrian Somers 619fb994b07SBrian Somers packetSock = -1; 62024084f9bSBrian Somers } 62124084f9bSBrian Somers 62224084f9bSBrian Somers static void HandleRoutingInfo (int fd) 62324084f9bSBrian Somers { 62424084f9bSBrian Somers int bytes; 62524084f9bSBrian Somers struct if_msghdr ifMsg; 62624084f9bSBrian Somers /* 62724084f9bSBrian Somers * Get packet from socket. 62824084f9bSBrian Somers */ 62924084f9bSBrian Somers bytes = read (fd, &ifMsg, sizeof ifMsg); 63024084f9bSBrian Somers if (bytes == -1) { 63124084f9bSBrian Somers 6320fc81af1SPhilippe Charnier Warn ("read from routing socket failed"); 63324084f9bSBrian Somers return; 63424084f9bSBrian Somers } 63524084f9bSBrian Somers 63624084f9bSBrian Somers if (ifMsg.ifm_version != RTM_VERSION) { 63724084f9bSBrian Somers 6380fc81af1SPhilippe Charnier Warn ("unexpected packet read from routing socket"); 63924084f9bSBrian Somers return; 64024084f9bSBrian Somers } 64124084f9bSBrian Somers 64224084f9bSBrian Somers if (verbose) 6436f3dbe5eSRuslan Ermilov printf ("Routing message %#x received.\n", ifMsg.ifm_type); 64424084f9bSBrian Somers 6456f3dbe5eSRuslan Ermilov if ((ifMsg.ifm_type == RTM_NEWADDR || ifMsg.ifm_type == RTM_IFINFO) && 6466f3dbe5eSRuslan Ermilov ifMsg.ifm_index == ifIndex) { 6476f3dbe5eSRuslan Ermilov if (verbose) 6486f3dbe5eSRuslan Ermilov printf("Interface address/MTU has probably changed.\n"); 64924084f9bSBrian Somers assignAliasAddr = 1; 65024084f9bSBrian Somers } 6516f3dbe5eSRuslan Ermilov } 65224084f9bSBrian Somers 65324084f9bSBrian Somers static void PrintPacket (struct ip* ip) 65424084f9bSBrian Somers { 65559a7c613SBrian Somers printf ("%s", FormatPacket (ip)); 65659a7c613SBrian Somers } 65759a7c613SBrian Somers 658902cb50aSBrian Somers static void SyslogPacket (struct ip* ip, int priority, const char *label) 65959a7c613SBrian Somers { 66059a7c613SBrian Somers syslog (priority, "%s %s", label, FormatPacket (ip)); 66159a7c613SBrian Somers } 66259a7c613SBrian Somers 66359a7c613SBrian Somers static char* FormatPacket (struct ip* ip) 66459a7c613SBrian Somers { 66559a7c613SBrian Somers static char buf[256]; 66624084f9bSBrian Somers struct tcphdr* tcphdr; 66759a7c613SBrian Somers struct udphdr* udphdr; 66859a7c613SBrian Somers struct icmp* icmphdr; 66959a7c613SBrian Somers char src[20]; 67059a7c613SBrian Somers char dst[20]; 67124084f9bSBrian Somers 67259a7c613SBrian Somers strcpy (src, inet_ntoa (ip->ip_src)); 67359a7c613SBrian Somers strcpy (dst, inet_ntoa (ip->ip_dst)); 67459a7c613SBrian Somers 67559a7c613SBrian Somers switch (ip->ip_p) { 67659a7c613SBrian Somers case IPPROTO_TCP: 67724084f9bSBrian Somers tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2)); 67859a7c613SBrian Somers sprintf (buf, "[TCP] %s:%d -> %s:%d", 67959a7c613SBrian Somers src, 68059a7c613SBrian Somers ntohs (tcphdr->th_sport), 68159a7c613SBrian Somers dst, 68259a7c613SBrian Somers ntohs (tcphdr->th_dport)); 68359a7c613SBrian Somers break; 68424084f9bSBrian Somers 68559a7c613SBrian Somers case IPPROTO_UDP: 68659a7c613SBrian Somers udphdr = (struct udphdr*) ((char*) ip + (ip->ip_hl << 2)); 68759a7c613SBrian Somers sprintf (buf, "[UDP] %s:%d -> %s:%d", 68859a7c613SBrian Somers src, 68959a7c613SBrian Somers ntohs (udphdr->uh_sport), 69059a7c613SBrian Somers dst, 69159a7c613SBrian Somers ntohs (udphdr->uh_dport)); 69259a7c613SBrian Somers break; 69324084f9bSBrian Somers 69459a7c613SBrian Somers case IPPROTO_ICMP: 69559a7c613SBrian Somers icmphdr = (struct icmp*) ((char*) ip + (ip->ip_hl << 2)); 696b71e869dSBrian Somers sprintf (buf, "[ICMP] %s -> %s %u(%u)", 69759a7c613SBrian Somers src, 69859a7c613SBrian Somers dst, 699b71e869dSBrian Somers icmphdr->icmp_type, 700b71e869dSBrian Somers icmphdr->icmp_code); 70159a7c613SBrian Somers break; 70259a7c613SBrian Somers 70359a7c613SBrian Somers default: 70459a7c613SBrian Somers sprintf (buf, "[%d] %s -> %s ", ip->ip_p, src, dst); 70559a7c613SBrian Somers break; 70659a7c613SBrian Somers } 70759a7c613SBrian Somers 70859a7c613SBrian Somers return buf; 70924084f9bSBrian Somers } 71024084f9bSBrian Somers 7114c04fa4cSRuslan Ermilov static void 7124c04fa4cSRuslan Ermilov SetAliasAddressFromIfName(const char *ifn) 71324084f9bSBrian Somers { 7144c04fa4cSRuslan Ermilov size_t needed; 7154c04fa4cSRuslan Ermilov int mib[6]; 7164c04fa4cSRuslan Ermilov char *buf, *lim, *next; 7174c04fa4cSRuslan Ermilov struct if_msghdr *ifm; 7184c04fa4cSRuslan Ermilov struct ifa_msghdr *ifam; 7194c04fa4cSRuslan Ermilov struct sockaddr_dl *sdl; 7204c04fa4cSRuslan Ermilov struct sockaddr_in *sin; 72124084f9bSBrian Somers 7224c04fa4cSRuslan Ermilov mib[0] = CTL_NET; 7234c04fa4cSRuslan Ermilov mib[1] = PF_ROUTE; 7244c04fa4cSRuslan Ermilov mib[2] = 0; 7254c04fa4cSRuslan Ermilov mib[3] = AF_INET; /* Only IP addresses please */ 7264c04fa4cSRuslan Ermilov mib[4] = NET_RT_IFLIST; 7274c04fa4cSRuslan Ermilov mib[5] = 0; /* ifIndex??? */ 72824084f9bSBrian Somers /* 72924084f9bSBrian Somers * Get interface data. 73024084f9bSBrian Somers */ 7314c04fa4cSRuslan Ermilov if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) 7324c04fa4cSRuslan Ermilov err(1, "iflist-sysctl-estimate"); 7334c04fa4cSRuslan Ermilov if ((buf = malloc(needed)) == NULL) 7344c04fa4cSRuslan Ermilov errx(1, "malloc failed"); 7354c04fa4cSRuslan Ermilov if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) 7364c04fa4cSRuslan Ermilov err(1, "iflist-sysctl-get"); 7374c04fa4cSRuslan Ermilov lim = buf + needed; 73824084f9bSBrian Somers /* 73924084f9bSBrian Somers * Loop through interfaces until one with 74024084f9bSBrian Somers * given name is found. This is done to 74124084f9bSBrian Somers * find correct interface index for routing 74224084f9bSBrian Somers * message processing. 74324084f9bSBrian Somers */ 7444c04fa4cSRuslan Ermilov ifIndex = 0; 7454c04fa4cSRuslan Ermilov next = buf; 7464c04fa4cSRuslan Ermilov while (next < lim) { 7474c04fa4cSRuslan Ermilov ifm = (struct if_msghdr *)next; 7484c04fa4cSRuslan Ermilov next += ifm->ifm_msglen; 7494c04fa4cSRuslan Ermilov if (ifm->ifm_version != RTM_VERSION) { 7504c04fa4cSRuslan Ermilov if (verbose) 7514c04fa4cSRuslan Ermilov warnx("routing message version %d " 7524c04fa4cSRuslan Ermilov "not understood", ifm->ifm_version); 7534c04fa4cSRuslan Ermilov continue; 7544c04fa4cSRuslan Ermilov } 7554c04fa4cSRuslan Ermilov if (ifm->ifm_type == RTM_IFINFO) { 7564c04fa4cSRuslan Ermilov sdl = (struct sockaddr_dl *)(ifm + 1); 7574c04fa4cSRuslan Ermilov if (strlen(ifn) == sdl->sdl_nlen && 7584c04fa4cSRuslan Ermilov strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) { 7594c04fa4cSRuslan Ermilov ifIndex = ifm->ifm_index; 7604c04fa4cSRuslan Ermilov ifMTU = ifm->ifm_data.ifi_mtu; 76124084f9bSBrian Somers break; 76224084f9bSBrian Somers } 76324084f9bSBrian Somers } 76424084f9bSBrian Somers } 7654c04fa4cSRuslan Ermilov if (!ifIndex) 7664c04fa4cSRuslan Ermilov errx(1, "unknown interface name %s", ifn); 76724084f9bSBrian Somers /* 76824084f9bSBrian Somers * Get interface address. 76924084f9bSBrian Somers */ 7704c04fa4cSRuslan Ermilov sin = NULL; 7714c04fa4cSRuslan Ermilov while (next < lim) { 7724c04fa4cSRuslan Ermilov ifam = (struct ifa_msghdr *)next; 7734c04fa4cSRuslan Ermilov next += ifam->ifam_msglen; 7744c04fa4cSRuslan Ermilov if (ifam->ifam_version != RTM_VERSION) { 7754c04fa4cSRuslan Ermilov if (verbose) 7764c04fa4cSRuslan Ermilov warnx("routing message version %d " 7774c04fa4cSRuslan Ermilov "not understood", ifam->ifam_version); 7784c04fa4cSRuslan Ermilov continue; 7794c04fa4cSRuslan Ermilov } 7804c04fa4cSRuslan Ermilov if (ifam->ifam_type != RTM_NEWADDR) 7814c04fa4cSRuslan Ermilov break; 7824c04fa4cSRuslan Ermilov if (ifam->ifam_addrs & RTA_IFA) { 7834c04fa4cSRuslan Ermilov int i; 7844c04fa4cSRuslan Ermilov char *cp = (char *)(ifam + 1); 78524084f9bSBrian Somers 7864c04fa4cSRuslan Ermilov #define ROUNDUP(a) \ 7874c04fa4cSRuslan Ermilov ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 7884c04fa4cSRuslan Ermilov #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 7894c04fa4cSRuslan Ermilov 7904c04fa4cSRuslan Ermilov for (i = 1; i < RTA_IFA; i <<= 1) 7914c04fa4cSRuslan Ermilov if (ifam->ifam_addrs & i) 7924c04fa4cSRuslan Ermilov ADVANCE(cp, (struct sockaddr *)cp); 7934c04fa4cSRuslan Ermilov if (((struct sockaddr *)cp)->sa_family == AF_INET) { 7944c04fa4cSRuslan Ermilov sin = (struct sockaddr_in *)cp; 7954c04fa4cSRuslan Ermilov break; 7964c04fa4cSRuslan Ermilov } 7974c04fa4cSRuslan Ermilov } 7984c04fa4cSRuslan Ermilov } 7994c04fa4cSRuslan Ermilov if (sin == NULL) 8004c04fa4cSRuslan Ermilov errx(1, "%s: cannot get interface address", ifn); 8014c04fa4cSRuslan Ermilov 8024c04fa4cSRuslan Ermilov PacketAliasSetAddress(sin->sin_addr); 80324084f9bSBrian Somers syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes", 8044c04fa4cSRuslan Ermilov inet_ntoa(sin->sin_addr), ifMTU); 80524084f9bSBrian Somers 8064c04fa4cSRuslan Ermilov free(buf); 80724084f9bSBrian Somers } 80824084f9bSBrian Somers 809902cb50aSBrian Somers void Quit (const char* msg) 81024084f9bSBrian Somers { 81124084f9bSBrian Somers Warn (msg); 81224084f9bSBrian Somers exit (1); 81324084f9bSBrian Somers } 81424084f9bSBrian Somers 815902cb50aSBrian Somers void Warn (const char* msg) 81624084f9bSBrian Somers { 81724084f9bSBrian Somers if (background) 81824084f9bSBrian Somers syslog (LOG_ALERT, "%s (%m)", msg); 81924084f9bSBrian Somers else 8200fc81af1SPhilippe Charnier warn (msg); 82124084f9bSBrian Somers } 82224084f9bSBrian Somers 823902cb50aSBrian Somers static void RefreshAddr (int sig) 82424084f9bSBrian Somers { 82524084f9bSBrian Somers if (ifName) 82624084f9bSBrian Somers assignAliasAddr = 1; 82724084f9bSBrian Somers } 82824084f9bSBrian Somers 829902cb50aSBrian Somers static void InitiateShutdown (int sig) 83024084f9bSBrian Somers { 83124084f9bSBrian Somers /* 83224084f9bSBrian Somers * Start timer to allow kernel gracefully 83324084f9bSBrian Somers * shutdown existing connections when system 83424084f9bSBrian Somers * is shut down. 83524084f9bSBrian Somers */ 836cd45c931SRuslan Ermilov siginterrupt(SIGALRM, 1); 83724084f9bSBrian Somers signal (SIGALRM, Shutdown); 83824084f9bSBrian Somers alarm (10); 83924084f9bSBrian Somers } 84024084f9bSBrian Somers 841902cb50aSBrian Somers static void Shutdown (int sig) 84224084f9bSBrian Somers { 84324084f9bSBrian Somers running = 0; 84424084f9bSBrian Somers } 84524084f9bSBrian Somers 84624084f9bSBrian Somers /* 84724084f9bSBrian Somers * Different options recognized by this program. 84824084f9bSBrian Somers */ 84924084f9bSBrian Somers 85024084f9bSBrian Somers enum Option { 85124084f9bSBrian Somers 85224084f9bSBrian Somers PacketAliasOption, 85324084f9bSBrian Somers Verbose, 85424084f9bSBrian Somers InPort, 85524084f9bSBrian Somers OutPort, 85624084f9bSBrian Somers Port, 85724084f9bSBrian Somers AliasAddress, 85824084f9bSBrian Somers InterfaceName, 85924084f9bSBrian Somers RedirectPort, 86024084f9bSBrian Somers RedirectAddress, 86124084f9bSBrian Somers ConfigFile, 86259a7c613SBrian Somers DynamicMode, 86359a7c613SBrian Somers PptpAlias, 86459a7c613SBrian Somers ProxyRule, 86559a7c613SBrian Somers LogDenied, 86659a7c613SBrian Somers LogFacility 86724084f9bSBrian Somers }; 86824084f9bSBrian Somers 86924084f9bSBrian Somers enum Param { 87024084f9bSBrian Somers 87124084f9bSBrian Somers YesNo, 87224084f9bSBrian Somers Numeric, 87324084f9bSBrian Somers String, 87424084f9bSBrian Somers None, 87524084f9bSBrian Somers Address, 87624084f9bSBrian Somers Service 87724084f9bSBrian Somers }; 87824084f9bSBrian Somers 87924084f9bSBrian Somers /* 88024084f9bSBrian Somers * Option information structure (used by ParseOption). 88124084f9bSBrian Somers */ 88224084f9bSBrian Somers 88324084f9bSBrian Somers struct OptionInfo { 88424084f9bSBrian Somers 88524084f9bSBrian Somers enum Option type; 88624084f9bSBrian Somers int packetAliasOpt; 88724084f9bSBrian Somers enum Param parm; 888902cb50aSBrian Somers const char* parmDescription; 889902cb50aSBrian Somers const char* description; 890902cb50aSBrian Somers const char* name; 891902cb50aSBrian Somers const char* shortName; 89224084f9bSBrian Somers }; 89324084f9bSBrian Somers 89424084f9bSBrian Somers /* 89524084f9bSBrian Somers * Table of known options. 89624084f9bSBrian Somers */ 89724084f9bSBrian Somers 89824084f9bSBrian Somers static struct OptionInfo optionTable[] = { 89924084f9bSBrian Somers 90024084f9bSBrian Somers { PacketAliasOption, 90124084f9bSBrian Somers PKT_ALIAS_UNREGISTERED_ONLY, 90224084f9bSBrian Somers YesNo, 90324084f9bSBrian Somers "[yes|no]", 90424084f9bSBrian Somers "alias only unregistered addresses", 90524084f9bSBrian Somers "unregistered_only", 90624084f9bSBrian Somers "u" }, 90724084f9bSBrian Somers 90824084f9bSBrian Somers { PacketAliasOption, 90924084f9bSBrian Somers PKT_ALIAS_LOG, 91024084f9bSBrian Somers YesNo, 91124084f9bSBrian Somers "[yes|no]", 91224084f9bSBrian Somers "enable logging", 91324084f9bSBrian Somers "log", 91424084f9bSBrian Somers "l" }, 91524084f9bSBrian Somers 91624084f9bSBrian Somers { PacketAliasOption, 91759a7c613SBrian Somers PKT_ALIAS_PROXY_ONLY, 91859a7c613SBrian Somers YesNo, 91959a7c613SBrian Somers "[yes|no]", 92059a7c613SBrian Somers "proxy only", 92159a7c613SBrian Somers "proxy_only", 92259a7c613SBrian Somers NULL }, 92359a7c613SBrian Somers 92459a7c613SBrian Somers { PacketAliasOption, 92559a7c613SBrian Somers PKT_ALIAS_REVERSE, 92659a7c613SBrian Somers YesNo, 92759a7c613SBrian Somers "[yes|no]", 92859a7c613SBrian Somers "operate in reverse mode", 92959a7c613SBrian Somers "reverse", 93059a7c613SBrian Somers NULL }, 93159a7c613SBrian Somers 93259a7c613SBrian Somers { PacketAliasOption, 93324084f9bSBrian Somers PKT_ALIAS_DENY_INCOMING, 93424084f9bSBrian Somers YesNo, 93524084f9bSBrian Somers "[yes|no]", 93624084f9bSBrian Somers "allow incoming connections", 93724084f9bSBrian Somers "deny_incoming", 93824084f9bSBrian Somers "d" }, 93924084f9bSBrian Somers 94024084f9bSBrian Somers { PacketAliasOption, 94124084f9bSBrian Somers PKT_ALIAS_USE_SOCKETS, 94224084f9bSBrian Somers YesNo, 94324084f9bSBrian Somers "[yes|no]", 94424084f9bSBrian Somers "use sockets to inhibit port conflict", 94524084f9bSBrian Somers "use_sockets", 94624084f9bSBrian Somers "s" }, 94724084f9bSBrian Somers 94824084f9bSBrian Somers { PacketAliasOption, 94924084f9bSBrian Somers PKT_ALIAS_SAME_PORTS, 95024084f9bSBrian Somers YesNo, 95124084f9bSBrian Somers "[yes|no]", 95224084f9bSBrian Somers "try to keep original port numbers for connections", 95324084f9bSBrian Somers "same_ports", 95424084f9bSBrian Somers "m" }, 95524084f9bSBrian Somers 95624084f9bSBrian Somers { Verbose, 95724084f9bSBrian Somers 0, 95824084f9bSBrian Somers YesNo, 95924084f9bSBrian Somers "[yes|no]", 96024084f9bSBrian Somers "verbose mode, dump packet information", 96124084f9bSBrian Somers "verbose", 96224084f9bSBrian Somers "v" }, 96324084f9bSBrian Somers 96424084f9bSBrian Somers { DynamicMode, 96524084f9bSBrian Somers 0, 96624084f9bSBrian Somers YesNo, 96724084f9bSBrian Somers "[yes|no]", 96824084f9bSBrian Somers "dynamic mode, automatically detect interface address changes", 96924084f9bSBrian Somers "dynamic", 97024084f9bSBrian Somers NULL }, 97124084f9bSBrian Somers 97224084f9bSBrian Somers { InPort, 97324084f9bSBrian Somers 0, 97424084f9bSBrian Somers Service, 97524084f9bSBrian Somers "number|service_name", 97624084f9bSBrian Somers "set port for incoming packets", 97724084f9bSBrian Somers "in_port", 97824084f9bSBrian Somers "i" }, 97924084f9bSBrian Somers 98024084f9bSBrian Somers { OutPort, 98124084f9bSBrian Somers 0, 98224084f9bSBrian Somers Service, 98324084f9bSBrian Somers "number|service_name", 98424084f9bSBrian Somers "set port for outgoing packets", 98524084f9bSBrian Somers "out_port", 98624084f9bSBrian Somers "o" }, 98724084f9bSBrian Somers 98824084f9bSBrian Somers { Port, 98924084f9bSBrian Somers 0, 99024084f9bSBrian Somers Service, 99124084f9bSBrian Somers "number|service_name", 99224084f9bSBrian Somers "set port (defaults to natd/divert)", 99324084f9bSBrian Somers "port", 99424084f9bSBrian Somers "p" }, 99524084f9bSBrian Somers 99624084f9bSBrian Somers { AliasAddress, 99724084f9bSBrian Somers 0, 99824084f9bSBrian Somers Address, 99924084f9bSBrian Somers "x.x.x.x", 100024084f9bSBrian Somers "address to use for aliasing", 100124084f9bSBrian Somers "alias_address", 100224084f9bSBrian Somers "a" }, 100324084f9bSBrian Somers 100424084f9bSBrian Somers { InterfaceName, 100524084f9bSBrian Somers 0, 100624084f9bSBrian Somers String, 100724084f9bSBrian Somers "network_if_name", 100824084f9bSBrian Somers "take aliasing address from interface", 100924084f9bSBrian Somers "interface", 101024084f9bSBrian Somers "n" }, 101124084f9bSBrian Somers 101259a7c613SBrian Somers { ProxyRule, 101324084f9bSBrian Somers 0, 101424084f9bSBrian Somers String, 101559a7c613SBrian Somers "[type encode_ip_hdr|encode_tcp_stream] port xxxx server " 101659a7c613SBrian Somers "a.b.c.d:yyyy", 101759a7c613SBrian Somers "add transparent proxying / destination NAT", 101859a7c613SBrian Somers "proxy_rule", 101924084f9bSBrian Somers NULL }, 102024084f9bSBrian Somers 102124084f9bSBrian Somers { RedirectPort, 102224084f9bSBrian Somers 0, 102324084f9bSBrian Somers String, 1024bd690510SRuslan Ermilov "tcp|udp local_addr:local_port_range[,...] [public_addr:]public_port_range" 10255d8ee958SBrian Somers " [remote_addr[:remote_port_range]]", 10265d8ee958SBrian Somers "redirect a port (or ports) for incoming traffic", 102724084f9bSBrian Somers "redirect_port", 102824084f9bSBrian Somers NULL }, 102924084f9bSBrian Somers 103024084f9bSBrian Somers { RedirectAddress, 103124084f9bSBrian Somers 0, 103224084f9bSBrian Somers String, 1033bd690510SRuslan Ermilov "local_addr[,...] public_addr", 103424084f9bSBrian Somers "define mapping between local and public addresses", 103524084f9bSBrian Somers "redirect_address", 103624084f9bSBrian Somers NULL }, 103724084f9bSBrian Somers 103859a7c613SBrian Somers { PptpAlias, 103959a7c613SBrian Somers 0, 104059a7c613SBrian Somers String, 104159a7c613SBrian Somers "src", 104259a7c613SBrian Somers "define inside machine for PPTP traffic", 104359a7c613SBrian Somers "pptpalias", 104459a7c613SBrian Somers NULL }, 104559a7c613SBrian Somers 104624084f9bSBrian Somers { ConfigFile, 104724084f9bSBrian Somers 0, 104824084f9bSBrian Somers String, 104924084f9bSBrian Somers "file_name", 105024084f9bSBrian Somers "read options from configuration file", 105124084f9bSBrian Somers "config", 105259a7c613SBrian Somers "f" }, 105359a7c613SBrian Somers 105459a7c613SBrian Somers { LogDenied, 105559a7c613SBrian Somers 0, 105659a7c613SBrian Somers YesNo, 105759a7c613SBrian Somers "[yes|no]", 105859a7c613SBrian Somers "enable logging of denied incoming packets", 105959a7c613SBrian Somers "log_denied", 106059a7c613SBrian Somers NULL }, 106159a7c613SBrian Somers 106259a7c613SBrian Somers { LogFacility, 106359a7c613SBrian Somers 0, 106459a7c613SBrian Somers String, 106559a7c613SBrian Somers "facility", 106659a7c613SBrian Somers "name of syslog facility to use for logging", 106759a7c613SBrian Somers "log_facility", 106859a7c613SBrian Somers NULL } 106959a7c613SBrian Somers 107024084f9bSBrian Somers }; 107124084f9bSBrian Somers 1072902cb50aSBrian Somers static void ParseOption (const char* option, const char* parms, int cmdLine) 107324084f9bSBrian Somers { 107424084f9bSBrian Somers int i; 107524084f9bSBrian Somers struct OptionInfo* info; 107624084f9bSBrian Somers int yesNoValue; 107724084f9bSBrian Somers int aliasValue; 107824084f9bSBrian Somers int numValue; 107967a886fbSBrian Somers u_short uNumValue; 1080902cb50aSBrian Somers const char* strValue; 108124084f9bSBrian Somers struct in_addr addrValue; 108224084f9bSBrian Somers int max; 108324084f9bSBrian Somers char* end; 108459a7c613SBrian Somers CODE* fac_record = NULL; 108524084f9bSBrian Somers /* 108624084f9bSBrian Somers * Find option from table. 108724084f9bSBrian Somers */ 108824084f9bSBrian Somers max = sizeof (optionTable) / sizeof (struct OptionInfo); 108924084f9bSBrian Somers for (i = 0, info = optionTable; i < max; i++, info++) { 109024084f9bSBrian Somers 109124084f9bSBrian Somers if (!strcmp (info->name, option)) 109224084f9bSBrian Somers break; 109324084f9bSBrian Somers 109424084f9bSBrian Somers if (info->shortName) 109524084f9bSBrian Somers if (!strcmp (info->shortName, option)) 109624084f9bSBrian Somers break; 109724084f9bSBrian Somers } 109824084f9bSBrian Somers 109924084f9bSBrian Somers if (i >= max) { 110024084f9bSBrian Somers 11010fc81af1SPhilippe Charnier warnx ("unknown option %s", option); 110224084f9bSBrian Somers Usage (); 110324084f9bSBrian Somers } 110424084f9bSBrian Somers 110567a886fbSBrian Somers uNumValue = 0; 110624084f9bSBrian Somers yesNoValue = 0; 110724084f9bSBrian Somers numValue = 0; 110824084f9bSBrian Somers strValue = NULL; 110924084f9bSBrian Somers /* 111024084f9bSBrian Somers * Check parameters. 111124084f9bSBrian Somers */ 111224084f9bSBrian Somers switch (info->parm) { 111324084f9bSBrian Somers case YesNo: 111424084f9bSBrian Somers if (!parms) 111524084f9bSBrian Somers parms = "yes"; 111624084f9bSBrian Somers 111724084f9bSBrian Somers if (!strcmp (parms, "yes")) 111824084f9bSBrian Somers yesNoValue = 1; 111924084f9bSBrian Somers else 112024084f9bSBrian Somers if (!strcmp (parms, "no")) 112124084f9bSBrian Somers yesNoValue = 0; 11220fc81af1SPhilippe Charnier else 11230fc81af1SPhilippe Charnier errx (1, "%s needs yes/no parameter", option); 112424084f9bSBrian Somers break; 112524084f9bSBrian Somers 112624084f9bSBrian Somers case Service: 11270fc81af1SPhilippe Charnier if (!parms) 112867a886fbSBrian Somers errx (1, "%s needs service name or " 112967a886fbSBrian Somers "port number parameter", 113067a886fbSBrian Somers option); 113124084f9bSBrian Somers 113267a886fbSBrian Somers uNumValue = StrToPort (parms, "divert"); 113324084f9bSBrian Somers break; 113424084f9bSBrian Somers 113524084f9bSBrian Somers case Numeric: 113624084f9bSBrian Somers if (parms) 113724084f9bSBrian Somers numValue = strtol (parms, &end, 10); 113824084f9bSBrian Somers else 1139902cb50aSBrian Somers end = NULL; 114024084f9bSBrian Somers 11410fc81af1SPhilippe Charnier if (end == parms) 11420fc81af1SPhilippe Charnier errx (1, "%s needs numeric parameter", option); 114324084f9bSBrian Somers break; 114424084f9bSBrian Somers 114524084f9bSBrian Somers case String: 114624084f9bSBrian Somers strValue = parms; 11470fc81af1SPhilippe Charnier if (!strValue) 11480fc81af1SPhilippe Charnier errx (1, "%s needs parameter", option); 114924084f9bSBrian Somers break; 115024084f9bSBrian Somers 115124084f9bSBrian Somers case None: 11520fc81af1SPhilippe Charnier if (parms) 11530fc81af1SPhilippe Charnier errx (1, "%s does not take parameters", option); 115424084f9bSBrian Somers break; 115524084f9bSBrian Somers 115624084f9bSBrian Somers case Address: 11570fc81af1SPhilippe Charnier if (!parms) 11580fc81af1SPhilippe Charnier errx (1, "%s needs address/host parameter", option); 115924084f9bSBrian Somers 116024084f9bSBrian Somers StrToAddr (parms, &addrValue); 116124084f9bSBrian Somers break; 116224084f9bSBrian Somers } 116324084f9bSBrian Somers 116424084f9bSBrian Somers switch (info->type) { 116524084f9bSBrian Somers case PacketAliasOption: 116624084f9bSBrian Somers 116724084f9bSBrian Somers aliasValue = yesNoValue ? info->packetAliasOpt : 0; 1168fb994b07SBrian Somers PacketAliasSetMode (aliasValue, info->packetAliasOpt); 116924084f9bSBrian Somers break; 117024084f9bSBrian Somers 117124084f9bSBrian Somers case Verbose: 117224084f9bSBrian Somers verbose = yesNoValue; 117324084f9bSBrian Somers break; 117424084f9bSBrian Somers 117524084f9bSBrian Somers case DynamicMode: 117624084f9bSBrian Somers dynamicMode = yesNoValue; 117724084f9bSBrian Somers break; 117824084f9bSBrian Somers 117924084f9bSBrian Somers case InPort: 118067a886fbSBrian Somers inPort = uNumValue; 118124084f9bSBrian Somers break; 118224084f9bSBrian Somers 118324084f9bSBrian Somers case OutPort: 118467a886fbSBrian Somers outPort = uNumValue; 118524084f9bSBrian Somers break; 118624084f9bSBrian Somers 118724084f9bSBrian Somers case Port: 118867a886fbSBrian Somers inOutPort = uNumValue; 118924084f9bSBrian Somers break; 119024084f9bSBrian Somers 119124084f9bSBrian Somers case AliasAddress: 119224084f9bSBrian Somers memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr)); 119324084f9bSBrian Somers break; 119424084f9bSBrian Somers 119524084f9bSBrian Somers case RedirectPort: 119624084f9bSBrian Somers SetupPortRedirect (strValue); 119724084f9bSBrian Somers break; 119824084f9bSBrian Somers 119924084f9bSBrian Somers case RedirectAddress: 120024084f9bSBrian Somers SetupAddressRedirect (strValue); 120124084f9bSBrian Somers break; 120224084f9bSBrian Somers 120359a7c613SBrian Somers case PptpAlias: 120459a7c613SBrian Somers SetupPptpAlias (strValue); 120559a7c613SBrian Somers break; 120659a7c613SBrian Somers 120759a7c613SBrian Somers case ProxyRule: 120859a7c613SBrian Somers PacketAliasProxyRule (strValue); 120959a7c613SBrian Somers break; 121059a7c613SBrian Somers 121124084f9bSBrian Somers case InterfaceName: 121224084f9bSBrian Somers if (ifName) 121324084f9bSBrian Somers free (ifName); 121424084f9bSBrian Somers 121524084f9bSBrian Somers ifName = strdup (strValue); 121624084f9bSBrian Somers break; 121724084f9bSBrian Somers 121824084f9bSBrian Somers case ConfigFile: 121924084f9bSBrian Somers ReadConfigFile (strValue); 122024084f9bSBrian Somers break; 122159a7c613SBrian Somers 122259a7c613SBrian Somers case LogDenied: 122359a7c613SBrian Somers logDropped = 1; 122459a7c613SBrian Somers break; 122559a7c613SBrian Somers 122659a7c613SBrian Somers case LogFacility: 122759a7c613SBrian Somers 122859a7c613SBrian Somers fac_record = facilitynames; 122959a7c613SBrian Somers while (fac_record->c_name != NULL) { 123059a7c613SBrian Somers 123159a7c613SBrian Somers if (!strcmp (fac_record->c_name, strValue)) { 123259a7c613SBrian Somers 123359a7c613SBrian Somers logFacility = fac_record->c_val; 123459a7c613SBrian Somers break; 123559a7c613SBrian Somers 123659a7c613SBrian Somers } 123759a7c613SBrian Somers else 123859a7c613SBrian Somers fac_record++; 123959a7c613SBrian Somers } 124059a7c613SBrian Somers 124159a7c613SBrian Somers if(fac_record->c_name == NULL) 124259a7c613SBrian Somers errx(1, "Unknown log facility name: %s", strValue); 124359a7c613SBrian Somers 124459a7c613SBrian Somers break; 124524084f9bSBrian Somers } 124624084f9bSBrian Somers } 124724084f9bSBrian Somers 1248902cb50aSBrian Somers void ReadConfigFile (const char* fileName) 124924084f9bSBrian Somers { 125024084f9bSBrian Somers FILE* file; 1251d99cc1daSRuslan Ermilov char *buf; 1252d99cc1daSRuslan Ermilov size_t len; 12532e7e7c71SRuslan Ermilov char *ptr, *p; 125424084f9bSBrian Somers char* option; 125524084f9bSBrian Somers 125624084f9bSBrian Somers file = fopen (fileName, "r"); 1257d99cc1daSRuslan Ermilov if (!file) 1258d99cc1daSRuslan Ermilov err(1, "cannot open config file %s", fileName); 125924084f9bSBrian Somers 1260d99cc1daSRuslan Ermilov while ((buf = fgetln(file, &len)) != NULL) { 1261d99cc1daSRuslan Ermilov if (buf[len - 1] == '\n') 1262d99cc1daSRuslan Ermilov buf[len - 1] = '\0'; 1263d99cc1daSRuslan Ermilov else 1264d99cc1daSRuslan Ermilov errx(1, "config file format error: " 1265d99cc1daSRuslan Ermilov "last line should end with newline"); 126624084f9bSBrian Somers 126724084f9bSBrian Somers /* 12682e7e7c71SRuslan Ermilov * Check for comments, strip off trailing spaces. 126924084f9bSBrian Somers */ 12702e7e7c71SRuslan Ermilov if ((ptr = strchr(buf, '#'))) 12712e7e7c71SRuslan Ermilov *ptr = '\0'; 12722e7e7c71SRuslan Ermilov for (ptr = buf; isspace(*ptr); ++ptr) 12732e7e7c71SRuslan Ermilov continue; 127424084f9bSBrian Somers if (*ptr == '\0') 127524084f9bSBrian Somers continue; 12762e7e7c71SRuslan Ermilov for (p = strchr(buf, '\0'); isspace(*--p);) 12772e7e7c71SRuslan Ermilov continue; 12782e7e7c71SRuslan Ermilov *++p = '\0'; 12792e7e7c71SRuslan Ermilov 128024084f9bSBrian Somers /* 128124084f9bSBrian Somers * Extract option name. 128224084f9bSBrian Somers */ 128324084f9bSBrian Somers option = ptr; 128424084f9bSBrian Somers while (*ptr && !isspace (*ptr)) 128524084f9bSBrian Somers ++ptr; 128624084f9bSBrian Somers 128724084f9bSBrian Somers if (*ptr != '\0') { 128824084f9bSBrian Somers 128924084f9bSBrian Somers *ptr = '\0'; 129024084f9bSBrian Somers ++ptr; 129124084f9bSBrian Somers } 129224084f9bSBrian Somers /* 129324084f9bSBrian Somers * Skip white space between name and parms. 129424084f9bSBrian Somers */ 129524084f9bSBrian Somers while (*ptr && isspace (*ptr)) 129624084f9bSBrian Somers ++ptr; 129724084f9bSBrian Somers 129824084f9bSBrian Somers ParseOption (option, *ptr ? ptr : NULL, 0); 129924084f9bSBrian Somers } 130024084f9bSBrian Somers 130124084f9bSBrian Somers fclose (file); 130224084f9bSBrian Somers } 130324084f9bSBrian Somers 130424084f9bSBrian Somers static void Usage () 130524084f9bSBrian Somers { 130624084f9bSBrian Somers int i; 130724084f9bSBrian Somers int max; 130824084f9bSBrian Somers struct OptionInfo* info; 130924084f9bSBrian Somers 131024084f9bSBrian Somers fprintf (stderr, "Recognized options:\n\n"); 131124084f9bSBrian Somers 131224084f9bSBrian Somers max = sizeof (optionTable) / sizeof (struct OptionInfo); 131324084f9bSBrian Somers for (i = 0, info = optionTable; i < max; i++, info++) { 131424084f9bSBrian Somers 131524084f9bSBrian Somers fprintf (stderr, "-%-20s %s\n", info->name, 131624084f9bSBrian Somers info->parmDescription); 131724084f9bSBrian Somers 131824084f9bSBrian Somers if (info->shortName) 131924084f9bSBrian Somers fprintf (stderr, "-%-20s %s\n", info->shortName, 132024084f9bSBrian Somers info->parmDescription); 132124084f9bSBrian Somers 132224084f9bSBrian Somers fprintf (stderr, " %s\n\n", info->description); 132324084f9bSBrian Somers } 132424084f9bSBrian Somers 132524084f9bSBrian Somers exit (1); 132624084f9bSBrian Somers } 132724084f9bSBrian Somers 1328902cb50aSBrian Somers void SetupPptpAlias (const char* parms) 132924084f9bSBrian Somers { 133024084f9bSBrian Somers char buf[128]; 133124084f9bSBrian Somers char* ptr; 133224084f9bSBrian Somers struct in_addr srcAddr; 133324084f9bSBrian Somers 133424084f9bSBrian Somers strcpy (buf, parms); 133524084f9bSBrian Somers 133624084f9bSBrian Somers /* 133724084f9bSBrian Somers * Extract source address. 133824084f9bSBrian Somers */ 133959a7c613SBrian Somers ptr = strtok (buf, " \t"); 13400fc81af1SPhilippe Charnier if (!ptr) 134159a7c613SBrian Somers errx(1, "pptpalias: missing src address"); 134224084f9bSBrian Somers 134359a7c613SBrian Somers StrToAddr (ptr, &srcAddr); 134459a7c613SBrian Somers PacketAliasPptp (srcAddr); 134524084f9bSBrian Somers } 134624084f9bSBrian Somers 1347902cb50aSBrian Somers void SetupPortRedirect (const char* parms) 134824084f9bSBrian Somers { 134924084f9bSBrian Somers char buf[128]; 135024084f9bSBrian Somers char* ptr; 1351bd690510SRuslan Ermilov char* serverPool; 135224084f9bSBrian Somers struct in_addr localAddr; 135324084f9bSBrian Somers struct in_addr publicAddr; 135424084f9bSBrian Somers struct in_addr remoteAddr; 13555d8ee958SBrian Somers port_range portRange; 13565d8ee958SBrian Somers u_short localPort = 0; 13575d8ee958SBrian Somers u_short publicPort = 0; 13585d8ee958SBrian Somers u_short remotePort = 0; 13595d8ee958SBrian Somers u_short numLocalPorts = 0; 13605d8ee958SBrian Somers u_short numPublicPorts = 0; 13615d8ee958SBrian Somers u_short numRemotePorts = 0; 136224084f9bSBrian Somers int proto; 136324084f9bSBrian Somers char* protoName; 136424084f9bSBrian Somers char* separator; 13655d8ee958SBrian Somers int i; 1366bd690510SRuslan Ermilov struct alias_link *link = NULL; 136724084f9bSBrian Somers 136824084f9bSBrian Somers strcpy (buf, parms); 136924084f9bSBrian Somers /* 137024084f9bSBrian Somers * Extract protocol. 137124084f9bSBrian Somers */ 137224084f9bSBrian Somers protoName = strtok (buf, " \t"); 13730fc81af1SPhilippe Charnier if (!protoName) 13740fc81af1SPhilippe Charnier errx (1, "redirect_port: missing protocol"); 137524084f9bSBrian Somers 137624084f9bSBrian Somers proto = StrToProto (protoName); 137724084f9bSBrian Somers /* 137824084f9bSBrian Somers * Extract local address. 137924084f9bSBrian Somers */ 138024084f9bSBrian Somers ptr = strtok (NULL, " \t"); 13810fc81af1SPhilippe Charnier if (!ptr) 13820fc81af1SPhilippe Charnier errx (1, "redirect_port: missing local address"); 138324084f9bSBrian Somers 1384bd690510SRuslan Ermilov separator = strchr(ptr, ','); 1385bd690510SRuslan Ermilov if (separator) { /* LSNAT redirection syntax. */ 1386bd690510SRuslan Ermilov localAddr.s_addr = INADDR_NONE; 1387bd690510SRuslan Ermilov localPort = ~0; 1388bd690510SRuslan Ermilov numLocalPorts = 1; 1389bd690510SRuslan Ermilov serverPool = ptr; 1390bd690510SRuslan Ermilov } else { 13915d8ee958SBrian Somers if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 ) 13925d8ee958SBrian Somers errx (1, "redirect_port: invalid local port range"); 13935d8ee958SBrian Somers 13945d8ee958SBrian Somers localPort = GETLOPORT(portRange); 13955d8ee958SBrian Somers numLocalPorts = GETNUMPORTS(portRange); 1396bd690510SRuslan Ermilov serverPool = NULL; 1397bd690510SRuslan Ermilov } 13985d8ee958SBrian Somers 139924084f9bSBrian Somers /* 14009c501140SBrian Somers * Extract public port and optionally address. 140124084f9bSBrian Somers */ 140224084f9bSBrian Somers ptr = strtok (NULL, " \t"); 14030fc81af1SPhilippe Charnier if (!ptr) 14040fc81af1SPhilippe Charnier errx (1, "redirect_port: missing public port"); 140524084f9bSBrian Somers 140624084f9bSBrian Somers separator = strchr (ptr, ':'); 14075d8ee958SBrian Somers if (separator) { 14085d8ee958SBrian Somers if (StrToAddrAndPortRange (ptr, &publicAddr, protoName, &portRange) != 0 ) 14095d8ee958SBrian Somers errx (1, "redirect_port: invalid public port range"); 141024084f9bSBrian Somers } 14115d8ee958SBrian Somers else { 14125d8ee958SBrian Somers publicAddr.s_addr = INADDR_ANY; 14135d8ee958SBrian Somers if (StrToPortRange (ptr, protoName, &portRange) != 0) 14145d8ee958SBrian Somers errx (1, "redirect_port: invalid public port range"); 14155d8ee958SBrian Somers } 14165d8ee958SBrian Somers 14175d8ee958SBrian Somers publicPort = GETLOPORT(portRange); 14185d8ee958SBrian Somers numPublicPorts = GETNUMPORTS(portRange); 141924084f9bSBrian Somers 142024084f9bSBrian Somers /* 142124084f9bSBrian Somers * Extract remote address and optionally port. 142224084f9bSBrian Somers */ 142324084f9bSBrian Somers ptr = strtok (NULL, " \t"); 142424084f9bSBrian Somers if (ptr) { 142524084f9bSBrian Somers separator = strchr (ptr, ':'); 1426ebe70c8fSWarner Losh if (separator) { 14275d8ee958SBrian Somers if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0) 14285d8ee958SBrian Somers errx (1, "redirect_port: invalid remote port range"); 1429ebe70c8fSWarner Losh } else { 14305d8ee958SBrian Somers SETLOPORT(portRange, 0); 14315d8ee958SBrian Somers SETNUMPORTS(portRange, 1); 143224084f9bSBrian Somers StrToAddr (ptr, &remoteAddr); 143324084f9bSBrian Somers } 143424084f9bSBrian Somers } 143524084f9bSBrian Somers else { 14365d8ee958SBrian Somers SETLOPORT(portRange, 0); 14375d8ee958SBrian Somers SETNUMPORTS(portRange, 1); 143824084f9bSBrian Somers remoteAddr.s_addr = INADDR_ANY; 143924084f9bSBrian Somers } 144024084f9bSBrian Somers 14415d8ee958SBrian Somers remotePort = GETLOPORT(portRange); 14425d8ee958SBrian Somers numRemotePorts = GETNUMPORTS(portRange); 14435d8ee958SBrian Somers 14445d8ee958SBrian Somers /* 14455d8ee958SBrian Somers * Make sure port ranges match up, then add the redirect ports. 14465d8ee958SBrian Somers */ 14475d8ee958SBrian Somers if (numLocalPorts != numPublicPorts) 14485d8ee958SBrian Somers errx (1, "redirect_port: port ranges must be equal in size"); 14495d8ee958SBrian Somers 14505d8ee958SBrian Somers /* Remote port range is allowed to be '0' which means all ports. */ 145129d97436SBrian Somers if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0)) 14525d8ee958SBrian Somers errx (1, "redirect_port: remote port must be 0 or equal to local port range in size"); 14535d8ee958SBrian Somers 14545d8ee958SBrian Somers for (i = 0 ; i < numPublicPorts ; ++i) { 14555d8ee958SBrian Somers /* If remotePort is all ports, set it to 0. */ 14565d8ee958SBrian Somers u_short remotePortCopy = remotePort + i; 14575d8ee958SBrian Somers if (numRemotePorts == 1 && remotePort == 0) 14585d8ee958SBrian Somers remotePortCopy = 0; 14595d8ee958SBrian Somers 1460bd690510SRuslan Ermilov link = PacketAliasRedirectPort (localAddr, 14615d8ee958SBrian Somers htons(localPort + i), 146224084f9bSBrian Somers remoteAddr, 14635d8ee958SBrian Somers htons(remotePortCopy), 146424084f9bSBrian Somers publicAddr, 14655d8ee958SBrian Somers htons(publicPort + i), 146624084f9bSBrian Somers proto); 146724084f9bSBrian Somers } 1468bd690510SRuslan Ermilov 1469bd690510SRuslan Ermilov /* 1470bd690510SRuslan Ermilov * Setup LSNAT server pool. 1471bd690510SRuslan Ermilov */ 1472bd690510SRuslan Ermilov if (serverPool != NULL && link != NULL) { 1473bd690510SRuslan Ermilov ptr = strtok(serverPool, ","); 1474bd690510SRuslan Ermilov while (ptr != NULL) { 1475bd690510SRuslan Ermilov if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0) 1476bd690510SRuslan Ermilov errx(1, "redirect_port: invalid local port range"); 1477bd690510SRuslan Ermilov 1478bd690510SRuslan Ermilov localPort = GETLOPORT(portRange); 1479bd690510SRuslan Ermilov if (GETNUMPORTS(portRange) != 1) 1480bd690510SRuslan Ermilov errx(1, "redirect_port: local port must be single in this context"); 1481bd690510SRuslan Ermilov PacketAliasAddServer(link, localAddr, htons(localPort)); 1482bd690510SRuslan Ermilov ptr = strtok(NULL, ","); 1483bd690510SRuslan Ermilov } 1484bd690510SRuslan Ermilov } 14855d8ee958SBrian Somers } 148624084f9bSBrian Somers 1487902cb50aSBrian Somers void SetupAddressRedirect (const char* parms) 148824084f9bSBrian Somers { 148924084f9bSBrian Somers char buf[128]; 149024084f9bSBrian Somers char* ptr; 1491bd690510SRuslan Ermilov char* separator; 149224084f9bSBrian Somers struct in_addr localAddr; 149324084f9bSBrian Somers struct in_addr publicAddr; 1494bd690510SRuslan Ermilov char* serverPool; 1495bd690510SRuslan Ermilov struct alias_link *link; 149624084f9bSBrian Somers 149724084f9bSBrian Somers strcpy (buf, parms); 149824084f9bSBrian Somers /* 149924084f9bSBrian Somers * Extract local address. 150024084f9bSBrian Somers */ 150124084f9bSBrian Somers ptr = strtok (buf, " \t"); 15020fc81af1SPhilippe Charnier if (!ptr) 15030fc81af1SPhilippe Charnier errx (1, "redirect_address: missing local address"); 150424084f9bSBrian Somers 1505bd690510SRuslan Ermilov separator = strchr(ptr, ','); 1506bd690510SRuslan Ermilov if (separator) { /* LSNAT redirection syntax. */ 1507bd690510SRuslan Ermilov localAddr.s_addr = INADDR_NONE; 1508bd690510SRuslan Ermilov serverPool = ptr; 1509bd690510SRuslan Ermilov } else { 151024084f9bSBrian Somers StrToAddr (ptr, &localAddr); 1511bd690510SRuslan Ermilov serverPool = NULL; 1512bd690510SRuslan Ermilov } 151324084f9bSBrian Somers /* 151424084f9bSBrian Somers * Extract public address. 151524084f9bSBrian Somers */ 151624084f9bSBrian Somers ptr = strtok (NULL, " \t"); 15170fc81af1SPhilippe Charnier if (!ptr) 15180fc81af1SPhilippe Charnier errx (1, "redirect_address: missing public address"); 151924084f9bSBrian Somers 152024084f9bSBrian Somers StrToAddr (ptr, &publicAddr); 1521bd690510SRuslan Ermilov link = PacketAliasRedirectAddr(localAddr, publicAddr); 1522bd690510SRuslan Ermilov 1523bd690510SRuslan Ermilov /* 1524bd690510SRuslan Ermilov * Setup LSNAT server pool. 1525bd690510SRuslan Ermilov */ 1526bd690510SRuslan Ermilov if (serverPool != NULL && link != NULL) { 1527bd690510SRuslan Ermilov ptr = strtok(serverPool, ","); 1528bd690510SRuslan Ermilov while (ptr != NULL) { 1529bd690510SRuslan Ermilov StrToAddr(ptr, &localAddr); 1530bd690510SRuslan Ermilov PacketAliasAddServer(link, localAddr, htons(~0)); 1531bd690510SRuslan Ermilov ptr = strtok(NULL, ","); 1532bd690510SRuslan Ermilov } 1533bd690510SRuslan Ermilov } 153424084f9bSBrian Somers } 153524084f9bSBrian Somers 1536902cb50aSBrian Somers void StrToAddr (const char* str, struct in_addr* addr) 153724084f9bSBrian Somers { 153824084f9bSBrian Somers struct hostent* hp; 153924084f9bSBrian Somers 154024084f9bSBrian Somers if (inet_aton (str, addr)) 154124084f9bSBrian Somers return; 154224084f9bSBrian Somers 154324084f9bSBrian Somers hp = gethostbyname (str); 15440fc81af1SPhilippe Charnier if (!hp) 15450fc81af1SPhilippe Charnier errx (1, "unknown host %s", str); 154624084f9bSBrian Somers 154724084f9bSBrian Somers memcpy (addr, hp->h_addr, sizeof (struct in_addr)); 154824084f9bSBrian Somers } 154924084f9bSBrian Somers 1550902cb50aSBrian Somers u_short StrToPort (const char* str, const char* proto) 155124084f9bSBrian Somers { 155267a886fbSBrian Somers u_short port; 155324084f9bSBrian Somers struct servent* sp; 155424084f9bSBrian Somers char* end; 155524084f9bSBrian Somers 155624084f9bSBrian Somers port = strtol (str, &end, 10); 155724084f9bSBrian Somers if (end != str) 155827c20503SBrian Somers return htons (port); 155924084f9bSBrian Somers 156024084f9bSBrian Somers sp = getservbyname (str, proto); 15610fc81af1SPhilippe Charnier if (!sp) 15620fc81af1SPhilippe Charnier errx (1, "unknown service %s/%s", str, proto); 156324084f9bSBrian Somers 156424084f9bSBrian Somers return sp->s_port; 156524084f9bSBrian Somers } 156624084f9bSBrian Somers 1567902cb50aSBrian Somers int StrToPortRange (const char* str, const char* proto, port_range *portRange) 15685d8ee958SBrian Somers { 15695d8ee958SBrian Somers char* sep; 15705d8ee958SBrian Somers struct servent* sp; 15715d8ee958SBrian Somers char* end; 15725d8ee958SBrian Somers u_short loPort; 15735d8ee958SBrian Somers u_short hiPort; 15745d8ee958SBrian Somers 15755d8ee958SBrian Somers /* First see if this is a service, return corresponding port if so. */ 15765d8ee958SBrian Somers sp = getservbyname (str,proto); 15775d8ee958SBrian Somers if (sp) { 15785d8ee958SBrian Somers SETLOPORT(*portRange, ntohs(sp->s_port)); 15795d8ee958SBrian Somers SETNUMPORTS(*portRange, 1); 15805d8ee958SBrian Somers return 0; 15815d8ee958SBrian Somers } 15825d8ee958SBrian Somers 15835d8ee958SBrian Somers /* Not a service, see if it's a single port or port range. */ 15845d8ee958SBrian Somers sep = strchr (str, '-'); 15855d8ee958SBrian Somers if (sep == NULL) { 15865d8ee958SBrian Somers SETLOPORT(*portRange, strtol(str, &end, 10)); 15875d8ee958SBrian Somers if (end != str) { 15885d8ee958SBrian Somers /* Single port. */ 15895d8ee958SBrian Somers SETNUMPORTS(*portRange, 1); 15905d8ee958SBrian Somers return 0; 15915d8ee958SBrian Somers } 15925d8ee958SBrian Somers 15935d8ee958SBrian Somers /* Error in port range field. */ 15945d8ee958SBrian Somers errx (1, "unknown service %s/%s", str, proto); 15955d8ee958SBrian Somers } 15965d8ee958SBrian Somers 15975d8ee958SBrian Somers /* Port range, get the values and sanity check. */ 15985d8ee958SBrian Somers sscanf (str, "%hu-%hu", &loPort, &hiPort); 15995d8ee958SBrian Somers SETLOPORT(*portRange, loPort); 16005d8ee958SBrian Somers SETNUMPORTS(*portRange, 0); /* Error by default */ 16015d8ee958SBrian Somers if (loPort <= hiPort) 16025d8ee958SBrian Somers SETNUMPORTS(*portRange, hiPort - loPort + 1); 16035d8ee958SBrian Somers 16045d8ee958SBrian Somers if (GETNUMPORTS(*portRange) == 0) 16055d8ee958SBrian Somers errx (1, "invalid port range %s", str); 16065d8ee958SBrian Somers 16075d8ee958SBrian Somers return 0; 16085d8ee958SBrian Somers } 16095d8ee958SBrian Somers 16105d8ee958SBrian Somers 1611902cb50aSBrian Somers int StrToProto (const char* str) 161224084f9bSBrian Somers { 161324084f9bSBrian Somers if (!strcmp (str, "tcp")) 161424084f9bSBrian Somers return IPPROTO_TCP; 161524084f9bSBrian Somers 161624084f9bSBrian Somers if (!strcmp (str, "udp")) 161724084f9bSBrian Somers return IPPROTO_UDP; 161824084f9bSBrian Somers 16190fc81af1SPhilippe Charnier errx (1, "unknown protocol %s. Expected tcp or udp", str); 162024084f9bSBrian Somers } 162124084f9bSBrian Somers 1622902cb50aSBrian Somers int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange) 162324084f9bSBrian Somers { 162424084f9bSBrian Somers char* ptr; 162524084f9bSBrian Somers 162624084f9bSBrian Somers ptr = strchr (str, ':'); 16270fc81af1SPhilippe Charnier if (!ptr) 16280fc81af1SPhilippe Charnier errx (1, "%s is missing port number", str); 162924084f9bSBrian Somers 163024084f9bSBrian Somers *ptr = '\0'; 163124084f9bSBrian Somers ++ptr; 163224084f9bSBrian Somers 163324084f9bSBrian Somers StrToAddr (str, addr); 16345d8ee958SBrian Somers return StrToPortRange (ptr, proto, portRange); 163524084f9bSBrian Somers } 1636