/* gre-tun - create a GRE tunnel between two nodes as defined in RFCs 1701 and 1702. Stuff that's broken: [1] I only partly handle the header. I don't accept packets with routing information and the sequence numbering doesn't work properly on outbound packets. [2] For some reason the ip->dst in the packet dump code on inbound packets is generating the same as ip->src which is NOT what I want. [3] Need to have the local address = first up and running network interface IP address. [4] Haven't got the checksumming code working yet. [5] And, of course, other protocol support would be nice... Legal Info: GRE-TUN, a GRE Tunnelling software package for FreeBSD. Copyright (C) 1999 Michael C. Newell. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. If you add features or correct bugs please send updates to the author at "mnewell@spottydogs.org". All copies of this program or derivative works must include this copyright notice. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. The GNU General Public License text is available from the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. It may also be accessed online at "http://www.gnu.org/copyleft/gpl.es.html". */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Local defines */ #define TUNNEL_PROTOCOL 47 /* protocol for the tunnel */ #define PACKET_BUFFER_LEN 8192 /* buffer space size */ /* ARGV parameters */ char *ProgramName = NULL; /* where the program's name will be stored */ char *SrcName = NULL; /* tunnel source address */ char *DstName = NULL; /* tunnel destination address */ char *TgtName = NULL; /* tunnel target host */ char *LocName = NULL; /* tunnel local host */ char *TunName = NULL; /* name of the tunnel */ unsigned int NetMask = 0xfffffff8; /* network mask; default = point-to-point */ unsigned long KeyValue = 0; /* key to check */ int KeyFlag = 0; /* don't do key checking by default */ int DebugFlag = 0; /* Turn debugging on */ int SequenceFlag = 0; /* do sequencing */ int ChecksumFlag = 0; /* do checksumming */ /* Misc parameters */ int DetachedFlag = 0; /* did we do a detach? */ char *ProgramBaseName = NULL; /* name without file stuff */ /* tunnel work parameters */ int TunnelFD = -1; /* File descriptor for the tunnel device */ int NetworkFD = -1; /* File descriptor for network connection */ char *TunnelDeviceName = NULL; /* Actual path name of the tunnel device */ char *TunnelIFName = NULL; /* tunnel name without rest of the stuff */ struct sockaddr SA_Src; /* tunnel source socket address block */ struct sockaddr SA_Dst; /* tunnel destination socket address */ struct sockaddr SA_Local; /* local end address block */ struct sockaddr SA_Remote; /* remote end address block */ unsigned long ReceiveSequence = 0; /* Seq # of last received packet */ unsigned long SendSequence = 0; /* Seq # of last sent packet */ /* Prints log messages in the proper context. If we are detached they are sent to syslog; otherwise they are displayed on controlling terminal. */ #include #define MAX_LOG_MESSAGE 1024 void LogMessage(char *format, ...) { char buffer[MAX_LOG_MESSAGE]; va_list ap; va_start(ap,format); vsnprintf(buffer,MAX_LOG_MESSAGE,format,ap); va_end(ap); if (DetachedFlag) { /* do something with syslog */ } else { fputs(buffer,stderr); } } /* Turns us off */ void CloseTunnel(); void ShutDown(const int code) { if (DebugFlag) LogMessage("Info[Die]: Shutting down/n"); CloseTunnel(); exit(code); } /* Sets the address on a sockaddr_in structure. */ void SetAddress(char *name, struct sockaddr_in *sainp) { bzero((char *)sainp, sizeof(struct sockaddr_in)); sainp->sin_family = AF_INET; if ((sainp->sin_addr.s_addr = inet_addr(name)) == (u_long) -1) { struct hostent *hep = gethostbyname(name); if (!hep) { fprintf(stderr,"Error[SetAddress]: Host name lookup failure for '%s'\n", name); ShutDown(1); } sainp->sin_family = hep->h_addrtype; bcopy(hep->h_addr, (caddr_t)&sainp->sin_addr, hep->h_length); } } /* Routine to open the tunnel */ int OpenTunnel(char *tunname) { int tunfd; int sock; char *cp; char buffer[1024]; struct ifreq ifrb; unsigned int nm; /* Get the tunnel device; if none is supplied try to find one. */ if (tunname == NULL) { int ii; if ((TunnelDeviceName = (char *) malloc(16)) == NULL) { fprintf(stderr, "Error[OpenTunnel]: Cannot allocate device name buffer; %s\n", strerror(errno)); exit(1); } for (ii = 0; ii < 100; ++ii) { sprintf(TunnelDeviceName,"/dev/tun%d",ii); if (DebugFlag) fprintf(stderr,"Debug[OpenTunnel]: Trying tunnel '%s'\n", TunnelDeviceName); if ((tunfd = open(TunnelDeviceName,O_RDWR)) > 0) { if (DebugFlag) fprintf(stderr,"Info[OpenTunnel]: Using tunnel device '%s'\n", TunnelDeviceName); break; } else if (errno != EBUSY) { fprintf(stderr, "Error[OpenTunnel]: Cannot find an available tunnel device; %s\n", strerror(errno)); exit(1); } } } else { TunnelDeviceName = strdup(tunname); if (DebugFlag) fprintf(stderr,"Debug[OpenTunnel]: Opening the specified tunnel '%s'\n", TunnelDeviceName); if ((tunfd = open(TunnelDeviceName,O_RDWR)) < 0) { fprintf(stderr,"Error: cannot open device '%s'; %s\n", TunnelDeviceName,strerror(errno)); exit(1); } } if ((cp = strrchr(TunnelDeviceName,'/')) != NULL) TunnelIFName = strdup(++cp); else TunnelIFName = strdup(TunnelDeviceName); if (DebugFlag) fprintf(stderr,"Debug[OpenTunnel]: Open device '%s', interface '%s'\n", TunnelDeviceName,TunnelIFName); /* Grab a socket on which to communicate. */ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { fprintf(stderr,"Error[OpenTunnel]: Cannot allocate a socket; %s\n", strerror(errno)); ShutDown(1); } /* Clear the current interface IP address. */ bzero(&ifrb, sizeof(ifrb)); strncpy(ifrb.ifr_name,TunnelIFName,IFNAMSIZ); if (ioctl(sock,SIOCDIFADDR,&ifrb) < 0) { if (errno != EADDRNOTAVAIL) { fprintf(stderr,"Error[OpenTunnel]: Can't clear previous addresses; %s\n", strerror(errno)); ShutDown(1); } } /* Set the proper IP address */ bcopy(&SA_Src,&ifrb.ifr_addr,sizeof(SA_Src)); ((struct sockaddr_in *)&ifrb.ifr_addr)->sin_len = sizeof(struct sockaddr_in); if (ioctl(sock,SIOCSIFADDR,&ifrb) < 0) { fprintf(stderr,"Error[OpenTunnel]: Cannot set tunnel source address on '%s'; %s\n", ifrb.ifr_name,strerror(errno)); ShutDown(1); } /* Now the proper destination. Note that I can do the address setting with a single socket alias call, but I'm trying to be really generic here... */ bcopy(&SA_Dst,&ifrb.ifr_addr,sizeof(SA_Dst)); ((struct sockaddr_in *)&ifrb.ifr_addr)->sin_len = sizeof(struct sockaddr_in); if (ioctl(sock,SIOCSIFDSTADDR,&ifrb) < 0) { fprintf(stderr,"Error[OpenTunnel]: Cannot set tunnel destination address on '%s'; %s\n", ifrb.ifr_name,strerror(errno)); ShutDown(1); } /* Ok, now set the netmask. */ ifrb.ifr_addr.sa_family = AF_INET; nm = htonl(NetMask); bcopy(&nm,&((struct sockaddr_in *)&ifrb.ifr_addr)->sin_addr,sizeof(nm)); ((struct sockaddr_in *)&ifrb.ifr_addr)->sin_len = sizeof(nm); if (ioctl(sock,SIOCSIFNETMASK,&ifrb) < 0) { fprintf(stderr,"Error[OpenTunnel]: Cannot set netmask on tunnel '%s'; %s\nn", ifrb.ifr_name,strerror(errno)); ShutDown(1); } /* Now turn it on! */ if (ioctl(sock,SIOCGIFFLAGS, &ifrb) < 0) { fprintf(stderr,"Error[OpenTunnel]: cannot get flags for '%s'; %s\n", ifrb.ifr_name, strerror(errno)); ShutDown(1); } ifrb.ifr_flags |= IFF_UP; if (ioctl(sock,SIOCSIFFLAGS,&ifrb) < 0) { fprintf(stderr,"Error[OpenTunnel]: Cannot set '%s' to UP; %s\n", ifrb.ifr_name,strerror(errno)); ShutDown(1); } close(sock); return tunfd; } /* Closes the tunnel. Almost no error checking; I'm a lazy bastard and I want to get to the cool stuff... */ void CloseTunnel() { int sock; struct ifreq ifrb; if (DebugFlag) LogMessage("Info[CloseTunnel]: Closing tunnel device\n"); /* Grab a socket on which to communicate. */ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { LogMessage("Error[CloseTunnel]: Cannot allocate a socket; %s\n", strerror(errno)); exit(1); } /* try to clear the UP flag */ bzero((char *)&ifrb,sizeof(ifrb)); strncpy(ifrb.ifr_name, TunnelIFName,IFNAMSIZ); if (ioctl(sock,SIOCGIFFLAGS, &ifrb) < 0) { LogMessage("Warning[CloseTunnel]: Cannot read tunnel state flags; %s\n", strerror(errno)); } else { ifrb.ifr_flags &= ~(IFF_UP | IFF_RUNNING); if (ioctl(sock,SIOCSIFFLAGS,&ifrb) < 0) LogMessage("Warning[CloseTunnel]: cannot unset 'up' flag; %s\n", strerror(errno)); } /* Clear the IP addresses */ bzero(&ifrb.ifr_addr,sizeof(struct sockaddr)); if (ioctl(sock,SIOCSIFADDR,&ifrb) < 0) { fprintf(stderr,"Error[OpenTunnel]: Cannot clear tunnel source address on '%s'; %s\n", ifrb.ifr_name,strerror(errno)); ShutDown(1); } if (ioctl(sock,SIOCSIFDSTADDR,&ifrb) < 0) { fprintf(stderr,"Error[OpenTunnel]: Cannot clear tunnel destination address on '%s'; %s\n", ifrb.ifr_name,strerror(errno)); ShutDown(1); } #ifdef NOWAY /* Try to clear the IP addresses */ bzero((char *)&ifarb,sizeof(ifarb)); strncpy(ifarb.ifra_name,TunnelIFName,IFNAMSIZ); if (ioctl(sock,SIOCDIFADDR,&ifarb) < 0) { if (errno != EADDRNOTAVAIL) fprintf(stderr, "Warning[CloseTunnel]: Cannot clear previous addresses; %s\n", strerror(errno)); } #endif closelog(); close(TunnelFD); close(sock); } /* Parses out a subnet mask; looks for hex (0x...), octal (0...), and dotted quads. */ unsigned int ParseNetMask(char *string) { char *cp; unsigned int mask=0; while (*string == ' ') string++; /* Dotted quad */ if ((cp = strchr(string,'.')) != NULL) { unsigned int jj = 0; while (-1) { unsigned int ii = 0; if (isdigit(*string)) { while (isdigit(*string)) ii = 10*ii + (*string++ - '0'); if (ii > 255) { fprintf(stderr,"Error: invalid octet in netmask\n"); exit(1); } mask = (mask << 8) + ii; } else if (*string == '.') { if (++jj > 3) { fprintf(stderr,"Error: too many octets in netmask\n"); exit(1); } ++string; if (!isdigit(*string)) { fprintf(stderr,"Error: syntax error in netmask near '%s'\n",string); exit(1); } } else if (*string == '\0') { if (jj < 3) { fprintf(stderr,"Error: too few octets in netmask\n"); exit(1); } return mask; } else { fprintf(stderr,"Error: invalid netmask\n"); exit(1); } } } /* Hexadecmial */ if (!strncmp(string,"0x",2)) { char *hexdigs = "0123456789abcdef"; string += 2; while ((cp = strchr(hexdigs,*string)) != NULL) { mask = (mask << 4) + (unsigned int) (cp - hexdigs); if (*++string == '\0') break;; } if (*string != '\0') { fprintf(stderr,"Error: invalid hex digit at '%s' in netmask\n",string); exit(1); } return mask; } /* Octal */ if (*string == '0') { while (isdigit(*string)) { unsigned int ii = *string++ - '0'; if (ii > 7) { fprintf(stderr,"Error: invalid octal digit at '%s' in netmask\n",string); exit(1); } mask = (8*mask) + ii; } if (*string != '\0') { fprintf(stderr,"Error: invalid digit at '%s' in netmask\n",string); exit(1); } return mask; } fprintf(stderr,"Error: invalid subnet mask '%s'\n",string); exit(1); } /* Parses off an unsigned long value */ unsigned long ParseUL(char *string) { unsigned long ul = 0; char *cp; /* Hexadecmial */ if (!strncmp(string,"0x",2)) { char *hexdigs = "0123456789abcdef"; string += 2; while ((cp = strchr(hexdigs,*string)) != NULL) { ul = (ul << 4) + (unsigned int) (cp - hexdigs); if (*++string == '\0') break;; } if (*string != '\0') { fprintf(stderr,"Error: invalid hex digit at '%s'\n",string); exit(1); } return ul; } /* Octal */ if (*string == '0') { while (isdigit(*string)) { unsigned int ii = *string++ - '0'; if (ii > 7) { fprintf(stderr,"Error: invalid octal digit at '%s' in netmask\n",string); exit(1); } ul = (8*ul) + ii; } if (*string != '\0') { fprintf(stderr,"Error: invalid digit at '%s'\n",string); exit(1); } return ul; } /* Decimal */ while (isdigit(*string)) ul = 10*ul + (*string++ - '0'); if (*string != '\0') { fprintf(stderr,"Error: invalid digit at '%s'\n",string); exit(1); } return ul; } /* Parses off the argument list */ void ParseArgs(char **argv) { char *cp; ProgramName = *argv; ProgramBaseName = ((cp = strrchr(ProgramName,'/')) == NULL) ? ProgramName : ++cp; while (*++argv) { int argl = strlen(*argv); int arglmin = (argl > 3) ? argl : 3; if (!strcmp(*argv,"-debug")) { ++DebugFlag; } else if (!strncmp(*argv,"-source",arglmin)) { if ((SrcName = *++argv) == NULL) { fprintf(stderr,"Error: missing source address\n"); exit(1); } } else if (!strncmp(*argv,"-destination",arglmin)) { if ((DstName = *++argv) == NULL) { fprintf(stderr,"Error: missing destination address\n"); exit(1); } } else if (!strncmp(*argv,"-remote",arglmin)) { if ((TgtName = *++argv) == NULL) { fprintf(stderr,"Error: missing target address\n"); exit(1); } } else if (!strncmp(*argv,"-local",arglmin)) { if ((LocName = *++argv) == NULL) { fprintf(stderr,"Error: missing local host address\n"); exit(1); } } else if (!strncmp(*argv,"-tunnel",arglmin)) { if ((TunName = *++argv) == NULL) { fprintf(stderr,"Error: missing tunnel device name\n"); exit(1); } } else if (!strncmp(*argv,"-netmask",arglmin)) { char *cp = *++argv; if (cp == NULL) { fprintf(stderr,"Error: missing netmask\n"); exit(1); } } else if (!strncmp(*argv,"-key",arglmin)) { char *cp = *++argv; if (cp == NULL) { fprintf(stderr,"Error: missing key value\n"); exit(1); } KeyValue = ParseUL(cp); KeyFlag = -1; } else if (!strncmp(*argv,"-sequence",arglmin)) { SequenceFlag = -1; } else { if (strcmp(*argv,"-help")) fprintf(stderr,"Error: unknown flag '%s'\n",*argv); fprintf(stderr,"Usage: %s {options}\n",ProgramName); fputs(" options:\n",stderr); fputs("\t-source\t\t\tset tunnel source address\n",stderr); fputs("\t-destination\t\tset tunnel destination address\n", stderr); fputs("\t-remote\t\t\tset address of remote tunnel host\n", stderr); fputs("\t-local\t\t\tset address of local tunnel host\n", stderr); fputs("\t-tunnel\t\t\tset tunnel device name\n",stderr); fputs("\t-netmask\t\t\tset tunnel network mask\n",stderr); fputs("\t-key\t\t\t\tset the tunnel key\n",stderr); fputs("\t-sequence\t\t\tturn on packet sequencing\n",stderr); fputs("\t-checksum\t\t\tturn on packet checksumming\n",stderr); fputs("\t-debug\t\t\t\tturn on debugging\n",stderr); exit(1); } } /* Make sure adequate parameters are set */ if (SrcName == NULL) SrcName = LocName; if (DstName == NULL) DstName = TgtName; if (LocName == NULL) { fprintf(stderr,"Error: local address not specified\n"); exit(1); } if (TgtName == NULL) { fprintf(stderr,"Error: target address not specified\n"); exit(1); } if (DebugFlag) { fprintf(stderr,"Debugging = ON\n"); fprintf(stderr,"Source address = %s\n",SrcName); fprintf(stderr,"Destination address = %s\n",DstName); fprintf(stderr,"Local address = %s\n",LocName); fprintf(stderr,"Remote address = %s\n",TgtName); fprintf(stderr,"Tunnel device = %s\n",TunName); fprintf(stderr,"Subnet mask = 0x%08x\n",NetMask); fprintf(stderr,"Key = 0x%08x (%s)\n",KeyValue,(KeyFlag)?"set":"not set"); fprintf(stderr,"Sequence numbering = %s\n",(SequenceFlag)?"Yes":"No"); } } /* Dumps a packet to the log area. I should take the time to do this right and decode the packet, but I'm really lazy... */ void DumpPacket(char *buffer, int len) { char hexline[1024]; char ascline[1024]; char *bp=buffer, *hp=hexline, *ap=ascline; char *hexdigits="0123456789abcdef"; char *fmt = " %s %s\n"; int ii=0; while (len--) { int ch = (0x000000ff) & (int) *buffer++; ++ii; *hp++ = hexdigits[0x0f & (ch >> 4)]; *hp++ = hexdigits[0x0f & ch]; *ap++ = ((ch >= ' ') && (ch <= '~')) ? ch : '.'; if (ii == 16) { *ap = '\0'; *hp= '\0'; LogMessage(fmt,hexline,ascline); hp = hexline; ap = ascline; ii = 0; } } if (ii) { while (ii < 16) { *ap++ = ' '; *hp ++ = ' '; *hp ++ = ' '; ++ii; } *ap = '\0'; *hp= '\0'; LogMessage(fmt,hexline,ascline); } } #define GRE_FLAG_CHECKSUM 0x8000 #define GRE_FLAG_ROUTING 0x4000 #define GRE_FLAG_KEY 0x2000 #define GRE_FLAG_SEQUENCE 0x1000 #define GRE_FLAG_SRCRTE 0x0800 #define GRE_MASK_RECUR 0x0700 #define GRE_MASK_FLAGS 0x00F8 #define GRE_MASK_VERSION 0x0007 /* Processes a packet received over the GRE tunnel */ ProcessGREPacket(struct ip *ipp, int len) { int iphl = ipp->ip_hl << 2; char *pp = (char *)ipp + iphl; unsigned short flagsver; unsigned short protocol; unsigned short checksum; unsigned short offset = 0; unsigned long key; unsigned long sequence; unsigned long routing; len -= iphl; /* First off get the flags and protocol which we know are there */ flagsver = ntohs(*((unsigned short *)pp)++); len -= sizeof(unsigned short); protocol = ntohs(*((unsigned short *)pp)++); len -= sizeof(unsigned short); if (DebugFlag) LogMessage("Debug[ProcessGREPacket]: flags = 0x%04x, protocol = 0x%04x\n", flagsver, protocol); if (flagsver & (GRE_FLAG_CHECKSUM | GRE_FLAG_ROUTING)) { checksum = ntohs(*((unsigned short *)pp)++); len -= sizeof(unsigned short); offset = ntohs(*((unsigned short *)pp)++); len -= sizeof(unsigned short); if (DebugFlag) LogMessage("Debug[ProcessGREPacket]: Checksum = 0x%04x, offset = 0x%04x\n", checksum, offset); } if (flagsver & GRE_FLAG_KEY) { key = ntohl(*((unsigned long *)pp)++); len -= sizeof(unsigned long); if (DebugFlag) LogMessage("Debug[ProcessGREPacket]: Key = 0x%08x\n",key); if (KeyFlag) { if (key != KeyValue) { LogMessage("Warning[ProcessGREPacket]: Received key = 0x%08x, expecting 0x%08x\n", key,KeyValue); return; } } else { LogMessage("Warning[ProcessGREPacket]: Received key = 0x%08x but keys not expected\n", key,KeyValue); } } else if (KeyFlag) { LogMessage("Warning[ProcessGREPacket]: Expecting keys but none supplied\n"); return; } if (flagsver & GRE_FLAG_SEQUENCE) { sequence = ntohl(*((unsigned long *)pp)++); len -= sizeof(unsigned long); if (DebugFlag) LogMessage("Debug[ProcessGREPacket]: Sequence # = 0x%08x\n",sequence); if (++ReceiveSequence != sequence) { LogMessage("Warning[ProcessGREPacket]: Received sequence = %d, expecting %d\n", sequence,ReceiveSequence); ReceiveSequence = sequence; } } if (flagsver & GRE_FLAG_ROUTING) { LogMessage("Warning[ProcessGREPacket]: Routing flag set; not supported\n"); return; } if (flagsver & GRE_FLAG_SRCRTE) { LogMessage("Warning[ProcessGREPacket]: Source routing flag set; not supported\n"); return; } if (flagsver & GRE_MASK_RECUR) { LogMessage("Warning[ProcessGREPacket]: Recursion control set; not supported\n"); return; } if (flagsver & GRE_MASK_VERSION) { LogMessage("Warning[ProcessGREPacket]: Version set to %d, should be zero\n", flagsver & GRE_MASK_VERSION); } ipp = (struct ip *)pp; if (DebugFlag) { LogMessage("Debug[main]: Raw IP packet from %s to %s len %d: \n", inet_ntoa(ipp->ip_src), inet_ntoa(ipp->ip_dst),len); DumpPacket((char *)ipp,len); } if (write(TunnelFD,(char *)ipp,len) <= 0) LogMessage("Warning[main]: Failed to write packet to tunnel; %s\n", strerror(errno)); return; } /* Processes a packet heading outbound. This is probably less effient than it needs to be, but... We will NOT generate packets with routing information. */ ProcessOutboundPacket(struct ip *ipp, int len) { char buffer[PACKET_BUFFER_LEN]; int bufflen = 4; unsigned short *flags = (unsigned short *)&buffer; unsigned short *protocol = (unsigned short *)&buffer[2]; char *buffp = &buffer[4]; unsigned short *checksum = NULL; unsigned short *offset = NULL; unsigned long *seqnum = NULL; unsigned long *key = NULL; if (DebugFlag) { LogMessage("Received packet from tunnel to '%s' length = %d:\n",inet_ntoa(ipp->ip_dst),len); DumpPacket((char *)ipp,len); } /* Set the appropriate flags and protocol in the header */ *flags = 0; if (ChecksumFlag) { *flags |= GRE_FLAG_CHECKSUM; checksum = ((unsigned short *)buffp)++; bufflen += sizeof(unsigned short); offset = ((unsigned short *)buffp)++; bufflen += sizeof(unsigned short); *checksum = 0; *offset = 0; } if (KeyFlag) { *flags |= GRE_FLAG_KEY; key = ((unsigned long *)buffp)++; bufflen += sizeof(unsigned long); *key = htonl(KeyValue); } if (SequenceFlag) { *flags |= GRE_FLAG_SEQUENCE; seqnum = ((unsigned long *)buffp)++; bufflen += sizeof(unsigned long); *seqnum = ++SendSequence; } *flags = htons(*flags); *protocol = htons(0x0800); /* Copy the packet into the buffer and send it. */ if ((bufflen + len) > PACKET_BUFFER_LEN) { LogMessage("Error[ProcessOutboundPacket]: buffer overflow; length = %d\n",len); return; } bcopy(ipp,buffp,len); bufflen += len; if (DebugFlag) { LogMessage("Debug[ProcessOutboundPacket]: Packet length = %d\n",bufflen); DumpPacket(buffer,bufflen); } if (send(NetworkFD,buffer,bufflen,0) <= 0) LogMessage("Warning[main]: Can't send packet to remote; %s\n", strerror(errno)); } /* MAIN program! */ main(int argc, char **argv) { fd_set readfds, writefds, exceptfds; int nfd; int maxfd; char pbuffer[PACKET_BUFFER_LEN]; /* Parse the argument list */ ParseArgs(argv); /* Set up the socket address blocks so we know that they are valid, then open the tunnel. */ SetAddress(SrcName,(struct sockaddr_in *)&SA_Src); SetAddress(DstName,(struct sockaddr_in *)&SA_Dst); SetAddress(LocName,(struct sockaddr_in *)&SA_Local); SetAddress(TgtName,(struct sockaddr_in *)&SA_Remote); TunnelFD = OpenTunnel(TunName); /* Open the syslog */ openlog(ProgramBaseName,LOG_PID, LOG_DAEMON); /* Open a connection to the remote host */ if ((NetworkFD = socket(AF_INET, SOCK_RAW, TUNNEL_PROTOCOL)) < 0) { LogMessage("Error[main]: Cannot establish socket to remote host; %s\n", strerror(errno)); ShutDown(1); } if (connect(NetworkFD,&SA_Remote,sizeof(struct sockaddr_in)) < 0) { LogMessage("Error[main]: Cannot connect remote stream to socket; %s\n", strerror(errno)); ShutDown(1); } /* If we are not debugging now would be a good time to detach. */ if (!DebugFlag) { if (daemon(0,0) < 0) { fprintf(stderr,"Error[main]: Cannot daemonize; %s\n",strerror(errno)); ShutDown(1); } } /* Install signal handlers */ signal(SIGHUP,ShutDown); signal(SIGTERM,ShutDown); /* Now do the process loop */ maxfd = (TunnelFD > NetworkFD) ? TunnelFD : NetworkFD; while (-1) { /* Set up for the select() */ FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); FD_SET(TunnelFD,&readfds); FD_SET(NetworkFD,&readfds); if ((nfd = select(maxfd+10,&readfds,&writefds,&exceptfds,NULL)) < 0) { LogMessage("Error[main]: Main select was interrupted\n"); ShutDown(1); } else if (nfd == 0) { LogMessage("Error[main]: Main select returned zero!\n"); ShutDown(1); } if (DebugFlag) LogMessage("Debug[main]: Select returned %d\n",nfd); /* Check for socket packet; note that it's gotta come from our partner or we will reject it. */ if (FD_ISSET(NetworkFD,&readfds)) { struct ip *ipp = (struct ip *)pbuffer; struct sockaddr_in *sainp = (struct sockaddr_in *) &SA_Remote; int len = read(NetworkFD,pbuffer,sizeof(pbuffer)); if (DebugFlag) { LogMessage("Received packet from socket from %s length = %d:\n", inet_ntoa(ipp->ip_src),len); DumpPacket(pbuffer,len); } /* If the packet came from our partner deal with it; else don't */ if (ipp->ip_src.s_addr == sainp->sin_addr.s_addr) { ProcessGREPacket(ipp,len); } else { /* Packet is not from our partner; ignore it */ LogMessage("Warning[main]: Received packet from '%s'\n", inet_ntoa(ipp->ip_src)); } } /* Check for tunnel packet */ if (FD_ISSET(TunnelFD,&readfds)) { int len = read(TunnelFD,&pbuffer,sizeof(pbuffer)); struct ip *ipp = (struct ip *)&pbuffer; ProcessOutboundPacket(ipp,len); } } }