diff options
Diffstat (limited to 'usr.sbin/natd/natd.c')
-rw-r--r-- | usr.sbin/natd/natd.c | 1396 |
1 files changed, 1396 insertions, 0 deletions
diff --git a/usr.sbin/natd/natd.c b/usr.sbin/natd/natd.c new file mode 100644 index 0000000..f166765 --- /dev/null +++ b/usr.sbin/natd/natd.c @@ -0,0 +1,1396 @@ +/* + * natd - Network Address Translation Daemon for FreeBSD. + * + * This software ois provided free of charge, with no + * warranty of any kind, either expressed or implied. + * Use at your own risk. + * + * You may copy, modify and distribute this software (natd.c) freely. + * + * Ari Suutari (ari@kn6-045.ktvlpr.inet.fi, ari@ps.carel.fi) + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <errno.h> +#include <signal.h> + +#include <netdb.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <machine/in_cksum.h> +#include <netinet/tcp.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/route.h> +#include <arpa/inet.h> + +#include <syslog.h> +#include <alias.h> + +#include "natd.h" + +/* + * Default values for input and output + * divert socket ports. + */ + +#define DEFAULT_SERVICE "natd" + +/* + * Function prototypes. + */ + +static void DoAliasing (int fd); +static void DaemonMode (); +static void HandleRoutingInfo (int fd); +static void Usage (); +static void PrintPacket (struct ip*); +static void SetAliasAddressFromIfName (char* ifName); +static void InitiateShutdown (); +static void Shutdown (); +static void RefreshAddr (); +static void ParseOption (char* option, char* parms, int cmdLine); +static void ReadConfigFile (char* fileName); +static void SetupPermanentLink (char* parms); +static void SetupPortRedirect (char* parms); +static void SetupAddressRedirect (char* parms); +static void StrToAddr (char* str, struct in_addr* addr); +static int StrToPort (char* str, char* proto); +static int StrToProto (char* str); +static int StrToAddrAndPort (char* str, struct in_addr* addr, char* proto); +static void ParseArgs (int argc, char** argv); + +/* + * Globals. + */ + +static int verbose; +static int background; +static int running; +static int assignAliasAddr; +static char* ifName; +static int ifIndex; +static int inPort; +static int outPort; +static int inOutPort; +static struct in_addr aliasAddr; +static int dynamicMode; +static int ifMTU; +static int aliasOverhead; +static int icmpSock; + +int main (int argc, char** argv) +{ + int divertIn; + int divertOut; + int divertInOut; + int routeSock; + struct sockaddr_in addr; + fd_set readMask; + int fdMax; +/* + * Initialize packet aliasing software. + * Done already here to be able to alter option bits + * during command line and configuration file processing. + */ + InitPacketAlias (); +/* + * Parse options. + */ + inPort = 0; + outPort = 0; + verbose = 0; + inOutPort = 0; + ifName = NULL; + ifMTU = -1; + background = 0; + running = 1; + assignAliasAddr = 0; + aliasAddr.s_addr = INADDR_NONE; + aliasOverhead = 12; + dynamicMode = 0; + + ParseArgs (argc, argv); +/* + * Check that valid aliasing address has been given. + */ + if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL) { + + fprintf (stderr, "Aliasing address not given.\n"); + exit (1); + } + + if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL) { + + fprintf (stderr, "Both alias address and interface name " + "are not allowed.\n"); + exit (1); + } +/* + * Check that valid port number is known. + */ + if (inPort != 0 || outPort != 0) + if (inPort == 0 || outPort == 0) { + + fprintf (stderr, "Both input and output ports" + " are required.\n"); + exit (1); + } + + if (inPort == 0 && outPort == 0 && inOutPort == 0) + ParseOption ("port", DEFAULT_SERVICE, 0); + +/* + * Create divert sockets. Use only one socket if -p was specified + * on command line. Otherwise, create separate sockets for + * outgoing and incoming connnections. + */ + if (inOutPort) { + + divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); + if (divertInOut == -1) + Quit ("Unable to create divert socket."); + + divertIn = -1; + divertOut = -1; +/* + * Bind socket. + */ + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = inOutPort; + + if (bind (divertInOut, + (struct sockaddr*) &addr, + sizeof addr) == -1) + Quit ("Unable to bind divert socket."); + } + else { + + divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); + if (divertIn == -1) + Quit ("Unable to create incoming divert socket."); + + divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT); + if (divertOut == -1) + Quit ("Unable to create outgoing divert socket."); + + divertInOut = -1; + +/* + * Bind divert sockets. + */ + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = inPort; + + if (bind (divertIn, + (struct sockaddr*) &addr, + sizeof addr) == -1) + Quit ("Unable to bind incoming divert socket."); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = outPort; + + if (bind (divertOut, + (struct sockaddr*) &addr, + sizeof addr) == -1) + Quit ("Unable to bind outgoing divert socket."); + } +/* + * Create routing socket if interface name specified. + */ + if (ifName && dynamicMode) { + + routeSock = socket (PF_ROUTE, SOCK_RAW, 0); + if (routeSock == -1) + Quit ("Unable to create routing info socket."); + } + else + routeSock = -1; +/* + * Create socket for sending ICMP messages. + */ + icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (icmpSock == -1) + Quit ("Unable to create ICMP socket."); +/* + * Become a daemon unless verbose mode was requested. + */ + if (!verbose) + DaemonMode (); +/* + * Catch signals to manage shutdown and + * refresh of interface address. + */ + signal (SIGTERM, InitiateShutdown); + signal (SIGHUP, RefreshAddr); +/* + * Set alias address if it has been given. + */ + if (aliasAddr.s_addr != INADDR_NONE) + SetPacketAliasAddress (aliasAddr); + + if (divertInOut != -1 && !ifName) { + +/* + * When using only one socket, just call + * DoAliasing repeatedly to process packets. + */ + while (running) + DoAliasing (divertInOut); + } + else { +/* + * We need largest descriptor number for select. + */ + + fdMax = -1; + + if (divertIn > fdMax) + fdMax = divertIn; + + if (divertOut > fdMax) + fdMax = divertOut; + + if (divertInOut > fdMax) + fdMax = divertInOut; + + if (routeSock > fdMax) + fdMax = routeSock; + + while (running) { +/* + * Build read mask from socket descriptors to select. + */ + FD_ZERO (&readMask); + + if (divertIn != -1) + FD_SET (divertIn, &readMask); + + if (divertOut != -1) + FD_SET (divertOut, &readMask); + + if (divertInOut != -1) + FD_SET (divertInOut, &readMask); + + if (routeSock != -1) + FD_SET (routeSock, &readMask); + + if (select (fdMax + 1, + &readMask, + NULL, + NULL, + NULL) == -1) { + + if (errno == EINTR) + continue; + + Quit ("Select failed."); + } + + if (divertIn != -1) + if (FD_ISSET (divertIn, &readMask)) + DoAliasing (divertIn); + + if (divertOut != -1) + if (FD_ISSET (divertOut, &readMask)) + DoAliasing (divertOut); + + if (divertInOut != -1) + if (FD_ISSET (divertInOut, &readMask)) + DoAliasing (divertInOut); + + if (routeSock != -1) + if (FD_ISSET (routeSock, &readMask)) + HandleRoutingInfo (routeSock); + } + } + + if (background) + unlink (PIDFILE); + + return 0; +} + +static void DaemonMode () +{ + FILE* pidFile; + + daemon (0, 0); + background = 1; + + pidFile = fopen (PIDFILE, "w"); + if (pidFile) { + + fprintf (pidFile, "%d\n", getpid ()); + fclose (pidFile); + } +} + +static void ParseArgs (int argc, char** argv) +{ + int arg; + char* parm; + char* opt; + char parmBuf[256]; + + for (arg = 1; arg < argc; arg++) { + + opt = argv[arg]; + if (*opt != '-') { + + fprintf (stderr, "Invalid option %s.\n", opt); + Usage (); + } + + parm = NULL; + parmBuf[0] = '\0'; + + while (arg < argc - 1) { + + if (argv[arg + 1][0] == '-') + break; + + if (parm) + strcat (parmBuf, " "); + + ++arg; + parm = parmBuf; + strcat (parmBuf, argv[arg]); + } + + ParseOption (opt + 1, parm, 1); + } +} + +static void DoAliasing (int fd) +{ + int bytes; + int origBytes; + char buf[IP_MAXPACKET]; + struct sockaddr_in addr; + int wrote; + int addrSize; + struct ip* ip; + char msgBuf[80]; + + if (assignAliasAddr) { + + SetAliasAddressFromIfName (ifName); + assignAliasAddr = 0; + } +/* + * Get packet from socket. + */ + addrSize = sizeof addr; + origBytes = recvfrom (fd, + buf, + sizeof buf, + 0, + (struct sockaddr*) &addr, + &addrSize); + + if (origBytes == -1) { + + if (errno != EINTR) + Warn ("Read from divert socket failed."); + + return; + } +/* + * This is a IP packet. + */ + ip = (struct ip*) buf; + + if (verbose) { + +/* + * Print packet direction and protocol type. + */ + + if (addr.sin_addr.s_addr == INADDR_ANY) + printf ("Out "); + else + printf ("In "); + + switch (ip->ip_p) { + case IPPROTO_TCP: + printf ("[TCP] "); + break; + + case IPPROTO_UDP: + printf ("[UDP] "); + break; + + case IPPROTO_ICMP: + printf ("[ICMP] "); + break; + + default: + printf ("[?] "); + break; + } +/* + * Print addresses. + */ + PrintPacket (ip); + } + + if (addr.sin_addr.s_addr == INADDR_ANY) { +/* + * Outgoing packets. Do aliasing. + */ + PacketAliasOut (buf, IP_MAXPACKET); + } + else { +/* + * Incoming packets may have ip checksum zeroed + * when read from divert socket. Re-calculate it. + */ + if (ip->ip_sum == 0) + ip->ip_sum = in_cksum_hdr (ip); +/* + * Do aliasing. + */ + PacketAliasIn (buf, IP_MAXPACKET); + } +/* + * Length might have changed during aliasing. + */ + bytes = ntohs (ip->ip_len); +/* + * Update alias overhead size for outgoing packets. + */ + if (addr.sin_addr.s_addr == INADDR_ANY && + bytes - origBytes > aliasOverhead) + aliasOverhead = bytes - origBytes; + + if (verbose) { + +/* + * Print addresses after aliasing. + */ + printf (" aliased to\n"); + printf (" "); + PrintPacket (ip); + printf ("\n"); + } +/* + * Put packet back for processing. + */ + wrote = sendto (fd, + buf, + bytes, + 0, + (struct sockaddr*) &addr, + sizeof addr); + + if (wrote != bytes) { + + if (errno == EMSGSIZE) { + + if (addr.sin_addr.s_addr == INADDR_ANY && + ifMTU != -1) + SendNeedFragIcmp (icmpSock, + (struct ip*) buf, + ifMTU - aliasOverhead); + } + else { + + sprintf (msgBuf, "Failed to write packet back."); + Warn (msgBuf); + } + } +} + +static void HandleRoutingInfo (int fd) +{ + int bytes; + struct if_msghdr ifMsg; +/* + * Get packet from socket. + */ + bytes = read (fd, &ifMsg, sizeof ifMsg); + if (bytes == -1) { + + Warn ("Read from routing socket failed."); + return; + } + + if (ifMsg.ifm_version != RTM_VERSION) { + + Warn ("Unexpected packet read from routing socket."); + return; + } + + if (verbose) + printf ("Routing message %X received.\n", ifMsg.ifm_type); + + if (ifMsg.ifm_type != RTM_NEWADDR) + return; + + if (verbose && ifMsg.ifm_index == ifIndex) + printf ("Interface address has changed.\n"); + + if (ifMsg.ifm_index == ifIndex) + assignAliasAddr = 1; +} + +static void PrintPacket (struct ip* ip) +{ + struct tcphdr* tcphdr; + + if (ip->ip_p == IPPROTO_TCP) + tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2)); + else + tcphdr = NULL; + + printf ("%s", inet_ntoa (ip->ip_src)); + if (tcphdr) + printf (":%d", ntohs (tcphdr->th_sport)); + + printf (" -> "); + printf ("%s", inet_ntoa (ip->ip_dst)); + if (tcphdr) + printf (":%d", ntohs (tcphdr->th_dport)); +} + +static void SetAliasAddressFromIfName (char* ifName) +{ + struct ifconf cf; + struct ifreq buf[32]; + char msg[80]; + struct ifreq* ifPtr; + int extra; + int helperSock; + int bytes; + struct sockaddr_in* addr; + int found; + struct ifreq req; + char last[10]; +/* + * Create a dummy socket to access interface information. + */ + helperSock = socket (AF_INET, SOCK_DGRAM, 0); + if (helperSock == -1) { + + Quit ("Failed to create helper socket."); + exit (1); + } + + cf.ifc_len = sizeof (buf); + cf.ifc_req = buf; +/* + * Get interface data. + */ + if (ioctl (helperSock, SIOCGIFCONF, &cf) == -1) { + + Quit ("Ioctl SIOCGIFCONF failed."); + exit (1); + } + + ifIndex = 0; + ifPtr = buf; + bytes = cf.ifc_len; + found = 0; + last[0] = '\0'; +/* + * Loop through interfaces until one with + * given name is found. This is done to + * find correct interface index for routing + * message processing. + */ + while (bytes) { + + if (ifPtr->ifr_addr.sa_family == AF_INET && + !strcmp (ifPtr->ifr_name, ifName)) { + + found = 1; + break; + } + + if (strcmp (last, ifPtr->ifr_name)) { + + strcpy (last, ifPtr->ifr_name); + ++ifIndex; + } + + extra = ifPtr->ifr_addr.sa_len - sizeof (struct sockaddr); + + ifPtr++; + ifPtr = (struct ifreq*) ((char*) ifPtr + extra); + bytes -= sizeof (struct ifreq) + extra; + } + + if (!found) { + + close (helperSock); + sprintf (msg, "Unknown interface name %s.\n", ifName); + Quit (msg); + } +/* + * Get MTU size. + */ + strcpy (req.ifr_name, ifName); + + if (ioctl (helperSock, SIOCGIFMTU, &req) == -1) + Quit ("Cannot get interface mtu size."); + + ifMTU = req.ifr_mtu; +/* + * Get interface address. + */ + if (ioctl (helperSock, SIOCGIFADDR, &req) == -1) + Quit ("Cannot get interface address."); + + addr = (struct sockaddr_in*) &req.ifr_addr; + SetPacketAliasAddress (addr->sin_addr); + syslog (LOG_INFO, "Aliasing to %s, mtu %d bytes", + inet_ntoa (addr->sin_addr), + ifMTU); + + close (helperSock); +} + +void Quit (char* msg) +{ + Warn (msg); + exit (1); +} + +void Warn (char* msg) +{ + if (background) + syslog (LOG_ALERT, "%s (%m)", msg); + else + perror (msg); +} + +static void RefreshAddr () +{ + signal (SIGHUP, RefreshAddr); + if (ifName) + assignAliasAddr = 1; +} + +static void InitiateShutdown () +{ +/* + * Start timer to allow kernel gracefully + * shutdown existing connections when system + * is shut down. + */ + signal (SIGALRM, Shutdown); + alarm (10); +} + +static void Shutdown () +{ + running = 0; +} + +/* + * Different options recognized by this program. + */ + +enum Option { + + PacketAliasOption, + Verbose, + InPort, + OutPort, + Port, + AliasAddress, + InterfaceName, + PermanentLink, + RedirectPort, + RedirectAddress, + ConfigFile, + DynamicMode +}; + +enum Param { + + YesNo, + Numeric, + String, + None, + Address, + Service +}; + +/* + * Option information structure (used by ParseOption). + */ + +struct OptionInfo { + + enum Option type; + int packetAliasOpt; + enum Param parm; + char* parmDescription; + char* description; + char* name; + char* shortName; +}; + +/* + * Table of known options. + */ + +static struct OptionInfo optionTable[] = { + + { PacketAliasOption, + PKT_ALIAS_UNREGISTERED_ONLY, + YesNo, + "[yes|no]", + "alias only unregistered addresses", + "unregistered_only", + "u" }, + + { PacketAliasOption, + PKT_ALIAS_LOG, + YesNo, + "[yes|no]", + "enable logging", + "log", + "l" }, + + { PacketAliasOption, + PKT_ALIAS_DENY_INCOMING, + YesNo, + "[yes|no]", + "allow incoming connections", + "deny_incoming", + "d" }, + + { PacketAliasOption, + PKT_ALIAS_USE_SOCKETS, + YesNo, + "[yes|no]", + "use sockets to inhibit port conflict", + "use_sockets", + "s" }, + + { PacketAliasOption, + PKT_ALIAS_SAME_PORTS, + YesNo, + "[yes|no]", + "try to keep original port numbers for connections", + "same_ports", + "m" }, + + { Verbose, + 0, + YesNo, + "[yes|no]", + "verbose mode, dump packet information", + "verbose", + "v" }, + + { DynamicMode, + 0, + YesNo, + "[yes|no]", + "dynamic mode, automatically detect interface address changes", + "dynamic", + NULL }, + + { InPort, + 0, + Service, + "number|service_name", + "set port for incoming packets", + "in_port", + "i" }, + + { OutPort, + 0, + Service, + "number|service_name", + "set port for outgoing packets", + "out_port", + "o" }, + + { Port, + 0, + Service, + "number|service_name", + "set port (defaults to natd/divert)", + "port", + "p" }, + + { AliasAddress, + 0, + Address, + "x.x.x.x", + "address to use for aliasing", + "alias_address", + "a" }, + + { InterfaceName, + 0, + String, + "network_if_name", + "take aliasing address from interface", + "interface", + "n" }, + + { PermanentLink, + 0, + String, + "tcp|udp src:port dst:port alias", + "define permanent link for incoming connection", + "permanent_link", + NULL }, + + { RedirectPort, + 0, + String, + "tcp|udp local_addr:local_port [public_addr:]public_port" + " [remote_addr[:remote_port]]", + "redirect a port for incoming traffic", + "redirect_port", + NULL }, + + { RedirectAddress, + 0, + String, + "local_addr public_addr", + "define mapping between local and public addresses", + "redirect_address", + NULL }, + + { ConfigFile, + 0, + String, + "file_name", + "read options from configuration file", + "config", + "f" } +}; + +static void ParseOption (char* option, char* parms, int cmdLine) +{ + int i; + struct OptionInfo* info; + int yesNoValue; + int aliasValue; + int numValue; + char* strValue; + struct in_addr addrValue; + int max; + char* end; +/* + * Find option from table. + */ + max = sizeof (optionTable) / sizeof (struct OptionInfo); + for (i = 0, info = optionTable; i < max; i++, info++) { + + if (!strcmp (info->name, option)) + break; + + if (info->shortName) + if (!strcmp (info->shortName, option)) + break; + } + + if (i >= max) { + + fprintf (stderr, "Unknown option %s.\n", option); + Usage (); + } + + yesNoValue = 0; + numValue = 0; + strValue = NULL; +/* + * Check parameters. + */ + switch (info->parm) { + case YesNo: + if (!parms) + parms = "yes"; + + if (!strcmp (parms, "yes")) + yesNoValue = 1; + else + if (!strcmp (parms, "no")) + yesNoValue = 0; + else { + + fprintf (stderr, "%s needs yes/no parameter.\n", + option); + exit (1); + } + break; + + case Service: + if (!parms) { + + fprintf (stderr, "%s needs service name or " + "port number parameter.\n", + option); + exit (1); + } + + numValue = StrToPort (parms, "divert"); + break; + + case Numeric: + if (parms) + numValue = strtol (parms, &end, 10); + else + end = parms; + + if (end == parms) { + + fprintf (stderr, "%s needs numeric parameter.\n", + option); + exit (1); + } + break; + + case String: + strValue = parms; + if (!strValue) { + + fprintf (stderr, "%s needs parameter.\n", + option); + exit (1); + } + break; + + case None: + if (parms) { + + fprintf (stderr, "%s does not take parameters.\n", + option); + exit (1); + } + break; + + case Address: + if (!parms) { + + fprintf (stderr, "%s needs address/host parameter.\n", + option); + exit (1); + } + + StrToAddr (parms, &addrValue); + break; + } + + switch (info->type) { + case PacketAliasOption: + + aliasValue = yesNoValue ? info->packetAliasOpt : 0; + SetPacketAliasMode (aliasValue, info->packetAliasOpt); + break; + + case Verbose: + verbose = yesNoValue; + break; + + case DynamicMode: + dynamicMode = yesNoValue; + break; + + case InPort: + inPort = numValue; + break; + + case OutPort: + outPort = numValue; + break; + + case Port: + inOutPort = numValue; + break; + + case AliasAddress: + memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr)); + break; + + case PermanentLink: + SetupPermanentLink (strValue); + break; + + case RedirectPort: + SetupPortRedirect (strValue); + break; + + case RedirectAddress: + SetupAddressRedirect (strValue); + break; + + case InterfaceName: + if (ifName) + free (ifName); + + ifName = strdup (strValue); + assignAliasAddr = 1; + break; + + case ConfigFile: + ReadConfigFile (strValue); + break; + } +} + +void ReadConfigFile (char* fileName) +{ + FILE* file; + char buf[128]; + char* ptr; + char* option; + + file = fopen (fileName, "r"); + if (!file) { + + sprintf (buf, "Cannot open config file %s.\n", fileName); + Quit (buf); + } + + while (fgets (buf, sizeof (buf), file)) { + + ptr = strchr (buf, '\n'); + if (!ptr) { + + fprintf (stderr, "config line too link: %s\n", buf); + exit (1); + } + + *ptr = '\0'; + if (buf[0] == '#') + continue; + + ptr = buf; +/* + * Skip white space at beginning of line. + */ + while (*ptr && isspace (*ptr)) + ++ptr; + + if (*ptr == '\0') + continue; +/* + * Extract option name. + */ + option = ptr; + while (*ptr && !isspace (*ptr)) + ++ptr; + + if (*ptr != '\0') { + + *ptr = '\0'; + ++ptr; + } +/* + * Skip white space between name and parms. + */ + while (*ptr && isspace (*ptr)) + ++ptr; + + ParseOption (option, *ptr ? ptr : NULL, 0); + } + + fclose (file); +} + +static void Usage () +{ + int i; + int max; + struct OptionInfo* info; + + fprintf (stderr, "Recognized options:\n\n"); + + max = sizeof (optionTable) / sizeof (struct OptionInfo); + for (i = 0, info = optionTable; i < max; i++, info++) { + + fprintf (stderr, "-%-20s %s\n", info->name, + info->parmDescription); + + if (info->shortName) + fprintf (stderr, "-%-20s %s\n", info->shortName, + info->parmDescription); + + fprintf (stderr, " %s\n\n", info->description); + } + + exit (1); +} + +void SetupPermanentLink (char* parms) +{ + char buf[128]; + char* ptr; + struct in_addr srcAddr; + struct in_addr dstAddr; + int srcPort; + int dstPort; + int aliasPort; + int proto; + char* protoName; + + strcpy (buf, parms); +/* + * Extract protocol. + */ + protoName = strtok (buf, " \t"); + if (!protoName) { + + fprintf (stderr, "permanent_link: missing protocol.\n"); + exit (1); + } + + proto = StrToProto (protoName); +/* + * Extract source address. + */ + ptr = strtok (NULL, " \t"); + if (!ptr) { + + fprintf (stderr, "permanent_link: missing src address.\n"); + exit (1); + } + + srcPort = StrToAddrAndPort (ptr, &srcAddr, protoName); +/* + * Extract destination address. + */ + ptr = strtok (NULL, " \t"); + if (!ptr) { + + fprintf (stderr, "permanent_link: missing dst address.\n"); + exit (1); + } + + dstPort = StrToAddrAndPort (ptr, &dstAddr, protoName); +/* + * Export alias port. + */ + ptr = strtok (NULL, " \t"); + if (!ptr) { + + fprintf (stderr, "permanent_link: missing alias port.\n"); + exit (1); + } + + aliasPort = StrToPort (ptr, protoName); + + PacketAliasPermanentLink (srcAddr, + srcPort, + dstAddr, + dstPort, + aliasPort, + proto); +} + +void SetupPortRedirect (char* parms) +{ + char buf[128]; + char* ptr; + struct in_addr localAddr; + struct in_addr publicAddr; + struct in_addr remoteAddr; + int localPort; + int publicPort; + int remotePort; + int proto; + char* protoName; + char* separator; + + strcpy (buf, parms); +/* + * Extract protocol. + */ + protoName = strtok (buf, " \t"); + if (!protoName) { + + fprintf (stderr, "redirect_port: missing protocol.\n"); + exit (1); + } + + proto = StrToProto (protoName); +/* + * Extract local address. + */ + ptr = strtok (NULL, " \t"); + if (!ptr) { + + fprintf (stderr, "redirect_port: missing local address.\n"); + exit (1); + } + + localPort = StrToAddrAndPort (ptr, &localAddr, protoName); +/* + * Extract public port and optinally address. + */ + ptr = strtok (NULL, " \t"); + if (!ptr) { + + fprintf (stderr, "redirect_port: missing public port.\n"); + exit (1); + } + + separator = strchr (ptr, ':'); + if (separator) + publicPort = StrToAddrAndPort (ptr, &publicAddr, protoName); + else { + + publicAddr.s_addr = INADDR_ANY; + publicPort = StrToPort (ptr, protoName); + } + +/* + * Extract remote address and optionally port. + */ + ptr = strtok (NULL, " \t"); + if (ptr) { + + + separator = strchr (ptr, ':'); + if (separator) + remotePort = StrToAddrAndPort (ptr, + &remoteAddr, + protoName); + else { + + remotePort = 0; + StrToAddr (ptr, &remoteAddr); + } + } + else { + + remotePort = 0; + remoteAddr.s_addr = INADDR_ANY; + } + + PacketAliasRedirectPort (localAddr, + localPort, + remoteAddr, + remotePort, + publicAddr, + publicPort, + proto); +} + +void SetupAddressRedirect (char* parms) +{ + char buf[128]; + char* ptr; + struct in_addr localAddr; + struct in_addr publicAddr; + + strcpy (buf, parms); +/* + * Extract local address. + */ + ptr = strtok (buf, " \t"); + if (!ptr) { + + fprintf (stderr, "redirect_address: missing local address.\n"); + exit (1); + } + + StrToAddr (ptr, &localAddr); +/* + * Extract public address. + */ + ptr = strtok (NULL, " \t"); + if (!ptr) { + + fprintf (stderr, "redirect_address: missing public address.\n"); + exit (1); + } + + StrToAddr (ptr, &publicAddr); + PacketAliasRedirectAddr (localAddr, publicAddr); +} + +void StrToAddr (char* str, struct in_addr* addr) +{ + struct hostent* hp; + + if (inet_aton (str, addr)) + return; + + hp = gethostbyname (str); + if (!hp) { + + fprintf (stderr, "Unknown host %s.\n", str); + exit (1); + } + + memcpy (addr, hp->h_addr, sizeof (struct in_addr)); +} + +int StrToPort (char* str, char* proto) +{ + int port; + struct servent* sp; + char* end; + + port = strtol (str, &end, 10); + if (end != str) + return htons (port); + + sp = getservbyname (str, proto); + if (!sp) { + + fprintf (stderr, "Unknown service %s/%s.\n", + str, proto); + exit (1); + } + + return sp->s_port; +} + +int StrToProto (char* str) +{ + if (!strcmp (str, "tcp")) + return IPPROTO_TCP; + + if (!strcmp (str, "udp")) + return IPPROTO_UDP; + + fprintf (stderr, "Unknown protocol %s. Expected tcp or udp.\n", str); + exit (1); +} + +int StrToAddrAndPort (char* str, struct in_addr* addr, char* proto) +{ + char* ptr; + + ptr = strchr (str, ':'); + if (!ptr) { + + fprintf (stderr, "%s is missing port number.\n", str); + exit (1); + } + + *ptr = '\0'; + ++ptr; + + StrToAddr (str, addr); + return StrToPort (ptr, proto); +} + |