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; }
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; }