Ejemplo n.º 1
0
int udp_session(struct vtun_host *host)
{
    struct sockaddr_in saddr;
    short port;
    int s, opt;
    extern int is_rmt_fd_connected;

    if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
	vtun_syslog(LOG_ERR, "Can't create socket");
	return -1;
    }

    opt = 1;
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    /* Set local address and port */
    local_addr(&saddr, host, 1);
    if (bind(s, (struct sockaddr *) &saddr, sizeof(saddr))) {
	vtun_syslog(LOG_ERR, "Can't bind to the socket");
	return -1;
    }

    opt = sizeof(saddr);
    if (getsockname(s, (struct sockaddr *) &saddr, &opt)) {
	vtun_syslog(LOG_ERR, "Can't get socket name");
	return -1;
    }

    /* Write port of the new UDP socket */
    port = saddr.sin_port;
    if (write_n(host->rmt_fd, (char *) &port, sizeof(short)) < 0) {
	vtun_syslog(LOG_ERR, "Can't write port number");
	return -1;
    }
    host->sopt.lport = htons(port);

    /* Read port of the other's end UDP socket */
    if (readn_t(host->rmt_fd, &port, sizeof(short), host->timeout) < 0) {
	vtun_syslog(LOG_ERR, "Can't read port number %s", strerror(errno));
	return -1;
    }

    opt = sizeof(saddr);
    if (getpeername(host->rmt_fd, (struct sockaddr *) &saddr, &opt)) {
	vtun_syslog(LOG_ERR, "Can't get peer name");
	return -1;
    }

    saddr.sin_port = port;

    /* if the config says to delay the UDP connection, we wait for an
       incoming packet and then force a connection back.  We need to
       put this here because we need to keep that incoming triggering
       packet and pass it back up the chain. */

    if (VTUN_USE_NAT_HACK(host))
	is_rmt_fd_connected = 0;
    else {
	if (connect(s, (struct sockaddr *) &saddr, sizeof(saddr))) {
	    vtun_syslog(LOG_ERR, "Can't connect socket");
	    return -1;
	}
	is_rmt_fd_connected = 1;
    }

    host->sopt.rport = htons(port);

    /* Close TCP socket and replace with UDP socket */
    close(host->rmt_fd);
    host->rmt_fd = s;

    vtun_syslog(LOG_INFO, "UDP connection initialized");
    return s;
}
Ejemplo n.º 2
0
static int lfd_linker(void)
{
     int fd1 = lfd_host->rmt_fd;
     int fd2 = lfd_host->loc_fd; 
     register int len, fl;
     struct timeval tv;
     char *buf, *out;
     fd_set fdset;
     int maxfd, idle = 0, tmplen;

     if( !(buf = lfd_alloc(VTUN_FRAME_SIZE + VTUN_FRAME_OVERHEAD)) ){
	vtun_syslog(LOG_ERR,"Can't allocate buffer for the linker"); 
        return 0; 
     }
	
     /* Delay sending of first UDP packet over broken NAT routers
	because we will probably be disconnected.  Wait for the remote
	end to send us something first, and use that connection. */
     if (!VTUN_USE_NAT_HACK(lfd_host))
        proto_write(fd1, buf, VTUN_ECHO_REQ);

     maxfd = (fd1 > fd2 ? fd1 : fd2) + 1;

     linker_term = 0;
     while( !linker_term ){
	errno = 0;

        /* Wait for data */
        FD_ZERO(&fdset);
	FD_SET(fd1, &fdset);
	FD_SET(fd2, &fdset);

 	tv.tv_sec  = lfd_host->ka_interval;
	tv.tv_usec = 0;

	if( (len = select(maxfd, &fdset, NULL, NULL, &tv)) < 0 ){
	   if( errno != EAGAIN && errno != EINTR )
	      break;
	   else
	      continue;
	} 

	if( ka_need_verify ){
	  if( idle > lfd_host->ka_maxfail ){
	    vtun_syslog(LOG_INFO,"Session %s network timeout", lfd_host->host);
	    break;
	  }
	  if (idle++ > 0) {  /* No input frames, check connection with ECHO */
	    if( proto_write(fd1, buf, VTUN_ECHO_REQ) < 0 ){
	      vtun_syslog(LOG_ERR,"Failed to send ECHO_REQ");
	      break;
	    }
	  }
	  ka_need_verify = 0;
	}

	if (send_a_packet)
        {
           send_a_packet = 0;
           tmplen = 1;
	   lfd_host->stat.byte_out += tmplen; 
	   if( (tmplen=lfd_run_down(tmplen,buf,&out)) == -1 )
	      break;
	   if( tmplen && proto_write(fd1, out, tmplen) < 0 )
	      break;
	   lfd_host->stat.comp_out += tmplen; 
        }

	/* Read frames from network(fd1), decode and pass them to 
         * the local device (fd2) */
	if( FD_ISSET(fd1, &fdset) && lfd_check_up() ){
	   idle = 0;  ka_need_verify = 0;
	   if( (len=proto_read(fd1, buf)) <= 0 )
	      break;

	   /* Handle frame flags */
	   fl = len & ~VTUN_FSIZE_MASK;
           len = len & VTUN_FSIZE_MASK;
	   if( fl ){
	      if( fl==VTUN_BAD_FRAME ){
		 vtun_syslog(LOG_ERR, "Received bad frame");
		 continue;
	      }
	      if( fl==VTUN_ECHO_REQ ){
		 /* Send ECHO reply */
	 	 if( proto_write(fd1, buf, VTUN_ECHO_REP) < 0 )
		    break;
		 continue;
	      }
   	      if( fl==VTUN_ECHO_REP ){
		 /* Just ignore ECHO reply, ka_need_verify==0 already */
		 continue;
	      }
	      if( fl==VTUN_CONN_CLOSE ){
	         vtun_syslog(LOG_INFO,"Connection closed by other side");
		 break;
	      }
	   }   

	   lfd_host->stat.comp_in += len; 
	   if( (len=lfd_run_up(len,buf,&out)) == -1 )
	      break;	
	   if( len && dev_write(fd2,out,len) < 0 ){
              if( errno != EAGAIN && errno != EINTR )
                 break;
              else
                 continue;
           }
	   lfd_host->stat.byte_in += len; 
	}

	/* Read data from the local device(fd2), encode and pass it to 
         * the network (fd1) */
	if( FD_ISSET(fd2, &fdset) && lfd_check_down() ){
	   if( (len = dev_read(fd2, buf, VTUN_FRAME_SIZE)) < 0 ){
	      if( errno != EAGAIN && errno != EINTR )
	         break;
	      else
		 continue;
	   }
	   if( !len ) break;
	
	   lfd_host->stat.byte_out += len; 
	   if( (len=lfd_run_down(len,buf,&out)) == -1 )
	      break;
	   if( len && proto_write(fd1, out, len) < 0 )
	      break;
	   lfd_host->stat.comp_out += len; 
	}
     }
     if( !linker_term && errno )
	vtun_syslog(LOG_INFO,"%s (%d)", strerror(errno), errno);

     if (linker_term == VTUN_SIG_TERM) {
       lfd_host->persist = 0;
     }

     /* Notify other end about our close */
     proto_write(fd1, buf, VTUN_CONN_CLOSE);
     lfd_free(buf);

     return 0;
}