commit 7bbba74073a7dc4f6bec1eb1e9acc3c00273d1b1 Author: unknown Date: Sun Jul 25 19:05:59 2010 +0200 Added multicast support diff --git a/NEWS b/NEWS index b5ce496..a753f1c 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,3 @@ -Version 1.0.14 not released yet - - * Fixed reading configuration files that do not end with a newline. Again. - Version 1.0.13 Apr 11 2010 * Allow building tinc without LZO and/or Zlib. diff --git a/configure.in b/configure.in index 8e1ae87..a9c9dcf 100644 --- a/configure.in +++ b/configure.in @@ -3,7 +3,7 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) AC_INIT AC_CONFIG_SRCDIR([src/tincd.c]) -AM_INIT_AUTOMAKE(tinc, 1.0.13+git) +AM_INIT_AUTOMAKE(tinc, 1.0.13) AC_CONFIG_HEADERS([config.h]) AM_MAINTAINER_MODE @@ -63,7 +63,7 @@ case $host_os in *mingw*) AC_DEFINE(HAVE_MINGW, 1, [MinGW]) [ rm -f src/device.c; cp -f src/mingw/device.c src/device.c ] - LIBS="$LIBS -lws2_32" + LIBS="$LIBS -lws2_32 -liphlpapi" ;; *) AC_MSG_ERROR("Unknown operating system.") @@ -109,6 +109,15 @@ AC_CHECK_HEADERS([netinet/if_ether.h netinet/ip.h netinet/ip6.h], AC_CHECK_HEADERS([netinet/tcp.h netinet/ip_icmp.h netinet/icmp6.h], [], [], [#include "have.h"] ) +AC_CHECK_HEADERS([ifaddrs.h sys/sockio.h linux/sockios.h], + [], [], [#include "have.h"] +) +AC_CHECK_HEADERS([sys/sockio.h], + [], [], [#include "have.h"] +) +AC_CHECK_HEADERS([linux/sockios.h], + [], [], [#include "have.h"] +) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST diff --git a/have.h b/have.h index 923e76a..c8d97bb 100644 --- a/have.h +++ b/have.h @@ -21,14 +21,6 @@ #ifndef __TINC_HAVE_H__ #define __TINC_HAVE_H__ -#ifdef HAVE_MINGW -#ifdef WITH_WINDOWS2000 -#define WINVER Windows2000 -#else -#define WINVER WindowsXP -#endif -#endif - #include #include #include @@ -40,6 +32,11 @@ #include #ifdef HAVE_MINGW +#ifdef WITH_WINDOWS2000 +#define WINVER Windows2000 +#else +#define WINVER WindowsXP +#endif #include #include #include @@ -178,4 +175,16 @@ #include #endif +#ifdef HAVE_IFADDRS_H +#include +#endif + +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif + +#ifdef HAVE_LINUX_SOCKIOS_H +#include +#endif + #endif /* __TINC_SYSTEM_H__ */ diff --git a/src/Makefile.am b/src/Makefile.am index 491f011..39cbefc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,9 +4,10 @@ sbin_PROGRAMS = tincd EXTRA_DIST = linux/device.c bsd/device.c solaris/device.c cygwin/device.c mingw/device.c mingw/common.h raw_socket/device.c uml_socket/device.c -tincd_SOURCES = conf.c connection.c edge.c event.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \ - net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c \ - protocol_key.c protocol_subnet.c route.c subnet.c tincd.c +tincd_SOURCES = conf.c connection.c edge.c event.c graph.c logger.c meta.c multicast.c multicast_announce.c multicast_response.c \ + net.c net_packet.c net_setup.c net_socket.c netutl.c \ + node.c process.c protocol.c protocol_auth.c protocol_edge.c \ + protocol_misc.c protocol_key.c protocol_subnet.c route.c subnet.c tincd.c ifaddrs-compat.c if TUNEMU tincd_SOURCES += bsd/tunemu.c @@ -19,7 +20,7 @@ DEFAULT_INCLUDES = INCLUDES = @INCLUDES@ -I$(top_builddir) -I$(top_srcdir)/lib noinst_HEADERS = conf.h connection.h device.h edge.h event.h graph.h logger.h meta.h net.h netutl.h node.h process.h \ - protocol.h route.h subnet.h bsd/tunemu.h + protocol.h route.h subnet.h bsd/tunemu.h multicast.h ifaddrs-compat.h LIBS = @LIBS@ diff --git a/src/graph.c b/src/graph.c index 1e6043d..3f5e2e1 100644 --- a/src/graph.c +++ b/src/graph.c @@ -240,6 +240,7 @@ void sssp_bfs(void) { if(n->status.visited != n->status.reachable) { n->status.reachable = !n->status.reachable; + n->status.local = n->status.local && n->status.reachable; if(n->status.reachable) { ifdebug(TRAFFIC) logger(LOG_DEBUG, "Node %s (%s) became reachable", @@ -288,8 +289,10 @@ void sssp_bfs(void) { subnet_update(n, NULL, n->status.reachable); - if(!n->status.reachable) + if(!n->status.reachable) { update_node_udp(n, NULL); + update_node_udp_local(n, NULL, 0, 0); + } else if(n->connection) send_ans_key(n); } diff --git a/src/ifaddrs-compat.c b/src/ifaddrs-compat.c new file mode 100644 index 0000000..6cfe74b --- /dev/null +++ b/src/ifaddrs-compat.c @@ -0,0 +1,283 @@ +/* ifaddrs-compat.c -- implementation for getting network interface addresses + Copyright (C) 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include "system.h" + +#if defined(HAVE_IFADDRS_H) +#include "device.h" +#include "ifaddrs.h" +#else +#include "ifaddrs-compat.h" +#include "net.h" +#include "xalloc.h" +#include +#include "logger.h" +#include +#include "utils.h" + +# if !defined(IF_NAMESIZE) +# if defined(IFNAMSIZ) +# define IF_NAMESIZE IFNAMSIZ +# else +# define IFNAMSIZ 256 +# endif +#endif + +#if defined(HAVE_MINGW) +#include +const ULONG flags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST; + +struct ifreq { + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + }ifr_ifrn; + + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + unsigned int ifru_flags; + void * ifru_data; + }ifr_ifru; +}; + +#define ifr_name ifr_ifrn.ifrn_name /* interface name */ +#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ +#define ifr_addr ifr_ifru.ifru_addr /* address */ +/* other end of p-p lnk */ +#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ +#define ifr_dstaddr ifr_ifru.ifru_dstaddr +#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ +#define ifr_flags ifr_ifru.ifru_flags /* flags */ +#define ifr_index ifr_ifru.ifru_index /* index of the interface */ + +#else +#include +#include +#include +#include +#endif + +#if defined(HAVE_SYS_SOCKIO_H) +#include +#elif defined(HAVE_LINUX_SOCKIOS_H) +#include +#endif + +#define MAX_INTERFACES 128 +#define STEPSZ 8 + +static void add_interface(struct ifaddrs **ifa, struct ifreq *ifNameAddress, struct ifreq *ifFlags, int ifIndex, struct sockaddr *sa) { + struct ifaddrs *ifa_new = NULL; + size_t sasize; + int family = ifNameAddress->ifr_addr.sa_family; + + if (family != AF_INET && family != AF_INET6) + return; + + sasize = SALEN((*sa)); + + if ((ifa_new = malloc(sizeof(*ifa_new))) == NULL) + goto fail; + memset(ifa_new, 0, sizeof(*ifa_new)); + + if ((ifa_new->ifa_name = strdup(ifNameAddress->ifr_name)) == NULL) + goto fail; + if ((ifa_new->ifa_addr = malloc(sasize)) == NULL) + goto fail; + if (memcpy(ifa_new->ifa_addr, sa, sasize) == NULL) + goto fail; + ifa_new->ifa_flags = ifFlags->ifr_flags; + ifa_new->ifa_index = ifIndex; + printf("%i\n", ifFlags->ifr_flags); + printf("%i\n", ifa_new->ifa_flags); + + ifa_new->ifa_next = *ifa; + *ifa = ifa_new; + + return; + fail: if (ifa_new->ifa_name != NULL) + free(ifa_new->ifa_name); + if (ifa_new->ifa_addr != NULL) + free(ifa_new->ifa_addr); + if (ifa_new != NULL) + free(ifa_new); +} + +void freeifaddrs(struct ifaddrs *ifap) { + struct ifaddrs *tmpifa = ifap; + + while (ifap != NULL) { + free(ifap->ifa_name); + free(ifap->ifa_addr); + + tmpifa = ifap->ifa_next; + free(ifap); + ifap = tmpifa; + } + return; +} + +#if defined(HAVE_MINGW) + +int getifaddrs(struct ifaddrs **ifa) { + /* Declare and initialize variables */ + DWORD dwSize = 0; + DWORD dwRetVal = 0; + char *afname = NULL, *address = NULL; + + unsigned int i = 0; + + LPVOID lpMsgBuf = NULL; + + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + ULONG outBufLen = 0; + + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL; + PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL; + PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL; + IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL; + IP_ADAPTER_PREFIX *pPrefix = NULL; + + /* Determine required buffer size */ + do { + free(pAddresses); + pAddresses = (IP_ADAPTER_ADDRESSES *) xmalloc(outBufLen); + dwRetVal = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAddresses, &outBufLen); + if (dwRetVal != ERROR_SUCCESS && dwRetVal != ERROR_BUFFER_OVERFLOW) { + logger(LOG_ERR, "Call to %s failed: %d %s", "GetAdaptersAddresses", dwRetVal, winerror(dwRetVal)); + } + } while (dwRetVal != ERROR_SUCCESS && dwRetVal != ERROR_NO_DATA); + + if (dwRetVal == ERROR_NO_DATA) { + /* No data found */ + logger(LOG_ERR, "Call to %s failed: %d %s", "GetAdaptersAddresses", dwRetVal, winerror(dwRetVal)); + free(pAddresses); + return -1; + } + pCurrAddresses = pAddresses; + while (pCurrAddresses) { + for (pUnicast = pCurrAddresses->FirstUnicastAddress; pUnicast != NULL;) { + if (pUnicast->Flags & IP_ADAPTER_ADDRESS_DNS_ELIGIBLE == 0) { + /* address should not be used to announce */ + pUnicast = pUnicast->Next; + continue; + } + sockaddr_t sa; + sa.sa.sa_family = pUnicast->Address.lpSockaddr->sa_family; + if (sa.sa.sa_family == AF_INET) { + sa.in = *((struct sockaddr_in *) pUnicast->Address.lpSockaddr); + } + if (sa.sa.sa_family == AF_INET6) { + sa.in6 = *((struct sockaddr_in6 *) pUnicast->Address.lpSockaddr); + } + struct ifreq ifNameAddress, ifFlags; + int ifIndex; + strncpy((char*) &ifNameAddress.ifr_name, pCurrAddresses->AdapterName, IFNAMSIZ); + ifNameAddress.ifr_addr.sa_family = + pUnicast->Address.lpSockaddr->sa_family; + ifFlags.ifr_flags = 0; + ifFlags.ifr_flags |= ((pCurrAddresses->OperStatus & IfOperStatusUp) ? IFF_UP : 0); + ifFlags.ifr_flags |= ((pCurrAddresses->IfType & IF_TYPE_SOFTWARE_LOOPBACK) ? IFF_LOOPBACK : 0); + ifFlags.ifr_flags |= ((pCurrAddresses->Flags & IP_ADAPTER_NO_MULTICAST) ? 0 : IFF_MULTICAST); + + ifIndex = pCurrAddresses->IfIndex; + add_interface(ifa, &ifNameAddress, &ifFlags, ifIndex, (struct sockaddr*) &sa); + pUnicast = pUnicast->Next; + } + pCurrAddresses = pCurrAddresses->Next; + } + + free(pAddresses); + return 0; +} + +#elif defined(HAVE_SYS_SOCKIO_H) || defined(HAVE_LINUX_SOCKIOS_H) + +int getifaddrs(struct ifaddrs **ifa) { + int fd, i, oldlen = 0; + size_t buflen = 0; + char *oldbuf = NULL, *buf = NULL; + struct ifconf ifc; + struct ifreq *ifr; + struct sockaddr *sa; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + logger(LOG_ERR, "getifaddrs: Call to &s failed", "socket"); + return -1; + } + + *ifa = NULL; + + for (i = STEPSZ; i < MAX_INTERFACES; i += STEPSZ) { + buflen = i * sizeof(struct ifreq); + oldbuf = buf; + if ((buf = xrealloc(buf, buflen)) == NULL) { + logger(LOG_ERR, "getifaddrs: Call to &s failed", "xrealloc"); + goto fail; + } + ifc.ifc_buf = buf; + ifc.ifc_len = buflen; + + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + logger(LOG_ERR, "getifaddrs: Call to &s failed", "SIOCGIFCONF"); + goto fail; + } + if (oldlen == ifc.ifc_len) + break; + oldlen = ifc.ifc_len; + } + + for (oldbuf = buf; buf < oldbuf + ifc.ifc_len;) { + ifr = (struct ifreq *)buf; + sa = (struct sockaddr *)&ifr->ifr_addr; + buf += IF_NAMESIZE + SALEN((*sa)); + + struct ifreq ifFlags = *ifr; + struct ifreq ifIndex = *ifr; + if (ioctl(fd, SIOCGIFFLAGS, &ifFlags) < 0) { + logger(LOG_ERR, "getifaddrs: Call to &s failed", "SIOCGIFFLAGS"); + goto fail; + } + if (ioctl(fd, SIOCGIFINDEX, &ifIndex) < 0) { + logger(LOG_ERR, "getifaddrs: Call to &s failed", "SIOCGIFINDEX"); + goto fail; + } + + add_interface(ifa, ifr, &ifFlags, ifIndex.ifr_ifindex, sa); + } + + free(buf); + close(fd); + return 0; + +fail: + free(oldbuf); + if (*ifa != NULL) { + freeifaddrs(*ifa); + *ifa = NULL; + } + close(fd); + return -1; +} +# endif + +#endif /* HAVE_GETIFADDRS */ diff --git a/src/ifaddrs-compat.h b/src/ifaddrs-compat.h new file mode 100644 index 0000000..5715b39 --- /dev/null +++ b/src/ifaddrs-compat.h @@ -0,0 +1,84 @@ +/* ifaddrs-compat.h -- declarations for getting network interface addresses + Copyright (C) 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _IFADDRS_COMPAT_H +#define _IFADDRS_COMPAT_H 1 + +#include "system.h" + +#if defined(HAVE_NET_IF_H) +#include +#endif + +#if !defined(IFF_POINTTOPOINT) && defined(IFF_POINTOPOINT) +# define IFF_POINTTOPOINT IFF_POINTOPOINT +#endif + +#if defined(HAVE_IFADDRS_H) +#include +#else + #if !defined(HAVE_MINGW) + #include + #endif + +/* The `getifaddrs' function generates a linked list of these structures. + Each element of the list describes one network interface. */ +struct ifaddrs { + struct ifaddrs *ifa_next; /* Pointer to the next structure. */ + + char *ifa_name; /* Name of this network interface. */ + unsigned int ifa_flags; /* Flags as from SIOCGIFFLAGS ioctl. */ + + struct sockaddr *ifa_addr; /* Network address of this interface. */ + struct sockaddr *ifa_netmask; /* Netmask of this interface. */ + unsigned int ifa_index; + + union ifa_ifu { + /* At most one of the following two is valid. If the IFF_BROADCAST + bit is set in `ifa_flags', then `ifa_broadaddr' is valid. If the + IFF_POINTOPOINT bit is set, then `ifa_dstaddr' is valid. + It is never the case that both these bits are set at once. */ + struct sockaddr *ifu_broadaddr; /* Broadcast address of this interface. */ + struct sockaddr *ifu_dstaddr; /* Point-to-point destination address. */ + } ifa_ifu; + /* These very same macros are defined by for `struct ifaddr'. + So if they are defined already, the existing definitions will be fine. */ +# ifndef ifa_broadaddr +# define ifa_broadaddr ifa_ifu.ifu_broadaddr +# endif +# ifndef ifa_dstaddr +# define ifa_dstaddr ifa_ifu.ifu_dstaddr +# endif + + void *ifa_data; /* Address-specific data (may be unused). */ +}; + +/* Create a linked list of `struct ifaddrs' structures, one for each + network interface on the host machine. If successful, store the + list in *IFAP and return 0. On errors, return -1 and set `errno'. + + The storage returned in *IFAP is allocated dynamically and can + only be properly freed by passing it to `freeifaddrs'. */ +extern int getifaddrs(struct ifaddrs **__ifap); + +/* Reclaim the storage allocated by a previous `getifaddrs' call. */ +extern void freeifaddrs(struct ifaddrs *__ifa); + +#endif // HAVE_IFADDRS_H +#endif /* IFADDRS_COMPAT_H */ diff --git a/src/logger.c b/src/logger.c index bc20438..a800373 100644 --- a/src/logger.c +++ b/src/logger.c @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "time.h" #include "system.h" @@ -65,18 +66,25 @@ void openlogger(const char *ident, logmode_t mode) { } void logger(int priority, const char *format, ...) { + time_t t = time(NULL); + char* time = asctime(localtime(&t)); + char* newline = strchr(time, '\n'); + if(newline) { + *newline = '\0'; + } va_list ap; va_start(ap, format); switch(logmode) { case LOGMODE_STDERR: + fprintf(stderr, "%s%s ", time, ":"); vfprintf(stderr, format, ap); fprintf(stderr, "\n"); fflush(stderr); break; case LOGMODE_FILE: - fprintf(logfile, "%ld %s[%ld]: ", time(NULL), logident, (long)logpid); + fprintf(logfile, "%s %s %s[%ld]: ", __DATE__, __TIME__, logident, (long)logpid); vfprintf(logfile, format, ap); fprintf(logfile, "\n"); fflush(logfile); diff --git a/src/multicast.c b/src/multicast.c new file mode 100644 index 0000000..8b94089 --- /dev/null +++ b/src/multicast.c @@ -0,0 +1,344 @@ +/* + * multicast.c + * + * Created on: 05.07.2010 + * Author: schalli + */ + +#include "system.h" + +#include + +#include "ifaddrs-compat.h" + +#include "device.h" +#include "multicast.h" +#include "netutl.h" + +#ifdef HAVE_MINGW +#include +#endif + +#include + +const int multicastTTL = 1; + +const int multicastPort = 16655; + +struct sockaddr_in multicastAddr4; + +struct sockaddr_in6 multicastAddr6; + +listen_socket_t multicast_socket[MAXSOCKETS]; +int multicast_sockets = 0; + +char mc_challenge[MC_CHALLENGE_SIZE] = { 0 }; +struct timeval mc_timestamp; + +/** Check MAC of incoming multicast packet */ +int check_signature(const node_t *n, mcpacket_t *packet) { + if (memcmp(mc_challenge, (char *) &packet->challenge, sizeof(MC_CHALLENGE_SIZE))) { + ifdebug(PROTOCOL) + logger(LOG_DEBUG, "Got bogus multicast response from %s", n->name); + return 0; + } + if (n->indigest && n->inmaclength) { + unsigned char hmac[EVP_MAX_MD_SIZE]; + HMAC(n->indigest, n->inkey, n->inkeylength, (unsigned char *) packet, (char *) &packet->response.signature - (char *) packet, (unsigned char *) &hmac, + NULL); + if (memcmp(hmac, (char *) &packet->response.signature, n->inmaclength)) { + ifdebug(PROTOCOL) + logger(LOG_DEBUG, "Got unauthenticated multicast response from %s", n->name); + return 0; + } + // challenge and signature ok + return 1; + } else { + ifdebug(PROTOCOL) + logger(LOG_WARNING, "Unable to check signature in multicast response from %s", n->name); + return 0; + } +} + +/* Add the message authentication code */ +void set_signature(const node_t *n, mcpacket_t* packet) { + if (n->outdigest && n->outmaclength) { + HMAC(n->outdigest, n->outkey, n->outkeylength, (unsigned char *) packet, (char *) &packet->response.signature - (char *) packet, + (unsigned char *) &packet->response.signature, NULL); + } else { + ifdebug(PROTOCOL) + logger(LOG_WARNING, "Unable to sign multicast response to %s", n->name); + } +} + +mcpacket_t *new_mcpacket(void) { + return xmalloc_and_zero(sizeof(mcpacket_t)); +} + +void free_mcpacket(mcpacket_t *p) { + free(p); +} + +void init_multicast(void) { + /** Initialize */ + const char *multicastIP4 = "224.0.0.1"; + multicastAddr4 = str2sockaddr(multicastIP4, 0x0).in; + multicastAddr4.sin_port = htons(multicastPort); /* Multicast port */ + + const char *multicastIP6 = "FF02:0:0:0:0:0:0:1"; + multicastAddr6 = str2sockaddr(multicastIP6, 0x0).in6; + multicastAddr6.sin6_port = htons(multicastPort); /* Multicast port */ + /** Done initializing */ +} + +/** Bind to sockets */ +void bind_multicast_sockets(void) { + char *mode = NULL; + if (get_config_string(lookup_config(config_tree, "LocalAnnounce"), &mode)) { + if (strcasecmp(mode, "ReceiveOnly") && strcasecmp(mode, "AnnounceAndReceive")) { + free(mode); + return; + } + } else { + return; + } + free(mode); + + int afamily = AF_UNSPEC; + char *afname = NULL, *address = NULL; + multicast_sockets = 0; + + if (get_config_string(lookup_config(config_tree, "AddressFamily"), &afname)) { + if (!strcasecmp(afname, "IPv4")) + afamily = AF_INET; + else if (!strcasecmp(afname, "IPv6")) + afamily = AF_INET6; + else if (!strcasecmp(afname, "any")) + afamily = AF_UNSPEC; + else { + logger(LOG_ERR, "Invalid address family!"); + free(afname); + return; + } + free(afname); + } + + if (get_config_string(lookup_config(config_tree, "BindToAddress"), &address)) { + logger(LOG_ERR, "BindToAddress not yet supported"); + free(address); + return; + } else { + // bind to all interfaces + struct ifaddrs *ifaddr, *ifa; + int family, s; + char host[NI_MAXHOST]; + if (getifaddrs(&ifaddr) < 0) { + logger(LOG_ERR, "bind_multicast_sockets: Call to %s failed", "getifaddrs"); + return; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + family = ifa->ifa_addr->sa_family; + // printf("Testing if\n"); + + /* For an AF_INET* interface address, display the address */ + if (family != AF_INET && family != AF_INET6) { + // neither ipv4 nor ipv6 + // printf("Neither ipv4 nor ipv6\n"); + continue; + } + if (!(ifa->ifa_flags & IFF_UP)) { + // interface is down + // printf("If is down\n"); + continue; + } + + if (ifa->ifa_flags & IFF_LOOPBACK) { + // loopback + // printf("If is loopback\n"); + continue; + } + + if (ifa->ifa_flags & IFF_POINTTOPOINT) { + // p2p + //printf("If is p2p\n"); + continue; + } + if (!(ifa->ifa_flags & IFF_MULTICAST)) { + // does not support multicast + //printf("If does not support multicast\n"); + continue; + } + if (strcmp(iface, ifa->ifa_name) == 0) { + // it's the tinc interface + //printf("It's the tinc interface\n"); + continue; + } + + sockaddr_t sa; + memcpy(&sa, ifa->ifa_addr, SALEN((*ifa->ifa_addr))); + //char* address; + //sockaddr2str(&sa, &address, NULL); + //printf("Binding %s\n", address); + //free(address); + + int ifIndex; +#ifdef HAVE_MINGW + ifIndex = ifa->ifa_index; +#else + ifIndex = if_nametoindex(ifa->ifa_name); +#endif + if (ifIndex == 0) { + logger(LOG_WARNING, "Failed to call if_nametoindex()"); + continue; + } + bind_multicast_socket(&sa, ifIndex); + // printf("Bound interfaces %i\n", multicast_sockets); + } + + freeifaddrs(ifaddr); + } +} + +void bind_multicast_socket(sockaddr_t *sa, int ifIndex) { + int fd; + fd = socket(sa->sa.sa_family, SOCK_DGRAM, 0); + if (fd < 0) { + ifdebug(PROTOCOL) + logger(LOG_WARNING, "Creating ipv4 multicast listener socket failed: %s", sockstrerror(sockerrno)); + return; + } + + /* Double bind to address */ + int option = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &option, sizeof(option)) < 0) { + ifdebug(PROTOCOL) + logger(LOG_WARNING, "Could not set socket options: %s", sockstrerror(sockerrno)); + closesocket(fd); + return; + } + + if (sa->sa.sa_family == AF_INET) { + sa->in.sin_port = htons(multicastPort); /* Multicast port */ + + /* Bind to the multicast port */ + if (bind(fd, (struct sockaddr *) &sa->in, sizeof(sa->in)) < 0) { + ifdebug(PROTOCOL) + logger(LOG_WARNING, "Could not bind to ipv4 multicast socket: %d %s", sockerrno, sockstrerror(sockerrno)); + closesocket(fd); + return; + } + + /* Join the multicast address */ + struct ip_mreq multicastRequest; + multicastRequest.imr_multiaddr = multicastAddr4.sin_addr; +#ifdef HAVE_MINGW + multicastRequest.imr_interface.s_addr = sa->in.sin_addr.S_un.S_addr; +#else + multicastRequest.imr_interface = sa->in.sin_addr; +#endif + if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &multicastRequest, sizeof(multicastRequest)) < 0) { + ifdebug(PROTOCOL) + logger(LOG_WARNING, "Could not join ipv4 multicast group: %d %s", sockerrno, sockstrerror(sockerrno)); + closesocket(fd); + return; + } + } else if (sa->sa.sa_family == AF_INET6) { + sa->in6.sin6_port = htons(multicastPort); /* Multicast port */ + + /* Bind to the multicast port */ + if (bind(fd, (struct sockaddr *) &sa->in6, sizeof(sa->in6)) < 0) { + ifdebug(PROTOCOL) + logger(LOG_WARNING, "Could not bind to ipv6 multicast socket: %d %s", sockerrno, sockstrerror(sockerrno)); + closesocket(fd); + return; + } + + /* Join the multicast address */ + struct ipv6_mreq multicastRequest; + multicastRequest.ipv6mr_multiaddr = multicastAddr6.sin6_addr; + multicastRequest.ipv6mr_interface = ifIndex; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *) &multicastRequest, sizeof(multicastRequest)) < 0) { + ifdebug(PROTOCOL) + logger(LOG_WARNING, "Could not join ipv6 multicast group: %d %s", sockerrno, sockstrerror(sockerrno)); + closesocket(fd); + return; + } + } else { + ifdebug(PROTOCOL) + logger(LOG_WARNING, "Cannot announce endpoint: %s", "Unknown address family"); + closesocket(fd); + return; + } + + multicast_socket[multicast_sockets].sa = *sa; + multicast_socket[multicast_sockets++].udp = fd; +} + +void handle_multicast_data(int sock) { + length_t len; + sockaddr_t from; + socklen_t fromlen = sizeof(from); + mcpacket_t packet; + + len = recvfrom(sock, (char *) &packet, sizeof(mcpacket_t), 0, &from.sa, &fromlen); + + if (len != sizeof(mcpacket_t)) { + logger(LOG_INFO, "Received bogus multicast data"); + return; + } + receive_multicast_packet(sock, &packet, &from); +} + +void receive_multicast_packet(int fd, mcpacket_t *packet, sockaddr_t *from) { + if (strncmp(packet->netname, netname, STRLEN) != 0) { + /* wrong netname */ + return; + } + + if (strncmp(packet->hostname, myself->name, STRLEN) == 0) { + /* from myself --> ignore */ + return; + } + + node_t *n = lookup_node(packet->hostname); + if (!n) { + /* node not found */ + logger(LOG_WARNING, "Node %s not found", packet->hostname); + return; + } + + // printf("Receiving Multicast Data\n"); + if (packet->type == MCPKT_ANNOUNCE) { + // printf("Multicast Announce reveived from %s\n", packet->hostname); + receive_multicast_announce(fd, packet, n, from); + } else if (packet->type == MCPKT_RESPONSE) { + // printf("Multicast Response reveived from %s\n", packet->hostname); + if (!check_signature(n, packet)) { + /* Packet not properly signed */ + logger(LOG_WARNING, "Packet from %s not properly signed", n->name); + return; + } + + receive_multicast_response(n, from, packet); + } else { + // ifdebug(PROTOCOL) + logger(LOG_WARNING, "Unknown Multicast Packet received from %s", n->name); + } +} + +void close_multicast_sockets() { + for (int i = 0; i < multicast_sockets; i++) { + close_multicast_socket(i); + } + multicast_sockets = 0; +} + +void close_multicast_socket(const int i) { + close(multicast_socket[i].udp); + multicast_socket[i].udp = -1; + // for (int i = socketIdx + 1; i < multicast_sockets; i++) { + // multicast_socket[i - 1] = multicast_socket[i]; + // } + // multicast_sockets--; +} diff --git a/src/multicast.h b/src/multicast.h new file mode 100644 index 0000000..168ef31 --- /dev/null +++ b/src/multicast.h @@ -0,0 +1,86 @@ +/* + * multicast.h + * + * Created on: 05.07.2010 + * Author: schalli + */ + +#ifndef __TINC_MULTICAST_H__ +#define __TINC_MULTICAST_H__ + +#include "system.h" + +#include "logger.h" +#include "net.h" +#include "utils.h" + +// Multicast TTL is 1, since we announce to local network +extern const int multicastTTL; + +// Multicast port is 16655 for every network +extern const int multicastPort; + +/* IPv4 Multicast address */ +extern struct sockaddr_in multicastAddr4; + +/* IPv6 Multicast address */ +extern struct sockaddr_in6 multicastAddr6; + +/** keeps all multicast sockets */ +extern listen_socket_t multicast_socket[MAXSOCKETS]; +extern int multicast_sockets; + +/** The challenge we sent */ +#define MC_CHALLENGE_SIZE 128 +extern char mc_challenge[]; +extern struct timeval mc_timestamp; + +#define STRLEN 64 + +typedef enum mcpacket_type_t { + MCPKT_ANNOUNCE, /* Multicast announce */ + MCPKT_RESPONSE +/* Multicast response to announce */ +} mcpacket_type_t; + +typedef struct mcpacket_announce_t { +} mcpacket_announce_t; + +typedef struct mcpacket_response_t { + u_short port; + char signature[EVP_MAX_MD_SIZE]; +} mcpacket_response_t; + +typedef struct mcpacket_t { + mcpacket_type_t type; + char netname[STRLEN]; + char hostname[STRLEN]; + char challenge[MC_CHALLENGE_SIZE]; + union { + mcpacket_announce_t announce; + mcpacket_response_t response; + }; +} mcpacket_t; + +extern mcpacket_t *new_mcpacket(void) __attribute__ ((__malloc__)); +extern void free_mcpacket(mcpacket_t *); +extern int check_signature(const node_t *, mcpacket_t *); +extern void set_signature(const node_t *, mcpacket_t *); + +extern void init_multicast(void); + +extern void handle_multicast_data(int); +extern void receive_multicast_packet(int, mcpacket_t *, union sockaddr_t *); + +extern void send_multicast_announces(void); +extern void send_multicast_announce(const mcpacket_t *, const int socketIdx); +extern void receive_multicast_announce(const int, const mcpacket_t *, const node_t *, const sockaddr_t *); +extern void send_multicast_response(const int, const mcpacket_t *, const node_t *, const sockaddr_t *); +extern void receive_multicast_response(node_t *, const sockaddr_t *, const mcpacket_t *); + +extern void bind_multicast_sockets(void); +extern void bind_multicast_socket(sockaddr_t *, int); +extern void close_multicast_sockets(void); +extern void close_multicast_socket(const int); + +#endif /* __TINC_MULTICAST_H__ */ diff --git a/src/multicast_announce.c b/src/multicast_announce.c new file mode 100644 index 0000000..dd6d46b --- /dev/null +++ b/src/multicast_announce.c @@ -0,0 +1,74 @@ +/* + * multicast_announce.c + * + * Created on: 05.07.2010 + * Author: schalli + */ + +#include "system.h" + +#include "multicast.h" +#include "device.h" +#include + +#ifdef HAVE_MINGW +#include +#include +#endif + +#include + +void send_multicast_announces(void) { + char *mode = NULL; + if (get_config_string(lookup_config(config_tree, "LocalAnnounce"), &mode)) { + if (strcasecmp(mode, "AnnounceOnly") && strcasecmp(mode, "AnnounceAndReceive")) { + free(mode); + return; + } + } else { + return; + } + free(mode); + + mcpacket_t multicastPacket; + multicastPacket.type = MCPKT_ANNOUNCE; + strncpy(multicastPacket.hostname, myself->name, sizeof(multicastPacket.hostname)); + if (netname) + strncpy(multicastPacket.netname, netname, sizeof(multicastPacket.netname)); + + RAND_pseudo_bytes((char*) &mc_challenge, MC_CHALLENGE_SIZE); + memcpy(multicastPacket.challenge, mc_challenge, MC_CHALLENGE_SIZE); + gettimeofday(&mc_timestamp, NULL); + + for (int i = 0; i < multicast_sockets; i++) { + send_multicast_announce(&multicastPacket, i); + } +} + +void send_multicast_announce(const mcpacket_t *multicastPacket, const int i) { + listen_socket_t *socket = &multicast_socket[i]; + if (socket->sa.sa.sa_family == AF_INET) { + if (sendto(socket->udp, (char *) multicastPacket, sizeof(mcpacket_t), 0, (struct sockaddr *) &multicastAddr4, sizeof(multicastAddr4)) < 0 + && !sockwouldblock(sockerrno)) { + ifdebug(PROTOCOL) + logger(LOG_WARNING, "Could not announce ipv4 endpoint: %d %s", sockerrno, sockstrerror(sockerrno)); + close_multicast_socket(i); + return; + } + } else if (socket->sa.sa.sa_family == AF_INET6) { + if (sendto(socket->udp, (char *) multicastPacket, sizeof(mcpacket_t), 0, (struct sockaddr *) &multicastAddr6, sizeof(multicastAddr6)) < 0 + && !sockwouldblock(sockerrno)) { + ifdebug(PROTOCOL) + logger(LOG_WARNING, "Could not announce ipv6 endpoint: %d %s", sockerrno, sockstrerror(sockerrno)); + close_multicast_socket(i); + return; + } + + } else { + // Unknown multicast family + } +} + +void receive_multicast_announce(const int fd, const mcpacket_t *packet, const node_t *n, const sockaddr_t *sa) { + send_multicast_response(fd, packet, n, sa); +} diff --git a/src/multicast_response.c b/src/multicast_response.c new file mode 100644 index 0000000..a8fd8d9 --- /dev/null +++ b/src/multicast_response.c @@ -0,0 +1,57 @@ +/* + * multicast_response.c + * + * Created on: 05.07.2010 + * Author: schalli + */ + + #include "system.h" + +#include "multicast.h" +#include "netutl.h" + +void send_multicast_response(const int fd, const mcpacket_t *packet, const node_t *n, const sockaddr_t *sa) { + mcpacket_t response = *packet; + strncpy((char*) &response.hostname, myself->name, sizeof(response.hostname)); + response.type = MCPKT_RESPONSE; + response.response.port = htons(atoi(myport)); + sockaddr_t to = *sa; + to.in.sin_port = htons(multicastPort); + + // sign packet + set_signature(n, &response); + + // send + if (sendto(fd, (char *) &response, sizeof(mcpacket_t), 0, &to.sa, SALEN(to.sa)) < 0 && !sockwouldblock(sockerrno)) { + char *address, *port; + sockaddr2str(sa, &address, &port); + ifdebug(PROTOCOL) + logger(LOG_ERR, "Error sending packet to %s (%s:%s): %s", n->name, address, port, sockstrerror(sockerrno)); + free(address); + free(port); + } +} + +void receive_multicast_response(node_t *n, const sockaddr_t *sa, const mcpacket_t *packet) { + struct timeval now; + gettimeofday(&now, NULL); + + /* latency = response time in ns */ + long latency = (now.tv_sec - mc_timestamp.tv_sec) * 1000; + latency += (now.tv_usec - mc_timestamp.tv_usec) / 1000; + latency -= (now.tv_usec < mc_timestamp.tv_usec) ? 1000 : 0; + latency = (latency > 0) ? latency : 0; + + /* + char *address; + int port = ntohs(packet->response.port); + sockaddr2str(sa, &address, NULL); + printf("Got local address for %s, %s%s%i, latency %i\n", n->name, address, ":", port, latency); + free(address); + */ + + sockaddr_t newAddress = *sa; + newAddress.in.sin_port = packet->response.port; + update_node_udp_local(n, &newAddress, time(NULL), latency); + //update_node_udp(n, &newAddress); +} diff --git a/src/net.c b/src/net.c index ee58ac0..c2d810d 100644 --- a/src/net.c +++ b/src/net.c @@ -32,6 +32,7 @@ #include "graph.h" #include "logger.h" #include "meta.h" +#include "multicast.h" #include "net.h" #include "netutl.h" #include "process.h" @@ -44,8 +45,6 @@ bool do_purge = false; volatile bool running = false; time_t now = 0; -int contradicting_add_edge = 0; -int contradicting_del_edge = 0; /* Purge edges and subnets of unreachable nodes. Use carefully. */ @@ -149,7 +148,15 @@ static int build_fdset(fd_set *readset, fd_set *writeset) { FD_SET(device_fd, readset); if(device_fd > max) max = device_fd; - + + for(i = 0; i < multicast_sockets; i++) { + if(multicast_socket[i].udp < 0) + continue; + FD_SET(multicast_socket[i].udp, readset); + if(multicast_socket[i].udp > max) + max = multicast_socket[i].udp; + } + return max; } @@ -334,6 +341,12 @@ static void check_network_activity(fd_set * readset, fd_set * writeset) { if(FD_ISSET(listen_socket[i].tcp, readset)) handle_new_meta_connection(listen_socket[i].tcp); } + + for(i = 0; i < multicast_sockets; i++) { + if(FD_ISSET(multicast_socket[i].udp, readset)) { + handle_multicast_data(multicast_socket[i].udp); + } + } } /* @@ -343,13 +356,15 @@ int main_loop(void) { fd_set readset, writeset; struct timeval tv; int r, maxfd; - time_t last_ping_check, last_config_check, last_graph_dump; + time_t last_ping_check, last_config_check, last_graph_dump, last_endpoint_announce, last_endpoint_bind; event_t *event; last_ping_check = now; last_config_check = now; last_graph_dump = now; - + last_endpoint_announce = now - LOCAL_ANNOUNCE_INTERVAL + 1; + last_endpoint_bind = 0; + srand(now); running = true; @@ -417,19 +432,6 @@ int main_loop(void) { send_key_changed(broadcast, myself); keyexpires = now + keylifetime; } - - if(contradicting_del_edge && contradicting_add_edge) { - logger(LOG_WARNING, "Possible node with same Name as us!"); - - if(rand() % 3 == 0) { - logger(LOG_ERR, "Shutting down, check configuration of all nodes for duplicate Names!"); - running = false; - break; - } - - contradicting_add_edge = 0; - contradicting_del_edge = 0; - } } if(sigalrm) { @@ -453,9 +455,9 @@ int main_loop(void) { avl_node_t *node, *next; char *fname; struct stat s; - + sighup = false; - + /* Reread our own configuration file */ exit_configuration(&config_tree); @@ -492,10 +494,10 @@ int main_loop(void) { list_delete_list(outgoing_list); /* Close connections to hosts that have a changed or deleted host config file */ - + for(node = connection_tree->head; node; node = node->next) { c = node->data; - + xasprintf(&fname, "%s/hosts/%s", confbase, c->name); if(stat(fname, &s) || s.st_mtime > last_config_check) terminate_connection(c, c->status.active); @@ -535,16 +537,28 @@ int main_loop(void) { } /* Try to make outgoing connections */ - + try_outgoing_connections(); } - + /* Dump graph if wanted every 60 seconds*/ if(last_graph_dump + 60 < now) { dump_graph(); last_graph_dump = now; } + + /* re-bind multicast interfaces, in case of network changes */ + if(last_endpoint_bind + LOCAL_BIND_INTERVAL < now) { + close_multicast_sockets(); + bind_multicast_sockets(); + last_endpoint_bind = now; + } + /* Announce local endpoints if wanted */ + if(last_endpoint_announce + LOCAL_ANNOUNCE_INTERVAL < now) { + send_multicast_announces(); + last_endpoint_announce = now; + } } return 0; diff --git a/src/net.h b/src/net.h index eae979c..50b5e02 100644 --- a/src/net.h +++ b/src/net.h @@ -70,6 +70,12 @@ typedef union sockaddr_t { #endif } sockaddr_t; +struct sockaddr_t_node { + union sockaddr_t address; + struct sockaddr_t_node* next; +}; +typedef struct sockaddr_t_node sockaddr_t_list; + #ifdef SA_LEN #define SALEN(s) SA_LEN(&s) #else @@ -109,14 +115,15 @@ extern int addressfamily; extern listen_socket_t listen_socket[MAXSOCKETS]; extern int listen_sockets; + extern int keyexpires; extern int keylifetime; extern bool do_prune; extern bool do_purge; extern char *myport; extern time_t now; -extern int contradicting_add_edge; -extern int contradicting_del_edge; +#define LOCAL_ANNOUNCE_INTERVAL 10 +#define LOCAL_BIND_INTERVAL 60 /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */ #include "connection.h" @@ -124,11 +131,13 @@ extern int contradicting_del_edge; extern void retry_outgoing(outgoing_t *); extern void handle_incoming_vpn_data(int); +extern void handle_multicast_data(int); extern void finish_connecting(struct connection_t *); extern void do_outgoing_connection(struct connection_t *); extern bool handle_new_meta_connection(int); extern int setup_listen_socket(const sockaddr_t *); extern int setup_vpn_in_socket(const sockaddr_t *); +extern int setup_multicast_listener(const sockaddr_t *); extern void send_packet(const struct node_t *, vpn_packet_t *); extern void receive_tcppacket(struct connection_t *, char *, int); extern void broadcast_packet(const struct node_t *, vpn_packet_t *); @@ -143,6 +152,8 @@ extern bool read_rsa_public_key(struct connection_t *); extern void send_mtu_probe(struct node_t *); extern void load_all_subnets(); +extern void signature_set(char* data, int dataLen); + #ifndef HAVE_MINGW #define closesocket(s) close(s) #else diff --git a/src/net_packet.c b/src/net_packet.c index cf5fb93..9eeca1f 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -455,9 +455,26 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) { } /* Determine which socket we have to use */ - + sockaddr_t *address; + if(n->status.local) { + if(n->local.timestamp + LOCAL_ANNOUNCE_INTERVAL * 2 > now) { + // use local address + address = &n->local.address; + // printf("Sending to %s via LOCAL\n", n->name); + }else{ + // use public address, because the local address timed out + update_node_udp_local(n, NULL, 0, 0); + //printf("Local address for %s timed out.\n", n->name); + address = &n->address; + // printf("Sending to %s via REMOTE (timeout)\n", n->name); + } + }else{ + // use public address + address = &n->address; + // printf("Sending to %s via REMOTE (not local)\n", n->name); + } for(sock = 0; sock < listen_sockets; sock++) - if(n->address.sa.sa_family == listen_socket[sock].sa.sa.sa_family) + if(address->sa.sa_family == listen_socket[sock].sa.sa.sa_family) break; if(sock >= listen_sockets) @@ -475,7 +492,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) { } #endif - if(sendto(listen_socket[sock].udp, (char *) &inpkt->seqno, inpkt->len, 0, &(n->address.sa), SALEN(n->address.sa)) < 0 && !sockwouldblock(sockerrno)) { + if(sendto(listen_socket[sock].udp, (char *) &inpkt->seqno, inpkt->len, 0, &address->sa, SALEN(address->sa)) < 0 && !sockwouldblock(sockerrno)) { if(sockmsgsize(sockerrno)) { if(n->maxmtu >= origlen) n->maxmtu = origlen - 1; diff --git a/src/net_setup.c b/src/net_setup.c index cb70926..8625ae7 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -510,6 +510,8 @@ bool setup_myself(void) { myself->nexthop = myself; myself->via = myself; myself->status.reachable = true; + myself->status.local = true; + node_add(myself); graph(); diff --git a/src/node.c b/src/node.c index b323dca..104fbe2 100644 --- a/src/node.c +++ b/src/node.c @@ -1,22 +1,22 @@ /* - node.c -- node tree management - Copyright (C) 2001-2009 Guus Sliepen , - 2001-2005 Ivo Timmermans + node.c -- node tree management + Copyright (C) 2001-2009 Guus Sliepen , + 2001-2005 Ivo Timmermans - This program 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 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. + 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, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ #include "system.h" @@ -28,8 +28,9 @@ #include "utils.h" #include "xalloc.h" -avl_tree_t *node_tree; /* Known nodes, sorted by name */ -avl_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */ +avl_tree_t *node_tree; /* Known nodes, sorted by name */ +avl_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */ +avl_tree_t *node_udp_local_tree; /* Known nodes, sorted by local address and port */ node_t *myself; @@ -38,15 +39,23 @@ static int node_compare(const node_t *a, const node_t *b) { } static int node_udp_compare(const node_t *a, const node_t *b) { - return sockaddrcmp(&a->address, &b->address); + return sockaddrcmp(&a->address, &b->address); +} + +static int node_udp_local_compare(const node_t *a, const node_t *b) { + if (!a->status.local || !b->status.local) + return 1; + return sockaddrcmp(&a->local.address, &b->local.address); } void init_nodes(void) { node_tree = avl_alloc_tree((avl_compare_t) node_compare, (avl_action_t) free_node); node_udp_tree = avl_alloc_tree((avl_compare_t) node_udp_compare, NULL); + node_udp_local_tree = avl_alloc_tree((avl_compare_t) node_udp_local_compare, NULL); } void exit_nodes(void) { + avl_delete_tree(node_udp_local_tree); avl_delete_tree(node_udp_tree); avl_delete_tree(node_tree); } @@ -65,16 +74,16 @@ node_t *new_node(void) { } void free_node(node_t *n) { - if(n->inkey) + if (n->inkey) free(n->inkey); - if(n->outkey) + if (n->outkey) free(n->outkey); - if(n->subnet_tree) + if (n->subnet_tree) free_subnet_tree(n->subnet_tree); - if(n->edge_tree) + if (n->edge_tree) free_edge_tree(n->edge_tree); sockaddrfree(&n->address); @@ -82,13 +91,13 @@ void free_node(node_t *n) { EVP_CIPHER_CTX_cleanup(&n->inctx); EVP_CIPHER_CTX_cleanup(&n->outctx); - if(n->mtuevent) + if (n->mtuevent) event_del(n->mtuevent); - - if(n->hostname) + + if (n->hostname) free(n->hostname); - if(n->name) + if (n->name) free(n->name); free(n); @@ -103,13 +112,13 @@ void node_del(node_t *n) { edge_t *e; subnet_t *s; - for(node = n->subnet_tree->head; node; node = next) { + for (node = n->subnet_tree->head; node; node = next) { next = node->next; s = node->data; subnet_del(n, s); } - for(node = n->edge_tree->head; node; node = next) { + for (node = n->edge_tree->head; node; node = next) { next = node->next; e = node->data; edge_del(e); @@ -120,7 +129,7 @@ void node_del(node_t *n) { } node_t *lookup_node(char *name) { - node_t n = {0}; + node_t n = { 0 }; n.name = name; @@ -128,45 +137,104 @@ node_t *lookup_node(char *name) { } node_t *lookup_node_udp(const sockaddr_t *sa) { - node_t n = {0}; + node_t n = { 0 }; n.address = *sa; n.name = NULL; - return avl_search(node_udp_tree, &n); + node_t *result = NULL; + result = avl_search(node_udp_tree, &n); + + if (!result) { + // Search local UDP addresses + n.status.local = true; + n.local.address = *sa; + result = avl_search(node_udp_local_tree, &n); + } + + return result; } void update_node_udp(node_t *n, const sockaddr_t *sa) { avl_delete(node_udp_tree, n); - if(n->hostname) + if (n->hostname) free(n->hostname); - if(sa) { + if (sa) { n->address = *sa; n->hostname = sockaddr2hostname(&n->address); avl_insert(node_udp_tree, n); - ifdebug(PROTOCOL) logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname); + ifdebug(PROTOCOL) + logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname); } else { memset(&n->address, 0, sizeof n->address); n->hostname = 0; - ifdebug(PROTOCOL) logger(LOG_DEBUG, "UDP address of %s cleared", n->name); + ifdebug(PROTOCOL) + logger(LOG_DEBUG, "UDP address of %s cleared", n->name); } } +void update_node_udp_local(node_t *n, const sockaddr_t *sa, const time_t t, const long l) { + avl_delete(node_udp_local_tree, n); + + if (sa) { + bool local = n->status.local; + bool newAddress = local && sockaddrcmp(&n->local.address, sa); + bool timeout = (n->local.timestamp + LOCAL_ANNOUNCE_INTERVAL * 1.5 < t); + bool betterLatency = (l < n->local.latency); + bool addressSet = false; + + if (!local) { + n->local.timestamp = t; + n->local.address = *sa; + n->local.latency = l; + addressSet = true; + } + if (local && !newAddress) { + n->local.timestamp = t; + n->local.address = *sa; + n->local.latency = betterLatency ? l : n->local.latency; + addressSet = true; + } + if (local && newAddress && (betterLatency || timeout)) { + n->local.timestamp = t; + n->local.address = *sa; + n->local.latency = l; + addressSet = true; + } + + if(addressSet) { + ifdebug(PROTOCOL) { + char *address = sockaddr2hostname(sa); + logger(LOG_DEBUG, "Local UDP address of %s set to %s", n->name, address); + free(address); + } + } + avl_insert(node_udp_local_tree, n); + } else { + memset(&n->local.address, 0, sizeof n->local.address); + n->local.timestamp = 0; + n->local.latency = LONG_MAX; + ifdebug(PROTOCOL) + logger(LOG_DEBUG, "Local UDP address of %s cleared", n->name); + } + + n->status.local = (sa != NULL); +} + void dump_nodes(void) { avl_node_t *node; node_t *n; logger(LOG_DEBUG, "Nodes:"); - for(node = node_tree->head; node; node = node->next) { + for (node = node_tree->head; node; node = node->next) { n = node->data; logger(LOG_DEBUG, " %s at %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s pmtu %d (min %d max %d)", - n->name, n->hostname, n->outcipher ? n->outcipher->nid : 0, - n->outdigest ? n->outdigest->type : 0, n->outmaclength, n->outcompression, - n->options, bitfield_to_int(&n->status, sizeof n->status), n->nexthop ? n->nexthop->name : "-", - n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu); + n->name, n->hostname, n->outcipher ? n->outcipher->nid : 0, n->outdigest ? n->outdigest->type : 0, n->outmaclength, n->outcompression, + n->options, bitfield_to_int(&n->status, sizeof n->status), n->nexthop ? n->nexthop->name : "-", n->via ? n->via->name : "-", n->mtu, n->minmtu, + n->maxmtu); } logger(LOG_DEBUG, "End of nodes."); diff --git a/src/node.h b/src/node.h index 83e89c7..125fe52 100644 --- a/src/node.h +++ b/src/node.h @@ -33,15 +33,23 @@ typedef struct node_status_t { int unused_waitingforkey:1; /* 1 if we already sent out a request */ int visited:1; /* 1 if this node has been visited by one of the graph algorithms */ int reachable:1; /* 1 if this node is reachable in the graph */ + int local:1; /* 1 if this node is reachable via LAN */ int indirect:1; /* 1 if this node is not directly reachable by us */ - int unused:26; + int unused:25; } node_status_t; typedef struct node_t { - char *name; /* name of this node */ + char *name; /* name of this node */ uint32_t options; /* options turned on for this node */ sockaddr_t address; /* his real (internet) ip to send UDP packets to */ + + struct { + sockaddr_t address; /* LAN address */ + long latency; /* latency over LAN link */ + long timestamp; /* last time, a response was received over LAN link */ + } local; + char *hostname; /* the hostname of its real ip */ node_status_t status; @@ -51,12 +59,12 @@ typedef struct node_t { char *inkey; /* Cipher key and iv */ int inkeylength; /* Cipher key and iv length */ EVP_CIPHER_CTX inctx; /* Cipher context */ - + const EVP_CIPHER *outcipher; /* Cipher type for UDP packets sent to him*/ char *outkey; /* Cipher key and iv */ int outkeylength; /* Cipher key and iv length */ EVP_CIPHER_CTX outctx; /* Cipher context */ - + const EVP_MD *indigest; /* Digest type for MAC of packets received from him */ int inmaclength; /* Length of MAC */ @@ -89,6 +97,7 @@ typedef struct node_t { extern struct node_t *myself; extern avl_tree_t *node_tree; extern avl_tree_t *node_udp_tree; +extern avl_tree_t *node_udp_local_tree; extern void init_nodes(void); extern void exit_nodes(void); @@ -99,6 +108,7 @@ extern void node_del(node_t *); extern node_t *lookup_node(char *); extern node_t *lookup_node_udp(const sockaddr_t *); extern void update_node_udp(node_t *, const sockaddr_t *); +extern void update_node_udp_local(node_t *, const sockaddr_t *, const time_t, const long); extern void dump_nodes(void); #endif /* __TINC_NODE_H__ */ diff --git a/src/tincd.c b/src/tincd.c index 70aa6ba..1fbdfdd 100644 --- a/src/tincd.c +++ b/src/tincd.c @@ -53,6 +53,7 @@ #include "conf.h" #include "device.h" #include "logger.h" +#include "multicast.h" #include "net.h" #include "netutl.h" #include "process.h" @@ -345,12 +346,12 @@ static bool keygen(int bits) { if(disable_old_keys(f)) fprintf(stderr, "Warning: old key(s) found and disabled.\n"); - + #ifdef HAVE_FCHMOD /* Make it unreadable for others. */ fchmod(fileno(f), 0600); #endif - + PEM_write_RSAPrivateKey(f, rsa_key, NULL, NULL, 0, NULL, NULL); fclose(f); free(filename); @@ -497,7 +498,7 @@ int main(int argc, char **argv) { if(!parse_options(argc, argv)) return 1; - + make_names(); if(show_version) { @@ -623,8 +624,10 @@ int main2(int argc, char **argv) { if (!drop_privs()) goto end; - /* Start main loop. It only exits when tinc is killed. */ + /* Initialize endpoint structures */ + init_multicast(); + /* Start main loop. It only exits when tinc is killed. */ status = main_loop(); /* Shutdown properly. */