]> git.meshlink.io Git - meshlink/blob - src/discovery.c
Handle network change detection on macOS and iOS.
[meshlink] / src / discovery.c
1 #define __APPLE_USE_RFC_3542
2 #include "system.h"
3
4 #if defined(__APPLE__)
5 #include <CoreFoundation/CoreFoundation.h>
6 #include <CoreFoundation/CFArray.h>
7 #include <CoreFoundation/CFString.h>
8 #include <SystemConfiguration/SystemConfiguration.h>
9 #include <net/if.h>
10 #include <netinet/in.h>
11 #elif defined(__unix) && !defined(__linux)
12 #include <net/if.h>
13 #include <net/route.h>
14 #include <netinet/in.h>
15 #elif defined(__linux)
16 #include <asm/types.h>
17 #include <net/if.h>
18 #include <linux/if_link.h>
19 #include <linux/netlink.h>
20 #include <linux/rtnetlink.h>
21 #endif
22
23 #include "mdns.h"
24 #include "meshlink_internal.h"
25 #include "event.h"
26 #include "discovery.h"
27 #include "sockaddr.h"
28 #include "logger.h"
29 #include "netutl.h"
30 #include "node.h"
31 #include "connection.h"
32 #include "utils.h"
33 #include "xalloc.h"
34
35 #define MESHLINK_MDNS_SERVICE_TYPE "_%s._tcp"
36 #define MESHLINK_MDNS_NAME_KEY "name"
37 #define MESHLINK_MDNS_FINGERPRINT_KEY "fingerprint"
38
39 #ifndef MSG_NOSIGNAL
40 #define MSG_NOSIGNAL 0
41 #endif
42
43 #ifndef IPV6_ADD_MEMBERSHIP
44 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
45 #endif
46
47 #ifndef IPV6_DROP_MEMBERSHIP
48 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
49 #endif
50
51 static const sockaddr_t mdns_address_ipv4 = {
52         .in.sin_family = AF_INET,
53         .in.sin_addr.s_addr = 0xfb0000e0,
54         .in.sin_port = 0xe914,
55 };
56
57 static const sockaddr_t mdns_address_ipv6 = {
58         .in6.sin6_family = AF_INET6,
59         .in6.sin6_addr.s6_addr[0x0] = 0xfd,
60         .in6.sin6_addr.s6_addr[0x1] = 0x02,
61         .in6.sin6_addr.s6_addr[0xf] = 0xfb,
62         .in6.sin6_port = 0xe914,
63 };
64
65 typedef struct discovery_address {
66         int index;
67         bool up;
68         sockaddr_t address;
69 } discovery_address_t;
70
71 static int iface_compare(const void *va, const void *vb) {
72         const int *a = va;
73         const int *b = vb;
74         return *a - *b;
75 }
76
77 static int address_compare(const void *va, const void *vb) {
78         const discovery_address_t *a = va;
79         const discovery_address_t *b = vb;
80
81         if(a->index != b->index) {
82                 return a->index - b->index;
83         }
84
85         return sockaddrcmp_noport(&a->address, &b->address);
86 }
87
88 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) {
89 #ifdef IP_PKTINFO
90         struct iovec iov  = {
91                 .iov_base = data,
92                 .iov_len = len,
93         };
94
95         struct in_pktinfo pkti = {
96                 .ipi_ifindex = index,
97                 .ipi_addr = src->in.sin_addr,
98         };
99
100         union {
101                 char buf[CMSG_SPACE(sizeof(pkti))];
102                 struct cmsghdr align;
103         } u;
104
105         struct msghdr msg = {
106                 .msg_name = (struct sockaddr *) &dest->sa,
107                 .msg_namelen = SALEN(dest->sa),
108                 .msg_iov = &iov,
109                 .msg_iovlen = 1,
110                 .msg_control = u.buf,
111                 .msg_controllen = sizeof(u.buf),
112         };
113
114
115         struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
116         cmsg->cmsg_level = IPPROTO_IP;
117         cmsg->cmsg_type = IP_PKTINFO;
118         cmsg->cmsg_len = CMSG_LEN(sizeof(pkti));
119         memcpy(CMSG_DATA(cmsg), &pkti, sizeof(pkti));
120
121         // Send the packet
122         ssize_t result = sendmsg(fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL);
123 #else
124         (void)index;
125         (void)src;
126
127         // Send the packet
128         ssize_t result = sendto(fd, data, len, MSG_DONTWAIT | MSG_NOSIGNAL, &dest->sa, SALEN(dest->sa));
129 #endif
130
131         if(result <= 0) {
132                 logger(mesh, MESHLINK_ERROR, "Error sending multicast packet: %s", strerror(errno));
133         }
134 }
135
136 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) {
137 #ifdef IPV6_PKTINFO
138         struct iovec iov  = {
139                 .iov_base = data,
140                 .iov_len = len,
141         };
142
143         struct in6_pktinfo pkti = {
144                 .ipi6_ifindex = index,
145                 .ipi6_addr = src->in6.sin6_addr,
146         };
147
148         union {
149                 char buf[CMSG_SPACE(sizeof(pkti))];
150                 struct cmsghdr align;
151         } u;
152
153         memset(&u, 0, sizeof u);
154
155         struct msghdr msg = {
156                 .msg_name = (struct sockaddr *) &dest->sa,
157                 .msg_namelen = SALEN(dest->sa),
158                 .msg_iov = &iov,
159                 .msg_iovlen = 1,
160                 .msg_control = u.buf,
161                 .msg_controllen = CMSG_LEN(sizeof(pkti)),
162         };
163
164         struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
165         cmsg->cmsg_level = IPPROTO_IPV6;
166         cmsg->cmsg_type = IPV6_PKTINFO;
167         cmsg->cmsg_len = CMSG_LEN(sizeof(pkti));
168         memcpy(CMSG_DATA(cmsg), &pkti, sizeof(pkti));
169
170         // Send the packet
171         ssize_t result = sendmsg(fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL);
172 #else
173         (void)index;
174         (void)src;
175
176         // Send the packet
177         ssize_t result = sendto(fd, data, len, MSG_DONTWAIT | MSG_NOSIGNAL, &dest->sa, SALEN(dest->sa));
178 #endif
179
180         if(result <= 0) {
181                 logger(mesh, MESHLINK_ERROR, "Error sending multicast packet: %s", strerror(errno));
182         }
183 }
184
185 static void send_mdns_packet(meshlink_handle_t *mesh, const discovery_address_t *addr) {
186         // Configure the socket to send the packet to the right interface
187         int fd;
188         uint8_t request[1024], response[1024];
189         char *fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self);
190         const char *keys[] = {MESHLINK_MDNS_NAME_KEY, MESHLINK_MDNS_FINGERPRINT_KEY};
191         const char *values[] = {mesh->name, fingerprint};
192         size_t request_size = prepare_request(request, sizeof request, mesh->appname, "tcp");
193         size_t response_size = prepare_response(response, sizeof response, fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, values);
194         free(fingerprint);
195
196         switch(addr->address.sa.sa_family) {
197         case AF_INET:
198                 fd = mesh->discovery.sockets[0].fd;
199 #ifdef IP_MULTICAST_IF
200                 {
201                         struct ip_mreqn mreq = {
202                                 .imr_address = addr->address.in.sin_addr,
203                                 .imr_ifindex = addr->index,
204                         };
205
206                         if(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) != 0) {
207                                 logger(mesh, MESHLINK_ERROR, "Could not set outgoing multicast interface on IPv4 socket");
208                                 return;
209                         }
210                 }
211
212 #endif
213
214                 send_mdns_packet_ipv4(mesh, fd, addr->index, &addr->address, &mdns_address_ipv4, request, request_size);
215                 send_mdns_packet_ipv4(mesh, fd, addr->index, &addr->address, &mdns_address_ipv4, response, response_size);
216                 break;
217
218         case AF_INET6:
219                 fd = mesh->discovery.sockets[1].fd;
220 #ifdef IPV6_MULTICAST_IF
221
222                 if(setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &addr->index, sizeof(addr->index)) != 0) {
223                         logger(mesh, MESHLINK_ERROR, "Could not set outgoing multicast interface on IPv6 socket");
224                         return;
225                 }
226
227 #endif
228
229                 send_mdns_packet_ipv6(mesh, fd, addr->index, &addr->address, &mdns_address_ipv6, request, request_size);
230                 send_mdns_packet_ipv6(mesh, fd, addr->index, &addr->address, &mdns_address_ipv6, response, response_size);
231                 break;
232
233         default:
234                 break;
235         }
236 }
237
238 static void mdns_io_handler(event_loop_t *loop, void *data, int flags) {
239         (void)flags;
240         meshlink_handle_t *mesh = loop->data;
241         io_t *io = data;
242         uint8_t buf[1024];
243         sockaddr_t sa;
244         socklen_t sl = sizeof(sa);
245
246         ssize_t len = recvfrom(io->fd, buf, sizeof(buf), MSG_DONTWAIT, &sa.sa, &sl);
247
248         if(len == -1) {
249                 if(!sockwouldblock(errno)) {
250                         logger(mesh, MESHLINK_ERROR, "Error reading from mDNS discovery socket: %s", strerror(errno));
251                         io_set(loop, io, 0);
252                 }
253
254                 return;
255         }
256
257         char *name = NULL;
258         uint16_t port = 0;
259         const char *keys[2] = {MESHLINK_MDNS_NAME_KEY, MESHLINK_MDNS_FINGERPRINT_KEY};
260         char *values[2] = {NULL, NULL};
261
262         if(parse_response(buf, len, &name, mesh->appname, "tcp", &port, 2, keys, values)) {
263                 node_t *n = (node_t *)meshlink_get_node(mesh, values[0]);
264
265                 if(n) {
266                         if(n != mesh->self) {
267                                 logger(mesh, MESHLINK_INFO, "Node %s discovered on the local network.\n", n->name);
268                         }
269
270                         switch(sa.sa.sa_family) {
271                         case AF_INET:
272                                 sa.in.sin_port = htons(port);
273                                 break;
274
275                         case AF_INET6:
276                                 sa.in6.sin6_port = htons(port);
277                                 break;
278
279                         default:
280                                 logger(mesh, MESHLINK_WARNING, "Could not resolve node %s to a known address family type.\n", n->name);
281                                 sa.sa.sa_family = AF_UNKNOWN;
282                                 break;
283                         }
284
285                         if(sa.sa.sa_family != AF_UNKNOWN) {
286                                 n->catta_address = sa;
287                                 n->last_connect_try = 0;
288                                 node_add_recent_address(mesh, n, &sa);
289
290                                 if(n->connection) {
291                                         n->connection->last_ping_time = -3600;
292                                 }
293
294                                 for list_each(outgoing_t, outgoing, mesh->outgoings) {
295                                         if(outgoing->node != n) {
296                                                 continue;
297                                         }
298
299                                         outgoing->timeout = 0;
300
301                                         if(outgoing->ev.cb) {
302                                                 timeout_set(&mesh->loop, &outgoing->ev, &(struct timespec) {
303                                                         0, 0
304                                                 });
305                                         }
306                                 }
307                         }
308                 }
309         } else if(parse_request(buf, len, mesh->appname, "tcp")) {
310                 // Send a unicast response back
311                 char *fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self);
312                 const char *response_values[] = {mesh->name, fingerprint};
313                 size_t size = prepare_response(buf, sizeof(buf), fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, response_values);
314                 sendto(io->fd, buf, size, MSG_DONTWAIT | MSG_NOSIGNAL, &sa.sa, sl);
315                 free(fingerprint);
316         }
317
318         free(name);
319
320         for(int i = 0; i < 2; i++) {
321                 free(values[i]);
322         }
323 }
324
325 static void iface_up(meshlink_handle_t *mesh, int index) {
326         int *p = bsearch(&index, mesh->discovery.ifaces, mesh->discovery.iface_count, sizeof(*p), iface_compare);
327
328         if(p) {
329                 return;
330         }
331
332         mesh->discovery.ifaces = xrealloc(mesh->discovery.ifaces, ++mesh->discovery.iface_count * sizeof(*p));
333         mesh->discovery.ifaces[mesh->discovery.iface_count - 1] = index;
334         qsort(mesh->discovery.ifaces, mesh->discovery.iface_count, sizeof(*p), iface_compare);
335
336         // Add multicast membership
337         struct ip_mreqn mreq4 = {
338                 .imr_multiaddr = mdns_address_ipv4.in.sin_addr,
339                 .imr_ifindex = index,
340         };
341         setsockopt(mesh->discovery.sockets[0].fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq4, sizeof(mreq4));
342         setsockopt(mesh->discovery.sockets[0].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq4, sizeof(mreq4));
343
344         struct ipv6_mreq mreq6 = {
345                 .ipv6mr_multiaddr = mdns_address_ipv6.in6.sin6_addr,
346                 .ipv6mr_interface = index,
347         };
348         setsockopt(mesh->discovery.sockets[1].fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
349         setsockopt(mesh->discovery.sockets[1].fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
350
351         // Send an announcement for all addresses associated with this interface
352         for(int i = 0; i < mesh->discovery.address_count; i++) {
353                 if(mesh->discovery.addresses[i].index == index) {
354                         send_mdns_packet(mesh, &mesh->discovery.addresses[i]);
355                 }
356         }
357
358         handle_network_change(mesh, true);
359 }
360
361 static void iface_down(meshlink_handle_t *mesh, int index) {
362         int *p = bsearch(&index, mesh->discovery.ifaces, mesh->discovery.iface_count, sizeof(*p), iface_compare);
363
364         if(!p) {
365                 return;
366         }
367
368         // Drop multicast membership
369         struct ip_mreqn mreq4 = {
370                 .imr_multiaddr = mdns_address_ipv4.in.sin_addr,
371                 .imr_ifindex = index,
372         };
373         setsockopt(mesh->discovery.sockets[0].fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq4, sizeof(mreq4));
374
375         struct ipv6_mreq mreq6 = {
376                 .ipv6mr_multiaddr = mdns_address_ipv6.in6.sin6_addr,
377                 .ipv6mr_interface = index,
378         };
379         setsockopt(mesh->discovery.sockets[1].fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
380
381         memmove(p, p + 1, (mesh->discovery.ifaces + --mesh->discovery.iface_count - p) * sizeof(*p));
382
383         handle_network_change(mesh, mesh->discovery.iface_count);
384 }
385
386 static void addr_add(meshlink_handle_t *mesh, const discovery_address_t *addr) {
387         discovery_address_t *p = bsearch(addr, mesh->discovery.addresses, mesh->discovery.address_count, sizeof(*p), address_compare);
388
389         if(p) {
390                 return;
391         }
392
393         bool up = bsearch(&addr->index, mesh->discovery.ifaces, mesh->discovery.iface_count, sizeof(int), iface_compare);
394
395         mesh->discovery.addresses = xrealloc(mesh->discovery.addresses, ++mesh->discovery.address_count * sizeof(*p));
396         mesh->discovery.addresses[mesh->discovery.address_count - 1] = *addr;
397         mesh->discovery.addresses[mesh->discovery.address_count - 1].up = up;
398
399         if(up) {
400                 send_mdns_packet(mesh, &mesh->discovery.addresses[mesh->discovery.address_count - 1]);
401         }
402
403         qsort(mesh->discovery.addresses, mesh->discovery.address_count, sizeof(*p), address_compare);
404 }
405
406 static void addr_del(meshlink_handle_t *mesh, const discovery_address_t *addr) {
407         discovery_address_t *p = bsearch(addr, mesh->discovery.addresses, mesh->discovery.address_count, sizeof(*p), address_compare);
408
409         if(!p) {
410                 return;
411         }
412
413         memmove(p, p + 1, (mesh->discovery.addresses + --mesh->discovery.address_count - p) * sizeof(*p));
414 }
415
416 #if !defined(__linux) && (defined(RTM_NEWADDR) || defined(__APPLE__))
417 static void scan_ifaddrs(meshlink_handle_t *mesh) {
418         struct ifaddrs *ifa = NULL;
419
420         if(getifaddrs(&ifa) == -1) {
421                 logger(mesh, MESHLINK_ERROR, "Could not get list of interface addresses: %s", strerror(errno));
422                 return;
423         }
424
425         // Check for interfaces being removed
426         for(int i = 0; i < mesh->discovery.iface_count;) {
427                 bool found = false;
428
429                 for(struct ifaddrs *ifap = ifa; ifap; ifap = ifap->ifa_next) {
430                         if(!ifap->ifa_name) {
431                                 continue;
432                         }
433
434                         int index = if_nametoindex(ifap->ifa_name);
435
436                         if(mesh->discovery.ifaces[i] == index) {
437                                 found = true;
438                                 break;
439                         }
440                 }
441
442                 if(!found) {
443                         iface_down(mesh, mesh->discovery.ifaces[i]);
444                 } else {
445                         i++;
446                 }
447         }
448
449         // Check for addresses being removed
450         for(int i = 0; i < mesh->discovery.address_count;) {
451                 discovery_address_t *p = &mesh->discovery.addresses[i];
452                 bool found = false;
453
454                 for(struct ifaddrs *ifap = ifa; ifap; ifap = ifap->ifa_next) {
455                         if(!ifap->ifa_name || !ifap->ifa_addr) {
456                                 continue;
457                         }
458
459                         int index = if_nametoindex(ifap->ifa_name);
460
461                         if(p->index == index && sockaddrcmp_noport(&p->address, (sockaddr_t *)ifap->ifa_addr) == 0) {
462                                 found = true;
463                                 break;
464                         }
465                 }
466
467                 if(!found) {
468                         (void)addr_del;
469                         memmove(p, p + 1, (mesh->discovery.addresses + --mesh->discovery.address_count - p) * sizeof(*p));
470                 } else {
471                         i++;
472                 }
473         }
474
475         // Check for interfaces state changes and addresses going up
476         for(struct ifaddrs *ifap = ifa; ifap; ifap = ifap->ifa_next) {
477                 if(!ifap->ifa_name) {
478                         continue;
479                 }
480
481                 int index = if_nametoindex(ifap->ifa_name);
482
483                 if(ifap->ifa_flags & IFF_UP && ifap->ifa_flags & IFF_MULTICAST && !(ifap->ifa_flags & IFF_LOOPBACK)) {
484                         iface_up(mesh, index);
485                 } else {
486                         iface_down(mesh, index);
487                 }
488
489                 if(!ifap->ifa_addr) {
490                         continue;
491                 }
492
493                 discovery_address_t addr  = {
494                         .index = index,
495                 };
496
497                 sockaddr_t *sa = (sockaddr_t *)ifap->ifa_addr;
498
499                 if(sa->sa.sa_family == AF_INET) {
500                         memcpy(&addr.address.in, &sa->in, sizeof(sa->in));
501                         addr.address.in.sin_port = ntohs(5353);
502                 } else if(sa->sa.sa_family == AF_INET6) {
503                         memcpy(&addr.address.in6, &sa->in6, sizeof(sa->in6));
504                         addr.address.in6.sin6_port = ntohs(5353);
505                 } else {
506                         addr.address.sa.sa_family = AF_UNKNOWN;
507                 }
508
509                 if(addr.address.sa.sa_family != AF_UNKNOWN) {
510                         addr_add(mesh, &addr);
511                 }
512         }
513
514         freeifaddrs(ifa);
515 }
516 #endif
517
518 #if defined(__linux)
519 static void netlink_getlink(int fd) {
520         static const struct {
521                 struct nlmsghdr nlm;
522                 struct ifinfomsg ifi;
523         } msg = {
524                 .nlm.nlmsg_len = NLMSG_LENGTH(sizeof(msg.ifi)),
525                 .nlm.nlmsg_type = RTM_GETLINK,
526                 .nlm.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
527                 .nlm.nlmsg_seq = 1,
528                 .ifi.ifi_family = AF_UNSPEC,
529         };
530         send(fd, &msg, msg.nlm.nlmsg_len, 0);
531 }
532
533 static void netlink_getaddr(int fd) {
534         static const struct {
535                 struct nlmsghdr nlm;
536                 struct ifaddrmsg ifa;
537         } msg = {
538                 .nlm.nlmsg_len = NLMSG_LENGTH(sizeof(msg.ifa)),
539                 .nlm.nlmsg_type = RTM_GETADDR,
540                 .nlm.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
541                 .nlm.nlmsg_seq = 2,
542                 .ifa.ifa_family = AF_UNSPEC,
543         };
544         send(fd, &msg, msg.nlm.nlmsg_len, 0);
545 }
546
547 static void netlink_parse_link(meshlink_handle_t *mesh, const struct nlmsghdr *nlm) {
548         const struct ifinfomsg *ifi = (const struct ifinfomsg *)(nlm + 1);
549
550         if(ifi->ifi_flags & IFF_UP && ifi->ifi_flags & IFF_MULTICAST) {
551                 iface_up(mesh, ifi->ifi_index);
552         } else {
553                 iface_down(mesh, ifi->ifi_index);
554         }
555 }
556
557 static void netlink_parse_addr(meshlink_handle_t *mesh, const struct nlmsghdr *nlm) {
558         const struct ifaddrmsg *ifa = (const struct ifaddrmsg *)(nlm + 1);
559         const uint8_t *ptr = (const uint8_t *)(ifa + 1);
560         size_t len = nlm->nlmsg_len - (ptr - (const uint8_t *)nlm);
561
562         while(len >= sizeof(struct rtattr)) {
563                 const struct rtattr *rta = (const struct rtattr *)ptr;
564
565                 if(rta->rta_len <= 0 || rta->rta_len > len) {
566                         break;
567                 }
568
569                 if(rta->rta_type == IFA_ADDRESS) {
570                         discovery_address_t addr  = {
571                                 .index = ifa->ifa_index,
572                         };
573
574                         if(rta->rta_len == 8) {
575                                 addr.address.sa.sa_family = AF_INET;
576                                 memcpy(&addr.address.in.sin_addr, ptr + 4, 4);
577                                 addr.address.in.sin_port = ntohs(5353);
578                         } else if(rta->rta_len == 20) {
579                                 addr.address.sa.sa_family = AF_INET6;
580                                 memcpy(&addr.address.in6.sin6_addr, ptr + 4, 16);
581                                 addr.address.in6.sin6_port = ntohs(5353);
582                                 addr.address.in6.sin6_scope_id = ifa->ifa_index;
583                         } else {
584                                 addr.address.sa.sa_family = AF_UNKNOWN;
585                         }
586
587                         if(addr.address.sa.sa_family != AF_UNKNOWN) {
588                                 if(nlm->nlmsg_type == RTM_NEWADDR) {
589                                         addr_add(mesh, &addr);
590                                 } else {
591                                         addr_del(mesh, &addr);
592                                 }
593                         }
594                 }
595
596                 unsigned short rta_len = (rta->rta_len + 3) & ~3;
597                 ptr += rta_len;
598                 len -= rta_len;
599         }
600 }
601
602 static void netlink_parse(meshlink_handle_t *mesh, const void *data, size_t len) {
603         const uint8_t *ptr = data;
604
605         while(len >= sizeof(struct nlmsghdr)) {
606                 const struct nlmsghdr *nlm = (const struct nlmsghdr *)ptr;
607
608                 if(nlm->nlmsg_len > len) {
609                         break;
610                 }
611
612                 switch(nlm->nlmsg_type) {
613                 case RTM_NEWLINK:
614                 case RTM_DELLINK:
615                         netlink_parse_link(mesh, nlm);
616                         break;
617
618                 case RTM_NEWADDR:
619                 case RTM_DELADDR:
620                         netlink_parse_addr(mesh, nlm);
621                 }
622
623                 ptr += nlm->nlmsg_len;
624                 len -= nlm->nlmsg_len;
625         }
626 }
627
628 static void netlink_io_handler(event_loop_t *loop, void *data, int flags) {
629         (void)flags;
630         (void)data;
631         meshlink_handle_t *mesh = loop->data;
632
633         struct {
634                 struct nlmsghdr nlm;
635                 char data[16384];
636         } msg;
637
638         while(true) {
639                 ssize_t result = recv(mesh->discovery.pfroute_io.fd, &msg, sizeof(msg), MSG_DONTWAIT);
640
641                 if(result <= 0) {
642                         if(result == 0 || errno == EAGAIN || errno == EINTR) {
643                                 break;
644                         }
645
646                         logger(mesh, MESHLINK_ERROR, "Reading from Netlink socket failed: %s\n", strerror(errno));
647                         io_set(loop, &mesh->discovery.pfroute_io, 0);
648                 }
649
650                 if((size_t)result < sizeof(msg.nlm)) {
651                         logger(mesh, MESHLINK_ERROR, "Invalid Netlink message\n");
652                         break;
653                 }
654
655                 if(msg.nlm.nlmsg_type == NLMSG_DONE) {
656                         if(msg.nlm.nlmsg_seq == 1) {
657                                 // We just got the result of GETLINK, now send GETADDR.
658                                 netlink_getaddr(mesh->discovery.pfroute_io.fd);
659                         }
660                 } else {
661                         netlink_parse(mesh, &msg, result);
662
663                         if(loop->now.tv_sec > mesh->discovery.last_update + 5) {
664                                 mesh->discovery.last_update = loop->now.tv_sec;
665                                 handle_network_change(mesh, 1);
666                         }
667                 }
668         }
669 }
670 #elif defined(__APPLE__)
671 static void network_change_callback(SCDynamicStoreRef store, CFArrayRef keys, void *info) {
672         (void)store;
673         (void)keys;
674
675         meshlink_handle_t *mesh = info;
676
677         pthread_mutex_lock(&mesh->mutex);
678
679         logger(mesh, MESHLINK_ERROR, "Network change detected!");
680         scan_ifaddrs(mesh);
681
682         if(mesh->loop.now.tv_sec > mesh->discovery.last_update + 5) {
683                 mesh->discovery.last_update = mesh->loop.now.tv_sec;
684                 handle_network_change(mesh, 1);
685         }
686
687         pthread_mutex_unlock(&mesh->mutex);
688 }
689
690 static void *network_change_handler(void *arg) {
691         meshlink_handle_t *mesh = arg;
692
693         mesh->discovery.runloop = CFRunLoopGetCurrent();
694
695         SCDynamicStoreContext context = {0, mesh, NULL, NULL, NULL};
696         SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("network_change_handler"), network_change_callback, &context);
697         CFStringRef interfaces = SCDynamicStoreKeyCreate(NULL, CFSTR("State:/Network/Interface"), kCFStringEncodingUTF8);
698         CFStringRef ipv4 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
699         CFStringRef ipv6 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6);
700         CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
701         CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
702         CFRunLoopSourceRef runloop_source = NULL;
703
704         if(!store) {
705                 logger(mesh, MESHLINK_ERROR, "Error setting up network change handler: %s\n", SCErrorString(SCError()));
706                 goto exit;
707         }
708
709         if(!interfaces || !ipv4 || !ipv6 || !keys || !patterns) {
710                 logger(mesh, MESHLINK_ERROR, "Error setting up network change handler: %s\n", SCErrorString(SCError()));
711                 goto exit;
712         }
713
714         CFArrayAppendValue(keys, interfaces);
715         CFArrayAppendValue(patterns, ipv4);
716         CFArrayAppendValue(patterns, ipv6);
717
718         if(!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) {
719                 logger(mesh, MESHLINK_ERROR, "Error setting up network change handler: %s\n", SCErrorString(SCError()));
720                 goto exit;
721         }
722
723         runloop_source = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
724
725         if(!runloop_source) {
726                 logger(mesh, MESHLINK_ERROR, "Error setting up network change handler: %s\n", SCErrorString(SCError()));
727                 goto exit;
728         }
729
730         CFRunLoopAddSource(CFRunLoopGetCurrent(), runloop_source, kCFRunLoopDefaultMode);
731         CFRunLoopRun();
732
733 exit:
734
735         if(runloop_source) {
736                 CFRelease(runloop_source);
737         }
738
739         if(interfaces) {
740                 CFRelease(interfaces);
741         }
742
743         if(ipv4) {
744                 CFRelease(ipv4);
745         }
746
747         if(ipv6) {
748                 CFRelease(ipv6);
749         }
750
751         if(keys) {
752                 CFRelease(keys);
753         }
754
755         if(patterns) {
756                 CFRelease(patterns);
757         }
758
759         if(store) {
760                 CFRelease(store);
761         }
762
763         mesh->discovery.runloop = NULL;
764
765         return NULL;
766
767 }
768 #elif defined(RTM_NEWADDR)
769 static void pfroute_parse_iface(meshlink_handle_t *mesh, const struct rt_msghdr *rtm) {
770         const struct if_msghdr *ifm = (const struct if_msghdr *)rtm;
771
772         if(ifm->ifm_flags & IFF_UP && ifm->ifm_flags & IFF_MULTICAST && !(ifm->ifm_flags & IFF_LOOPBACK)) {
773                 iface_up(mesh, ifm->ifm_index);
774         } else {
775                 iface_down(mesh, ifm->ifm_index);
776         }
777 }
778
779 static void pfroute_parse_addr(meshlink_handle_t *mesh, const struct rt_msghdr *rtm) {
780         const struct ifa_msghdr *ifam = (const struct ifa_msghdr *)rtm;
781         const char *p = (const char *)(ifam + 1);
782
783         for(unsigned int i = 1; i; i <<= 1) {
784                 if(!(ifam->ifam_addrs & i)) {
785                         continue;
786                 }
787
788                 const sockaddr_t *sa = (const sockaddr_t *)p;
789
790                 if(i == RTA_IFA) {
791                         discovery_address_t addr = {
792                                 .index = ifam->ifam_index,
793                         };
794
795                         if(sa->sa.sa_family == AF_INET) {
796                                 addr.address.in = sa->in;
797                                 addr.address.in.sin_port = ntohs(5353);
798                         } else if(sa->sa.sa_family == AF_INET6) {
799                                 addr.address.in6 = sa->in6;
800                                 addr.address.in6.sin6_port = ntohs(5353);
801                         } else {
802                                 addr.address.sa.sa_family = AF_UNKNOWN;
803                         }
804
805                         if(addr.address.sa.sa_family != AF_UNKNOWN) {
806                                 if(ifam->ifam_type == RTM_NEWADDR) {
807                                         addr_add(mesh, &addr);
808                                 } else {
809                                         addr_del(mesh, &addr);
810                                 }
811                         }
812
813                         break;
814                 }
815
816                 size_t len = (sa->sa.sa_len + 3) & ~3;
817                 p += len;
818         }
819 }
820
821 static void pfroute_io_handler(event_loop_t *loop, void *data, int flags) {
822         (void)flags;
823         (void)data;
824         meshlink_handle_t *mesh = loop->data;
825
826         struct {
827                 struct rt_msghdr rtm;
828                 char data[2048];
829         } msg;
830
831         while(true) {
832                 msg.rtm.rtm_version = 0;
833                 ssize_t result = recv(mesh->discovery.pfroute_io.fd, &msg, sizeof(msg), MSG_DONTWAIT);
834
835                 if(result <= 0) {
836                         if(result == 0 || errno == EAGAIN || errno == EINTR) {
837                                 break;
838                         }
839
840                         logger(mesh, MESHLINK_ERROR, "Reading from PFROUTE socket failed: %s\n", strerror(errno));
841                         io_set(loop, &mesh->discovery.pfroute_io, 0);
842                 }
843
844                 if(msg.rtm.rtm_version != RTM_VERSION) {
845                         logger(mesh, MESHLINK_ERROR, "Invalid PFROUTE message version\n");
846                         break;
847                 }
848
849                 switch(msg.rtm.rtm_type) {
850                 case RTM_IFINFO:
851                         pfroute_parse_iface(mesh, &msg.rtm);
852                         break;
853
854                 case RTM_NEWADDR:
855                 case RTM_DELADDR:
856                         pfroute_parse_addr(mesh, &msg.rtm);
857                         break;
858
859                 default:
860                         break;
861                 }
862         }
863 }
864 #endif
865
866 bool discovery_start(meshlink_handle_t *mesh) {
867         logger(mesh, MESHLINK_DEBUG, "discovery_start called\n");
868
869         assert(mesh);
870
871         // Set up multicast sockets for mDNS
872         static const int one = 1;
873         static const int ttl = 255;
874         static const uint8_t one8 = 1;
875         static const uint8_t ttl8 = 255;
876
877         int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
878
879         if(fd == -1) {
880                 logger(mesh, MESHLINK_ERROR, "Error creating IPv4 socket: %s", strerror(errno));
881         }
882
883         sockaddr_t sa4 = {
884                 .in.sin_family = AF_INET,
885                 .in.sin_port = ntohs(5353),
886         };
887         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
888         setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
889         setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one8, sizeof(one8));
890         setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl8, sizeof(ttl8));
891
892         if(bind(fd, &sa4.sa, SALEN(sa4.sa)) == -1) {
893                 logger(mesh, MESHLINK_ERROR, "Error binding to IPv4 multicast socket: %s", strerror(errno));
894         } else {
895                 io_add(&mesh->loop, &mesh->discovery.sockets[0], mdns_io_handler, &mesh->discovery.sockets[0], fd, IO_READ);
896         }
897
898         sockaddr_t sa6 = {
899                 .in6.sin6_family = AF_INET6,
900                 .in6.sin6_port = ntohs(5353),
901         };
902         fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
903
904         if(fd == -1) {
905                 logger(mesh, MESHLINK_ERROR, "Error creating IPv6 socket: %s", strerror(errno));
906         }
907
908         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
909         setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
910         setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
911         setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
912         setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
913         setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
914
915         if(bind(fd, &sa6.sa, SALEN(sa6.sa)) == -1) {
916                 logger(mesh, MESHLINK_ERROR, "Error binding to IPv4 multicast socket: %s", strerror(errno));
917         } else {
918                 io_add(&mesh->loop, &mesh->discovery.sockets[1], mdns_io_handler, &mesh->discovery.sockets[1], fd, IO_READ);
919         }
920
921 #if defined(__linux)
922         int sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
923
924         if(sock != -1) {
925                 struct sockaddr_nl sa;
926                 memset(&sa, 0, sizeof(sa));
927                 sa.nl_family = AF_NETLINK;
928                 sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
929
930                 if(bind(sock, (struct sockaddr *)&sa, sizeof(sa)) != -1) {
931                         io_add(&mesh->loop, &mesh->discovery.pfroute_io, netlink_io_handler, NULL, sock, IO_READ);
932                         netlink_getlink(sock);
933                 } else {
934                         logger(mesh, MESHLINK_WARNING, "Could not bind AF_NETLINK socket: %s", strerror(errno));
935                 }
936         } else {
937                 logger(mesh, MESHLINK_WARNING, "Could not open AF_NETLINK socket: %s", strerror(errno));
938         }
939
940 #elif defined(__APPLE__)
941         pthread_create(&mesh->discovery.thread, NULL, network_change_handler, mesh);
942         // TODO: Do we need to wait for the thread to start succesfully?
943         scan_ifaddrs(mesh);
944 #elif defined(RTM_NEWADDR)
945         int sock = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
946
947         if(sock != -1) {
948                 io_add(&mesh->loop, &mesh->discovery.pfroute_io, pfroute_io_handler, NULL, sock, IO_READ);
949         } else {
950                 logger(mesh, MESHLINK_WARNING, "Could not open PF_ROUTE socket: %s", strerror(errno));
951         }
952
953         scan_ifaddrs(mesh);
954 #endif
955
956         return true;
957 }
958
959 void discovery_stop(meshlink_handle_t *mesh) {
960         logger(mesh, MESHLINK_DEBUG, "discovery_stop called\n");
961
962         assert(mesh);
963
964         free(mesh->discovery.ifaces);
965         free(mesh->discovery.addresses);
966         mesh->discovery.ifaces = NULL;
967         mesh->discovery.addresses = NULL;
968         mesh->discovery.iface_count = 0;
969         mesh->discovery.address_count = 0;
970
971 #if defined(__APPLE__)
972
973         if(mesh->discovery.runloop) {
974                 CFRunLoopStop(mesh->discovery.runloop);
975                 pthread_join(mesh->discovery.thread, NULL);
976         }
977
978 #endif
979
980         if(mesh->discovery.pfroute_io.cb) {
981                 close(mesh->discovery.pfroute_io.fd);
982                 io_del(&mesh->loop, &mesh->discovery.pfroute_io);
983         }
984
985         for(int i = 0; i < 2; i++) {
986                 if(mesh->discovery.sockets[i].cb) {
987                         close(mesh->discovery.sockets[i].fd);
988                         io_del(&mesh->loop, &mesh->discovery.sockets[i]);
989                 }
990         }
991 }
992
993 void discovery_refresh(meshlink_handle_t *mesh) {
994         for(int i = 0; i < mesh->discovery.address_count; i++) {
995                 if(mesh->discovery.addresses[i].up) {
996                         send_mdns_packet(mesh, &mesh->discovery.addresses[i]);
997                 }
998         }
999 }