--- gvpe/src/vpn.C 2008/08/10 01:34:36 1.49 +++ gvpe/src/vpn.C 2011/12/17 22:05:34 1.62 @@ -1,6 +1,6 @@ /* vpn.C -- handle the protocol, encryption, handshaking etc. - Copyright (C) 2003-2008 Marc Lehmann + Copyright (C) 2003-2008,2010,2011 Marc Lehmann This file is part of GVPE. @@ -122,21 +122,79 @@ } int +vpn::setup_socket (u8 prot, int family, int type, int proto) +{ + int fd = socket (family, type, proto); + + if (fd < 0) + { + slog (L_ERR, _("unable to create %s socket: %s."), strprotocol (prot), strerror (errno)); + return fd; + } + + fcntl (fd, F_SETFL, O_NONBLOCK); + fcntl (fd, F_SETFD, FD_CLOEXEC); + +#ifdef SO_MARK + if (::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; +} + +int vpn::setup () { + int success = 0; + +#if 1//D2 + ipv42_tos = -1; + ipv42_fd = -1; + + if (THISNODE->protocols & PROT_IPv42 && ::conf.ip_proto) + { + ipv42_fd = setup_socket (PROT_IPv42, PF_INET, SOCK_RAW, ::conf.ip2_proto); + + if (ipv42_fd < 0) + return -1; + +#if defined(SOL_IP) && defined(IP_MTU_DISCOVER) + // this I really consider a linux bug. I am neither connected + // nor do I fragment myself. Linux still sets DF and doesn't + // fragment for me sometimes. + { + int oval = IP_PMTUDISC_DONT; + setsockopt (ipv42_fd, SOL_IP, IP_MTU_DISCOVER, &oval, sizeof oval); + } +#endif + + sockinfo si (THISNODE, PROT_IPv42); + + if (bind (ipv42_fd, si.sav4 (), si.salenv4 ())) + { + slog (L_ERR, _("can't bind ipv42 socket on %s: %s, exiting."), (const char *)si, strerror (errno)); + return -1; + } + + ipv42_ev_watcher.start (ipv42_fd, EV_READ); + ++success; + } + else + THISNODE->protocols &= ~PROT_IPv42; +#endif + ipv4_tos = -1; ipv4_fd = -1; if (THISNODE->protocols & PROT_IPv4 && ::conf.ip_proto) { - ipv4_fd = socket (PF_INET, SOCK_RAW, ::conf.ip_proto); + ipv4_fd = setup_socket (PROT_IPv4, PF_INET, SOCK_RAW, ::conf.ip_proto); if (ipv4_fd < 0) return -1; - fcntl (ipv4_fd, F_SETFL, O_NONBLOCK); - fcntl (ipv4_fd, F_SETFD, FD_CLOEXEC); - #if defined(SOL_IP) && defined(IP_MTU_DISCOVER) // this I really consider a linux bug. I am neither connected // nor do I fragment myself. Linux still sets DF and doesn't @@ -152,25 +210,25 @@ if (bind (ipv4_fd, si.sav4 (), si.salenv4 ())) { slog (L_ERR, _("can't bind ipv4 socket on %s: %s, exiting."), (const char *)si, strerror (errno)); - exit (EXIT_FAILURE); + return -1; } ipv4_ev_watcher.start (ipv4_fd, EV_READ); + ++success; } + else + THISNODE->protocols &= ~PROT_IPv4; udpv4_tos = -1; udpv4_fd = -1; if (THISNODE->protocols & PROT_UDPv4 && THISNODE->udp_port) { - udpv4_fd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); + udpv4_fd = setup_socket (PROT_UDPv4, PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (udpv4_fd < 0) return -1; - fcntl (udpv4_fd, F_SETFL, O_NONBLOCK); - fcntl (udpv4_fd, F_SETFD, FD_CLOEXEC); - // standard daemon practise... { int oval = 1; @@ -192,11 +250,14 @@ if (bind (udpv4_fd, si.sav4 (), si.salenv4 ())) { slog (L_ERR, _("can't bind udpv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); - exit (EXIT_FAILURE); + return -1; } udpv4_ev_watcher.start (udpv4_fd, EV_READ); + ++success; } + else + THISNODE->protocols &= ~PROT_UDPv4; icmpv4_tos = -1; icmpv4_fd = -1; @@ -204,14 +265,11 @@ #if ENABLE_ICMP if (THISNODE->protocols & PROT_ICMPv4) { - icmpv4_fd = socket (PF_INET, SOCK_RAW, IPPROTO_ICMP); + icmpv4_fd = setup_socket (PROT_ICMPv4, PF_INET, SOCK_RAW, IPPROTO_ICMP); if (icmpv4_fd < 0) return -1; - fcntl (icmpv4_fd, F_SETFL, O_NONBLOCK); - fcntl (icmpv4_fd, F_SETFD, FD_CLOEXEC); - #ifdef ICMP_FILTER { icmp_filter oval; @@ -238,10 +296,11 @@ if (bind (icmpv4_fd, si.sav4 (), si.salenv4 ())) { slog (L_ERR, _("can't bind icmpv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); - exit (EXIT_FAILURE); + return -1; } icmpv4_ev_watcher.start (icmpv4_fd, EV_READ); + ++success; } #endif @@ -250,14 +309,11 @@ #if ENABLE_TCP if (THISNODE->protocols & PROT_TCPv4 && THISNODE->tcp_port) { - tcpv4_fd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + tcpv4_fd = setup_socket (PROT_TCPv4, PF_INET, SOCK_STREAM, IPPROTO_TCP); if (tcpv4_fd < 0) return -1; - fcntl (tcpv4_fd, F_SETFL, O_NONBLOCK); - fcntl (tcpv4_fd, F_SETFD, FD_CLOEXEC); - // standard daemon practise... { int oval = 1; @@ -269,17 +325,20 @@ if (bind (tcpv4_fd, si.sav4 (), si.salenv4 ())) { slog (L_ERR, _("can't bind tcpv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); - exit (EXIT_FAILURE); + return -1; } if (listen (tcpv4_fd, 5)) { slog (L_ERR, _("can't listen tcpv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); - exit (EXIT_FAILURE); + return -1; } tcpv4_ev_watcher.start (tcpv4_fd, EV_READ); + ++success; } + else + THISNODE->protocols &= ~PROT_TCPv4; #endif dnsv4_tos = -1; @@ -290,14 +349,11 @@ { dns_forwarder.set (::conf.dns_forw_host, ::conf.dns_forw_port, PROT_DNSv4); - dnsv4_fd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); + dnsv4_fd = setup_socket (PROT_DNSv4, PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (dnsv4_fd < 0) return -1; - fcntl (dnsv4_fd, F_SETFL, O_NONBLOCK); - fcntl (dnsv4_fd, F_SETFD, FD_CLOEXEC); - # if defined(SOL_IP) && defined(IP_MTU_DISCOVER) // this I really consider a linux bug. I am neither connected // nor do I fragment myself. Linux still sets DF and doesn't @@ -321,15 +377,22 @@ if (bind (dnsv4_fd, si.sav4 (), si.salenv4 ())) { slog (L_ERR, _("can't bind dnsv4 on %s: %s, exiting."), (const char *)si, strerror (errno)); - exit (EXIT_FAILURE); + return -1; } dnsv4_ev_watcher.start (dnsv4_fd, EV_READ); + ++success; } #endif ///////////////////////////////////////////////////////////////////////////// + if (!success) + { + slog (L_ERR, _("no protocols enabled.")); + return -1; + } + reconnect_all (); ///////////////////////////////////////////////////////////////////////////// @@ -337,8 +400,8 @@ tap = new tap_device (); if (!tap) //D this, of course, never catches { - slog (L_ERR, _("cannot create network interface '%s', exiting."), conf.ifname); - exit (EXIT_FAILURE); + slog (L_ERR, _("cannot create network interface '%s'."), conf.ifname); + return -1; } fcntl (tap->fd, F_SETFD, FD_CLOEXEC); @@ -349,16 +412,16 @@ if (tap->if_up () && !run_script (cb, true)) { - slog (L_ERR, _("interface initialization command '%s' failed, exiting."), + slog (L_ERR, _("interface initialization command '%s' failed."), tap->if_up ()); - exit (EXIT_FAILURE); + return -1; } cb.set (this); if (!run_script (cb, true)) { - slog (L_ERR, _("if-up command execution failed, exiting.")); - exit (EXIT_FAILURE); + slog (L_ERR, _("if-up command execution failed.")); + return -1; } tap_ev_watcher.start (tap->fd, EV_READ); @@ -375,6 +438,17 @@ return true; } +#if 1 //D +bool +vpn::send_ipv42_packet (vpn_packet *pkt, const sockinfo &si, int tos) +{ + set_tos (ipv42_fd, ipv42_tos, tos); + sendto (ipv42_fd, &((*pkt)[0]), pkt->len, 0, si.sav4 (), si.salenv4 ()); + + return true; +} +#endif + static u16 ipv4_checksum (u16 *data, unsigned int len) { @@ -459,9 +533,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]; @@ -490,13 +561,18 @@ { switch (si.prot) { +#if 1//D2 + case PROT_IPv42: + return send_ipv42_packet (pkt, si, tos); +#endif + case PROT_IPv4: return send_ipv4_packet (pkt, si, tos); case PROT_UDPv4: return send_udpv4_packet (pkt, si, tos); -#if ENABLE_TCP +#if ENABLE_TCP case PROT_TCPv4: return send_tcpv4_packet (pkt, si, tos); #endif @@ -515,6 +591,48 @@ return false; } +#if 1//D2 +inline void +vpn::ipv42_ev (ev::io &w, int revents) +{ + if (revents & EV_READ) + { + vpn_packet *pkt = new vpn_packet; + struct sockaddr_in sa; + socklen_t sa_len = sizeof (sa); + int len; + + len = recvfrom (w.fd, &((*pkt)[0]), MAXSIZE, 0, (sockaddr *)&sa, &sa_len); + + sockinfo si(sa, PROT_IPv42); + + if (len > 0) + { + pkt->len = len; + + // raw sockets deliver the ipv4 header, but don't expect it on sends + pkt->skip_hdr (pkt->ipv4_hdr_len ()); + + recv_vpn_packet (pkt, si); + } + else + { + // probably ECONNRESET or somesuch + slog (L_DEBUG, _("%s: %s."), (const char *)si, strerror (errno)); + } + + delete pkt; + } + else + { + slog (L_ERR, + _("FATAL: unknown revents %08x in socket, exiting.\n"), + revents); + exit (EXIT_FAILURE); + } +} +#endif + inline void vpn::ipv4_ev (ev::io &w, int revents) { @@ -534,7 +652,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); } @@ -581,7 +699,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); } @@ -726,16 +844,15 @@ 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 +bool +vpn::can_direct (conf_node *src, conf_node *dst) const { return src != dst && src->may_direct (dst) @@ -746,11 +863,13 @@ // 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; - // first try to find a router with a direct connection + // first try to find a router with a direct connection, route there + // regardless of any other considerations. { u32 prio = 1; @@ -760,16 +879,11 @@ if (c->conf->routerprio > prio && c->conf != THISNODE - && c != dst - && can_direct (c->conf, dst->conf)) + && can_direct (c->conf, dst->conf) + && c->ictx && c->octx) { - if (c->ictx && c->octx) - { - prio = c->conf->routerprio; - router = c; - } - else - c->establish_connection (); + prio = c->conf->routerprio; + router = c; } } } @@ -777,33 +891,30 @@ if (router) return router; - // second try find the router with the highest priority higher than ours + // second try find the router with the highest priority, higher than ours { - u32 prio = 1; + u32 prio = THISNODE->routerprio ? THISNODE->routerprio : 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) + && c->conf != THISNODE + && c->ictx && c->octx) { - if (c->ictx && c->octx) - { - prio = c->conf->routerprio; - router = c; - } - else - c->establish_connection (); + prio = c->conf->routerprio; + router = c; } } } + 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) { @@ -821,18 +932,20 @@ } } -void vpn::send_connect_request (int id) +void +vpn::send_connect_request (connection *c) { - connection *c = find_router_for (conns[id]); + connection *r = find_router_for (c); - if (c) + if (r) { - slog (L_TRACE, _("%s: no way to connect, sending mediated connection request via %s."), - conns[id]->conf->nodename, c->conf->nodename); - c->send_connect_request (id); + slog (L_TRACE, _("%s: no address known, sending mediated connection request via %s."), + c->conf->nodename, r->conf->nodename); + r->send_connect_request (c->conf->id); } else - slog (L_DEBUG, _("%s: no way to connect and no router found: unable to connect."), conns[id]->conf->nodename); + slog (L_DEBUG, _("%s: no way to connect and no router found: unable to connect at this time."), + c->conf->nodename); } void @@ -861,6 +974,9 @@ event .set (this); udpv4_ev_watcher .set (this); ipv4_ev_watcher .set (this); +#if 1//D2 + ipv42_ev_watcher .set (this); +#endif #if ENABLE_TCP tcpv4_ev_watcher .set (this); #endif