vtun/tunnel.c
mtbishop f3c3e13798 Merge changes from Andrey Mazo:
Clean up Configure.in for autoheader
        Static declarations where sensible
        Check/handle no fork() for no-MMU client systems (eg blackfin)
2013-07-07 19:54:20 +00:00

259 lines
6.1 KiB
C

/*
VTun - Virtual Tunnel over TCP/IP network.
Copyright (C) 1998-2008 Maxim Krasnyansky <max_mk@yahoo.com>
VTun has been derived from VPPP package by Maxim Krasnyansky.
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.
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.
*/
/*
* $Id: tunnel.c,v 1.14.2.3 2013/07/07 19:55:17 mtbishop Exp $
*/
#include "config.h"
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <syslog.h>
#include <signal.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_IN_SYSTM_H
#include <netinet/in_systm.h>
#endif
#ifdef HAVE_NETINET_IP_H
#include <netinet/ip.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#include "vtun.h"
#include "linkfd.h"
#include "lib.h"
#include "netlib.h"
#include "driver.h"
int (*dev_write)(int fd, char *buf, int len);
int (*dev_read)(int fd, char *buf, int len);
int (*proto_write)(int fd, char *buf, int len);
int (*proto_read)(int fd, char *buf);
/* Initialize and start the tunnel.
Returns:
-1 - critical error
0 - normal close or noncritical error
*/
int tunnel(struct vtun_host *host)
{
int null_fd, pid, opt;
int fd[2]={-1, -1};
char dev[VTUN_DEV_LEN]="";
int interface_already_open = 0;
if ( (host->persist == VTUN_PERSIST_KEEPIF) &&
(host->loc_fd >= 0) )
interface_already_open = 1;
/* Initialize device. */
if( host->dev ){
strncpy(dev, host->dev, VTUN_DEV_LEN);
dev[VTUN_DEV_LEN-1]='\0';
}
if( ! interface_already_open ){
switch( host->flags & VTUN_TYPE_MASK ){
case VTUN_TTY:
if( (fd[0]=pty_open(dev)) < 0 ){
vtun_syslog(LOG_ERR,"Can't allocate pseudo tty. %s(%d)", strerror(errno), errno);
return -1;
}
break;
case VTUN_PIPE:
if( pipe_open(fd) < 0 ){
vtun_syslog(LOG_ERR,"Can't create pipe. %s(%d)", strerror(errno), errno);
return -1;
}
break;
case VTUN_ETHER:
if( (fd[0]=tap_open(dev)) < 0 ){
vtun_syslog(LOG_ERR,"Can't allocate tap device %s. %s(%d)", dev, strerror(errno), errno);
return -1;
}
break;
case VTUN_TUN:
if( (fd[0]=tun_open(dev)) < 0 ){
vtun_syslog(LOG_ERR,"Can't allocate tun device %s. %s(%d)", dev, strerror(errno), errno);
return -1;
}
break;
}
host->loc_fd = fd[0];
}
host->sopt.dev = strdup(dev);
/* Initialize protocol. */
switch( host->flags & VTUN_PROT_MASK ){
case VTUN_TCP:
opt=1;
setsockopt(host->rmt_fd,SOL_SOCKET,SO_KEEPALIVE,&opt,sizeof(opt) );
opt=1;
setsockopt(host->rmt_fd,IPPROTO_TCP,TCP_NODELAY,&opt,sizeof(opt) );
proto_write = tcp_write;
proto_read = tcp_read;
break;
case VTUN_UDP:
if( (opt = udp_session(host)) == -1){
vtun_syslog(LOG_ERR,"Can't establish UDP session");
close(fd[1]);
if( ! ( host->persist == VTUN_PERSIST_KEEPIF ) )
close(fd[0]);
return 0;
}
proto_write = udp_write;
proto_read = udp_read;
break;
}
#ifdef HAVE_WORKING_FORK
switch( (pid=fork()) ){
case -1:
vtun_syslog(LOG_ERR,"Couldn't fork()");
if( ! ( host->persist == VTUN_PERSIST_KEEPIF ) )
close(fd[0]);
close(fd[1]);
return 0;
case 0:
/* do this only the first time when in persist = keep mode */
if( ! interface_already_open ){
switch( host->flags & VTUN_TYPE_MASK ){
case VTUN_TTY:
/* Open pty slave (becomes controlling terminal) */
if( (fd[1] = open(dev, O_RDWR)) < 0){
vtun_syslog(LOG_ERR,"Couldn't open slave pty");
exit(0);
}
/* Fall through */
case VTUN_PIPE:
null_fd = open("/dev/null", O_RDWR);
close(fd[0]);
close(0); dup(fd[1]);
close(1); dup(fd[1]);
close(fd[1]);
/* Route stderr to /dev/null */
close(2); dup(null_fd);
close(null_fd);
break;
case VTUN_ETHER:
case VTUN_TUN:
break;
}
}
/* Run list of up commands */
set_title("%s running up commands", host->host);
llist_trav(&host->up, run_cmd, &host->sopt);
exit(0);
}
#else
vtun_syslog(LOG_ERR,"Couldn't run up commands: fork() not available");
#endif
switch( host->flags & VTUN_TYPE_MASK ){
case VTUN_TTY:
set_title("%s tty", host->host);
dev_read = pty_read;
dev_write = pty_write;
break;
case VTUN_PIPE:
/* Close second end of the pipe */
close(fd[1]);
set_title("%s pipe", host->host);
dev_read = pipe_read;
dev_write = pipe_write;
break;
case VTUN_ETHER:
set_title("%s ether %s", host->host, dev);
dev_read = tap_read;
dev_write = tap_write;
break;
case VTUN_TUN:
set_title("%s tun %s", host->host, dev);
dev_read = tun_read;
dev_write = tun_write;
break;
}
opt = linkfd(host);
#ifdef HAVE_WORKING_FORK
set_title("%s running down commands", host->host);
llist_trav(&host->down, run_cmd, &host->sopt);
#else
vtun_syslog(LOG_ERR,"Couldn't run down commands: fork() not available");
#endif
if(! ( host->persist == VTUN_PERSIST_KEEPIF ) ) {
set_title("%s closing", host->host);
/* Gracefully destroy interface */
switch( host->flags & VTUN_TYPE_MASK ){
case VTUN_TUN:
tun_close(fd[0], dev);
break;
case VTUN_ETHER:
tap_close(fd[0], dev);
break;
}
close(host->loc_fd);
}
/* Close all other fds */
close(host->rmt_fd);
close(fd[1]);
return opt;
}