From c87f73a07df468eccedbe1fdfa82bdd5b633a0d5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 22 Nov 2018 22:04:48 +0100 Subject: move network type logic out of ATS code, move performance preferences into MQ --- src/nt/nt.c | 410 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 410 insertions(+) create mode 100644 src/nt/nt.c (limited to 'src/nt/nt.c') diff --git a/src/nt/nt.c b/src/nt/nt.c new file mode 100644 index 000000000..5764f8f4a --- /dev/null +++ b/src/nt/nt.c @@ -0,0 +1,410 @@ +/* + This file is part of GNUnet. + Copyright (C) 2010-2015, 2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +/** + * @file nt/nt_api_scanner.c + * @brief LAN interface scanning to determine IPs in LAN + * @author Christian Grothoff + * @author Matthias Wachs + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_nt_lib.h" + +/** + * How frequently do we scan the interfaces for changes to the addresses? + */ +#define INTERFACE_PROCESSING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2) + + +/** + * Convert a `enum GNUNET_NetworkType` to a string + * + * @param net the network type + * @return a string or NULL if invalid + */ +const char * +GNUNET_NT_to_string (enum GNUNET_NetworkType net) +{ + switch (net) + { + case GNUNET_NT_UNSPECIFIED: + return "UNSPECIFIED"; + case GNUNET_NT_LOOPBACK: + return "LOOPBACK"; + case GNUNET_NT_LAN: + return "LAN"; + case GNUNET_NT_WAN: + return "WAN"; + case GNUNET_NT_WLAN: + return "WLAN"; + case GNUNET_NT_BT: + return "BLUETOOTH"; + default: + return NULL; + } +} + + +/** + * We keep a list of our local networks so we can answer + * LAN vs. WAN questions. Note: WLAN is not detected yet. + * (maybe we can do that heuristically based on interface + * name in the future?). + */ +struct NT_Network +{ + /** + * Kept in a DLL. + */ + struct NT_Network *next; + + /** + * Kept in a DLL. + */ + struct NT_Network *prev; + + /** + * Network address. + */ + struct sockaddr *network; + + /** + * Netmask to determine what is in the LAN. + */ + struct sockaddr *netmask; + + /** + * How long are @e network and @e netmask? + */ + socklen_t length; +}; + + +/** + * Handle to the interface scanner. + */ +struct GNUNET_NT_InterfaceScanner +{ + + /** + * Head of LAN networks list. + */ + struct NT_Network *net_head; + + /** + * Tail of LAN networks list. + */ + struct NT_Network *net_tail; + + /** + * Task for periodically refreshing our LAN network list. + */ + struct GNUNET_SCHEDULER_Task *interface_task; + +}; + + +/** + * Delete all entries from the current network list. + * + * @param is scanner to clean up + */ +static void +delete_networks (struct GNUNET_NT_InterfaceScanner *is) +{ + struct NT_Network *cur; + + while (NULL != (cur = is->net_head)) + { + GNUNET_CONTAINER_DLL_remove (is->net_head, + is->net_tail, + cur); + GNUNET_free (cur); + } +} + + +/** + * Function invoked for each interface found. Adds the interface's + * network addresses to the respective DLL, so we can distinguish + * between LAN and WAN. + * + * @param cls closure with the `struct GNUNET_NT_InterfaceScanner` + * @param name name of the interface (can be NULL for unknown) + * @param isDefault is this presumably the default interface + * @param addr address of this interface (can be NULL for unknown or unassigned) + * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned) + * @param netmask the network mask (can be NULL for unknown or unassigned) + * @param addrlen length of the address + * @return #GNUNET_OK to continue iteration + */ +static int +interface_proc (void *cls, + const char *name, + int isDefault, + const struct sockaddr *addr, + const struct sockaddr *broadcast_addr, + const struct sockaddr *netmask, + socklen_t addrlen) +{ + struct GNUNET_NT_InterfaceScanner *is = cls; + /* Calculate network */ + struct NT_Network *net = NULL; + + /* Skipping IPv4 loopback addresses since we have special check */ + if (addr->sa_family == AF_INET) + { + const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr; + + if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000)) + return GNUNET_OK; + } + /* Skipping IPv6 loopback addresses since we have special check */ + if (addr->sa_family == AF_INET6) + { + const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr; + if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr)) + return GNUNET_OK; + } + + if (addr->sa_family == AF_INET) + { + const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr; + const struct sockaddr_in *netmask4 = (const struct sockaddr_in *) netmask; + struct sockaddr_in *tmp; + struct sockaddr_in network4; + + net = GNUNET_malloc (sizeof (struct NT_Network) + 2 * sizeof (struct sockaddr_in)); + tmp = (struct sockaddr_in *) &net[1]; + net->network = (struct sockaddr *) &tmp[0]; + net->netmask = (struct sockaddr *) &tmp[1]; + net->length = addrlen; + + memset (&network4, 0, sizeof (network4)); + network4.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + network4.sin_len = sizeof (network4); +#endif + network4.sin_addr.s_addr = (addr4->sin_addr.s_addr & netmask4->sin_addr.s_addr); + + GNUNET_memcpy (net->netmask, netmask4, sizeof (struct sockaddr_in)); + GNUNET_memcpy (net->network, &network4, sizeof (struct sockaddr_in)); + } + + if (addr->sa_family == AF_INET6) + { + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr; + const struct sockaddr_in6 *netmask6 = (const struct sockaddr_in6 *) netmask; + struct sockaddr_in6 * tmp; + struct sockaddr_in6 network6; + + net = GNUNET_malloc (sizeof (struct NT_Network) + 2 * sizeof (struct sockaddr_in6)); + tmp = (struct sockaddr_in6 *) &net[1]; + net->network = (struct sockaddr *) &tmp[0]; + net->netmask = (struct sockaddr *) &tmp[1]; + net->length = addrlen; + + memset (&network6, 0, sizeof (network6)); + network6.sin6_family = AF_INET6; +#if HAVE_SOCKADDR_IN_SIN_LEN + network6.sin6_len = sizeof (network6); +#endif + unsigned int c = 0; + uint32_t *addr_elem = (uint32_t *) &addr6->sin6_addr; + uint32_t *mask_elem = (uint32_t *) &netmask6->sin6_addr; + uint32_t *net_elem = (uint32_t *) &network6.sin6_addr; + for (c = 0; c < 4; c++) + net_elem[c] = addr_elem[c] & mask_elem[c]; + + GNUNET_memcpy (net->netmask, netmask6, sizeof (struct sockaddr_in6)); + GNUNET_memcpy (net->network, &network6, sizeof (struct sockaddr_in6)); + } + if (NULL == net) + return GNUNET_OK; /* odd / unsupported address family */ + + /* Store in list */ +#if VERBOSE_NT + char * netmask = GNUNET_strdup (GNUNET_a2s((struct sockaddr *) net->netmask, addrlen)); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "nt", + "Adding network `%s', netmask `%s'\n", + GNUNET_a2s ((struct sockaddr *) net->network, + addrlen), + netmask); + GNUNET_free (netmask); +#endif + GNUNET_CONTAINER_DLL_insert (is->net_head, + is->net_tail, + net); + + return GNUNET_OK; +} + + +/** + * Periodically get list of network addresses from our interfaces. + * + * @param cls closure + */ +static void +get_addresses (void *cls) +{ + struct GNUNET_NT_InterfaceScanner *is = cls; + + is->interface_task = NULL; + delete_networks (is); + GNUNET_OS_network_interfaces_list (&interface_proc, + is); + is->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVAL, + &get_addresses, + is); +} + + +/** + * Returns where the address is located: LAN or WAN or ... + * + * @param is the interface scanner handle + * @param addr address + * @param addrlen address length + * @return type of the network the address belongs to + */ +enum GNUNET_NetworkType +GNUNET_NT_scanner_address_get_type (struct GNUNET_NT_InterfaceScanner *is, + const struct sockaddr *addr, + socklen_t addrlen) +{ + struct NT_Network *cur = is->net_head; + enum GNUNET_NetworkType type = GNUNET_NT_UNSPECIFIED; + + switch (addr->sa_family) + { + case AF_UNIX: + type = GNUNET_NT_LOOPBACK; + break; + case AF_INET: + { + const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr; + + if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000)) + type = GNUNET_NT_LOOPBACK; + break; + } + case AF_INET6: + { + const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr; + + if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr)) + type = GNUNET_NT_LOOPBACK; + break; + } + default: + GNUNET_break (0); + break; + } + + /* Check local networks */ + while ((NULL != cur) && (GNUNET_NT_UNSPECIFIED == type)) + { + if (addrlen != cur->length) + { + cur = cur->next; + continue; + } + if (addr->sa_family == AF_INET) + { + const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr; + const struct sockaddr_in *net4 = (const struct sockaddr_in *) cur->network; + const struct sockaddr_in *mask4 = (const struct sockaddr_in *) cur->netmask; + + if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) == net4->sin_addr.s_addr) + type = GNUNET_NT_LAN; + } + if (addr->sa_family == AF_INET6) + { + const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr; + const struct sockaddr_in6 *net6 = (const struct sockaddr_in6 *) cur->network; + const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *) cur->netmask; + + int res = GNUNET_YES; + int c = 0; + uint32_t *addr_elem = (uint32_t *) &a6->sin6_addr; + uint32_t *mask_elem = (uint32_t *) &mask6->sin6_addr; + uint32_t *net_elem = (uint32_t *) &net6->sin6_addr; + for (c = 0; c < 4; c++) + if ((addr_elem[c] & mask_elem[c]) != net_elem[c]) + res = GNUNET_NO; + + if (res == GNUNET_YES) + type = GNUNET_NT_LAN; + } + cur = cur->next; + } + + /* no local network found for this address, default: WAN */ + if (type == GNUNET_NT_UNSPECIFIED) + type = GNUNET_NT_WAN; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "nt-scanner-api", + "`%s' is in network `%s'\n", + GNUNET_a2s (addr, + addrlen), + GNUNET_NT_to_string (type)); + return type; +} + + +/** + * Initialize the interface scanner. + * + * @return interface scanner + */ +struct GNUNET_NT_InterfaceScanner * +GNUNET_NT_scanner_init () +{ + struct GNUNET_NT_InterfaceScanner *is; + + is = GNUNET_new (struct GNUNET_NT_InterfaceScanner); + GNUNET_OS_network_interfaces_list (&interface_proc, + is); + is->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVAL, + &get_addresses, + is); + return is; +} + + +/** + * Client is done with the interface scanner, release resources. + * + * @param is handle to release + */ +void +GNUNET_NT_scanner_done (struct GNUNET_NT_InterfaceScanner *is) +{ + if (NULL != is->interface_task) + { + GNUNET_SCHEDULER_cancel (is->interface_task); + is->interface_task = NULL; + } + delete_networks (is); + GNUNET_free (is); +} + + +/* end of nt.c */ -- cgit v1.2.3