summaryrefslogtreecommitdiffstats
path: root/sbin/natd/natd.c
diff options
context:
space:
mode:
authorbrian <brian@FreeBSD.org>1997-06-22 04:19:08 +0000
committerbrian <brian@FreeBSD.org>1997-06-22 04:19:08 +0000
commit59a701d2eb6099d33d593a0f994d15fe5a605440 (patch)
treef925a9dd22becc0f3a478fc6b70e4f2a7475099f /sbin/natd/natd.c
downloadFreeBSD-src-59a701d2eb6099d33d593a0f994d15fe5a605440.zip
FreeBSD-src-59a701d2eb6099d33d593a0f994d15fe5a605440.tar.gz
Bring natd into main source tree now that the
pppd/natd combination works ok. Submitted by: Ari Suutari <ari.suutari@ps.carel.fi>
Diffstat (limited to 'sbin/natd/natd.c')
-rw-r--r--sbin/natd/natd.c1396
1 files changed, 1396 insertions, 0 deletions
diff --git a/sbin/natd/natd.c b/sbin/natd/natd.c
new file mode 100644
index 0000000..f166765
--- /dev/null
+++ b/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);
+}
+
OpenPOWER on IntegriCloud