X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=src%2Fdiscovery.c;h=4d05f7bc98e0373b4e2afc5685291c1025a639a4;hb=325d837df2ec867c668b6a911b97e719f196ba0e;hp=439bcbef479a1e6053ab2109bee4f9650098934f;hpb=e7086ece14ba7e10f701af465c04c77d2b060f2a;p=meshlink diff --git a/src/discovery.c b/src/discovery.c index 439bcbef..4d05f7bc 100644 --- a/src/discovery.c +++ b/src/discovery.c @@ -1,7 +1,33 @@ +/* + discovery.c -- local network discovery + Copyright (C) 2014-2021 Guus Sliepen + + 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__) || defined(__unix) && !defined(__linux) +#if defined(__APPLE__) +#include +#include +#include +#include +#include +#include +#elif defined(__unix) && !defined(__linux) #include #include #include @@ -95,6 +121,8 @@ static void send_mdns_packet_ipv4(meshlink_handle_t *mesh, int fd, int index, co struct cmsghdr align; } u; + memset(&u, 0, sizeof(u)); + struct msghdr msg = { .msg_name = (struct sockaddr *) &dest->sa, .msg_namelen = SALEN(dest->sa), @@ -104,7 +132,6 @@ static void send_mdns_packet_ipv4(meshlink_handle_t *mesh, int fd, int index, co .msg_controllen = sizeof(u.buf), }; - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = IPPROTO_IP; cmsg->cmsg_type = IP_PKTINFO; @@ -178,11 +205,12 @@ static void send_mdns_packet_ipv6(meshlink_handle_t *mesh, int fd, int index, co static void send_mdns_packet(meshlink_handle_t *mesh, const discovery_address_t *addr) { // Configure the socket to send the packet to the right interface int fd; - uint8_t data[1024]; + uint8_t request[1024], response[1024]; char *fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self); const char *keys[] = {MESHLINK_MDNS_NAME_KEY, MESHLINK_MDNS_FINGERPRINT_KEY}; const char *values[] = {mesh->name, fingerprint}; - size_t size = prepare_packet(data, sizeof data, fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, values, false); + size_t request_size = prepare_request(request, sizeof request, mesh->appname, "tcp"); + size_t response_size = prepare_response(response, sizeof response, fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, values); free(fingerprint); switch(addr->address.sa.sa_family) { @@ -203,7 +231,8 @@ static void send_mdns_packet(meshlink_handle_t *mesh, const discovery_address_t #endif - send_mdns_packet_ipv4(mesh, fd, addr->index, &addr->address, &mdns_address_ipv4, data, size); + send_mdns_packet_ipv4(mesh, fd, addr->index, &addr->address, &mdns_address_ipv4, request, request_size); + send_mdns_packet_ipv4(mesh, fd, addr->index, &addr->address, &mdns_address_ipv4, response, response_size); break; case AF_INET6: @@ -217,7 +246,8 @@ static void send_mdns_packet(meshlink_handle_t *mesh, const discovery_address_t #endif - send_mdns_packet_ipv6(mesh, fd, addr->index, &addr->address, &mdns_address_ipv6, data, size); + send_mdns_packet_ipv6(mesh, fd, addr->index, &addr->address, &mdns_address_ipv6, request, request_size); + send_mdns_packet_ipv6(mesh, fd, addr->index, &addr->address, &mdns_address_ipv6, response, response_size); break; default: @@ -248,9 +278,8 @@ static void mdns_io_handler(event_loop_t *loop, void *data, int flags) { uint16_t port = 0; const char *keys[2] = {MESHLINK_MDNS_NAME_KEY, MESHLINK_MDNS_FINGERPRINT_KEY}; char *values[2] = {NULL, NULL}; - bool response; - if(parse_packet(buf, len, &name, mesh->appname, "tcp", &port, 2, keys, values, &response)) { + 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) { @@ -258,15 +287,6 @@ static void mdns_io_handler(event_loop_t *loop, void *data, int flags) { logger(mesh, MESHLINK_INFO, "Node %s discovered on the local network.\n", n->name); } - if(!response && n != mesh->self) { - // Send a unicast response back - char *fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self); - const char *response_values[] = {mesh->name, fingerprint}; - size_t size = prepare_packet(buf, sizeof(buf), fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, response_values, true); - sendto(io->fd, buf, size, MSG_DONTWAIT | MSG_NOSIGNAL, &sa.sa, sl); - free(fingerprint); - } - switch(sa.sa.sa_family) { case AF_INET: sa.in.sin_port = htons(port); @@ -306,6 +326,13 @@ static void mdns_io_handler(event_loop_t *loop, void *data, int flags) { } } } + } else if(parse_request(buf, len, mesh->appname, "tcp")) { + // Send a unicast response back + 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); + sendto(io->fd, buf, size, MSG_DONTWAIT | MSG_NOSIGNAL, &sa.sa, sl); + free(fingerprint); } free(name); @@ -351,7 +378,7 @@ static void iface_up(meshlink_handle_t *mesh, int index) { handle_network_change(mesh, true); } -static void iface_down(meshlink_handle_t *const mesh, int index) { +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) { @@ -406,6 +433,110 @@ static void addr_del(meshlink_handle_t *mesh, const discovery_address_t *addr) { 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 { @@ -519,7 +650,6 @@ static void netlink_parse(meshlink_handle_t *mesh, const void *data, size_t len) static void netlink_io_handler(event_loop_t *loop, void *data, int flags) { (void)flags; (void)data; - static time_t prev_update; meshlink_handle_t *mesh = loop->data; struct { @@ -552,13 +682,75 @@ static void netlink_io_handler(event_loop_t *loop, void *data, int flags) { } else { netlink_parse(mesh, &msg, result); - if(loop->now.tv_sec > prev_update + 5) { - prev_update = loop->now.tv_sec; - handle_network_change(mesh, 1); + 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; @@ -679,7 +871,9 @@ bool discovery_start(meshlink_handle_t *mesh) { .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 setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one8, sizeof(one8)); setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl8, sizeof(ttl8)); @@ -700,7 +894,9 @@ bool discovery_start(meshlink_handle_t *mesh) { } setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); +#ifdef SO_REUSEPORT setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &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)); @@ -726,11 +922,17 @@ bool discovery_start(meshlink_handle_t *mesh) { 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); @@ -740,51 +942,7 @@ bool discovery_start(meshlink_handle_t *mesh) { logger(mesh, MESHLINK_WARNING, "Could not open PF_ROUTE socket: %s", strerror(errno)); } - struct ifaddrs *ifa = NULL; - - if(getifaddrs(&ifa) == -1) { - logger(mesh, MESHLINK_ERROR, "Could not get list of interface addresses: %s", strerror(errno)); - return true; - } - - 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); - } - - 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); - + scan_ifaddrs(mesh); #endif return true; @@ -802,6 +960,15 @@ void discovery_stop(meshlink_handle_t *mesh) { 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);