static const sockaddr_t mdns_address_ipv6 = {
.in6.sin6_family = AF_INET6,
- .in6.sin6_addr.s6_addr[0x0] = 0xfd,
+ .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,
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) {
}
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,
struct cmsghdr align;
} u;
+ memset(&u, 0, sizeof(u));
+
struct msghdr msg = {
.msg_name = (struct sockaddr *) &dest->sa,
.msg_namelen = SALEN(dest->sa),
.msg_controllen = sizeof(u.buf),
};
-
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
}
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,
}
}
-static void send_mdns_packet(meshlink_handle_t *mesh, const discovery_address_t *addr) {
+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
- int fd;
- 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 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);
+ 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:
- fd = mesh->discovery.sockets[0].fd;
-#ifdef IP_MULTICAST_IF
- {
- struct ip_mreqn mreq = {
- .imr_address = addr->address.in.sin_addr,
- .imr_ifindex = addr->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
-
- 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);
+ send_mdns_packet_ipv4(mesh, mesh->discovery.sockets[0].fd, addr->index, &addr->address, &mdns_address_ipv4, msg, msg_size);
break;
case AF_INET6:
- fd = mesh->discovery.sockets[1].fd;
-#ifdef IPV6_MULTICAST_IF
-
- if(setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &addr->index, sizeof(addr->index)) != 0) {
- logger(mesh, MESHLINK_ERROR, "Could not set outgoing multicast interface on IPv6 socket");
- return;
- }
-
-#endif
-
- 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);
+ send_mdns_packet_ipv6(mesh, mesh->discovery.sockets[1].fd, addr->index, &addr->address, &mdns_address_ipv6, msg, msg_size);
break;
default:
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)) {
}
}
} 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);
+
+ 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);
}
.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));
.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]);
+ send_mdns_packet(mesh, &mesh->discovery.addresses[i], false);
}
}
mesh->discovery.addresses[mesh->discovery.address_count - 1].up = up;
if(up) {
- send_mdns_packet(mesh, &mesh->discovery.addresses[mesh->discovery.address_count - 1]);
+ 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);
if(loop->now.tv_sec > mesh->discovery.last_update + 5) {
mesh->discovery.last_update = loop->now.tv_sec;
- handle_network_change(mesh, 1);
+ handle_network_change(mesh, true);
}
}
}
}
#elif defined(__APPLE__)
-static void network_change_callback(SCDynamicStoreRef store, CFArrayRef keys, void *info) {
- (void)store;
- (void)keys;
+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);
- logger(mesh, MESHLINK_ERROR, "Network change detected!");
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, 1);
+ handle_network_change(mesh, true);
}
pthread_mutex_unlock(&mesh->mutex);
mesh->discovery.runloop = CFRunLoopGetCurrent();
- SCDynamicStoreContext context = {0, mesh, NULL, NULL, NULL};
- SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("network_change_handler"), network_change_callback, &context);
- CFStringRef interfaces = SCDynamicStoreKeyCreate(NULL, CFSTR("State:/Network/Interface"), kCFStringEncodingUTF8);
- CFStringRef ipv4 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
- CFStringRef ipv6 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6);
- CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- CFRunLoopSourceRef runloop_source = NULL;
-
- if(!store) {
- logger(mesh, MESHLINK_ERROR, "Error setting up network change handler: %s\n", SCErrorString(SCError()));
- goto exit;
- }
-
- if(!interfaces || !ipv4 || !ipv6 || !keys || !patterns) {
- logger(mesh, MESHLINK_ERROR, "Error setting up network change handler: %s\n", SCErrorString(SCError()));
- goto exit;
- }
+ SCNetworkReachabilityRef reach_v4 = SCNetworkReachabilityCreateWithName(NULL, "93.184.216.34");
+ SCNetworkReachabilityRef reach_v6 = SCNetworkReachabilityCreateWithName(NULL, "2606:2800:220:1:248:1893:25c8:1946");
- CFArrayAppendValue(keys, interfaces);
- CFArrayAppendValue(patterns, ipv4);
- CFArrayAppendValue(patterns, ipv6);
+ SCNetworkReachabilityContext context;
+ memset(&context, 0, sizeof(context));
+ context.info = mesh;
- if(!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) {
- logger(mesh, MESHLINK_ERROR, "Error setting up network change handler: %s\n", SCErrorString(SCError()));
- goto exit;
+ 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");
}
- runloop_source = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
-
- if(!runloop_source) {
- logger(mesh, MESHLINK_ERROR, "Error setting up network change handler: %s\n", SCErrorString(SCError()));
- goto exit;
+ 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");
}
- CFRunLoopAddSource(CFRunLoopGetCurrent(), runloop_source, kCFRunLoopDefaultMode);
CFRunLoopRun();
-exit:
-
- if(runloop_source) {
- CFRelease(runloop_source);
- }
-
- if(interfaces) {
- CFRelease(interfaces);
- }
-
- if(ipv4) {
- CFRelease(ipv4);
- }
-
- if(ipv6) {
- CFRelease(ipv6);
- }
-
- if(keys) {
- CFRelease(keys);
- }
+ mesh->discovery.runloop = NULL;
- if(patterns) {
- CFRelease(patterns);
+ if(reach_v4) {
+ SCNetworkReachabilityUnscheduleFromRunLoop(reach_v4, mesh->discovery.runloop, kCFRunLoopDefaultMode);
+ CFRelease(reach_v4);
}
- if(store) {
- CFRelease(store);
+ if(reach_v6) {
+ SCNetworkReachabilityUnscheduleFromRunLoop(reach_v6, mesh->discovery.runloop, kCFRunLoopDefaultMode);
+ CFRelease(reach_v6);
}
- mesh->discovery.runloop = NULL;
-
return NULL;
-
}
#elif defined(RTM_NEWADDR)
static void pfroute_parse_iface(meshlink_handle_t *mesh, const struct rt_msghdr *rtm) {
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));
+ 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));
+ 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));
+ 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);
+ 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);
+ }
}
- sockaddr_t sa6 = {
- .in6.sin6_family = AF_INET6,
- .in6.sin6_port = ntohs(5353),
- };
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));
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
#ifdef SO_REUSEPORT
- setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
+ 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));
+ 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 IPv4 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(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)
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]);
+ send_mdns_packet(mesh, &mesh->discovery.addresses[i], false);
}
}
}