summaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--sbin/natd/Makefile10
-rw-r--r--sbin/natd/icmp.c112
-rw-r--r--sbin/natd/natd.8380
-rw-r--r--sbin/natd/natd.c1396
-rw-r--r--sbin/natd/natd.h5
-rw-r--r--sbin/natd/samples/natd.cf.sample52
-rw-r--r--sbin/natd/samples/natd.test14
-rw-r--r--usr.sbin/natd/.depend30
-rw-r--r--usr.sbin/natd/Makefile10
-rw-r--r--usr.sbin/natd/icmp.c112
-rw-r--r--usr.sbin/natd/natd.8380
-rw-r--r--usr.sbin/natd/natd.c1396
-rw-r--r--usr.sbin/natd/natd.h5
-rw-r--r--usr.sbin/natd/samples/natd.cf.sample52
-rw-r--r--usr.sbin/natd/samples/natd.test14
15 files changed, 3968 insertions, 0 deletions
diff --git a/sbin/natd/Makefile b/sbin/natd/Makefile
new file mode 100644
index 0000000..015d645
--- /dev/null
+++ b/sbin/natd/Makefile
@@ -0,0 +1,10 @@
+# $Id:$
+
+PROG= natd
+SRCS= natd.c icmp.c
+CFLAGS+=-Wall
+LDADD= -lalias
+DPADD= ${LIBALIAS}
+MAN8= natd.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/natd/icmp.c b/sbin/natd/icmp.c
new file mode 100644
index 0000000..40464ff
--- /dev/null
+++ b/sbin/natd/icmp.c
@@ -0,0 +1,112 @@
+#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 <netinet/ip_icmp.h>
+#include <machine/in_cksum.h>
+
+#include <alias.h>
+
+#include "natd.h"
+
+int SendNeedFragIcmp (int sock, struct ip* failedDgram, int mtu)
+{
+ char icmpBuf[IP_MAXPACKET];
+ struct ip* ip;
+ struct icmp* icmp;
+ int icmpLen;
+ int failBytes;
+ int failHdrLen;
+ struct sockaddr_in addr;
+ int wrote;
+ struct in_addr swap;
+/*
+ * Don't send error if packet is
+ * not the first fragment.
+ */
+ if (ntohs (failedDgram->ip_off) & ~(IP_MF | IP_DF))
+ return 0;
+/*
+ * Dont respond if failed datagram is ICMP.
+ */
+ if (failedDgram->ip_p == IPPROTO_ICMP)
+ return 0;
+/*
+ * Start building the message.
+ */
+ ip = (struct ip*) icmpBuf;
+ icmp = (struct icmp*) (icmpBuf + sizeof (struct ip));
+/*
+ * Complete ICMP part.
+ */
+ icmp->icmp_type = ICMP_UNREACH;
+ icmp->icmp_code = ICMP_UNREACH_NEEDFRAG;
+ icmp->icmp_cksum = 0;
+ icmp->icmp_void = 0;
+ icmp->icmp_nextmtu = htons (mtu);
+/*
+ * Copy header + 64 bits of original datagram.
+ */
+ failHdrLen = (failedDgram->ip_hl << 2);
+ failBytes = failedDgram->ip_len - failHdrLen;
+ if (failBytes > 8)
+ failBytes = 8;
+
+ failBytes += failHdrLen;
+ icmpLen = ICMP_MINLEN + failBytes;
+
+ memcpy (&icmp->icmp_ip, failedDgram, failBytes);
+/*
+ * Calculate checksum.
+ */
+ icmp->icmp_cksum = InternetChecksum ((u_short*) icmp, icmpLen);
+/*
+ * Add IP header using old IP header as template.
+ */
+ memcpy (ip, failedDgram, sizeof (struct ip));
+
+ ip->ip_v = 4;
+ ip->ip_hl = 5;
+ ip->ip_len = htons (sizeof (struct ip) + icmpLen);
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_tos = 0;
+
+ swap = ip->ip_dst;
+ ip->ip_dst = ip->ip_src;
+ ip->ip_src = swap;
+
+ PacketAliasIn ((char*) ip, IP_MAXPACKET);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr = ip->ip_dst;
+ addr.sin_port = 0;
+/*
+ * Put packet into processing queue.
+ */
+ wrote = sendto (sock,
+ icmp,
+ icmpLen,
+ 0,
+ (struct sockaddr*) &addr,
+ sizeof addr);
+
+ if (wrote != icmpLen)
+ Warn ("Cannot send ICMP message.");
+
+ return 1;
+}
+
+
diff --git a/sbin/natd/natd.8 b/sbin/natd/natd.8
new file mode 100644
index 0000000..91d7b6d
--- /dev/null
+++ b/sbin/natd/natd.8
@@ -0,0 +1,380 @@
+.\" manual page [] for natd 1.4
+.Dd 15 April 1997
+.Os FreeBSD
+.Dt NATD 8
+.Sh NAME
+.Nm natd
+.Nd
+Network Address Translation Daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl ldsmvu
+.Op Fl permanent_link
+.Op Fl dynamic
+.Op Fl i Ar inport
+.Op Fl o Ar outport
+.Op Fl p Ar port
+.Op Fl a Ar address
+.Op Fl i Ar interface
+.Op Fl f Ar configfile
+
+.Nm
+.Op Fl log
+.Op Fl deny_incoming
+.Op Fl use_sockets
+.Op Fl same_ports
+.Op Fl verbose
+.Op Fl unregistered_only
+.Op Fl permanent_link
+.Op Fl dynamic
+.Op Fl inport Ar inport
+.Op Fl outport Ar outport
+.Op Fl port Ar port
+.Op Fl alias_address Ar address
+.Op Fl interface Ar interface
+.Op Fl config Ar configfile
+
+.Sh DESCRIPTION
+This program provides a Network Address Translation facility for use
+with
+.Xr divert 4
+sockets under FreeBSD. Most of the command line options are available
+in a single character short form or in a long form. Use of the long
+form is encouraged as it makes things clearer to the casual observer.
+
+.Pp
+.Nm Natd
+normally runs in the background as a daemon. It is passed raw IP packets
+as they travel into and out of the machine, and will possibly change these
+before re-injecting them back into the IP packet stream.
+
+.Pp
+.Nm Natd
+changes all packets destined for another host so that their source
+IP number is that of the current machine. For each packet changed
+in this manner, an internal table entry is created to record this
+fact. The source port number is also changed to indicate the
+table entry applying to the packet. Packets that are received with
+a target IP of the current host are checked against this internal
+table. If an entry is found, it is used to determine the correct
+target IP number and port to place in the packet.
+
+.Pp
+The following command line options are available.
+.Bl -tag -width Fl
+
+.It Fl log | l
+Log various aliasing statistics and information to the file
+.Pa /var/log/alias.log .
+This file is truncated each time natd is started.
+
+.It Fl deny_incoming | d
+Reject packets destined for the current IP number that have no entry
+in the internal translation table.
+
+.It Fl use_sockets | s
+Allocate a
+.Xr socket 2
+in order to establish an FTP data or IRC DCC send connection. This
+option uses more system resources, but guarantees successful connections
+when port numbers conflict.
+
+.It Fl same_ports | m
+Try to keep the same port number when altering outgoing packets.
+With this option, protocols such as RPC will have a better chance
+of working. If it is not possible to maintain the port number, it
+will be silently changed as per normal.
+
+.It Fl verbose | v
+Don't call
+.Xr fork 2
+or
+.Xr daemon 3
+on startup. Instead, stay attached to the controling terminal and
+display all packet alterations to the standard output. This option
+should only be used for debugging purposes.
+
+.It Fl unregistered_only | u
+Only alter outgoing packets with an unregistered source address.
+According to rfc 1918, unregistered source addresses are 10.0.0.0/8,
+172.16.0.0/12 and 192.168.0.0/16.
+
+.It Fl redirect_port Ar linkspec
+Redirect incoming connections arriving to given port to another host and port.
+Linkspec is of the form
+
+ proto targetIP:targetPORT [aliasIP:]aliasPORT [remoteIP[:remotePORT]]
+
+where proto is either tcp or udp, targetIP is the desired target IP
+number, targetPORT is the desired target PORT number, aliasPORT
+is the requested PORT number and aliasIP is the aliasing address.
+RemoteIP and remotePORT can be used to specify the connection
+more accurately if necessary.
+For example, the argument
+
+.Ar tcp inside1:telnet 6666
+
+means that tcp packets destined for port 6666 on this machine will
+be sent to the telnet port on the inside1 machine.
+
+.It Fl redirect_address Ar localIP publicIP
+Redirect traffic for public IP address to a machine on the local
+network. This function is known as "static NAT". Normally static NAT
+is useful if your ISP has allocated a small block of IP addresses to you,
+but it can even be used in the case of single address:
+
+ redirect_address 10.0.0.8 0.0.0.0
+
+The above command would redirect all incoming traffic
+to machine 10.0.0.8.
+
+If several address aliases specify the same public address
+as follows
+
+ redirect_address 192.168.0.2 public_addr
+ redirect_address 192.168.0.3 public_addr
+ redirect_address 192.168.0.4 public_addr
+
+the incoming traffic will be directed to the last
+translated local address (192.168.0.4), but outgoing
+traffice to the first two addresses will still be aliased
+to specified public address.
+
+.It Fl permanent_link Ar linkspec
+Create a permanent entry in the internal alias table. Linkspec is
+of the form
+
+ proto targetIP:targetPORT sourceIP:sourcePORT aliasPORT
+
+where proto is either tcp or udp, targetIP is the desired target IP
+number, targetPORT is the desired target PORT number, sourceIP and
+sourcePORT match the incoming packet, and aliasPORT is the requested
+PORT number. Values of zero are considered as wildcards. For example,
+the argument
+
+.Ar tcp inside1:telnet outside1:0 6666
+
+means that tcp packets destined for port 6666 on this machine from the
+outside1 machine (any port) will be sent to the telnet port on the
+inside1 machine.
+
+New installations are encouraged to use redirect_port instead.
+
+.It Fl dynamic
+If the
+.Fl n
+or
+.Fl interface
+option is used,
+.Nm natd
+will monitor the routing socket for alterations to the
+.Ar interface
+passed. If the interfaces IP number is changed,
+.Nm natd
+will dynamically alter its concept of the alias address.
+
+.It Fl i | inport Ar inport
+Read from and write to
+.Ar inport ,
+treating all packets as packets coming into the machine.
+
+.It Fl o | outport Ar outport
+Read from and write to
+.Ar outport ,
+treating all packets as packets going out of the machine.
+
+.It Fl p | port Ar port
+Read from and write to
+.Ar port ,
+distinguishing packets as incoming our outgoing using the rules specified in
+.Xr divert 4 .
+If
+.Ar port
+is not numeric, it is searched for in the
+.Pa /etc/services
+database using the
+.Xr getservbyname 3
+function. If this flag is not specified, the divert port named natd will
+be used as a default. An example entry in the
+.Pa /etc/services
+database would be:
+
+ natd 6668/divert # Network Address Translation socket
+
+Refer to
+.Xr services 5
+for further details.
+
+.It Fl a | alias_address Ar address
+Use
+.Ar address
+as the alias address. If this option is not specified, the
+.Fl n
+or
+.Fl interface
+option must be used.
+
+.It Fl n | interface Ar interface
+Use
+.Ar interface
+to determine the alias address. If there is a possibility that the
+IP number associated with
+.Ar interface
+may change, the
+.Fl dynamic
+flag should also be used. If this option is not specified, the
+.Fl a
+or
+.Fl alias_address
+flag must be used.
+
+.It Fl f | config Ar configfile
+Read configuration from
+.Ar configfile .
+.Ar Configfile
+contains a list of options, one per line in the same form as the
+long form of the above command line flags. For example, the line
+
+ alias_address 158.152.17.1
+
+would specify an alias address of 158.152.17.1. Options that don't
+take an argument are specified with an option of
+.Ar yes
+or
+.Ar no
+in the configuration file. For example, the line
+
+ log yes
+
+is synonomous with
+.Fl log .
+Empty lines and lines beginning with '#' are ignored.
+
+.El
+
+.Sh RUNNING NATD
+The following steps are necessary before attempting to run
+.Nm natd :
+
+.Bl -enum
+.It
+Get FreeBSD version 2.2 or higher. Versions before this do not support
+.Xr divert 4
+sockets.
+
+.It
+Build a custom kernel with the following options:
+
+ options IPFIREWALL
+ options IPDIVERT
+
+Refer to the handbook for detailed instructions on building a custom
+kernel.
+
+.It
+Ensure that your machine is acting as a gateway. This can be done by
+specifying the line
+
+ gateway=YES
+
+in
+.Pa /etc/sysconfig ,
+or using the command
+
+ sysctl -w net.inet.ip.forwarding=1
+
+.It
+If you wish to use the
+.Fl n
+or
+.Fl interface
+flags, make sure that your interface is already configured. If, for
+example, you wish to specify tun0 as your
+.Ar interface ,
+and you're using
+.Xr ppp 8
+on that interface, you must make sure that you start
+.Nm ppp
+prior to starting
+.Nm natd .
+
+.It
+Create an entry in
+.Pa /etc/services :
+
+ natd 6668/divert # Network Address Translation socket
+
+This gives a default for the
+.Fl p
+or
+.Fl port
+flag.
+
+.El
+.Pp
+Running
+.Nm natd
+is fairly straight forward. The line
+
+ natd -interface ed0
+
+should suffice in most cases (substituting the correct interface name). Once
+.Nm natd
+is running, you must ensure that traffic is diverted to natd:
+
+.Bl -enum
+.It
+You will need to adjust the
+.Pa /etc/rc.firewall
+script to taste. If you're not interested in having a firewall, the
+following lines will do:
+
+ /sbin/ipfw -f flush
+ /sbin/ipfw add divert 6668 all from any to any via ed0
+ /sbin/ipfw add pass all from any to any
+
+The second line depends on your interface and assumes that you've updated
+.Pa /etc/services
+as above. If you specify real firewall rules, it's best to specify
+line 2 at the start of the script so that
+.Nm natd
+sees all packets before they are dropped by the firewall. The firewall
+rules will be run again on each packet after translation by
+.Nm natd ,
+minus any divert rules.
+
+.It
+Enable your firewall by setting
+
+ firewall=YES
+
+in
+.Pa /etc/sysconfig .
+This tells the system startup scripts to run the
+.Pa /etc/rc.firewall
+script. If you don't wish to reboot now, just run this by hand from the
+console. NEVER run this from a virtual session unless you put it into
+the background. If you do, you'll lock yourself out after the flush
+takes place, and execution of
+.Pa /etc/rc.firewall
+will stop at this point - blocking all accesses permanently. Running
+the script in the background should be enough to prevent this disaster.
+
+.El
+
+.Sh SEE ALSO
+.Xr socket 2 ,
+.Xr getservbyname 2 ,
+.Xr divert 4 ,
+.Xr services 5 ,
+.Xr ipfw 8
+
+.Sh AUTHORS
+This program is the result of the efforts of many people at different
+times:
+
+ Divert sockets: Archie Cobbs <archie@whistle.com>
+ Packet aliasing: Charles Mott <cmott@srv.net>
+ IRC support & misc additions: Eivind Eklund <eivind@dimaga.com>
+ Natd: Ari Suutari <ari.suutari@ps.carel.fi>
+ Glue: Brian Somers <brian@awfulhak.org>
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);
+}
+
diff --git a/sbin/natd/natd.h b/sbin/natd/natd.h
new file mode 100644
index 0000000..d398241
--- /dev/null
+++ b/sbin/natd/natd.h
@@ -0,0 +1,5 @@
+#define PIDFILE "/var/run/natd.pid"
+
+extern void Quit (char* msg);
+extern void Warn (char* msg);
+extern int SendNeedFragIcmp (int sock, struct ip* failedDgram, int mtu);
diff --git a/sbin/natd/samples/natd.cf.sample b/sbin/natd/samples/natd.cf.sample
new file mode 100644
index 0000000..46eee91
--- /dev/null
+++ b/sbin/natd/samples/natd.cf.sample
@@ -0,0 +1,52 @@
+#
+# Configuration file for natd.
+#
+#
+# Logging to /var/log
+#
+log no
+#
+# Incoming connections.
+#
+deny_incoming no
+#
+# Use sockets to avoid port clashes.
+#
+use_sockets no
+#
+# Avoid port changes if possible. Makes rlogin work
+# in most cases.
+#
+same_port yes
+#
+# Verbose mode. Enables dumping of packets and disables
+# forking to background.
+#
+verbose no
+#
+# Divert port. Can be a name in /etc/services or numeric value.
+#
+port 32000
+#
+# Interface name or address being aliased. Either one,
+# not both is required.
+#
+# alias_address 192.168.0.1
+interface ep0
+#
+# Alias unregistered addresses or all addresses.
+#
+unregistered_only no
+#
+# Configure permanent links. If you use host names instead
+# of addresses here, be sure that name server works BEFORE
+# natd is up - this is usually not the case. So either use
+# numeric addresses or hosts that are in /etc/hosts.
+#
+# Map connections coming to port 30000 to telnet in my_private_host.
+# Remember to allow the connection /etc/rc.firewall also.
+#permanent_link tcp my_private_host:telnet 0.0.0.0:0 30000
+#
+# Map connections coming from host.xyz.com to port 30001 to
+# telnet in another_host.
+#permanent_link tcp another_host:telnet host.xyz.com:0 30001
diff --git a/sbin/natd/samples/natd.test b/sbin/natd/samples/natd.test
new file mode 100644
index 0000000..cfdbd15
--- /dev/null
+++ b/sbin/natd/samples/natd.test
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+ if [ $# != 1 ]
+ then
+ echo "usage: natd.test ifname"
+ exit 1
+ fi
+
+ ipfw flush
+ ipfw add divert 32000 ip from any to any via $1
+ ipfw add pass ip from any to any
+
+ ./natd -port 32000 -interface $1 -verbose
+
diff --git a/usr.sbin/natd/.depend b/usr.sbin/natd/.depend
new file mode 100644
index 0000000..86e9112
--- /dev/null
+++ b/usr.sbin/natd/.depend
@@ -0,0 +1,30 @@
+# natd.c icmp.c
+natd.o: natd.c /usr/include/stdlib.h /usr/include/machine/ansi.h \
+ /usr/include/machine/types.h /usr/include/sys/cdefs.h \
+ /usr/include/stdio.h /usr/include/unistd.h /usr/include/sys/types.h \
+ /usr/include/machine/endian.h /usr/include/sys/unistd.h \
+ /usr/include/string.h /usr/include/ctype.h /usr/include/runetype.h \
+ /usr/include/sys/socket.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/errno.h /usr/include/signal.h /usr/include/sys/signal.h \
+ /usr/include/machine/signal.h /usr/include/machine/trap.h \
+ /usr/include/netdb.h /usr/include/netinet/in.h \
+ /usr/include/netinet/in_systm.h /usr/include/netinet/ip.h \
+ /usr/include/machine/in_cksum.h /usr/include/netinet/tcp.h \
+ /usr/include/sys/ioctl.h /usr/include/sys/ttycom.h \
+ /usr/include/sys/ioccom.h /usr/include/sys/filio.h \
+ /usr/include/sys/sockio.h /usr/include/net/if.h \
+ /usr/include/net/route.h /usr/include/net/radix.h \
+ /usr/include/arpa/inet.h /usr/include/syslog.h /usr/include/alias.h \
+ natd.h
+icmp.o: icmp.c /usr/include/stdlib.h /usr/include/machine/ansi.h \
+ /usr/include/machine/types.h /usr/include/sys/cdefs.h \
+ /usr/include/stdio.h /usr/include/unistd.h /usr/include/sys/types.h \
+ /usr/include/machine/endian.h /usr/include/sys/unistd.h \
+ /usr/include/string.h /usr/include/ctype.h /usr/include/runetype.h \
+ /usr/include/sys/socket.h /usr/include/sys/time.h /usr/include/time.h \
+ /usr/include/errno.h /usr/include/signal.h /usr/include/sys/signal.h \
+ /usr/include/machine/signal.h /usr/include/machine/trap.h \
+ /usr/include/netdb.h /usr/include/netinet/in.h \
+ /usr/include/netinet/in_systm.h /usr/include/netinet/ip.h \
+ /usr/include/netinet/ip_icmp.h /usr/include/machine/in_cksum.h \
+ /usr/include/alias.h natd.h
diff --git a/usr.sbin/natd/Makefile b/usr.sbin/natd/Makefile
new file mode 100644
index 0000000..015d645
--- /dev/null
+++ b/usr.sbin/natd/Makefile
@@ -0,0 +1,10 @@
+# $Id:$
+
+PROG= natd
+SRCS= natd.c icmp.c
+CFLAGS+=-Wall
+LDADD= -lalias
+DPADD= ${LIBALIAS}
+MAN8= natd.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/natd/icmp.c b/usr.sbin/natd/icmp.c
new file mode 100644
index 0000000..40464ff
--- /dev/null
+++ b/usr.sbin/natd/icmp.c
@@ -0,0 +1,112 @@
+#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 <netinet/ip_icmp.h>
+#include <machine/in_cksum.h>
+
+#include <alias.h>
+
+#include "natd.h"
+
+int SendNeedFragIcmp (int sock, struct ip* failedDgram, int mtu)
+{
+ char icmpBuf[IP_MAXPACKET];
+ struct ip* ip;
+ struct icmp* icmp;
+ int icmpLen;
+ int failBytes;
+ int failHdrLen;
+ struct sockaddr_in addr;
+ int wrote;
+ struct in_addr swap;
+/*
+ * Don't send error if packet is
+ * not the first fragment.
+ */
+ if (ntohs (failedDgram->ip_off) & ~(IP_MF | IP_DF))
+ return 0;
+/*
+ * Dont respond if failed datagram is ICMP.
+ */
+ if (failedDgram->ip_p == IPPROTO_ICMP)
+ return 0;
+/*
+ * Start building the message.
+ */
+ ip = (struct ip*) icmpBuf;
+ icmp = (struct icmp*) (icmpBuf + sizeof (struct ip));
+/*
+ * Complete ICMP part.
+ */
+ icmp->icmp_type = ICMP_UNREACH;
+ icmp->icmp_code = ICMP_UNREACH_NEEDFRAG;
+ icmp->icmp_cksum = 0;
+ icmp->icmp_void = 0;
+ icmp->icmp_nextmtu = htons (mtu);
+/*
+ * Copy header + 64 bits of original datagram.
+ */
+ failHdrLen = (failedDgram->ip_hl << 2);
+ failBytes = failedDgram->ip_len - failHdrLen;
+ if (failBytes > 8)
+ failBytes = 8;
+
+ failBytes += failHdrLen;
+ icmpLen = ICMP_MINLEN + failBytes;
+
+ memcpy (&icmp->icmp_ip, failedDgram, failBytes);
+/*
+ * Calculate checksum.
+ */
+ icmp->icmp_cksum = InternetChecksum ((u_short*) icmp, icmpLen);
+/*
+ * Add IP header using old IP header as template.
+ */
+ memcpy (ip, failedDgram, sizeof (struct ip));
+
+ ip->ip_v = 4;
+ ip->ip_hl = 5;
+ ip->ip_len = htons (sizeof (struct ip) + icmpLen);
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_tos = 0;
+
+ swap = ip->ip_dst;
+ ip->ip_dst = ip->ip_src;
+ ip->ip_src = swap;
+
+ PacketAliasIn ((char*) ip, IP_MAXPACKET);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr = ip->ip_dst;
+ addr.sin_port = 0;
+/*
+ * Put packet into processing queue.
+ */
+ wrote = sendto (sock,
+ icmp,
+ icmpLen,
+ 0,
+ (struct sockaddr*) &addr,
+ sizeof addr);
+
+ if (wrote != icmpLen)
+ Warn ("Cannot send ICMP message.");
+
+ return 1;
+}
+
+
diff --git a/usr.sbin/natd/natd.8 b/usr.sbin/natd/natd.8
new file mode 100644
index 0000000..91d7b6d
--- /dev/null
+++ b/usr.sbin/natd/natd.8
@@ -0,0 +1,380 @@
+.\" manual page [] for natd 1.4
+.Dd 15 April 1997
+.Os FreeBSD
+.Dt NATD 8
+.Sh NAME
+.Nm natd
+.Nd
+Network Address Translation Daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl ldsmvu
+.Op Fl permanent_link
+.Op Fl dynamic
+.Op Fl i Ar inport
+.Op Fl o Ar outport
+.Op Fl p Ar port
+.Op Fl a Ar address
+.Op Fl i Ar interface
+.Op Fl f Ar configfile
+
+.Nm
+.Op Fl log
+.Op Fl deny_incoming
+.Op Fl use_sockets
+.Op Fl same_ports
+.Op Fl verbose
+.Op Fl unregistered_only
+.Op Fl permanent_link
+.Op Fl dynamic
+.Op Fl inport Ar inport
+.Op Fl outport Ar outport
+.Op Fl port Ar port
+.Op Fl alias_address Ar address
+.Op Fl interface Ar interface
+.Op Fl config Ar configfile
+
+.Sh DESCRIPTION
+This program provides a Network Address Translation facility for use
+with
+.Xr divert 4
+sockets under FreeBSD. Most of the command line options are available
+in a single character short form or in a long form. Use of the long
+form is encouraged as it makes things clearer to the casual observer.
+
+.Pp
+.Nm Natd
+normally runs in the background as a daemon. It is passed raw IP packets
+as they travel into and out of the machine, and will possibly change these
+before re-injecting them back into the IP packet stream.
+
+.Pp
+.Nm Natd
+changes all packets destined for another host so that their source
+IP number is that of the current machine. For each packet changed
+in this manner, an internal table entry is created to record this
+fact. The source port number is also changed to indicate the
+table entry applying to the packet. Packets that are received with
+a target IP of the current host are checked against this internal
+table. If an entry is found, it is used to determine the correct
+target IP number and port to place in the packet.
+
+.Pp
+The following command line options are available.
+.Bl -tag -width Fl
+
+.It Fl log | l
+Log various aliasing statistics and information to the file
+.Pa /var/log/alias.log .
+This file is truncated each time natd is started.
+
+.It Fl deny_incoming | d
+Reject packets destined for the current IP number that have no entry
+in the internal translation table.
+
+.It Fl use_sockets | s
+Allocate a
+.Xr socket 2
+in order to establish an FTP data or IRC DCC send connection. This
+option uses more system resources, but guarantees successful connections
+when port numbers conflict.
+
+.It Fl same_ports | m
+Try to keep the same port number when altering outgoing packets.
+With this option, protocols such as RPC will have a better chance
+of working. If it is not possible to maintain the port number, it
+will be silently changed as per normal.
+
+.It Fl verbose | v
+Don't call
+.Xr fork 2
+or
+.Xr daemon 3
+on startup. Instead, stay attached to the controling terminal and
+display all packet alterations to the standard output. This option
+should only be used for debugging purposes.
+
+.It Fl unregistered_only | u
+Only alter outgoing packets with an unregistered source address.
+According to rfc 1918, unregistered source addresses are 10.0.0.0/8,
+172.16.0.0/12 and 192.168.0.0/16.
+
+.It Fl redirect_port Ar linkspec
+Redirect incoming connections arriving to given port to another host and port.
+Linkspec is of the form
+
+ proto targetIP:targetPORT [aliasIP:]aliasPORT [remoteIP[:remotePORT]]
+
+where proto is either tcp or udp, targetIP is the desired target IP
+number, targetPORT is the desired target PORT number, aliasPORT
+is the requested PORT number and aliasIP is the aliasing address.
+RemoteIP and remotePORT can be used to specify the connection
+more accurately if necessary.
+For example, the argument
+
+.Ar tcp inside1:telnet 6666
+
+means that tcp packets destined for port 6666 on this machine will
+be sent to the telnet port on the inside1 machine.
+
+.It Fl redirect_address Ar localIP publicIP
+Redirect traffic for public IP address to a machine on the local
+network. This function is known as "static NAT". Normally static NAT
+is useful if your ISP has allocated a small block of IP addresses to you,
+but it can even be used in the case of single address:
+
+ redirect_address 10.0.0.8 0.0.0.0
+
+The above command would redirect all incoming traffic
+to machine 10.0.0.8.
+
+If several address aliases specify the same public address
+as follows
+
+ redirect_address 192.168.0.2 public_addr
+ redirect_address 192.168.0.3 public_addr
+ redirect_address 192.168.0.4 public_addr
+
+the incoming traffic will be directed to the last
+translated local address (192.168.0.4), but outgoing
+traffice to the first two addresses will still be aliased
+to specified public address.
+
+.It Fl permanent_link Ar linkspec
+Create a permanent entry in the internal alias table. Linkspec is
+of the form
+
+ proto targetIP:targetPORT sourceIP:sourcePORT aliasPORT
+
+where proto is either tcp or udp, targetIP is the desired target IP
+number, targetPORT is the desired target PORT number, sourceIP and
+sourcePORT match the incoming packet, and aliasPORT is the requested
+PORT number. Values of zero are considered as wildcards. For example,
+the argument
+
+.Ar tcp inside1:telnet outside1:0 6666
+
+means that tcp packets destined for port 6666 on this machine from the
+outside1 machine (any port) will be sent to the telnet port on the
+inside1 machine.
+
+New installations are encouraged to use redirect_port instead.
+
+.It Fl dynamic
+If the
+.Fl n
+or
+.Fl interface
+option is used,
+.Nm natd
+will monitor the routing socket for alterations to the
+.Ar interface
+passed. If the interfaces IP number is changed,
+.Nm natd
+will dynamically alter its concept of the alias address.
+
+.It Fl i | inport Ar inport
+Read from and write to
+.Ar inport ,
+treating all packets as packets coming into the machine.
+
+.It Fl o | outport Ar outport
+Read from and write to
+.Ar outport ,
+treating all packets as packets going out of the machine.
+
+.It Fl p | port Ar port
+Read from and write to
+.Ar port ,
+distinguishing packets as incoming our outgoing using the rules specified in
+.Xr divert 4 .
+If
+.Ar port
+is not numeric, it is searched for in the
+.Pa /etc/services
+database using the
+.Xr getservbyname 3
+function. If this flag is not specified, the divert port named natd will
+be used as a default. An example entry in the
+.Pa /etc/services
+database would be:
+
+ natd 6668/divert # Network Address Translation socket
+
+Refer to
+.Xr services 5
+for further details.
+
+.It Fl a | alias_address Ar address
+Use
+.Ar address
+as the alias address. If this option is not specified, the
+.Fl n
+or
+.Fl interface
+option must be used.
+
+.It Fl n | interface Ar interface
+Use
+.Ar interface
+to determine the alias address. If there is a possibility that the
+IP number associated with
+.Ar interface
+may change, the
+.Fl dynamic
+flag should also be used. If this option is not specified, the
+.Fl a
+or
+.Fl alias_address
+flag must be used.
+
+.It Fl f | config Ar configfile
+Read configuration from
+.Ar configfile .
+.Ar Configfile
+contains a list of options, one per line in the same form as the
+long form of the above command line flags. For example, the line
+
+ alias_address 158.152.17.1
+
+would specify an alias address of 158.152.17.1. Options that don't
+take an argument are specified with an option of
+.Ar yes
+or
+.Ar no
+in the configuration file. For example, the line
+
+ log yes
+
+is synonomous with
+.Fl log .
+Empty lines and lines beginning with '#' are ignored.
+
+.El
+
+.Sh RUNNING NATD
+The following steps are necessary before attempting to run
+.Nm natd :
+
+.Bl -enum
+.It
+Get FreeBSD version 2.2 or higher. Versions before this do not support
+.Xr divert 4
+sockets.
+
+.It
+Build a custom kernel with the following options:
+
+ options IPFIREWALL
+ options IPDIVERT
+
+Refer to the handbook for detailed instructions on building a custom
+kernel.
+
+.It
+Ensure that your machine is acting as a gateway. This can be done by
+specifying the line
+
+ gateway=YES
+
+in
+.Pa /etc/sysconfig ,
+or using the command
+
+ sysctl -w net.inet.ip.forwarding=1
+
+.It
+If you wish to use the
+.Fl n
+or
+.Fl interface
+flags, make sure that your interface is already configured. If, for
+example, you wish to specify tun0 as your
+.Ar interface ,
+and you're using
+.Xr ppp 8
+on that interface, you must make sure that you start
+.Nm ppp
+prior to starting
+.Nm natd .
+
+.It
+Create an entry in
+.Pa /etc/services :
+
+ natd 6668/divert # Network Address Translation socket
+
+This gives a default for the
+.Fl p
+or
+.Fl port
+flag.
+
+.El
+.Pp
+Running
+.Nm natd
+is fairly straight forward. The line
+
+ natd -interface ed0
+
+should suffice in most cases (substituting the correct interface name). Once
+.Nm natd
+is running, you must ensure that traffic is diverted to natd:
+
+.Bl -enum
+.It
+You will need to adjust the
+.Pa /etc/rc.firewall
+script to taste. If you're not interested in having a firewall, the
+following lines will do:
+
+ /sbin/ipfw -f flush
+ /sbin/ipfw add divert 6668 all from any to any via ed0
+ /sbin/ipfw add pass all from any to any
+
+The second line depends on your interface and assumes that you've updated
+.Pa /etc/services
+as above. If you specify real firewall rules, it's best to specify
+line 2 at the start of the script so that
+.Nm natd
+sees all packets before they are dropped by the firewall. The firewall
+rules will be run again on each packet after translation by
+.Nm natd ,
+minus any divert rules.
+
+.It
+Enable your firewall by setting
+
+ firewall=YES
+
+in
+.Pa /etc/sysconfig .
+This tells the system startup scripts to run the
+.Pa /etc/rc.firewall
+script. If you don't wish to reboot now, just run this by hand from the
+console. NEVER run this from a virtual session unless you put it into
+the background. If you do, you'll lock yourself out after the flush
+takes place, and execution of
+.Pa /etc/rc.firewall
+will stop at this point - blocking all accesses permanently. Running
+the script in the background should be enough to prevent this disaster.
+
+.El
+
+.Sh SEE ALSO
+.Xr socket 2 ,
+.Xr getservbyname 2 ,
+.Xr divert 4 ,
+.Xr services 5 ,
+.Xr ipfw 8
+
+.Sh AUTHORS
+This program is the result of the efforts of many people at different
+times:
+
+ Divert sockets: Archie Cobbs <archie@whistle.com>
+ Packet aliasing: Charles Mott <cmott@srv.net>
+ IRC support & misc additions: Eivind Eklund <eivind@dimaga.com>
+ Natd: Ari Suutari <ari.suutari@ps.carel.fi>
+ Glue: Brian Somers <brian@awfulhak.org>
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);
+}
+
diff --git a/usr.sbin/natd/natd.h b/usr.sbin/natd/natd.h
new file mode 100644
index 0000000..d398241
--- /dev/null
+++ b/usr.sbin/natd/natd.h
@@ -0,0 +1,5 @@
+#define PIDFILE "/var/run/natd.pid"
+
+extern void Quit (char* msg);
+extern void Warn (char* msg);
+extern int SendNeedFragIcmp (int sock, struct ip* failedDgram, int mtu);
diff --git a/usr.sbin/natd/samples/natd.cf.sample b/usr.sbin/natd/samples/natd.cf.sample
new file mode 100644
index 0000000..46eee91
--- /dev/null
+++ b/usr.sbin/natd/samples/natd.cf.sample
@@ -0,0 +1,52 @@
+#
+# Configuration file for natd.
+#
+#
+# Logging to /var/log
+#
+log no
+#
+# Incoming connections.
+#
+deny_incoming no
+#
+# Use sockets to avoid port clashes.
+#
+use_sockets no
+#
+# Avoid port changes if possible. Makes rlogin work
+# in most cases.
+#
+same_port yes
+#
+# Verbose mode. Enables dumping of packets and disables
+# forking to background.
+#
+verbose no
+#
+# Divert port. Can be a name in /etc/services or numeric value.
+#
+port 32000
+#
+# Interface name or address being aliased. Either one,
+# not both is required.
+#
+# alias_address 192.168.0.1
+interface ep0
+#
+# Alias unregistered addresses or all addresses.
+#
+unregistered_only no
+#
+# Configure permanent links. If you use host names instead
+# of addresses here, be sure that name server works BEFORE
+# natd is up - this is usually not the case. So either use
+# numeric addresses or hosts that are in /etc/hosts.
+#
+# Map connections coming to port 30000 to telnet in my_private_host.
+# Remember to allow the connection /etc/rc.firewall also.
+#permanent_link tcp my_private_host:telnet 0.0.0.0:0 30000
+#
+# Map connections coming from host.xyz.com to port 30001 to
+# telnet in another_host.
+#permanent_link tcp another_host:telnet host.xyz.com:0 30001
diff --git a/usr.sbin/natd/samples/natd.test b/usr.sbin/natd/samples/natd.test
new file mode 100644
index 0000000..cfdbd15
--- /dev/null
+++ b/usr.sbin/natd/samples/natd.test
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+ if [ $# != 1 ]
+ then
+ echo "usage: natd.test ifname"
+ exit 1
+ fi
+
+ ipfw flush
+ ipfw add divert 32000 ip from any to any via $1
+ ipfw add pass ip from any to any
+
+ ./natd -port 32000 -interface $1 -verbose
+
OpenPOWER on IntegriCloud