]> git.saurik.com Git - apple/network_cmds.git/blobdiff - natd.tproj/natd.c
network_cmds-329.2.2.tar.gz
[apple/network_cmds.git] / natd.tproj / natd.c
index bbb86786a92922e8381cd6c5a611238b93439d19..2ec16213d2bddb5f803000d684eb67a2722e7b55 100644 (file)
@@ -1,3 +1,24 @@
+/*
+ * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * The contents of this file constitute Original Code as defined in and
+ * are subject to the Apple Public Source License Version 1.1 (the
+ * "License").  You may not use this file except in compliance with the
+ * License.  Please obtain a copy of the License at
+ * http://www.apple.com/publicsource and read it before using this file.
+ * 
+ * This Original Code and all software distributed under the License are
+ * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
 /*
  * natd - Network Address Translation Daemon for FreeBSD.
  *
  *
  * Ari Suutari <suutari@iki.fi>
  *
- *     $Id: natd.c,v 1.1.1.1 2000/01/11 01:48:51 wsanchez Exp $
+ * Based upon:
+ * $FreeBSD: src/sbin/natd/natd.c,v 1.25.2.3 2000/07/11 20:00:57 ru Exp $
  */
 
 #define SYSLOG_NAMES
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+#include <sys/sysctl.h>
 #include <sys/time.h>
 
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
 #include <netinet/ip_icmp.h>
-#include <sys/ioctl.h>
 #include <net/if.h>
+#include <net/if_dl.h>
 #include <net/route.h>
 #include <arpa/inet.h>
 
 #include <alias.h>
+void DumpInfo(void);
+
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
@@ -40,6 +67,9 @@
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
+#include <fcntl.h>
+#include <mach/mach_time.h>
+#include <mach/clock_types.h>
 
 #include "natd.h"
 
@@ -57,7 +87,7 @@
  * NOTES:   - Port values are not stored in network byte order.
  */
 
-typedef u_long port_range;
+typedef uint32_t port_range;
 
 #define GETLOPORT(x)     ((x) >> 0x10)
 #define GETNUMPORTS(x)   ((x) & 0x0000ffff)
@@ -80,15 +110,16 @@ static void        Usage (void);
 static char*   FormatPacket (struct ip*);
 static void    PrintPacket (struct ip*);
 static void    SyslogPacket (struct ip*, int priority, const char *label);
-static void    SetAliasAddressFromIfName (char* ifName);
+static void    SetAliasAddressFromIfName (const char *ifName);
 static void    InitiateShutdown (int);
 static void    Shutdown (int);
 static void    RefreshAddr (int);
-static void    ParseOption (const char* option, const char* parms, int cmdLine);
+static void    HandleInfo (int);
+static void    ParseOption (const char* option, const char* parms);
 static void    ReadConfigFile (const char* fileName);
 static void    SetupPortRedirect (const char* parms);
+static void    SetupProtoRedirect(const char* parms);
 static void    SetupAddressRedirect (const char* parms);
-static void    SetupPptpAlias (const char* parms);
 static void    StrToAddr (const char* str, struct in_addr* addr);
 static u_short  StrToPort (const char* str, const char* proto);
 static int      StrToPortRange (const char* str, const char* proto, port_range *portRange);
@@ -96,6 +127,8 @@ static int   StrToProto (const char* str);
 static int      StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange);
 static void    ParseArgs (int argc, char** argv);
 static void    FlushPacketBuffer (int fd);
+static void    DiscardIncomingPackets (int fd);
+static void    SetupPunchFW(const char *strValue);
 
 /*
  * Globals.
@@ -112,6 +145,7 @@ static      u_short                 outPort;
 static u_short                 inOutPort;
 static struct in_addr          aliasAddr;
 static         int                     dynamicMode;
+static  int                    clampMSS;
 static  int                    ifMTU;
 static int                     aliasOverhead;
 static         int                     icmpSock;
@@ -123,6 +157,106 @@ static    int                     packetDirection;
 static  int                    dropIgnoredIncoming;
 static  int                    logDropped;
 static int                     logFacility;
+static int                     dumpinfo;
+
+#define        NATPORTMAP              1
+
+#ifdef NATPORTMAP
+
+/*
+ * Note: more details on NAT-PMP can be found at :
+ *         <http://files.dns-sd.org/draft-cheshire-nat-pmp.txt>
+ */
+
+#define NATPMP_ANNOUNCEMENT_PORT       5350
+#define NATPMP_PORT                                    5351
+
+#define NATPMVERSION   0
+#define PUBLICADDRREQ  0
+#define MAPUDPREQ              1
+#define MAPTCPREQ              2
+#define MAPUDPTCPREQ   3
+#define SERVERREPLYOP   128
+
+#define SUCCESS                                        0
+#define NOTSUPPORTEDVERSION            1
+#define NOTAUTHORIZED                  2
+#define NETWORKFAILURE                 3
+#define OUTOFRESOURCES                 4
+#define UNSUPPORTEDOPCODE              5
+
+#define MAXRETRY        10
+#define TIMER_RATE      250000
+
+#define FAILED                                 -1
+
+typedef struct  stdportmaprequest {
+       uint8_t                 version;
+       uint8_t                 opcode;
+} stdportmaprequest;
+
+typedef struct  publicportreq {
+       uint8_t                 version;
+       uint8_t                 opcode;
+       uint16_t                reserved;
+       uint16_t                privateport;
+       uint16_t                publicport;
+       uint32_t                lifetime;               /* in seconds */
+} publicportreq;
+
+typedef struct  publicaddrreply {
+       uint8_t                 version;
+       uint8_t                 opcode;
+       uint16_t                result;
+       uint32_t                epoch;
+       struct in_addr  addr;
+} publicaddrreply;
+
+typedef struct  publicportreply {
+       uint8_t                 version;
+       uint8_t                 opcode;
+       uint16_t                result;
+       uint32_t                epoch;
+       uint16_t                privateport;
+       uint16_t                publicport;
+       uint32_t                lifetime;               /* in seconds */
+} publicportreply;
+
+typedef struct  stderrreply {
+       uint8_t                 version;
+       uint8_t                 opcode;
+       uint16_t                result;
+       uint32_t                epoch;
+} stderrreply;
+
+
+static int             enable_natportmap = 0; 
+static struct in_addr          lastassignaliasAddr;
+static int             portmapSock = -1;
+static struct  in_addr *forwardedinterfaceaddr;
+static char    **forwardedinterfacename;
+static int             numofinterfaces = 0;                    /* has to be at least one */
+static int      numoftries=MAXRETRY;
+static struct itimerval itval;
+static int             Natdtimerset = 0;
+static  double         secdivisor;
+
+
+static void            HandlePortMap( int fd );
+static void            SendPortMapResponse( int fd, struct sockaddr_in *clientaddr, int clientaddrlen, unsigned char origopcode, unsigned short result);
+static void            SendPublicAddress( int fd, struct sockaddr_in *clientaddr, int clientaddrlen );
+static void            SendPublicPortResponse( int fd, struct sockaddr_in *clientaddr, int clientaddrlen, publicportreq *reply, u_short publicport, int result);
+static void            Doubletime( struct timeval *tvp);
+static void            Stoptimer();
+static void            Natdtimer();
+static void            SendPortMapMulti( );
+static void            NotifyPublicAddress();
+static void            DoPortMapping( int fd, struct sockaddr_in *clientaddr, int clientaddrlen, publicportreq *req);
+static void            NatPortMapPInit();
+
+extern int FindAliasPortOut(struct in_addr src_addr,  struct in_addr dst_addr, u_short src_port, u_short pub_port, u_char proto, int lifetime, char addmapping);
+
+#endif /* NATPORTMAP */
 
 int main (int argc, char** argv)
 {
@@ -134,6 +268,7 @@ int main (int argc, char** argv)
        fd_set                  readMask;
        fd_set                  writeMask;
        int                     fdMax;
+       int                     fdFlags;
 /* 
  * Initialize packet aliasing software.
  * Done already here to be able to alter option bits
@@ -153,6 +288,9 @@ int main (int argc, char** argv)
        running                 = 1;
        assignAliasAddr         = 0;
        aliasAddr.s_addr        = INADDR_NONE;
+#ifdef NATPORTMAP
+       lastassignaliasAddr.s_addr      = INADDR_NONE;
+#endif
        aliasOverhead           = 12;
        dynamicMode             = 0;
        logDropped              = 0;
@@ -167,16 +305,14 @@ int main (int argc, char** argv)
 /*
  * Open syslog channel.
  */
-       openlog ("natd", LOG_CONS | LOG_PID, logFacility);
+       openlog ("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0),
+                logFacility);
 /*
  * Check that valid aliasing address has been given.
  */
        if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL)
                errx (1, "aliasing address not given");
 
