conf.c conf.h \
connection.c connection.h \
crypto.c crypto.h \
- discovery.c discovery.h \
dropin.c dropin.h \
ecdh.h \
ecdsa.h \
ecdsagen.h \
- edge.c edge.h \
event.c event.h \
- graph.c graph.h \
hash.c hash.h \
have.h \
list.c list.h \
logger.c logger.h \
- mdns.c mdns.h \
meshlink.c meshlink-tiny.h meshlink.sym \
meshlink_internal.h \
meshlink_queue.h \
} connection_status_t;
#include "ecdsa.h"
-#include "edge.h"
#include "net.h"
#include "node.h"
#include "submesh.h"
struct outgoing_t *outgoing; /* used to keep track of outgoing connections */
- struct edge_t *edge; /* edge associated with this connection */
struct submesh_t *submesh; /* his submesh handle if available in invitation file */
// Only used during authentication
void (*devtool_adns_resolve_probe)(void) = nop_probe;
void (*devtool_sptps_renewal_probe)(meshlink_node_t *node) = sptps_renewal_nop_probe;
-/* Return an array of edges in the current network graph.
- * Data captures the current state and will not be updated.
- * Caller must deallocate data when done.
- */
-devtool_edge_t *devtool_get_all_edges(meshlink_handle_t *mesh, devtool_edge_t *edges, size_t *nmemb) {
- if(!mesh || !nmemb || (*nmemb && !edges)) {
- meshlink_errno = MESHLINK_EINVAL;
- return NULL;
- }
-
- if(pthread_mutex_lock(&mesh->mutex) != 0) {
- abort();
- }
-
- devtool_edge_t *result = NULL;
- unsigned int result_size = 0;
-
- result_size = mesh->edges->count / 2;
-
- // if result is smaller than edges, we have to dealloc all the excess devtool_edge_t
- if((size_t)result_size > *nmemb) {
- result = xrealloc(edges, result_size * sizeof(*result));
- } else {
- result = edges;
- }
-
- if(result) {
- devtool_edge_t *p = result;
- unsigned int n = 0;
-
- for splay_each(edge_t, e, mesh->edges) {
- // skip edges that do not represent a two-directional connection
- if(!e->reverse || e->reverse->to != e->from) {
- continue;
- }
-
- // don't count edges twice
- if(e->to < e->from) {
- continue;
- }
-
- assert(n < result_size);
-
- p->from = (meshlink_node_t *)e->from;
- p->to = (meshlink_node_t *)e->to;
- p->address = e->address.storage;
- p->weight = e->weight;
-
- n++;
- p++;
- }
-
- // shrink result to the actual amount of memory used
- result = xrealloc(result, n * sizeof(*result));
- *nmemb = n;
- } else {
- *nmemb = 0;
- meshlink_errno = MESHLINK_ENOMEM;
- }
-
- pthread_mutex_unlock(&mesh->mutex);
-
- return result;
-}
-
-static bool fstrwrite(const char *str, FILE *stream) {
- assert(stream);
-
- size_t len = strlen(str);
-
- if(fwrite((void *)str, 1, len, stream) != len) {
- return false;
- }
-
- return true;
-}
-
-bool devtool_export_json_all_edges_state(meshlink_handle_t *mesh, FILE *stream) {
- assert(stream);
-
- bool result = true;
-
- if(pthread_mutex_lock(&mesh->mutex) != 0) {
- abort();
- }
-
- // export edges and nodes
- size_t node_count = 0;
- size_t edge_count = 0;
-
- meshlink_node_t **nodes = meshlink_get_all_nodes(mesh, NULL, &node_count);
- devtool_edge_t *edges = devtool_get_all_edges(mesh, NULL, &edge_count);
-
- if((!nodes && node_count != 0) || (!edges && edge_count != 0)) {
- goto fail;
- }
-
- // export begin
- if(!fstrwrite("{\n", stream)) {
- goto fail;
- }
-
- // export nodes
- if(!fstrwrite("\t\"nodes\": {\n", stream)) {
- goto fail;
- }
-
- char buf[16];
-
- for(size_t i = 0; i < node_count; ++i) {
- if(!fstrwrite("\t\t\"", stream) || !fstrwrite(((node_t *)nodes[i])->name, stream) || !fstrwrite("\": {\n", stream)) {
- goto fail;
- }
-
- if(!fstrwrite("\t\t\t\"name\": \"", stream) || !fstrwrite(((node_t *)nodes[i])->name, stream) || !fstrwrite("\",\n", stream)) {
- goto fail;
- }
-
- snprintf(buf, sizeof(buf), "%d", ((node_t *)nodes[i])->devclass);
-
- if(!fstrwrite("\t\t\t\"devclass\": ", stream) || !fstrwrite(buf, stream) || !fstrwrite("\n", stream)) {
- goto fail;
- }
-
- if(!fstrwrite((i + 1) != node_count ? "\t\t},\n" : "\t\t}\n", stream)) {
- goto fail;
- }
- }
-
- if(!fstrwrite("\t},\n", stream)) {
- goto fail;
- }
-
- // export edges
-
- if(!fstrwrite("\t\"edges\": {\n", stream)) {
- goto fail;
- }
-
- for(size_t i = 0; i < edge_count; ++i) {
- if(!fstrwrite("\t\t\"", stream) || !fstrwrite(edges[i].from->name, stream) || !fstrwrite("_to_", stream) || !fstrwrite(edges[i].to->name, stream) || !fstrwrite("\": {\n", stream)) {
- goto fail;
- }
-
- if(!fstrwrite("\t\t\t\"from\": \"", stream) || !fstrwrite(edges[i].from->name, stream) || !fstrwrite("\",\n", stream)) {
- goto fail;
- }
-
- if(!fstrwrite("\t\t\t\"to\": \"", stream) || !fstrwrite(edges[i].to->name, stream) || !fstrwrite("\",\n", stream)) {
- goto fail;
- }
-
- char *host = NULL, *port = NULL, *address = NULL;
- sockaddr2str((const sockaddr_t *)&edges[i].address, &host, &port);
-
- if(host && port) {
- xasprintf(&address, "{ \"host\": \"%s\", \"port\": %s }", host, port);
- }
-
- free(host);
- free(port);
-
- if(!fstrwrite("\t\t\t\"address\": ", stream) || !fstrwrite(address ? address : "null", stream) || !fstrwrite(",\n", stream)) {
- free(address);
- goto fail;
- }
-
- free(address);
-
- snprintf(buf, sizeof(buf), "%d", edges[i].weight);
-
- if(!fstrwrite("\t\t\t\"weight\": ", stream) || !fstrwrite(buf, stream) || !fstrwrite("\n", stream)) {
- goto fail;
- }
-
- if(!fstrwrite((i + 1) != edge_count ? "\t\t},\n" : "\t\t}\n", stream)) {
- goto fail;
- }
- }
-
- if(!fstrwrite("\t}\n", stream)) {
- goto fail;
- }
-
- // DONE!
-
- if(!fstrwrite("}", stream)) {
- goto fail;
- }
-
- goto done;
-
-fail:
- result = false;
-
-done:
- free(nodes);
- free(edges);
-
- pthread_mutex_unlock(&mesh->mutex);
-
- return result;
-}
-
void devtool_get_node_status(meshlink_handle_t *mesh, meshlink_node_t *node, devtool_node_status_t *status) {
if(!mesh || !node || !status) {
meshlink_errno = MESHLINK_EINVAL;
* Applications should not depend on any of these functions for their normal operation.
*/
-/// An edge in the MeshLink network.
-typedef struct devtool_edge devtool_edge_t;
-
-/// An edge in the MeshLink network.
-struct devtool_edge {
- struct meshlink_node *from; ///< Pointer to a node. Node memory is
- // owned by meshlink and should not be
- // deallocated. Node contents may be
- // changed by meshlink.
- struct meshlink_node *to; ///< Pointer to a node. Node memory is
- // owned by meshlink and should not be
- // deallocated. Node contents may be
- // changed by meshlink.
- struct sockaddr_storage address;///< The address information associated
- // with this edge.
- int weight; ///< Weight assigned to this edge.
-};
-
-/// Get a list of edges.
-/** This function returns an array with copies of all known bidirectional edges.
- * The edges are copied to capture the mesh state at call time, since edges
- * mutate frequently. The nodes pointed to within the devtool_edge_t type
- * are not copies; these are the same pointers that one would get from a call
- * to meshlink_get_all_nodes().
- *
- * @param mesh A handle which represents an instance of MeshLink.
- * @param edges A pointer to a previously allocated array of
- * devtool_edge_t, or NULL in which case MeshLink will
- * allocate a new array.
- * The application is allowed to call free() on the array whenever it wishes.
- * The pointers in the devtool_edge_t elements are valid until
- * meshlink_close() is called.
- * @param nmemb A pointer to a variable holding the number of elements that
- * are stored in the array. In case the @a edges @a
- * argument is not NULL, MeshLink might call realloc()
- * on the array to change its size.
- * The contents of this variable will be changed to reflect
- * the new size of the array.
- * @return A pointer to an array containing devtool_edge_t elements,
- * or NULL in case of an error.
- * If the @a edges @a argument was not NULL, then the
- * return value can be either the same value or a different
- * value. If the new values is NULL, then the old array
- * will have been freed by Meshlink.
- */
-devtool_edge_t *devtool_get_all_edges(meshlink_handle_t *mesh, devtool_edge_t *edges, size_t *nmemb);
-
-/// Export a list of edges to a file in JSON format.
-/* @param mesh A handle which represents an instance of MeshLink.
- * @param FILE An open file descriptor to which a JSON representation of the edges will be written.
- *
- * @return True in case of success, false otherwise.
- */
-bool devtool_export_json_all_edges_state(meshlink_handle_t *mesh, FILE *stream);
-
/// The status of a node.
typedef struct devtool_node_status devtool_node_status_t;
+++ /dev/null
-/*
- discovery.c -- local network discovery
- Copyright (C) 2014-2021 Guus Sliepen <guus@meshlink.io>
-
- 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.
-
- 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.
-*/
-
-#define __APPLE_USE_RFC_3542
-#include "system.h"
-
-#if defined(__APPLE__)
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreFoundation/CFArray.h>
-#include <CoreFoundation/CFString.h>
-#include <SystemConfiguration/SystemConfiguration.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#elif defined(__unix) && !defined(__linux)
-#include <net/if.h>
-#include <net/route.h>
-#include <netinet/in.h>
-#elif defined(__linux)
-#include <asm/types.h>
-#include <net/if.h>
-#include <linux/if_link.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-#endif
-
-#include "mdns.h"
-#include "meshlink_internal.h"
-#include "event.h"
-#include "discovery.h"
-#include "sockaddr.h"
-#include "logger.h"
-#include "netutl.h"
-#include "node.h"
-#include "connection.h"
-#include "utils.h"
-#include "xalloc.h"
-
-#define MESHLINK_MDNS_SERVICE_TYPE "_%s._tcp"
-#define MESHLINK_MDNS_NAME_KEY "name"
-#define MESHLINK_MDNS_FINGERPRINT_KEY "fingerprint"
-
-#ifndef MSG_NOSIGNAL
-#define MSG_NOSIGNAL 0
-#endif
-
-#ifndef IPV6_ADD_MEMBERSHIP
-#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
-#endif
-
-#ifndef IPV6_DROP_MEMBERSHIP
-#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
-#endif
-
-static const sockaddr_t mdns_address_ipv4 = {
- .in.sin_family = AF_INET,
- .in.sin_addr.s_addr = 0xfb0000e0,
- .in.sin_port = 0xe914,
-};
-
-static const sockaddr_t mdns_address_ipv6 = {
- .in6.sin6_family = AF_INET6,
- .in6.sin6_addr.s6_addr[0x0] = 0xff,
- .in6.sin6_addr.s6_addr[0x1] = 0x02,
- .in6.sin6_addr.s6_addr[0xf] = 0xfb,
- .in6.sin6_port = 0xe914,
-};
-
-typedef struct discovery_address {
- int index;
- bool up;
- sockaddr_t address;
- time_t last_response_sent;
-} discovery_address_t;
-
-static int iface_compare(const void *va, const void *vb) {
- const int *a = va;
- const int *b = vb;
- return *a - *b;
-}
-
-static int address_compare(const void *va, const void *vb) {
- const discovery_address_t *a = va;
- const discovery_address_t *b = vb;
-
- if(a->index != b->index) {
- return a->index - b->index;
- }
-
- return sockaddrcmp_noport(&a->address, &b->address);
-}
-
-static void send_mdns_packet_ipv4(meshlink_handle_t *mesh, int fd, int index, const sockaddr_t *src, const sockaddr_t *dest, void *data, size_t len) {
-#ifdef IP_MULTICAST_IF
- struct ip_mreqn mreq = {
- .imr_address = src->in.sin_addr,
- .imr_ifindex = index,
- };
-
- if(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) != 0) {
- logger(mesh, MESHLINK_ERROR, "Could not set outgoing multicast interface on IPv4 socket");
- return;
- }
-
-#endif
-
-#ifdef IP_PKTINFO
- struct iovec iov = {
- .iov_base = data,
- .iov_len = len,
- };
-
- struct in_pktinfo pkti = {
- .ipi_ifindex = index,
- .ipi_addr = src->in.sin_addr,
- };
-
- union {
- char buf[CMSG_SPACE(sizeof(pkti))];
- struct cmsghdr align;
- } u;
-
- memset(&u, 0, sizeof(u));
-
- struct msghdr msg = {
- .msg_name = (struct sockaddr *) &dest->sa,
- .msg_namelen = SALEN(dest->sa),
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = u.buf,
- .msg_controllen = sizeof(u.buf),
- };
-
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = IPPROTO_IP;
- cmsg->cmsg_type = IP_PKTINFO;
- cmsg->cmsg_len = CMSG_LEN(sizeof(pkti));
- memcpy(CMSG_DATA(cmsg), &pkti, sizeof(pkti));
-
- // Send the packet
- ssize_t result = sendmsg(fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL);
-#else
- (void)index;
- (void)src;
-
- // Send the packet
- ssize_t result = sendto(fd, data, len, MSG_DONTWAIT | MSG_NOSIGNAL, &dest->sa, SALEN(dest->sa));
-#endif
-
- if(result <= 0) {
- logger(mesh, MESHLINK_ERROR, "Error sending multicast packet: %s", strerror(errno));
- }
-}
-
-static void send_mdns_packet_ipv6(meshlink_handle_t *mesh, int fd, int index, const sockaddr_t *src, const sockaddr_t *dest, void *data, size_t len) {
-#ifdef IPV6_MULTICAST_IF
-
- if(setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index)) != 0) {
- logger(mesh, MESHLINK_ERROR, "Could not set outgoing multicast interface on IPv6 socket");
- return;
- }
-
-#endif
-
-#ifdef IPV6_PKTINFO
- struct iovec iov = {
- .iov_base = data,
- .iov_len = len,
- };
-
- struct in6_pktinfo pkti = {
- .ipi6_ifindex = index,
- .ipi6_addr = src->in6.sin6_addr,
- };
-
- union {
- char buf[CMSG_SPACE(sizeof(pkti))];
- struct cmsghdr align;
- } u;
-
- memset(&u, 0, sizeof u);
-
- struct msghdr msg = {
- .msg_name = (struct sockaddr *) &dest->sa,
- .msg_namelen = SALEN(dest->sa),
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = u.buf,
- .msg_controllen = CMSG_LEN(sizeof(pkti)),
- };
-
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = IPPROTO_IPV6;
- cmsg->cmsg_type = IPV6_PKTINFO;
- cmsg->cmsg_len = CMSG_LEN(sizeof(pkti));
- memcpy(CMSG_DATA(cmsg), &pkti, sizeof(pkti));
-
- // Send the packet
- ssize_t result = sendmsg(fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL);
-#else
- (void)index;
- (void)src;
-
- // Send the packet
- ssize_t result = sendto(fd, data, len, MSG_DONTWAIT | MSG_NOSIGNAL, &dest->sa, SALEN(dest->sa));
-#endif
-
- if(result <= 0) {
- logger(mesh, MESHLINK_ERROR, "Error sending multicast packet: %s", strerror(errno));
- }
-}
-
-static void send_mdns_packet(meshlink_handle_t *mesh, discovery_address_t *addr, bool response) {
- // Configure the socket to send the packet to the right interface
- uint8_t msg[1024];
- size_t msg_size;
-
- if(response) {
- char *fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self);
- static const char *keys[] = {MESHLINK_MDNS_NAME_KEY, MESHLINK_MDNS_FINGERPRINT_KEY};
- const char *values[] = {mesh->name, fingerprint};
- msg_size = prepare_response(msg, sizeof msg, fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, values);
- free(fingerprint);
- addr->last_response_sent = mesh->loop.now.tv_sec;
- } else {
- msg_size = prepare_request(msg, sizeof msg, mesh->appname, "tcp");
- }
-
- switch(addr->address.sa.sa_family) {
- case AF_INET:
- send_mdns_packet_ipv4(mesh, mesh->discovery.sockets[0].fd, addr->index, &addr->address, &mdns_address_ipv4, msg, msg_size);
- break;
-
- case AF_INET6:
- send_mdns_packet_ipv6(mesh, mesh->discovery.sockets[1].fd, addr->index, &addr->address, &mdns_address_ipv6, msg, msg_size);
- break;
-
- default:
- break;
- }
-}
-
-static void mdns_io_handler(event_loop_t *loop, void *data, int flags) {
- (void)flags;
- meshlink_handle_t *mesh = loop->data;
- io_t *io = data;
- uint8_t buf[1024];
- sockaddr_t sa;
- socklen_t sl = sizeof(sa);
-
-#ifdef IP_PKTINFO
- struct iovec iov = {
- .iov_base = buf,
- .iov_len = sizeof(buf),
- };
-
- union {
- char buf[1024];
- struct cmsghdr align;
- } u;
-
- struct msghdr msg = {
- .msg_name = &sa,
- .msg_namelen = sl,
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = u.buf,
- .msg_controllen = sizeof(u.buf),
- };
-
- ssize_t len = recvmsg(io->fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL);
-
- sl = msg.msg_namelen;
-#else
- ssize_t len = recvfrom(io->fd, buf, sizeof(buf), MSG_DONTWAIT, &sa.sa, &sl);
-#endif
-
- if(len == -1) {
- if(!sockwouldblock(errno)) {
- logger(mesh, MESHLINK_ERROR, "Error reading from mDNS discovery socket: %s", strerror(errno));
- io_set(loop, io, 0);
- }
-
- return;
- }
-
- char *name = NULL;
- uint16_t port = 0;
- const char *keys[2] = {MESHLINK_MDNS_NAME_KEY, MESHLINK_MDNS_FINGERPRINT_KEY};
- char *values[2] = {NULL, NULL};
-
- if(parse_response(buf, len, &name, mesh->appname, "tcp", &port, 2, keys, values)) {
- node_t *n = (node_t *)meshlink_get_node(mesh, values[0]);
-
- if(n) {
- if(n != mesh->self) {
- logger(mesh, MESHLINK_INFO, "Node %s discovered on the local network.\n", n->name);
- }
-
- switch(sa.sa.sa_family) {
- case AF_INET:
- sa.in.sin_port = htons(port);
- break;
-
- case AF_INET6:
- sa.in6.sin6_port = htons(port);
- break;
-
- default:
- logger(mesh, MESHLINK_WARNING, "Could not resolve node %s to a known address family type.\n", n->name);
- sa.sa.sa_family = AF_UNKNOWN;
- break;
- }
-
- if(sa.sa.sa_family != AF_UNKNOWN) {
- n->catta_address = sa;
- n->last_connect_try = 0;
- node_add_recent_address(mesh, n, &sa);
-
- if(n->connection) {
- n->connection->last_ping_time = -3600;
- }
-
- for list_each(outgoing_t, outgoing, mesh->outgoings) {
- if(outgoing->node != n) {
- continue;
- }
-
- outgoing->timeout = 0;
-
- if(outgoing->ev.cb) {
- timeout_set(&mesh->loop, &outgoing->ev, &(struct timespec) {
- 0, 0
- });
- }
- }
- }
- }
- } else if(parse_request(buf, len, mesh->appname, "tcp")) {
- char *fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self);
- const char *response_values[] = {mesh->name, fingerprint};
- size_t size = prepare_response(buf, sizeof(buf), fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, response_values);
-
- int index = -1;
- int family = AF_UNSPEC;
-
- for(struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; cmptr = CMSG_NXTHDR(&msg, cmptr)) {
-#ifdef IP_PKTINFO
-
- if(cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) {
- struct in_pktinfo *pktinfo = (struct in_pktinfo *) CMSG_DATA(cmptr);
- index = pktinfo->ipi_ifindex;
- family = AF_INET;
- break;
- } else
-#endif
-#ifdef IPV6_PKTINFO
- if(cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == IPV6_PKTINFO) {
- struct in6_pktinfo *pktinfo = (struct in6_pktinfo *) CMSG_DATA(cmptr);
- index = pktinfo->ipi6_ifindex;
- family = AF_INET6;
- break;
- }
-
-#endif
- }
-
- if(index) {
- // Send a multicast response back using all addresses matching the interface
- for(int i = 0; i < mesh->discovery.address_count; i++) {
- discovery_address_t *p = &mesh->discovery.addresses[i];
-
- if(p->index == index && p->address.sa.sa_family == family) {
- send_mdns_packet(mesh, p, true);
- }
- }
- } else {
- // Send a unicast response back
- sendto(io->fd, buf, size, MSG_DONTWAIT | MSG_NOSIGNAL, &sa.sa, sl);
- }
-
- free(fingerprint);
- }
-
- free(name);
-
- for(int i = 0; i < 2; i++) {
- free(values[i]);
- }
-}
-
-static void iface_up(meshlink_handle_t *mesh, int index) {
- int *p = bsearch(&index, mesh->discovery.ifaces, mesh->discovery.iface_count, sizeof(*p), iface_compare);
-
- if(p) {
- return;
- }
-
- mesh->discovery.ifaces = xrealloc(mesh->discovery.ifaces, ++mesh->discovery.iface_count * sizeof(*p));
- mesh->discovery.ifaces[mesh->discovery.iface_count - 1] = index;
- qsort(mesh->discovery.ifaces, mesh->discovery.iface_count, sizeof(*p), iface_compare);
-
- // Add multicast membership
- struct ip_mreqn mreq4 = {
- .imr_multiaddr = mdns_address_ipv4.in.sin_addr,
- .imr_ifindex = index,
- };
-
- setsockopt(mesh->discovery.sockets[0].fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq4, sizeof(mreq4));
- setsockopt(mesh->discovery.sockets[0].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq4, sizeof(mreq4));
-
- struct ipv6_mreq mreq6 = {
- .ipv6mr_multiaddr = mdns_address_ipv6.in6.sin6_addr,
- .ipv6mr_interface = index,
- };
-
- setsockopt(mesh->discovery.sockets[1].fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
- setsockopt(mesh->discovery.sockets[1].fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
-
- // Send an announcement for all addresses associated with this interface
- for(int i = 0; i < mesh->discovery.address_count; i++) {
- if(mesh->discovery.addresses[i].index == index) {
- send_mdns_packet(mesh, &mesh->discovery.addresses[i], false);
- }
- }
-
- handle_network_change(mesh, true);
-}
-
-static void iface_down(meshlink_handle_t *mesh, int index) {
- int *p = bsearch(&index, mesh->discovery.ifaces, mesh->discovery.iface_count, sizeof(*p), iface_compare);
-
- if(!p) {
- return;
- }
-
- // Drop multicast membership
- struct ip_mreqn mreq4 = {
- .imr_multiaddr = mdns_address_ipv4.in.sin_addr,
- .imr_ifindex = index,
- };
- setsockopt(mesh->discovery.sockets[0].fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq4, sizeof(mreq4));
-
- struct ipv6_mreq mreq6 = {
- .ipv6mr_multiaddr = mdns_address_ipv6.in6.sin6_addr,
- .ipv6mr_interface = index,
- };
- setsockopt(mesh->discovery.sockets[1].fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
-
- memmove(p, p + 1, (mesh->discovery.ifaces + --mesh->discovery.iface_count - p) * sizeof(*p));
-
- handle_network_change(mesh, mesh->discovery.iface_count);
-}
-
-static void addr_add(meshlink_handle_t *mesh, const discovery_address_t *addr) {
- discovery_address_t *p = bsearch(addr, mesh->discovery.addresses, mesh->discovery.address_count, sizeof(*p), address_compare);
-
- if(p) {
- return;
- }
-
- bool up = bsearch(&addr->index, mesh->discovery.ifaces, mesh->discovery.iface_count, sizeof(int), iface_compare);
-
- mesh->discovery.addresses = xrealloc(mesh->discovery.addresses, ++mesh->discovery.address_count * sizeof(*p));
- mesh->discovery.addresses[mesh->discovery.address_count - 1] = *addr;
- mesh->discovery.addresses[mesh->discovery.address_count - 1].up = up;
-
- if(up) {
- send_mdns_packet(mesh, &mesh->discovery.addresses[mesh->discovery.address_count - 1], false);
- }
-
- qsort(mesh->discovery.addresses, mesh->discovery.address_count, sizeof(*p), address_compare);
-}
-
-static void addr_del(meshlink_handle_t *mesh, const discovery_address_t *addr) {
- discovery_address_t *p = bsearch(addr, mesh->discovery.addresses, mesh->discovery.address_count, sizeof(*p), address_compare);
-
- if(!p) {
- return;
- }
-
- memmove(p, p + 1, (mesh->discovery.addresses + --mesh->discovery.address_count - p) * sizeof(*p));
-}
-
-void scan_ifaddrs(meshlink_handle_t *mesh) {
-#ifdef HAVE_GETIFADDRS
- struct ifaddrs *ifa = NULL;
-
- if(getifaddrs(&ifa) == -1) {
- logger(mesh, MESHLINK_ERROR, "Could not get list of interface addresses: %s", strerror(errno));
- return;
- }
-
- // Check for interfaces being removed
- for(int i = 0; i < mesh->discovery.iface_count;) {
- bool found = false;
-
- for(struct ifaddrs *ifap = ifa; ifap; ifap = ifap->ifa_next) {
- if(!ifap->ifa_name) {
- continue;
- }
-
- int index = if_nametoindex(ifap->ifa_name);
-
- if(mesh->discovery.ifaces[i] == index) {
- found = true;
- break;
- }
- }
-
- if(!found) {
- iface_down(mesh, mesh->discovery.ifaces[i]);
- } else {
- i++;
- }
- }
-
- // Check for addresses being removed
- for(int i = 0; i < mesh->discovery.address_count;) {
- discovery_address_t *p = &mesh->discovery.addresses[i];
- bool found = false;
-
- for(struct ifaddrs *ifap = ifa; ifap; ifap = ifap->ifa_next) {
- if(!ifap->ifa_name || !ifap->ifa_addr) {
- continue;
- }
-
- int index = if_nametoindex(ifap->ifa_name);
-
- if(p->index == index && sockaddrcmp_noport(&p->address, (sockaddr_t *)ifap->ifa_addr) == 0) {
- found = true;
- break;
- }
- }
-
- if(!found) {
- (void)addr_del;
- memmove(p, p + 1, (mesh->discovery.addresses + --mesh->discovery.address_count - p) * sizeof(*p));
- } else {
- i++;
- }
- }
-
- // Check for interfaces state changes and addresses going up
- for(struct ifaddrs *ifap = ifa; ifap; ifap = ifap->ifa_next) {
- if(!ifap->ifa_name) {
- continue;
- }
-
- int index = if_nametoindex(ifap->ifa_name);
-
- if(ifap->ifa_flags & IFF_UP && ifap->ifa_flags & IFF_MULTICAST && !(ifap->ifa_flags & IFF_LOOPBACK)) {
- iface_up(mesh, index);
- } else {
- iface_down(mesh, index);
- }
-
- if(!ifap->ifa_addr) {
- continue;
- }
-
- discovery_address_t addr = {
- .index = index,
- };
-
- sockaddr_t *sa = (sockaddr_t *)ifap->ifa_addr;
-
- if(sa->sa.sa_family == AF_INET) {
- memcpy(&addr.address.in, &sa->in, sizeof(sa->in));
- addr.address.in.sin_port = ntohs(5353);
- } else if(sa->sa.sa_family == AF_INET6) {
- memcpy(&addr.address.in6, &sa->in6, sizeof(sa->in6));
- addr.address.in6.sin6_port = ntohs(5353);
- } else {
- addr.address.sa.sa_family = AF_UNKNOWN;
- }
-
- if(addr.address.sa.sa_family != AF_UNKNOWN) {
- addr_add(mesh, &addr);
- }
- }
-
- freeifaddrs(ifa);
-#else
- (void)mesh;
-#endif
-}
-
-#if defined(__linux)
-static void netlink_getlink(int fd) {
- static const struct {
- struct nlmsghdr nlm;
- struct ifinfomsg ifi;
- } msg = {
- .nlm.nlmsg_len = NLMSG_LENGTH(sizeof(msg.ifi)),
- .nlm.nlmsg_type = RTM_GETLINK,
- .nlm.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
- .nlm.nlmsg_seq = 1,
- .ifi.ifi_family = AF_UNSPEC,
- };
- send(fd, &msg, msg.nlm.nlmsg_len, 0);
-}
-
-static void netlink_getaddr(int fd) {
- static const struct {
- struct nlmsghdr nlm;
- struct ifaddrmsg ifa;
- } msg = {
- .nlm.nlmsg_len = NLMSG_LENGTH(sizeof(msg.ifa)),
- .nlm.nlmsg_type = RTM_GETADDR,
- .nlm.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
- .nlm.nlmsg_seq = 2,
- .ifa.ifa_family = AF_UNSPEC,
- };
- send(fd, &msg, msg.nlm.nlmsg_len, 0);
-}
-
-static void netlink_parse_link(meshlink_handle_t *mesh, const struct nlmsghdr *nlm) {
- const struct ifinfomsg *ifi = (const struct ifinfomsg *)(nlm + 1);
-
- if(ifi->ifi_flags & IFF_UP && ifi->ifi_flags & IFF_MULTICAST) {
- iface_up(mesh, ifi->ifi_index);
- } else {
- iface_down(mesh, ifi->ifi_index);
- }
-}
-
-static void netlink_parse_addr(meshlink_handle_t *mesh, const struct nlmsghdr *nlm) {
- const struct ifaddrmsg *ifa = (const struct ifaddrmsg *)(nlm + 1);
- const uint8_t *ptr = (const uint8_t *)(ifa + 1);
- size_t len = nlm->nlmsg_len - (ptr - (const uint8_t *)nlm);
-
- while(len >= sizeof(struct rtattr)) {
- const struct rtattr *rta = (const struct rtattr *)ptr;
-
- if(rta->rta_len <= 0 || rta->rta_len > len) {
- break;
- }
-
- if(rta->rta_type == IFA_ADDRESS) {
- discovery_address_t addr = {
- .index = ifa->ifa_index,
- };
-
- if(rta->rta_len == 8) {
- addr.address.sa.sa_family = AF_INET;
- memcpy(&addr.address.in.sin_addr, ptr + 4, 4);
- addr.address.in.sin_port = ntohs(5353);
- } else if(rta->rta_len == 20) {
- addr.address.sa.sa_family = AF_INET6;
- memcpy(&addr.address.in6.sin6_addr, ptr + 4, 16);
- addr.address.in6.sin6_port = ntohs(5353);
- addr.address.in6.sin6_scope_id = ifa->ifa_index;
- } else {
- addr.address.sa.sa_family = AF_UNKNOWN;
- }
-
- if(addr.address.sa.sa_family != AF_UNKNOWN) {
- if(nlm->nlmsg_type == RTM_NEWADDR) {
- addr_add(mesh, &addr);
- } else {
- addr_del(mesh, &addr);
- }
- }
- }
-
- unsigned short rta_len = (rta->rta_len + 3) & ~3;
- ptr += rta_len;
- len -= rta_len;
- }
-}
-
-static void netlink_parse(meshlink_handle_t *mesh, const void *data, size_t len) {
- const uint8_t *ptr = data;
-
- while(len >= sizeof(struct nlmsghdr)) {
- const struct nlmsghdr *nlm = (const struct nlmsghdr *)ptr;
-
- if(nlm->nlmsg_len > len) {
- break;
- }
-
- switch(nlm->nlmsg_type) {
- case RTM_NEWLINK:
- case RTM_DELLINK:
- netlink_parse_link(mesh, nlm);
- break;
-
- case RTM_NEWADDR:
- case RTM_DELADDR:
- netlink_parse_addr(mesh, nlm);
- }
-
- ptr += nlm->nlmsg_len;
- len -= nlm->nlmsg_len;
- }
-}
-
-static void netlink_io_handler(event_loop_t *loop, void *data, int flags) {
- (void)flags;
- (void)data;
- meshlink_handle_t *mesh = loop->data;
-
- struct {
- struct nlmsghdr nlm;
- char data[16384];
- } msg;
-
- while(true) {
- ssize_t result = recv(mesh->discovery.pfroute_io.fd, &msg, sizeof(msg), MSG_DONTWAIT);
-
- if(result <= 0) {
- if(result == 0 || errno == EAGAIN || errno == EINTR) {
- break;
- }
-
- logger(mesh, MESHLINK_ERROR, "Reading from Netlink socket failed: %s\n", strerror(errno));
- io_set(loop, &mesh->discovery.pfroute_io, 0);
- }
-
- if((size_t)result < sizeof(msg.nlm)) {
- logger(mesh, MESHLINK_ERROR, "Invalid Netlink message\n");
- break;
- }
-
- if(msg.nlm.nlmsg_type == NLMSG_DONE) {
- if(msg.nlm.nlmsg_seq == 1) {
- // We just got the result of GETLINK, now send GETADDR.
- netlink_getaddr(mesh->discovery.pfroute_io.fd);
- }
- } else {
- netlink_parse(mesh, &msg, result);
-
- if(loop->now.tv_sec > mesh->discovery.last_update + 5) {
- mesh->discovery.last_update = loop->now.tv_sec;
- handle_network_change(mesh, true);
- }
- }
- }
-}
-#elif defined(__APPLE__)
-static void reachability_change_callback(SCNetworkReachabilityRef reachability, SCNetworkReachabilityFlags flags, void *info) {
- (void)reachability;
- (void)flags;
-
- meshlink_handle_t *mesh = info;
-
- pthread_mutex_lock(&mesh->mutex);
-
- scan_ifaddrs(mesh);
-
- if(mesh->loop.now.tv_sec > mesh->discovery.last_update + 5) {
- logger(mesh, MESHLINK_INFO, "Network change detected");
- mesh->discovery.last_update = mesh->loop.now.tv_sec;
- handle_network_change(mesh, true);
- }
-
- pthread_mutex_unlock(&mesh->mutex);
-}
-
-static void *network_change_handler(void *arg) {
- meshlink_handle_t *mesh = arg;
-
- mesh->discovery.runloop = CFRunLoopGetCurrent();
-
- SCNetworkReachabilityRef reach_v4 = SCNetworkReachabilityCreateWithName(NULL, "93.184.216.34");
- SCNetworkReachabilityRef reach_v6 = SCNetworkReachabilityCreateWithName(NULL, "2606:2800:220:1:248:1893:25c8:1946");
-
- SCNetworkReachabilityContext context;
- memset(&context, 0, sizeof(context));
- context.info = mesh;
-
- if(reach_v4) {
- SCNetworkReachabilitySetCallback(reach_v4, reachability_change_callback, &context);
- SCNetworkReachabilityScheduleWithRunLoop(reach_v4, mesh->discovery.runloop, kCFRunLoopDefaultMode);
- } else {
- logger(mesh, MESHLINK_ERROR, "Could not create IPv4 network reachability watcher");
- }
-
- if(reach_v6) {
- SCNetworkReachabilitySetCallback(reach_v6, reachability_change_callback, &context);
- SCNetworkReachabilityScheduleWithRunLoop(reach_v6, mesh->discovery.runloop, kCFRunLoopDefaultMode);
- } else {
- logger(mesh, MESHLINK_ERROR, "Could not create IPv6 network reachability watcher");
- }
-
- CFRunLoopRun();
-
- mesh->discovery.runloop = NULL;
-
- if(reach_v4) {
- SCNetworkReachabilityUnscheduleFromRunLoop(reach_v4, mesh->discovery.runloop, kCFRunLoopDefaultMode);
- CFRelease(reach_v4);
- }
-
- if(reach_v6) {
- SCNetworkReachabilityUnscheduleFromRunLoop(reach_v6, mesh->discovery.runloop, kCFRunLoopDefaultMode);
- CFRelease(reach_v6);
- }
-
- return NULL;
-}
-#elif defined(RTM_NEWADDR)
-static void pfroute_parse_iface(meshlink_handle_t *mesh, const struct rt_msghdr *rtm) {
- const struct if_msghdr *ifm = (const struct if_msghdr *)rtm;
-
- if(ifm->ifm_flags & IFF_UP && ifm->ifm_flags & IFF_MULTICAST && !(ifm->ifm_flags & IFF_LOOPBACK)) {
- iface_up(mesh, ifm->ifm_index);
- } else {
- iface_down(mesh, ifm->ifm_index);
- }
-}
-
-static void pfroute_parse_addr(meshlink_handle_t *mesh, const struct rt_msghdr *rtm) {
- const struct ifa_msghdr *ifam = (const struct ifa_msghdr *)rtm;
- const char *p = (const char *)(ifam + 1);
-
- for(unsigned int i = 1; i; i <<= 1) {
- if(!(ifam->ifam_addrs & i)) {
- continue;
- }
-
- const sockaddr_t *sa = (const sockaddr_t *)p;
-
- if(i == RTA_IFA) {
- discovery_address_t addr = {
- .index = ifam->ifam_index,
- };
-
- if(sa->sa.sa_family == AF_INET) {
- addr.address.in = sa->in;
- addr.address.in.sin_port = ntohs(5353);
- } else if(sa->sa.sa_family == AF_INET6) {
- addr.address.in6 = sa->in6;
- addr.address.in6.sin6_port = ntohs(5353);
- } else {
- addr.address.sa.sa_family = AF_UNKNOWN;
- }
-
- if(addr.address.sa.sa_family != AF_UNKNOWN) {
- if(ifam->ifam_type == RTM_NEWADDR) {
- addr_add(mesh, &addr);
- } else {
- addr_del(mesh, &addr);
- }
- }
-
- break;
- }
-
- size_t len = (sa->sa.sa_len + 3) & ~3;
- p += len;
- }
-}
-
-static void pfroute_io_handler(event_loop_t *loop, void *data, int flags) {
- (void)flags;
- (void)data;
- meshlink_handle_t *mesh = loop->data;
-
- struct {
- struct rt_msghdr rtm;
- char data[2048];
- } msg;
-
- while(true) {
- msg.rtm.rtm_version = 0;
- ssize_t result = recv(mesh->discovery.pfroute_io.fd, &msg, sizeof(msg), MSG_DONTWAIT);
-
- if(result <= 0) {
- if(result == 0 || errno == EAGAIN || errno == EINTR) {
- break;
- }
-
- logger(mesh, MESHLINK_ERROR, "Reading from PFROUTE socket failed: %s\n", strerror(errno));
- io_set(loop, &mesh->discovery.pfroute_io, 0);
- }
-
- if(msg.rtm.rtm_version != RTM_VERSION) {
- logger(mesh, MESHLINK_ERROR, "Invalid PFROUTE message version\n");
- break;
- }
-
- switch(msg.rtm.rtm_type) {
- case RTM_IFINFO:
- pfroute_parse_iface(mesh, &msg.rtm);
- break;
-
- case RTM_NEWADDR:
- case RTM_DELADDR:
- pfroute_parse_addr(mesh, &msg.rtm);
- break;
-
- default:
- break;
- }
- }
-}
-#endif
-
-bool discovery_start(meshlink_handle_t *mesh) {
- logger(mesh, MESHLINK_DEBUG, "discovery_start called\n");
-
- assert(mesh);
-
- // Set up multicast sockets for mDNS
- static const int one = 1;
- static const int ttl = 255;
- static const uint8_t one8 = 1;
- static const uint8_t ttl8 = 255;
-
- int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-
- if(fd == -1) {
- logger(mesh, MESHLINK_ERROR, "Error creating IPv4 socket: %s", strerror(errno));
- } else {
-
- sockaddr_t sa4 = {
- .in.sin_family = AF_INET,
- .in.sin_port = ntohs(5353),
- };
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
-#ifdef SO_REUSEPORT
- setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
-#endif
-#ifdef IP_PKTINFO
- setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
-#endif
- setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one8, sizeof(one8));
- setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl8, sizeof(ttl8));
-
- if(bind(fd, &sa4.sa, SALEN(sa4.sa)) == -1) {
- logger(mesh, MESHLINK_ERROR, "Error binding to IPv4 multicast socket: %s", strerror(errno));
- } else {
- io_add(&mesh->loop, &mesh->discovery.sockets[0], mdns_io_handler, &mesh->discovery.sockets[0], fd, IO_READ);
- }
- }
-
- fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
-
- if(fd == -1) {
- logger(mesh, MESHLINK_ERROR, "Error creating IPv6 socket: %s", strerror(errno));
- } else {
- sockaddr_t sa6 = {
- .in6.sin6_family = AF_INET6,
- .in6.sin6_port = ntohs(5353),
- };
-
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
-#ifdef SO_REUSEPORT
- setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
-#endif
-#ifdef IPV6_RECVPKTINFO
- setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
-#endif
- setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
- setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
- setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
- setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
-
- if(bind(fd, &sa6.sa, SALEN(sa6.sa)) == -1) {
- logger(mesh, MESHLINK_ERROR, "Error binding to IPv6 multicast socket: %s", strerror(errno));
- } else {
- io_add(&mesh->loop, &mesh->discovery.sockets[1], mdns_io_handler, &mesh->discovery.sockets[1], fd, IO_READ);
- }
- }
-
-#if defined(__linux)
- int sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
-
- if(sock != -1) {
- struct sockaddr_nl sa;
- memset(&sa, 0, sizeof(sa));
- sa.nl_family = AF_NETLINK;
- sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
-
- if(bind(sock, (struct sockaddr *)&sa, sizeof(sa)) != -1) {
- io_add(&mesh->loop, &mesh->discovery.pfroute_io, netlink_io_handler, NULL, sock, IO_READ);
- netlink_getlink(sock);
- } else {
- logger(mesh, MESHLINK_WARNING, "Could not bind AF_NETLINK socket: %s", strerror(errno));
- scan_ifaddrs(mesh);
- }
- } else {
- logger(mesh, MESHLINK_WARNING, "Could not open AF_NETLINK socket: %s", strerror(errno));
- scan_ifaddrs(mesh);
- }
-
-#elif defined(__APPLE__)
- pthread_create(&mesh->discovery.thread, NULL, network_change_handler, mesh);
- // TODO: Do we need to wait for the thread to start succesfully?
- scan_ifaddrs(mesh);
-#elif defined(RTM_NEWADDR)
- int sock = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
-
- if(sock != -1) {
- io_add(&mesh->loop, &mesh->discovery.pfroute_io, pfroute_io_handler, NULL, sock, IO_READ);
- } else {
- logger(mesh, MESHLINK_WARNING, "Could not open PF_ROUTE socket: %s", strerror(errno));
- }
-
- scan_ifaddrs(mesh);
-#endif
-
- return true;
-}
-
-void discovery_stop(meshlink_handle_t *mesh) {
- logger(mesh, MESHLINK_DEBUG, "discovery_stop called\n");
-
- assert(mesh);
-
- free(mesh->discovery.ifaces);
- free(mesh->discovery.addresses);
- mesh->discovery.ifaces = NULL;
- mesh->discovery.addresses = NULL;
- mesh->discovery.iface_count = 0;
- mesh->discovery.address_count = 0;
-
-#if defined(__APPLE__)
-
- if(mesh->discovery.runloop) {
- CFRunLoopStop(mesh->discovery.runloop);
- pthread_join(mesh->discovery.thread, NULL);
- }
-
-#endif
-
- if(mesh->discovery.pfroute_io.cb) {
- close(mesh->discovery.pfroute_io.fd);
- io_del(&mesh->loop, &mesh->discovery.pfroute_io);
- }
-
- for(int i = 0; i < 2; i++) {
- if(mesh->discovery.sockets[i].cb) {
- close(mesh->discovery.sockets[i].fd);
- io_del(&mesh->loop, &mesh->discovery.sockets[i]);
- }
- }
-}
-
-void discovery_refresh(meshlink_handle_t *mesh) {
- for(int i = 0; i < mesh->discovery.address_count; i++) {
- if(mesh->discovery.addresses[i].up) {
- send_mdns_packet(mesh, &mesh->discovery.addresses[i], false);
- }
- }
-}
+++ /dev/null
-#ifndef MESHLINK_DISCOVERY_H
-#define MESHLINK_DISCOVERY_H
-
-/*
- discovery.h -- header for discovery.c
- Copyright (C) 2014-2021 Guus Sliepen <guus@meshlink.io>
-
- 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.
-
- 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 <stdbool.h>
-
-bool discovery_start(meshlink_handle_t *mesh);
-void discovery_stop(meshlink_handle_t *mesh);
-void discovery_refresh(meshlink_handle_t *mesh);
-void scan_ifaddrs(meshlink_handle_t *mesh);
-
-#endif
+++ /dev/null
-/*
- edge.c -- edge tree management
- Copyright (C) 2014 Guus Sliepen <guus@meshlink.io>
-
- 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.
-
- 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"
-
-#include "splay_tree.h"
-#include "edge.h"
-#include "logger.h"
-#include "meshlink_internal.h"
-#include "netutl.h"
-#include "node.h"
-#include "utils.h"
-#include "xalloc.h"
-
-static int edge_compare(const edge_t *a, const edge_t *b) {
- return strcmp(a->to->name, b->to->name);
-}
-
-static int edge_weight_compare(const edge_t *a, const edge_t *b) {
- int result;
-
- result = a->weight - b->weight;
-
- if(result) {
- return result;
- }
-
- result = strcmp(a->from->name, b->from->name);
-
- if(result) {
- return result;
- }
-
- return strcmp(a->to->name, b->to->name);
-}
-
-void init_edges(meshlink_handle_t *mesh) {
- mesh->edges = splay_alloc_tree((splay_compare_t) edge_weight_compare, NULL);
-}
-
-splay_tree_t *new_edge_tree(void) {
- return splay_alloc_tree((splay_compare_t) edge_compare, (splay_action_t) free_edge);
-}
-
-void free_edge_tree(splay_tree_t *edge_tree) {
- splay_delete_tree(edge_tree);
-}
-
-void exit_edges(meshlink_handle_t *mesh) {
- if(mesh->edges) {
- splay_delete_tree(mesh->edges);
- }
-
- mesh->edges = NULL;
-}
-
-/* Creation and deletion of connection elements */
-
-edge_t *new_edge(void) {
- return xzalloc(sizeof(edge_t));
-}
-
-void free_edge(edge_t *e) {
- sockaddrfree(&e->address);
-
- free(e);
-}
-
-void edge_add(meshlink_handle_t *mesh, edge_t *e) {
- splay_insert(mesh->edges, e);
- splay_insert(e->from->edge_tree, e);
-
- e->reverse = lookup_edge(e->to, e->from);
-
- if(e->reverse) {
- e->reverse->reverse = e;
- }
-}
-
-void edge_del(meshlink_handle_t *mesh, edge_t *e) {
- if(e->reverse) {
- e->reverse->reverse = NULL;
- }
-
- splay_delete(mesh->edges, e);
- splay_delete(e->from->edge_tree, e);
-}
-
-edge_t *lookup_edge(node_t *from, node_t *to) {
- assert(from);
- assert(to);
-
- edge_t v;
-
- v.from = from;
- v.to = to;
-
- return splay_search(from->edge_tree, &v);
-}
+++ /dev/null
-#ifndef MESHLINK_EDGE_H
-#define MESHLINK_EDGE_H
-
-/*
- edge.h -- header for edge.c
- Copyright (C) 2014, 2017 Guus Sliepen <guus@meshlink.io>
-
- 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.
-
- 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 "splay_tree.h"
-#include "connection.h"
-#include "net.h"
-#include "node.h"
-
-typedef struct edge_t {
- struct node_t *from;
- struct node_t *to;
- sockaddr_t address;
-
- struct connection_t *connection; /* connection associated with this edge, if available */
- struct edge_t *reverse; /* edge in the opposite direction, if available */
-
- int weight; /* weight of this edge */
- uint32_t session_id; /* the session_id of the from node */
-} edge_t;
-
-void init_edges(struct meshlink_handle *mesh);
-void exit_edges(struct meshlink_handle *mesh);
-edge_t *new_edge(void) __attribute__((__malloc__));
-void free_edge(edge_t *);
-struct splay_tree_t *new_edge_tree(void) __attribute__((__malloc__));
-void free_edge_tree(struct splay_tree_t *);
-void edge_add(struct meshlink_handle *mesh, edge_t *);
-void edge_del(struct meshlink_handle *mesh, edge_t *);
-edge_t *lookup_edge(struct node_t *, struct node_t *) __attribute__((__warn_unused_result__));
-
-#endif
+++ /dev/null
-/*
- graph.c -- graph algorithms
- Copyright (C) 2014 Guus Sliepen <guus@meshlink.io>
-
- 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.
-
- 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.
-*/
-
-/* We need to generate two trees from the graph:
-
- 1. A minimum spanning tree for broadcasts,
- 2. A single-source shortest path tree for unicasts.
-
- Actually, the first one alone would suffice but would make unicast packets
- take longer routes than necessary.
-
- For the MST algorithm we can choose from Prim's or Kruskal's. I personally
- favour Kruskal's, because we make an extra AVL tree of edges sorted on
- weights (metric). That tree only has to be updated when an edge is added or
- removed, and during the MST algorithm we just have go linearly through that
- tree, adding safe edges until #edges = #nodes - 1. The implementation here
- however is not so fast, because I tried to avoid having to make a forest and
- merge trees.
-
- For the SSSP algorithm Dijkstra's seems to be a nice choice. Currently a
- simple breadth-first search is presented here.
-
- The SSSP algorithm will also be used to determine whether nodes are
- reachable from the source. It will also set the correct destination address
- and port of a node if possible.
-*/
-
-#include "system.h"
-
-#include "connection.h"
-#include "edge.h"
-#include "graph.h"
-#include "list.h"
-#include "logger.h"
-#include "meshlink_internal.h"
-#include "netutl.h"
-#include "node.h"
-#include "protocol.h"
-#include "utils.h"
-#include "xalloc.h"
-#include "graph.h"
-
-/* Implementation of a simple breadth-first search algorithm.
- Running time: O(E)
-*/
-
-static void sssp_bfs(meshlink_handle_t *mesh) {
- list_t *todo_list = list_alloc(NULL);
-
- /* Clear visited status on nodes */
-
- for splay_each(node_t, n, mesh->nodes) {
- n->status.visited = false;
- n->distance = -1;
- }
-
- /* Begin with mesh->self */
-
- mesh->self->status.visited = mesh->threadstarted;
- mesh->self->nexthop = mesh->self;
- mesh->self->prevedge = NULL;
- mesh->self->distance = 0;
- list_insert_head(todo_list, mesh->self);
-
- /* Loop while todo_list is filled */
-
- for list_each(node_t, n, todo_list) { /* "n" is the node from which we start */
- logger(mesh, MESHLINK_DEBUG, " Examining edges from %s", n->name);
-
- if(n->distance < 0) {
- abort();
- }
-
- for splay_each(edge_t, e, n->edge_tree) { /* "e" is the edge connected to "from" */
- if(!e->reverse) {
- continue;
- }
-
- /* Situation:
-
- /
- /
- ----->(n)---e-->(e->to)
- \
- \
-
- Where e is an edge, (n) and (e->to) are nodes.
- n->address is set to the e->address of the edge left of n to n.
- We are currently examining the edge e right of n from n:
-
- - If edge e provides for better reachability of e->to, update
- e->to and (re)add it to the todo_list to (re)examine the reachability
- of nodes behind it.
- */
-
- if(e->to->status.visited
- && (e->to->distance != n->distance + 1 || e->weight >= e->to->prevedge->weight)) {
- continue;
- }
-
- e->to->status.visited = true;
- e->to->nexthop = (n->nexthop == mesh->self) ? e->to : n->nexthop;
- e->to->prevedge = e;
- e->to->distance = n->distance + 1;
-
- if(!e->to->status.reachable || (e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN)) {
- update_node_udp(mesh, e->to, &e->address);
- }
-
- list_insert_tail(todo_list, e->to);
- }
-
- list_next = list_node->next; /* Because the list_insert_tail() above could have added something extra for us! */
- list_delete_node(todo_list, list_node);
- }
-
- list_free(todo_list);
-}
-
-static void check_reachability(meshlink_handle_t *mesh) {
- /* Check reachability status. */
-
- int reachable = -1; /* Don't count ourself */
-
- for splay_each(node_t, n, mesh->nodes) {
- if(n->status.visited) {
- reachable++;
- }
-
- /* Check for nodes that have changed session_id */
- if(n->status.visited && n->prevedge && n->prevedge->reverse->session_id != n->session_id) {
- logger(mesh, MESHLINK_DEBUG, "Node %s has a new session ID", n->name);
-
- n->session_id = n->prevedge->reverse->session_id;
-
- if(n->utcp) {
- utcp_reset_all_connections(n->utcp);
- }
-
- n->status.validkey = false;
- sptps_stop(&n->sptps);
- n->status.waitingforkey = false;
- n->last_req_key = -3600;
-
- n->status.udp_confirmed = false;
- n->maxmtu = MTU;
- n->minmtu = 0;
- n->mtuprobes = 0;
-
- timeout_del(&mesh->loop, &n->mtutimeout);
- }
-
- if(n->status.visited != n->status.reachable) {
- n->status.reachable = !n->status.reachable;
- n->status.dirty = true;
-
- if(!n->status.blacklisted) {
- if(n->status.reachable) {
- logger(mesh, MESHLINK_DEBUG, "Node %s became reachable", n->name);
- bool first_time_reachable = !n->last_reachable;
- n->last_reachable = time(NULL);
-
- if(first_time_reachable) {
- if(!node_write_config(mesh, n, false)) {
- logger(mesh, MESHLINK_WARNING, "Could not write host config file for node %s!\n", n->name);
-
- }
- }
- } else {
- logger(mesh, MESHLINK_DEBUG, "Node %s became unreachable", n->name);
- n->last_unreachable = time(NULL);
- }
- }
-
- n->status.udp_confirmed = false;
- n->maxmtu = MTU;
- n->minmtu = 0;
- n->mtuprobes = 0;
-
- timeout_del(&mesh->loop, &n->mtutimeout);
-
- if(!n->status.blacklisted) {
- update_node_status(mesh, n);
- }
-
- if(!n->status.reachable) {
- update_node_udp(mesh, n, NULL);
- n->status.broadcast = false;
- }
-
- if(n->utcp) {
- utcp_offline(n->utcp, !n->status.reachable);
- }
- }
- }
-
- if(mesh->reachable != reachable) {
- if(!reachable) {
- mesh->last_unreachable = mesh->loop.now.tv_sec;
-
- if(mesh->threadstarted && mesh->periodictimer.cb) {
- timeout_set(&mesh->loop, &mesh->periodictimer, &(struct timespec) {
- 0, prng(mesh, TIMER_FUDGE)
- });
- }
- }
-
- mesh->reachable = reachable;
- }
-}
-
-void graph(meshlink_handle_t *mesh) {
- sssp_bfs(mesh);
- check_reachability(mesh);
-}
+++ /dev/null
-#ifndef MESHLINK_GRAPH_H
-#define MESHLINK_GRAPH_H
-
-/*
- graph.h -- header for graph.c
- Copyright (C) 2014, 2017 Guus Sliepen <guus@meshlink.io>
-
- 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.
-
- 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.
-*/
-
-void graph(struct meshlink_handle *mesh);
-
-#endif
+++ /dev/null
-// SPDX-FileCopyrightText: 2020 Guus Sliepen <guus@meshlink.io>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "system.h"
-
-#include <stddef.h>
-
-#include "mdns.h"
-#include "xalloc.h"
-
-// Creating a buffer
-
-typedef struct {
- uint8_t *ptr;
- ptrdiff_t len;
-} buf_t;
-
-static void buf_add(buf_t *buf, const void *data, uint32_t len) {
- if(buf->len >= len) {
- memcpy(buf->ptr, data, len);
- buf->ptr += len;
- buf->len -= len;
- } else {
- buf->len = -1;
- }
-}
-
-static void buf_add_uint8(buf_t *buf, uint8_t val) {
- if(buf->len >= 1) {
- buf->ptr[0] = val;
- buf->ptr++;
- buf->len--;
- } else {
- buf->len = -1;
- }
-}
-
-static void buf_add_uint16(buf_t *buf, uint16_t val) {
- uint16_t nval = htons(val);
- buf_add(buf, &nval, sizeof(nval));
-}
-
-static void buf_add_uint32(buf_t *buf, uint32_t val) {
- uint32_t nval = htonl(val);
- buf_add(buf, &nval, sizeof(nval));
-}
-
-static void buf_add_label(buf_t *buf, const char *str) {
- size_t len = strlen(str);
-
- if(len < 256) {
- buf_add_uint8(buf, len);
- buf_add(buf, str, len);
- } else {
- buf->len = -1;
- }
-}
-
-static void buf_add_ulabel(buf_t *buf, const char *str) {
- size_t len = strlen(str);
-
- if(len + 1 < 256) {
- buf_add_uint8(buf, len + 1);
- buf_add_uint8(buf, '_');
- buf_add(buf, str, len);
- } else {
- buf->len = -1;
- }
-}
-
-static void buf_add_kvp(buf_t *buf, const char *key, const char *val) {
- size_t key_len = strlen(key);
- size_t val_len = strlen(val);
-
- if(key_len + val_len + 1 < 256) {
- buf_add_uint8(buf, key_len + val_len + 1);
- buf_add(buf, key, key_len);
- buf_add_uint8(buf, '=');
- buf_add(buf, val, val_len);
- } else {
- buf->len = -1;
- }
-}
-
-static uint8_t *buf_len_start(buf_t *buf) {
- if(buf->len < 2) {
- buf->len = -1;
- return NULL;
- } else {
- uint8_t *ptr = buf->ptr;
- buf->ptr += 2;
- buf->len -= 2;
- return ptr;
- }
-}
-
-static void buf_len_end(buf_t *buf, uint8_t *ptr) {
- if(buf->len < 0) {
- return;
- }
-
- uint16_t len = htons(buf->ptr - ptr - 2);
- memcpy(ptr, &len, sizeof(len));
-}
-
-// Functions reading a buffer
-
-typedef struct {
- const uint8_t *ptr;
- ptrdiff_t len;
-} cbuf_t;
-
-static void buf_check(cbuf_t *buf, const void *data, uint32_t len) {
- if(buf->len >= len && !memcmp(buf->ptr, data, len)) {
- buf->ptr += len;
- buf->len -= len;
- } else {
- buf->len = -1;
- }
-}
-
-static void buf_check_uint8(cbuf_t *buf, uint8_t val) {
- if(buf->len >= 1 && buf->ptr[0] == val) {
- buf->ptr++;
- buf->len--;
- } else {
- buf->len = -1;
- }
-}
-
-static void buf_check_uint16(cbuf_t *buf, uint16_t val) {
- uint16_t nval = htons(val);
- buf_check(buf, &nval, sizeof(nval));
-}
-
-static uint16_t buf_get_uint16(cbuf_t *buf) {
- uint16_t nval;
-
- if(buf->len >= 2) {
- memcpy(&nval, buf->ptr, 2);
- buf->ptr += 2;
- buf->len -= 2;
- return ntohs(nval);
- } else {
- buf->len = -1;
- return 0;
- }
-}
-
-static void buf_check_uint32(cbuf_t *buf, uint32_t val) {
- uint32_t nval = htonl(val);
- buf_check(buf, &nval, sizeof(nval));
-}
-
-static void buf_check_label(cbuf_t *buf, const char *str) {
- size_t len = strlen(str);
-
- if(len < 256) {
- buf_check_uint8(buf, len);
- buf_check(buf, str, len);
- } else {
- buf->len = -1;
- }
-}
-
-static char *buf_get_label(cbuf_t *buf) {
- if(buf->len < 1) {
- buf->len = -1;
- return NULL;
- }
-
- uint8_t len = buf->ptr[0];
- buf->ptr++;
- buf->len--;
-
- if(buf->len < len) {
- buf->len = -1;
- return NULL;
- }
-
- char *label = xmalloc(len + 1);
- memcpy(label, buf->ptr, len);
- label[len] = 0;
- buf->ptr += len;
- buf->len -= len;
- return label;
-}
-
-static void buf_check_ulabel(cbuf_t *buf, const char *str) {
- size_t len = strlen(str);
-
- if(len + 1 < 256) {
- buf_check_uint8(buf, len + 1);
- buf_check_uint8(buf, '_');
- buf_check(buf, str, len);
- } else {
- buf->len = -1;
- }
-}
-
-static void buf_get_kvp(cbuf_t *buf, const char *key, char **val) {
- char *kvp = buf_get_label(buf);
-
- if(buf->len == -1) {
- return;
- }
-
- char *split = strchr(kvp, '=');
-
- if(!split) {
- buf->len = -1;
- return;
- }
-
- *split++ = 0;
-
- if(strcmp(kvp, key)) {
- buf->len = -1;
- return;
- }
-
- memmove(kvp, split, strlen(split) + 1);
- *val = kvp;
-}
-
-static const uint8_t *buf_check_len_start(cbuf_t *buf) {
- if(buf->len < 2) {
- buf->len = -1;
- return NULL;
- } else {
- const uint8_t *ptr = buf->ptr;
- buf->ptr += 2;
- buf->len -= 2;
- return ptr;
- }
-}
-
-static void buf_check_len_end(cbuf_t *buf, const uint8_t *ptr) {
- if(buf->len < 0) {
- return;
- }
-
- uint16_t len = htons(buf->ptr - ptr - 2);
-
- if(memcmp(ptr, &len, sizeof(len))) {
- buf->len = -1;
- }
-}
-
-size_t prepare_request(void *vdata, size_t size, const char *protocol, const char *transport) {
- uint8_t *data = vdata;
- buf_t buf = {data, size};
-
- // Header
- buf_add_uint16(&buf, 0); // TX ID
- buf_add_uint16(&buf, 0); // flags
- buf_add_uint16(&buf, 1); // 1 question
- buf_add_uint16(&buf, 0); // 0 answer RR
- buf_add_uint16(&buf, 0); // 0 authority RRs
- buf_add_uint16(&buf, 0); // 0 additional RR
-
- // Question section: _protocol._transport.local PTR IN
- buf_add_ulabel(&buf, protocol);
- buf_add_ulabel(&buf, transport);
- buf_add_label(&buf, "local");
- buf_add_uint8(&buf, 0);
- buf_add_uint16(&buf, 0xc); // PTR
- buf_add_uint16(&buf, 0x1); // IN
-
- // Done.
- if(buf.len < 0) {
- return 0;
- } else {
- return buf.ptr - data;
- }
-}
-
-bool parse_request(const void *vdata, size_t size, const char *protocol, const char *transport) {
- const uint8_t *data = vdata;
- cbuf_t buf = {data, size};
-
- // Header
- buf_get_uint16(&buf); // TX ID
- buf_check_uint16(&buf, 0); // flags
- buf_check_uint16(&buf, 1); // 1 question
- buf_get_uint16(&buf); // ? answer RR
- buf_get_uint16(&buf); // ? authority RRs
- buf_get_uint16(&buf); // ? additional RR
-
- if(buf.len == -1) {
- return false;
- }
-
- // Question section: _protocol._transport.local PTR IN
- buf_check_ulabel(&buf, protocol);
- buf_check_ulabel(&buf, transport);
- buf_check_label(&buf, "local");
- buf_check_uint8(&buf, 0);
- buf_check_uint16(&buf, 0xc); // PTR
- buf_check_uint16(&buf, 0x1); // IN
-
- if(buf.len == -1) {
- return false;
- }
-
- // Done.
- return buf.len != -1;
-}
-
-size_t prepare_response(void *vdata, size_t size, const char *name, const char *protocol, const char *transport, uint16_t port, int nkeys, const char **keys, const char **values) {
- uint8_t *data = vdata;
- buf_t buf = {data, size};
-
- // Header
- buf_add_uint16(&buf, 0); // TX ID
- buf_add_uint16(&buf, 0x8400); // flags
- buf_add_uint16(&buf, 0); // 1 question
- buf_add_uint16(&buf, 3); // 1 answer RR
- buf_add_uint16(&buf, 0); // 0 authority RRs
- buf_add_uint16(&buf, 0); // 1 additional RR
-
- // Add the TXT record: _protocol._transport local TXT IN 3600 name._protocol._transport key=value...
- uint16_t full_name = buf.ptr - data; // remember start of full name
- buf_add_label(&buf, name);
- uint16_t protocol_offset = buf.ptr - data; // remember start of _protocol
- buf_add_ulabel(&buf, protocol);
- buf_add_ulabel(&buf, transport);
- uint16_t local_offset = buf.ptr - data; // remember start of local
- buf_add_label(&buf, "local");
- buf_add_uint8(&buf, 0);
- buf_add_uint16(&buf, 0x10); // TXT
- buf_add_uint16(&buf, 0x1); // IN
- buf_add_uint32(&buf, 3600); // TTL
-
- uint8_t *len_ptr = buf_len_start(&buf);
-
- for(int i = 0; i < nkeys; i++) {
- buf_add_kvp(&buf, keys[i], values[i]);
- }
-
- buf_len_end(&buf, len_ptr);
-
- // Add the PTR record: _protocol._transport.local PTR IN 3600 name._protocol._transport.local
- buf_add_uint16(&buf, 0xc000 | protocol_offset);
- buf_add_uint16(&buf, 0xc); // PTR
- buf_add_uint16(&buf, 0x8001); // IN (flush)
- buf_add_uint32(&buf, 3600); // TTL
- len_ptr = buf_len_start(&buf);
- buf_add_uint16(&buf, 0xc000 | full_name);
- buf_len_end(&buf, len_ptr);
-
- // Add the SRV record: name._protocol._transport.local SRV IN 120 0 0 port name.local
- buf_add_uint16(&buf, 0xc000 | full_name);
- buf_add_uint16(&buf, 0x21); // SRV
- buf_add_uint16(&buf, 0x8001); // IN (flush)
- buf_add_uint32(&buf, 120); // TTL
- len_ptr = buf_len_start(&buf);
- buf_add_uint16(&buf, 0); // priority
- buf_add_uint16(&buf, 0); // weight
- buf_add_uint16(&buf, port); // port
- buf_add_label(&buf, name);
- buf_add_uint16(&buf, 0xc000 | local_offset);
- buf_len_end(&buf, len_ptr);
-
- // Done.
- if(buf.len < 0) {
- return 0;
- } else {
- return buf.ptr - data;
- }
-}
-
-bool parse_response(const void *vdata, size_t size, char **name, const char *protocol, const char *transport, uint16_t *port, int nkeys, const char **keys, char **values) {
- const uint8_t *data = vdata;
- cbuf_t buf = {data, size};
-
- // Header
- buf_check_uint16(&buf, 0); // TX ID
- buf_check_uint16(&buf, 0x8400); // flags
- buf_check_uint16(&buf, 0); // 0 question
- buf_check_uint16(&buf, 3); // 1 answer RR
- buf_check_uint16(&buf, 0); // 0 authority RRs
- buf_check_uint16(&buf, 0); // 0 checkitional RR
-
- if(buf.len == -1) {
- return false;
- }
-
- // Check the TXT record: _protocol._transport local TXT IN 3600 name._protocol._transport key=value...
- uint16_t full_name = buf.ptr - data; // remember start of full name
- *name = buf_get_label(&buf);
- uint16_t protocol_offset = buf.ptr - data; // remember start of _protocol
- buf_check_ulabel(&buf, protocol);
- buf_check_ulabel(&buf, transport);
- uint16_t local_offset = buf.ptr - data; // remember start of local
- buf_check_label(&buf, "local");
- buf_check_uint8(&buf, 0);
- buf_check_uint16(&buf, 0x10); // TXT
- buf_check_uint16(&buf, 0x1); // IN
- buf_check_uint32(&buf, 3600); // TTL
- const uint8_t *len_ptr = buf_check_len_start(&buf);
-
- for(int i = 0; i < nkeys; i++) {
- buf_get_kvp(&buf, keys[i], &values[i]);
- }
-
- buf_check_len_end(&buf, len_ptr);
-
- if(buf.len == -1) {
- return false;
- }
-
- // Check the PTR record: _protocol._transport.local PTR IN 3600 name._protocol._transport.local
- buf_check_uint16(&buf, 0xc000 | protocol_offset);
- buf_check_uint16(&buf, 0xc); // PTR
- buf_check_uint16(&buf, 0x8001); // IN (flush)
- buf_check_uint32(&buf, 3600); // TTL
- len_ptr = buf_check_len_start(&buf);
- buf_check_uint16(&buf, 0xc000 | full_name);
- buf_check_len_end(&buf, len_ptr);
-
- if(buf.len == -1) {
- return false;
- }
-
- // Check the SRV record: name._protocol._transport.local SRV IN 120 0 0 port name.local
- buf_check_uint16(&buf, 0xc000 | full_name);
- buf_check_uint16(&buf, 0x21); // SRV
- buf_check_uint16(&buf, 0x8001); // IN (flush)
- buf_check_uint32(&buf, 120); // TTL
- len_ptr = buf_check_len_start(&buf);
- buf_check_uint16(&buf, 0); // priority
- buf_check_uint16(&buf, 0); // weight
- *port = buf_get_uint16(&buf); // port
- buf_check_label(&buf, *name);
- buf_check_uint16(&buf, 0xc000 | local_offset);
- buf_check_len_end(&buf, len_ptr);
-
- // Done.
- return buf.len == 0;
-}
+++ /dev/null
-#pragma once
-
-// SPDX-FileCopyrightText: 2020 Guus Sliepen <guus@meshlink.io>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <stdint.h>
-#include <unistd.h>
-
-size_t prepare_packet(void *buf, size_t size, const char *name, const char *protocol, const char *transport, uint16_t port, int nkeys, const char **keys, const char **values, bool response);
-size_t prepare_request(void *buf, size_t size, const char *protocol, const char *transport);
-size_t prepare_response(void *buf, size_t size, const char *name, const char *protocol, const char *transport, uint16_t port, int nkeys, const char **keys, const char **values);
-bool parse_response(const void *buf, size_t size, char **name, const char *protocol, const char *transport, uint16_t *port, int nkeys, const char **keys, char **values);
-bool parse_request(const void *buf, size_t size, const char *protocol, const char *transport);
-
return meshlink_channel_get_mss(handle, channel);
};
- /// Enable or disable zeroconf discovery of local peers
- /** This controls whether zeroconf discovery using the Catta library will be
- * enabled to search for peers on the local network. By default, it is enabled.
- *
- * @param enable Set to true to enable discovery, false to disable.
- */
- void enable_discovery(bool enable = true) {
- meshlink_enable_discovery(handle, enable);
- }
-
/// Inform MeshLink that the local network configuration might have changed
/** This is intended to be used when there is no way for MeshLink to get notifications of local network changes.
* It forces MeshLink to scan all network interfaces for changes in up/down status and new/removed addresses,
meshlink_set_inviter_commits_first(handle, inviter_commits_first);
}
- /// Set the URL used to discover the host's external address
- /** For generating invitation URLs, MeshLink can look up the externally visible address of the local node.
- * It does so by querying an external service. By default, this is http://meshlink.io/host.cgi.
- * Only URLs starting with http:// are supported.
- *
- * @param url The URL to use for external address queries, or NULL to revert back to the default URL.
- */
- void set_external_address_discovery_url(const char *url) {
- meshlink_set_external_address_discovery_url(handle, url);
- }
-
private:
// non-copyable:
mesh(const mesh &) /* TODO: C++11: = delete */;
*/
void meshlink_hint_address(struct meshlink_handle *mesh, struct meshlink_node *node, const struct sockaddr *addr);
-/// Enable or disable zeroconf discovery of local peers
-/** This controls whether zeroconf discovery using the Catta library will be
- * enabled to search for peers on the local network. By default, it is enabled.
- *
- * \memberof meshlink_handle
- * @param mesh A handle which represents an instance of MeshLink.
- * @param enable Set to true to enable discovery, false to disable.
- */
-void meshlink_enable_discovery(struct meshlink_handle *mesh, bool enable);
-
/// Inform MeshLink that the local network configuration might have changed
/** This is intended to be used when there is no way for MeshLink to get notifications of local network changes.
* It forces MeshLink to scan all network interfaces for changes in up/down status and new/removed addresses,
*/
void meshlink_set_inviter_commits_first(struct meshlink_handle *mesh, bool inviter_commits_first);
-/// Set the URL used to discover the host's external address
-/** For generating invitation URLs, MeshLink can look up the externally visible address of the local node.
- * It does so by querying an external service. By default, this is http://meshlink.io/host.cgi.
- * Only URLs starting with http:// are supported.
- *
- * \memberof meshlink_handle
- * @param mesh A handle which represents an instance of MeshLink.
- * @param url The URL to use for external address queries, or NULL to revert back to the default URL.
- */
-void meshlink_set_external_address_discovery_url(struct meshlink_handle *mesh, const char *url);
-
/// Set the scheduling granularity of the application
/** This should be set to the effective scheduling granularity for the application.
* This depends on the scheduling granularity of the operating system, the application's
#include "utils.h"
#include "xalloc.h"
#include "ed25519/sha512.h"
-#include "discovery.h"
#include "devtools.h"
-#include "graph.h"
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
}
}
- /* If TCP worked, then we require that UDP works as well. */
-
- int udp_fd = setup_udp_listen_socket(mesh, aip);
-
- if(udp_fd == -1) {
- closesocket(tcp_fd);
- success = false;
- break;
- }
-
closesocket(tcp_fd);
- closesocket(udp_fd);
success = true;
}
}
}
- /* Clear the reachability times, since we ourself have never seen these nodes yet */
- n->last_reachable = 0;
- n->last_unreachable = 0;
-
if(!node_write_config(mesh, n, true)) {
free_node(n);
return false;
mesh->appname = xstrdup(params->appname);
mesh->devclass = params->devclass;
- mesh->discovery.enabled = true;
mesh->netns = params->netns;
mesh->submeshes = NULL;
mesh->log_cb = global_log_cb;
#endif // HAVE_SETNS
}
- if(mesh->discovery.enabled) {
- discovery_start(mesh);
- }
-
if(pthread_mutex_lock(&mesh->mutex) != 0) {
abort();
}
pthread_mutex_unlock(&mesh->mutex);
- // Stop discovery
- if(mesh->discovery.enabled) {
- discovery_stop(mesh);
- }
-
return NULL;
}
pthread_cond_wait(&mesh->cond, &mesh->mutex);
mesh->threadstarted = true;
- // Ensure we are considered reachable
- graph(mesh);
-
pthread_mutex_unlock(&mesh->mutex);
return true;
}
// Shut down the main thread
event_loop_stop(&mesh->loop);
- // Send ourselves a UDP packet to kick the event loop
- for(int i = 0; i < mesh->listen_sockets; i++) {
- sockaddr_t sa;
- socklen_t salen = sizeof(sa);
-
- if(getsockname(mesh->listen_socket[i].udp.fd, &sa.sa, &salen) == -1) {
- logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "getsockname", sockstrerror(sockerrno));
- continue;
- }
-
- if(sendto(mesh->listen_socket[i].udp.fd, "", 1, MSG_NOSIGNAL, &sa.sa, salen) == -1) {
- logger(mesh, MESHLINK_ERROR, "Could not send a UDP packet to ourself: %s", sockstrerror(sockerrno));
- }
- }
+ // TODO: send something to a local socket to kick the event loop
if(mesh->threadstarted) {
// Wait for the main thread to finish
exit_adns(mesh);
exit_outgoings(mesh);
- // Ensure we are considered unreachable
- if(mesh->nodes) {
- graph(mesh);
- }
-
// Try to write out any changed node config files, ignore errors at this point.
if(mesh->nodes) {
for splay_each(node_t, n, mesh->nodes) {
time_t end;
};
-static bool search_node_by_last_reachable(const node_t *node, const void *condition) {
- const struct time_range *range = condition;
- time_t start = node->last_reachable;
- time_t end = node->last_unreachable;
-
- if(end < start) {
- end = time(NULL);
-
- if(end < start) {
- start = end;
- }
- }
-
- if(range->end >= range->start) {
- return start <= range->end && end >= range->start;
- } else {
- return start > range->start || end < range->end;
- }
-}
-
meshlink_node_t **meshlink_get_all_nodes_by_dev_class(meshlink_handle_t *mesh, dev_class_t devclass, meshlink_node_t **nodes, size_t *nmemb) {
if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT || !nmemb) {
meshlink_errno = MESHLINK_EINVAL;
return meshlink_get_all_nodes_by_condition(mesh, submesh, nodes, nmemb, search_node_by_submesh);
}
-meshlink_node_t **meshlink_get_all_nodes_by_last_reachable(meshlink_handle_t *mesh, time_t start, time_t end, meshlink_node_t **nodes, size_t *nmemb) {
- if(!mesh || !nmemb) {
- meshlink_errno = MESHLINK_EINVAL;
- return NULL;
- }
-
- struct time_range range = {start, end};
-
- return meshlink_get_all_nodes_by_condition(mesh, &range, nodes, nmemb, search_node_by_last_reachable);
-}
-
dev_class_t meshlink_get_node_dev_class(meshlink_handle_t *mesh, meshlink_node_t *node) {
if(!mesh || !node) {
meshlink_errno = MESHLINK_EINVAL;
reachable = n->status.reachable && !n->status.blacklisted;
- if(last_reachable) {
- *last_reachable = n->last_reachable;
- }
-
- if(last_unreachable) {
- *last_unreachable = n->last_unreachable;
- }
+ // TODO: handle reachable times?
+ (void)last_reachable;
+ (void)last_unreachable;
pthread_mutex_unlock(&mesh->mutex);
break;
}
- /* Clear the reachability times, since we ourself have never seen these nodes yet */
- n->last_reachable = 0;
- n->last_unreachable = 0;
-
if(!node_write_config(mesh, n, true)) {
free_node(n);
free(buf);
mesh->node_duplicate_cb(mesh, (meshlink_node_t *)n);
}
-void meshlink_enable_discovery(meshlink_handle_t *mesh, bool enable) {
- logger(mesh, MESHLINK_DEBUG, "meshlink_enable_discovery(%d)", enable);
-
- if(!mesh) {
- meshlink_errno = MESHLINK_EINVAL;
- return;
- }
-
- if(pthread_mutex_lock(&mesh->mutex) != 0) {
- abort();
- }
-
- if(mesh->discovery.enabled == enable) {
- goto end;
- }
-
- if(mesh->threadstarted) {
- if(enable) {
- discovery_start(mesh);
- } else {
- discovery_stop(mesh);
- }
- }
-
- mesh->discovery.enabled = enable;
-
-end:
- pthread_mutex_unlock(&mesh->mutex);
-}
-
void meshlink_hint_network_change(struct meshlink_handle *mesh) {
logger(mesh, MESHLINK_DEBUG, "meshlink_hint_network_change()");
abort();
}
- if(mesh->discovery.enabled) {
- scan_ifaddrs(mesh);
- }
-
- if(mesh->loop.now.tv_sec > mesh->discovery.last_update + 5) {
- mesh->discovery.last_update = mesh->loop.now.tv_sec;
- handle_network_change(mesh, 1);
- }
-
pthread_mutex_unlock(&mesh->mutex);
}
handle_network_change(mesh, true);
- if(mesh->discovery.enabled) {
- discovery_refresh(mesh);
- }
-
pthread_mutex_unlock(&mesh->mutex);
}
pthread_mutex_unlock(&mesh->mutex);
}
-void meshlink_set_external_address_discovery_url(struct meshlink_handle *mesh, const char *url) {
- logger(mesh, MESHLINK_DEBUG, "meshlink_set_external_address_discovery_url(%s)", url ? url : "(null)");
-
- if(!mesh) {
- meshlink_errno = EINVAL;
- return;
- }
-
- if(url && (strncmp(url, "http://", 7) || strchr(url, ' '))) {
- meshlink_errno = EINVAL;
- return;
- }
-
- if(pthread_mutex_lock(&mesh->mutex) != 0) {
- abort();
- }
-
- free(mesh->external_address_url);
- mesh->external_address_url = url ? xstrdup(url) : NULL;
- pthread_mutex_unlock(&mesh->mutex);
-}
-
void meshlink_set_scheduling_granularity(struct meshlink_handle *mesh, long granularity) {
logger(mesh, MESHLINK_DEBUG, "meshlink_set_scheduling_granularity(%ld)", granularity);
__emutls_v.meshlink_errno
-devtool_export_json_all_edges_state
-devtool_get_all_edges
devtool_get_all_submeshes
devtool_get_node_status
devtool_keyrotate_probe
meshlink_close
meshlink_destroy
meshlink_destroy_ex
-meshlink_enable_discovery
meshlink_encrypted_key_rotate
meshlink_errno
meshlink_export
meshlink_set_dev_class_maxtimeout
meshlink_set_dev_class_timeouts
meshlink_set_error_cb
-meshlink_set_external_address_discovery_url
meshlink_set_inviter_commits_first
meshlink_set_log_cb
meshlink_set_node_channel_timeout
typedef struct listen_socket_t {
struct io_t tcp;
- struct io_t udp;
sockaddr_t sa;
sockaddr_t broadcast_sa;
} listen_socket_t;
meshlink_queue_t outpacketqueue;
signal_t datafromapp;
- hash_t *node_udp_cache;
-
struct splay_tree_t *nodes;
- struct splay_tree_t *edges;
struct list_t *connections;
struct list_t *outgoings;
dev_class_t devclass;
- int udp_choice;
-
dev_class_traits_t dev_class_traits[DEV_CLASS_COUNT];
int netns;
pthread_cond_t cond;
bool threadstarted;
- // mDNS discovery
- struct {
- bool enabled;
- io_t pfroute_io;
- int *ifaces;
- struct discovery_address *addresses;
- int iface_count;
- int address_count;
- io_t sockets[2];
- time_t last_update;
-#ifdef __APPLE__
- pthread_t thread;
- void *runloop;
-#endif
- } discovery;
-
// ADNS
pthread_t adns_thread;
pthread_cond_t adns_cond;
#include "conf.h"
#include "connection.h"
#include "devtools.h"
-#include "graph.h"
#include "logger.h"
#include "meshlink_internal.h"
#include "meta.h"
/*
Terminate a connection:
- Mark it as inactive
- - Remove the edge representing this connection
- Kill it with fire
- Check if we need to retry making an outgoing connection
*/
void terminate_connection(meshlink_handle_t *mesh, connection_t *c, bool report) {
+ (void)report;
+
if(c->status.active) {
logger(mesh, MESHLINK_INFO, "Closing connection with %s", c->name);
}
c->status.active = false;
- if(c->edge) {
- if(report) {
- send_del_edge(mesh, mesh->everyone, c->edge, 0);
- }
-
- edge_del(mesh, c->edge);
- c->edge = NULL;
-
- /* Run MST and SSSP algorithms */
-
- graph(mesh);
-
- /* If the node is not reachable anymore but we remember it had an edge to us, clean it up */
-
- if(report && c->node && !c->node->status.reachable) {
- edge_t *e;
- e = lookup_edge(c->node, mesh->self);
-
- if(e) {
- send_del_edge(mesh, mesh->everyone, e, 0);
- edge_del(mesh, e);
- }
- }
- }
-
outgoing_t *outgoing = c->outgoing;
connection_del(mesh, c);
void do_outgoing_connection(struct meshlink_handle *mesh, struct outgoing_t *);
void handle_new_meta_connection(struct event_loop_t *loop, void *, int);
int setup_tcp_listen_socket(struct meshlink_handle *mesh, const struct addrinfo *aip) __attribute__((__warn_unused_result__));
-int setup_udp_listen_socket(struct meshlink_handle *mesh, const struct addrinfo *aip) __attribute__((__warn_unused_result__));
bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len);
bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len) __attribute__((__warn_unused_result__));
void send_packet(struct meshlink_handle *mesh, struct node_t *, struct vpn_packet_t *);
#include "conf.h"
#include "connection.h"
#include "crypto.h"
-#include "graph.h"
#include "logger.h"
#include "meshlink_internal.h"
#include "net.h"
int keylifetime = 0;
-static void send_udppacket(meshlink_handle_t *mesh, node_t *, vpn_packet_t *);
-
-#define MAX_SEQNO 1073741824
-
-/* mtuprobes == 1..30: initial discovery, send bursts with 1 second interval
- mtuprobes == 31: sleep pinginterval seconds
- mtuprobes == 32: send 1 burst, sleep pingtimeout second
- mtuprobes == 33: no response from other side, restart PMTU discovery process
-
- Probes are sent in batches of at least three, with random sizes between the
- lower and upper boundaries for the MTU thus far discovered.
-
- After the initial discovery, a fourth packet is added to each batch with a
- size larger than the currently known PMTU, to test if the PMTU has increased.
-
- In case local discovery is enabled, another packet is added to each batch,
- which will be broadcast to the local network.
-
-*/
-
-static void send_mtu_probe_handler(event_loop_t *loop, void *data) {
- meshlink_handle_t *mesh = loop->data;
- node_t *n = data;
- int timeout = 1;
-
- n->mtuprobes++;
-
- if(!n->status.reachable || !n->status.validkey) {
- logger(mesh, MESHLINK_INFO, "Trying to send MTU probe to unreachable or rekeying node %s", n->name);
- n->mtuprobes = 0;
- return;
- }
-
- if(n->mtuprobes > 32) {
- if(!n->minmtu) {
- n->mtuprobes = 31;
- timeout = mesh->dev_class_traits[n->devclass].pinginterval;
- goto end;
- }
-
- logger(mesh, MESHLINK_INFO, "%s did not respond to UDP ping, restarting PMTU discovery", n->name);
- n->status.udp_confirmed = false;
- n->mtuprobes = 1;
- n->minmtu = 0;
- n->maxmtu = MTU;
-
- update_node_pmtu(mesh, n);
- }
-
- if(n->mtuprobes >= 10 && n->mtuprobes < 32 && !n->minmtu) {
- logger(mesh, MESHLINK_INFO, "No response to MTU probes from %s", n->name);
- n->mtuprobes = 31;
- }
-
- if(n->mtuprobes == 30 || (n->mtuprobes < 30 && n->minmtu >= n->maxmtu)) {
- if(n->minmtu > n->maxmtu) {
- n->minmtu = n->maxmtu;
- update_node_pmtu(mesh, n);
- } else {
- n->maxmtu = n->minmtu;
- }
-
- n->mtu = n->minmtu;
- logger(mesh, MESHLINK_INFO, "Fixing MTU of %s to %d after %d probes", n->name, n->mtu, n->mtuprobes);
- n->mtuprobes = 31;
- }
-
- if(n->mtuprobes == 31) {
- if(!n->minmtu && n->status.want_udp && n->nexthop && n->nexthop->connection) {
- /* Send a dummy ANS_KEY to try to update the reflexive UDP address */
- send_request(mesh, n->nexthop->connection, NULL, "%d %s %s . -1 -1 -1 0", ANS_KEY, mesh->self->name, n->name);
- n->status.want_udp = false;
- }
-
- timeout = mesh->dev_class_traits[n->devclass].pinginterval;
- goto end;
- } else if(n->mtuprobes == 32) {
- timeout = mesh->dev_class_traits[n->devclass].pingtimeout;
- }
-
- for(int i = 0; i < 5; i++) {
- int len;
-
- if(i == 0) {
- if(n->mtuprobes < 30 || n->maxmtu + 8 >= MTU) {
- continue;
- }
-
- len = n->maxmtu + 8;
- } else if(n->maxmtu <= n->minmtu) {
- len = n->maxmtu;
- } else {
- len = n->minmtu + 1 + prng(mesh, n->maxmtu - n->minmtu);
- }
-
- if(len < 64) {
- len = 64;
- }
-
- vpn_packet_t packet;
- packet.probe = true;
- memset(packet.data, 0, 14);
- randomize(packet.data + 14, len - 14);
- packet.len = len;
- n->status.broadcast = i >= 4 && n->mtuprobes <= 10 && n->prevedge;
-
- logger(mesh, MESHLINK_DEBUG, "Sending MTU probe length %d to %s", len, n->name);
-
- send_udppacket(mesh, n, &packet);
- }
-
- n->status.broadcast = false;
-
-end:
- timeout_set(&mesh->loop, &n->mtutimeout, &(struct timespec) {
- timeout, prng(mesh, TIMER_FUDGE)
- });
-}
-
-void send_mtu_probe(meshlink_handle_t *mesh, node_t *n) {
- timeout_add(&mesh->loop, &n->mtutimeout, send_mtu_probe_handler, n, &(struct timespec) {
- 1, 0
- });
- send_mtu_probe_handler(&mesh->loop, n);
-}
-
-static void mtu_probe_h(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet, uint16_t len) {
- if(len < 64) {
- logger(mesh, MESHLINK_WARNING, "Got too short MTU probe length %d from %s", packet->len, n->name);
- return;
- }
-
- logger(mesh, MESHLINK_DEBUG, "Got MTU probe length %d from %s", packet->len, n->name);
-
- if(!packet->data[0]) {
- /* It's a probe request, send back a reply */
-
- packet->data[0] = 1;
-
- /* Temporarily set udp_confirmed, so that the reply is sent
- back exactly the way it came in. */
-
- bool udp_confirmed = n->status.udp_confirmed;
- n->status.udp_confirmed = true;
- send_udppacket(mesh, n, packet);
- n->status.udp_confirmed = udp_confirmed;
- } else {
- /* It's a valid reply: now we know bidirectional communication
- is possible using the address and socket that the reply
- packet used. */
-
- if(!n->status.udp_confirmed) {
- char *address, *port;
- sockaddr2str(&n->address, &address, &port);
-
- if(n->nexthop && n->nexthop->connection) {
- send_request(mesh, n->nexthop->connection, NULL, "%d %s %s . -1 -1 -1 0 %s %s", ANS_KEY, n->name, n->name, address, port);
- } else {
- logger(mesh, MESHLINK_WARNING, "Cannot send reflexive address to %s via %s", n->name, n->nexthop ? n->nexthop->name : n->name);
- }
-
- free(address);
- free(port);
- n->status.udp_confirmed = true;
- }
-
- /* If we haven't established the PMTU yet, restart the discovery process. */
-
- if(n->mtuprobes > 30) {
- if(len == n->maxmtu + 8) {
- logger(mesh, MESHLINK_INFO, "Increase in PMTU to %s detected, restarting PMTU discovery", n->name);
- n->maxmtu = MTU;
- n->mtuprobes = 10;
- return;
- }
-
- if(n->minmtu) {
- n->mtuprobes = 30;
- } else {
- n->mtuprobes = 1;
- }
- }
-
- /* If applicable, raise the minimum supported MTU */
-
- if(len > n->maxmtu) {
- len = n->maxmtu;
- }
-
- if(n->minmtu < len) {
- n->minmtu = len;
- update_node_pmtu(mesh, n);
- }
- }
-}
-
/* VPN packet I/O */
static void receive_packet(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet) {
}
}
-static bool try_mac(meshlink_handle_t *mesh, node_t *n, const vpn_packet_t *inpkt) {
- (void)mesh;
- return sptps_verify_datagram(&n->sptps, inpkt->data, inpkt->len);
-}
-
-static void receive_udppacket(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *inpkt) {
- if(!n->status.reachable) {
- logger(mesh, MESHLINK_ERROR, "Got SPTPS data from unreachable node %s", n->name);
- return;
- }
-
- if(!n->sptps.state) {
- if(!n->status.waitingforkey) {
- logger(mesh, MESHLINK_DEBUG, "Got packet from %s but we haven't exchanged keys yet", n->name);
- send_req_key(mesh, n);
- } else {
- logger(mesh, MESHLINK_DEBUG, "Got packet from %s but he hasn't got our key yet", n->name);
- }
-
- return;
- }
-
- if(!sptps_receive_data(&n->sptps, inpkt->data, inpkt->len)) {
- logger(mesh, MESHLINK_ERROR, "Could not process SPTPS data from %s: %s", n->name, strerror(errno));
- }
-}
-
static void send_sptps_packet(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *origpkt) {
if(!n->status.reachable) {
logger(mesh, MESHLINK_ERROR, "Trying to send SPTPS data to unreachable node %s", n->name);
return;
}
-static void choose_udp_address(meshlink_handle_t *mesh, const node_t *n, const sockaddr_t **sa, int *sock, sockaddr_t *sa_buf) {
- /* Latest guess */
- *sa = &n->address;
- *sock = n->sock;
-
- /* If the UDP address is confirmed, use it. */
- if(n->status.udp_confirmed) {
- return;
- }
-
- /* Send every third packet to n->address; that could be set
- to the node's reflexive UDP address discovered during key
- exchange. */
-
- if(++mesh->udp_choice >= 3) {
- mesh->udp_choice = 0;
- return;
- }
-
- /* If we have learned an address via Catta, try this once every batch */
- if(mesh->udp_choice == 1 && n->catta_address.sa.sa_family != AF_UNSPEC) {
- *sa = &n->catta_address;
- goto check_socket;
- }
-
- /* Else, if we have a canonical address, try this once every batch */
- if(mesh->udp_choice == 1 && n->canonical_address) {
- char *host = xstrdup(n->canonical_address);
- char *port = strchr(host, ' ');
-
- if(port) {
- *port++ = 0;
- *sa_buf = str2sockaddr_random(mesh, host, port);
- *sa = sa_buf;
-
- if(sa_buf->sa.sa_family != AF_UNKNOWN) {
- free(host);
- goto check_socket;
- }
- }
-
- free(host);
- }
-
- /* Otherwise, address are found in edges to this node.
- So we pick a random edge and a random socket. */
-
- edge_t *candidate = NULL;
-
- {
- int i = 0;
- int j = prng(mesh, n->edge_tree->count);
-
- for splay_each(edge_t, e, n->edge_tree) {
- if(i++ == j) {
- candidate = e->reverse;
- break;
- }
- }
- }
-
- if(candidate) {
- *sa = &candidate->address;
- *sock = prng(mesh, mesh->listen_sockets);
- }
-
-check_socket:
-
- /* Make sure we have a suitable socket for the chosen address */
- if(mesh->listen_socket[*sock].sa.sa.sa_family != (*sa)->sa.sa_family) {
- for(int i = 0; i < mesh->listen_sockets; i++) {
- if(mesh->listen_socket[i].sa.sa.sa_family == (*sa)->sa.sa_family) {
- *sock = i;
- break;
- }
- }
- }
-}
-
-static void choose_broadcast_address(meshlink_handle_t *mesh, const node_t *n, const sockaddr_t **sa, int *sock) {
- *sock = prng(mesh, mesh->listen_sockets);
- sockaddr_t *broadcast_sa = &mesh->listen_socket[*sock].broadcast_sa;
-
- if(broadcast_sa->sa.sa_family == AF_INET6) {
- broadcast_sa->in6.sin6_port = n->prevedge->address.in.sin_port;
- } else {
- broadcast_sa->in.sin_port = n->prevedge->address.in.sin_port;
- }
-
- *sa = broadcast_sa;
-}
-
-static void send_udppacket(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *origpkt) {
- if(!n->status.reachable) {
- logger(mesh, MESHLINK_INFO, "Trying to send UDP packet to unreachable node %s", n->name);
- return;
- }
-
- send_sptps_packet(mesh, n, origpkt);
-}
-
bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len) {
assert(handle);
assert(data);
return false;
}
- /* Send it via TCP if it is a handshake packet, TCPOnly is in use, or this packet is larger than the MTU. */
-
- if(type >= SPTPS_HANDSHAKE || (type != PKT_PROBE && (len - 21) > to->minmtu)) {
- char buf[len * 4 / 3 + 5];
- b64encode(data, buf, len);
- if(!to->nexthop || !to->nexthop->connection) {
- logger(mesh, MESHLINK_WARNING, "Unable to forward SPTPS packet to %s via %s", to->name, to->nexthop ? to->nexthop->name : to->name);
- return false;
- }
-
- /* If no valid key is known yet, send the packets using ANS_KEY requests,
- to ensure we get to learn the reflexive UDP address. */
- if(!to->status.validkey) {
- return send_request(mesh, to->nexthop->connection, NULL, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, mesh->self->name, to->name, buf, 0);
- } else {
- return send_request(mesh, to->nexthop->connection, NULL, "%d %s %s %d %s", REQ_KEY, mesh->self->name, to->name, REQ_SPTPS, buf);
- }
+ if(type == PKT_PROBE) {
+ /* Probe packets are not supported. */
+ return false;
}
- /* Otherwise, send the packet via UDP */
+ /* Send it via TCP. */
- sockaddr_t sa_buf;
- const sockaddr_t *sa;
- int sock;
+ char buf[len * 4 / 3 + 5];
+ b64encode(data, buf, len);
- if(to->status.broadcast) {
- choose_broadcast_address(mesh, to, &sa, &sock);
- } else {
- choose_udp_address(mesh, to, &sa, &sock, &sa_buf);
+ if(!to->nexthop || !to->nexthop->connection) {
+ logger(mesh, MESHLINK_WARNING, "Unable to forward SPTPS packet to %s via %s", to->name, to->nexthop ? to->nexthop->name : to->name);
+ return false;
}
- if(sendto(mesh->listen_socket[sock].udp.fd, data, len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
- if(sockmsgsize(sockerrno)) {
- if(to->maxmtu >= len) {
- to->maxmtu = len - 1;
- }
-
- if(to->mtu >= len) {
- to->mtu = len - 1;
- }
- } else {
- logger(mesh, MESHLINK_WARNING, "Error sending UDP SPTPS packet to %s: %s", to->name, sockstrerror(sockerrno));
- return false;
- }
+ /* If no valid key is known yet, send the packets using ANS_KEY requests,
+ to ensure we get to learn the reflexive UDP address. */
+ if(!to->status.validkey) {
+ return send_request(mesh, to->nexthop->connection, NULL, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, mesh->self->name, to->name, buf, 0);
+ } else {
+ return send_request(mesh, to->nexthop->connection, NULL, "%d %s %s %d %s", REQ_KEY, mesh->self->name, to->name, REQ_SPTPS, buf);
}
-
- return true;
}
bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len) {
vpn_packet_t inpkt;
if(type == PKT_PROBE) {
- inpkt.len = len;
- inpkt.probe = true;
- memcpy(inpkt.data, data, len);
- mtu_probe_h(mesh, from, &inpkt, len);
- return true;
+ /* We shouldn't receive any UDP probe packets. */
+ return false;
} else {
inpkt.probe = false;
}
send_sptps_packet(mesh, n, packet);
return;
}
-
-static node_t *try_harder(meshlink_handle_t *mesh, const sockaddr_t *from, const vpn_packet_t *pkt) {
- node_t *n = NULL;
- bool hard = false;
-
- for splay_each(edge_t, e, mesh->edges) {
- if(!e->to->status.reachable || e->to == mesh->self) {
- continue;
- }
-
- if(sockaddrcmp_noport(from, &e->address)) {
- if(mesh->last_hard_try == mesh->loop.now.tv_sec) {
- continue;
- }
-
- hard = true;
- }
-
- if(!try_mac(mesh, e->to, pkt)) {
- continue;
- }
-
- n = e->to;
- break;
- }
-
- if(hard) {
- mesh->last_hard_try = mesh->loop.now.tv_sec;
- }
-
- return n;
-}
-
-void handle_incoming_vpn_data(event_loop_t *loop, void *data, int flags) {
- (void)flags;
- meshlink_handle_t *mesh = loop->data;
- listen_socket_t *ls = data;
- vpn_packet_t pkt;
- char *hostname;
- sockaddr_t from;
- socklen_t fromlen = sizeof(from);
- node_t *n;
- int len;
-
- memset(&from, 0, sizeof(from));
-
- len = recvfrom(ls->udp.fd, pkt.data, MAXSIZE, 0, &from.sa, &fromlen);
-
- if(len <= 0 || len > MAXSIZE) {
- if(!sockwouldblock(sockerrno)) {
- logger(mesh, MESHLINK_ERROR, "Receiving packet failed: %s", sockstrerror(sockerrno));
- }
-
- return;
- }
-
- pkt.len = len;
-
- sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
-
- n = lookup_node_udp(mesh, &from);
-
- if(!n) {
- n = try_harder(mesh, &from, &pkt);
-
- if(n) {
- update_node_udp(mesh, n, &from);
- } else if(mesh->log_level <= MESHLINK_WARNING) {
- hostname = sockaddr2hostname(&from);
- logger(mesh, MESHLINK_WARNING, "Received UDP packet from unknown source %s", hostname);
- free(hostname);
- return;
- } else {
- return;
- }
- }
-
- if(n->status.blacklisted) {
- logger(mesh, MESHLINK_WARNING, "Dropping packet from blacklisted node %s", n->name);
- return;
- }
-
- n->sock = ls - mesh->listen_socket;
-
- receive_udppacket(mesh, n, &pkt);
-}
#include "conf.h"
#include "connection.h"
#include "ecdsa.h"
-#include "graph.h"
#include "logger.h"
#include "meshlink_internal.h"
#include "net.h"
}
}
- time_t last_reachable = packmsg_get_int64(&in);
- time_t last_unreachable = packmsg_get_int64(&in);
-
- if(!n->last_reachable) {
- n->last_reachable = last_reachable;
- }
-
- if(!n->last_unreachable) {
- n->last_unreachable = last_unreachable;
- }
+ packmsg_skip_element(&in); // last_reachable
+ packmsg_skip_element(&in); // last_unreachable
config_free(&config);
return true;
}
}
- n->last_reachable = packmsg_get_int64(&in);
- n->last_unreachable = packmsg_get_int64(&in);
+ packmsg_skip_element(&in); // last_reachable
+ packmsg_skip_element(&in); // last_unreachable
return packmsg_done(&in);
}
packmsg_add_sockaddr(&out, &n->recent[i]);
}
- packmsg_add_int64(&out, n->last_reachable);
- packmsg_add_int64(&out, n->last_unreachable);
+ packmsg_add_int64(&out, 0); // last_reachable
+ packmsg_add_int64(&out, 0); // last_unreachable
if(!packmsg_output_ok(&out)) {
meshlink_errno = MESHLINK_EINTERNAL;
return nfd;
}
-int setup_udp_listen_socket(meshlink_handle_t *mesh, const struct addrinfo *aip) {
- int nfd = socket(aip->ai_family, SOCK_DGRAM, IPPROTO_UDP);
-
- if(nfd == -1) {
- return -1;
- }
-
-#ifdef FD_CLOEXEC
- fcntl(nfd, F_SETFD, FD_CLOEXEC);
-#endif
-
-#ifdef O_NONBLOCK
- int flags = fcntl(nfd, F_GETFL);
-
- if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) {
- closesocket(nfd);
- logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "fcntl", strerror(errno));
- return -1;
- }
-
-#elif defined(WIN32)
- unsigned long arg = 1;
-
- if(ioctlsocket(nfd, FIONBIO, &arg) != 0) {
- closesocket(nfd);
- logger(mesh, MESHLINK_ERROR, "Call to `%s' failed: %s", "ioctlsocket", sockstrerror(sockerrno));
- return -1;
- }
-
-#endif
-
- int option = 1;
- setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
- setsockopt(nfd, SOL_SOCKET, SO_BROADCAST, (void *)&option, sizeof(option));
-
-#if defined(IPV6_V6ONLY)
-
- if(aip->ai_family == AF_INET6) {
- setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
- }
-
-#endif
-
-#if defined(IP_DONTFRAG) && !defined(IP_DONTFRAGMENT)
-#define IP_DONTFRAGMENT IP_DONTFRAG
-#endif
-
-#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
- option = IP_PMTUDISC_DO;
- setsockopt(nfd, IPPROTO_IP, IP_MTU_DISCOVER, (void *)&option, sizeof(option));
-#elif defined(IP_DONTFRAGMENT)
- option = 1;
- setsockopt(nfd, IPPROTO_IP, IP_DONTFRAGMENT, (void *)&option, sizeof(option));
-#endif
-
- if(aip->ai_family == AF_INET6) {
-#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
- option = IPV6_PMTUDISC_DO;
- setsockopt(nfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, (void *)&option, sizeof(option));
-#elif defined(IPV6_DONTFRAG)
- option = 1;
- setsockopt(nfd, IPPROTO_IPV6, IPV6_DONTFRAG, (void *)&option, sizeof(option));
-#endif
- }
-
- if(bind(nfd, aip->ai_addr, aip->ai_addrlen)) {
- closesocket(nfd);
- return -1;
- }
-
- return nfd;
-}
-
/*
Add listening sockets.
*/
}
}
- /* If TCP worked, then we require that UDP works as well. */
-
- int udp_fd = setup_udp_listen_socket(mesh, aip);
-
- if(udp_fd == -1) {
- closesocket(tcp_fd);
- success = false;
- break;
- }
-
io_add(&mesh->loop, &mesh->listen_socket[mesh->listen_sockets].tcp, handle_new_meta_connection, &mesh->listen_socket[mesh->listen_sockets], tcp_fd, IO_READ);
- io_add(&mesh->loop, &mesh->listen_socket[mesh->listen_sockets].udp, handle_incoming_vpn_data, &mesh->listen_socket[mesh->listen_sockets], udp_fd, IO_READ);
if(mesh->log_level <= MESHLINK_INFO) {
char *hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
if(!success) {
for(int i = 0; i < mesh->listen_sockets; i++) {
io_del(&mesh->loop, &mesh->listen_socket[i].tcp);
- io_del(&mesh->loop, &mesh->listen_socket[i].udp);
closesocket(mesh->listen_socket[i].tcp.fd);
- closesocket(mesh->listen_socket[i].udp.fd);
}
mesh->listen_sockets = 0;
init_connections(mesh);
init_submeshes(mesh);
init_nodes(mesh);
- init_edges(mesh);
init_requests(mesh);
if(!setup_myself(mesh)) {
for(int i = 0; i < mesh->listen_sockets; i++) {
io_del(&mesh->loop, &mesh->listen_socket[i].tcp);
- io_del(&mesh->loop, &mesh->listen_socket[i].udp);
closesocket(mesh->listen_socket[i].tcp.fd);
- closesocket(mesh->listen_socket[i].udp.fd);
}
exit_requests(mesh);
- exit_edges(mesh);
exit_nodes(mesh);
exit_submeshes(mesh);
exit_connections(mesh);
}
}
-// Find edges pointing to this node, and use them to build a list of unique, known addresses.
-static struct addrinfo *get_known_addresses(node_t *n) {
- struct addrinfo *ai = NULL;
-
- for splay_each(edge_t, e, n->edge_tree) {
- if(!e->reverse) {
- continue;
- }
-
- bool found = false;
-
- for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
- if(!sockaddrcmp(&e->reverse->address, (sockaddr_t *)aip->ai_addr)) {
- found = true;
- break;
- }
- }
-
- if(found) {
- continue;
- }
-
- // Create a new struct addrinfo, and put it at the head of the list.
- struct addrinfo *nai = xzalloc(sizeof(*nai) + SALEN(e->reverse->address.sa));
- nai->ai_next = ai;
- ai = nai;
-
- ai->ai_family = e->reverse->address.sa.sa_family;
- ai->ai_socktype = SOCK_STREAM;
- ai->ai_protocol = IPPROTO_TCP;
- ai->ai_addrlen = SALEN(e->reverse->address.sa);
- ai->ai_addr = (struct sockaddr *)(nai + 1);
- memcpy(ai->ai_addr, &e->reverse->address, ai->ai_addrlen);
- }
-
- return ai;
-}
-
// Build a list of recently seen addresses.
static struct addrinfo *get_recent_addresses(node_t *n) {
struct addrinfo *ai = NULL;
return true;
}
- free_known_addresses(outgoing->ai);
- outgoing->ai = NULL;
- outgoing->aip = NULL;
- outgoing->state = OUTGOING_KNOWN;
- }
-
- if(outgoing->state == OUTGOING_KNOWN) {
- if(!outgoing->aip) {
- outgoing->ai = get_known_addresses(outgoing->node);
- outgoing->aip = outgoing->ai;
- } else {
- outgoing->aip = outgoing->aip->ai_next;
- }
-
- if(outgoing->aip) {
- return true;
- }
-
free_known_addresses(outgoing->ai);
outgoing->ai = NULL;
outgoing->aip = NULL;
void init_nodes(meshlink_handle_t *mesh) {
mesh->nodes = splay_alloc_tree((splay_compare_t) node_compare, (splay_action_t) free_node);
- mesh->node_udp_cache = hash_alloc(0x100, sizeof(sockaddr_t));
}
void exit_nodes(meshlink_handle_t *mesh) {
- if(mesh->node_udp_cache) {
- hash_free(mesh->node_udp_cache);
- }
-
if(mesh->nodes) {
splay_delete_tree(mesh->nodes);
}
- mesh->node_udp_cache = NULL;
mesh->nodes = NULL;
}
node_t *new_node(void) {
node_t *n = xzalloc(sizeof(*n));
- n->edge_tree = new_edge_tree();
n->mtu = MTU;
n->maxmtu = MTU;
n->devclass = DEV_CLASS_UNKNOWN;
utcp_exit(n->utcp);
- if(n->edge_tree) {
- free_edge_tree(n->edge_tree);
- }
-
sockaddrfree(&n->address);
ecdsa_free(n->ecdsa);
void node_del(meshlink_handle_t *mesh, node_t *n) {
timeout_del(&mesh->loop, &n->mtutimeout);
-
- for splay_each(edge_t, e, n->edge_tree) {
- edge_del(mesh, e);
- }
-
splay_delete(mesh->nodes, n);
}
return result;
}
-node_t *lookup_node_udp(meshlink_handle_t *mesh, const sockaddr_t *sa) {
- return hash_search(mesh->node_udp_cache, sa);
-}
-
-void update_node_udp(meshlink_handle_t *mesh, node_t *n, const sockaddr_t *sa) {
- if(n == mesh->self) {
- logger(mesh, MESHLINK_WARNING, "Trying to update UDP address of mesh->self!");
- return;
- }
-
- hash_insert(mesh->node_udp_cache, &n->address, NULL);
-
- if(sa) {
- n->address = *sa;
- n->sock = 0;
-
- for(int i = 0; i < mesh->listen_sockets; i++) {
- if(mesh->listen_socket[i].sa.sa.sa_family == sa->sa.sa_family) {
- n->sock = i;
- break;
- }
- }
-
- hash_insert(mesh->node_udp_cache, sa, n);
-
- node_add_recent_address(mesh, n, sa);
-
- if(mesh->log_level <= MESHLINK_DEBUG) {
- char *hostname = sockaddr2hostname(&n->address);
- logger(mesh, MESHLINK_DEBUG, "UDP address of %s set to %s", n->name, hostname);
- free(hostname);
- }
- }
-}
-
bool node_add_recent_address(meshlink_handle_t *mesh, node_t *n, const sockaddr_t *sa) {
(void)mesh;
bool found = false;
sockaddr_t recent[MAX_RECENT]; /* Recently seen addresses */
sockaddr_t catta_address; /* Latest address seen by Catta */
- // Graph-related member variables
- time_t last_reachable;
- time_t last_unreachable;
-
- int distance;
struct node_t *nexthop; /* nearest node from us to him */
- struct edge_t *prevedge; /* nearest node from him to us */
-
- struct splay_tree_t *edge_tree; /* Edges with this node as one of the endpoints */
} node_t;
void init_nodes(struct meshlink_handle *mesh);
void node_add(struct meshlink_handle *mesh, node_t *n);
void node_del(struct meshlink_handle *mesh, node_t *n);
node_t *lookup_node(struct meshlink_handle *mesh, const char *name) __attribute__((__warn_unused_result__));
-node_t *lookup_node_udp(struct meshlink_handle *mesh, const sockaddr_t *sa) __attribute__((__warn_unused_result__));
-void update_node_udp(struct meshlink_handle *mesh, node_t *n, const sockaddr_t *sa);
bool node_add_recent_address(struct meshlink_handle *mesh, node_t *n, const sockaddr_t *addr);
#endif
#define MAX_STRING_SIZE 2049
#define MAX_STRING "%2048s"
-#include "edge.h"
#include "net.h"
#include "node.h"
bool send_error(struct meshlink_handle *mesh, struct connection_t *, request_error_t, const char *);
bool send_ping(struct meshlink_handle *mesh, struct connection_t *);
bool send_pong(struct meshlink_handle *mesh, struct connection_t *);
-bool send_add_edge(struct meshlink_handle *mesh, struct connection_t *, const struct edge_t *, int contradictions);
-bool send_del_edge(struct meshlink_handle *mesh, struct connection_t *, const struct edge_t *, int contradictions);
bool send_req_key(struct meshlink_handle *mesh, struct node_t *);
bool send_canonical_address(struct meshlink_handle *mesh, struct node_t *);
#include "connection.h"
#include "devtools.h"
#include "ecdsa.h"
-#include "edge.h"
-#include "graph.h"
#include "logger.h"
#include "meshlink_internal.h"
#include "meta.h"
return send_request(mesh, c, NULL, "%d %s %d %x", ACK, mesh->myport, mesh->devclass, OPTION_PMTU_DISCOVERY | (PROT_MINOR << 24));
}
-static void send_everything(meshlink_handle_t *mesh, connection_t *c) {
- /* Send all known subnets and edges */
-
- for splay_each(node_t, n, mesh->nodes) {
- for inner_splay_each(edge_t, e, n->edge_tree) {
- send_add_edge(mesh, c, e, 0);
- }
- }
-}
-
bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
assert(request);
assert(*request);
n->connection->outgoing = NULL;
}
- /* Remove the edge before terminating the connection, to prevent a graph update. */
- edge_del(mesh, n->connection->edge);
- n->connection->edge = NULL;
-
terminate_connection(mesh, n->connection, false);
}
}
}
}
- /* Send him everything we know */
-
- send_everything(mesh, c);
-
- /* Create an edge_t for this connection */
-
- assert(devclass >= 0 && devclass < DEV_CLASS_COUNT);
-
- c->edge = new_edge();
- c->edge->from = mesh->self;
- c->edge->to = n;
- sockaddrcpy_setport(&c->edge->address, &c->address, atoi(hisport));
- c->edge->weight = mesh->dev_class_traits[devclass].edge_weight;
- c->edge->connection = c;
-
- node_add_recent_address(mesh, n, &c->address);
- edge_add(mesh, c->edge);
-
- /* Notify everyone of the new edge */
-
- send_add_edge(mesh, mesh->everyone, c->edge, 0);
-
- /* Run MST and SSSP algorithms */
-
- graph(mesh);
+ /* TODO: Create an edge_t for this connection, send it */
/* Request a session key to jump start UDP traffic */
#include "conf.h"
#include "connection.h"
-#include "edge.h"
-#include "graph.h"
#include "logger.h"
#include "meshlink_internal.h"
#include "meta.h"
#include "xalloc.h"
#include "submesh.h"
+#if 0
bool send_add_edge(meshlink_handle_t *mesh, connection_t *c, const edge_t *e, int contradictions) {
bool x;
char *address, *port;
return x;
}
+#endif
bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
assert(request);
assert(*request);
+ (void)mesh;
+ (void)c;
+#if 0
edge_t *e;
node_t *from, *to;
char from_name[MAX_STRING_SIZE];
/* Tell the rest about the new edge */
forward_request(mesh, c, s, request);
+#endif
+ /* TODO: Check if this is an edge we would own */
return true;
}
+#if 0
bool send_del_edge(meshlink_handle_t *mesh, connection_t *c, const edge_t *e, int contradictions) {
submesh_t *s = NULL;
return send_request(mesh, c, s, "%d %x %s %s %d %x", DEL_EDGE, prng(mesh, UINT_MAX),
e->from->name, e->to->name, contradictions, e->session_id);
}
+#endif
bool del_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
assert(request);
assert(*request);
+ (void)mesh;
+ (void)c;
+#if 0
edge_t *e;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
}
}
+#endif
+
+ /* TODO: Check if this is an edge we would own. */
return true;
}
return false;
}
- /* Append the known UDP address of the from node, if we have a confirmed one */
- if(!*address && from->status.udp_confirmed && from->address.sa.sa_family != AF_UNSPEC) {
- char *reflexive_address, *reflexive_port;
- logger(mesh, MESHLINK_DEBUG, "Appending reflexive UDP address to ANS_KEY from %s to %s", from->name, to->name);
- sockaddr2str(&from->address, &reflexive_address, &reflexive_port);
- send_request(mesh, to->nexthop->connection, NULL, "%s %s %s", request, reflexive_address, reflexive_port);
- free(reflexive_address);
- free(reflexive_port);
- return true;
- }
-
return send_request(mesh, to->nexthop->connection, NULL, "%s", request);
}
if(from == mesh->self) {
if(*key == '.' && *address && *port) {
- logger(mesh, MESHLINK_DEBUG, "Learned our own reflexive UDP address from %s: %s port %s", c->name, address, port);
-
- /* Inform all other nodes we want to communicate with and which are reachable via this connection */
- for splay_each(node_t, n, mesh->nodes) {
- if(n->nexthop != c->node) {
- continue;
- }
-
- if(n->status.udp_confirmed) {
- continue;
- }
-
- if(!n->status.waitingforkey && !n->status.validkey) {
- continue;
- }
-
- if(!n->nexthop->connection) {
- continue;
- }
-
- logger(mesh, MESHLINK_DEBUG, "Forwarding our own reflexive UDP address to %s", n->name);
- send_request(mesh, c, NULL, "%d %s %s . -1 -1 -1 0 %s %s", ANS_KEY, mesh->self->name, n->name, address, port);
- }
+ /* Ignore reflexive UDP address */
} else {
logger(mesh, MESHLINK_WARNING, "Got %s from %s from %s to %s",
"ANS_KEY", c->name, from_name, to_name);
if(from->status.validkey) {
if(*address && *port) {
- logger(mesh, MESHLINK_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
- sockaddr_t sa = str2sockaddr(address, port);
- update_node_udp(mesh, from, &sa);
+ /* Ignore reflexive UDP address */
}
-
- send_mtu_probe(mesh, from);
}
return true;
assert(self);
assert(!strcmp(self->name, "foo"));
- // Check that we are not reachable.
-
- time_t last_reachable;
- time_t last_unreachable;
- assert(!meshlink_get_node_reachability(mesh, self, &last_reachable, &last_unreachable));
- assert(!last_reachable);
- assert(!last_unreachable);
-
// Start and stop the mesh.
assert(meshlink_start(mesh));
- // Check that we are now reachable
-
- assert(meshlink_get_node_reachability(mesh, self, &last_reachable, &last_unreachable));
- assert(last_reachable);
- assert(!last_unreachable);
-
meshlink_stop(mesh);
- // Check that we are no longer reachable.
-
- assert(!meshlink_get_node_reachability(mesh, self, &last_reachable, &last_unreachable));
- assert(last_reachable);
- assert(last_unreachable);
-
// Make sure we can start and stop the mesh again.
assert(meshlink_start(mesh));
assert(!strcmp(mesh->name, "foo"));
assert(!strcmp(self->name, "foo"));
- // Check that we remembered we were reachable
-
- assert(!meshlink_get_node_reachability(mesh, self, &last_reachable, &last_unreachable));
- assert(last_reachable);
- assert(last_unreachable);
-
// Check that the name is ignored now, and that we still are "foo".
assert(!meshlink_get_node(mesh, "bar"));
assert(self);
assert(!strcmp(self->name, "foo"));
- // Disable local discovery.
-
- mesh.enable_discovery(false);
-
// Start and stop the mesh.
assert(mesh.start());
// Start and stop the mesh.
- mesh.enable_discovery(false);
-
assert(mesh.start());
mesh.stop();
}
mesh_b->priv = in_infos;
- meshlink_enable_discovery(mesh_a, false);
- meshlink_enable_discovery(mesh_b, false);
-
// Set the callbacks.
meshlink_set_channel_accept_cb(mesh_b, accept_cb);
meshlink_handle_t *mesh = meshlink_open("channels_fork_conf.1", "foo", "channels-fork", DEV_CLASS_BACKBONE);
assert(mesh);
- meshlink_enable_discovery(mesh, false);
-
assert(meshlink_set_canonical_address(mesh, meshlink_get_self(mesh), "localhost", NULL));
char *data = meshlink_export(mesh);
meshlink_handle_t *mesh = meshlink_open("channels_fork_conf.2", "bar", "channels-fork", DEV_CLASS_BACKBONE);
assert(mesh);
- meshlink_enable_discovery(mesh, false);
-
assert(meshlink_set_canonical_address(mesh, meshlink_get_self(mesh), "localhost", NULL));
char *data = meshlink_export(mesh);
assert(meshlink_destroy("channels_udp_conf.0"));
meshlink_handle_t *server = meshlink_open("channels_udp_conf.0", "server", "channels-udp", DEV_CLASS_BACKBONE);
assert(server);
- meshlink_enable_discovery(server, false);
server->priv = channels;
meshlink_set_channel_accept_cb(server, accept_cb);
assert(meshlink_start(server));
clients[i].mesh = meshlink_open(dir, names[i], "channels-udp", DEV_CLASS_STATIONARY);
assert(clients[i].mesh);
clients[i].mesh->priv = &clients[i];
- meshlink_enable_discovery(clients[i].mesh, false);
link_meshlink_pair(server, clients[i].mesh);
meshlink_set_node_status_cb(clients[i].mesh, status_cb);
assert(meshlink_start(clients[i].mesh));
assert(mesh[i]);
assert(meshlink_set_canonical_address(mesh[i], meshlink_get_self(mesh[i]), "localhost", NULL));
- meshlink_enable_discovery(mesh[i], false);
meshlink_set_node_duplicate_cb(mesh[i], handle_duplicate);
}
assert(nnodes == 1);
assert(nodes[0] == meshlink_get_self(mesh[0]));
- // We should never have been online.
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, -1, nodes, &nnodes);
- assert(nnodes == 0);
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, 0, nodes, &nnodes);
- assert(nnodes == 1);
- assert(nodes[0] == meshlink_get_self(mesh[0]));
-
// Let nodes know about each other.
for(int i = 0; i < 3; i++) {
- meshlink_enable_discovery(mesh[i], false);
assert(meshlink_set_canonical_address(mesh[i], meshlink_get_self(mesh[i]), "localhost", NULL));
char *data = meshlink_export(mesh[i]);
assert(data);
nodes = meshlink_get_all_nodes_by_dev_class(mesh[0], DEV_CLASS_STATIONARY, nodes, &nnodes);
assert(nnodes == 2);
- // But no node should have been online.
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, -1, nodes, &nnodes);
- assert(nnodes == 0);
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, 0, nodes, &nnodes);
- assert(nnodes == 3);
-
// Start foo.
time_t foo_started = time(NULL);
assert(meshlink_start(mesh[0]));
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, -1, nodes, &nnodes);
- assert(nnodes == 1);
- assert(nodes[0] == meshlink_get_self(mesh[0]));
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, 0, nodes, &nnodes);
- assert(nnodes == 2);
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], foo_started - 1, -1, nodes, &nnodes);
- assert(nnodes == 1);
- assert(nodes[0] == meshlink_get_self(mesh[0]));
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 1, foo_started - 1, nodes, &nnodes);
- assert(nnodes == 0);
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 1, foo_started + 1, nodes, &nnodes);
- assert(nnodes == 1);
- assert(nodes[0] == meshlink_get_self(mesh[0]));
-
// Start bar and wait for it to connect.
meshlink_set_node_status_cb(mesh[0], status_cb);
assert(wait_sync_flag(&bar_reachable, 20));
time_t bar_started = time(NULL);
- // Validate time ranges.
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, -1, nodes, &nnodes);
- assert(nnodes == 2);
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, 0, nodes, &nnodes);
- assert(nnodes == 1);
- assert(nodes[0] == meshlink_get_node(mesh[0], "baz"));
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 1, foo_started + 1, nodes, &nnodes);
- assert(nnodes == 1);
- assert(nodes[0] == meshlink_get_self(mesh[0]));
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], bar_started, bar_started, nodes, &nnodes);
- assert(nnodes == 2);
- assert(nodes[0] == meshlink_get_node(mesh[0], "bar"));
- assert(nodes[1] == meshlink_get_self(mesh[0]));
-
// Stop bar.
meshlink_stop(mesh[1]);
sleep(2);
- time_t bar_stopped = time(NULL);
-
- // Validate we can see when bar was reachable.
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], bar_stopped, bar_stopped, nodes, &nnodes);
- assert(nnodes == 1);
- assert(nodes[0] == meshlink_get_self(mesh[0]));
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], bar_started, bar_started, nodes, &nnodes);
- assert(nnodes == 2);
- assert(nodes[0] == meshlink_get_node(mesh[0], "bar"));
- assert(nodes[1] == meshlink_get_self(mesh[0]));
// Close and restart foo, check that it remembers correctly.
meshlink_close(mesh[0]);
sleep(2);
- time_t foo_stopped = time(NULL);
mesh[0] = meshlink_open("get_all_nodes_conf.1", "foo", "get-all_nodes", DEV_CLASS_BACKBONE);
assert(mesh[0]);
nodes = meshlink_get_all_nodes_by_dev_class(mesh[0], DEV_CLASS_STATIONARY, nodes, &nnodes);
assert(nnodes == 2);
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, 0, nodes, &nnodes);
- assert(nnodes == 1);
- assert(nodes[0] == meshlink_get_node(mesh[0], "baz"));
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 0, -1, nodes, &nnodes);
- assert(nnodes == 2);
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 1, foo_started - 1, nodes, &nnodes);
- assert(nnodes == 0);
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], 1, foo_started + 1, nodes, &nnodes);
- assert(nnodes == 1);
- assert(nodes[0] == meshlink_get_self(mesh[0]));
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], bar_started, bar_started, nodes, &nnodes);
- assert(nnodes == 2);
- assert(nodes[0] == meshlink_get_node(mesh[0], "bar"));
- assert(nodes[1] == meshlink_get_self(mesh[0]));
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], bar_stopped, bar_stopped, nodes, &nnodes);
- assert(nnodes == 1);
- assert(nodes[0] == meshlink_get_self(mesh[0]));
-
- nodes = meshlink_get_all_nodes_by_last_reachable(mesh[0], foo_stopped, -1, nodes, &nnodes);
- assert(nnodes == 0);
-
// Clean up.
for(int i = 0; i < 3; i++) {
meshlink_handle_t *mesh2 = meshlink_open("import_export_conf.2", "bar", "import-export", DEV_CLASS_BACKBONE);
assert(mesh2);
- // Disable local discovery
-
- meshlink_enable_discovery(mesh1, false);
- meshlink_enable_discovery(mesh2, false);
-
// Import and export both side's data
assert(meshlink_set_canonical_address(mesh1, meshlink_get_self(mesh1), "localhost", NULL));
meshlink_handle_t *mesh3 = meshlink_open("invite_join_conf.3", "quux", "invite-join", DEV_CLASS_BACKBONE);
assert(mesh3);
- // Disable local discovery.
-
- meshlink_enable_discovery(mesh1, false);
- meshlink_enable_discovery(mesh2, false);
- meshlink_enable_discovery(mesh3, false);
-
// Have the first instance generate invitations.
meshlink_set_node_status_cb(mesh1, status_cb);
assert(peers[i].mesh);
free(params);
free(conf_path);
-
- meshlink_enable_discovery(peers[i].mesh, false);
}
}
mesh2 = meshlink_open("storage-policy_conf.2", "bar", "storage-policy", DEV_CLASS_BACKBONE);
assert(mesh1);
assert(mesh2);
- meshlink_enable_discovery(mesh1, false);
- meshlink_enable_discovery(mesh2, false);
meshlink_set_storage_policy(mesh1, MESHLINK_STORAGE_DISABLED);
meshlink_set_storage_policy(mesh2, MESHLINK_STORAGE_DISABLED);
mesh2 = meshlink_open("storage-policy_conf.2", "bar", "storage-policy", DEV_CLASS_BACKBONE);
assert(mesh1);
assert(mesh2);
- meshlink_enable_discovery(mesh1, false);
- meshlink_enable_discovery(mesh2, false);
meshlink_set_storage_policy(mesh1, MESHLINK_STORAGE_KEYS_ONLY);
meshlink_set_storage_policy(mesh2, MESHLINK_STORAGE_KEYS_ONLY);
mesh2 = meshlink_open("storage-policy_conf.2", "bar", "storage-policy", DEV_CLASS_BACKBONE);
assert(mesh1);
assert(mesh2);
- meshlink_enable_discovery(mesh1, false);
- meshlink_enable_discovery(mesh2, false);
meshlink_set_storage_policy(mesh1, MESHLINK_STORAGE_KEYS_ONLY);
meshlink_set_storage_policy(mesh2, MESHLINK_STORAGE_KEYS_ONLY);
mesh2 = meshlink_open("storage-policy_conf.2", "bar", "storage-policy", DEV_CLASS_BACKBONE);
assert(mesh1);
assert(mesh2);
- meshlink_enable_discovery(mesh1, false);
- meshlink_enable_discovery(mesh2, false);
meshlink_set_storage_policy(mesh1, MESHLINK_STORAGE_KEYS_ONLY);
meshlink_set_storage_policy(mesh2, MESHLINK_STORAGE_KEYS_ONLY);
mesh2 = meshlink_open("storage-policy_conf.2", "bar", "storage-policy", DEV_CLASS_BACKBONE);
assert(mesh1);
assert(mesh2);
- meshlink_enable_discovery(mesh1, false);
- meshlink_enable_discovery(mesh2, false);
assert(!meshlink_get_node_reachability(mesh1, meshlink_get_node(mesh1, "bar"), &last_reachable, &last_unreachable));
assert(last_reachable);
mesh2 = meshlink_open("storage-policy_conf.2", "bar", "storage-policy", DEV_CLASS_BACKBONE);
assert(mesh1);
assert(mesh2);
- meshlink_enable_discovery(mesh1, false);
- meshlink_enable_discovery(mesh2, false);
meshlink_set_storage_policy(mesh1, MESHLINK_STORAGE_KEYS_ONLY);
meshlink_set_storage_policy(mesh2, MESHLINK_STORAGE_KEYS_ONLY);
assert(wait_sync_flag(&received, 15));
- // Check that the second and third node have autoconnected to each other
-
- devtool_edge_t *edges = NULL;
- size_t nedges = 0;
- assert_after((edges = devtool_get_all_edges(mesh[1], edges, &nedges), nedges == 3), 15);
- free(edges);
-
// Stop the first node
meshlink_stop(mesh[0]);
assert(wait_sync_flag(&bar_learned_baz, 5));
assert(wait_sync_flag(&baz_learned_bar, 5));
- // Check that the second and third node autoconnect to each other
-
- devtool_edge_t *edges = NULL;
- size_t nedges = 0;
- assert_after((edges = devtool_get_all_edges(mesh[1], edges, &nedges), nedges == 3), 15);
- free(edges);
-
// Stop the nodes nodes
for(int i = 0; i < 3; i++) {
free(a_name);
free(b_name);
- meshlink_enable_discovery(a, false);
- meshlink_enable_discovery(b, false);
-
link_meshlink_pair(a, b);
*pa = a;
assert(a);
assert(b);
- meshlink_enable_discovery(a, false);
- meshlink_enable_discovery(b, false);
-
link_meshlink_pair(a, b);
*pa = a;