*
* @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 file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ 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
+ * The 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.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <sys/types.h>
#include <sys/socket.h>
+#include <net/if.h>
+#include <ifaddrs.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
+#include <mach/mach_time.h>
+#include <mach/clock_types.h>
#include "natd.h"
static u_short inOutPort;
static struct in_addr aliasAddr;
static int dynamicMode;
+static int clampMSS;
static int ifMTU;
static int aliasOverhead;
static int icmpSock;
static int logDropped;
static int logFacility;
+#define NATPORTMAP 1
+
+#ifdef NATPORTMAP
+#define NATPMPORT 5351
+
+#define NATPMVERSION 0
+#define PUBLICADDRREQ 0
+#define MAPUDPREQ 1
+#define MAPTCPREQ 2
+#define MAPUDPTCPREQ 3
+#define SERVERREPLYOP 128
+#define PUBLICADDRRLY SERVERREPLYOP+PUBLICADDRREQ
+
+#define SUCCESS 0
+#define NOTSUPPORTEDVERSION 1
+#define NOTAUTHORIZED 2
+#define NETWORKFAILURE 3
+#define OUTOFRESOURCES 4
+#define UNSUPPORTEDOPCODE 5
+#define MAXRETRY 10
+#define TIMER_RATE 250
+
+#define FAILED -1
+
+typedef struct stdportmaprequest{
+ char version;
+ unsigned char opcode;
+ unsigned short result;
+ unsigned int epoch;
+ char data[4];
+ }stdportmaprequest;
+
+typedef struct publicaddrreply{
+ char version;
+ unsigned char opcode;
+ unsigned short result;
+ unsigned int epoch;
+ struct in_addr addr;
+ }publicaddrreply;
+typedef struct publicportreq{
+ char version;
+ unsigned char opcode;
+ unsigned short result;
+ unsigned int epoch;
+ unsigned short privateport;
+ unsigned short publicport;
+ int lifetime; /* in second */
+ }publicportreq;
+typedef struct stderrreply{
+ char version;
+ unsigned char opcode;
+ unsigned short result;
+ unsigned int 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 u_short natPMPport;
+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, int publicport);
+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();
+static u_short get_natportmap_port(void);
+
+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
+
int main (int argc, char** argv)
{
int divertIn;
running = 1;
assignAliasAddr = 0;
aliasAddr.s_addr = INADDR_NONE;
+#ifdef NATPORTMAP
+ lastassignaliasAddr.s_addr = INADDR_NONE;
+#endif
aliasOverhead = 12;
dynamicMode = 0;
logDropped = 0;
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.
*/
assignAliasAddr = 1;
}
- else
+ else{
SetAliasAddressFromIfName (ifName);
+ }
}
/*
* Create socket for sending ICMP messages.
*/
shutdown(icmpSock, SHUT_RD);
+
+#if NATPORTMAP
+ if ( enable_natportmap )
+ {
+ /* create socket to listen for port mapping */
+ portmapSock = socket( AF_INET, SOCK_DGRAM, 0);
+ if ( portmapSock != -1 )
+ {
+ natPMPport = get_natportmap_port();
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = NATPMPORT;
+
+ 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
/*
* Become a daemon unless verbose mode was requested.
*/
* 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.
*/
if (routeSock > fdMax)
fdMax = routeSock;
+#ifdef NATPORTMAP
+ if ( portmapSock > fdMax )
+ fdMax = portmapSock;
+
+#endif
+
while (running) {
if (divertInOut != -1 && !ifName && packetSock == -1) {
*/
if (routeSock != -1)
FD_SET (routeSock, &readMask);
-
+#ifdef NATPORTMAP
+ if ( portmapSock != -1 )
+ FD_SET (portmapSock, &readMask);
+#endif
if (select (fdMax + 1,
&readMask,
&writeMask,
if (routeSock != -1)
if (FD_ISSET (routeSock, &readMask))
HandleRoutingInfo (routeSock);
+#ifdef NATPORTMAP
+ if ( portmapSock != -1)
+ if (FD_ISSET (portmapSock, &readMask))
+ HandlePortMap( portmapSock );
+#endif
}
if (background)
}
}
+#ifdef NATPORTMAP
+
+void getdivisor()
+{
+ struct mach_timebase_info info;
+
+ (void) mach_timebase_info (&info);
+
+ secdivisor = ( (double)info.denom / (double)info.numer) * 1000;
+
+}
+
+unsigned int getuptime()
+{
+ uint64_t now;
+ unsigned long epochtime;
+
+ now = mach_absolute_time();
+ epochtime = (now / secdivisor) /USEC_PER_SEC;
+ return( epochtime );
+
+}
+
+/* return NATPORTMAP port defined in /etc/servcies if there's one, else use NATPMPORT */
+static u_short get_natportmap_port(void)
+{
+ struct servent *ent;
+
+ ent = getservbyname( "natportmap", "udp" );
+ if (ent != NULL){
+ return( ent->s_port );
+ }
+ return( NATPMPORT );
+}
+
+/* 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 */
+ getifaddrs(&ifap);
+ 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 = result;
+ reply.epoch = 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 = 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 *reply, int publicport)
+{
+
+ int bytes;
+
+ reply->version = NATPMVERSION;
+ reply->opcode = SERVERREPLYOP + reply->opcode;
+ if ( publicport <= 0)
+ /* error in port mapping */
+ reply->result = OUTOFRESOURCES;
+ else
+ reply->result = SUCCESS;
+ reply->epoch = getuptime();
+
+ if ( reply->lifetime ) /* not delete mapping */
+ reply->publicport = publicport;
+ bytes = sendto (fd, (void*)reply, sizeof(publicportreq), 0, (struct sockaddr*)clientaddr, clientaddrlen);
+ if ( bytes != sizeof(publicportreq) )
+ printf( "PORTMAP::problem sending portmap reply - opcode %d\n", reply->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(NATPMPORT);
+ 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)
+{
+
+ if ( tvp->tv_sec )
+ tvp->tv_sec *= 2;
+ if ( tvp->tv_usec )
+ tvp->tv_usec *= 2;
+ if (tvp->tv_usec >= 1000000) {
+ tvp->tv_sec += tvp->tv_usec / 1000000;
+ tvp->tv_usec = tvp->tv_usec % 1000000;
+ }
+}
+
+/* stop running natd timer */
+static void Stoptimer()
+{
+ 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 = itval.it_value;
+ if (setitimer(ITIMER_REAL, &itval, (struct itimerval *)NULL) < 0)
+ printf( "setitimer err: %d\n", errno);
+ }
+ else
+ {
+ Stoptimer();
+ return;
+ }
+
+}
+
+/* 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 = TIMER_RATE;
+ if (setitimer(ITIMER_REAL, &itval, (struct itimerval *)NULL) < 0)
+ printf( "setitimer err: %d\n", errno);
+
+}
+
+/* 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;
+
+ if ( req->opcode == MAPUDPREQ)
+ proto = IPPROTO_UDP;
+ if ( req->lifetime == 0)
+ {
+ /* remove port mapping */
+ if ( !FindAliasPortOut( clientaddr->sin_addr, lastassignaliasAddr, req->privateport, req->publicport, proto, req->lifetime, 0))
+ /* FindAliasPortOut returns no error, port successfully removed, return no error response to client */
+ SendPublicPortResponse( fd, clientaddr, clientaddrlen, req, 1 );
+ else
+ /* deleting port fails, return error */
+ SendPublicPortResponse( fd, clientaddr, clientaddrlen, req, -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, lastassignaliasAddr, req->privateport, req->publicport, proto, 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 );
+
+ }
+}
+
+/* HandlePortMap */
+/* handle all packets sent to NATPORTMAP port */
+static void HandlePortMap( int fd )
+{
+#define MAXBUFFERSIZE 100
+
+ struct sockaddr_in clientaddr;
+ int 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;
+ }
+ req = (struct stdportmaprequest*)buffer;
+
+#ifdef DEBUG
+ {
+ int i;
+
+ for ( i = 0; i<bytes; i++)
+ {
+ printf("%d", buffer[i]);
+ }
+ printf("\n");
+ }
+#endif
+ /* 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:
+ {
+ SendPublicAddress(fd, &clientaddr, clientaddrlen);
+ break;
+ }
+
+ case MAPUDPREQ:
+ case MAPTCPREQ:
+ case MAPUDPTCPREQ:
+ {
+ DoPortMapping( fd, &clientaddr, clientaddrlen, (publicportreq*)req);
+ break;
+ }
+
+
+ default:
+ SendPortMapResponse( fd, &clientaddr, clientaddrlen, req->opcode, UNSUPPORTEDOPCODE );
+ }
+
+}
+
+
+#endif
+
static void PrintPacket (struct ip* ip)
{
printf ("%s", FormatPacket (ip));
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;
}
}
/*
* Get interface address.
*/
+ if (aliasAddr.s_addr == INADDR_NONE) {
sin = NULL;
while (next < lim) {
ifam = (struct ifa_msghdr *)next;
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);
+ }
free(buf);
}
RedirectAddress,
ConfigFile,
DynamicMode,
+ ClampMSS,
ProxyRule,
LogDenied,
LogFacility,
- PunchFW
+ PunchFW,
+#ifdef NATPORTMAP
+ NATPortMap,
+ ToInterfaceName
+#endif
};
enum Param {
"dynamic",
NULL },
+ { ClampMSS,
+ 0,
+ YesNo,
+ "[yes|no]",
+ "enable TCP MSS clamping",
+ "clamp_mss",
+ NULL },
+
{ InPort,
0,
Service,
"basenumber:count",
"punch holes in the firewall for incoming FTP/IRC DCC connections",
"punch_fw",
- NULL }
+ 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)
dynamicMode = yesNoValue;
break;
+ case ClampMSS:
+ clampMSS = yesNoValue;
+ break;
+
case InPort:
inPort = uNumValue;
break;
case PunchFW:
SetupPunchFW(strValue);
break;
+
+#ifdef NATPORTMAP
+ case NATPortMap:
+ enable_natportmap = yesNoValue;
+ break;
+
+
+ case ToInterfaceName:
+ {
+ if (forwardedinterfacename != NULL)
+ {
+ if ( 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
+
}
}