-       if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL)
-               errx (1, "both alias address and interface "
-                        "name are not allowed");
 /*
  * Check that valid port number is known.
  */
@@ -185,7 +321,7 @@ int main (int argc, char** argv)
                        errx (1, "both input and output ports are required");
 
        if (inPort == 0 && outPort == 0 && inOutPort == 0)
-               ParseOption ("port", DEFAULT_SERVICE, 0);
+               ParseOption ("port", DEFAULT_SERVICE);
 
 /*
  * Check if ignored packets should be dropped.
@@ -253,16 +389,22 @@ int main (int argc, char** argv)
                        Quit ("Unable to bind outgoing divert socket.");
        }
 /*
- * Create routing socket if interface name specified.
+ * Create routing socket if interface name specified and in dynamic mode.
  */
-       if (ifName && dynamicMode) {
+       routeSock = -1;
+       if (ifName) {
+               if (dynamicMode) {
 
-               routeSock = socket (PF_ROUTE, SOCK_RAW, 0);
-               if (routeSock == -1)
-                       Quit ("Unable to create routing info socket.");
+                       routeSock = socket (PF_ROUTE, SOCK_RAW, 0);
+                       if (routeSock == -1)
+                               Quit ("Unable to create routing info socket.");
+
+                       assignAliasAddr = 1;
+               }
+               else{
+                       SetAliasAddressFromIfName (ifName);
+               }
        }
-       else
-               routeSock = -1;
 /*
  * Create socket for sending ICMP messages.
  */
@@ -270,11 +412,37 @@ int main (int argc, char** argv)
        if (icmpSock == -1)
                Quit ("Unable to create ICMP socket.");
 
-/*
- * And disable reads for the socket, otherwise it slowly fills
- * up with received icmps which we do not use.
- */
-       shutdown(icmpSock, SHUT_RD);
+       if ((fdFlags = fcntl(icmpSock, F_GETFL, 0)) == -1)
+               Quit ("fcntl F_GETFL ICMP socket.");
+       fdFlags |= O_NONBLOCK;
+       if (fcntl(icmpSock, F_SETFL, fdFlags) == -1)
+               Quit ("fcntl F_SETFL ICMP socket.");    
+
+#if NATPORTMAP
+       if ( enable_natportmap )
+       {
+               /* create socket to listen for port mapping  */
+               portmapSock = socket( AF_INET, SOCK_DGRAM, 0);
+               if ( portmapSock != -1 )
+               {
+                       addr.sin_family         = AF_INET;
+                       addr.sin_addr.s_addr    = INADDR_ANY;
+                       addr.sin_port           = htons(NATPMP_PORT);
+
+                       if (bind ( portmapSock,
+                                 (struct sockaddr*) &addr,
+                                 sizeof addr) == -1)
+                               printf("Binding to NATPM port failed!\n");
+
+                       /* NATPORTMAPP initial set up */
+                       NatPortMapPInit();
+               }
+                if ( !Natdtimerset ){
+                        Natdtimerset = 1;
+                        signal(SIGALRM, Natdtimer);
+                }
+       }
+#endif /* NATPORTMAP */
 
 /*
  * Become a daemon unless verbose mode was requested.
@@ -285,13 +453,24 @@ int main (int argc, char** argv)
  * Catch signals to manage shutdown and
  * refresh of interface address.
  */
+       siginterrupt(SIGTERM, 1);
+       siginterrupt(SIGHUP, 1);
        signal (SIGTERM, InitiateShutdown);
        signal (SIGHUP, RefreshAddr);
+       signal (SIGINFO, HandleInfo);
 /*
  * Set alias address if it has been given.
  */
        if (aliasAddr.s_addr != INADDR_NONE)
+       {
                PacketAliasSetAddress (aliasAddr);
+#ifdef NATPORTMAP
+               if ( (enable_natportmap) && (aliasAddr.s_addr != lastassignaliasAddr.s_addr) ){
+                       lastassignaliasAddr.s_addr = aliasAddr.s_addr;
+                       NotifyPublicAddress();
+               }
+#endif
+       }
 /*
  * We need largest descriptor number for select.
  */
@@ -310,6 +489,15 @@ int main (int argc, char** argv)
        if (routeSock > fdMax)
                fdMax = routeSock;
 
+       if (icmpSock > fdMax)
+               fdMax = icmpSock;
+       
+#ifdef NATPORTMAP
+       if ( portmapSock > fdMax )
+               fdMax = portmapSock;
+                       
+#endif
+
        while (running) {
 
                if (divertInOut != -1 && !ifName && packetSock == -1) {
@@ -347,22 +535,32 @@ int main (int argc, char** argv)
 
                        if (divertInOut != -1)
                                FD_SET (divertInOut, &readMask);
+
+                       if (icmpSock != -1)
+                               FD_SET(icmpSock, &readMask);
                }
 /*
  * Routing info is processed always.
  */
                if (routeSock != -1)
                        FD_SET (routeSock, &readMask);
-
+#ifdef NATPORTMAP
+               if ( portmapSock != -1 )
+                       FD_SET (portmapSock, &readMask);
+#endif
                if (select (fdMax + 1,
                            &readMask,
                            &writeMask,
                            NULL,
                            NULL) == -1) {
 
-                       if (errno == EINTR)
+                       if (errno == EINTR) {
+                               if (dumpinfo) {
+                                       DumpInfo();
+                                       dumpinfo = 0;
+                               }
                                continue;
-
+                       }
                        Quit ("Select failed.");
                }
 
@@ -385,6 +583,16 @@ int main (int argc, char** argv)
                if (routeSock != -1)
                        if (FD_ISSET (routeSock, &readMask))
                                HandleRoutingInfo (routeSock);
+
+               if (icmpSock != -1)
+                       if (FD_ISSET (icmpSock, &readMask))
+                               DiscardIncomingPackets (icmpSock);
+
+#ifdef NATPORTMAP
+               if ( portmapSock != -1)
+                       if (FD_ISSET (portmapSock, &readMask))
+                               HandlePortMap( portmapSock );
+#endif
        }
 
        if (background)
@@ -406,14 +614,30 @@ static void DaemonMode ()
                fprintf (pidFile, "%d\n", getpid ());
                fclose (pidFile);
        }
