X-Git-Url: https://git.saurik.com/apple/network_cmds.git/blobdiff_plain/7ba0088d6898d7fd2873f736f1f556673a8be855..ffda1f4a07bc29b162ac42e2babde7cf0d4efa38:/natd.tproj/natd.c diff --git a/natd.tproj/natd.c b/natd.tproj/natd.c index bea14a9..510d024 100644 --- a/natd.tproj/natd.c +++ b/natd.tproj/natd.c @@ -3,19 +3,20 @@ * * @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@ */ @@ -38,6 +39,8 @@ #include #include +#include +#include #include #include @@ -63,6 +66,8 @@ #include #include #include +#include +#include #include "natd.h" @@ -136,6 +141,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; @@ -148,6 +154,92 @@ static int dropIgnoredIncoming; 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; @@ -177,6 +269,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; @@ -199,9 +294,6 @@ int main (int argc, char** argv) 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. */ @@ -290,8 +382,9 @@ int main (int argc, char** argv) assignAliasAddr = 1; } - else + else{ SetAliasAddressFromIfName (ifName); + } } /* * Create socket for sending ICMP messages. @@ -306,6 +399,33 @@ int main (int argc, char** argv) */ 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. */ @@ -323,7 +443,15 @@ int main (int argc, char** argv) * 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. */ @@ -342,6 +470,12 @@ int main (int argc, char** argv) if (routeSock > fdMax) fdMax = routeSock; +#ifdef NATPORTMAP + if ( portmapSock > fdMax ) + fdMax = portmapSock; + +#endif + while (running) { if (divertInOut != -1 && !ifName && packetSock == -1) { @@ -385,7 +519,10 @@ int main (int argc, char** argv) */ if (routeSock != -1) FD_SET (routeSock, &readMask); - +#ifdef NATPORTMAP + if ( portmapSock != -1 ) + FD_SET (portmapSock, &readMask); +#endif if (select (fdMax + 1, &readMask, &writeMask, @@ -417,6 +554,11 @@ int main (int argc, char** argv) 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) @@ -676,6 +818,357 @@ static void HandleRoutingInfo (int fd) } } +#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; iversion > 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)); @@ -784,6 +1277,8 @@ SetAliasAddressFromIfName(const char *ifn) 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; } } @@ -793,6 +1288,7 @@ SetAliasAddressFromIfName(const char *ifn) /* * Get interface address. */ + if (aliasAddr.s_addr == INADDR_NONE) { sin = NULL; while (next < lim) { ifam = (struct ifa_msghdr *)next; @@ -826,8 +1322,21 @@ SetAliasAddressFromIfName(const char *ifn) 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); } @@ -888,10 +1397,15 @@ enum Option { RedirectAddress, ConfigFile, DynamicMode, + ClampMSS, ProxyRule, LogDenied, LogFacility, - PunchFW + PunchFW, +#ifdef NATPORTMAP + NATPortMap, + ToInterfaceName +#endif }; enum Param { @@ -997,6 +1511,14 @@ static struct OptionInfo optionTable[] = { "dynamic", NULL }, + { ClampMSS, + 0, + YesNo, + "[yes|no]", + "enable TCP MSS clamping", + "clamp_mss", + NULL }, + { InPort, 0, Service, @@ -1109,7 +1631,26 @@ static struct OptionInfo optionTable[] = { "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) @@ -1219,6 +1760,10 @@ static void ParseOption (const char* option, const char* parms) dynamicMode = yesNoValue; break; + case ClampMSS: + clampMSS = yesNoValue; + break; + case InPort: inPort = uNumValue; break; @@ -1293,6 +1838,37 @@ static void ParseOption (const char* option, const char* parms) 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 + } }