124084f9bSBrian Somers /* 224084f9bSBrian Somers * natd - Network Address Translation Daemon for FreeBSD. 324084f9bSBrian Somers * 4f13f9fadSAlexander Langer * This software is provided free of charge, with no 524084f9bSBrian Somers * warranty of any kind, either expressed or implied. 624084f9bSBrian Somers * Use at your own risk. 724084f9bSBrian Somers * 824084f9bSBrian Somers * You may copy, modify and distribute this software (natd.c) freely. 924084f9bSBrian Somers * 10fb994b07SBrian Somers * Ari Suutari <suutari@iki.fi> 1124084f9bSBrian Somers * 127f3dea24SPeter Wemm * $FreeBSD$ 1324084f9bSBrian Somers */ 1424084f9bSBrian Somers 1559a7c613SBrian Somers #define SYSLOG_NAMES 1659a7c613SBrian Somers 1724084f9bSBrian Somers #include <sys/types.h> 1824084f9bSBrian Somers #include <sys/socket.h> 194c04fa4cSRuslan Ermilov #include <sys/sysctl.h> 2024084f9bSBrian Somers #include <sys/time.h> 2124084f9bSBrian Somers 2224084f9bSBrian Somers #include <netinet/in.h> 2324084f9bSBrian Somers #include <netinet/in_systm.h> 2424084f9bSBrian Somers #include <netinet/ip.h> 2524084f9bSBrian Somers #include <machine/in_cksum.h> 2624084f9bSBrian Somers #include <netinet/tcp.h> 2759a7c613SBrian Somers #include <netinet/udp.h> 2859a7c613SBrian Somers #include <netinet/ip_icmp.h> 2924084f9bSBrian Somers #include <net/if.h> 304c04fa4cSRuslan Ermilov #include <net/if_dl.h> 3124084f9bSBrian Somers #include <net/route.h> 3224084f9bSBrian Somers #include <arpa/inet.h> 3324084f9bSBrian Somers 3424084f9bSBrian Somers #include <alias.h> 350fc81af1SPhilippe Charnier #include <ctype.h> 360fc81af1SPhilippe Charnier #include <err.h> 370fc81af1SPhilippe Charnier #include <errno.h> 380fc81af1SPhilippe Charnier #include <netdb.h> 390fc81af1SPhilippe Charnier #include <signal.h> 400fc81af1SPhilippe Charnier #include <stdio.h> 410fc81af1SPhilippe Charnier #include <stdlib.h> 420fc81af1SPhilippe Charnier #include <string.h> 430fc81af1SPhilippe Charnier #include <syslog.h> 440fc81af1SPhilippe Charnier #include <unistd.h> 4567a886fbSBrian Somers 4624084f9bSBrian Somers #include "natd.h" 4724084f9bSBrian Somers 4824084f9bSBrian Somers /* 4924084f9bSBrian Somers * Default values for input and output 5024084f9bSBrian Somers * divert socket ports. 5124084f9bSBrian Somers */ 5224084f9bSBrian Somers 5324084f9bSBrian Somers #define DEFAULT_SERVICE "natd" 5424084f9bSBrian Somers 5524084f9bSBrian Somers /* 565d8ee958SBrian Somers * Definition of a port range, and macros to deal with values. 575d8ee958SBrian Somers * FORMAT: HI 16-bits == first port in range, 0 == all ports. 585d8ee958SBrian Somers * LO 16-bits == number of ports in range 595d8ee958SBrian Somers * NOTES: - Port values are not stored in network byte order. 605d8ee958SBrian Somers */ 615d8ee958SBrian Somers 625d8ee958SBrian Somers typedef u_long port_range; 635d8ee958SBrian Somers 645d8ee958SBrian Somers #define GETLOPORT(x) ((x) >> 0x10) 655d8ee958SBrian Somers #define GETNUMPORTS(x) ((x) & 0x0000ffff) 665d8ee958SBrian Somers #define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x))) 675d8ee958SBrian Somers 685d8ee958SBrian Somers /* Set y to be the low-port value in port_range variable x. */ 695d8ee958SBrian Somers #define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10)) 705d8ee958SBrian Somers 715d8ee958SBrian Somers /* Set y to be the number of ports in port_range variable x. */ 725d8ee958SBrian Somers #define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y)) 735d8ee958SBrian Somers 745d8ee958SBrian Somers /* 7524084f9bSBrian Somers * Function prototypes. 7624084f9bSBrian Somers */ 7724084f9bSBrian Somers 7859a7c613SBrian Somers static void DoAliasing (int fd, int direction); 79902cb50aSBrian Somers static void DaemonMode (void); 8024084f9bSBrian Somers static void HandleRoutingInfo (int fd); 81902cb50aSBrian Somers static void Usage (void); 8259a7c613SBrian Somers static char* FormatPacket (struct ip*); 8324084f9bSBrian Somers static void PrintPacket (struct ip*); 84902cb50aSBrian Somers static void SyslogPacket (struct ip*, int priority, const char *label); 854c04fa4cSRuslan Ermilov static void SetAliasAddressFromIfName (const char *ifName); 86902cb50aSBrian Somers static void InitiateShutdown (int); 87902cb50aSBrian Somers static void Shutdown (int); 88902cb50aSBrian Somers static void RefreshAddr (int); 89b0f55af6SRuslan Ermilov static void ParseOption (const char* option, const char* parms); 90902cb50aSBrian Somers static void ReadConfigFile (const char* fileName); 91902cb50aSBrian Somers static void SetupPortRedirect (const char* parms); 924330006dSRuslan Ermilov static void SetupProtoRedirect(const char* parms); 93902cb50aSBrian Somers static void SetupAddressRedirect (const char* parms); 94902cb50aSBrian Somers static void 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); 101bc4ebb98SRuslan Ermilov static void SetupPunchFW(const char *strValue); 10224084f9bSBrian Somers 10324084f9bSBrian Somers /* 10424084f9bSBrian Somers * Globals. 10524084f9bSBrian Somers */ 10624084f9bSBrian Somers 10724084f9bSBrian Somers static int verbose; 10824084f9bSBrian Somers static int background; 10924084f9bSBrian Somers static int running; 11024084f9bSBrian Somers static int assignAliasAddr; 11124084f9bSBrian Somers static char* ifName; 11224084f9bSBrian Somers static int ifIndex; 11367a886fbSBrian Somers static u_short inPort; 11467a886fbSBrian Somers static u_short outPort; 11567a886fbSBrian Somers static u_short inOutPort; 11624084f9bSBrian Somers static struct in_addr aliasAddr; 11724084f9bSBrian Somers static int dynamicMode; 11824084f9bSBrian Somers static int ifMTU; 11924084f9bSBrian Somers static int aliasOverhead; 12024084f9bSBrian Somers static int icmpSock; 121fb994b07SBrian Somers static char packetBuf[IP_MAXPACKET]; 122fb994b07SBrian Somers static int packetLen; 123fb994b07SBrian Somers static struct sockaddr_in packetAddr; 124fb994b07SBrian Somers static int packetSock; 12559a7c613SBrian Somers static int packetDirection; 126f9b06d5cSBrian Somers static int dropIgnoredIncoming; 12759a7c613SBrian Somers static int logDropped; 12859a7c613SBrian Somers static int logFacility; 12924084f9bSBrian Somers 13024084f9bSBrian Somers int main (int argc, char** argv) 13124084f9bSBrian Somers { 13224084f9bSBrian Somers int divertIn; 13324084f9bSBrian Somers int divertOut; 13424084f9bSBrian Somers int divertInOut; 13524084f9bSBrian Somers int routeSock; 13624084f9bSBrian Somers struct sockaddr_in addr; 13724084f9bSBrian Somers fd_set readMask; 138fb994b07SBrian Somers fd_set writeMask; 13924084f9bSBrian Somers int fdMax; 14024084f9bSBrian Somers /* 14124084f9bSBrian Somers * Initialize packet aliasing software. 14224084f9bSBrian Somers * Done already here to be able to alter option bits 14324084f9bSBrian Somers * during command line and configuration file processing. 14424084f9bSBrian Somers */ 145fb994b07SBrian Somers PacketAliasInit (); 14624084f9bSBrian Somers /* 14724084f9bSBrian Somers * Parse options. 14824084f9bSBrian Somers */ 14924084f9bSBrian Somers inPort = 0; 15024084f9bSBrian Somers outPort = 0; 15124084f9bSBrian Somers verbose = 0; 15224084f9bSBrian Somers inOutPort = 0; 15324084f9bSBrian Somers ifName = NULL; 15424084f9bSBrian Somers ifMTU = -1; 15524084f9bSBrian Somers background = 0; 15624084f9bSBrian Somers running = 1; 15724084f9bSBrian Somers assignAliasAddr = 0; 15824084f9bSBrian Somers aliasAddr.s_addr = INADDR_NONE; 15924084f9bSBrian Somers aliasOverhead = 12; 16024084f9bSBrian Somers dynamicMode = 0; 16159a7c613SBrian Somers logDropped = 0; 16259a7c613SBrian Somers logFacility = LOG_DAEMON; 163fb994b07SBrian Somers /* 164fb994b07SBrian Somers * Mark packet buffer empty. 165fb994b07SBrian Somers */ 166fb994b07SBrian Somers packetSock = -1; 16759a7c613SBrian Somers packetDirection = DONT_KNOW; 16824084f9bSBrian Somers 16924084f9bSBrian Somers ParseArgs (argc, argv); 17024084f9bSBrian Somers /* 17159a7c613SBrian Somers * Open syslog channel. 17259a7c613SBrian Somers */ 1734c04fa4cSRuslan Ermilov openlog ("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0), 1744c04fa4cSRuslan Ermilov logFacility); 17559a7c613SBrian Somers /* 17624084f9bSBrian Somers * Check that valid aliasing address has been given. 17724084f9bSBrian Somers */ 1780fc81af1SPhilippe Charnier if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL) 1790fc81af1SPhilippe Charnier errx (1, "aliasing address not given"); 18024084f9bSBrian Somers 1810fc81af1SPhilippe Charnier if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL) 18267a886fbSBrian Somers errx (1, "both alias address and interface " 18367a886fbSBrian Somers "name are not allowed"); 18424084f9bSBrian Somers /* 18524084f9bSBrian Somers * Check that valid port number is known. 18624084f9bSBrian Somers */ 18724084f9bSBrian Somers if (inPort != 0 || outPort != 0) 1880fc81af1SPhilippe Charnier if (inPort == 0 || outPort == 0) 1890fc81af1SPhilippe Charnier errx (1, "both input and output ports are required"); 19024084f9bSBrian Somers 19124084f9bSBrian Somers if (inPort == 0 && outPort == 0 && inOutPort == 0) 192b0f55af6SRuslan Ermilov ParseOption ("port", DEFAULT_SERVICE); 19324084f9bSBrian Somers 19424084f9bSBrian Somers /* 195f9b06d5cSBrian Somers * Check if ignored packets should be dropped. 196f9b06d5cSBrian Somers */ 197f9b06d5cSBrian Somers dropIgnoredIncoming = PacketAliasSetMode (0, 0); 198f9b06d5cSBrian Somers dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING; 199f9b06d5cSBrian Somers /* 20024084f9bSBrian Somers * Create divert sockets. Use only one socket if -p was specified 20124084f9bSBrian Somers * on command line. Otherwise, create separate sockets for 20224084f9bSBrian Somers * outgoing and incoming connnections. 20324084f9bSBrian Somers */ 20424084f9bSBrian Somers if (inOutPort) { 20524084f9bSBrian Somers 20624084f9bSBrian Somers divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 20724084f9bSBrian Somers if (divertInOut == -1) 20824084f9bSBrian Somers Quit ("Unable to create divert socket."); 20924084f9bSBrian Somers 21024084f9bSBrian Somers divertIn = -1; 21124084f9bSBrian Somers divertOut = -1; 21224084f9bSBrian Somers /* 21324084f9bSBrian Somers * Bind socket. 21424084f9bSBrian Somers */ 21524084f9bSBrian Somers 21624084f9bSBrian Somers addr.sin_family = AF_INET; 21724084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 21824084f9bSBrian Somers addr.sin_port = inOutPort; 21924084f9bSBrian Somers 22024084f9bSBrian Somers if (bind (divertInOut, 22124084f9bSBrian Somers (struct sockaddr*) &addr, 22224084f9bSBrian Somers sizeof addr) == -1) 22324084f9bSBrian Somers Quit ("Unable to bind divert socket."); 22424084f9bSBrian Somers } 22524084f9bSBrian Somers else { 22624084f9bSBrian Somers 22724084f9bSBrian Somers divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 22824084f9bSBrian Somers if (divertIn == -1) 22924084f9bSBrian Somers Quit ("Unable to create incoming divert socket."); 23024084f9bSBrian Somers 23124084f9bSBrian Somers divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 23224084f9bSBrian Somers if (divertOut == -1) 23324084f9bSBrian Somers Quit ("Unable to create outgoing divert socket."); 23424084f9bSBrian Somers 23524084f9bSBrian Somers divertInOut = -1; 23624084f9bSBrian Somers 23724084f9bSBrian Somers /* 23824084f9bSBrian Somers * Bind divert sockets. 23924084f9bSBrian Somers */ 24024084f9bSBrian Somers 24124084f9bSBrian Somers addr.sin_family = AF_INET; 24224084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 24324084f9bSBrian Somers addr.sin_port = inPort; 24424084f9bSBrian Somers 24524084f9bSBrian Somers if (bind (divertIn, 24624084f9bSBrian Somers (struct sockaddr*) &addr, 24724084f9bSBrian Somers sizeof addr) == -1) 24824084f9bSBrian Somers Quit ("Unable to bind incoming divert socket."); 24924084f9bSBrian Somers 25024084f9bSBrian Somers addr.sin_family = AF_INET; 25124084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 25224084f9bSBrian Somers addr.sin_port = outPort; 25324084f9bSBrian Somers 25424084f9bSBrian Somers if (bind (divertOut, 25524084f9bSBrian Somers (struct sockaddr*) &addr, 25624084f9bSBrian Somers sizeof addr) == -1) 25724084f9bSBrian Somers Quit ("Unable to bind outgoing divert socket."); 25824084f9bSBrian Somers } 25924084f9bSBrian Somers /* 260f2da55a2SRuslan Ermilov * Create routing socket if interface name specified and in dynamic mode. 26124084f9bSBrian Somers */ 262f2da55a2SRuslan Ermilov routeSock = -1; 263f2da55a2SRuslan Ermilov if (ifName) { 264f2da55a2SRuslan Ermilov if (dynamicMode) { 26524084f9bSBrian Somers 26624084f9bSBrian Somers routeSock = socket (PF_ROUTE, SOCK_RAW, 0); 26724084f9bSBrian Somers if (routeSock == -1) 26824084f9bSBrian Somers Quit ("Unable to create routing info socket."); 269f2da55a2SRuslan Ermilov 270f2da55a2SRuslan Ermilov assignAliasAddr = 1; 27124084f9bSBrian Somers } 27224084f9bSBrian Somers else 273f2da55a2SRuslan Ermilov SetAliasAddressFromIfName (ifName); 274f2da55a2SRuslan Ermilov } 27524084f9bSBrian Somers /* 27624084f9bSBrian Somers * Create socket for sending ICMP messages. 27724084f9bSBrian Somers */ 27824084f9bSBrian Somers icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); 27924084f9bSBrian Somers if (icmpSock == -1) 28024084f9bSBrian Somers Quit ("Unable to create ICMP socket."); 281f3d64024SBrian Somers 282f3d64024SBrian Somers /* 283f3d64024SBrian Somers * And disable reads for the socket, otherwise it slowly fills 284f3d64024SBrian Somers * up with received icmps which we do not use. 285f3d64024SBrian Somers */ 286f3d64024SBrian Somers shutdown(icmpSock, SHUT_RD); 287f3d64024SBrian Somers 28824084f9bSBrian Somers /* 28924084f9bSBrian Somers * Become a daemon unless verbose mode was requested. 29024084f9bSBrian Somers */ 29124084f9bSBrian Somers if (!verbose) 29224084f9bSBrian Somers DaemonMode (); 29324084f9bSBrian Somers /* 29424084f9bSBrian Somers * Catch signals to manage shutdown and 29524084f9bSBrian Somers * refresh of interface address. 29624084f9bSBrian Somers */ 297cd45c931SRuslan Ermilov siginterrupt(SIGTERM, 1); 298cd45c931SRuslan Ermilov siginterrupt(SIGHUP, 1); 29924084f9bSBrian Somers signal (SIGTERM, InitiateShutdown); 30024084f9bSBrian Somers signal (SIGHUP, RefreshAddr); 30124084f9bSBrian Somers /* 30224084f9bSBrian Somers * Set alias address if it has been given. 30324084f9bSBrian Somers */ 30424084f9bSBrian Somers if (aliasAddr.s_addr != INADDR_NONE) 305fb994b07SBrian Somers PacketAliasSetAddress (aliasAddr); 30624084f9bSBrian Somers /* 30724084f9bSBrian Somers * We need largest descriptor number for select. 30824084f9bSBrian Somers */ 30924084f9bSBrian Somers 31024084f9bSBrian Somers fdMax = -1; 31124084f9bSBrian Somers 31224084f9bSBrian Somers if (divertIn > fdMax) 31324084f9bSBrian Somers fdMax = divertIn; 31424084f9bSBrian Somers 31524084f9bSBrian Somers if (divertOut > fdMax) 31624084f9bSBrian Somers fdMax = divertOut; 31724084f9bSBrian Somers 31824084f9bSBrian Somers if (divertInOut > fdMax) 31924084f9bSBrian Somers fdMax = divertInOut; 32024084f9bSBrian Somers 32124084f9bSBrian Somers if (routeSock > fdMax) 32224084f9bSBrian Somers fdMax = routeSock; 32324084f9bSBrian Somers 32424084f9bSBrian Somers while (running) { 325fb994b07SBrian Somers 326fb994b07SBrian Somers if (divertInOut != -1 && !ifName && packetSock == -1) { 327fb994b07SBrian Somers /* 328fb994b07SBrian Somers * When using only one socket, just call 329fb994b07SBrian Somers * DoAliasing repeatedly to process packets. 330fb994b07SBrian Somers */ 33159a7c613SBrian Somers DoAliasing (divertInOut, DONT_KNOW); 332fb994b07SBrian Somers continue; 333fb994b07SBrian Somers } 33424084f9bSBrian Somers /* 33524084f9bSBrian Somers * Build read mask from socket descriptors to select. 33624084f9bSBrian Somers */ 33724084f9bSBrian Somers FD_ZERO (&readMask); 338fb994b07SBrian Somers FD_ZERO (&writeMask); 33924084f9bSBrian Somers 340fb994b07SBrian Somers /* 341fb994b07SBrian Somers * If there is unsent packet in buffer, use select 342fb994b07SBrian Somers * to check when socket comes writable again. 343fb994b07SBrian Somers */ 344fb994b07SBrian Somers if (packetSock != -1) { 345fb994b07SBrian Somers 346fb994b07SBrian Somers FD_SET (packetSock, &writeMask); 347fb994b07SBrian Somers } 348fb994b07SBrian Somers else { 349fb994b07SBrian Somers /* 350fb994b07SBrian Somers * No unsent packet exists - safe to check if 351fb994b07SBrian Somers * new ones are available. 352fb994b07SBrian Somers */ 35324084f9bSBrian Somers if (divertIn != -1) 35424084f9bSBrian Somers FD_SET (divertIn, &readMask); 35524084f9bSBrian Somers 35624084f9bSBrian Somers if (divertOut != -1) 35724084f9bSBrian Somers FD_SET (divertOut, &readMask); 35824084f9bSBrian Somers 35924084f9bSBrian Somers if (divertInOut != -1) 36024084f9bSBrian Somers FD_SET (divertInOut, &readMask); 361fb994b07SBrian Somers } 362fb994b07SBrian Somers /* 363fb994b07SBrian Somers * Routing info is processed always. 364fb994b07SBrian Somers */ 36524084f9bSBrian Somers if (routeSock != -1) 36624084f9bSBrian Somers FD_SET (routeSock, &readMask); 36724084f9bSBrian Somers 36824084f9bSBrian Somers if (select (fdMax + 1, 36924084f9bSBrian Somers &readMask, 370fb994b07SBrian Somers &writeMask, 37124084f9bSBrian Somers NULL, 37224084f9bSBrian Somers NULL) == -1) { 37324084f9bSBrian Somers 37424084f9bSBrian Somers if (errno == EINTR) 37524084f9bSBrian Somers continue; 37624084f9bSBrian Somers 37724084f9bSBrian Somers Quit ("Select failed."); 37824084f9bSBrian Somers } 37924084f9bSBrian Somers 380fb994b07SBrian Somers if (packetSock != -1) 381fb994b07SBrian Somers if (FD_ISSET (packetSock, &writeMask)) 382fb994b07SBrian Somers FlushPacketBuffer (packetSock); 383fb994b07SBrian Somers 38424084f9bSBrian Somers if (divertIn != -1) 38524084f9bSBrian Somers if (FD_ISSET (divertIn, &readMask)) 38659a7c613SBrian Somers DoAliasing (divertIn, INPUT); 38724084f9bSBrian Somers 38824084f9bSBrian Somers if (divertOut != -1) 38924084f9bSBrian Somers if (FD_ISSET (divertOut, &readMask)) 39059a7c613SBrian Somers DoAliasing (divertOut, OUTPUT); 39124084f9bSBrian Somers 39224084f9bSBrian Somers if (divertInOut != -1) 39324084f9bSBrian Somers if (FD_ISSET (divertInOut, &readMask)) 39459a7c613SBrian Somers DoAliasing (divertInOut, DONT_KNOW); 39524084f9bSBrian Somers 39624084f9bSBrian Somers if (routeSock != -1) 39724084f9bSBrian Somers if (FD_ISSET (routeSock, &readMask)) 39824084f9bSBrian Somers HandleRoutingInfo (routeSock); 39924084f9bSBrian Somers } 40024084f9bSBrian Somers 40124084f9bSBrian Somers if (background) 40224084f9bSBrian Somers unlink (PIDFILE); 40324084f9bSBrian Somers 40424084f9bSBrian Somers return 0; 40524084f9bSBrian Somers } 40624084f9bSBrian Somers 40724084f9bSBrian Somers static void DaemonMode () 40824084f9bSBrian Somers { 40924084f9bSBrian Somers FILE* pidFile; 41024084f9bSBrian Somers 41124084f9bSBrian Somers daemon (0, 0); 41224084f9bSBrian Somers background = 1; 41324084f9bSBrian Somers 41424084f9bSBrian Somers pidFile = fopen (PIDFILE, "w"); 41524084f9bSBrian Somers if (pidFile) { 41624084f9bSBrian Somers 41724084f9bSBrian Somers fprintf (pidFile, "%d\n", getpid ()); 41824084f9bSBrian Somers fclose (pidFile); 41924084f9bSBrian Somers } 42024084f9bSBrian Somers } 42124084f9bSBrian Somers 42224084f9bSBrian Somers static void ParseArgs (int argc, char** argv) 42324084f9bSBrian Somers { 42424084f9bSBrian Somers int arg; 42524084f9bSBrian Somers char* opt; 42624084f9bSBrian Somers char parmBuf[256]; 42730395bb5SJosef Karthauser int len; /* bounds checking */ 42824084f9bSBrian Somers 42924084f9bSBrian Somers for (arg = 1; arg < argc; arg++) { 43024084f9bSBrian Somers 43124084f9bSBrian Somers opt = argv[arg]; 43224084f9bSBrian Somers if (*opt != '-') { 43324084f9bSBrian Somers 4340fc81af1SPhilippe Charnier warnx ("invalid option %s", opt); 43524084f9bSBrian Somers Usage (); 43624084f9bSBrian Somers } 43724084f9bSBrian Somers 43824084f9bSBrian Somers parmBuf[0] = '\0'; 43930395bb5SJosef Karthauser len = 0; 44024084f9bSBrian Somers 44124084f9bSBrian Somers while (arg < argc - 1) { 44224084f9bSBrian Somers 44324084f9bSBrian Somers if (argv[arg + 1][0] == '-') 44424084f9bSBrian Somers break; 44524084f9bSBrian Somers 44630395bb5SJosef Karthauser if (len) { 44730395bb5SJosef Karthauser strncat (parmBuf, " ", sizeof(parmBuf) - (len + 1)); 44830395bb5SJosef Karthauser len += strlen(parmBuf + len); 44924084f9bSBrian Somers } 45024084f9bSBrian Somers 45130395bb5SJosef Karthauser ++arg; 45230395bb5SJosef Karthauser strncat (parmBuf, argv[arg], sizeof(parmBuf) - (len + 1)); 45330395bb5SJosef Karthauser len += strlen(parmBuf + len); 45430395bb5SJosef Karthauser 45530395bb5SJosef Karthauser } 45630395bb5SJosef Karthauser 457b0f55af6SRuslan Ermilov ParseOption (opt + 1, (len ? parmBuf : NULL)); 45830395bb5SJosef Karthauser 45924084f9bSBrian Somers } 46024084f9bSBrian Somers } 46124084f9bSBrian Somers 46259a7c613SBrian Somers static void DoAliasing (int fd, int direction) 46324084f9bSBrian Somers { 46424084f9bSBrian Somers int bytes; 46524084f9bSBrian Somers int origBytes; 466f9b06d5cSBrian Somers int status; 46724084f9bSBrian Somers int addrSize; 46824084f9bSBrian Somers struct ip* ip; 46924084f9bSBrian Somers 47024084f9bSBrian Somers if (assignAliasAddr) { 47124084f9bSBrian Somers 47224084f9bSBrian Somers SetAliasAddressFromIfName (ifName); 47324084f9bSBrian Somers assignAliasAddr = 0; 47424084f9bSBrian Somers } 47524084f9bSBrian Somers /* 47624084f9bSBrian Somers * Get packet from socket. 47724084f9bSBrian Somers */ 478fb994b07SBrian Somers addrSize = sizeof packetAddr; 47924084f9bSBrian Somers origBytes = recvfrom (fd, 480fb994b07SBrian Somers packetBuf, 481fb994b07SBrian Somers sizeof packetBuf, 48224084f9bSBrian Somers 0, 483fb994b07SBrian Somers (struct sockaddr*) &packetAddr, 48424084f9bSBrian Somers &addrSize); 48524084f9bSBrian Somers 48624084f9bSBrian Somers if (origBytes == -1) { 48724084f9bSBrian Somers 48824084f9bSBrian Somers if (errno != EINTR) 4890fc81af1SPhilippe Charnier Warn ("read from divert socket failed"); 49024084f9bSBrian Somers 49124084f9bSBrian Somers return; 49224084f9bSBrian Somers } 49324084f9bSBrian Somers /* 49424084f9bSBrian Somers * This is a IP packet. 49524084f9bSBrian Somers */ 496fb994b07SBrian Somers ip = (struct ip*) packetBuf; 497ebe70c8fSWarner Losh if (direction == DONT_KNOW) { 49859a7c613SBrian Somers if (packetAddr.sin_addr.s_addr == INADDR_ANY) 49959a7c613SBrian Somers direction = OUTPUT; 50059a7c613SBrian Somers else 50159a7c613SBrian Somers direction = INPUT; 502ebe70c8fSWarner Losh } 50324084f9bSBrian Somers 50424084f9bSBrian Somers if (verbose) { 50524084f9bSBrian Somers /* 50624084f9bSBrian Somers * Print packet direction and protocol type. 50724084f9bSBrian Somers */ 50859a7c613SBrian Somers printf (direction == OUTPUT ? "Out " : "In "); 50924084f9bSBrian Somers 51024084f9bSBrian Somers switch (ip->ip_p) { 51124084f9bSBrian Somers case IPPROTO_TCP: 51224084f9bSBrian Somers printf ("[TCP] "); 51324084f9bSBrian Somers break; 51424084f9bSBrian Somers 51524084f9bSBrian Somers case IPPROTO_UDP: 51624084f9bSBrian Somers printf ("[UDP] "); 51724084f9bSBrian Somers break; 51824084f9bSBrian Somers 51924084f9bSBrian Somers case IPPROTO_ICMP: 52024084f9bSBrian Somers printf ("[ICMP] "); 52124084f9bSBrian Somers break; 52224084f9bSBrian Somers 52324084f9bSBrian Somers default: 52459a7c613SBrian Somers printf ("[%d] ", ip->ip_p); 52524084f9bSBrian Somers break; 52624084f9bSBrian Somers } 52724084f9bSBrian Somers /* 52824084f9bSBrian Somers * Print addresses. 52924084f9bSBrian Somers */ 53024084f9bSBrian Somers PrintPacket (ip); 53124084f9bSBrian Somers } 53224084f9bSBrian Somers 53359a7c613SBrian Somers if (direction == OUTPUT) { 53424084f9bSBrian Somers /* 53524084f9bSBrian Somers * Outgoing packets. Do aliasing. 53624084f9bSBrian Somers */ 537fb994b07SBrian Somers PacketAliasOut (packetBuf, IP_MAXPACKET); 53824084f9bSBrian Somers } 53924084f9bSBrian Somers else { 54059a7c613SBrian Somers 54124084f9bSBrian Somers /* 54224084f9bSBrian Somers * Do aliasing. 54324084f9bSBrian Somers */ 544f9b06d5cSBrian Somers status = PacketAliasIn (packetBuf, IP_MAXPACKET); 545f9b06d5cSBrian Somers if (status == PKT_ALIAS_IGNORED && 546f9b06d5cSBrian Somers dropIgnoredIncoming) { 547f9b06d5cSBrian Somers 54859a7c613SBrian Somers if (verbose) 549f9b06d5cSBrian Somers printf (" dropped.\n"); 55059a7c613SBrian Somers 55159a7c613SBrian Somers if (logDropped) 55259a7c613SBrian Somers SyslogPacket (ip, LOG_WARNING, "denied"); 55359a7c613SBrian Somers 554f9b06d5cSBrian Somers return; 555f9b06d5cSBrian Somers } 55624084f9bSBrian Somers } 55724084f9bSBrian Somers /* 55824084f9bSBrian Somers * Length might have changed during aliasing. 55924084f9bSBrian Somers */ 56024084f9bSBrian Somers bytes = ntohs (ip->ip_len); 56124084f9bSBrian Somers /* 56224084f9bSBrian Somers * Update alias overhead size for outgoing packets. 56324084f9bSBrian Somers */ 56459a7c613SBrian Somers if (direction == OUTPUT && 56524084f9bSBrian Somers bytes - origBytes > aliasOverhead) 56624084f9bSBrian Somers aliasOverhead = bytes - origBytes; 56724084f9bSBrian Somers 56824084f9bSBrian Somers if (verbose) { 56924084f9bSBrian Somers 57024084f9bSBrian Somers /* 57124084f9bSBrian Somers * Print addresses after aliasing. 57224084f9bSBrian Somers */ 57324084f9bSBrian Somers printf (" aliased to\n"); 57424084f9bSBrian Somers printf (" "); 57524084f9bSBrian Somers PrintPacket (ip); 57624084f9bSBrian Somers printf ("\n"); 57724084f9bSBrian Somers } 578fb994b07SBrian Somers 579fb994b07SBrian Somers packetLen = bytes; 580fb994b07SBrian Somers packetSock = fd; 58159a7c613SBrian Somers packetDirection = direction; 58259a7c613SBrian Somers 583fb994b07SBrian Somers FlushPacketBuffer (fd); 584fb994b07SBrian Somers } 585fb994b07SBrian Somers 586fb994b07SBrian Somers static void FlushPacketBuffer (int fd) 587fb994b07SBrian Somers { 588fb994b07SBrian Somers int wrote; 589fb994b07SBrian Somers char msgBuf[80]; 59024084f9bSBrian Somers /* 59124084f9bSBrian Somers * Put packet back for processing. 59224084f9bSBrian Somers */ 59324084f9bSBrian Somers wrote = sendto (fd, 594fb994b07SBrian Somers packetBuf, 595fb994b07SBrian Somers packetLen, 59624084f9bSBrian Somers 0, 597fb994b07SBrian Somers (struct sockaddr*) &packetAddr, 598fb994b07SBrian Somers sizeof packetAddr); 59924084f9bSBrian Somers 600fb994b07SBrian Somers if (wrote != packetLen) { 601fb994b07SBrian Somers /* 602fb994b07SBrian Somers * If buffer space is not available, 603fb994b07SBrian Somers * just return. Main loop will take care of 604fb994b07SBrian Somers * retrying send when space becomes available. 605fb994b07SBrian Somers */ 606fb994b07SBrian Somers if (errno == ENOBUFS) 607fb994b07SBrian Somers return; 60824084f9bSBrian Somers 60924084f9bSBrian Somers if (errno == EMSGSIZE) { 61024084f9bSBrian Somers 61159a7c613SBrian Somers if (packetDirection == OUTPUT && 61224084f9bSBrian Somers ifMTU != -1) 61324084f9bSBrian Somers SendNeedFragIcmp (icmpSock, 614fb994b07SBrian Somers (struct ip*) packetBuf, 61524084f9bSBrian Somers ifMTU - aliasOverhead); 61624084f9bSBrian Somers } 61724084f9bSBrian Somers else { 61824084f9bSBrian Somers 6190fc81af1SPhilippe Charnier sprintf (msgBuf, "failed to write packet back"); 62024084f9bSBrian Somers Warn (msgBuf); 62124084f9bSBrian Somers } 62224084f9bSBrian Somers } 623fb994b07SBrian Somers 624fb994b07SBrian Somers packetSock = -1; 62524084f9bSBrian Somers } 62624084f9bSBrian Somers 62724084f9bSBrian Somers static void HandleRoutingInfo (int fd) 62824084f9bSBrian Somers { 62924084f9bSBrian Somers int bytes; 63024084f9bSBrian Somers struct if_msghdr ifMsg; 63124084f9bSBrian Somers /* 63224084f9bSBrian Somers * Get packet from socket. 63324084f9bSBrian Somers */ 63424084f9bSBrian Somers bytes = read (fd, &ifMsg, sizeof ifMsg); 63524084f9bSBrian Somers if (bytes == -1) { 63624084f9bSBrian Somers 6370fc81af1SPhilippe Charnier Warn ("read from routing socket failed"); 63824084f9bSBrian Somers return; 63924084f9bSBrian Somers } 64024084f9bSBrian Somers 64124084f9bSBrian Somers if (ifMsg.ifm_version != RTM_VERSION) { 64224084f9bSBrian Somers 6430fc81af1SPhilippe Charnier Warn ("unexpected packet read from routing socket"); 64424084f9bSBrian Somers return; 64524084f9bSBrian Somers } 64624084f9bSBrian Somers 64724084f9bSBrian Somers if (verbose) 6486f3dbe5eSRuslan Ermilov printf ("Routing message %#x received.\n", ifMsg.ifm_type); 64924084f9bSBrian Somers 6506f3dbe5eSRuslan Ermilov if ((ifMsg.ifm_type == RTM_NEWADDR || ifMsg.ifm_type == RTM_IFINFO) && 6516f3dbe5eSRuslan Ermilov ifMsg.ifm_index == ifIndex) { 6526f3dbe5eSRuslan Ermilov if (verbose) 6536f3dbe5eSRuslan Ermilov printf("Interface address/MTU has probably changed.\n"); 65424084f9bSBrian Somers assignAliasAddr = 1; 65524084f9bSBrian Somers } 6566f3dbe5eSRuslan Ermilov } 65724084f9bSBrian Somers 65824084f9bSBrian Somers static void PrintPacket (struct ip* ip) 65924084f9bSBrian Somers { 66059a7c613SBrian Somers printf ("%s", FormatPacket (ip)); 66159a7c613SBrian Somers } 66259a7c613SBrian Somers 663902cb50aSBrian Somers static void SyslogPacket (struct ip* ip, int priority, const char *label) 66459a7c613SBrian Somers { 66559a7c613SBrian Somers syslog (priority, "%s %s", label, FormatPacket (ip)); 66659a7c613SBrian Somers } 66759a7c613SBrian Somers 66859a7c613SBrian Somers static char* FormatPacket (struct ip* ip) 66959a7c613SBrian Somers { 67059a7c613SBrian Somers static char buf[256]; 67124084f9bSBrian Somers struct tcphdr* tcphdr; 67259a7c613SBrian Somers struct udphdr* udphdr; 67359a7c613SBrian Somers struct icmp* icmphdr; 67459a7c613SBrian Somers char src[20]; 67559a7c613SBrian Somers char dst[20]; 67624084f9bSBrian Somers 67759a7c613SBrian Somers strcpy (src, inet_ntoa (ip->ip_src)); 67859a7c613SBrian Somers strcpy (dst, inet_ntoa (ip->ip_dst)); 67959a7c613SBrian Somers 68059a7c613SBrian Somers switch (ip->ip_p) { 68159a7c613SBrian Somers case IPPROTO_TCP: 68224084f9bSBrian Somers tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2)); 68359a7c613SBrian Somers sprintf (buf, "[TCP] %s:%d -> %s:%d", 68459a7c613SBrian Somers src, 68559a7c613SBrian Somers ntohs (tcphdr->th_sport), 68659a7c613SBrian Somers dst, 68759a7c613SBrian Somers ntohs (tcphdr->th_dport)); 68859a7c613SBrian Somers break; 68924084f9bSBrian Somers 69059a7c613SBrian Somers case IPPROTO_UDP: 69159a7c613SBrian Somers udphdr = (struct udphdr*) ((char*) ip + (ip->ip_hl << 2)); 69259a7c613SBrian Somers sprintf (buf, "[UDP] %s:%d -> %s:%d", 69359a7c613SBrian Somers src, 69459a7c613SBrian Somers ntohs (udphdr->uh_sport), 69559a7c613SBrian Somers dst, 69659a7c613SBrian Somers ntohs (udphdr->uh_dport)); 69759a7c613SBrian Somers break; 69824084f9bSBrian Somers 69959a7c613SBrian Somers case IPPROTO_ICMP: 70059a7c613SBrian Somers icmphdr = (struct icmp*) ((char*) ip + (ip->ip_hl << 2)); 701b71e869dSBrian Somers sprintf (buf, "[ICMP] %s -> %s %u(%u)", 70259a7c613SBrian Somers src, 70359a7c613SBrian Somers dst, 704b71e869dSBrian Somers icmphdr->icmp_type, 705b71e869dSBrian Somers icmphdr->icmp_code); 70659a7c613SBrian Somers break; 70759a7c613SBrian Somers 70859a7c613SBrian Somers default: 70959a7c613SBrian Somers sprintf (buf, "[%d] %s -> %s ", ip->ip_p, src, dst); 71059a7c613SBrian Somers break; 71159a7c613SBrian Somers } 71259a7c613SBrian Somers 71359a7c613SBrian Somers return buf; 71424084f9bSBrian Somers } 71524084f9bSBrian Somers 7164c04fa4cSRuslan Ermilov static void 7174c04fa4cSRuslan Ermilov SetAliasAddressFromIfName(const char *ifn) 71824084f9bSBrian Somers { 7194c04fa4cSRuslan Ermilov size_t needed; 7204c04fa4cSRuslan Ermilov int mib[6]; 7214c04fa4cSRuslan Ermilov char *buf, *lim, *next; 7224c04fa4cSRuslan Ermilov struct if_msghdr *ifm; 7234c04fa4cSRuslan Ermilov struct ifa_msghdr *ifam; 7244c04fa4cSRuslan Ermilov struct sockaddr_dl *sdl; 7254c04fa4cSRuslan Ermilov struct sockaddr_in *sin; 72624084f9bSBrian Somers 7274c04fa4cSRuslan Ermilov mib[0] = CTL_NET; 7284c04fa4cSRuslan Ermilov mib[1] = PF_ROUTE; 7294c04fa4cSRuslan Ermilov mib[2] = 0; 7304c04fa4cSRuslan Ermilov mib[3] = AF_INET; /* Only IP addresses please */ 7314c04fa4cSRuslan Ermilov mib[4] = NET_RT_IFLIST; 7324c04fa4cSRuslan Ermilov mib[5] = 0; /* ifIndex??? */ 73324084f9bSBrian Somers /* 73424084f9bSBrian Somers * Get interface data. 73524084f9bSBrian Somers */ 7364c04fa4cSRuslan Ermilov if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) 7374c04fa4cSRuslan Ermilov err(1, "iflist-sysctl-estimate"); 7384c04fa4cSRuslan Ermilov if ((buf = malloc(needed)) == NULL) 7394c04fa4cSRuslan Ermilov errx(1, "malloc failed"); 7404c04fa4cSRuslan Ermilov if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) 7414c04fa4cSRuslan Ermilov err(1, "iflist-sysctl-get"); 7424c04fa4cSRuslan Ermilov lim = buf + needed; 74324084f9bSBrian Somers /* 74424084f9bSBrian Somers * Loop through interfaces until one with 74524084f9bSBrian Somers * given name is found. This is done to 74624084f9bSBrian Somers * find correct interface index for routing 74724084f9bSBrian Somers * message processing. 74824084f9bSBrian Somers */ 7494c04fa4cSRuslan Ermilov ifIndex = 0; 7504c04fa4cSRuslan Ermilov next = buf; 7514c04fa4cSRuslan Ermilov while (next < lim) { 7524c04fa4cSRuslan Ermilov ifm = (struct if_msghdr *)next; 7534c04fa4cSRuslan Ermilov next += ifm->ifm_msglen; 7544c04fa4cSRuslan Ermilov if (ifm->ifm_version != RTM_VERSION) { 7554c04fa4cSRuslan Ermilov if (verbose) 7564c04fa4cSRuslan Ermilov warnx("routing message version %d " 7574c04fa4cSRuslan Ermilov "not understood", ifm->ifm_version); 7584c04fa4cSRuslan Ermilov continue; 7594c04fa4cSRuslan Ermilov } 7604c04fa4cSRuslan Ermilov if (ifm->ifm_type == RTM_IFINFO) { 7614c04fa4cSRuslan Ermilov sdl = (struct sockaddr_dl *)(ifm + 1); 7624c04fa4cSRuslan Ermilov if (strlen(ifn) == sdl->sdl_nlen && 7634c04fa4cSRuslan Ermilov strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) { 7644c04fa4cSRuslan Ermilov ifIndex = ifm->ifm_index; 7654c04fa4cSRuslan Ermilov ifMTU = ifm->ifm_data.ifi_mtu; 76624084f9bSBrian Somers break; 76724084f9bSBrian Somers } 76824084f9bSBrian Somers } 76924084f9bSBrian Somers } 7704c04fa4cSRuslan Ermilov if (!ifIndex) 7714c04fa4cSRuslan Ermilov errx(1, "unknown interface name %s", ifn); 77224084f9bSBrian Somers /* 77324084f9bSBrian Somers * Get interface address. 77424084f9bSBrian Somers */ 7754c04fa4cSRuslan Ermilov sin = NULL; 7764c04fa4cSRuslan Ermilov while (next < lim) { 7774c04fa4cSRuslan Ermilov ifam = (struct ifa_msghdr *)next; 7784c04fa4cSRuslan Ermilov next += ifam->ifam_msglen; 7794c04fa4cSRuslan Ermilov if (ifam->ifam_version != RTM_VERSION) { 7804c04fa4cSRuslan Ermilov if (verbose) 7814c04fa4cSRuslan Ermilov warnx("routing message version %d " 7824c04fa4cSRuslan Ermilov "not understood", ifam->ifam_version); 7834c04fa4cSRuslan Ermilov continue; 7844c04fa4cSRuslan Ermilov } 7854c04fa4cSRuslan Ermilov if (ifam->ifam_type != RTM_NEWADDR) 7864c04fa4cSRuslan Ermilov break; 7874c04fa4cSRuslan Ermilov if (ifam->ifam_addrs & RTA_IFA) { 7884c04fa4cSRuslan Ermilov int i; 7894c04fa4cSRuslan Ermilov char *cp = (char *)(ifam + 1); 79024084f9bSBrian Somers 7914c04fa4cSRuslan Ermilov #define ROUNDUP(a) \ 7924c04fa4cSRuslan Ermilov ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 7934c04fa4cSRuslan Ermilov #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 7944c04fa4cSRuslan Ermilov 7954c04fa4cSRuslan Ermilov for (i = 1; i < RTA_IFA; i <<= 1) 7964c04fa4cSRuslan Ermilov if (ifam->ifam_addrs & i) 7974c04fa4cSRuslan Ermilov ADVANCE(cp, (struct sockaddr *)cp); 7984c04fa4cSRuslan Ermilov if (((struct sockaddr *)cp)->sa_family == AF_INET) { 7994c04fa4cSRuslan Ermilov sin = (struct sockaddr_in *)cp; 8004c04fa4cSRuslan Ermilov break; 8014c04fa4cSRuslan Ermilov } 8024c04fa4cSRuslan Ermilov } 8034c04fa4cSRuslan Ermilov } 8044c04fa4cSRuslan Ermilov if (sin == NULL) 8054c04fa4cSRuslan Ermilov errx(1, "%s: cannot get interface address", ifn); 8064c04fa4cSRuslan Ermilov 8074c04fa4cSRuslan Ermilov PacketAliasSetAddress(sin->sin_addr); 80824084f9bSBrian Somers syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes", 8094c04fa4cSRuslan Ermilov inet_ntoa(sin->sin_addr), ifMTU); 81024084f9bSBrian Somers 8114c04fa4cSRuslan Ermilov free(buf); 81224084f9bSBrian Somers } 81324084f9bSBrian Somers 814902cb50aSBrian Somers void Quit (const char* msg) 81524084f9bSBrian Somers { 81624084f9bSBrian Somers Warn (msg); 81724084f9bSBrian Somers exit (1); 81824084f9bSBrian Somers } 81924084f9bSBrian Somers 820902cb50aSBrian Somers void Warn (const char* msg) 82124084f9bSBrian Somers { 82224084f9bSBrian Somers if (background) 82324084f9bSBrian Somers syslog (LOG_ALERT, "%s (%m)", msg); 82424084f9bSBrian Somers else 8250fc81af1SPhilippe Charnier warn (msg); 82624084f9bSBrian Somers } 82724084f9bSBrian Somers 828902cb50aSBrian Somers static void RefreshAddr (int sig) 82924084f9bSBrian Somers { 83024084f9bSBrian Somers if (ifName) 83124084f9bSBrian Somers assignAliasAddr = 1; 83224084f9bSBrian Somers } 83324084f9bSBrian Somers 834902cb50aSBrian Somers static void InitiateShutdown (int sig) 83524084f9bSBrian Somers { 83624084f9bSBrian Somers /* 83724084f9bSBrian Somers * Start timer to allow kernel gracefully 83824084f9bSBrian Somers * shutdown existing connections when system 83924084f9bSBrian Somers * is shut down. 84024084f9bSBrian Somers */ 841cd45c931SRuslan Ermilov siginterrupt(SIGALRM, 1); 84224084f9bSBrian Somers signal (SIGALRM, Shutdown); 84324084f9bSBrian Somers alarm (10); 84424084f9bSBrian Somers } 84524084f9bSBrian Somers 846902cb50aSBrian Somers static void Shutdown (int sig) 84724084f9bSBrian Somers { 84824084f9bSBrian Somers running = 0; 84924084f9bSBrian Somers } 85024084f9bSBrian Somers 85124084f9bSBrian Somers /* 85224084f9bSBrian Somers * Different options recognized by this program. 85324084f9bSBrian Somers */ 85424084f9bSBrian Somers 85524084f9bSBrian Somers enum Option { 85624084f9bSBrian Somers 85724084f9bSBrian Somers PacketAliasOption, 85824084f9bSBrian Somers Verbose, 85924084f9bSBrian Somers InPort, 86024084f9bSBrian Somers OutPort, 86124084f9bSBrian Somers Port, 86224084f9bSBrian Somers AliasAddress, 86311c2b3bfSRuslan Ermilov TargetAddress, 86424084f9bSBrian Somers InterfaceName, 86524084f9bSBrian Somers RedirectPort, 8664330006dSRuslan Ermilov RedirectProto, 86724084f9bSBrian Somers RedirectAddress, 86824084f9bSBrian Somers ConfigFile, 86959a7c613SBrian Somers DynamicMode, 87059a7c613SBrian Somers ProxyRule, 87159a7c613SBrian Somers LogDenied, 872bc4ebb98SRuslan Ermilov LogFacility, 873bc4ebb98SRuslan Ermilov PunchFW 87424084f9bSBrian Somers }; 87524084f9bSBrian Somers 87624084f9bSBrian Somers enum Param { 87724084f9bSBrian Somers 87824084f9bSBrian Somers YesNo, 87924084f9bSBrian Somers Numeric, 88024084f9bSBrian Somers String, 88124084f9bSBrian Somers None, 88224084f9bSBrian Somers Address, 88324084f9bSBrian Somers Service 88424084f9bSBrian Somers }; 88524084f9bSBrian Somers 88624084f9bSBrian Somers /* 88724084f9bSBrian Somers * Option information structure (used by ParseOption). 88824084f9bSBrian Somers */ 88924084f9bSBrian Somers 89024084f9bSBrian Somers struct OptionInfo { 89124084f9bSBrian Somers 89224084f9bSBrian Somers enum Option type; 89324084f9bSBrian Somers int packetAliasOpt; 89424084f9bSBrian Somers enum Param parm; 895902cb50aSBrian Somers const char* parmDescription; 896902cb50aSBrian Somers const char* description; 897902cb50aSBrian Somers const char* name; 898902cb50aSBrian Somers const char* shortName; 89924084f9bSBrian Somers }; 90024084f9bSBrian Somers 90124084f9bSBrian Somers /* 90224084f9bSBrian Somers * Table of known options. 90324084f9bSBrian Somers */ 90424084f9bSBrian Somers 90524084f9bSBrian Somers static struct OptionInfo optionTable[] = { 90624084f9bSBrian Somers 90724084f9bSBrian Somers { PacketAliasOption, 90824084f9bSBrian Somers PKT_ALIAS_UNREGISTERED_ONLY, 90924084f9bSBrian Somers YesNo, 91024084f9bSBrian Somers "[yes|no]", 91124084f9bSBrian Somers "alias only unregistered addresses", 91224084f9bSBrian Somers "unregistered_only", 91324084f9bSBrian Somers "u" }, 91424084f9bSBrian Somers 91524084f9bSBrian Somers { PacketAliasOption, 91624084f9bSBrian Somers PKT_ALIAS_LOG, 91724084f9bSBrian Somers YesNo, 91824084f9bSBrian Somers "[yes|no]", 91924084f9bSBrian Somers "enable logging", 92024084f9bSBrian Somers "log", 92124084f9bSBrian Somers "l" }, 92224084f9bSBrian Somers 92324084f9bSBrian Somers { PacketAliasOption, 92459a7c613SBrian Somers PKT_ALIAS_PROXY_ONLY, 92559a7c613SBrian Somers YesNo, 92659a7c613SBrian Somers "[yes|no]", 92759a7c613SBrian Somers "proxy only", 92859a7c613SBrian Somers "proxy_only", 92959a7c613SBrian Somers NULL }, 93059a7c613SBrian Somers 93159a7c613SBrian Somers { PacketAliasOption, 93259a7c613SBrian Somers PKT_ALIAS_REVERSE, 93359a7c613SBrian Somers YesNo, 93459a7c613SBrian Somers "[yes|no]", 93559a7c613SBrian Somers "operate in reverse mode", 93659a7c613SBrian Somers "reverse", 93759a7c613SBrian Somers NULL }, 93859a7c613SBrian Somers 93959a7c613SBrian Somers { PacketAliasOption, 94024084f9bSBrian Somers PKT_ALIAS_DENY_INCOMING, 94124084f9bSBrian Somers YesNo, 94224084f9bSBrian Somers "[yes|no]", 94324084f9bSBrian Somers "allow incoming connections", 94424084f9bSBrian Somers "deny_incoming", 94524084f9bSBrian Somers "d" }, 94624084f9bSBrian Somers 94724084f9bSBrian Somers { PacketAliasOption, 94824084f9bSBrian Somers PKT_ALIAS_USE_SOCKETS, 94924084f9bSBrian Somers YesNo, 95024084f9bSBrian Somers "[yes|no]", 95124084f9bSBrian Somers "use sockets to inhibit port conflict", 95224084f9bSBrian Somers "use_sockets", 95324084f9bSBrian Somers "s" }, 95424084f9bSBrian Somers 95524084f9bSBrian Somers { PacketAliasOption, 95624084f9bSBrian Somers PKT_ALIAS_SAME_PORTS, 95724084f9bSBrian Somers YesNo, 95824084f9bSBrian Somers "[yes|no]", 95924084f9bSBrian Somers "try to keep original port numbers for connections", 96024084f9bSBrian Somers "same_ports", 96124084f9bSBrian Somers "m" }, 96224084f9bSBrian Somers 96324084f9bSBrian Somers { Verbose, 96424084f9bSBrian Somers 0, 96524084f9bSBrian Somers YesNo, 96624084f9bSBrian Somers "[yes|no]", 96724084f9bSBrian Somers "verbose mode, dump packet information", 96824084f9bSBrian Somers "verbose", 96924084f9bSBrian Somers "v" }, 97024084f9bSBrian Somers 97124084f9bSBrian Somers { DynamicMode, 97224084f9bSBrian Somers 0, 97324084f9bSBrian Somers YesNo, 97424084f9bSBrian Somers "[yes|no]", 97524084f9bSBrian Somers "dynamic mode, automatically detect interface address changes", 97624084f9bSBrian Somers "dynamic", 97724084f9bSBrian Somers NULL }, 97824084f9bSBrian Somers 97924084f9bSBrian Somers { InPort, 98024084f9bSBrian Somers 0, 98124084f9bSBrian Somers Service, 98224084f9bSBrian Somers "number|service_name", 98324084f9bSBrian Somers "set port for incoming packets", 98424084f9bSBrian Somers "in_port", 98524084f9bSBrian Somers "i" }, 98624084f9bSBrian Somers 98724084f9bSBrian Somers { OutPort, 98824084f9bSBrian Somers 0, 98924084f9bSBrian Somers Service, 99024084f9bSBrian Somers "number|service_name", 99124084f9bSBrian Somers "set port for outgoing packets", 99224084f9bSBrian Somers "out_port", 99324084f9bSBrian Somers "o" }, 99424084f9bSBrian Somers 99524084f9bSBrian Somers { Port, 99624084f9bSBrian Somers 0, 99724084f9bSBrian Somers Service, 99824084f9bSBrian Somers "number|service_name", 99924084f9bSBrian Somers "set port (defaults to natd/divert)", 100024084f9bSBrian Somers "port", 100124084f9bSBrian Somers "p" }, 100224084f9bSBrian Somers 100324084f9bSBrian Somers { AliasAddress, 100424084f9bSBrian Somers 0, 100524084f9bSBrian Somers Address, 100624084f9bSBrian Somers "x.x.x.x", 100724084f9bSBrian Somers "address to use for aliasing", 100824084f9bSBrian Somers "alias_address", 100924084f9bSBrian Somers "a" }, 101024084f9bSBrian Somers 101111c2b3bfSRuslan Ermilov { TargetAddress, 101211c2b3bfSRuslan Ermilov 0, 101311c2b3bfSRuslan Ermilov Address, 101411c2b3bfSRuslan Ermilov "x.x.x.x", 101511c2b3bfSRuslan Ermilov "address to use for incoming sessions", 101611c2b3bfSRuslan Ermilov "target_address", 101711c2b3bfSRuslan Ermilov "t" }, 101811c2b3bfSRuslan Ermilov 101924084f9bSBrian Somers { InterfaceName, 102024084f9bSBrian Somers 0, 102124084f9bSBrian Somers String, 102224084f9bSBrian Somers "network_if_name", 102324084f9bSBrian Somers "take aliasing address from interface", 102424084f9bSBrian Somers "interface", 102524084f9bSBrian Somers "n" }, 102624084f9bSBrian Somers 102759a7c613SBrian Somers { ProxyRule, 102824084f9bSBrian Somers 0, 102924084f9bSBrian Somers String, 103059a7c613SBrian Somers "[type encode_ip_hdr|encode_tcp_stream] port xxxx server " 103159a7c613SBrian Somers "a.b.c.d:yyyy", 103259a7c613SBrian Somers "add transparent proxying / destination NAT", 103359a7c613SBrian Somers "proxy_rule", 103424084f9bSBrian Somers NULL }, 103524084f9bSBrian Somers 103624084f9bSBrian Somers { RedirectPort, 103724084f9bSBrian Somers 0, 103824084f9bSBrian Somers String, 1039bd690510SRuslan Ermilov "tcp|udp local_addr:local_port_range[,...] [public_addr:]public_port_range" 10405d8ee958SBrian Somers " [remote_addr[:remote_port_range]]", 10415d8ee958SBrian Somers "redirect a port (or ports) for incoming traffic", 104224084f9bSBrian Somers "redirect_port", 104324084f9bSBrian Somers NULL }, 104424084f9bSBrian Somers 10454330006dSRuslan Ermilov { RedirectProto, 10464330006dSRuslan Ermilov 0, 10474330006dSRuslan Ermilov String, 10484330006dSRuslan Ermilov "proto local_addr [public_addr] [remote_addr]", 10494330006dSRuslan Ermilov "redirect packets of a given proto", 10504330006dSRuslan Ermilov "redirect_proto", 10514330006dSRuslan Ermilov NULL }, 10524330006dSRuslan Ermilov 105324084f9bSBrian Somers { RedirectAddress, 105424084f9bSBrian Somers 0, 105524084f9bSBrian Somers String, 1056bd690510SRuslan Ermilov "local_addr[,...] public_addr", 105724084f9bSBrian Somers "define mapping between local and public addresses", 105824084f9bSBrian Somers "redirect_address", 105924084f9bSBrian Somers NULL }, 106024084f9bSBrian Somers 106124084f9bSBrian Somers { ConfigFile, 106224084f9bSBrian Somers 0, 106324084f9bSBrian Somers String, 106424084f9bSBrian Somers "file_name", 106524084f9bSBrian Somers "read options from configuration file", 106624084f9bSBrian Somers "config", 106759a7c613SBrian Somers "f" }, 106859a7c613SBrian Somers 106959a7c613SBrian Somers { LogDenied, 107059a7c613SBrian Somers 0, 107159a7c613SBrian Somers YesNo, 107259a7c613SBrian Somers "[yes|no]", 107359a7c613SBrian Somers "enable logging of denied incoming packets", 107459a7c613SBrian Somers "log_denied", 107559a7c613SBrian Somers NULL }, 107659a7c613SBrian Somers 107759a7c613SBrian Somers { LogFacility, 107859a7c613SBrian Somers 0, 107959a7c613SBrian Somers String, 108059a7c613SBrian Somers "facility", 108159a7c613SBrian Somers "name of syslog facility to use for logging", 108259a7c613SBrian Somers "log_facility", 1083bc4ebb98SRuslan Ermilov NULL }, 108459a7c613SBrian Somers 1085bc4ebb98SRuslan Ermilov { PunchFW, 1086bc4ebb98SRuslan Ermilov 0, 1087bc4ebb98SRuslan Ermilov String, 1088bc4ebb98SRuslan Ermilov "basenumber:count", 1089bc4ebb98SRuslan Ermilov "punch holes in the firewall for incoming FTP/IRC DCC connections", 1090bc4ebb98SRuslan Ermilov "punch_fw", 1091bc4ebb98SRuslan Ermilov NULL } 109224084f9bSBrian Somers }; 109324084f9bSBrian Somers 1094b0f55af6SRuslan Ermilov static void ParseOption (const char* option, const char* parms) 109524084f9bSBrian Somers { 109624084f9bSBrian Somers int i; 109724084f9bSBrian Somers struct OptionInfo* info; 109824084f9bSBrian Somers int yesNoValue; 109924084f9bSBrian Somers int aliasValue; 110024084f9bSBrian Somers int numValue; 110167a886fbSBrian Somers u_short uNumValue; 1102902cb50aSBrian Somers const char* strValue; 110324084f9bSBrian Somers struct in_addr addrValue; 110424084f9bSBrian Somers int max; 110524084f9bSBrian Somers char* end; 110659a7c613SBrian Somers CODE* fac_record = NULL; 110724084f9bSBrian Somers /* 110824084f9bSBrian Somers * Find option from table. 110924084f9bSBrian Somers */ 111024084f9bSBrian Somers max = sizeof (optionTable) / sizeof (struct OptionInfo); 111124084f9bSBrian Somers for (i = 0, info = optionTable; i < max; i++, info++) { 111224084f9bSBrian Somers 111324084f9bSBrian Somers if (!strcmp (info->name, option)) 111424084f9bSBrian Somers break; 111524084f9bSBrian Somers 111624084f9bSBrian Somers if (info->shortName) 111724084f9bSBrian Somers if (!strcmp (info->shortName, option)) 111824084f9bSBrian Somers break; 111924084f9bSBrian Somers } 112024084f9bSBrian Somers 112124084f9bSBrian Somers if (i >= max) { 112224084f9bSBrian Somers 11230fc81af1SPhilippe Charnier warnx ("unknown option %s", option); 112424084f9bSBrian Somers Usage (); 112524084f9bSBrian Somers } 112624084f9bSBrian Somers 112767a886fbSBrian Somers uNumValue = 0; 112824084f9bSBrian Somers yesNoValue = 0; 112924084f9bSBrian Somers numValue = 0; 113024084f9bSBrian Somers strValue = NULL; 113124084f9bSBrian Somers /* 113224084f9bSBrian Somers * Check parameters. 113324084f9bSBrian Somers */ 113424084f9bSBrian Somers switch (info->parm) { 113524084f9bSBrian Somers case YesNo: 113624084f9bSBrian Somers if (!parms) 113724084f9bSBrian Somers parms = "yes"; 113824084f9bSBrian Somers 113924084f9bSBrian Somers if (!strcmp (parms, "yes")) 114024084f9bSBrian Somers yesNoValue = 1; 114124084f9bSBrian Somers else 114224084f9bSBrian Somers if (!strcmp (parms, "no")) 114324084f9bSBrian Somers yesNoValue = 0; 11440fc81af1SPhilippe Charnier else 11450fc81af1SPhilippe Charnier errx (1, "%s needs yes/no parameter", option); 114624084f9bSBrian Somers break; 114724084f9bSBrian Somers 114824084f9bSBrian Somers case Service: 11490fc81af1SPhilippe Charnier if (!parms) 115067a886fbSBrian Somers errx (1, "%s needs service name or " 115167a886fbSBrian Somers "port number parameter", 115267a886fbSBrian Somers option); 115324084f9bSBrian Somers 115467a886fbSBrian Somers uNumValue = StrToPort (parms, "divert"); 115524084f9bSBrian Somers break; 115624084f9bSBrian Somers 115724084f9bSBrian Somers case Numeric: 115824084f9bSBrian Somers if (parms) 115924084f9bSBrian Somers numValue = strtol (parms, &end, 10); 116024084f9bSBrian Somers else 1161902cb50aSBrian Somers end = NULL; 116224084f9bSBrian Somers 11630fc81af1SPhilippe Charnier if (end == parms) 11640fc81af1SPhilippe Charnier errx (1, "%s needs numeric parameter", option); 116524084f9bSBrian Somers break; 116624084f9bSBrian Somers 116724084f9bSBrian Somers case String: 116824084f9bSBrian Somers strValue = parms; 11690fc81af1SPhilippe Charnier if (!strValue) 11700fc81af1SPhilippe Charnier errx (1, "%s needs parameter", option); 117124084f9bSBrian Somers break; 117224084f9bSBrian Somers 117324084f9bSBrian Somers case None: 11740fc81af1SPhilippe Charnier if (parms) 11750fc81af1SPhilippe Charnier errx (1, "%s does not take parameters", option); 117624084f9bSBrian Somers break; 117724084f9bSBrian Somers 117824084f9bSBrian Somers case Address: 11790fc81af1SPhilippe Charnier if (!parms) 11800fc81af1SPhilippe Charnier errx (1, "%s needs address/host parameter", option); 118124084f9bSBrian Somers 118224084f9bSBrian Somers StrToAddr (parms, &addrValue); 118324084f9bSBrian Somers break; 118424084f9bSBrian Somers } 118524084f9bSBrian Somers 118624084f9bSBrian Somers switch (info->type) { 118724084f9bSBrian Somers case PacketAliasOption: 118824084f9bSBrian Somers 118924084f9bSBrian Somers aliasValue = yesNoValue ? info->packetAliasOpt : 0; 1190fb994b07SBrian Somers PacketAliasSetMode (aliasValue, info->packetAliasOpt); 119124084f9bSBrian Somers break; 119224084f9bSBrian Somers 119324084f9bSBrian Somers case Verbose: 119424084f9bSBrian Somers verbose = yesNoValue; 119524084f9bSBrian Somers break; 119624084f9bSBrian Somers 119724084f9bSBrian Somers case DynamicMode: 119824084f9bSBrian Somers dynamicMode = yesNoValue; 119924084f9bSBrian Somers break; 120024084f9bSBrian Somers 120124084f9bSBrian Somers case InPort: 120267a886fbSBrian Somers inPort = uNumValue; 120324084f9bSBrian Somers break; 120424084f9bSBrian Somers 120524084f9bSBrian Somers case OutPort: 120667a886fbSBrian Somers outPort = uNumValue; 120724084f9bSBrian Somers break; 120824084f9bSBrian Somers 120924084f9bSBrian Somers case Port: 121067a886fbSBrian Somers inOutPort = uNumValue; 121124084f9bSBrian Somers break; 121224084f9bSBrian Somers 121324084f9bSBrian Somers case AliasAddress: 121424084f9bSBrian Somers memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr)); 121524084f9bSBrian Somers break; 121624084f9bSBrian Somers 121711c2b3bfSRuslan Ermilov case TargetAddress: 121811c2b3bfSRuslan Ermilov PacketAliasSetTarget(addrValue); 121911c2b3bfSRuslan Ermilov break; 122011c2b3bfSRuslan Ermilov 122124084f9bSBrian Somers case RedirectPort: 122224084f9bSBrian Somers SetupPortRedirect (strValue); 122324084f9bSBrian Somers break; 122424084f9bSBrian Somers 12254330006dSRuslan Ermilov case RedirectProto: 12264330006dSRuslan Ermilov SetupProtoRedirect(strValue); 12274330006dSRuslan Ermilov break; 12284330006dSRuslan Ermilov 122924084f9bSBrian Somers case RedirectAddress: 123024084f9bSBrian Somers SetupAddressRedirect (strValue); 123124084f9bSBrian Somers break; 123224084f9bSBrian Somers 123359a7c613SBrian Somers case ProxyRule: 123459a7c613SBrian Somers PacketAliasProxyRule (strValue); 123559a7c613SBrian Somers break; 123659a7c613SBrian Somers 123724084f9bSBrian Somers case InterfaceName: 123824084f9bSBrian Somers if (ifName) 123924084f9bSBrian Somers free (ifName); 124024084f9bSBrian Somers 124124084f9bSBrian Somers ifName = strdup (strValue); 124224084f9bSBrian Somers break; 124324084f9bSBrian Somers 124424084f9bSBrian Somers case ConfigFile: 124524084f9bSBrian Somers ReadConfigFile (strValue); 124624084f9bSBrian Somers break; 124759a7c613SBrian Somers 124859a7c613SBrian Somers case LogDenied: 124959a7c613SBrian Somers logDropped = 1; 125059a7c613SBrian Somers break; 125159a7c613SBrian Somers 125259a7c613SBrian Somers case LogFacility: 125359a7c613SBrian Somers 125459a7c613SBrian Somers fac_record = facilitynames; 125559a7c613SBrian Somers while (fac_record->c_name != NULL) { 125659a7c613SBrian Somers 125759a7c613SBrian Somers if (!strcmp (fac_record->c_name, strValue)) { 125859a7c613SBrian Somers 125959a7c613SBrian Somers logFacility = fac_record->c_val; 126059a7c613SBrian Somers break; 126159a7c613SBrian Somers 126259a7c613SBrian Somers } 126359a7c613SBrian Somers else 126459a7c613SBrian Somers fac_record++; 126559a7c613SBrian Somers } 126659a7c613SBrian Somers 126759a7c613SBrian Somers if(fac_record->c_name == NULL) 126859a7c613SBrian Somers errx(1, "Unknown log facility name: %s", strValue); 126959a7c613SBrian Somers 127059a7c613SBrian Somers break; 1271bc4ebb98SRuslan Ermilov 1272bc4ebb98SRuslan Ermilov case PunchFW: 1273bc4ebb98SRuslan Ermilov SetupPunchFW(strValue); 1274bc4ebb98SRuslan Ermilov break; 127524084f9bSBrian Somers } 127624084f9bSBrian Somers } 127724084f9bSBrian Somers 1278902cb50aSBrian Somers void ReadConfigFile (const char* fileName) 127924084f9bSBrian Somers { 128024084f9bSBrian Somers FILE* file; 1281d99cc1daSRuslan Ermilov char *buf; 1282d99cc1daSRuslan Ermilov size_t len; 12832e7e7c71SRuslan Ermilov char *ptr, *p; 128424084f9bSBrian Somers char* option; 128524084f9bSBrian Somers 128624084f9bSBrian Somers file = fopen (fileName, "r"); 1287d99cc1daSRuslan Ermilov if (!file) 1288d99cc1daSRuslan Ermilov err(1, "cannot open config file %s", fileName); 128924084f9bSBrian Somers 1290d99cc1daSRuslan Ermilov while ((buf = fgetln(file, &len)) != NULL) { 1291d99cc1daSRuslan Ermilov if (buf[len - 1] == '\n') 1292d99cc1daSRuslan Ermilov buf[len - 1] = '\0'; 1293d99cc1daSRuslan Ermilov else 1294d99cc1daSRuslan Ermilov errx(1, "config file format error: " 1295d99cc1daSRuslan Ermilov "last line should end with newline"); 129624084f9bSBrian Somers 129724084f9bSBrian Somers /* 12982e7e7c71SRuslan Ermilov * Check for comments, strip off trailing spaces. 129924084f9bSBrian Somers */ 13002e7e7c71SRuslan Ermilov if ((ptr = strchr(buf, '#'))) 13012e7e7c71SRuslan Ermilov *ptr = '\0'; 13022e7e7c71SRuslan Ermilov for (ptr = buf; isspace(*ptr); ++ptr) 13032e7e7c71SRuslan Ermilov continue; 130424084f9bSBrian Somers if (*ptr == '\0') 130524084f9bSBrian Somers continue; 13062e7e7c71SRuslan Ermilov for (p = strchr(buf, '\0'); isspace(*--p);) 13072e7e7c71SRuslan Ermilov continue; 13082e7e7c71SRuslan Ermilov *++p = '\0'; 13092e7e7c71SRuslan Ermilov 131024084f9bSBrian Somers /* 131124084f9bSBrian Somers * Extract option name. 131224084f9bSBrian Somers */ 131324084f9bSBrian Somers option = ptr; 131424084f9bSBrian Somers while (*ptr && !isspace (*ptr)) 131524084f9bSBrian Somers ++ptr; 131624084f9bSBrian Somers 131724084f9bSBrian Somers if (*ptr != '\0') { 131824084f9bSBrian Somers 131924084f9bSBrian Somers *ptr = '\0'; 132024084f9bSBrian Somers ++ptr; 132124084f9bSBrian Somers } 132224084f9bSBrian Somers /* 132324084f9bSBrian Somers * Skip white space between name and parms. 132424084f9bSBrian Somers */ 132524084f9bSBrian Somers while (*ptr && isspace (*ptr)) 132624084f9bSBrian Somers ++ptr; 132724084f9bSBrian Somers 1328b0f55af6SRuslan Ermilov ParseOption (option, *ptr ? ptr : NULL); 132924084f9bSBrian Somers } 133024084f9bSBrian Somers 133124084f9bSBrian Somers fclose (file); 133224084f9bSBrian Somers } 133324084f9bSBrian Somers 133424084f9bSBrian Somers static void Usage () 133524084f9bSBrian Somers { 133624084f9bSBrian Somers int i; 133724084f9bSBrian Somers int max; 133824084f9bSBrian Somers struct OptionInfo* info; 133924084f9bSBrian Somers 134024084f9bSBrian Somers fprintf (stderr, "Recognized options:\n\n"); 134124084f9bSBrian Somers 134224084f9bSBrian Somers max = sizeof (optionTable) / sizeof (struct OptionInfo); 134324084f9bSBrian Somers for (i = 0, info = optionTable; i < max; i++, info++) { 134424084f9bSBrian Somers 134524084f9bSBrian Somers fprintf (stderr, "-%-20s %s\n", info->name, 134624084f9bSBrian Somers info->parmDescription); 134724084f9bSBrian Somers 134824084f9bSBrian Somers if (info->shortName) 134924084f9bSBrian Somers fprintf (stderr, "-%-20s %s\n", info->shortName, 135024084f9bSBrian Somers info->parmDescription); 135124084f9bSBrian Somers 135224084f9bSBrian Somers fprintf (stderr, " %s\n\n", info->description); 135324084f9bSBrian Somers } 135424084f9bSBrian Somers 135524084f9bSBrian Somers exit (1); 135624084f9bSBrian Somers } 135724084f9bSBrian Somers 1358902cb50aSBrian Somers void SetupPortRedirect (const char* parms) 135924084f9bSBrian Somers { 136024084f9bSBrian Somers char buf[128]; 136124084f9bSBrian Somers char* ptr; 1362bd690510SRuslan Ermilov char* serverPool; 136324084f9bSBrian Somers struct in_addr localAddr; 136424084f9bSBrian Somers struct in_addr publicAddr; 136524084f9bSBrian Somers struct in_addr remoteAddr; 13665d8ee958SBrian Somers port_range portRange; 13675d8ee958SBrian Somers u_short localPort = 0; 13685d8ee958SBrian Somers u_short publicPort = 0; 13695d8ee958SBrian Somers u_short remotePort = 0; 13705d8ee958SBrian Somers u_short numLocalPorts = 0; 13715d8ee958SBrian Somers u_short numPublicPorts = 0; 13725d8ee958SBrian Somers u_short numRemotePorts = 0; 137324084f9bSBrian Somers int proto; 137424084f9bSBrian Somers char* protoName; 137524084f9bSBrian Somers char* separator; 13765d8ee958SBrian Somers int i; 1377bd690510SRuslan Ermilov struct alias_link *link = NULL; 137824084f9bSBrian Somers 137924084f9bSBrian Somers strcpy (buf, parms); 138024084f9bSBrian Somers /* 138124084f9bSBrian Somers * Extract protocol. 138224084f9bSBrian Somers */ 138324084f9bSBrian Somers protoName = strtok (buf, " \t"); 13840fc81af1SPhilippe Charnier if (!protoName) 13850fc81af1SPhilippe Charnier errx (1, "redirect_port: missing protocol"); 138624084f9bSBrian Somers 138724084f9bSBrian Somers proto = StrToProto (protoName); 138824084f9bSBrian Somers /* 138924084f9bSBrian Somers * Extract local address. 139024084f9bSBrian Somers */ 139124084f9bSBrian Somers ptr = strtok (NULL, " \t"); 13920fc81af1SPhilippe Charnier if (!ptr) 13930fc81af1SPhilippe Charnier errx (1, "redirect_port: missing local address"); 139424084f9bSBrian Somers 1395bd690510SRuslan Ermilov separator = strchr(ptr, ','); 1396bd690510SRuslan Ermilov if (separator) { /* LSNAT redirection syntax. */ 1397bd690510SRuslan Ermilov localAddr.s_addr = INADDR_NONE; 1398bd690510SRuslan Ermilov localPort = ~0; 1399bd690510SRuslan Ermilov numLocalPorts = 1; 1400bd690510SRuslan Ermilov serverPool = ptr; 1401bd690510SRuslan Ermilov } else { 14025d8ee958SBrian Somers if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 ) 14035d8ee958SBrian Somers errx (1, "redirect_port: invalid local port range"); 14045d8ee958SBrian Somers 14055d8ee958SBrian Somers localPort = GETLOPORT(portRange); 14065d8ee958SBrian Somers numLocalPorts = GETNUMPORTS(portRange); 1407bd690510SRuslan Ermilov serverPool = NULL; 1408bd690510SRuslan Ermilov } 14095d8ee958SBrian Somers 141024084f9bSBrian Somers /* 14119c501140SBrian Somers * Extract public port and optionally address. 141224084f9bSBrian Somers */ 141324084f9bSBrian Somers ptr = strtok (NULL, " \t"); 14140fc81af1SPhilippe Charnier if (!ptr) 14150fc81af1SPhilippe Charnier errx (1, "redirect_port: missing public port"); 141624084f9bSBrian Somers 141724084f9bSBrian Somers separator = strchr (ptr, ':'); 14185d8ee958SBrian Somers if (separator) { 14195d8ee958SBrian Somers if (StrToAddrAndPortRange (ptr, &publicAddr, protoName, &portRange) != 0 ) 14205d8ee958SBrian Somers errx (1, "redirect_port: invalid public port range"); 142124084f9bSBrian Somers } 14225d8ee958SBrian Somers else { 14235d8ee958SBrian Somers publicAddr.s_addr = INADDR_ANY; 14245d8ee958SBrian Somers if (StrToPortRange (ptr, protoName, &portRange) != 0) 14255d8ee958SBrian Somers errx (1, "redirect_port: invalid public port range"); 14265d8ee958SBrian Somers } 14275d8ee958SBrian Somers 14285d8ee958SBrian Somers publicPort = GETLOPORT(portRange); 14295d8ee958SBrian Somers numPublicPorts = GETNUMPORTS(portRange); 143024084f9bSBrian Somers 143124084f9bSBrian Somers /* 143224084f9bSBrian Somers * Extract remote address and optionally port. 143324084f9bSBrian Somers */ 143424084f9bSBrian Somers ptr = strtok (NULL, " \t"); 143524084f9bSBrian Somers if (ptr) { 143624084f9bSBrian Somers separator = strchr (ptr, ':'); 1437ebe70c8fSWarner Losh if (separator) { 14385d8ee958SBrian Somers if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0) 14395d8ee958SBrian Somers errx (1, "redirect_port: invalid remote port range"); 1440ebe70c8fSWarner Losh } else { 14415d8ee958SBrian Somers SETLOPORT(portRange, 0); 14425d8ee958SBrian Somers SETNUMPORTS(portRange, 1); 144324084f9bSBrian Somers StrToAddr (ptr, &remoteAddr); 144424084f9bSBrian Somers } 144524084f9bSBrian Somers } 144624084f9bSBrian Somers else { 14475d8ee958SBrian Somers SETLOPORT(portRange, 0); 14485d8ee958SBrian Somers SETNUMPORTS(portRange, 1); 144924084f9bSBrian Somers remoteAddr.s_addr = INADDR_ANY; 145024084f9bSBrian Somers } 145124084f9bSBrian Somers 14525d8ee958SBrian Somers remotePort = GETLOPORT(portRange); 14535d8ee958SBrian Somers numRemotePorts = GETNUMPORTS(portRange); 14545d8ee958SBrian Somers 14555d8ee958SBrian Somers /* 14565d8ee958SBrian Somers * Make sure port ranges match up, then add the redirect ports. 14575d8ee958SBrian Somers */ 14585d8ee958SBrian Somers if (numLocalPorts != numPublicPorts) 14595d8ee958SBrian Somers errx (1, "redirect_port: port ranges must be equal in size"); 14605d8ee958SBrian Somers 14615d8ee958SBrian Somers /* Remote port range is allowed to be '0' which means all ports. */ 146229d97436SBrian Somers if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0)) 14635d8ee958SBrian Somers errx (1, "redirect_port: remote port must be 0 or equal to local port range in size"); 14645d8ee958SBrian Somers 14655d8ee958SBrian Somers for (i = 0 ; i < numPublicPorts ; ++i) { 14665d8ee958SBrian Somers /* If remotePort is all ports, set it to 0. */ 14675d8ee958SBrian Somers u_short remotePortCopy = remotePort + i; 14685d8ee958SBrian Somers if (numRemotePorts == 1 && remotePort == 0) 14695d8ee958SBrian Somers remotePortCopy = 0; 14705d8ee958SBrian Somers 1471bd690510SRuslan Ermilov link = PacketAliasRedirectPort (localAddr, 14725d8ee958SBrian Somers htons(localPort + i), 147324084f9bSBrian Somers remoteAddr, 14745d8ee958SBrian Somers htons(remotePortCopy), 147524084f9bSBrian Somers publicAddr, 14765d8ee958SBrian Somers htons(publicPort + i), 147724084f9bSBrian Somers proto); 147824084f9bSBrian Somers } 1479bd690510SRuslan Ermilov 1480bd690510SRuslan Ermilov /* 1481bd690510SRuslan Ermilov * Setup LSNAT server pool. 1482bd690510SRuslan Ermilov */ 1483bd690510SRuslan Ermilov if (serverPool != NULL && link != NULL) { 1484bd690510SRuslan Ermilov ptr = strtok(serverPool, ","); 1485bd690510SRuslan Ermilov while (ptr != NULL) { 1486bd690510SRuslan Ermilov if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0) 1487bd690510SRuslan Ermilov errx(1, "redirect_port: invalid local port range"); 1488bd690510SRuslan Ermilov 1489bd690510SRuslan Ermilov localPort = GETLOPORT(portRange); 1490bd690510SRuslan Ermilov if (GETNUMPORTS(portRange) != 1) 1491bd690510SRuslan Ermilov errx(1, "redirect_port: local port must be single in this context"); 1492bd690510SRuslan Ermilov PacketAliasAddServer(link, localAddr, htons(localPort)); 1493bd690510SRuslan Ermilov ptr = strtok(NULL, ","); 1494bd690510SRuslan Ermilov } 1495bd690510SRuslan Ermilov } 14965d8ee958SBrian Somers } 149724084f9bSBrian Somers 14984330006dSRuslan Ermilov void 14994330006dSRuslan Ermilov SetupProtoRedirect(const char* parms) 15004330006dSRuslan Ermilov { 15014330006dSRuslan Ermilov char buf[128]; 15024330006dSRuslan Ermilov char* ptr; 15034330006dSRuslan Ermilov struct in_addr localAddr; 15044330006dSRuslan Ermilov struct in_addr publicAddr; 15054330006dSRuslan Ermilov struct in_addr remoteAddr; 15064330006dSRuslan Ermilov int proto; 15074330006dSRuslan Ermilov char* protoName; 15084330006dSRuslan Ermilov struct protoent *protoent; 15094330006dSRuslan Ermilov 15104330006dSRuslan Ermilov strcpy (buf, parms); 15114330006dSRuslan Ermilov /* 15124330006dSRuslan Ermilov * Extract protocol. 15134330006dSRuslan Ermilov */ 15144330006dSRuslan Ermilov protoName = strtok(buf, " \t"); 15154330006dSRuslan Ermilov if (!protoName) 15164330006dSRuslan Ermilov errx(1, "redirect_proto: missing protocol"); 15174330006dSRuslan Ermilov 15184330006dSRuslan Ermilov protoent = getprotobyname(protoName); 15194330006dSRuslan Ermilov if (protoent == NULL) 15204330006dSRuslan Ermilov errx(1, "redirect_proto: unknown protocol %s", protoName); 15214330006dSRuslan Ermilov else 15224330006dSRuslan Ermilov proto = protoent->p_proto; 15234330006dSRuslan Ermilov /* 15244330006dSRuslan Ermilov * Extract local address. 15254330006dSRuslan Ermilov */ 15264330006dSRuslan Ermilov ptr = strtok(NULL, " \t"); 15274330006dSRuslan Ermilov if (!ptr) 15284330006dSRuslan Ermilov errx(1, "redirect_proto: missing local address"); 15294330006dSRuslan Ermilov else 15304330006dSRuslan Ermilov StrToAddr(ptr, &localAddr); 15314330006dSRuslan Ermilov /* 15324330006dSRuslan Ermilov * Extract optional public address. 15334330006dSRuslan Ermilov */ 15344330006dSRuslan Ermilov ptr = strtok(NULL, " \t"); 15354330006dSRuslan Ermilov if (ptr) 15364330006dSRuslan Ermilov StrToAddr(ptr, &publicAddr); 15374330006dSRuslan Ermilov else 15384330006dSRuslan Ermilov publicAddr.s_addr = INADDR_ANY; 15394330006dSRuslan Ermilov /* 15404330006dSRuslan Ermilov * Extract optional remote address. 15414330006dSRuslan Ermilov */ 15424330006dSRuslan Ermilov ptr = strtok(NULL, " \t"); 15434330006dSRuslan Ermilov if (ptr) 15444330006dSRuslan Ermilov StrToAddr(ptr, &remoteAddr); 15454330006dSRuslan Ermilov else 15464330006dSRuslan Ermilov remoteAddr.s_addr = INADDR_ANY; 15474330006dSRuslan Ermilov /* 15484330006dSRuslan Ermilov * Create aliasing link. 15494330006dSRuslan Ermilov */ 15504330006dSRuslan Ermilov (void)PacketAliasRedirectProto(localAddr, remoteAddr, publicAddr, 15514330006dSRuslan Ermilov proto); 15524330006dSRuslan Ermilov } 15534330006dSRuslan Ermilov 1554902cb50aSBrian Somers void SetupAddressRedirect (const char* parms) 155524084f9bSBrian Somers { 155624084f9bSBrian Somers char buf[128]; 155724084f9bSBrian Somers char* ptr; 1558bd690510SRuslan Ermilov char* separator; 155924084f9bSBrian Somers struct in_addr localAddr; 156024084f9bSBrian Somers struct in_addr publicAddr; 1561bd690510SRuslan Ermilov char* serverPool; 1562bd690510SRuslan Ermilov struct alias_link *link; 156324084f9bSBrian Somers 156424084f9bSBrian Somers strcpy (buf, parms); 156524084f9bSBrian Somers /* 156624084f9bSBrian Somers * Extract local address. 156724084f9bSBrian Somers */ 156824084f9bSBrian Somers ptr = strtok (buf, " \t"); 15690fc81af1SPhilippe Charnier if (!ptr) 15700fc81af1SPhilippe Charnier errx (1, "redirect_address: missing local address"); 157124084f9bSBrian Somers 1572bd690510SRuslan Ermilov separator = strchr(ptr, ','); 1573bd690510SRuslan Ermilov if (separator) { /* LSNAT redirection syntax. */ 1574bd690510SRuslan Ermilov localAddr.s_addr = INADDR_NONE; 1575bd690510SRuslan Ermilov serverPool = ptr; 1576bd690510SRuslan Ermilov } else { 157724084f9bSBrian Somers StrToAddr (ptr, &localAddr); 1578bd690510SRuslan Ermilov serverPool = NULL; 1579bd690510SRuslan Ermilov } 158024084f9bSBrian Somers /* 158124084f9bSBrian Somers * Extract public address. 158224084f9bSBrian Somers */ 158324084f9bSBrian Somers ptr = strtok (NULL, " \t"); 15840fc81af1SPhilippe Charnier if (!ptr) 15850fc81af1SPhilippe Charnier errx (1, "redirect_address: missing public address"); 158624084f9bSBrian Somers 158724084f9bSBrian Somers StrToAddr (ptr, &publicAddr); 1588bd690510SRuslan Ermilov link = PacketAliasRedirectAddr(localAddr, publicAddr); 1589bd690510SRuslan Ermilov 1590bd690510SRuslan Ermilov /* 1591bd690510SRuslan Ermilov * Setup LSNAT server pool. 1592bd690510SRuslan Ermilov */ 1593bd690510SRuslan Ermilov if (serverPool != NULL && link != NULL) { 1594bd690510SRuslan Ermilov ptr = strtok(serverPool, ","); 1595bd690510SRuslan Ermilov while (ptr != NULL) { 1596bd690510SRuslan Ermilov StrToAddr(ptr, &localAddr); 1597bd690510SRuslan Ermilov PacketAliasAddServer(link, localAddr, htons(~0)); 1598bd690510SRuslan Ermilov ptr = strtok(NULL, ","); 1599bd690510SRuslan Ermilov } 1600bd690510SRuslan Ermilov } 160124084f9bSBrian Somers } 160224084f9bSBrian Somers 1603902cb50aSBrian Somers void StrToAddr (const char* str, struct in_addr* addr) 160424084f9bSBrian Somers { 160524084f9bSBrian Somers struct hostent* hp; 160624084f9bSBrian Somers 160724084f9bSBrian Somers if (inet_aton (str, addr)) 160824084f9bSBrian Somers return; 160924084f9bSBrian Somers 161024084f9bSBrian Somers hp = gethostbyname (str); 16110fc81af1SPhilippe Charnier if (!hp) 16120fc81af1SPhilippe Charnier errx (1, "unknown host %s", str); 161324084f9bSBrian Somers 161424084f9bSBrian Somers memcpy (addr, hp->h_addr, sizeof (struct in_addr)); 161524084f9bSBrian Somers } 161624084f9bSBrian Somers 1617902cb50aSBrian Somers u_short StrToPort (const char* str, const char* proto) 161824084f9bSBrian Somers { 161967a886fbSBrian Somers u_short port; 162024084f9bSBrian Somers struct servent* sp; 162124084f9bSBrian Somers char* end; 162224084f9bSBrian Somers 162324084f9bSBrian Somers port = strtol (str, &end, 10); 162424084f9bSBrian Somers if (end != str) 162527c20503SBrian Somers return htons (port); 162624084f9bSBrian Somers 162724084f9bSBrian Somers sp = getservbyname (str, proto); 16280fc81af1SPhilippe Charnier if (!sp) 16290fc81af1SPhilippe Charnier errx (1, "unknown service %s/%s", str, proto); 163024084f9bSBrian Somers 163124084f9bSBrian Somers return sp->s_port; 163224084f9bSBrian Somers } 163324084f9bSBrian Somers 1634902cb50aSBrian Somers int StrToPortRange (const char* str, const char* proto, port_range *portRange) 16355d8ee958SBrian Somers { 16365d8ee958SBrian Somers char* sep; 16375d8ee958SBrian Somers struct servent* sp; 16385d8ee958SBrian Somers char* end; 16395d8ee958SBrian Somers u_short loPort; 16405d8ee958SBrian Somers u_short hiPort; 16415d8ee958SBrian Somers 16425d8ee958SBrian Somers /* First see if this is a service, return corresponding port if so. */ 16435d8ee958SBrian Somers sp = getservbyname (str,proto); 16445d8ee958SBrian Somers if (sp) { 16455d8ee958SBrian Somers SETLOPORT(*portRange, ntohs(sp->s_port)); 16465d8ee958SBrian Somers SETNUMPORTS(*portRange, 1); 16475d8ee958SBrian Somers return 0; 16485d8ee958SBrian Somers } 16495d8ee958SBrian Somers 16505d8ee958SBrian Somers /* Not a service, see if it's a single port or port range. */ 16515d8ee958SBrian Somers sep = strchr (str, '-'); 16525d8ee958SBrian Somers if (sep == NULL) { 16535d8ee958SBrian Somers SETLOPORT(*portRange, strtol(str, &end, 10)); 16545d8ee958SBrian Somers if (end != str) { 16555d8ee958SBrian Somers /* Single port. */ 16565d8ee958SBrian Somers SETNUMPORTS(*portRange, 1); 16575d8ee958SBrian Somers return 0; 16585d8ee958SBrian Somers } 16595d8ee958SBrian Somers 16605d8ee958SBrian Somers /* Error in port range field. */ 16615d8ee958SBrian Somers errx (1, "unknown service %s/%s", str, proto); 16625d8ee958SBrian Somers } 16635d8ee958SBrian Somers 16645d8ee958SBrian Somers /* Port range, get the values and sanity check. */ 16655d8ee958SBrian Somers sscanf (str, "%hu-%hu", &loPort, &hiPort); 16665d8ee958SBrian Somers SETLOPORT(*portRange, loPort); 16675d8ee958SBrian Somers SETNUMPORTS(*portRange, 0); /* Error by default */ 16685d8ee958SBrian Somers if (loPort <= hiPort) 16695d8ee958SBrian Somers SETNUMPORTS(*portRange, hiPort - loPort + 1); 16705d8ee958SBrian Somers 16715d8ee958SBrian Somers if (GETNUMPORTS(*portRange) == 0) 16725d8ee958SBrian Somers errx (1, "invalid port range %s", str); 16735d8ee958SBrian Somers 16745d8ee958SBrian Somers return 0; 16755d8ee958SBrian Somers } 16765d8ee958SBrian Somers 16775d8ee958SBrian Somers 1678902cb50aSBrian Somers int StrToProto (const char* str) 167924084f9bSBrian Somers { 168024084f9bSBrian Somers if (!strcmp (str, "tcp")) 168124084f9bSBrian Somers return IPPROTO_TCP; 168224084f9bSBrian Somers 168324084f9bSBrian Somers if (!strcmp (str, "udp")) 168424084f9bSBrian Somers return IPPROTO_UDP; 168524084f9bSBrian Somers 16860fc81af1SPhilippe Charnier errx (1, "unknown protocol %s. Expected tcp or udp", str); 168724084f9bSBrian Somers } 168824084f9bSBrian Somers 1689902cb50aSBrian Somers int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange) 169024084f9bSBrian Somers { 169124084f9bSBrian Somers char* ptr; 169224084f9bSBrian Somers 169324084f9bSBrian Somers ptr = strchr (str, ':'); 16940fc81af1SPhilippe Charnier if (!ptr) 16950fc81af1SPhilippe Charnier errx (1, "%s is missing port number", str); 169624084f9bSBrian Somers 169724084f9bSBrian Somers *ptr = '\0'; 169824084f9bSBrian Somers ++ptr; 169924084f9bSBrian Somers 170024084f9bSBrian Somers StrToAddr (str, addr); 17015d8ee958SBrian Somers return StrToPortRange (ptr, proto, portRange); 170224084f9bSBrian Somers } 1703bc4ebb98SRuslan Ermilov 1704bc4ebb98SRuslan Ermilov static void 1705bc4ebb98SRuslan Ermilov SetupPunchFW(const char *strValue) 1706bc4ebb98SRuslan Ermilov { 1707bc4ebb98SRuslan Ermilov unsigned int base, num; 1708bc4ebb98SRuslan Ermilov 1709bc4ebb98SRuslan Ermilov if (sscanf(strValue, "%u:%u", &base, &num) != 2) 1710bc4ebb98SRuslan Ermilov errx(1, "punch_fw: basenumber:count parameter required"); 1711bc4ebb98SRuslan Ermilov 1712bc4ebb98SRuslan Ermilov PacketAliasSetFWBase(base, num); 1713bc4ebb98SRuslan Ermilov (void)PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW); 1714bc4ebb98SRuslan Ermilov } 1715