+
+#ifdef DEBUG
+#include <fcntl.h>
+       {
+               int     fd;
+
+               fd = open("/var/run/natd.log", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+               if (fd >= 0) {
+                       if (fd != STDOUT_FILENO) {
+                               dup2(fd, STDOUT_FILENO);
+                               close(fd);
+                       }
+                       dup2(STDOUT_FILENO, STDERR_FILENO);
+               }
+       }
+#endif
 }
 
 static void ParseArgs (int argc, char** argv)
 {
        int             arg;
-       char*           parm;
        char*           opt;
        char            parmBuf[256];
+       int             len; /* bounds checking */
 
        for (arg = 1; arg < argc; arg++) {
 
@@ -424,23 +648,27 @@ static void ParseArgs (int argc, char** argv)
                        Usage ();
                }
 
-               parm = NULL;
                parmBuf[0] = '\0';
+               len = 0;
 
                while (arg < argc - 1) {
 
                        if (argv[arg + 1][0] == '-')
                                break;
 
-                       if (parm)
-                               strcat (parmBuf, " ");
+                       if (len) {
+                               strncat (parmBuf, " ", sizeof(parmBuf) - (len + 1));
+                               len += strlen(parmBuf + len);
+                       }
 
                        ++arg;
-                       parm = parmBuf;
-                       strcat (parmBuf, argv[arg]);
+                       strncat (parmBuf, argv[arg], sizeof(parmBuf) - (len + 1));
+                       len += strlen(parmBuf + len);
+
                }
 
-               ParseOption (opt + 1, parm, 1);
+               ParseOption (opt + 1, (len ? parmBuf : NULL));
+
        }
 }
 
@@ -449,8 +677,8 @@ static void DoAliasing (int fd, int direction)
        int                     bytes;
        int                     origBytes;
        int                     status;
