ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/gvpe/src/vpn_tcp.C
Revision: 1.2
Committed: Sun Apr 6 18:12:18 2003 UTC (21 years, 1 month ago) by pcg
Content type: text/plain
Branch: MAIN
Changes since 1.1: +6 -4 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
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