--- gvpe/src/vpn.C 2007/12/06 00:35:29 1.45 +++ gvpe/src/vpn.C 2008/08/10 14:48:57 1.51 @@ -1,22 +1,32 @@ /* vpn.C -- handle the protocol, encryption, handshaking etc. - Copyright (C) 2003-2007 Marc Lehmann + Copyright (C) 2003-2008 Marc Lehmann This file is part of GVPE. - GVPE is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with gvpe; if not, write to the Free Software - Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + GVPE is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, see . + + Additional permission under GNU GPL version 3 section 7 + + If you modify this Program, or any covered work, by linking or + combining it with the OpenSSL project's OpenSSL library (or a modified + version of that library), containing parts covered by the terms of the + OpenSSL or SSLeay licenses, the licensors of this Program grant you + additional permission to convey the resulting work. Corresponding + Source for a non-source form of such a combination shall include the + source code for the parts of OpenSSL used as well as that of the + covered work. */ #include "config.h" @@ -114,6 +124,8 @@ int vpn::setup () { + int success = 0; + ipv4_tos = -1; ipv4_fd = -1; @@ -141,12 +153,15 @@ if (bind (ipv4_fd, si.sav4 (), si.salenv4 ())) { - slog (L_ERR, _("can't bind ipv4 socket on %s: %s"), (const char *)si, strerror (errno)); + slog (L_ERR, _("can't bind ipv4 socket on %s: %s, exiting."), (const char *)si, strerror (errno)); exit (EXIT_FAILURE); } ipv4_ev_watcher.start (ipv4_fd, EV_READ); + ++success; } + else + THISNODE->protocols &= ~PROT_IPv4; udpv4_tos = -1; udpv4_fd = -1; @@ -181,12 +196,15 @@ if (bind (udpv4_fd, si.sav4 (), si.salenv4 ())) { - slog (L_ERR, _("can't bind udpv4 on %s: %s"), (const char *)si, strerror (errno)); + slog (L_ERR, _("can't bind udpv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); exit (EXIT_FAILURE); } udpv4_ev_watcher.start (udpv4_fd, EV_READ); + ++success; } + else + THISNODE->protocols &= ~PROT_UDPv4; icmpv4_tos = -1; icmpv4_fd = -1; @@ -227,11 +245,12 @@ if (bind (icmpv4_fd, si.sav4 (), si.salenv4 ())) { - slog (L_ERR, _("can't bind icmpv4 on %s: %s"), (const char *)si, strerror (errno)); + slog (L_ERR, _("can't bind icmpv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); exit (EXIT_FAILURE); } icmpv4_ev_watcher.start (icmpv4_fd, EV_READ); + ++success; } #endif @@ -258,18 +277,21 @@ if (bind (tcpv4_fd, si.sav4 (), si.salenv4 ())) { - slog (L_ERR, _("can't bind tcpv4 on %s: %s"), (const char *)si, strerror (errno)); + slog (L_ERR, _("can't bind tcpv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); exit (EXIT_FAILURE); } if (listen (tcpv4_fd, 5)) { - slog (L_ERR, _("can't listen tcpv4 on %s: %s"), (const char *)si, strerror (errno)); + slog (L_ERR, _("can't listen tcpv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); exit (EXIT_FAILURE); } tcpv4_ev_watcher.start (tcpv4_fd, EV_READ); + ++success; } + else + THISNODE->protocols &= ~PROT_TCPv4; #endif dnsv4_tos = -1; @@ -310,16 +332,23 @@ if (bind (dnsv4_fd, si.sav4 (), si.salenv4 ())) { - slog (L_ERR, _("can't bind dnsv4 on %s: %s"), (const char *)si, strerror (errno)); + slog (L_ERR, _("can't bind dnsv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); exit (EXIT_FAILURE); } dnsv4_ev_watcher.start (dnsv4_fd, EV_READ); + ++success; } #endif ///////////////////////////////////////////////////////////////////////////// + if (!success) + { + slog (L_ERR, _("no protocols enabled, exiting.")); + exit (EXIT_FAILURE); + } + reconnect_all (); ///////////////////////////////////////////////////////////////////////////// @@ -327,7 +356,7 @@ tap = new tap_device (); if (!tap) //D this, of course, never catches { - slog (L_ERR, _("cannot create network interface '%s'"), conf.ifname); + slog (L_ERR, _("cannot create network interface '%s', exiting."), conf.ifname); exit (EXIT_FAILURE); } @@ -431,7 +460,7 @@ // we have to connect to all hosts... for (conns_vector::iterator c = conns.begin (); c != conns.end (); ++c) if ((*c)->conf != THISNODE) - (*c)->inject_data_packet (pkt, true); + (*c)->inject_data_packet (pkt); } } @@ -441,23 +470,23 @@ unsigned int src = pkt->src (); unsigned int dst = pkt->dst (); - slog (L_NOISE, _("<typ (), pkt->src (), pkt->dst (), pkt->len); if (src == 0 || src > conns.size () || dst > conns.size () || pkt->typ () >= vpn_packet::PT_MAX) - slog (L_WARN, _("(%s): received corrupted packet type %d (src %d, dst %d)"), + slog (L_WARN, _("(%s): received corrupted packet type %d (src %d, dst %d)."), (const char *)rsi, pkt->typ (), pkt->src (), pkt->dst ()); else if (dst > conns.size ()) - slog (L_WARN, _("(%s): received corrupted packet type %d (src %d, dst %d)"), + slog (L_WARN, _("(%s): received corrupted packet type %d (src %d, dst %d)."), (const char *)rsi, pkt->typ (), pkt->src (), pkt->dst ()); else { connection *c = conns[src - 1]; if (dst == 0) - slog (L_WARN, _("%s(%s): received broadcast (protocol violation)"), + slog (L_WARN, _("%s(%s): received broadcast (protocol violation)."), c->conf->nodename, (const char *)rsi); else if (dst != THISNODE->id) { @@ -466,7 +495,7 @@ conns[dst - 1]->inject_vpn_packet (pkt); else slog (L_WARN, - _("%s(%s): forwarding request (=> %s), but we are no router"), + _("%s(%s): request to forward packet to %s, but we are no router (config mismatch?)."), c->conf->nodename, (const char *)rsi, conns[dst - 1]->conf->nodename); } @@ -499,7 +528,7 @@ return send_dnsv4_packet (pkt, si, tos); #endif default: - slog (L_CRIT, _("%s: FATAL: trying to send packet with unsupported protocol"), (const char *)si); + slog (L_CRIT, _("%s: FATAL: trying to send packet with unsupported protocol."), (const char *)si); } return false; @@ -531,7 +560,7 @@ else { // probably ECONNRESET or somesuch - slog (L_DEBUG, _("%s: %s"), (const char *)si, strerror (errno)); + slog (L_DEBUG, _("%s: %s."), (const char *)si, strerror (errno)); } delete pkt; @@ -539,7 +568,7 @@ else { slog (L_ERR, - _("FATAL: unknown revents %08x in socket, terminating\n"), + _("FATAL: unknown revents %08x in socket, exiting.\n"), revents); exit (EXIT_FAILURE); } @@ -579,7 +608,7 @@ else { // probably ECONNRESET or somesuch - slog (L_DEBUG, _("%s: %s"), (const char *)si, strerror (errno)); + slog (L_DEBUG, _("%s: %s."), (const char *)si, strerror (errno)); } delete pkt; @@ -587,7 +616,7 @@ else { slog (L_ERR, - _("FATAL: unknown revents %08x in socket, terminating\n"), + _("FATAL: unknown revents %08x in socket, exiting.\n"), revents); exit (EXIT_FAILURE); } @@ -617,7 +646,7 @@ else { // probably ECONNRESET or somesuch - slog (L_DEBUG, _("%s: fd %d, %s"), (const char *)si, w.fd, strerror (errno)); + slog (L_DEBUG, _("%s: fd %d, %s."), (const char *)si, w.fd, strerror (errno)); } delete pkt; @@ -625,7 +654,7 @@ else { slog (L_ERR, - _("FATAL: unknown revents %08x in socket, terminating\n"), + _("FATAL: unknown revents %08x in socket, exiting.\n"), revents); exit (EXIT_FAILURE); } @@ -684,13 +713,13 @@ shutdown_all (); remove_pid (conf.pidfilename); - slog (L_INFO, _("terminating")); + slog (L_INFO, _("exiting.")); exit (EXIT_SUCCESS); } if (events & EVENT_RECONNECT) { - slog (L_INFO, _("forced reconnect")); + slog (L_INFO, _("forced reconnect.")); reconnect_all (); } @@ -716,48 +745,112 @@ connection_init (); - for (configuration::node_vector::iterator i = conf.nodes.begin (); - i != conf.nodes.end (); ++i) - { - connection *conn = new connection (this, *i); - conns.push_back (conn); - conn->establish_connection (); - } + for (configuration::node_vector::iterator i = conf.nodes.begin (); i != conf.nodes.end (); ++i) + conns.push_back (new connection (this, *i)); + + for (conns_vector::iterator c = conns.begin (); c != conns.end (); ++c) + (*c)->establish_connection (); +} + +bool vpn::can_direct (conf_node *src, conf_node *dst) const +{ + return src != dst + && src->may_direct (dst) + && dst->may_direct (src) + && (((src->protocols & dst->protocols) && src->connectmode == conf_node::C_ALWAYS) + || (src->protocols & dst->connectable_protocols ())); } -connection *vpn::find_router () +// only works for indirect and routed connections: find a router +// from THISNODE to dst +connection *vpn::find_router_for (const connection *dst) { - u32 prio = 1; connection *router = 0; + // first try to find a router with a direct connection + { + u32 prio = 1; + + for (conns_vector::iterator i = conns.begin (); i != conns.end (); ++i) + { + connection *c = *i; + + if (c->conf->routerprio > prio + && c->conf != THISNODE + && c != dst + && can_direct (c->conf, dst->conf)) + { + if (c->ictx && c->octx) + { + prio = c->conf->routerprio; + router = c; + } + else + c->establish_connection (); + } + } + } + + if (router) + return router; + + // second try find the router with the highest priority higher than ours + { + u32 prio = 1; + + for (conns_vector::iterator i = conns.begin (); i != conns.end (); ++i) + { + connection *c = *i; + + if (c->conf->routerprio > prio + && c->conf->routerprio > THISNODE->routerprio + && c != dst + && c->conf != THISNODE) + { + if (c->ictx && c->octx) + { + prio = c->conf->routerprio; + router = c; + } + else + c->establish_connection (); + } + } + } + return router; +} + +void vpn::connection_established (connection *c) +{ for (conns_vector::iterator i = conns.begin (); i != conns.end (); ++i) { - connection *c = *i; + connection *o = *i; - if (c->conf->routerprio > prio - && c->connectmode == conf_node::C_ALWAYS // so we don't drop the connection if in use - && c->ictx && c->octx - && c->conf != THISNODE) // redundant, since ictx==octx==0 always on thisnode + if (!o->is_direct + && o->si.valid () + && c->si != o->si + && c == find_router_for (o)) { - prio = c->conf->routerprio; - router = c; + slog (L_DEBUG, _("%s: can now route packets via %s, re-keying connection."), + o->conf->nodename, c->conf->nodename); + o->rekey (); } } - - return router; } -void vpn::send_connect_request (int id) +void vpn::send_connect_request (connection *c) { - connection *c = find_router (); + connection *r = find_router_for (c); - if (c) - c->send_connect_request (id); + if (r) + { + slog (L_TRACE, _("%s: no way to connect, sending mediated connection request via %s."), + c->conf->nodename, r->conf->nodename); + r->send_connect_request (c->conf->id); + } else - // no router found, aggressively connect to all routers - for (conns_vector::iterator i = conns.begin (); i != conns.end (); ++i) - if ((*i)->conf->routerprio && (*i)->conf != THISNODE) - (*i)->establish_connection (); + slog (L_DEBUG, _("%s: no way to connect and no router found: unable to connect."), + c->conf->nodename); } void @@ -768,8 +861,6 @@ connectmode, conf->connectmode, (const char *)si, (int)prot_minor); slog (L_NOTICE, _(" ictx/octx %08lx/%08lx / oseqno %d / retry_cnt %d"), (long)ictx, (long)octx, (int)oseqno, (int)retry_cnt); - slog (L_NOTICE, _(" establish_conn %ld / rekey %ld / keepalive %ld"), - (long)(establish_connection.at), (long)(rekey.at), (long)(keepalive.at)); } void