-       int                     addrSize;
-       struct ip*              ip;
+       socklen_t       addrSize;
+       struct ip*      ip;
 
        if (assignAliasAddr) {
 
@@ -479,14 +707,14 @@ static void DoAliasing (int fd, int direction)
  * This is a IP packet.
  */
        ip = (struct ip*) packetBuf;
-       if (direction == DONT_KNOW)
+       if (direction == DONT_KNOW) {
                if (packetAddr.sin_addr.s_addr == INADDR_ANY)
                        direction = OUTPUT;
                else
                        direction = INPUT;
+       }
 
        if (verbose) {
-               
 /*
  * Print packet direction and protocol type.
  */
@@ -630,18 +858,401 @@ static void HandleRoutingInfo (int fd)
        }
 
        if (verbose)
-               printf ("Routing message %X received.\n", ifMsg.ifm_type);
+               printf ("Routing message %#x received.\n", ifMsg.ifm_type);
+
+       if ((ifMsg.ifm_type == RTM_NEWADDR || ifMsg.ifm_type == RTM_IFINFO) &&
+           ifMsg.ifm_index == ifIndex) {
+               if (verbose)
+                       printf("Interface address/MTU has probably changed.\n");
+               assignAliasAddr = 1;
+       }
+}
+
+static void DiscardIncomingPackets (int fd)
+{
+       struct sockaddr_in sin;
+       char buffer[80];
+       socklen_t slen = sizeof(sin);
+       
+       while (recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&sin, &slen) != -1) {
+               ;
+       }
+}
+
+#ifdef NATPORTMAP
+
+void getdivisor()
+{
+       struct mach_timebase_info info;
+
+       (void) mach_timebase_info (&info);
+
+       secdivisor = ( (double)info.denom / (double)info.numer)  * 1000;
+
+}
+
+uint32_t getuptime()
+{
+       uint64_t        now;
+       uint32_t        epochtime;
+
+       now = mach_absolute_time();
+       epochtime = (now / secdivisor) / USEC_PER_SEC; 
+       return ( epochtime );
+
+}
+
+/* set up neccessary info for doing NatPortMapP */
+static void NatPortMapPInit()
+{
+       int i;
+       struct ifaddrs  *ifap, *ifa;
+
+       forwardedinterfaceaddr = (struct in_addr *)
+               malloc(numofinterfaces * sizeof(*forwardedinterfaceaddr));
+       bzero(forwardedinterfaceaddr, 
+             numofinterfaces * sizeof(*forwardedinterfaceaddr));
+       /* interface address hasn't been set up, get interface address */
+
+       if (getifaddrs(&ifap) == -1)
+               Quit ("getifaddrs failed.");
+
+       for ( ifa= ifap; ifa; ifa=ifa->ifa_next)
+       {
+               struct sockaddr_in * a;
+               if (ifa->ifa_addr->sa_family != AF_INET) 
+               {
+                       continue;
+               }
+               a = (struct sockaddr_in *)ifa->ifa_addr;
+               for ( i = 0; i < numofinterfaces; i++ )
+               {
+                       if (strcmp(ifa->ifa_name, forwardedinterfacename[i])) 
+                       {
+                               continue;
+                       }
+                       if (forwardedinterfaceaddr[i].s_addr == 0) 
+                       {
+                               /* copy the first IP address */
+                               forwardedinterfaceaddr[i] = a->sin_addr;
+                       }
+                       break;
+               }
+       }
+       freeifaddrs( ifap );
+       getdivisor();
+}
+
+/* SendPortMapResponse */
+/* send generic reponses to NATPORTMAP requests */
+static  void SendPortMapResponse( int fd, struct sockaddr_in *clientaddr, int clientaddrlen, unsigned char origopcode, unsigned short result)
+{
+       stderrreply reply;
+       int                     bytes;
+       
+       reply.version = NATPMVERSION;
+       reply.opcode = origopcode + SERVERREPLYOP;
+       reply.result = htons(result);
+       reply.epoch = htonl(getuptime());
+       bytes = sendto( fd, (void*)&reply, sizeof(reply), 0, (struct sockaddr*)clientaddr, clientaddrlen );
+       if ( bytes != sizeof(reply) )
+               printf( "PORTMAP::problem sending portmap reply - opcode %d\n", reply.opcode );
+}
+
+/* SendPublicAddress */
+/* return public address to requestor */
+static void SendPublicAddress( int fd, struct sockaddr_in *clientaddr, int clientaddrlen )
+{
+
+       publicaddrreply reply;
+       int                             bytes;
+       
+       reply.version = NATPMVERSION;
+       reply.opcode = SERVERREPLYOP + PUBLICADDRREQ;
+       reply.result = SUCCESS;
+       reply.addr = lastassignaliasAddr;
+       reply.epoch = htonl(getuptime());
+
+       bytes = sendto (fd, (void*)&reply, sizeof(reply), 0, (struct sockaddr*)clientaddr, clientaddrlen);
+       if ( bytes != sizeof(reply) )
+               printf( "PORTMAP::problem sending portmap reply - opcode %d\n", reply.opcode );
+}
+
+/* SendPublicPortResponse */
+/* response for portmap request and portmap removal request */
+/* publicport <= 0 means error */
+static void SendPublicPortResponse( int fd, struct sockaddr_in *clientaddr, int clientaddrlen, publicportreq *req, u_short publicport, int result)
+{
+
+       int                             bytes;
+       publicportreply                 reply;
+       
+       bzero(&reply, sizeof(publicportreply));
+       reply.version = NATPMVERSION;
+       reply.opcode = SERVERREPLYOP + req->opcode;
+       if (result)
+               /* error in port mapping */
+               reply.result = htons(OUTOFRESOURCES);
+       else
+               reply.result = SUCCESS;
+       
+       reply.epoch = htonl(getuptime());
+
+       reply.privateport = req->privateport;
+
+       /* adding or renewing a mapping */
+       if ( req->lifetime ) {
+               reply.publicport = publicport;
+               reply.lifetime = req->lifetime;
+       }
+       bytes = sendto (fd, (void*)&reply, sizeof(publicportreply), 0, (struct sockaddr*)clientaddr, clientaddrlen);
+       if ( bytes != sizeof(publicportreply) )
+               printf( "PORTMAP::problem sending portmap reply - opcode %d\n", req->opcode );
+}
+
+/* SendPortMapMulti */
+/* send multicast to local network for new alias address */
+static void SendPortMapMulti()
+{
+
+       publicaddrreply reply;
+       int                             bytes;
+       struct sockaddr_in multiaddr;
+       int                             multisock;
+       int                             i;
+       
+#define LOCALGROUP "224.0.0.1"
+       numoftries++;
+       memset(&multiaddr,0,sizeof(struct sockaddr_in));
+       multiaddr.sin_family=AF_INET;
+       multiaddr.sin_addr.s_addr=inet_addr(LOCALGROUP);
+       multiaddr.sin_port=htons(NATPMP_ANNOUNCEMENT_PORT);
+       reply.version = NATPMVERSION;
+       reply.opcode = SERVERREPLYOP + PUBLICADDRREQ;
+       reply.result = SUCCESS;
+       reply.addr = lastassignaliasAddr;
+       reply.epoch = 0;
+
+       /* send multicast to all forwarded interfaces */
+       for ( i = 0; i <  numofinterfaces; i++)
+       {
+               if (forwardedinterfaceaddr[i].s_addr == 0) 
+               {
+                       continue;
+               }
+               multisock = socket( AF_INET, SOCK_DGRAM, 0);
+               
+               if ( multisock == -1 )
+               {
+                       printf("cannot get socket for sending multicast\n");
+                       return;
+               }
+               if (setsockopt(multisock, IPPROTO_IP, IP_MULTICAST_IF, &forwardedinterfaceaddr[i], sizeof(struct in_addr)) < 0) 
+               {
+                       printf("setsockopt failed\n");
+                       close(multisock);
+                       continue;
+               }
+               bytes = sendto (multisock, (void*)&reply, sizeof(reply), 0, (struct sockaddr*)&multiaddr, sizeof(multiaddr));
+               if ( bytes != sizeof(reply) )
+                       printf( "PORTMAP::problem sending multicast alias address - opcode %d\n", reply.opcode );
+               close(multisock);
+       }
+
+}
+
+/* double the time value */
+static void Doubletime( struct timeval *tvp)
+{
+       
+       timeradd(tvp, tvp, tvp);
+       
+}
 
-       if (ifMsg.ifm_type != RTM_NEWADDR)
+/* stop running natd timer */
+static void Stoptimer()
+{
+       itval.it_value.tv_sec = 0;
+       itval.it_value.tv_usec = 0;
+       if (setitimer(ITIMER_REAL, &itval, (struct itimerval *)NULL) < 0)
+               printf( "setitimer err: %d\n", errno);
+}
+
+/* natdtimer */
+/* timer routine to send new public IP address */
+static void Natdtimer()
+{
+       if ( !enable_natportmap )
+               return;
+               
+       SendPortMapMulti();
+       
+       if ( numoftries < MAXRETRY ){
+               Doubletime( &itval.it_value);
+               itval.it_interval.tv_sec = 0;
+               itval.it_interval.tv_usec = 0;
+               if (setitimer(ITIMER_REAL, &itval, (struct itimerval *)NULL) < 0)
+                               printf( "setitimer err: %d\n", errno);
+       }
+       else
+       {
+               Stoptimer();
                return;
+       }
+       
+}
 
