--- gvpe/src/vpn.C 2009/03/23 15:22:00 1.54 +++ gvpe/src/vpn.C 2015/09/05 17:40:22 1.69 @@ -1,6 +1,6 @@ /* vpn.C -- handle the protocol, encryption, handshaking etc. - Copyright (C) 2003-2008 Marc Lehmann + Copyright (C) 2003-2008,2010,2011,2013 Marc Lehmann This file is part of GVPE. @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -54,10 +55,38 @@ #include "util.h" #include "vpn.h" +using namespace std; + vpn network; // THE vpn (bad design...) ///////////////////////////////////////////////////////////////////////////// +// hopefully temporary workaround for rare buffer full conditions +// if it happens, usually instantly retrying or retrying ~5ms later +// is good enough with current network technologies/kernels + +static ssize_t +xsendto (int fd, const void *buf, size_t len, int flags, + const struct sockaddr *sa, socklen_t salen) +{ + ssize_t res; + + for (int retry = 0; retry <= 13; ++retry) // ~100ms + { + res = sendto (fd, buf, len, flags, sa, salen); + + if (res >= 0 || errno != ENOBUFS) + break; + + struct timespec ts = { 0, 1000 * retry }; + nanosleep (&ts, 0); + } + + return res; +} + +///////////////////////////////////////////////////////////////////////////// + static void inline set_tos (int fd, int &tos_prev, int tos) { @@ -77,7 +106,7 @@ // the tricky part is rounding to the cipher key blocksize int mtu = conf.mtu - ETH_OVERHEAD - VPE_OVERHEAD - MAX_OVERHEAD; mtu += ETH_OVERHEAD - 6 - 6; // now we have the data portion - mtu -= mtu % EVP_CIPHER_block_size (CIPHER); // round + mtu -= mtu % BLOCK_SIZE (CIPHER); // round mtu -= ETH_OVERHEAD - 6 - 6; // and get interface mtu again char *env; @@ -112,13 +141,7 @@ { script_init_env (); - char *filename; - asprintf (&filename, - "%s/%s", - confbase, - ::conf.script_if_up ? ::conf.script_if_up : "if-up"); - - return filename; + return conf.config_filename (::conf.script_if_up, "if-up"); } int @@ -137,7 +160,8 @@ #ifdef SO_MARK if (::conf.nfmark) - setsockopt (ipv4_fd, SOL_SOCKET, SO_MARK, &::conf.nfmark, sizeof ::conf.nfmark); + if (setsockopt (fd, SOL_SOCKET, SO_MARK, &::conf.nfmark, sizeof ::conf.nfmark)) + slog (L_WARN, _("unable to set nfmark on %s socket: %s"), strprotocol (prot), strerror (errno)); #endif return fd; @@ -393,10 +417,78 @@ } bool +vpn::drop_privileges () +{ + if (::conf.change_root) + { + if (!strcmp (::conf.change_root, "/")) + { + char dir [L_tmpnam]; + if (!tmpnam (dir)) + { + slog (L_CRIT, _("unable to create anonymous root path.")); + return false; + } + + if (mkdir (dir, 0700)) + { + slog (L_CRIT, _("unable to create anonymous root directory.")); + return false; + } + + if (chdir (dir)) + { + slog (L_CRIT, _("unable to change to anonymous root directory.")); + return false; + } + + if (rmdir (dir)) + slog (L_ERR, _("unable to remove anonymous root directory, continuing.")); + } + else + { + if (chdir (::conf.change_root)) + { + slog (L_CRIT, _("%s: unable to change to specified root directory."), ::conf.change_root); + return false; + } + } + + if (chroot (".")) + { + slog (L_CRIT, _("unable to set new root directory.")); + return false; + } + + if (chdir ("/")) + { + slog (L_CRIT, _("unable to set cwd to new root directory.")); + return false; + } + } + + if (::conf.change_gid) + if (setgid (::conf.change_gid)) + { + slog (L_CRIT, _("unable to change group id to %d."), ::conf.change_gid); + return false; + } + + if (::conf.change_uid) + if (setuid (::conf.change_uid)) + { + slog (L_CRIT, _("unable to change user id to %d."), ::conf.change_uid); + return false; + } + + return true; +} + +bool vpn::send_ipv4_packet (vpn_packet *pkt, const sockinfo &si, int tos) { set_tos (ipv4_fd, ipv4_tos, tos); - sendto (ipv4_fd, &((*pkt)[0]), pkt->len, 0, si.sav4 (), si.salenv4 ()); + xsendto (ipv4_fd, &((*pkt)[0]), pkt->len, 0, si.sav4 (), si.salenv4 ()); return true; } @@ -437,7 +529,7 @@ hdr->checksum = ipv4_checksum ((u16 *)hdr, pkt->len); set_tos (icmpv4_fd, icmpv4_tos, tos); - sendto (icmpv4_fd, &((*pkt)[0]), pkt->len, 0, si.sav4 (), si.salenv4 ()); + xsendto (icmpv4_fd, &((*pkt)[0]), pkt->len, 0, si.sav4 (), si.salenv4 ()); return true; } @@ -447,7 +539,7 @@ vpn::send_udpv4_packet (vpn_packet *pkt, const sockinfo &si, int tos) { set_tos (udpv4_fd, udpv4_tos, tos); - sendto (udpv4_fd, &((*pkt)[0]), pkt->len, 0, si.sav4 (), si.salenv4 ()); + xsendto (udpv4_fd, &((*pkt)[0]), pkt->len, 0, si.sav4 (), si.salenv4 ()); return true; } @@ -485,9 +577,6 @@ || pkt->typ () >= vpn_packet::PT_MAX) 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)."), - (const char *)rsi, pkt->typ (), pkt->src (), pkt->dst ()); else { connection *c = conns[src - 1]; @@ -517,14 +606,14 @@ switch (si.prot) { case PROT_IPv4: - return send_ipv4_packet (pkt, si, tos); + return send_ipv4_packet (pkt, si, tos); case PROT_UDPv4: - return send_udpv4_packet (pkt, si, tos); + return send_udpv4_packet (pkt, si, tos); -#if ENABLE_TCP +#if ENABLE_TCP case PROT_TCPv4: - return send_tcpv4_packet (pkt, si, tos); + return send_tcpv4_packet (pkt, si, tos); #endif #if ENABLE_ICMP case PROT_ICMPv4: @@ -532,7 +621,7 @@ #endif #if ENABLE_DNS case PROT_DNSv4: - return send_dnsv4_packet (pkt, si, tos); + return send_dnsv4_packet (pkt, si, tos); #endif default: slog (L_CRIT, _("%s: FATAL: trying to send packet with unsupported protocol."), (const char *)si); @@ -560,7 +649,7 @@ pkt->len = len; // raw sockets deliver the ipv4 header, but don't expect it on sends - pkt->skip_hdr (IP_OVERHEAD); + pkt->skip_hdr (pkt->ipv4_hdr_len ()); recv_vpn_packet (pkt, si); } @@ -607,7 +696,7 @@ { // raw sockets deliver the ipv4, but don't expect it on sends // this is slow, but... - pkt->skip_hdr (ICMP_OVERHEAD); + pkt->skip_hdr (pkt->ipv4_hdr_len () + (ICMP_OVERHEAD - IP_OVERHEAD)); recv_vpn_packet (pkt, si); } @@ -759,7 +848,8 @@ (*c)->establish_connection (); } -bool vpn::can_direct (conf_node *src, conf_node *dst) const +bool +vpn::can_direct (conf_node *src, conf_node *dst) const { return src != dst && src->may_direct (dst) @@ -770,7 +860,8 @@ // only works for indirect and routed connections: find a router // from THISNODE to dst -connection *vpn::find_router_for (const connection *dst) +connection * +vpn::find_router_for (const connection *dst) { connection *router = 0; @@ -819,16 +910,17 @@ return router; } -void vpn::connection_established (connection *c) +void +vpn::connection_established (connection *c) { for (conns_vector::iterator i = conns.begin (); i != conns.end (); ++i) { connection *o = *i; - if (!o->is_direct - && o->si.valid () + if (o->si.valid () && c->si != o->si - && c == find_router_for (o)) + && c == find_router_for (o) + && !can_direct (THISNODE, o->conf)) { slog (L_DEBUG, _("%s: can now route packets via %s, re-keying connection."), o->conf->nodename, c->conf->nodename); @@ -837,7 +929,8 @@ } } -void vpn::send_connect_request (connection *c) +void +vpn::send_connect_request (connection *c) { connection *r = find_router_for (c);