--- gvpe/src/vpn.C 2003/04/02 03:25:17 1.1 +++ gvpe/src/vpn.C 2003/04/02 21:02:25 1.3 @@ -40,9 +40,16 @@ #include "util.h" #include "vpn.h" +#if ENABLE_TCP +# include +# include +# include +# include +#endif + ///////////////////////////////////////////////////////////////////////////// -const char *vpn::script_if_up (int) +const char *vpn::script_if_up () { // the tunnel device mtu should be the physical mtu - overhead // the tricky part is rounding to the cipher key blocksize @@ -73,10 +80,6 @@ int vpn::setup () { - sockinfo si; - - si.set (THISNODE); - udpv4_fd = -1; if (THISNODE->protocols & PROT_UDPv4) @@ -86,9 +89,17 @@ if (udpv4_fd < 0) return -1; + // standard daemon practise... + { + int oval = 1; + setsockopt (udpv4_fd, SOL_SOCKET, SO_REUSEADDR, &oval, sizeof oval); + } + + sockinfo si (THISNODE, PROT_UDPv4); + if (bind (udpv4_fd, si.sav4 (), si.salenv4 ())) { - slog (L_ERR, _("can't bind udpv4 to %s: %s"), (const char *)si, strerror (errno)); + slog (L_ERR, _("can't bind udpv4 on %s: %s"), (const char *)si, strerror (errno)); exit (1); } @@ -102,12 +113,6 @@ } #endif - // standard daemon practise... - { - int oval = 1; - setsockopt (udpv4_fd, SOL_SOCKET, SO_REUSEADDR, &oval, sizeof oval); - } - udpv4_ev_watcher.start (udpv4_fd, POLLIN); } @@ -119,9 +124,11 @@ if (ipv4_fd < 0) return -1; + sockinfo si (THISNODE, PROT_IPv4); + if (bind (ipv4_fd, si.sav4 (), si.salenv4 ())) { - slog (L_ERR, _("can't bind ipv4 socket to %s: %s"), (const char *)si, strerror (errno)); + slog (L_ERR, _("can't bind ipv4 socket on %s: %s"), (const char *)si, strerror (errno)); exit (1); } @@ -138,6 +145,38 @@ ipv4_ev_watcher.start (ipv4_fd, POLLIN); } +#if ENABLE_TCP + if (THISNODE->protocols & PROT_TCPv4) + { + tcpv4_fd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (tcpv4_fd < 0) + return -1; + + // standard daemon practise... + { + int oval = 1; + setsockopt (tcpv4_fd, SOL_SOCKET, SO_REUSEADDR, &oval, sizeof oval); + } + + sockinfo si (THISNODE, PROT_TCPv4); + + if (bind (tcpv4_fd, si.sav4 (), si.salenv4 ())) + { + slog (L_ERR, _("can't bind tcpv4 on %s: %s"), (const char *)si, strerror (errno)); + exit (1); + } + + if (listen (tcpv4_fd, 5)) + { + slog (L_ERR, _("can't listen tcpv4 on %s: %s"), (const char *)si, strerror (errno)); + exit (1); + } + + tcpv4_ev_watcher.start (tcpv4_fd, POLLIN); + } +#endif + tap = new tap_device (); if (!tap) //D this, of course, never catches { @@ -202,7 +241,7 @@ } void -vpn::udpv4_ev (short revents) +vpn::udpv4_ev (io_watcher &w, short revents) { if (revents & (POLLIN | POLLERR)) { @@ -211,7 +250,7 @@ socklen_t sa_len = sizeof (sa); int len; - len = recvfrom (udpv4_fd, &((*pkt)[0]), MAXSIZE, 0, (sockaddr *)&sa, &sa_len); + len = recvfrom (w.fd, &((*pkt)[0]), MAXSIZE, 0, (sockaddr *)&sa, &sa_len); sockinfo si(sa); @@ -245,7 +284,7 @@ } void -vpn::ipv4_ev (short revents) +vpn::ipv4_ev (io_watcher &w, short revents) { if (revents & (POLLIN | POLLERR)) { @@ -254,7 +293,7 @@ socklen_t sa_len = sizeof (sa); int len; - len = recvfrom (ipv4_fd, &((*pkt)[0]), MAXSIZE, 0, (sockaddr *)&sa, &sa_len); + len = recvfrom (w.fd, &((*pkt)[0]), MAXSIZE, 0, (sockaddr *)&sa, &sa_len); sockinfo si(sa, PROT_IPv4); @@ -291,8 +330,178 @@ } } +#if ENABLE_TCP + +struct tcp_connection; + +struct lt_sockinfo +{ + bool operator()(const sockinfo *a, const sockinfo *b) const + { + return *a < *b; + } +}; + +struct tcp_si_map : public map { + void cleaner_cb (time_watcher &w); time_watcher cleaner; + + tcp_si_map () + : cleaner(this, &tcp_si_map::cleaner_cb) + { + cleaner.start (0); + } +} tcp_si; + +struct tcp_connection : io_watcher { + tstamp last_activity; + const sockinfo si; + vpn &v; + bool ok; + + void tcpv4_ev (io_watcher &w, short revents); + + operator tcp_si_map::value_type() + { + return tcp_si_map::value_type (&si, this); + } + + tcp_connection (int fd_, const sockinfo &si_, vpn &v_) + : v(v_), si(si_), io_watcher(this, &tcp_connection::tcpv4_ev), ok(false) + { + last_activity = NOW; + start (fd_, POLLOUT); + } + + ~tcp_connection () { close (fd); } +}; + +void tcp_si_map::cleaner_cb (time_watcher &w) +{ + w.at = NOW + 600; + tstamp to = NOW - ::conf.keepalive - 30; + + for (iterator i = begin (); i != end(); ) + if (i->second->last_activity >= to) + ++i; + else + { + erase (i); + i = begin (); + } +} + void -vpn::tap_ev (short revents) +vpn::send_tcpv4_packet (vpn_packet *pkt, const sockinfo &si, int tos) +{ + tcp_si_map::iterator info = tcp_si.find (&si); + + if (info == tcp_si.end ()) + { + // woaw, the first lost packet ;) + int fd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (fd >= 0) + { + fcntl (fd, F_SETFL, O_NONBLOCK); + + if (connect (fd, si.sav4 (), si.salenv4 ()) >= 0 + || errno == EINPROGRESS) + { + tcp_connection *i = new tcp_connection (fd, si, *this); + + tcp_si.insert (*i); + } + else + close (fd); + } + } + else + { + tcp_connection *i = info->second; + + i->last_activity = NOW; + + if (i->ok) + { + setsockopt (i->fd, SOL_IP, IP_TOS, &tos, sizeof tos); + + // we use none of the advantages of tcp + write (i->fd, (void *)pkt, pkt->len + sizeof (u32)) != pkt->len + sizeof (u32); + } + } + +#if 0 + setsockopt (udpv4_fd, SOL_IP, IP_TOS, &tos, sizeof tos); + sendto (udpv4_fd, &((*pkt)[0]), pkt->len, 0, si.sav4 (), si.salenv4 ()); +#endif +} + +void +tcp_connection::tcpv4_ev (io_watcher &w, short revents) +{ + last_activity = NOW; + + if (!ok) // just established? + { + ok = true; + fcntl (fd, F_SETFL, 0); + stop (); + start (fd, POLLIN); + } + + if (revents & (POLLIN | POLLERR)) + { + u32 len; + + if (sizeof (len) == read (fd, &len, sizeof (len))) + { + vpn_packet *pkt = new vpn_packet; + + if (len == read (fd, &((*pkt)[0]), len)) + { + pkt->len = len; + + v.recv_vpn_packet (pkt, si); + return; + } + + delete pkt; + } + + tcp_si.erase (&si); + stop (); + } +} + +void +vpn::tcpv4_ev (io_watcher &w, short revents) +{ + if (revents & (POLLIN | POLLERR)) + { + struct sockaddr_in sa; + socklen_t sa_len = sizeof (sa); + int len; + + int fd = accept (w.fd, (sockaddr *)&sa, &sa_len); + + if (fd >= 0) + { + fcntl (fd, F_SETFL, O_NONBLOCK); + + sockinfo si(sa, PROT_TCPv4); + tcp_connection *i = new tcp_connection (fd, si, *this); + + slog (L_ERR, "accepted %d\n", fd);//D + + tcp_si.insert (*i); + } + } +} + +#endif + +void +vpn::tap_ev (io_watcher &w, short revents) { if (revents & POLLIN) { @@ -352,7 +561,7 @@ } void -vpn::event_cb (tstamp &ts) +vpn::event_cb (time_watcher &w) { if (events) { @@ -379,7 +588,7 @@ events = 0; } - ts = TSTAMP_CANCEL; + w.at = TSTAMP_CANCEL; } void @@ -470,10 +679,13 @@ } vpn::vpn (void) -: udpv4_ev_watcher(this, &vpn::udpv4_ev) -, ipv4_ev_watcher(this, &vpn::ipv4_ev) -, tap_ev_watcher(this, &vpn::tap_ev) -, event(this, &vpn::event_cb) +: event(this, &vpn::event_cb) +, udpv4_ev_watcher(this, &vpn::udpv4_ev) +, ipv4_ev_watcher (this, &vpn::ipv4_ev) +, tap_ev_watcher (this, &vpn::tap_ev) +#if ENABLE_TCP +, tcpv4_ev_watcher(this, &vpn::tcpv4_ev) +#endif { }