-       if (verbose && ifMsg.ifm_index == ifIndex)
-               printf ("Interface address has changed.\n");
+/* NotifyPublicAddress */
+/* Advertise new public address */
+static void NotifyPublicAddress()
+{
+       if ( numoftries <  MAXRETRY)
+       {
+               /* there is an old timer running, cancel it */
+               Stoptimer();
+       }
+       /* send up new timer */
+       numoftries = 0;
+       SendPortMapMulti();
+       itval.it_value.tv_sec = 0;
+       itval.it_value.tv_usec = TIMER_RATE;
+       itval.it_interval.tv_sec = 0;
+       itval.it_interval.tv_usec = 0;
+       if (setitimer(ITIMER_REAL, &itval, (struct itimerval *)NULL) < 0)
+                       printf( "setitimer err: %d\n", errno);
 
-       if (ifMsg.ifm_index == ifIndex)
-               assignAliasAddr = 1;
 }
 
+/* DoPortMapping */
+/* find/add/remove port mapping from alias manager */
+void DoPortMapping( int fd, struct sockaddr_in *clientaddr, int clientaddrlen, publicportreq *req)
+{
+       u_char          proto = IPPROTO_TCP;
+       int             aliasport;
+       struct in_addr inany = { INADDR_ANY };
+       
+       if ( req->opcode == MAPUDPREQ)
+               proto = IPPROTO_UDP;
+       if ( req->lifetime == 0)
+       {
+               /* remove port mapping */
+               if ( !FindAliasPortOut( clientaddr->sin_addr,
+                                                               inany,
+                                                               req->privateport,
+                                                               req->publicport,
+                                                               proto,
+                                                               ntohl(req->lifetime),
+                                                               0) )
+                       /* FindAliasPortOut returns no error, port successfully removed, return no error response to client */
+                       SendPublicPortResponse( fd, clientaddr, clientaddrlen, req, 0, 0 );
+               else
+                       /* deleting port fails, return error */
+                       SendPublicPortResponse( fd, clientaddr, clientaddrlen, req, 0, -1 );
+       }
+       else 
+       {
+               /* look for port mapping - public port is ignored in this case */
+               /* create port mapping - map provided public port to private port if public port is not 0 */
+               aliasport = FindAliasPortOut( clientaddr->sin_addr, 
+                                                                         inany, /* lastassignaliasAddr */
+                                                                         req->privateport, 
+                                                                         0, 
+                                                                         proto, 
+                                                                         ntohl(req->lifetime), 
+                                                                         1);
+               /* aliasport should be non zero if mapping is successfully, else -1 is returned, alias port shouldn't be zero???? */
+               SendPublicPortResponse( fd, clientaddr, clientaddrlen, req, aliasport, 0 );
+                       
+       }
+}
+
+/* HandlePortMap */
+/* handle all packets sent to NATPORTMAP port  */
+static void HandlePortMap( int fd )
+{
+       #define         MAXBUFFERSIZE           100
+       
+       struct sockaddr_in                      clientaddr;
+       socklen_t                                       clientaddrlen;
+       unsigned char                           buffer[MAXBUFFERSIZE];
+       int                                                     bytes;
+       unsigned short                          result = SUCCESS;
+       struct stdportmaprequest        *req;
+       
+       clientaddrlen = sizeof( clientaddr );
+       bytes = recvfrom( fd, buffer, sizeof(buffer), 0, (struct sockaddr*)&clientaddr, &clientaddrlen);
+       if ( bytes == -1 )
+       {
+               printf( "Read NATPM port error\n");
+               return;
+       }
+       else if ( bytes < sizeof(stdportmaprequest) )
+       {
+               /* drop any requests that are too short */
+               return;
+       }
+
+       req = (struct stdportmaprequest*)buffer;
+       
+       #ifdef DEBUG
+       {
+               int i;
+               
+               printf("HandlePortMap from %s:%u length= %d: ", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port, bytes);
+               for ( i = 0; i<bytes; i++)
+               {
+                       printf("%02x", buffer[i]);
+               }
+               printf("\n");
+       }
+       #endif                  
+
+       /* drop any reply packets that we receive on the floor */
+       if ( req->opcode >= SERVERREPLYOP )
+       {
+               return;
+       }
+
+       /* check client version */
+       if ( req->version > NATPMVERSION )
+               result = NOTSUPPORTEDVERSION;
+       else if ( !enable_natportmap )
+               /* natd wasn't launched with portmapping enabled */
+               result = NOTAUTHORIZED;
+               
+       if ( result )
+       {
+               SendPortMapResponse( fd, &clientaddr, clientaddrlen, req->opcode, result );
+               return;
+       }
+               
+       switch ( req->opcode )
+       {
+               case PUBLICADDRREQ:
+               {
+                       if ( bytes == sizeof(stdportmaprequest) )
+                               SendPublicAddress(fd, &clientaddr, clientaddrlen);
+                       break;
+               }
+               
+               case MAPUDPREQ:
+               case MAPTCPREQ:
+               case MAPUDPTCPREQ:
+               {
+                       if ( bytes == sizeof(publicportreq) )
+                               DoPortMapping( fd, &clientaddr, clientaddrlen, (publicportreq*)req);
+                       break;
+               }
+               
+               
+               default:
+                       SendPortMapResponse( fd, &clientaddr, clientaddrlen, req->opcode, UNSUPPORTEDOPCODE );
+       }
+               
+}
+
+#endif /* NATPORTMAP */
+
 static void PrintPacket (struct ip* ip)
 {
        printf ("%s", FormatPacket (ip));
@@ -700,101 +1311,118 @@ static char* FormatPacket (struct ip* ip)
        return buf;
 }
 
