ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/gvpe/src/vpn.C
Revision: 1.1
Committed: Wed Apr 2 03:25:17 2003 UTC (21 years, 1 month ago) by pcg
Content type: text/plain
Branch: MAIN
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 pcg 1.1 /*
2     vpn.C -- handle the protocol, encryption, handshaking etc.
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     #include <list>
22    
23     #include <cstdlib>
24     #include <cstring>
25     #include <cstdio>
26    
27     #include <sys/types.h>
28     #include <sys/socket.h>
29     #include <sys/poll.h>
30     #include <sys/wait.h>
31     #include <netinet/in.h>
32     #include <arpa/inet.h>
33     #include <errno.h>
34     #include <time.h>
35     #include <unistd.h>
36    
37     #include "pidfile.h"
38    
39     #include "connection.h"
40     #include "util.h"
41     #include "vpn.h"
42    
43     /////////////////////////////////////////////////////////////////////////////
44    
45     const char *vpn::script_if_up (int)
46     {
47     // the tunnel device mtu should be the physical mtu - overhead
48     // the tricky part is rounding to the cipher key blocksize
49     int mtu = conf.mtu - ETH_OVERHEAD - VPE_OVERHEAD - MAX_OVERHEAD;
50     mtu += ETH_OVERHEAD - 6 - 6; // now we have the data portion
51     mtu -= mtu % EVP_CIPHER_block_size (CIPHER); // round
52     mtu -= ETH_OVERHEAD - 6 - 6; // and get interface mtu again
53    
54     char *env;
55     asprintf (&env, "CONFBASE=%s", confbase);
56     putenv (env);
57     asprintf (&env, "NODENAME=%s", THISNODE->nodename);
58     putenv (env);
59     asprintf (&env, "NODEID=%d", THISNODE->id);
60     putenv (env);
61     asprintf (&env, "IFNAME=%s", tap->interface ());
62     putenv (env);
63     asprintf (&env, "MTU=%d", mtu);
64     putenv (env);
65     asprintf (&env, "MAC=%02x:%02x:%02x:%02x:%02x:%02x",
66     0xfe, 0xfd, 0x80, 0x00, THISNODE->id >> 8,
67     THISNODE->id & 0xff);
68     putenv (env);
69    
70     return ::conf.script_if_up ? ::conf.script_if_up : "if-up";
71     }
72    
73     int
74     vpn::setup ()
75     {
76     sockinfo si;
77    
78     si.set (THISNODE);
79    
80     udpv4_fd = -1;
81    
82     if (THISNODE->protocols & PROT_UDPv4)
83     {
84     udpv4_fd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP);
85    
86     if (udpv4_fd < 0)
87     return -1;
88    
89     if (bind (udpv4_fd, si.sav4 (), si.salenv4 ()))
90     {
91     slog (L_ERR, _("can't bind udpv4 to %s: %s"), (const char *)si, strerror (errno));
92     exit (1);
93     }
94    
95     #ifdef IP_MTU_DISCOVER
96     // this I really consider a linux bug. I am neither connected
97     // nor do I fragment myself. Linux still sets DF and doesn't
98     // fragment for me sometimes.
99     {
100     int oval = IP_PMTUDISC_DONT;
101     setsockopt (udpv4_fd, SOL_IP, IP_MTU_DISCOVER, &oval, sizeof oval);
102     }
103     #endif
104    
105     // standard daemon practise...
106     {
107     int oval = 1;
108     setsockopt (udpv4_fd, SOL_SOCKET, SO_REUSEADDR, &oval, sizeof oval);
109     }
110    
111     udpv4_ev_watcher.start (udpv4_fd, POLLIN);
112     }
113    
114     ipv4_fd = -1;
115     if (THISNODE->protocols & PROT_IPv4)
116     {
117     ipv4_fd = socket (PF_INET, SOCK_RAW, ::conf.ip_proto);
118    
119     if (ipv4_fd < 0)
120     return -1;
121    
122     if (bind (ipv4_fd, si.sav4 (), si.salenv4 ()))
123     {
124     slog (L_ERR, _("can't bind ipv4 socket to %s: %s"), (const char *)si, strerror (errno));
125     exit (1);
126     }
127    
128     #ifdef IP_MTU_DISCOVER
129     // this I really consider a linux bug. I am neither connected
130     // nor do I fragment myself. Linux still sets DF and doesn't
131     // fragment for me sometimes.
132     {
133     int oval = IP_PMTUDISC_DONT;
134     setsockopt (ipv4_fd, SOL_IP, IP_MTU_DISCOVER, &oval, sizeof oval);
135     }
136     #endif
137    
138     ipv4_ev_watcher.start (ipv4_fd, POLLIN);
139     }
140    
141     tap = new tap_device ();
142     if (!tap) //D this, of course, never catches
143     {
144     slog (L_ERR, _("cannot create network interface '%s'"), conf.ifname);
145     exit (1);
146     }
147    
148     run_script (run_script_cb (this, &vpn::script_if_up), true);
149    
150     tap_ev_watcher.start (tap->fd, POLLIN);
151    
152     reconnect_all ();
153    
154     return 0;
155     }
156    
157     void
158     vpn::send_ipv4_packet (vpn_packet *pkt, const sockinfo &si, int tos)
159     {
160     setsockopt (ipv4_fd, SOL_IP, IP_TOS, &tos, sizeof tos);
161     sendto (ipv4_fd, &((*pkt)[0]), pkt->len, 0, si.sav4 (), si.salenv4 ());
162     }
163    
164     void
165     vpn::send_udpv4_packet (vpn_packet *pkt, const sockinfo &si, int tos)
166     {
167     setsockopt (udpv4_fd, SOL_IP, IP_TOS, &tos, sizeof tos);
168     sendto (udpv4_fd, &((*pkt)[0]), pkt->len, 0, si.sav4 (), si.salenv4 ());
169     }
170    
171     void
172     vpn::recv_vpn_packet (vpn_packet *pkt, const sockinfo &rsi)
173     {
174     unsigned int src = pkt->src ();
175     unsigned int dst = pkt->dst ();
176    
177     slog (L_NOISE, _("<<?/%s received possible vpn packet type %d from %d to %d, length %d"),
178     (const char *)rsi, pkt->typ (), pkt->src (), pkt->dst (), pkt->len);
179    
180     if (src == 0 || src > conns.size ()
181     || dst > conns.size ()
182     || pkt->typ () >= vpn_packet::PT_MAX)
183     slog (L_WARN, _("(%s): received corrupted packet type %d (src %d, dst %d)"),
184     (const char *)rsi, pkt->typ (), pkt->src (), pkt->dst ());
185     else
186     {
187     connection *c = conns[src - 1];
188    
189     if (dst == 0 && !THISNODE->routerprio)
190     slog (L_WARN, _("%s(%s): received broadcast, but we are no router"),
191     c->conf->nodename, (const char *)rsi);
192     else if (dst != 0 && dst != THISNODE->id)
193     // FORWARDING NEEDED ;)
194     slog (L_WARN,
195     _("received frame for node %d ('%s') from %s, but this is node %d ('%s')"),
196     dst, conns[dst - 1]->conf->nodename,
197     (const char *)rsi,
198     THISNODE->id, THISNODE->nodename);
199     else
200     c->recv_vpn_packet (pkt, rsi);
201     }
202     }
203    
204     void
205     vpn::udpv4_ev (short revents)
206     {
207     if (revents & (POLLIN | POLLERR))
208     {
209     vpn_packet *pkt = new vpn_packet;
210     struct sockaddr_in sa;
211     socklen_t sa_len = sizeof (sa);
212     int len;
213    
214     len = recvfrom (udpv4_fd, &((*pkt)[0]), MAXSIZE, 0, (sockaddr *)&sa, &sa_len);
215    
216     sockinfo si(sa);
217    
218     if (len > 0)
219     {
220     pkt->len = len;
221    
222     recv_vpn_packet (pkt, si);
223     }
224     else
225     {
226     // probably ECONNRESET or somesuch
227     slog (L_DEBUG, _("%s: %s"), (const char *)si, strerror (errno));
228     }
229    
230     delete pkt;
231     }
232     else if (revents & POLLHUP)
233     {
234     // this cannot ;) happen on udp sockets
235     slog (L_ERR, _("FATAL: POLLHUP on udp v4 fd, terminating."));
236     exit (1);
237     }
238     else
239     {
240     slog (L_ERR,
241     _("FATAL: unknown revents %08x in socket, terminating\n"),
242     revents);
243     exit (1);
244     }
245     }
246    
247     void
248     vpn::ipv4_ev (short revents)
249     {
250     if (revents & (POLLIN | POLLERR))
251     {
252     vpn_packet *pkt = new vpn_packet;
253     struct sockaddr_in sa;
254     socklen_t sa_len = sizeof (sa);
255     int len;
256    
257     len = recvfrom (ipv4_fd, &((*pkt)[0]), MAXSIZE, 0, (sockaddr *)&sa, &sa_len);
258    
259     sockinfo si(sa, PROT_IPv4);
260    
261     if (len > 0)
262     {
263     pkt->len = len;
264    
265     // raw sockets deliver the ipv4, but don't expect it on sends
266     // this is slow, but...
267     pkt->skip_hdr (IP_OVERHEAD);
268    
269     recv_vpn_packet (pkt, si);
270     }
271     else
272     {
273     // probably ECONNRESET or somesuch
274     slog (L_DEBUG, _("%s: %s"), (const char *)si, strerror (errno));
275     }
276    
277     delete pkt;
278     }
279     else if (revents & POLLHUP)
280     {
281     // this cannot ;) happen on udp sockets
282     slog (L_ERR, _("FATAL: POLLHUP on ipv4 fd, terminating."));
283     exit (1);
284     }
285     else
286     {
287     slog (L_ERR,
288     _("FATAL: unknown revents %08x in socket, terminating\n"),
289     revents);
290     exit (1);
291     }
292     }
293    
294     void
295     vpn::tap_ev (short revents)
296     {
297     if (revents & POLLIN)
298     {
299     /* process data */
300     tap_packet *pkt;
301    
302     pkt = tap->recv ();
303    
304     int dst = mac2id (pkt->dst);
305     int src = mac2id (pkt->src);
306    
307     if (src != THISNODE->id)
308     {
309     slog (L_ERR, _("FATAL: tap packet not originating on current node received, terminating."));
310     exit (1);
311     }
312    
313     if (dst == THISNODE->id)
314     {
315     slog (L_ERR, _("FATAL: tap packet destined for current node received, terminating."));
316     exit (1);
317     }
318    
319     if (dst > conns.size ())
320     slog (L_ERR, _("tap packet for unknown node %d received, ignoring."), dst);
321     else
322     {
323     if (dst)
324     {
325     // unicast
326     if (dst != THISNODE->id)
327     conns[dst - 1]->inject_data_packet (pkt);
328     }
329     else
330     {
331     // broadcast, first check router, then self, then english
332     connection *router = find_router ();
333    
334     if (router)
335     router->inject_data_packet (pkt, true);
336     else
337     for (conns_vector::iterator c = conns.begin (); c != conns.end (); ++c)
338     if ((*c)->conf != THISNODE)
339     (*c)->inject_data_packet (pkt);
340     }
341     }
342    
343     delete pkt;
344     }
345     else if (revents & (POLLHUP | POLLERR))
346     {
347     slog (L_ERR, _("FATAL: POLLHUP or POLLERR on network device fd, terminating."));
348     exit (1);
349     }
350     else
351     abort ();
352     }
353    
354     void
355     vpn::event_cb (tstamp &ts)
356     {
357     if (events)
358     {
359     if (events & EVENT_SHUTDOWN)
360     {
361     slog (L_INFO, _("preparing shutdown..."));
362    
363     shutdown_all ();
364    
365     remove_pid (pidfilename);
366    
367     slog (L_INFO, _("terminating"));
368    
369     exit (0);
370     }
371    
372     if (events & EVENT_RECONNECT)
373     {
374     slog (L_INFO, _("forced reconnect"));
375    
376     reconnect_all ();
377     }
378    
379     events = 0;
380     }
381    
382     ts = TSTAMP_CANCEL;
383     }
384    
385     void
386     vpn::shutdown_all ()
387     {
388     for (conns_vector::iterator c = conns.begin (); c != conns.end (); ++c)
389     (*c)->shutdown ();
390     }
391    
392     void
393     vpn::reconnect_all ()
394     {
395     for (conns_vector::iterator c = conns.begin (); c != conns.end (); ++c)
396     delete *c;
397    
398     conns.clear ();
399    
400     connection_init ();
401    
402     for (configuration::node_vector::iterator i = conf.nodes.begin ();
403     i != conf.nodes.end (); ++i)
404     {
405     connection *conn = new connection (this);
406    
407     conn->conf = *i;
408     conns.push_back (conn);
409    
410     conn->establish_connection ();
411     }
412     }
413    
414     connection *vpn::find_router ()
415     {
416     u32 prio = 0;
417     connection *router = 0;
418    
419     for (conns_vector::iterator i = conns.begin (); i != conns.end (); ++i)
420     {
421     connection *c = *i;
422    
423     if (c->conf->routerprio > prio
424     && c->connectmode == conf_node::C_ALWAYS
425     && c->conf != THISNODE
426     && c->ictx && c->octx)
427     {
428     prio = c->conf->routerprio;
429     router = c;
430     }
431     }
432    
433     return router;
434     }
435    
436     void vpn::connect_request (int id)
437     {
438     connection *c = find_router ();
439    
440     if (c)
441     c->connect_request (id);
442     //else // does not work, because all others must connect to the same router
443     // // no router found, aggressively connect to all routers
444     // for (conns_vector::iterator i = conns.begin (); i != conns.end (); ++i)
445     // if ((*i)->conf->routerprio)
446     // (*i)->establish_connection ();
447     }
448    
449     void
450     connection::dump_status ()
451     {
452     slog (L_NOTICE, _("node %s (id %d)"), conf->nodename, conf->id);
453     slog (L_NOTICE, _(" connectmode %d (%d) / sockaddr %s / minor %d"),
454     connectmode, conf->connectmode, (const char *)si, (int)prot_minor);
455     slog (L_NOTICE, _(" ictx/octx %08lx/%08lx / oseqno %d / retry_cnt %d"),
456     (long)ictx, (long)octx, (int)oseqno, (int)retry_cnt);
457     slog (L_NOTICE, _(" establish_conn %ld / rekey %ld / keepalive %ld"),
458     (long)(establish_connection.at), (long)(rekey.at), (long)(keepalive.at));
459     }
460    
461     void
462     vpn::dump_status ()
463     {
464     slog (L_NOTICE, _("BEGIN status dump (%ld)"), (long)NOW);
465    
466     for (conns_vector::iterator c = conns.begin (); c != conns.end (); ++c)
467     (*c)->dump_status ();
468    
469     slog (L_NOTICE, _("END status dump"));
470     }
471    
472     vpn::vpn (void)
473     : udpv4_ev_watcher(this, &vpn::udpv4_ev)
474     , ipv4_ev_watcher(this, &vpn::ipv4_ev)
475     , tap_ev_watcher(this, &vpn::tap_ev)
476     , event(this, &vpn::event_cb)
477     {
478     }
479    
480     vpn::~vpn ()
481     {
482     }
483