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); 100bc4ebb98SRuslan Ermilov static void SetupPunchFW(const char *strValue); 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; 120f9b06d5cSBrian Somers static int dropIgnoredIncoming; 12159a7c613SBrian Somers static int logDropped; 12259a7c613SBrian Somers static int logFacility; 1233843533eSRuslan Ermilov static int logIpfwDenied; 12424084f9bSBrian Somers 12524084f9bSBrian Somers int main (int argc, char** argv) 12624084f9bSBrian Somers { 12724084f9bSBrian Somers int divertIn; 12824084f9bSBrian Somers int divertOut; 12924084f9bSBrian Somers int divertInOut; 13024084f9bSBrian Somers int routeSock; 13124084f9bSBrian Somers struct sockaddr_in addr; 13224084f9bSBrian Somers fd_set readMask; 13324084f9bSBrian Somers int fdMax; 13424084f9bSBrian Somers /* 13524084f9bSBrian Somers * Initialize packet aliasing software. 13624084f9bSBrian Somers * Done already here to be able to alter option bits 13724084f9bSBrian Somers * during command line and configuration file processing. 13824084f9bSBrian Somers */ 139fb994b07SBrian Somers PacketAliasInit (); 14024084f9bSBrian Somers /* 14124084f9bSBrian Somers * Parse options. 14224084f9bSBrian Somers */ 14324084f9bSBrian Somers inPort = 0; 14424084f9bSBrian Somers outPort = 0; 14524084f9bSBrian Somers verbose = 0; 14624084f9bSBrian Somers inOutPort = 0; 14724084f9bSBrian Somers ifName = NULL; 14824084f9bSBrian Somers ifMTU = -1; 14924084f9bSBrian Somers background = 0; 15024084f9bSBrian Somers running = 1; 15124084f9bSBrian Somers assignAliasAddr = 0; 15224084f9bSBrian Somers aliasAddr.s_addr = INADDR_NONE; 15324084f9bSBrian Somers aliasOverhead = 12; 15424084f9bSBrian Somers dynamicMode = 0; 15559a7c613SBrian Somers logDropped = 0; 15659a7c613SBrian Somers logFacility = LOG_DAEMON; 157c0956cf8SRuslan Ermilov logIpfwDenied = -1; 15824084f9bSBrian Somers 15924084f9bSBrian Somers ParseArgs (argc, argv); 16024084f9bSBrian Somers /* 161c0956cf8SRuslan Ermilov * Log ipfw(8) denied packets by default in verbose mode. 162c0956cf8SRuslan Ermilov */ 163c0956cf8SRuslan Ermilov if (logIpfwDenied == -1) 164c0956cf8SRuslan Ermilov logIpfwDenied = verbose; 165c0956cf8SRuslan Ermilov /* 16659a7c613SBrian Somers * Open syslog channel. 16759a7c613SBrian Somers */ 1684c04fa4cSRuslan Ermilov openlog ("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0), 1694c04fa4cSRuslan Ermilov logFacility); 17059a7c613SBrian Somers /* 17124084f9bSBrian Somers * Check that valid aliasing address has been given. 17224084f9bSBrian Somers */ 1730fc81af1SPhilippe Charnier if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL) 1740fc81af1SPhilippe Charnier errx (1, "aliasing address not given"); 17524084f9bSBrian Somers 1760fc81af1SPhilippe Charnier if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL) 17767a886fbSBrian Somers errx (1, "both alias address and interface " 17867a886fbSBrian Somers "name are not allowed"); 17924084f9bSBrian Somers /* 18024084f9bSBrian Somers * Check that valid port number is known. 18124084f9bSBrian Somers */ 18224084f9bSBrian Somers if (inPort != 0 || outPort != 0) 1830fc81af1SPhilippe Charnier if (inPort == 0 || outPort == 0) 1840fc81af1SPhilippe Charnier errx (1, "both input and output ports are required"); 18524084f9bSBrian Somers 18624084f9bSBrian Somers if (inPort == 0 && outPort == 0 && inOutPort == 0) 187b0f55af6SRuslan Ermilov ParseOption ("port", DEFAULT_SERVICE); 18824084f9bSBrian Somers 18924084f9bSBrian Somers /* 190f9b06d5cSBrian Somers * Check if ignored packets should be dropped. 191f9b06d5cSBrian Somers */ 192f9b06d5cSBrian Somers dropIgnoredIncoming = PacketAliasSetMode (0, 0); 193f9b06d5cSBrian Somers dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING; 194f9b06d5cSBrian Somers /* 19524084f9bSBrian Somers * Create divert sockets. Use only one socket if -p was specified 19624084f9bSBrian Somers * on command line. Otherwise, create separate sockets for 19724084f9bSBrian Somers * outgoing and incoming connnections. 19824084f9bSBrian Somers */ 19924084f9bSBrian Somers if (inOutPort) { 20024084f9bSBrian Somers 20124084f9bSBrian Somers divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 20224084f9bSBrian Somers if (divertInOut == -1) 20324084f9bSBrian Somers Quit ("Unable to create divert socket."); 20424084f9bSBrian Somers 20524084f9bSBrian Somers divertIn = -1; 20624084f9bSBrian Somers divertOut = -1; 20724084f9bSBrian Somers /* 20824084f9bSBrian Somers * Bind socket. 20924084f9bSBrian Somers */ 21024084f9bSBrian Somers 21124084f9bSBrian Somers addr.sin_family = AF_INET; 21224084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 21324084f9bSBrian Somers addr.sin_port = inOutPort; 21424084f9bSBrian Somers 21524084f9bSBrian Somers if (bind (divertInOut, 21624084f9bSBrian Somers (struct sockaddr*) &addr, 21724084f9bSBrian Somers sizeof addr) == -1) 21824084f9bSBrian Somers Quit ("Unable to bind divert socket."); 21924084f9bSBrian Somers } 22024084f9bSBrian Somers else { 22124084f9bSBrian Somers 22224084f9bSBrian Somers divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 22324084f9bSBrian Somers if (divertIn == -1) 22424084f9bSBrian Somers Quit ("Unable to create incoming divert socket."); 22524084f9bSBrian Somers 22624084f9bSBrian Somers divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); 22724084f9bSBrian Somers if (divertOut == -1) 22824084f9bSBrian Somers Quit ("Unable to create outgoing divert socket."); 22924084f9bSBrian Somers 23024084f9bSBrian Somers divertInOut = -1; 23124084f9bSBrian Somers 23224084f9bSBrian Somers /* 23324084f9bSBrian Somers * Bind divert sockets. 23424084f9bSBrian Somers */ 23524084f9bSBrian Somers 23624084f9bSBrian Somers addr.sin_family = AF_INET; 23724084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 23824084f9bSBrian Somers addr.sin_port = inPort; 23924084f9bSBrian Somers 24024084f9bSBrian Somers if (bind (divertIn, 24124084f9bSBrian Somers (struct sockaddr*) &addr, 24224084f9bSBrian Somers sizeof addr) == -1) 24324084f9bSBrian Somers Quit ("Unable to bind incoming divert socket."); 24424084f9bSBrian Somers 24524084f9bSBrian Somers addr.sin_family = AF_INET; 24624084f9bSBrian Somers addr.sin_addr.s_addr = INADDR_ANY; 24724084f9bSBrian Somers addr.sin_port = outPort; 24824084f9bSBrian Somers 24924084f9bSBrian Somers if (bind (divertOut, 25024084f9bSBrian Somers (struct sockaddr*) &addr, 25124084f9bSBrian Somers sizeof addr) == -1) 25224084f9bSBrian Somers Quit ("Unable to bind outgoing divert socket."); 25324084f9bSBrian Somers } 25424084f9bSBrian Somers /* 255f2da55a2SRuslan Ermilov * Create routing socket if interface name specified and in dynamic mode. 25624084f9bSBrian Somers */ 257f2da55a2SRuslan Ermilov routeSock = -1; 258f2da55a2SRuslan Ermilov if (ifName) { 259f2da55a2SRuslan Ermilov if (dynamicMode) { 26024084f9bSBrian Somers 26124084f9bSBrian Somers routeSock = socket (PF_ROUTE, SOCK_RAW, 0); 26224084f9bSBrian Somers if (routeSock == -1) 26324084f9bSBrian Somers Quit ("Unable to create routing info socket."); 264f2da55a2SRuslan Ermilov 265f2da55a2SRuslan Ermilov assignAliasAddr = 1; 26624084f9bSBrian Somers } 26724084f9bSBrian Somers else 268f2da55a2SRuslan Ermilov SetAliasAddressFromIfName (ifName); 269f2da55a2SRuslan Ermilov } 27024084f9bSBrian Somers /* 27124084f9bSBrian Somers * Create socket for sending ICMP messages. 27224084f9bSBrian Somers */ 27324084f9bSBrian Somers icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); 27424084f9bSBrian Somers if (icmpSock == -1) 27524084f9bSBrian Somers Quit ("Unable to create ICMP socket."); 276f3d64024SBrian Somers 277f3d64024SBrian Somers /* 278f3d64024SBrian Somers * And disable reads for the socket, otherwise it slowly fills 279f3d64024SBrian Somers * up with received icmps which we do not use. 280f3d64024SBrian Somers */ 281f3d64024SBrian Somers shutdown(icmpSock, SHUT_RD); 282f3d64024SBrian Somers 28324084f9bSBrian Somers /* 28424084f9bSBrian Somers * Become a daemon unless verbose mode was requested. 28524084f9bSBrian Somers */ 28624084f9bSBrian Somers if (!verbose) 28724084f9bSBrian Somers DaemonMode (); 28824084f9bSBrian Somers /* 28924084f9bSBrian Somers * Catch signals to manage shutdown and 29024084f9bSBrian Somers * refresh of interface address. 29124084f9bSBrian Somers */ 292cd45c931SRuslan Ermilov siginterrupt(SIGTERM, 1); 293cd45c931SRuslan Ermilov siginterrupt(SIGHUP, 1); 29424084f9bSBrian Somers signal (SIGTERM, InitiateShutdown); 29524084f9bSBrian Somers signal (SIGHUP, RefreshAddr); 29624084f9bSBrian Somers /* 29724084f9bSBrian Somers * Set alias address if it has been given. 29824084f9bSBrian Somers */ 29924084f9bSBrian Somers if (aliasAddr.s_addr != INADDR_NONE) 300fb994b07SBrian Somers PacketAliasSetAddress (aliasAddr); 30124084f9bSBrian Somers /* 30224084f9bSBrian Somers * We need largest descriptor number for select. 30324084f9bSBrian Somers */ 30424084f9bSBrian Somers 30524084f9bSBrian Somers fdMax = -1; 30624084f9bSBrian Somers 30724084f9bSBrian Somers if (divertIn > fdMax) 30824084f9bSBrian Somers fdMax = divertIn; 30924084f9bSBrian Somers 31024084f9bSBrian Somers if (divertOut > fdMax) 31124084f9bSBrian Somers fdMax = divertOut; 31224084f9bSBrian Somers 31324084f9bSBrian Somers if (divertInOut > fdMax) 31424084f9bSBrian Somers fdMax = divertInOut; 31524084f9bSBrian Somers 31624084f9bSBrian Somers if (routeSock > fdMax) 31724084f9bSBrian Somers fdMax = routeSock; 31824084f9bSBrian Somers 31924084f9bSBrian Somers while (running) { 320fb994b07SBrian Somers 3213daff242SRuslan Ermilov if (divertInOut != -1 && !ifName) { 322fb994b07SBrian Somers /* 323fb994b07SBrian Somers * When using only one socket, just call 324fb994b07SBrian Somers * DoAliasing repeatedly to process packets. 325fb994b07SBrian Somers */ 32659a7c613SBrian Somers DoAliasing (divertInOut, DONT_KNOW); 327fb994b07SBrian Somers continue; 328fb994b07SBrian Somers } 32924084f9bSBrian Somers /* 33024084f9bSBrian Somers * Build read mask from socket descriptors to select. 33124084f9bSBrian Somers */ 33224084f9bSBrian Somers FD_ZERO (&readMask); 333fb994b07SBrian Somers /* 3343daff242SRuslan Ermilov * Check if new packets are available. 335fb994b07SBrian Somers */ 33624084f9bSBrian Somers if (divertIn != -1) 33724084f9bSBrian Somers FD_SET (divertIn, &readMask); 33824084f9bSBrian Somers 33924084f9bSBrian Somers if (divertOut != -1) 34024084f9bSBrian Somers FD_SET (divertOut, &readMask); 34124084f9bSBrian Somers 34224084f9bSBrian Somers if (divertInOut != -1) 34324084f9bSBrian Somers FD_SET (divertInOut, &readMask); 344fb994b07SBrian Somers /* 345fb994b07SBrian Somers * Routing info is processed always. 346fb994b07SBrian Somers */ 34724084f9bSBrian Somers if (routeSock != -1) 34824084f9bSBrian Somers FD_SET (routeSock, &readMask); 34924084f9bSBrian Somers 35024084f9bSBrian Somers if (select (fdMax + 1, 35124084f9bSBrian Somers &readMask, 3523daff242SRuslan Ermilov NULL, 35324084f9bSBrian Somers NULL, 35424084f9bSBrian Somers NULL) == -1) { 35524084f9bSBrian Somers 35624084f9bSBrian Somers if (errno == EINTR) 35724084f9bSBrian Somers continue; 35824084f9bSBrian Somers 35924084f9bSBrian Somers Quit ("Select failed."); 36024084f9bSBrian Somers } 36124084f9bSBrian Somers 36224084f9bSBrian Somers if (divertIn != -1) 36324084f9bSBrian Somers if (FD_ISSET (divertIn, &readMask)) 36459a7c613SBrian Somers DoAliasing (divertIn, INPUT); 36524084f9bSBrian Somers 36624084f9bSBrian Somers if (divertOut != -1) 36724084f9bSBrian Somers if (FD_ISSET (divertOut, &readMask)) 36859a7c613SBrian Somers DoAliasing (divertOut, OUTPUT); 36924084f9bSBrian Somers 37024084f9bSBrian Somers if (divertInOut != -1) 37124084f9bSBrian Somers if (FD_ISSET (divertInOut, &readMask)) 37259a7c613SBrian Somers DoAliasing (divertInOut, DONT_KNOW); 37324084f9bSBrian Somers 37424084f9bSBrian Somers if (routeSock != -1) 37524084f9bSBrian Somers if (FD_ISSET (routeSock, &readMask)) 37624084f9bSBrian Somers HandleRoutingInfo (routeSock); 37724084f9bSBrian Somers } 37824084f9bSBrian Somers 37924084f9bSBrian Somers if (background) 38024084f9bSBrian Somers unlink (PIDFILE); 38124084f9bSBrian Somers 38224084f9bSBrian Somers return 0; 38324084f9bSBrian Somers } 38424084f9bSBrian Somers 38524084f9bSBrian Somers static void DaemonMode () 38624084f9bSBrian Somers { 38724084f9bSBrian Somers FILE* pidFile; 38824084f9bSBrian Somers 38924084f9bSBrian Somers daemon (0, 0); 39024084f9bSBrian Somers background = 1; 39124084f9bSBrian Somers 39224084f9bSBrian Somers pidFile = fopen (PIDFILE, "w"); 39324084f9bSBrian Somers if (pidFile) { 39424084f9bSBrian Somers 39524084f9bSBrian Somers fprintf (pidFile, "%d\n", getpid ()); 39624084f9bSBrian Somers fclose (pidFile); 39724084f9bSBrian Somers } 39824084f9bSBrian Somers } 39924084f9bSBrian Somers 40024084f9bSBrian Somers static void ParseArgs (int argc, char** argv) 40124084f9bSBrian Somers { 40224084f9bSBrian Somers int arg; 40324084f9bSBrian Somers char* opt; 40424084f9bSBrian Somers char parmBuf[256]; 40530395bb5SJosef Karthauser int len; /* bounds checking */ 40624084f9bSBrian Somers 40724084f9bSBrian Somers for (arg = 1; arg < argc; arg++) { 40824084f9bSBrian Somers 40924084f9bSBrian Somers opt = argv[arg]; 41024084f9bSBrian Somers if (*opt != '-') { 41124084f9bSBrian Somers 4120fc81af1SPhilippe Charnier warnx ("invalid option %s", opt); 41324084f9bSBrian Somers Usage (); 41424084f9bSBrian Somers } 41524084f9bSBrian Somers 41624084f9bSBrian Somers parmBuf[0] = '\0'; 41730395bb5SJosef Karthauser len = 0; 41824084f9bSBrian Somers 41924084f9bSBrian Somers while (arg < argc - 1) { 42024084f9bSBrian Somers 42124084f9bSBrian Somers if (argv[arg + 1][0] == '-') 42224084f9bSBrian Somers break; 42324084f9bSBrian Somers 42430395bb5SJosef Karthauser if (len) { 42530395bb5SJosef Karthauser strncat (parmBuf, " ", sizeof(parmBuf) - (len + 1)); 42630395bb5SJosef Karthauser len += strlen(parmBuf + len); 42724084f9bSBrian Somers } 42824084f9bSBrian Somers 42930395bb5SJosef Karthauser ++arg; 43030395bb5SJosef Karthauser strncat (parmBuf, argv[arg], sizeof(parmBuf) - (len + 1)); 43130395bb5SJosef Karthauser len += strlen(parmBuf + len); 43230395bb5SJosef Karthauser 43330395bb5SJosef Karthauser } 43430395bb5SJosef Karthauser 435b0f55af6SRuslan Ermilov ParseOption (opt + 1, (len ? parmBuf : NULL)); 43630395bb5SJosef Karthauser 43724084f9bSBrian Somers } 43824084f9bSBrian Somers } 43924084f9bSBrian Somers 44059a7c613SBrian Somers static void DoAliasing (int fd, int direction) 44124084f9bSBrian Somers { 44224084f9bSBrian Somers int bytes; 44324084f9bSBrian Somers int origBytes; 4443daff242SRuslan Ermilov char buf[IP_MAXPACKET]; 4453daff242SRuslan Ermilov struct sockaddr_in addr; 4463daff242SRuslan Ermilov int wrote; 447f9b06d5cSBrian Somers int status; 44824084f9bSBrian Somers int addrSize; 44924084f9bSBrian Somers struct ip* ip; 4503daff242SRuslan Ermilov char msgBuf[80]; 45124084f9bSBrian Somers 45224084f9bSBrian Somers if (assignAliasAddr) { 45324084f9bSBrian Somers 45424084f9bSBrian Somers SetAliasAddressFromIfName (ifName); 45524084f9bSBrian Somers assignAliasAddr = 0; 45624084f9bSBrian Somers } 45724084f9bSBrian Somers /* 45824084f9bSBrian Somers * Get packet from socket. 45924084f9bSBrian Somers */ 4603daff242SRuslan Ermilov addrSize = sizeof addr; 46124084f9bSBrian Somers origBytes = recvfrom (fd, 4623daff242SRuslan Ermilov buf, 4633daff242SRuslan Ermilov sizeof buf, 46424084f9bSBrian Somers 0, 4653daff242SRuslan Ermilov (struct sockaddr*) &addr, 46624084f9bSBrian Somers &addrSize); 46724084f9bSBrian Somers 46824084f9bSBrian Somers if (origBytes == -1) { 46924084f9bSBrian Somers 47024084f9bSBrian Somers if (errno != EINTR) 4710fc81af1SPhilippe Charnier Warn ("read from divert socket failed"); 47224084f9bSBrian Somers 47324084f9bSBrian Somers return; 47424084f9bSBrian Somers } 47524084f9bSBrian Somers /* 47624084f9bSBrian Somers * This is a IP packet. 47724084f9bSBrian Somers */ 4783daff242SRuslan Ermilov ip = (struct ip*) buf; 479ebe70c8fSWarner Losh if (direction == DONT_KNOW) { 4803daff242SRuslan Ermilov if (addr.sin_addr.s_addr == INADDR_ANY) 48159a7c613SBrian Somers direction = OUTPUT; 48259a7c613SBrian Somers else 48359a7c613SBrian Somers direction = INPUT; 484ebe70c8fSWarner Losh } 48524084f9bSBrian Somers 48624084f9bSBrian Somers if (verbose) { 48724084f9bSBrian Somers /* 48824084f9bSBrian Somers * Print packet direction and protocol type. 48924084f9bSBrian Somers */ 49059a7c613SBrian Somers printf (direction == OUTPUT ? "Out " : "In "); 49124084f9bSBrian Somers 49224084f9bSBrian Somers switch (ip->ip_p) { 49324084f9bSBrian Somers case IPPROTO_TCP: 49424084f9bSBrian Somers printf ("[TCP] "); 49524084f9bSBrian Somers break; 49624084f9bSBrian Somers 49724084f9bSBrian Somers case IPPROTO_UDP: 49824084f9bSBrian Somers printf ("[UDP] "); 49924084f9bSBrian Somers break; 50024084f9bSBrian Somers 50124084f9bSBrian Somers case IPPROTO_ICMP: 50224084f9bSBrian Somers printf ("[ICMP] "); 50324084f9bSBrian Somers break; 50424084f9bSBrian Somers 50524084f9bSBrian Somers default: 50659a7c613SBrian Somers printf ("[%d] ", ip->ip_p); 50724084f9bSBrian Somers break; 50824084f9bSBrian Somers } 50924084f9bSBrian Somers /* 51024084f9bSBrian Somers * Print addresses. 51124084f9bSBrian Somers */ 51224084f9bSBrian Somers PrintPacket (ip); 51324084f9bSBrian Somers } 51424084f9bSBrian Somers 51559a7c613SBrian Somers if (direction == OUTPUT) { 51624084f9bSBrian Somers /* 51724084f9bSBrian Somers * Outgoing packets. Do aliasing. 51824084f9bSBrian Somers */ 5193daff242SRuslan Ermilov PacketAliasOut (buf, IP_MAXPACKET); 52024084f9bSBrian Somers } 52124084f9bSBrian Somers else { 52259a7c613SBrian Somers 52324084f9bSBrian Somers /* 52424084f9bSBrian Somers * Do aliasing. 52524084f9bSBrian Somers */ 5263daff242SRuslan Ermilov status = PacketAliasIn (buf, IP_MAXPACKET); 527f9b06d5cSBrian Somers if (status == PKT_ALIAS_IGNORED && 528f9b06d5cSBrian Somers dropIgnoredIncoming) { 529f9b06d5cSBrian Somers 53059a7c613SBrian Somers if (verbose) 531f9b06d5cSBrian Somers printf (" dropped.\n"); 53259a7c613SBrian Somers 53359a7c613SBrian Somers if (logDropped) 53459a7c613SBrian Somers SyslogPacket (ip, LOG_WARNING, "denied"); 53559a7c613SBrian Somers 536f9b06d5cSBrian Somers return; 537f9b06d5cSBrian Somers } 53824084f9bSBrian Somers } 53924084f9bSBrian Somers /* 54024084f9bSBrian Somers * Length might have changed during aliasing. 54124084f9bSBrian Somers */ 54224084f9bSBrian Somers bytes = ntohs (ip->ip_len); 54324084f9bSBrian Somers /* 54424084f9bSBrian Somers * Update alias overhead size for outgoing packets. 54524084f9bSBrian Somers */ 54659a7c613SBrian Somers if (direction == OUTPUT && 54724084f9bSBrian Somers bytes - origBytes > aliasOverhead) 54824084f9bSBrian Somers aliasOverhead = bytes - origBytes; 54924084f9bSBrian Somers 55024084f9bSBrian Somers if (verbose) { 55124084f9bSBrian Somers 55224084f9bSBrian Somers /* 55324084f9bSBrian Somers * Print addresses after aliasing. 55424084f9bSBrian Somers */ 55524084f9bSBrian Somers printf (" aliased to\n"); 55624084f9bSBrian Somers printf (" "); 55724084f9bSBrian Somers PrintPacket (ip); 55824084f9bSBrian Somers printf ("\n"); 55924084f9bSBrian Somers } 560fb994b07SBrian Somers 56124084f9bSBrian Somers /* 56224084f9bSBrian Somers * Put packet back for processing. 56324084f9bSBrian Somers */ 56424084f9bSBrian Somers wrote = sendto (fd, 5653daff242SRuslan Ermilov buf, 5663daff242SRuslan Ermilov bytes, 56724084f9bSBrian Somers 0, 5683daff242SRuslan Ermilov (struct sockaddr*) &addr, 5693daff242SRuslan Ermilov sizeof addr); 57024084f9bSBrian Somers 5713daff242SRuslan Ermilov if (wrote != bytes) { 57224084f9bSBrian Somers 57324084f9bSBrian Somers if (errno == EMSGSIZE) { 57424084f9bSBrian Somers 5753daff242SRuslan Ermilov if (direction == OUTPUT && 57624084f9bSBrian Somers ifMTU != -1) 57724084f9bSBrian Somers SendNeedFragIcmp (icmpSock, 5783daff242SRuslan Ermilov (struct ip*) buf, 57924084f9bSBrian Somers ifMTU - aliasOverhead); 58024084f9bSBrian Somers } 5813843533eSRuslan Ermilov else if (errno == EACCES && logIpfwDenied) { 58224084f9bSBrian Somers 583d782daf0SJosef Karthauser sprintf (msgBuf, "failed to write packet back"); 58424084f9bSBrian Somers Warn (msgBuf); 58524084f9bSBrian Somers } 58624084f9bSBrian Somers } 58724084f9bSBrian Somers } 58824084f9bSBrian Somers 58924084f9bSBrian Somers static void HandleRoutingInfo (int fd) 59024084f9bSBrian Somers { 59124084f9bSBrian Somers int bytes; 59224084f9bSBrian Somers struct if_msghdr ifMsg; 59324084f9bSBrian Somers /* 59424084f9bSBrian Somers * Get packet from socket. 59524084f9bSBrian Somers */ 59624084f9bSBrian Somers bytes = read (fd, &ifMsg, sizeof ifMsg); 59724084f9bSBrian Somers if (bytes == -1) { 59824084f9bSBrian Somers 5990fc81af1SPhilippe Charnier Warn ("read from routing socket failed"); 60024084f9bSBrian Somers return; 60124084f9bSBrian Somers } 60224084f9bSBrian Somers 60324084f9bSBrian Somers if (ifMsg.ifm_version != RTM_VERSION) { 60424084f9bSBrian Somers 6050fc81af1SPhilippe Charnier Warn ("unexpected packet read from routing socket"); 60624084f9bSBrian Somers return; 60724084f9bSBrian Somers } 60824084f9bSBrian Somers 60924084f9bSBrian Somers if (verbose) 6106f3dbe5eSRuslan Ermilov printf ("Routing message %#x received.\n", ifMsg.ifm_type); 61124084f9bSBrian Somers 6126f3dbe5eSRuslan Ermilov if ((ifMsg.ifm_type == RTM_NEWADDR || ifMsg.ifm_type == RTM_IFINFO) && 6136f3dbe5eSRuslan Ermilov ifMsg.ifm_index == ifIndex) { 6146f3dbe5eSRuslan Ermilov if (verbose) 6156f3dbe5eSRuslan Ermilov printf("Interface address/MTU has probably changed.\n"); 61624084f9bSBrian Somers assignAliasAddr = 1; 61724084f9bSBrian Somers } 6186f3dbe5eSRuslan Ermilov } 61924084f9bSBrian Somers 62024084f9bSBrian Somers static void PrintPacket (struct ip* ip) 62124084f9bSBrian Somers { 62259a7c613SBrian Somers printf ("%s", FormatPacket (ip)); 62359a7c613SBrian Somers } 62459a7c613SBrian Somers 625902cb50aSBrian Somers static void SyslogPacket (struct ip* ip, int priority, const char *label) 62659a7c613SBrian Somers { 62759a7c613SBrian Somers syslog (priority, "%s %s", label, FormatPacket (ip)); 62859a7c613SBrian Somers } 62959a7c613SBrian Somers 63059a7c613SBrian Somers static char* FormatPacket (struct ip* ip) 63159a7c613SBrian Somers { 63259a7c613SBrian Somers static char buf[256]; 63324084f9bSBrian Somers struct tcphdr* tcphdr; 63459a7c613SBrian Somers struct udphdr* udphdr; 63559a7c613SBrian Somers struct icmp* icmphdr; 63659a7c613SBrian Somers char src[20]; 63759a7c613SBrian Somers char dst[20]; 63824084f9bSBrian Somers 63959a7c613SBrian Somers strcpy (src, inet_ntoa (ip->ip_src)); 64059a7c613SBrian Somers strcpy (dst, inet_ntoa (ip->ip_dst)); 64159a7c613SBrian Somers 64259a7c613SBrian Somers switch (ip->ip_p) { 64359a7c613SBrian Somers case IPPROTO_TCP: 64424084f9bSBrian Somers tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2)); 64559a7c613SBrian Somers sprintf (buf, "[TCP] %s:%d -> %s:%d", 64659a7c613SBrian Somers src, 64759a7c613SBrian Somers ntohs (tcphdr->th_sport), 64859a7c613SBrian Somers dst, 64959a7c613SBrian Somers ntohs (tcphdr->th_dport)); 65059a7c613SBrian Somers break; 65124084f9bSBrian Somers 65259a7c613SBrian Somers case IPPROTO_UDP: 65359a7c613SBrian Somers udphdr = (struct udphdr*) ((char*) ip + (ip->ip_hl << 2)); 65459a7c613SBrian Somers sprintf (buf, "[UDP] %s:%d -> %s:%d", 65559a7c613SBrian Somers src, 65659a7c613SBrian Somers ntohs (udphdr->uh_sport), 65759a7c613SBrian Somers dst, 65859a7c613SBrian Somers ntohs (udphdr->uh_dport)); 65959a7c613SBrian Somers break; 66024084f9bSBrian Somers 66159a7c613SBrian Somers case IPPROTO_ICMP: 66259a7c613SBrian Somers icmphdr = (struct icmp*) ((char*) ip + (ip->ip_hl << 2)); 663b71e869dSBrian Somers sprintf (buf, "[ICMP] %s -> %s %u(%u)", 66459a7c613SBrian Somers src, 66559a7c613SBrian Somers dst, 666b71e869dSBrian Somers icmphdr->icmp_type, 667b71e869dSBrian Somers icmphdr->icmp_code); 66859a7c613SBrian Somers break; 66959a7c613SBrian Somers 67059a7c613SBrian Somers default: 67159a7c613SBrian Somers sprintf (buf, "[%d] %s -> %s ", ip->ip_p, src, dst); 67259a7c613SBrian Somers break; 67359a7c613SBrian Somers } 67459a7c613SBrian Somers 67559a7c613SBrian Somers return buf; 67624084f9bSBrian Somers } 67724084f9bSBrian Somers 6784c04fa4cSRuslan Ermilov static void 6794c04fa4cSRuslan Ermilov SetAliasAddressFromIfName(const char *ifn) 68024084f9bSBrian Somers { 6814c04fa4cSRuslan Ermilov size_t needed; 6824c04fa4cSRuslan Ermilov int mib[6]; 6834c04fa4cSRuslan Ermilov char *buf, *lim, *next; 6844c04fa4cSRuslan Ermilov struct if_msghdr *ifm; 6854c04fa4cSRuslan Ermilov struct ifa_msghdr *ifam; 6864c04fa4cSRuslan Ermilov struct sockaddr_dl *sdl; 6874c04fa4cSRuslan Ermilov struct sockaddr_in *sin; 68824084f9bSBrian Somers 6894c04fa4cSRuslan Ermilov mib[0] = CTL_NET; 6904c04fa4cSRuslan Ermilov mib[1] = PF_ROUTE; 6914c04fa4cSRuslan Ermilov mib[2] = 0; 6924c04fa4cSRuslan Ermilov mib[3] = AF_INET; /* Only IP addresses please */ 6934c04fa4cSRuslan Ermilov mib[4] = NET_RT_IFLIST; 6944c04fa4cSRuslan Ermilov mib[5] = 0; /* ifIndex??? */ 69524084f9bSBrian Somers /* 69624084f9bSBrian Somers * Get interface data. 69724084f9bSBrian Somers */ 6984c04fa4cSRuslan Ermilov if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) 6994c04fa4cSRuslan Ermilov err(1, "iflist-sysctl-estimate"); 7004c04fa4cSRuslan Ermilov if ((buf = malloc(needed)) == NULL) 7014c04fa4cSRuslan Ermilov errx(1, "malloc failed"); 7024c04fa4cSRuslan Ermilov if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) 7034c04fa4cSRuslan Ermilov err(1, "iflist-sysctl-get"); 7044c04fa4cSRuslan Ermilov lim = buf + needed; 70524084f9bSBrian Somers /* 70624084f9bSBrian Somers * Loop through interfaces until one with 70724084f9bSBrian Somers * given name is found. This is done to 70824084f9bSBrian Somers * find correct interface index for routing 70924084f9bSBrian Somers * message processing. 71024084f9bSBrian Somers */ 7114c04fa4cSRuslan Ermilov ifIndex = 0; 7124c04fa4cSRuslan Ermilov next = buf; 7134c04fa4cSRuslan Ermilov while (next < lim) { 7144c04fa4cSRuslan Ermilov ifm = (struct if_msghdr *)next; 7154c04fa4cSRuslan Ermilov next += ifm->ifm_msglen; 7164c04fa4cSRuslan Ermilov if (ifm->ifm_version != RTM_VERSION) { 7174c04fa4cSRuslan Ermilov if (verbose) 7184c04fa4cSRuslan Ermilov warnx("routing message version %d " 7194c04fa4cSRuslan Ermilov "not understood", ifm->ifm_version); 7204c04fa4cSRuslan Ermilov continue; 7214c04fa4cSRuslan Ermilov } 7224c04fa4cSRuslan Ermilov if (ifm->ifm_type == RTM_IFINFO) { 7234c04fa4cSRuslan Ermilov sdl = (struct sockaddr_dl *)(ifm + 1); 7244c04fa4cSRuslan Ermilov if (strlen(ifn) == sdl->sdl_nlen && 7254c04fa4cSRuslan Ermilov strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) { 7264c04fa4cSRuslan Ermilov ifIndex = ifm->ifm_index; 7274c04fa4cSRuslan Ermilov ifMTU = ifm->ifm_data.ifi_mtu; 72824084f9bSBrian Somers break; 72924084f9bSBrian Somers } 73024084f9bSBrian Somers } 73124084f9bSBrian Somers } 7324c04fa4cSRuslan Ermilov if (!ifIndex) 7334c04fa4cSRuslan Ermilov errx(1, "unknown interface name %s", ifn); 73424084f9bSBrian Somers /* 73524084f9bSBrian Somers * Get interface address. 73624084f9bSBrian Somers */ 7374c04fa4cSRuslan Ermilov sin = NULL; 7384c04fa4cSRuslan Ermilov while (next < lim) { 7394c04fa4cSRuslan Ermilov ifam = (struct ifa_msghdr *)next; 7404c04fa4cSRuslan Ermilov next += ifam->ifam_msglen; 7414c04fa4cSRuslan Ermilov if (ifam->ifam_version != RTM_VERSION) { 7424c04fa4cSRuslan Ermilov if (verbose) 7434c04fa4cSRuslan Ermilov warnx("routing message version %d " 7444c04fa4cSRuslan Ermilov "not understood", ifam->ifam_version); 7454c04fa4cSRuslan Ermilov continue; 7464c04fa4cSRuslan Ermilov } 7474c04fa4cSRuslan Ermilov if (ifam->ifam_type != RTM_NEWADDR) 7484c04fa4cSRuslan Ermilov break; 7494c04fa4cSRuslan Ermilov if (ifam->ifam_addrs & RTA_IFA) { 7504c04fa4cSRuslan Ermilov int i; 7514c04fa4cSRuslan Ermilov char *cp = (char *)(ifam + 1); 75224084f9bSBrian Somers 7534c04fa4cSRuslan Ermilov #define ROUNDUP(a) \ 7544c04fa4cSRuslan Ermilov ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 7554c04fa4cSRuslan Ermilov #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 7564c04fa4cSRuslan Ermilov 7574c04fa4cSRuslan Ermilov for (i = 1; i < RTA_IFA; i <<= 1) 7584c04fa4cSRuslan Ermilov if (ifam->ifam_addrs & i) 7594c04fa4cSRuslan Ermilov ADVANCE(cp, (struct sockaddr *)cp); 7604c04fa4cSRuslan Ermilov if (((struct sockaddr *)cp)->sa_family == AF_INET) { 7614c04fa4cSRuslan Ermilov sin = (struct sockaddr_in *)cp; 7624c04fa4cSRuslan Ermilov break; 7634c04fa4cSRuslan Ermilov } 7644c04fa4cSRuslan Ermilov } 7654c04fa4cSRuslan Ermilov } 7664c04fa4cSRuslan Ermilov if (sin == NULL) 7674c04fa4cSRuslan Ermilov errx(1, "%s: cannot get interface address", ifn); 7684c04fa4cSRuslan Ermilov 7694c04fa4cSRuslan Ermilov PacketAliasSetAddress(sin->sin_addr); 77024084f9bSBrian Somers syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes", 7714c04fa4cSRuslan Ermilov inet_ntoa(sin->sin_addr), ifMTU); 77224084f9bSBrian Somers 7734c04fa4cSRuslan Ermilov free(buf); 77424084f9bSBrian Somers } 77524084f9bSBrian Somers 776902cb50aSBrian Somers void Quit (const char* msg) 77724084f9bSBrian Somers { 77824084f9bSBrian Somers Warn (msg); 77924084f9bSBrian Somers exit (1); 78024084f9bSBrian Somers } 78124084f9bSBrian Somers 782902cb50aSBrian Somers void Warn (const char* msg) 78324084f9bSBrian Somers { 78424084f9bSBrian Somers if (background) 78524084f9bSBrian Somers syslog (LOG_ALERT, "%s (%m)", msg); 78624084f9bSBrian Somers else 78704d06bb6SKris Kennaway warn ("%s", msg); 78824084f9bSBrian Somers } 78924084f9bSBrian Somers 790902cb50aSBrian Somers static void RefreshAddr (int sig) 79124084f9bSBrian Somers { 79224084f9bSBrian Somers if (ifName) 79324084f9bSBrian Somers assignAliasAddr = 1; 79424084f9bSBrian Somers } 79524084f9bSBrian Somers 796902cb50aSBrian Somers static void InitiateShutdown (int sig) 79724084f9bSBrian Somers { 79824084f9bSBrian Somers /* 79924084f9bSBrian Somers * Start timer to allow kernel gracefully 80024084f9bSBrian Somers * shutdown existing connections when system 80124084f9bSBrian Somers * is shut down. 80224084f9bSBrian Somers */ 803cd45c931SRuslan Ermilov siginterrupt(SIGALRM, 1); 80424084f9bSBrian Somers signal (SIGALRM, Shutdown); 80524084f9bSBrian Somers alarm (10); 80624084f9bSBrian Somers } 80724084f9bSBrian Somers 808902cb50aSBrian Somers static void Shutdown (int sig) 80924084f9bSBrian Somers { 81024084f9bSBrian Somers running = 0; 81124084f9bSBrian Somers } 81224084f9bSBrian Somers 81324084f9bSBrian Somers /* 81424084f9bSBrian Somers * Different options recognized by this program. 81524084f9bSBrian Somers */ 81624084f9bSBrian Somers 81724084f9bSBrian Somers enum Option { 81824084f9bSBrian Somers 81924084f9bSBrian Somers PacketAliasOption, 82024084f9bSBrian Somers Verbose, 82124084f9bSBrian Somers InPort, 82224084f9bSBrian Somers OutPort, 82324084f9bSBrian Somers Port, 82424084f9bSBrian Somers AliasAddress, 82511c2b3bfSRuslan Ermilov TargetAddress, 82624084f9bSBrian Somers InterfaceName, 82724084f9bSBrian Somers RedirectPort, 8284330006dSRuslan Ermilov RedirectProto, 82924084f9bSBrian Somers RedirectAddress, 83024084f9bSBrian Somers ConfigFile, 83159a7c613SBrian Somers DynamicMode, 83259a7c613SBrian Somers ProxyRule, 83359a7c613SBrian Somers LogDenied, 834bc4ebb98SRuslan Ermilov LogFacility, 83584ef95bdSPoul-Henning Kamp PunchFW, 83684ef95bdSPoul-Henning Kamp LogIpfwDenied 83724084f9bSBrian Somers }; 83824084f9bSBrian Somers 83924084f9bSBrian Somers enum Param { 84024084f9bSBrian Somers 84124084f9bSBrian Somers YesNo, 84224084f9bSBrian Somers Numeric, 84324084f9bSBrian Somers String, 84424084f9bSBrian Somers None, 84524084f9bSBrian Somers Address, 84624084f9bSBrian Somers Service 84724084f9bSBrian Somers }; 84824084f9bSBrian Somers 84924084f9bSBrian Somers /* 85024084f9bSBrian Somers * Option information structure (used by ParseOption). 85124084f9bSBrian Somers */ 85224084f9bSBrian Somers 85324084f9bSBrian Somers struct OptionInfo { 85424084f9bSBrian Somers 85524084f9bSBrian Somers enum Option type; 85624084f9bSBrian Somers int packetAliasOpt; 85724084f9bSBrian Somers enum Param parm; 858902cb50aSBrian Somers const char* parmDescription; 859902cb50aSBrian Somers const char* description; 860902cb50aSBrian Somers const char* name; 861902cb50aSBrian Somers const char* shortName; 86224084f9bSBrian Somers }; 86324084f9bSBrian Somers 86424084f9bSBrian Somers /* 86524084f9bSBrian Somers * Table of known options. 86624084f9bSBrian Somers */ 86724084f9bSBrian Somers 86824084f9bSBrian Somers static struct OptionInfo optionTable[] = { 86924084f9bSBrian Somers 87024084f9bSBrian Somers { PacketAliasOption, 87124084f9bSBrian Somers PKT_ALIAS_UNREGISTERED_ONLY, 87224084f9bSBrian Somers YesNo, 87324084f9bSBrian Somers "[yes|no]", 87424084f9bSBrian Somers "alias only unregistered addresses", 87524084f9bSBrian Somers "unregistered_only", 87624084f9bSBrian Somers "u" }, 87724084f9bSBrian Somers 87824084f9bSBrian Somers { PacketAliasOption, 87924084f9bSBrian Somers PKT_ALIAS_LOG, 88024084f9bSBrian Somers YesNo, 88124084f9bSBrian Somers "[yes|no]", 88224084f9bSBrian Somers "enable logging", 88324084f9bSBrian Somers "log", 88424084f9bSBrian Somers "l" }, 88524084f9bSBrian Somers 88624084f9bSBrian Somers { PacketAliasOption, 88759a7c613SBrian Somers PKT_ALIAS_PROXY_ONLY, 88859a7c613SBrian Somers YesNo, 88959a7c613SBrian Somers "[yes|no]", 89059a7c613SBrian Somers "proxy only", 89159a7c613SBrian Somers "proxy_only", 89259a7c613SBrian Somers NULL }, 89359a7c613SBrian Somers 89459a7c613SBrian Somers { PacketAliasOption, 89559a7c613SBrian Somers PKT_ALIAS_REVERSE, 89659a7c613SBrian Somers YesNo, 89759a7c613SBrian Somers "[yes|no]", 89859a7c613SBrian Somers "operate in reverse mode", 89959a7c613SBrian Somers "reverse", 90059a7c613SBrian Somers NULL }, 90159a7c613SBrian Somers 90259a7c613SBrian Somers { PacketAliasOption, 90324084f9bSBrian Somers PKT_ALIAS_DENY_INCOMING, 90424084f9bSBrian Somers YesNo, 90524084f9bSBrian Somers "[yes|no]", 90624084f9bSBrian Somers "allow incoming connections", 90724084f9bSBrian Somers "deny_incoming", 90824084f9bSBrian Somers "d" }, 90924084f9bSBrian Somers 91024084f9bSBrian Somers { PacketAliasOption, 91124084f9bSBrian Somers PKT_ALIAS_USE_SOCKETS, 91224084f9bSBrian Somers YesNo, 91324084f9bSBrian Somers "[yes|no]", 91424084f9bSBrian Somers "use sockets to inhibit port conflict", 91524084f9bSBrian Somers "use_sockets", 91624084f9bSBrian Somers "s" }, 91724084f9bSBrian Somers 91824084f9bSBrian Somers { PacketAliasOption, 91924084f9bSBrian Somers PKT_ALIAS_SAME_PORTS, 92024084f9bSBrian Somers YesNo, 92124084f9bSBrian Somers "[yes|no]", 92224084f9bSBrian Somers "try to keep original port numbers for connections", 92324084f9bSBrian Somers "same_ports", 92424084f9bSBrian Somers "m" }, 92524084f9bSBrian Somers 92624084f9bSBrian Somers { Verbose, 92724084f9bSBrian Somers 0, 92824084f9bSBrian Somers YesNo, 92924084f9bSBrian Somers "[yes|no]", 93024084f9bSBrian Somers "verbose mode, dump packet information", 93124084f9bSBrian Somers "verbose", 93224084f9bSBrian Somers "v" }, 93324084f9bSBrian Somers 93424084f9bSBrian Somers { DynamicMode, 93524084f9bSBrian Somers 0, 93624084f9bSBrian Somers YesNo, 93724084f9bSBrian Somers "[yes|no]", 93824084f9bSBrian Somers "dynamic mode, automatically detect interface address changes", 93924084f9bSBrian Somers "dynamic", 94024084f9bSBrian Somers NULL }, 94124084f9bSBrian Somers 94224084f9bSBrian Somers { InPort, 94324084f9bSBrian Somers 0, 94424084f9bSBrian Somers Service, 94524084f9bSBrian Somers "number|service_name", 94624084f9bSBrian Somers "set port for incoming packets", 94724084f9bSBrian Somers "in_port", 94824084f9bSBrian Somers "i" }, 94924084f9bSBrian Somers 95024084f9bSBrian Somers { OutPort, 95124084f9bSBrian Somers 0, 95224084f9bSBrian Somers Service, 95324084f9bSBrian Somers "number|service_name", 95424084f9bSBrian Somers "set port for outgoing packets", 95524084f9bSBrian Somers "out_port", 95624084f9bSBrian Somers "o" }, 95724084f9bSBrian Somers 95824084f9bSBrian Somers { Port, 95924084f9bSBrian Somers 0, 96024084f9bSBrian Somers Service, 96124084f9bSBrian Somers "number|service_name", 96224084f9bSBrian Somers "set port (defaults to natd/divert)", 96324084f9bSBrian Somers "port", 96424084f9bSBrian Somers "p" }, 96524084f9bSBrian Somers 96624084f9bSBrian Somers { AliasAddress, 96724084f9bSBrian Somers 0, 96824084f9bSBrian Somers Address, 96924084f9bSBrian Somers "x.x.x.x", 97024084f9bSBrian Somers "address to use for aliasing", 97124084f9bSBrian Somers "alias_address", 97224084f9bSBrian Somers "a" }, 97324084f9bSBrian Somers 97411c2b3bfSRuslan Ermilov { TargetAddress, 97511c2b3bfSRuslan Ermilov 0, 97611c2b3bfSRuslan Ermilov Address, 97711c2b3bfSRuslan Ermilov "x.x.x.x", 97811c2b3bfSRuslan Ermilov "address to use for incoming sessions", 97911c2b3bfSRuslan Ermilov "target_address", 98011c2b3bfSRuslan Ermilov "t" }, 98111c2b3bfSRuslan Ermilov 98224084f9bSBrian Somers { InterfaceName, 98324084f9bSBrian Somers 0, 98424084f9bSBrian Somers String, 98524084f9bSBrian Somers "network_if_name", 98624084f9bSBrian Somers "take aliasing address from interface", 98724084f9bSBrian Somers "interface", 98824084f9bSBrian Somers "n" }, 98924084f9bSBrian Somers 99059a7c613SBrian Somers { ProxyRule, 99124084f9bSBrian Somers 0, 99224084f9bSBrian Somers String, 99359a7c613SBrian Somers "[type encode_ip_hdr|encode_tcp_stream] port xxxx server " 99459a7c613SBrian Somers "a.b.c.d:yyyy", 99559a7c613SBrian Somers "add transparent proxying / destination NAT", 99659a7c613SBrian Somers "proxy_rule", 99724084f9bSBrian Somers NULL }, 99824084f9bSBrian Somers 99924084f9bSBrian Somers { RedirectPort, 100024084f9bSBrian Somers 0, 100124084f9bSBrian Somers String, 1002bd690510SRuslan Ermilov "tcp|udp local_addr:local_port_range[,...] [public_addr:]public_port_range" 10035d8ee958SBrian Somers " [remote_addr[:remote_port_range]]", 10045d8ee958SBrian Somers "redirect a port (or ports) for incoming traffic", 100524084f9bSBrian Somers "redirect_port", 100624084f9bSBrian Somers NULL }, 100724084f9bSBrian Somers 10084330006dSRuslan Ermilov { RedirectProto, 10094330006dSRuslan Ermilov 0, 10104330006dSRuslan Ermilov String, 10114330006dSRuslan Ermilov "proto local_addr [public_addr] [remote_addr]", 10124330006dSRuslan Ermilov "redirect packets of a given proto", 10134330006dSRuslan Ermilov "redirect_proto", 10144330006dSRuslan Ermilov NULL }, 10154330006dSRuslan Ermilov 101624084f9bSBrian Somers { RedirectAddress, 101724084f9bSBrian Somers 0, 101824084f9bSBrian Somers String, 1019bd690510SRuslan Ermilov "local_addr[,...] public_addr", 102024084f9bSBrian Somers "define mapping between local and public addresses", 102124084f9bSBrian Somers "redirect_address", 102224084f9bSBrian Somers NULL }, 102324084f9bSBrian Somers 102424084f9bSBrian Somers { ConfigFile, 102524084f9bSBrian Somers 0, 102624084f9bSBrian Somers String, 102724084f9bSBrian Somers "file_name", 102824084f9bSBrian Somers "read options from configuration file", 102924084f9bSBrian Somers "config", 103059a7c613SBrian Somers "f" }, 103159a7c613SBrian Somers 103259a7c613SBrian Somers { LogDenied, 103359a7c613SBrian Somers 0, 103459a7c613SBrian Somers YesNo, 103559a7c613SBrian Somers "[yes|no]", 103659a7c613SBrian Somers "enable logging of denied incoming packets", 103759a7c613SBrian Somers "log_denied", 103859a7c613SBrian Somers NULL }, 103959a7c613SBrian Somers 104059a7c613SBrian Somers { LogFacility, 104159a7c613SBrian Somers 0, 104259a7c613SBrian Somers String, 104359a7c613SBrian Somers "facility", 104459a7c613SBrian Somers "name of syslog facility to use for logging", 104559a7c613SBrian Somers "log_facility", 1046bc4ebb98SRuslan Ermilov NULL }, 104759a7c613SBrian Somers 1048bc4ebb98SRuslan Ermilov { PunchFW, 1049bc4ebb98SRuslan Ermilov 0, 1050bc4ebb98SRuslan Ermilov String, 1051bc4ebb98SRuslan Ermilov "basenumber:count", 1052bc4ebb98SRuslan Ermilov "punch holes in the firewall for incoming FTP/IRC DCC connections", 1053bc4ebb98SRuslan Ermilov "punch_fw", 105484ef95bdSPoul-Henning Kamp NULL }, 105584ef95bdSPoul-Henning Kamp 105684ef95bdSPoul-Henning Kamp { LogIpfwDenied, 105784ef95bdSPoul-Henning Kamp 0, 105884ef95bdSPoul-Henning Kamp YesNo, 105984ef95bdSPoul-Henning Kamp "[yes|no]", 106084ef95bdSPoul-Henning Kamp "log packets converted by natd, but denied by ipfw", 106184ef95bdSPoul-Henning Kamp "log_ipfw_denied", 106284ef95bdSPoul-Henning Kamp NULL }, 106324084f9bSBrian Somers }; 106424084f9bSBrian Somers 1065b0f55af6SRuslan Ermilov static void ParseOption (const char* option, const char* parms) 106624084f9bSBrian Somers { 106724084f9bSBrian Somers int i; 106824084f9bSBrian Somers struct OptionInfo* info; 106924084f9bSBrian Somers int yesNoValue; 107024084f9bSBrian Somers int aliasValue; 107124084f9bSBrian Somers int numValue; 107267a886fbSBrian Somers u_short uNumValue; 1073902cb50aSBrian Somers const char* strValue; 107424084f9bSBrian Somers struct in_addr addrValue; 107524084f9bSBrian Somers int max; 107624084f9bSBrian Somers char* end; 107759a7c613SBrian Somers CODE* fac_record = NULL; 107824084f9bSBrian Somers /* 107924084f9bSBrian Somers * Find option from table. 108024084f9bSBrian Somers */ 108124084f9bSBrian Somers max = sizeof (optionTable) / sizeof (struct OptionInfo); 108224084f9bSBrian Somers for (i = 0, info = optionTable; i < max; i++, info++) { 108324084f9bSBrian Somers 108424084f9bSBrian Somers if (!strcmp (info->name, option)) 108524084f9bSBrian Somers break; 108624084f9bSBrian Somers 108724084f9bSBrian Somers if (info->shortName) 108824084f9bSBrian Somers if (!strcmp (info->shortName, option)) 108924084f9bSBrian Somers break; 109024084f9bSBrian Somers } 109124084f9bSBrian Somers 109224084f9bSBrian Somers if (i >= max) { 109324084f9bSBrian Somers 10940fc81af1SPhilippe Charnier warnx ("unknown option %s", option); 109524084f9bSBrian Somers Usage (); 109624084f9bSBrian Somers } 109724084f9bSBrian Somers 109867a886fbSBrian Somers uNumValue = 0; 109924084f9bSBrian Somers yesNoValue = 0; 110024084f9bSBrian Somers numValue = 0; 110124084f9bSBrian Somers strValue = NULL; 110224084f9bSBrian Somers /* 110324084f9bSBrian Somers * Check parameters. 110424084f9bSBrian Somers */ 110524084f9bSBrian Somers switch (info->parm) { 110624084f9bSBrian Somers case YesNo: 110724084f9bSBrian Somers if (!parms) 110824084f9bSBrian Somers parms = "yes"; 110924084f9bSBrian Somers 111024084f9bSBrian Somers if (!strcmp (parms, "yes")) 111124084f9bSBrian Somers yesNoValue = 1; 111224084f9bSBrian Somers else 111324084f9bSBrian Somers if (!strcmp (parms, "no")) 111424084f9bSBrian Somers yesNoValue = 0; 11150fc81af1SPhilippe Charnier else 11160fc81af1SPhilippe Charnier errx (1, "%s needs yes/no parameter", option); 111724084f9bSBrian Somers break; 111824084f9bSBrian Somers 111924084f9bSBrian Somers case Service: 11200fc81af1SPhilippe Charnier if (!parms) 112167a886fbSBrian Somers errx (1, "%s needs service name or " 112267a886fbSBrian Somers "port number parameter", 112367a886fbSBrian Somers option); 112424084f9bSBrian Somers 112567a886fbSBrian Somers uNumValue = StrToPort (parms, "divert"); 112624084f9bSBrian Somers break; 112724084f9bSBrian Somers 112824084f9bSBrian Somers case Numeric: 112924084f9bSBrian Somers if (parms) 113024084f9bSBrian Somers numValue = strtol (parms, &end, 10); 113124084f9bSBrian Somers else 1132902cb50aSBrian Somers end = NULL; 113324084f9bSBrian Somers 11340fc81af1SPhilippe Charnier if (end == parms) 11350fc81af1SPhilippe Charnier errx (1, "%s needs numeric parameter", option); 113624084f9bSBrian Somers break; 113724084f9bSBrian Somers 113824084f9bSBrian Somers case String: 113924084f9bSBrian Somers strValue = parms; 11400fc81af1SPhilippe Charnier if (!strValue) 11410fc81af1SPhilippe Charnier errx (1, "%s needs parameter", option); 114224084f9bSBrian Somers break; 114324084f9bSBrian Somers 114424084f9bSBrian Somers case None: 11450fc81af1SPhilippe Charnier if (parms) 11460fc81af1SPhilippe Charnier errx (1, "%s does not take parameters", option); 114724084f9bSBrian Somers break; 114824084f9bSBrian Somers 114924084f9bSBrian Somers case Address: 11500fc81af1SPhilippe Charnier if (!parms) 11510fc81af1SPhilippe Charnier errx (1, "%s needs address/host parameter", option); 115224084f9bSBrian Somers 115324084f9bSBrian Somers StrToAddr (parms, &addrValue); 115424084f9bSBrian Somers break; 115524084f9bSBrian Somers } 115624084f9bSBrian Somers 115724084f9bSBrian Somers switch (info->type) { 115824084f9bSBrian Somers case PacketAliasOption: 115924084f9bSBrian Somers 116024084f9bSBrian Somers aliasValue = yesNoValue ? info->packetAliasOpt : 0; 1161fb994b07SBrian Somers PacketAliasSetMode (aliasValue, info->packetAliasOpt); 116224084f9bSBrian Somers break; 116324084f9bSBrian Somers 116424084f9bSBrian Somers case Verbose: 116524084f9bSBrian Somers verbose = yesNoValue; 116624084f9bSBrian Somers break; 116724084f9bSBrian Somers 116824084f9bSBrian Somers case DynamicMode: 116924084f9bSBrian Somers dynamicMode = yesNoValue; 117024084f9bSBrian Somers break; 117124084f9bSBrian Somers 117224084f9bSBrian Somers case InPort: 117367a886fbSBrian Somers inPort = uNumValue; 117424084f9bSBrian Somers break; 117524084f9bSBrian Somers 117624084f9bSBrian Somers case OutPort: 117767a886fbSBrian Somers outPort = uNumValue; 117824084f9bSBrian Somers break; 117924084f9bSBrian Somers 118024084f9bSBrian Somers case Port: 118167a886fbSBrian Somers inOutPort = uNumValue; 118224084f9bSBrian Somers break; 118324084f9bSBrian Somers 118424084f9bSBrian Somers case AliasAddress: 118524084f9bSBrian Somers memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr)); 118624084f9bSBrian Somers break; 118724084f9bSBrian Somers 118811c2b3bfSRuslan Ermilov case TargetAddress: 118911c2b3bfSRuslan Ermilov PacketAliasSetTarget(addrValue); 119011c2b3bfSRuslan Ermilov break; 119111c2b3bfSRuslan Ermilov 119224084f9bSBrian Somers case RedirectPort: 119324084f9bSBrian Somers SetupPortRedirect (strValue); 119424084f9bSBrian Somers break; 119524084f9bSBrian Somers 11964330006dSRuslan Ermilov case RedirectProto: 11974330006dSRuslan Ermilov SetupProtoRedirect(strValue); 11984330006dSRuslan Ermilov break; 11994330006dSRuslan Ermilov 120024084f9bSBrian Somers case RedirectAddress: 120124084f9bSBrian Somers SetupAddressRedirect (strValue); 120224084f9bSBrian Somers break; 120324084f9bSBrian Somers 120459a7c613SBrian Somers case ProxyRule: 120559a7c613SBrian Somers PacketAliasProxyRule (strValue); 120659a7c613SBrian Somers break; 120759a7c613SBrian Somers 120824084f9bSBrian Somers case InterfaceName: 120924084f9bSBrian Somers if (ifName) 121024084f9bSBrian Somers free (ifName); 121124084f9bSBrian Somers 121224084f9bSBrian Somers ifName = strdup (strValue); 121324084f9bSBrian Somers break; 121424084f9bSBrian Somers 121524084f9bSBrian Somers case ConfigFile: 121624084f9bSBrian Somers ReadConfigFile (strValue); 121724084f9bSBrian Somers break; 121859a7c613SBrian Somers 121959a7c613SBrian Somers case LogDenied: 12203843533eSRuslan Ermilov logDropped = yesNoValue; 122159a7c613SBrian Somers break; 122259a7c613SBrian Somers 122359a7c613SBrian Somers case LogFacility: 122459a7c613SBrian Somers 122559a7c613SBrian Somers fac_record = facilitynames; 122659a7c613SBrian Somers while (fac_record->c_name != NULL) { 122759a7c613SBrian Somers 122859a7c613SBrian Somers if (!strcmp (fac_record->c_name, strValue)) { 122959a7c613SBrian Somers 123059a7c613SBrian Somers logFacility = fac_record->c_val; 123159a7c613SBrian Somers break; 123259a7c613SBrian Somers 123359a7c613SBrian Somers } 123459a7c613SBrian Somers else 123559a7c613SBrian Somers fac_record++; 123659a7c613SBrian Somers } 123759a7c613SBrian Somers 123859a7c613SBrian Somers if(fac_record->c_name == NULL) 123959a7c613SBrian Somers errx(1, "Unknown log facility name: %s", strValue); 124059a7c613SBrian Somers 124159a7c613SBrian Somers break; 1242bc4ebb98SRuslan Ermilov 1243bc4ebb98SRuslan Ermilov case PunchFW: 1244bc4ebb98SRuslan Ermilov SetupPunchFW(strValue); 1245bc4ebb98SRuslan Ermilov break; 12463843533eSRuslan Ermilov 124784ef95bdSPoul-Henning Kamp case LogIpfwDenied: 12483843533eSRuslan Ermilov logIpfwDenied = yesNoValue;; 12493843533eSRuslan Ermilov break; 125024084f9bSBrian Somers } 125124084f9bSBrian Somers } 125224084f9bSBrian Somers 1253902cb50aSBrian Somers void ReadConfigFile (const char* fileName) 125424084f9bSBrian Somers { 125524084f9bSBrian Somers FILE* file; 1256d99cc1daSRuslan Ermilov char *buf; 1257d99cc1daSRuslan Ermilov size_t len; 12582e7e7c71SRuslan Ermilov char *ptr, *p; 125924084f9bSBrian Somers char* option; 126024084f9bSBrian Somers 126124084f9bSBrian Somers file = fopen (fileName, "r"); 1262d99cc1daSRuslan Ermilov if (!file) 1263d99cc1daSRuslan Ermilov err(1, "cannot open config file %s", fileName); 126424084f9bSBrian Somers 1265d99cc1daSRuslan Ermilov while ((buf = fgetln(file, &len)) != NULL) { 1266d99cc1daSRuslan Ermilov if (buf[len - 1] == '\n') 1267d99cc1daSRuslan Ermilov buf[len - 1] = '\0'; 1268d99cc1daSRuslan Ermilov else 1269d99cc1daSRuslan Ermilov errx(1, "config file format error: " 1270d99cc1daSRuslan Ermilov "last line should end with newline"); 127124084f9bSBrian Somers 127224084f9bSBrian Somers /* 12732e7e7c71SRuslan Ermilov * Check for comments, strip off trailing spaces. 127424084f9bSBrian Somers */ 12752e7e7c71SRuslan Ermilov if ((ptr = strchr(buf, '#'))) 12762e7e7c71SRuslan Ermilov *ptr = '\0'; 12772e7e7c71SRuslan Ermilov for (ptr = buf; isspace(*ptr); ++ptr) 12782e7e7c71SRuslan Ermilov continue; 127924084f9bSBrian Somers if (*ptr == '\0') 128024084f9bSBrian Somers continue; 12812e7e7c71SRuslan Ermilov for (p = strchr(buf, '\0'); isspace(*--p);) 12822e7e7c71SRuslan Ermilov continue; 12832e7e7c71SRuslan Ermilov *++p = '\0'; 12842e7e7c71SRuslan Ermilov 128524084f9bSBrian Somers /* 128624084f9bSBrian Somers * Extract option name. 128724084f9bSBrian Somers */ 128824084f9bSBrian Somers option = ptr; 128924084f9bSBrian Somers while (*ptr && !isspace (*ptr)) 129024084f9bSBrian Somers ++ptr; 129124084f9bSBrian Somers 129224084f9bSBrian Somers if (*ptr != '\0') { 129324084f9bSBrian Somers 129424084f9bSBrian Somers *ptr = '\0'; 129524084f9bSBrian Somers ++ptr; 129624084f9bSBrian Somers } 129724084f9bSBrian Somers /* 129824084f9bSBrian Somers * Skip white space between name and parms. 129924084f9bSBrian Somers */ 130024084f9bSBrian Somers while (*ptr && isspace (*ptr)) 130124084f9bSBrian Somers ++ptr; 130224084f9bSBrian Somers 1303b0f55af6SRuslan Ermilov ParseOption (option, *ptr ? ptr : NULL); 130424084f9bSBrian Somers } 130524084f9bSBrian Somers 130624084f9bSBrian Somers fclose (file); 130724084f9bSBrian Somers } 130824084f9bSBrian Somers 130924084f9bSBrian Somers static void Usage () 131024084f9bSBrian Somers { 131124084f9bSBrian Somers int i; 131224084f9bSBrian Somers int max; 131324084f9bSBrian Somers struct OptionInfo* info; 131424084f9bSBrian Somers 131524084f9bSBrian Somers fprintf (stderr, "Recognized options:\n\n"); 131624084f9bSBrian Somers 131724084f9bSBrian Somers max = sizeof (optionTable) / sizeof (struct OptionInfo); 131824084f9bSBrian Somers for (i = 0, info = optionTable; i < max; i++, info++) { 131924084f9bSBrian Somers 132024084f9bSBrian Somers fprintf (stderr, "-%-20s %s\n", info->name, 132124084f9bSBrian Somers info->parmDescription); 132224084f9bSBrian Somers 132324084f9bSBrian Somers if (info->shortName) 132424084f9bSBrian Somers fprintf (stderr, "-%-20s %s\n", info->shortName, 132524084f9bSBrian Somers info->parmDescription); 132624084f9bSBrian Somers 132724084f9bSBrian Somers fprintf (stderr, " %s\n\n", info->description); 132824084f9bSBrian Somers } 132924084f9bSBrian Somers 133024084f9bSBrian Somers exit (1); 133124084f9bSBrian Somers } 133224084f9bSBrian Somers 1333902cb50aSBrian Somers void SetupPortRedirect (const char* parms) 133424084f9bSBrian Somers { 133524084f9bSBrian Somers char buf[128]; 133624084f9bSBrian Somers char* ptr; 1337bd690510SRuslan Ermilov char* serverPool; 133824084f9bSBrian Somers struct in_addr localAddr; 133924084f9bSBrian Somers struct in_addr publicAddr; 134024084f9bSBrian Somers struct in_addr remoteAddr; 13415d8ee958SBrian Somers port_range portRange; 13425d8ee958SBrian Somers u_short localPort = 0; 13435d8ee958SBrian Somers u_short publicPort = 0; 13445d8ee958SBrian Somers u_short remotePort = 0; 13455d8ee958SBrian Somers u_short numLocalPorts = 0; 13465d8ee958SBrian Somers u_short numPublicPorts = 0; 13475d8ee958SBrian Somers u_short numRemotePorts = 0; 134824084f9bSBrian Somers int proto; 134924084f9bSBrian Somers char* protoName; 135024084f9bSBrian Somers char* separator; 13515d8ee958SBrian Somers int i; 1352bd690510SRuslan Ermilov struct alias_link *link = NULL; 135324084f9bSBrian Somers 135424084f9bSBrian Somers strcpy (buf, parms); 135524084f9bSBrian Somers /* 135624084f9bSBrian Somers * Extract protocol. 135724084f9bSBrian Somers */ 135824084f9bSBrian Somers protoName = strtok (buf, " \t"); 13590fc81af1SPhilippe Charnier if (!protoName) 13600fc81af1SPhilippe Charnier errx (1, "redirect_port: missing protocol"); 136124084f9bSBrian Somers 136224084f9bSBrian Somers proto = StrToProto (protoName); 136324084f9bSBrian Somers /* 136424084f9bSBrian Somers * Extract local address. 136524084f9bSBrian Somers */ 136624084f9bSBrian Somers ptr = strtok (NULL, " \t"); 13670fc81af1SPhilippe Charnier if (!ptr) 13680fc81af1SPhilippe Charnier errx (1, "redirect_port: missing local address"); 136924084f9bSBrian Somers 1370bd690510SRuslan Ermilov separator = strchr(ptr, ','); 1371bd690510SRuslan Ermilov if (separator) { /* LSNAT redirection syntax. */ 1372bd690510SRuslan Ermilov localAddr.s_addr = INADDR_NONE; 1373bd690510SRuslan Ermilov localPort = ~0; 1374bd690510SRuslan Ermilov numLocalPorts = 1; 1375bd690510SRuslan Ermilov serverPool = ptr; 1376bd690510SRuslan Ermilov } else { 13775d8ee958SBrian Somers if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 ) 13785d8ee958SBrian Somers errx (1, "redirect_port: invalid local port range"); 13795d8ee958SBrian Somers 13805d8ee958SBrian Somers localPort = GETLOPORT(portRange); 13815d8ee958SBrian Somers numLocalPorts = GETNUMPORTS(portRange); 1382bd690510SRuslan Ermilov serverPool = NULL; 1383bd690510SRuslan Ermilov } 13845d8ee958SBrian Somers 138524084f9bSBrian Somers /* 13869c501140SBrian Somers * Extract public port and optionally address. 138724084f9bSBrian Somers */ 138824084f9bSBrian Somers ptr = strtok (NULL, " \t"); 13890fc81af1SPhilippe Charnier if (!ptr) 13900fc81af1SPhilippe Charnier errx (1, "redirect_port: missing public port"); 139124084f9bSBrian Somers 139224084f9bSBrian Somers separator = strchr (ptr, ':'); 13935d8ee958SBrian Somers if (separator) { 13945d8ee958SBrian Somers if (StrToAddrAndPortRange (ptr, &publicAddr, protoName, &portRange) != 0 ) 13955d8ee958SBrian Somers errx (1, "redirect_port: invalid public port range"); 139624084f9bSBrian Somers } 13975d8ee958SBrian Somers else { 13985d8ee958SBrian Somers publicAddr.s_addr = INADDR_ANY; 13995d8ee958SBrian Somers if (StrToPortRange (ptr, protoName, &portRange) != 0) 14005d8ee958SBrian Somers errx (1, "redirect_port: invalid public port range"); 14015d8ee958SBrian Somers } 14025d8ee958SBrian Somers 14035d8ee958SBrian Somers publicPort = GETLOPORT(portRange); 14045d8ee958SBrian Somers numPublicPorts = GETNUMPORTS(portRange); 140524084f9bSBrian Somers 140624084f9bSBrian Somers /* 140724084f9bSBrian Somers * Extract remote address and optionally port. 140824084f9bSBrian Somers */ 140924084f9bSBrian Somers ptr = strtok (NULL, " \t"); 141024084f9bSBrian Somers if (ptr) { 141124084f9bSBrian Somers separator = strchr (ptr, ':'); 1412ebe70c8fSWarner Losh if (separator) { 14135d8ee958SBrian Somers if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0) 14145d8ee958SBrian Somers errx (1, "redirect_port: invalid remote port range"); 1415ebe70c8fSWarner Losh } else { 14165d8ee958SBrian Somers SETLOPORT(portRange, 0); 14175d8ee958SBrian Somers SETNUMPORTS(portRange, 1); 141824084f9bSBrian Somers StrToAddr (ptr, &remoteAddr); 141924084f9bSBrian Somers } 142024084f9bSBrian Somers } 142124084f9bSBrian Somers else { 14225d8ee958SBrian Somers SETLOPORT(portRange, 0); 14235d8ee958SBrian Somers SETNUMPORTS(portRange, 1); 142424084f9bSBrian Somers remoteAddr.s_addr = INADDR_ANY; 142524084f9bSBrian Somers } 142624084f9bSBrian Somers 14275d8ee958SBrian Somers remotePort = GETLOPORT(portRange); 14285d8ee958SBrian Somers numRemotePorts = GETNUMPORTS(portRange); 14295d8ee958SBrian Somers 14305d8ee958SBrian Somers /* 14315d8ee958SBrian Somers * Make sure port ranges match up, then add the redirect ports. 14325d8ee958SBrian Somers */ 14335d8ee958SBrian Somers if (numLocalPorts != numPublicPorts) 14345d8ee958SBrian Somers errx (1, "redirect_port: port ranges must be equal in size"); 14355d8ee958SBrian Somers 14365d8ee958SBrian Somers /* Remote port range is allowed to be '0' which means all ports. */ 143729d97436SBrian Somers if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0)) 14385d8ee958SBrian Somers errx (1, "redirect_port: remote port must be 0 or equal to local port range in size"); 14395d8ee958SBrian Somers 14405d8ee958SBrian Somers for (i = 0 ; i < numPublicPorts ; ++i) { 14415d8ee958SBrian Somers /* If remotePort is all ports, set it to 0. */ 14425d8ee958SBrian Somers u_short remotePortCopy = remotePort + i; 14435d8ee958SBrian Somers if (numRemotePorts == 1 && remotePort == 0) 14445d8ee958SBrian Somers remotePortCopy = 0; 14455d8ee958SBrian Somers 1446bd690510SRuslan Ermilov link = PacketAliasRedirectPort (localAddr, 14475d8ee958SBrian Somers htons(localPort + i), 144824084f9bSBrian Somers remoteAddr, 14495d8ee958SBrian Somers htons(remotePortCopy), 145024084f9bSBrian Somers publicAddr, 14515d8ee958SBrian Somers htons(publicPort + i), 145224084f9bSBrian Somers proto); 145324084f9bSBrian Somers } 1454bd690510SRuslan Ermilov 1455bd690510SRuslan Ermilov /* 1456bd690510SRuslan Ermilov * Setup LSNAT server pool. 1457bd690510SRuslan Ermilov */ 1458bd690510SRuslan Ermilov if (serverPool != NULL && link != NULL) { 1459bd690510SRuslan Ermilov ptr = strtok(serverPool, ","); 1460bd690510SRuslan Ermilov while (ptr != NULL) { 1461bd690510SRuslan Ermilov if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0) 1462bd690510SRuslan Ermilov errx(1, "redirect_port: invalid local port range"); 1463bd690510SRuslan Ermilov 1464bd690510SRuslan Ermilov localPort = GETLOPORT(portRange); 1465bd690510SRuslan Ermilov if (GETNUMPORTS(portRange) != 1) 1466bd690510SRuslan Ermilov errx(1, "redirect_port: local port must be single in this context"); 1467bd690510SRuslan Ermilov PacketAliasAddServer(link, localAddr, htons(localPort)); 1468bd690510SRuslan Ermilov ptr = strtok(NULL, ","); 1469bd690510SRuslan Ermilov } 1470bd690510SRuslan Ermilov } 14715d8ee958SBrian Somers } 147224084f9bSBrian Somers 14734330006dSRuslan Ermilov void 14744330006dSRuslan Ermilov SetupProtoRedirect(const char* parms) 14754330006dSRuslan Ermilov { 14764330006dSRuslan Ermilov char buf[128]; 14774330006dSRuslan Ermilov char* ptr; 14784330006dSRuslan Ermilov struct in_addr localAddr; 14794330006dSRuslan Ermilov struct in_addr publicAddr; 14804330006dSRuslan Ermilov struct in_addr remoteAddr; 14814330006dSRuslan Ermilov int proto; 14824330006dSRuslan Ermilov char* protoName; 14834330006dSRuslan Ermilov struct protoent *protoent; 14844330006dSRuslan Ermilov 14854330006dSRuslan Ermilov strcpy (buf, parms); 14864330006dSRuslan Ermilov /* 14874330006dSRuslan Ermilov * Extract protocol. 14884330006dSRuslan Ermilov */ 14894330006dSRuslan Ermilov protoName = strtok(buf, " \t"); 14904330006dSRuslan Ermilov if (!protoName) 14914330006dSRuslan Ermilov errx(1, "redirect_proto: missing protocol"); 14924330006dSRuslan Ermilov 14934330006dSRuslan Ermilov protoent = getprotobyname(protoName); 14944330006dSRuslan Ermilov if (protoent == NULL) 14954330006dSRuslan Ermilov errx(1, "redirect_proto: unknown protocol %s", protoName); 14964330006dSRuslan Ermilov else 14974330006dSRuslan Ermilov proto = protoent->p_proto; 14984330006dSRuslan Ermilov /* 14994330006dSRuslan Ermilov * Extract local address. 15004330006dSRuslan Ermilov */ 15014330006dSRuslan Ermilov ptr = strtok(NULL, " \t"); 15024330006dSRuslan Ermilov if (!ptr) 15034330006dSRuslan Ermilov errx(1, "redirect_proto: missing local address"); 15044330006dSRuslan Ermilov else 15054330006dSRuslan Ermilov StrToAddr(ptr, &localAddr); 15064330006dSRuslan Ermilov /* 15074330006dSRuslan Ermilov * Extract optional public address. 15084330006dSRuslan Ermilov */ 15094330006dSRuslan Ermilov ptr = strtok(NULL, " \t"); 15104330006dSRuslan Ermilov if (ptr) 15114330006dSRuslan Ermilov StrToAddr(ptr, &publicAddr); 15124330006dSRuslan Ermilov else 15134330006dSRuslan Ermilov publicAddr.s_addr = INADDR_ANY; 15144330006dSRuslan Ermilov /* 15154330006dSRuslan Ermilov * Extract optional remote address. 15164330006dSRuslan Ermilov */ 15174330006dSRuslan Ermilov ptr = strtok(NULL, " \t"); 15184330006dSRuslan Ermilov if (ptr) 15194330006dSRuslan Ermilov StrToAddr(ptr, &remoteAddr); 15204330006dSRuslan Ermilov else 15214330006dSRuslan Ermilov remoteAddr.s_addr = INADDR_ANY; 15224330006dSRuslan Ermilov /* 15234330006dSRuslan Ermilov * Create aliasing link. 15244330006dSRuslan Ermilov */ 15254330006dSRuslan Ermilov (void)PacketAliasRedirectProto(localAddr, remoteAddr, publicAddr, 15264330006dSRuslan Ermilov proto); 15274330006dSRuslan Ermilov } 15284330006dSRuslan Ermilov 1529902cb50aSBrian Somers void SetupAddressRedirect (const char* parms) 153024084f9bSBrian Somers { 153124084f9bSBrian Somers char buf[128]; 153224084f9bSBrian Somers char* ptr; 1533bd690510SRuslan Ermilov char* separator; 153424084f9bSBrian Somers struct in_addr localAddr; 153524084f9bSBrian Somers struct in_addr publicAddr; 1536bd690510SRuslan Ermilov char* serverPool; 1537bd690510SRuslan Ermilov struct alias_link *link; 153824084f9bSBrian Somers 153924084f9bSBrian Somers strcpy (buf, parms); 154024084f9bSBrian Somers /* 154124084f9bSBrian Somers * Extract local address. 154224084f9bSBrian Somers */ 154324084f9bSBrian Somers ptr = strtok (buf, " \t"); 15440fc81af1SPhilippe Charnier if (!ptr) 15450fc81af1SPhilippe Charnier errx (1, "redirect_address: missing local address"); 154624084f9bSBrian Somers 1547bd690510SRuslan Ermilov separator = strchr(ptr, ','); 1548bd690510SRuslan Ermilov if (separator) { /* LSNAT redirection syntax. */ 1549bd690510SRuslan Ermilov localAddr.s_addr = INADDR_NONE; 1550bd690510SRuslan Ermilov serverPool = ptr; 1551bd690510SRuslan Ermilov } else { 155224084f9bSBrian Somers StrToAddr (ptr, &localAddr); 1553bd690510SRuslan Ermilov serverPool = NULL; 1554bd690510SRuslan Ermilov } 155524084f9bSBrian Somers /* 155624084f9bSBrian Somers * Extract public address. 155724084f9bSBrian Somers */ 155824084f9bSBrian Somers ptr = strtok (NULL, " \t"); 15590fc81af1SPhilippe Charnier if (!ptr) 15600fc81af1SPhilippe Charnier errx (1, "redirect_address: missing public address"); 156124084f9bSBrian Somers 156224084f9bSBrian Somers StrToAddr (ptr, &publicAddr); 1563bd690510SRuslan Ermilov link = PacketAliasRedirectAddr(localAddr, publicAddr); 1564bd690510SRuslan Ermilov 1565bd690510SRuslan Ermilov /* 1566bd690510SRuslan Ermilov * Setup LSNAT server pool. 1567bd690510SRuslan Ermilov */ 1568bd690510SRuslan Ermilov if (serverPool != NULL && link != NULL) { 1569bd690510SRuslan Ermilov ptr = strtok(serverPool, ","); 1570bd690510SRuslan Ermilov while (ptr != NULL) { 1571bd690510SRuslan Ermilov StrToAddr(ptr, &localAddr); 1572bd690510SRuslan Ermilov PacketAliasAddServer(link, localAddr, htons(~0)); 1573bd690510SRuslan Ermilov ptr = strtok(NULL, ","); 1574bd690510SRuslan Ermilov } 1575bd690510SRuslan Ermilov } 157624084f9bSBrian Somers } 157724084f9bSBrian Somers 1578902cb50aSBrian Somers void StrToAddr (const char* str, struct in_addr* addr) 157924084f9bSBrian Somers { 158024084f9bSBrian Somers struct hostent* hp; 158124084f9bSBrian Somers 158224084f9bSBrian Somers if (inet_aton (str, addr)) 158324084f9bSBrian Somers return; 158424084f9bSBrian Somers 158524084f9bSBrian Somers hp = gethostbyname (str); 15860fc81af1SPhilippe Charnier if (!hp) 15870fc81af1SPhilippe Charnier errx (1, "unknown host %s", str); 158824084f9bSBrian Somers 158924084f9bSBrian Somers memcpy (addr, hp->h_addr, sizeof (struct in_addr)); 159024084f9bSBrian Somers } 159124084f9bSBrian Somers 1592902cb50aSBrian Somers u_short StrToPort (const char* str, const char* proto) 159324084f9bSBrian Somers { 159467a886fbSBrian Somers u_short port; 159524084f9bSBrian Somers struct servent* sp; 159624084f9bSBrian Somers char* end; 159724084f9bSBrian Somers 159824084f9bSBrian Somers port = strtol (str, &end, 10); 159924084f9bSBrian Somers if (end != str) 160027c20503SBrian Somers return htons (port); 160124084f9bSBrian Somers 160224084f9bSBrian Somers sp = getservbyname (str, proto); 16030fc81af1SPhilippe Charnier if (!sp) 16040fc81af1SPhilippe Charnier errx (1, "unknown service %s/%s", str, proto); 160524084f9bSBrian Somers 160624084f9bSBrian Somers return sp->s_port; 160724084f9bSBrian Somers } 160824084f9bSBrian Somers 1609902cb50aSBrian Somers int StrToPortRange (const char* str, const char* proto, port_range *portRange) 16105d8ee958SBrian Somers { 16115d8ee958SBrian Somers char* sep; 16125d8ee958SBrian Somers struct servent* sp; 16135d8ee958SBrian Somers char* end; 16145d8ee958SBrian Somers u_short loPort; 16155d8ee958SBrian Somers u_short hiPort; 16165d8ee958SBrian Somers 16175d8ee958SBrian Somers /* First see if this is a service, return corresponding port if so. */ 16185d8ee958SBrian Somers sp = getservbyname (str,proto); 16195d8ee958SBrian Somers if (sp) { 16205d8ee958SBrian Somers SETLOPORT(*portRange, ntohs(sp->s_port)); 16215d8ee958SBrian Somers SETNUMPORTS(*portRange, 1); 16225d8ee958SBrian Somers return 0; 16235d8ee958SBrian Somers } 16245d8ee958SBrian Somers 16255d8ee958SBrian Somers /* Not a service, see if it's a single port or port range. */ 16265d8ee958SBrian Somers sep = strchr (str, '-'); 16275d8ee958SBrian Somers if (sep == NULL) { 16285d8ee958SBrian Somers SETLOPORT(*portRange, strtol(str, &end, 10)); 16295d8ee958SBrian Somers if (end != str) { 16305d8ee958SBrian Somers /* Single port. */ 16315d8ee958SBrian Somers SETNUMPORTS(*portRange, 1); 16325d8ee958SBrian Somers return 0; 16335d8ee958SBrian Somers } 16345d8ee958SBrian Somers 16355d8ee958SBrian Somers /* Error in port range field. */ 16365d8ee958SBrian Somers errx (1, "unknown service %s/%s", str, proto); 16375d8ee958SBrian Somers } 16385d8ee958SBrian Somers 16395d8ee958SBrian Somers /* Port range, get the values and sanity check. */ 16405d8ee958SBrian Somers sscanf (str, "%hu-%hu", &loPort, &hiPort); 16415d8ee958SBrian Somers SETLOPORT(*portRange, loPort); 16425d8ee958SBrian Somers SETNUMPORTS(*portRange, 0); /* Error by default */ 16435d8ee958SBrian Somers if (loPort <= hiPort) 16445d8ee958SBrian Somers SETNUMPORTS(*portRange, hiPort - loPort + 1); 16455d8ee958SBrian Somers 16465d8ee958SBrian Somers if (GETNUMPORTS(*portRange) == 0) 16475d8ee958SBrian Somers errx (1, "invalid port range %s", str); 16485d8ee958SBrian Somers 16495d8ee958SBrian Somers return 0; 16505d8ee958SBrian Somers } 16515d8ee958SBrian Somers 16525d8ee958SBrian Somers 1653902cb50aSBrian Somers int StrToProto (const char* str) 165424084f9bSBrian Somers { 165524084f9bSBrian Somers if (!strcmp (str, "tcp")) 165624084f9bSBrian Somers return IPPROTO_TCP; 165724084f9bSBrian Somers 165824084f9bSBrian Somers if (!strcmp (str, "udp")) 165924084f9bSBrian Somers return IPPROTO_UDP; 166024084f9bSBrian Somers 16610fc81af1SPhilippe Charnier errx (1, "unknown protocol %s. Expected tcp or udp", str); 166224084f9bSBrian Somers } 166324084f9bSBrian Somers 1664902cb50aSBrian Somers int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange) 166524084f9bSBrian Somers { 166624084f9bSBrian Somers char* ptr; 166724084f9bSBrian Somers 166824084f9bSBrian Somers ptr = strchr (str, ':'); 16690fc81af1SPhilippe Charnier if (!ptr) 16700fc81af1SPhilippe Charnier errx (1, "%s is missing port number", str); 167124084f9bSBrian Somers 167224084f9bSBrian Somers *ptr = '\0'; 167324084f9bSBrian Somers ++ptr; 167424084f9bSBrian Somers 167524084f9bSBrian Somers StrToAddr (str, addr); 16765d8ee958SBrian Somers return StrToPortRange (ptr, proto, portRange); 167724084f9bSBrian Somers } 1678bc4ebb98SRuslan Ermilov 1679bc4ebb98SRuslan Ermilov static void 1680bc4ebb98SRuslan Ermilov SetupPunchFW(const char *strValue) 1681bc4ebb98SRuslan Ermilov { 1682bc4ebb98SRuslan Ermilov unsigned int base, num; 1683bc4ebb98SRuslan Ermilov 1684bc4ebb98SRuslan Ermilov if (sscanf(strValue, "%u:%u", &base, &num) != 2) 1685bc4ebb98SRuslan Ermilov errx(1, "punch_fw: basenumber:count parameter required"); 1686bc4ebb98SRuslan Ermilov 1687bc4ebb98SRuslan Ermilov PacketAliasSetFWBase(base, num); 1688bc4ebb98SRuslan Ermilov (void)PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW); 1689bc4ebb98SRuslan Ermilov } 1690