-static void SetAliasAddressFromIfName (char* ifn)
+static void
+SetAliasAddressFromIfName(const char *ifn)
 {
-       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;
+       size_t needed;
+       int mib[6];
+       char *buf, *lim, *next;
+       struct if_msghdr *ifm;
+       struct ifa_msghdr *ifam;
+       struct sockaddr_dl *sdl;
+       struct sockaddr_in *sin;
+
+       mib[0] = CTL_NET;
+       mib[1] = PF_ROUTE;
+       mib[2] = 0;
+       mib[3] = AF_INET;       /* Only IP addresses please */
+       mib[4] = NET_RT_IFLIST;
+       mib[5] = 0;             /* ifIndex??? */
 /*
  * 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';
+       if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
+               err(1, "iflist-sysctl-estimate");
+       if ((buf = malloc(needed)) == NULL)
+               errx(1, "malloc failed");
+       if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
+               err(1, "iflist-sysctl-get");
+       lim = buf + needed;
 /*
  * 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, ifn)) {
-
-                       found = 1;
-                       break;
+       ifIndex = 0;
+       next = buf;
+       while (next < lim) {
+               ifm = (struct if_msghdr *)next;
+               next += ifm->ifm_msglen;
+               if (ifm->ifm_version != RTM_VERSION) {
+                       if (verbose)
+                               warnx("routing message version %d "
+                                     "not understood", ifm->ifm_version);
+                       continue;
                }
-
-               if (strcmp (last, ifPtr->ifr_name)) {
-
-                       strcpy (last, ifPtr->ifr_name);
-                       ++ifIndex;
+               if (ifm->ifm_type == RTM_IFINFO) {
+                       sdl = (struct sockaddr_dl *)(ifm + 1);
+                       if (strlen(ifn) == sdl->sdl_nlen &&
+                           strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
+                               ifIndex = ifm->ifm_index;
+                               ifMTU = ifm->ifm_data.ifi_mtu;
+                               if (clampMSS)
+                                       PacketAliasClampMSS(ifMTU - sizeof(struct tcphdr) - sizeof(struct ip));
+                               break;
+                       }
                }
-
-               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", ifn);
-               Quit (msg);
-       }
-/*
- * Get MTU size.
- */
-       strcpy (req.ifr_name, ifn);
-
-       if (ioctl (helperSock, SIOCGIFMTU, &req) == -1)
-               Quit ("Cannot get interface mtu size.");
-
-       ifMTU = req.ifr_mtu;
+       if (!ifIndex)
+               errx(1, "unknown interface name %s", ifn);
 /*
  * Get interface address.
  */
-       if (ioctl (helperSock, SIOCGIFADDR, &req) == -1)
-               Quit ("Cannot get interface address.");
-
-       addr = (struct sockaddr_in*) &req.ifr_addr;
-       PacketAliasSetAddress (addr->sin_addr);
-       syslog (LOG_INFO, "Aliasing to %s, mtu %d bytes",
-                         inet_ntoa (addr->sin_addr),
-                         ifMTU);
+       if (aliasAddr.s_addr == INADDR_NONE) {
+       sin = NULL;
+       while (next < lim) {
+               ifam = (struct ifa_msghdr *)next;
+               next += ifam->ifam_msglen;
+               if (ifam->ifam_version != RTM_VERSION) {
+                       if (verbose)
+                               warnx("routing message version %d "
+                                     "not understood", ifam->ifam_version);
+                       continue;
+               }
+               if (ifam->ifam_type != RTM_NEWADDR)
+                       break;
+               if (ifam->ifam_addrs & RTA_IFA) {
+                       int i;
+                       char *cp = (char *)(ifam + 1);
+
+#define ROUNDUP(a) \
+       ((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+                       for (i = 1; i < RTA_IFA; i <<= 1)
+                               if (ifam->ifam_addrs & i)
+                                       ADVANCE(cp, (struct sockaddr *)cp);
+                       if (((struct sockaddr *)cp)->sa_family == AF_INET) {
+                               sin = (struct sockaddr_in *)cp;
+                               break;
+                       }
+               }
+       }
+       if (sin == NULL)
+               errx(1, "%s: cannot get interface address", ifn);
+
+       PacketAliasSetAddress(sin->sin_addr);
+#ifdef NATPORTMAP
+       if ( (enable_natportmap) && (sin->sin_addr.s_addr != lastassignaliasAddr.s_addr) )
+       {
+               lastassignaliasAddr.s_addr = sin->sin_addr.s_addr;
+               /* make sure the timer handler was set before setting timer */
+               if ( !Natdtimerset ){
+                       Natdtimerset = 1;       
+                       signal(SIGALRM, Natdtimer); 
+               }
+               NotifyPublicAddress();
+       }
+#endif
+       syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes",
+              inet_ntoa(sin->sin_addr), ifMTU);
+       }
 
-       close (helperSock);
+       free(buf);
 }
 
 void Quit (const char* msg)
@@ -808,12 +1436,11 @@ void Warn (const char* msg)
        if (background)
                syslog (LOG_ALERT, "%s (%m)", msg);
        else
-               warn (msg);
+               warn ("%s", msg);
 }
 
 static void RefreshAddr (int sig)
 {
-       signal (SIGHUP, RefreshAddr);
        if (ifName)
                assignAliasAddr = 1;
 }
@@ -825,6 +1452,7 @@ static void InitiateShutdown (int sig)
  * shutdown existing connections when system
  * is shut down.
  */
+       siginterrupt(SIGALRM, 1);
        signal (SIGALRM, Shutdown);
        alarm (10);
 }
@@ -834,6 +1462,11 @@ static void Shutdown (int sig)
        running = 0;
 }
 
+static void HandleInfo (int sig)
+{
+       dumpinfo++;
+}
+
 /* 
  * Different options recognized by this program.
  */
