| 1 |
pcg |
1.1 |
/* |
| 2 |
|
|
vpn_tcp.C -- handle the tcp part of the protocol. |
| 3 |
|
|
|
| 4 |
|
|
This program is free software; you can redistribute it and/or modify |
| 5 |
|
|
it under the terms of the GNU General Public License as published by |
| 6 |
|
|
the Free Software Foundation; either version 2 of the License, or |
| 7 |
|
|
(at your option) any later version. |
| 8 |
|
|
|
| 9 |
|
|
This program is distributed in the hope that it will be useful, |
| 10 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 |
|
|
GNU General Public License for more details. |
| 13 |
|
|
|
| 14 |
|
|
You should have received a copy of the GNU General Public License |
| 15 |
|
|
along with this program; if not, write to the Free Software |
| 16 |
|
|
Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 17 |
|
|
*/ |
| 18 |
|
|
|
| 19 |
|
|
#include "config.h" |
| 20 |
|
|
|
| 21 |
|
|
#if ENABLE_TCP |
| 22 |
|
|
|
| 23 |
|
|
// tcp processing is extremely ugly, since the vpe protocol is simply |
| 24 |
|
|
// designed for unreliable datagram networks. tcp is implemented by |
| 25 |
|
|
// multiplexing packets over tcp. errors are completely ignored, as we |
| 26 |
|
|
// rely on the higher level protocol to time out and reconnect. |
| 27 |
|
|
|
| 28 |
|
|
#include <cstring> |
| 29 |
|
|
|
| 30 |
|
|
#include <sys/types.h> |
| 31 |
|
|
#include <sys/socket.h> |
| 32 |
|
|
#include <sys/poll.h> |
| 33 |
|
|
#include <sys/wait.h> |
| 34 |
|
|
#include <netinet/in.h> |
| 35 |
|
|
#include <sys/uio.h> |
| 36 |
|
|
#include <arpa/inet.h> |
| 37 |
|
|
#include <errno.h> |
| 38 |
|
|
#include <time.h> |
| 39 |
|
|
#include <unistd.h> |
| 40 |
|
|
|
| 41 |
|
|
#include <map> |
| 42 |
|
|
#include <unistd.h> |
| 43 |
|
|
#include <fcntl.h> |
| 44 |
|
|
#include <sys/poll.h> |
| 45 |
|
|
|
| 46 |
|
|
#include "vpn.h" |
| 47 |
|
|
|
| 48 |
|
|
struct tcp_connection; |
| 49 |
|
|
|
| 50 |
|
|
struct lt_sockinfo |
| 51 |
|
|
{ |
| 52 |
|
|
bool operator()(const sockinfo *a, const sockinfo *b) const |
| 53 |
|
|
{ |
| 54 |
|
|
return *a < *b; |
| 55 |
|
|
} |
| 56 |
|
|
}; |
| 57 |
|
|
|
| 58 |
|
|
struct tcp_si_map : public map<const sockinfo *, tcp_connection *, lt_sockinfo> { |
| 59 |
|
|
void cleaner_cb (time_watcher &w); time_watcher cleaner; |
| 60 |
|
|
|
| 61 |
|
|
tcp_si_map () |
| 62 |
|
|
: cleaner(this, &tcp_si_map::cleaner_cb) |
| 63 |
|
|
{ |
| 64 |
|
|
cleaner.start (0); |
| 65 |
|
|
} |
| 66 |
|
|
} tcp_si; |
| 67 |
|
|
|
| 68 |
|
|
struct tcp_connection : io_watcher { |
| 69 |
|
|
tstamp last_activity; |
| 70 |
|
|
const sockinfo si; |
| 71 |
|
|
vpn &v; |
| 72 |
|
|
bool active; // this connection has been actively established |
| 73 |
|
|
enum { ERROR, IDLE, CONNECTING, ESTABLISHED } state; |
| 74 |
|
|
|
| 75 |
|
|
vpn_packet *r_pkt; |
| 76 |
|
|
u32 r_len, r_ofs; |
| 77 |
|
|
|
| 78 |
|
|
void tcpv4_ev (io_watcher &w, short revents); |
| 79 |
|
|
|
| 80 |
pcg |
1.2 |
bool send_packet (vpn_packet *pkt, int tos); |
| 81 |
pcg |
1.1 |
|
| 82 |
|
|
void error (); // abort conenction && cleanup |
| 83 |
|
|
|
| 84 |
|
|
operator tcp_si_map::value_type() |
| 85 |
|
|
{ |
| 86 |
|
|
return tcp_si_map::value_type (&si, this); |
| 87 |
|
|
} |
| 88 |
|
|
|
| 89 |
|
|
tcp_connection (int fd_, const sockinfo &si_, vpn &v_); |
| 90 |
|
|
~tcp_connection (); |
| 91 |
|
|
}; |
| 92 |
|
|
|
| 93 |
|
|
void tcp_si_map::cleaner_cb (time_watcher &w) |
| 94 |
|
|
{ |
| 95 |
|
|
w.at = NOW + 600; |
| 96 |
|
|
tstamp to = NOW - ::conf.keepalive - 30 - 60; |
| 97 |
|
|
|
| 98 |
|
|
for (iterator i = begin (); i != end(); ) |
| 99 |
|
|
if (i->second->last_activity >= to) |
| 100 |
|
|
++i; |
| 101 |
|
|
else |
| 102 |
|
|
{ |
| 103 |
|
|
erase (i); |
| 104 |
|
|
i = begin (); |
| 105 |
|
|
} |
| 106 |
|
|
} |
| 107 |
|
|
|
| 108 |
|
|
void |
| 109 |
|
|
vpn::tcpv4_ev (io_watcher &w, short revents) |
| 110 |
|
|
{ |
| 111 |
|
|
if (revents & (POLLIN | POLLERR)) |
| 112 |
|
|
{ |
| 113 |
|
|
struct sockaddr_in sa; |
| 114 |
|
|
socklen_t sa_len = sizeof (sa); |
| 115 |
|
|
int len; |
| 116 |
|
|
|
| 117 |
|
|
int fd = accept (w.fd, (sockaddr *)&sa, &sa_len); |
| 118 |
|
|
|
| 119 |
|
|
if (fd >= 0) |
| 120 |
|
|
{ |
| 121 |
|
|
sockinfo si(sa, PROT_TCPv4); |
| 122 |
|
|
|
| 123 |
|
|
slog (L_DEBUG, _("%s: accepted tcp connection"), (const char *)si);//D |
| 124 |
|
|
|
| 125 |
|
|
fcntl (fd, F_SETFL, O_NONBLOCK); |
| 126 |
|
|
|
| 127 |
|
|
tcp_connection *i = new tcp_connection (fd, si, *this); |
| 128 |
|
|
tcp_si.insert (*i); |
| 129 |
|
|
} |
| 130 |
|
|
} |
| 131 |
|
|
} |
| 132 |
|
|
|
| 133 |
pcg |
1.2 |
bool |
| 134 |
pcg |
1.1 |
vpn::send_tcpv4_packet (vpn_packet *pkt, const sockinfo &si, int tos) |
| 135 |
|
|
{ |
| 136 |
|
|
tcp_si_map::iterator info = tcp_si.find (&si); |
| 137 |
|
|
|
| 138 |
|
|
tcp_connection *i; |
| 139 |
|
|
|
| 140 |
|
|
if (info == tcp_si.end ()) |
| 141 |
|
|
{ |
| 142 |
|
|
i = new tcp_connection (-1, si, *this); |
| 143 |
|
|
tcp_si.insert (*i); |
| 144 |
|
|
} |
| 145 |
|
|
else |
| 146 |
|
|
i = info->second; |
| 147 |
|
|
|
| 148 |
pcg |
1.2 |
return i->send_packet (pkt, tos); |
| 149 |
pcg |
1.1 |
} |
| 150 |
|
|
|
| 151 |
|
|
void tcp_connection::error () |
| 152 |
|
|
{ |
| 153 |
|
|
if (fd >= 0) |
| 154 |
|
|
{ |
| 155 |
|
|
close (fd); |
| 156 |
|
|
fd = -1; |
| 157 |
|
|
} |
| 158 |
|
|
|
| 159 |
|
|
delete r_pkt; |
| 160 |
|
|
r_pkt = 0; |
| 161 |
|
|
|
| 162 |
|
|
stop (); |
| 163 |
|
|
state = active ? IDLE : ERROR; |
| 164 |
|
|
} |
| 165 |
|
|
|
| 166 |
|
|
void |
| 167 |
|
|
tcp_connection::tcpv4_ev (io_watcher &w, short revents) |
| 168 |
|
|
{ |
| 169 |
|
|
last_activity = NOW; |
| 170 |
|
|
|
| 171 |
|
|
if (revents & (POLLERR | POLLHUP)) |
| 172 |
|
|
error (); |
| 173 |
|
|
else if (revents & POLLOUT && state == CONNECTING) |
| 174 |
|
|
{ |
| 175 |
|
|
state = ESTABLISHED; |
| 176 |
|
|
set (POLLIN); |
| 177 |
|
|
} |
| 178 |
|
|
|
| 179 |
|
|
else if (revents & POLLIN) |
| 180 |
|
|
{ |
| 181 |
|
|
for (;;) |
| 182 |
|
|
{ |
| 183 |
|
|
if (!r_pkt) |
| 184 |
|
|
{ |
| 185 |
|
|
r_pkt = new vpn_packet; |
| 186 |
|
|
r_ofs = 0; |
| 187 |
|
|
r_len = 2; // header |
| 188 |
|
|
} |
| 189 |
|
|
|
| 190 |
|
|
ssize_t len = read (fd, &((*r_pkt)[r_ofs < 2 ? r_ofs : r_ofs - 2]), r_len); |
| 191 |
|
|
|
| 192 |
|
|
if (len > 0) |
| 193 |
|
|
{ |
| 194 |
|
|
r_len -= len; |
| 195 |
|
|
r_ofs += len; |
| 196 |
|
|
|
| 197 |
|
|
if (r_len == 0) |
| 198 |
|
|
{ |
| 199 |
|
|
if (r_ofs == 2) |
| 200 |
|
|
{ |
| 201 |
|
|
r_len = ntohs (*(u16 *)&((*r_pkt)[0])); |
| 202 |
|
|
r_pkt->len = r_len; |
| 203 |
|
|
|
| 204 |
|
|
if (r_len > 0 && r_len < MAXSIZE) |
| 205 |
|
|
continue; |
| 206 |
|
|
} |
| 207 |
|
|
else |
| 208 |
|
|
{ |
| 209 |
|
|
v.recv_vpn_packet (r_pkt, si); |
| 210 |
|
|
delete r_pkt; |
| 211 |
|
|
r_pkt = 0; |
| 212 |
|
|
|
| 213 |
|
|
continue; |
| 214 |
|
|
} |
| 215 |
|
|
} |
| 216 |
|
|
|
| 217 |
|
|
} |
| 218 |
|
|
else if (len < 0 && (errno == EINTR || errno == EAGAIN)) |
| 219 |
|
|
return; |
| 220 |
|
|
|
| 221 |
|
|
error (); |
| 222 |
|
|
return; |
| 223 |
|
|
} |
| 224 |
|
|
} |
| 225 |
|
|
} |
| 226 |
|
|
|
| 227 |
pcg |
1.2 |
bool |
| 228 |
pcg |
1.1 |
tcp_connection::send_packet (vpn_packet *pkt, int tos) |
| 229 |
|
|
{ |
| 230 |
|
|
last_activity = NOW; |
| 231 |
|
|
|
| 232 |
|
|
if (state == IDLE) |
| 233 |
|
|
{ |
| 234 |
|
|
// woaw, the first lost packet ;) |
| 235 |
|
|
fd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); |
| 236 |
|
|
|
| 237 |
|
|
if (fd >= 0) |
| 238 |
|
|
{ |
| 239 |
|
|
fcntl (fd, F_SETFL, O_NONBLOCK); |
| 240 |
|
|
|
| 241 |
|
|
if (connect (fd, si.sav4 (), si.salenv4 ()) >= 0 |
| 242 |
|
|
|| errno == EINPROGRESS) |
| 243 |
|
|
{ |
| 244 |
|
|
state = CONNECTING; |
| 245 |
|
|
start (fd, POLLOUT); |
| 246 |
|
|
} |
| 247 |
|
|
else |
| 248 |
|
|
close (fd); |
| 249 |
|
|
} |
| 250 |
|
|
} |
| 251 |
|
|
else if (state == ESTABLISHED) |
| 252 |
|
|
{ |
| 253 |
|
|
// how this maps to the underlying tcp packet we don't know |
| 254 |
|
|
// and we don't care. at least we tried ;) |
| 255 |
|
|
setsockopt (fd, SOL_IP, IP_TOS, &tos, sizeof tos); |
| 256 |
|
|
|
| 257 |
|
|
// we use none of the advantages of tcp; if an error occurs, just drop |
| 258 |
|
|
// (this happens when a tcp connection gets stuck, too, which might not be |
| 259 |
|
|
// the wisest thing to do.. either drop packet (too late) or make sure |
| 260 |
|
|
// it gets delivered) |
| 261 |
|
|
u16 len = htons (pkt->len); |
| 262 |
|
|
|
| 263 |
|
|
iovec vec[2]; |
| 264 |
|
|
vec[0].iov_base = &len; |
| 265 |
|
|
vec[0].iov_len = sizeof len; |
| 266 |
|
|
vec[1].iov_base = &((*pkt)[0]); |
| 267 |
|
|
vec[1].iov_len = pkt->len; |
| 268 |
|
|
|
| 269 |
|
|
if (sizeof (u16) + pkt->len != writev (fd, vec, 2)) |
| 270 |
|
|
error (); |
| 271 |
|
|
} |
| 272 |
pcg |
1.2 |
|
| 273 |
|
|
return state != ERROR; |
| 274 |
pcg |
1.1 |
} |
| 275 |
|
|
|
| 276 |
|
|
tcp_connection::tcp_connection (int fd_, const sockinfo &si_, vpn &v_) |
| 277 |
|
|
: v(v_), si(si_), io_watcher(this, &tcp_connection::tcpv4_ev) |
| 278 |
|
|
{ |
| 279 |
|
|
last_activity = NOW; |
| 280 |
|
|
r_pkt = 0; |
| 281 |
|
|
fd = fd_; |
| 282 |
|
|
|
| 283 |
|
|
if (fd < 0) |
| 284 |
|
|
{ |
| 285 |
|
|
active = true; |
| 286 |
|
|
state = IDLE; |
| 287 |
|
|
} |
| 288 |
|
|
else |
| 289 |
|
|
{ |
| 290 |
|
|
active = false; |
| 291 |
|
|
state = ESTABLISHED; |
| 292 |
|
|
start (fd, POLLIN); |
| 293 |
|
|
} |
| 294 |
|
|
} |
| 295 |
|
|
|
| 296 |
|
|
tcp_connection::~tcp_connection () |
| 297 |
|
|
{ |
| 298 |
|
|
error (); |
| 299 |
|
|
} |
| 300 |
|
|
|
| 301 |
|
|
#endif |
| 302 |
|
|
|