@@ -846,15 +1479,22 @@ enum Option {
        OutPort,
        Port,
        AliasAddress,
+       TargetAddress,
        InterfaceName,
        RedirectPort,
+       RedirectProto,
        RedirectAddress,
        ConfigFile,
        DynamicMode,
-       PptpAlias,
+       ClampMSS,
        ProxyRule,
        LogDenied,
-       LogFacility
+       LogFacility,
+       PunchFW,
+#ifdef NATPORTMAP
+       NATPortMap,
+       ToInterfaceName
+#endif
 };
 
 enum Param {
@@ -960,6 +1600,14 @@ static struct OptionInfo optionTable[] = {
                "dynamic",
                NULL },
        
+       { ClampMSS,
+               0,
+               YesNo,
+               "[yes|no]",
+               "enable TCP MSS clamping",
+               "clamp_mss",
+               NULL },
+       
        { InPort,
                0,
                Service,
@@ -992,6 +1640,14 @@ static struct OptionInfo optionTable[] = {
                "alias_address",
                "a" },
        
+       { TargetAddress,
+               0,
+               Address,
+               "x.x.x.x",
+               "address to use for incoming sessions",
+               "target_address",
+               "t" },
+       
        { InterfaceName,
                0,
                String,
@@ -1012,26 +1668,26 @@ static struct OptionInfo optionTable[] = {
        { RedirectPort,
                0,
                String,
-               "tcp|udp local_addr:local_port_range [public_addr:]public_port_range"
+               "tcp|udp local_addr:local_port_range[,...] [public_addr:]public_port_range"
                " [remote_addr[:remote_port_range]]",
                "redirect a port (or ports) for incoming traffic",
                "redirect_port",
                NULL },
 
-       { RedirectAddress,
+       { RedirectProto,
                0,
                String,
-               "local_addr public_addr",
-               "define mapping between local and public addresses",
-               "redirect_address",
+               "proto local_addr [public_addr] [remote_addr]",
+               "redirect packets of a given proto",
+               "redirect_proto",
                NULL },
 
-       { PptpAlias,
+       { RedirectAddress,
                0,
                String,
-               "src",
-               "define inside machine for PPTP traffic",
-               "pptpalias",
+               "local_addr[,...] public_addr",
+               "define mapping between local and public addresses",
+               "redirect_address",
                NULL },
 
        { ConfigFile,
@@ -1056,11 +1712,37 @@ static struct OptionInfo optionTable[] = {
                "facility",
                "name of syslog facility to use for logging",
                "log_facility",
-               NULL }
+               NULL },
 
+       { PunchFW,
+               0,
+               String,
+               "basenumber:count",
+               "punch holes in the firewall for incoming FTP/IRC DCC connections",
+               "punch_fw",
+               NULL },
+
+#ifdef NATPORTMAP
+       { NATPortMap,
+               0,
+               YesNo,
+               "[yes|no]",
+               "enable NATPortMap protocol",
+               "enable_natportmap",
+               NULL },
+               
+       { ToInterfaceName,
+               0,
+               String,
+               "network_if_name",
+               "take aliasing address to interface",
+               "natportmap_interface",
+               NULL },
+
+#endif
 };
        
-static void ParseOption (const char* option, const char* parms, int cmdLine)
+static void ParseOption (const char* option, const char* parms)
 {
        int                     i;
        struct OptionInfo*      info;
@@ -1167,6 +1849,10 @@ static void ParseOption (const char* option, const char* parms, int cmdLine)
                dynamicMode = yesNoValue;
                break;
 
+       case ClampMSS:
+               clampMSS = yesNoValue;
+               break;
+
        case InPort:
                inPort = uNumValue;
                break;
@@ -1183,16 +1869,20 @@ static void ParseOption (const char* option, const char* parms, int cmdLine)
                memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr));
                break;
 
+       case TargetAddress:
+               PacketAliasSetTarget(addrValue);
+               break;
+
        case RedirectPort:
                SetupPortRedirect (strValue);
                break;
 
-       case RedirectAddress:
-               SetupAddressRedirect (strValue);
+       case RedirectProto:
+               SetupProtoRedirect(strValue);
                break;
 
-       case PptpAlias:
-               SetupPptpAlias (strValue);
+       case RedirectAddress:
+               SetupAddressRedirect (strValue);
                break;
 
        case ProxyRule:
@@ -1204,7 +1894,6 @@ static void ParseOption (const char* option, const char* parms, int cmdLine)
                        free (ifName);
 
                ifName = strdup (strValue);
-               assignAliasAddr = 1;
                break;
 
        case ConfigFile:
@@ -1234,42 +1923,76 @@ static void ParseOption (const char* option, const char* parms, int cmdLine)
                        errx(1, "Unknown log facility name: %s", strValue);     
 
                break;
+
+       case PunchFW:
+               SetupPunchFW(strValue);
+               break;
+
+#ifdef NATPORTMAP
+       case NATPortMap:
+               enable_natportmap = yesNoValue;
+               break;
+
+
+       case ToInterfaceName:
+       {
+               if (forwardedinterfacename != NULL)
+               {
+                       if ( (forwardedinterfacename = realloc( forwardedinterfacename, (numofinterfaces+1) * sizeof(*forwardedinterfacename ))) == NULL ){
+                               printf("realloc error, cannot allocate memory for fowarded interface name.\n");
+                               return;
+                       }
+               }
+               else {
+                       if ( (forwardedinterfacename = malloc( sizeof(*forwardedinterfacename) )) == NULL ){
+                               printf("malloc error, cannot allocate memory for fowarded interface name.\n");
+                               return;
+                       }
+               }
+               
+               forwardedinterfacename[numofinterfaces] = strdup(strValue);
+               numofinterfaces++;
+
+               break;
+       }
+
+#endif
+
        }
 }
 
 void ReadConfigFile (const char* fileName)
 {
        FILE*   file;
-       char    buf[128];
-       char*   ptr;
+       char    *buf;
+       size_t  len;
+       char    *ptr, *p;
        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)) {
+       if (!file)
+               err(1, "cannot open config file %s", fileName);
 
-               ptr = strchr (buf, '\n');
-               if (!ptr)
-                       errx (1, "config line too long: %s", buf);
-
-               *ptr = '\0';
-               if (buf[0] == '#')
-                       continue;
+       while ((buf = fgetln(file, &len)) != NULL) {
+               if (buf[len - 1] == '\n')
+                       buf[len - 1] = '\0';
+               else
+                       errx(1, "config file format error: "
+                               "last line should end with newline");
 
-               ptr = buf;
 /*
- * Skip white space at beginning of line.
+ * Check for comments, strip off trailing spaces.
  */
-               while (*ptr && isspace (*ptr))
-                       ++ptr;
-
+               if ((ptr = strchr(buf, '#')))
+                       *ptr = '\0';
+               for (ptr = buf; isspace(*ptr); ++ptr)
+                       continue;
                if (*ptr == '\0')
                        continue;
+               for (p = strchr(buf, '\0'); isspace(*--p);)
+                       continue;
+               *++p = '\0';
+
 /*
  * Extract option name.
  */
@@ -1288,7 +2011,7 @@ void ReadConfigFile (const char* fileName)
                while (*ptr && isspace (*ptr))
                        ++ptr;
 
-               ParseOption (option, *ptr ? ptr : NULL, 0);
+               ParseOption (option, *ptr ? ptr : NULL);
        }
 
        fclose (file);
@@ -1318,29 +2041,11 @@ static void Usage ()
        exit (1);
 }
 
-void SetupPptpAlias (const char* parms)
-{
-       char            buf[128];
-       char*           ptr;
-       struct in_addr  srcAddr;
-
-       strcpy (buf, parms);
-
-/*
- * Extract source address.
- */
-       ptr = strtok (buf, " \t");
-       if (!ptr)
-               errx(1, "pptpalias: missing src address");
-
-       StrToAddr (ptr, &srcAddr);
-       PacketAliasPptp (srcAddr);
-}
-
 void SetupPortRedirect (const char* parms)
 {
        char            buf[128];
        char*           ptr;
+       char*           serverPool;
        struct in_addr  localAddr;
        struct in_addr  publicAddr;
        struct in_addr  remoteAddr;
@@ -1355,6 +2060,7 @@ void SetupPortRedirect (const char* parms)
        char*           protoName;
        char*           separator;
        int             i;
+       struct alias_link *link = NULL;
 
        strcpy (buf, parms);
 /*
@@ -1372,14 +2078,23 @@ void SetupPortRedirect (const char* parms)
        if (!ptr)
                errx (1, "redirect_port: missing local address");
 
-       if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 )
-               errx (1, "redirect_port: invalid local port range");
-
-       localPort     = GETLOPORT(portRange);
-       numLocalPorts = GETNUMPORTS(portRange);
+       separator = strchr(ptr, ',');
+       if (separator) {                /* LSNAT redirection syntax. */
+               localAddr.s_addr = INADDR_NONE;
+               localPort = ~0;
+               numLocalPorts = 1;
+               serverPool = ptr;
+       } else {
+               if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 )
+                       errx (1, "redirect_port: invalid local port range");
+
+               localPort     = GETLOPORT(portRange);
+               numLocalPorts = GETNUMPORTS(portRange);
+               serverPool = NULL;
+       }
 
 /*
- * Extract public port and optinally address.
+ * Extract public port and optionally address.
  */
        ptr = strtok (NULL, " \t");
        if (!ptr)
@@ -1405,10 +2120,10 @@ void SetupPortRedirect (const char* parms)
        ptr = strtok (NULL, " \t");
        if (ptr) {
                separator = strchr (ptr, ':');
-               if (separator)
+               if (separator) {
                        if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0)
                                errx (1, "redirect_port: invalid remote port range");
-               else {
+               else {
                        SETLOPORT(portRange, 0);
                        SETNUMPORTS(portRange, 1);
                        StrToAddr (ptr, &remoteAddr);
@@ -1430,7 +2145,7 @@ void SetupPortRedirect (const char* parms)
                errx (1, "redirect_port: port ranges must be equal in size");
 
        /* Remote port range is allowed to be '0' which means all ports. */
-       if (numRemotePorts != numLocalPorts && numRemotePorts != 1 && remotePort != 0)
+       if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0))
                errx (1, "redirect_port: remote port must be 0 or equal to local port range in size");
 
        for (i = 0 ; i < numPublicPorts ; ++i) {
@@ -1439,22 +2154,98 @@ void SetupPortRedirect (const char* parms)
                if (numRemotePorts == 1 && remotePort == 0)
                        remotePortCopy = 0;
 
-               PacketAliasRedirectPort (localAddr,
-                                        htons(localPort + i),
-                                        remoteAddr,
-                                        htons(remotePortCopy),
-                                        publicAddr,
-                                        htons(publicPort + i),
-                                        proto);
+               link = PacketAliasRedirectPort (localAddr,
+                                               htons(localPort + i),
+                                               remoteAddr,
+                                               htons(remotePortCopy),
+                                               publicAddr,
+                                               htons(publicPort + i),
+                                               proto);
+       }
+
+/*
+ * Setup LSNAT server pool.
+ */
+       if (serverPool != NULL && link != NULL) {
+               ptr = strtok(serverPool, ",");
+               while (ptr != NULL) {
+                       if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0)
+                               errx(1, "redirect_port: invalid local port range");
+
+                       localPort = GETLOPORT(portRange);
+                       if (GETNUMPORTS(portRange) != 1)
+                               errx(1, "redirect_port: local port must be single in this context");
+                       PacketAliasAddServer(link, localAddr, htons(localPort));
+                       ptr = strtok(NULL, ",");
+               }
        }
 }
 
+void
+SetupProtoRedirect(const char* parms)
+{
+       char            buf[128];
+       char*           ptr;
+       struct in_addr  localAddr;
+       struct in_addr  publicAddr;
+       struct in_addr  remoteAddr;
+       int             proto;
+       char*           protoName;
+       struct protoent *protoent;
+
+       strcpy (buf, parms);
+/*
+ * Extract protocol.
+ */
+       protoName = strtok(buf, " \t");
+       if (!protoName)
+               errx(1, "redirect_proto: missing protocol");
+
+       protoent = getprotobyname(protoName);
+       if (protoent == NULL)
+               errx(1, "redirect_proto: unknown protocol %s", protoName);
+       else
+               proto = protoent->p_proto;
+/*
+ * Extract local address.
+ */
+       ptr = strtok(NULL, " \t");
+       if (!ptr)
+               errx(1, "redirect_proto: missing local address");
+       else
+               StrToAddr(ptr, &localAddr);
+/*
+ * Extract optional public address.
+ */
+       ptr = strtok(NULL, " \t");
+       if (ptr)
+               StrToAddr(ptr, &publicAddr);
+       else
+               publicAddr.s_addr = INADDR_ANY;
+/*
+ * Extract optional remote address.
+ */
+       ptr = strtok(NULL, " \t");
+       if (ptr)
+               StrToAddr(ptr, &remoteAddr);
+       else
+               remoteAddr.s_addr = INADDR_ANY;
+/*
+ * Create aliasing link.
+ */
+       (void)PacketAliasRedirectProto(localAddr, remoteAddr, publicAddr,
+                                      proto);
+}
+
 void SetupAddressRedirect (const char* parms)
 {
        char            buf[128];
        char*           ptr;
+       char*           separator;
        struct in_addr  localAddr;
        struct in_addr  publicAddr;
+       char*           serverPool;
+       struct alias_link *link;
 
        strcpy (buf, parms);
 /*
@@ -1464,7 +2255,14 @@ void SetupAddressRedirect (const char* parms)
        if (!ptr)
                errx (1, "redirect_address: missing local address");
 
-       StrToAddr (ptr, &localAddr);
+       separator = strchr(ptr, ',');
+       if (separator) {                /* LSNAT redirection syntax. */
+               localAddr.s_addr = INADDR_NONE;
+               serverPool = ptr;
+       } else {
+               StrToAddr (ptr, &localAddr);
+               serverPool = NULL;
+       }
 /*
  * Extract public address.
  */
@@ -1473,7 +2271,19 @@ void SetupAddressRedirect (const char* parms)
                errx (1, "redirect_address: missing public address");
 
        StrToAddr (ptr, &publicAddr);
-       PacketAliasRedirectAddr (localAddr, publicAddr);
+       link = PacketAliasRedirectAddr(localAddr, publicAddr);
+
+/*
+ * Setup LSNAT server pool.
+ */
+       if (serverPool != NULL && link != NULL) {
+               ptr = strtok(serverPool, ",");
+               while (ptr != NULL) {
+                       StrToAddr(ptr, &localAddr);
+                       PacketAliasAddServer(link, localAddr, htons(~0));
+                       ptr = strtok(NULL, ",");
+               }
+       }
 }
 
 void StrToAddr (const char* str, struct in_addr* addr)
@@ -1576,3 +2386,15 @@ int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, p
        StrToAddr (str, addr);
        return StrToPortRange (ptr, proto, portRange);
 }
+
+static void
+SetupPunchFW(const char *strValue)
+{
+       unsigned int base, num;
+
+       if (sscanf(strValue, "%u:%u", &base, &num) != 2)
+               errx(1, "punch_fw: basenumber:count parameter required");
+
+       PacketAliasSetFWBase(base, num);
+       (void)PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
+}