]> git.meshlink.io Git - meshlink/blob - src/discovery.c
6fec9a2466299592fb51b6f6cf47e145723416b5
[meshlink] / src / discovery.c
1 #include "system.h"
2
3 #if defined(__APPLE__) || defined(__unix) && !defined(__linux)
4 #include <net/route.h>
5 #elif defined(__linux)
6 #include <asm/types.h>
7 #include <net/if.h>
8 #include <linux/if_link.h>
9 #include <linux/netlink.h>
10 #include <linux/rtnetlink.h>
11 #endif
12
13 #include "mdns.h"
14 #include "meshlink_internal.h"
15 #include "event.h"
16 #include "discovery.h"
17 #include "sockaddr.h"
18 #include "logger.h"
19 #include "netutl.h"
20 #include "node.h"
21 #include "connection.h"
22 #include "utils.h"
23 #include "xalloc.h"
24
25 #define MESHLINK_MDNS_SERVICE_TYPE "_%s._tcp"
26 #define MESHLINK_MDNS_NAME_KEY "name"
27 #define MESHLINK_MDNS_FINGERPRINT_KEY "fingerprint"
28
29 static const sockaddr_t mdns_address_ipv4 = {
30         .in.sin_family = AF_INET,
31         .in.sin_addr.s_addr = 0xfb0000e0,
32         .in.sin_port = 0xe914,
33 };
34
35 static const sockaddr_t mdns_address_ipv6 = {
36         .in6.sin6_family = AF_INET6,
37         .in6.sin6_addr.s6_addr[0x0] = 0xfd,
38         .in6.sin6_addr.s6_addr[0x1] = 0x02,
39         .in6.sin6_addr.s6_addr[0xf] = 0xfb,
40         .in6.sin6_port = 0xe914,
41 };
42
43 typedef struct discovery_address {
44         int index;
45         bool up;
46         sockaddr_t address;
47 } discovery_address_t;
48
49 static int iface_compare(const void *va, const void *vb) {
50         const int *a = va;
51         const int *b = vb;
52         return *a - *b;
53 }
54
55 static int address_compare(const void *va, const void *vb) {
56         const discovery_address_t *a = va;
57         const discovery_address_t *b = vb;
58
59         if(a->index != b->index) {
60                 return a->index - b->index;
61         }
62
63         return sockaddrcmp_noport(&a->address, &b->address);
64 }
65
66 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) {
67 #ifdef IP_PKTINFO
68         struct iovec iov  = {
69                 .iov_base = data,
70                 .iov_len = len,
71         };
72
73         struct in_pktinfo pkti = {
74                 .ipi_ifindex = index,
75                 .ipi_addr = src->in.sin_addr,
76         };
77
78         union {
79                 char buf[CMSG_SPACE(sizeof(pkti))];
80                 struct cmsghdr align;
81         } u;
82
83         struct msghdr msg = {
84                 .msg_name = (struct sockaddr *) &dest->sa,
85                 .msg_namelen = SALEN(dest->sa),
86                 .msg_iov = &iov,
87                 .msg_iovlen = 1,
88                 .msg_control = u.buf,
89                 .msg_controllen = sizeof(u.buf),
90         };
91
92
93         struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
94         cmsg->cmsg_level = IPPROTO_IP;
95         cmsg->cmsg_type = IP_PKTINFO;
96         cmsg->cmsg_len = CMSG_LEN(sizeof(pkti));
97         memcpy(CMSG_DATA(cmsg), &pkti, sizeof(pkti));
98
99         // Send the packet
100         ssize_t result = sendmsg(fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL);
101 #else
102         // Send the packet
103         ssize_t result = sendto(fd, data, len, MSG_DONTWAIT | MSG_NOSIGNAL, &dest->sa, SALEN(dest->sa));
104 #endif
105
106         if(result <= 0) {
107                 logger(mesh, MESHLINK_ERROR, "Error sending multicast packet: %s", strerror(errno));
108         }
109 }
110
111 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) {
112 #ifdef IPV6_PKTINFO
113         struct iovec iov  = {
114                 .iov_base = data,
115                 .iov_len = len,
116         };
117
118         struct in6_pktinfo pkti = {
119                 .ipi6_ifindex = index,
120                 .ipi6_addr = src->in6.sin6_addr,
121         };
122
123         union {
124                 char buf[CMSG_SPACE(sizeof(pkti))];
125                 struct cmsghdr align;
126         } u;
127
128         memset(&u, 0, sizeof u);
129
130         struct msghdr msg = {
131                 .msg_name = (struct sockaddr *) &dest->sa,
132                 .msg_namelen = SALEN(dest->sa),
133                 .msg_iov = &iov,
134                 .msg_iovlen = 1,
135                 .msg_control = u.buf,
136                 .msg_controllen = CMSG_LEN(sizeof(pkti)),
137         };
138
139         struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
140         cmsg->cmsg_level = IPPROTO_IPV6;
141         cmsg->cmsg_type = IPV6_PKTINFO;
142         cmsg->cmsg_len = CMSG_LEN(sizeof(pkti));
143         memcpy(CMSG_DATA(cmsg), &pkti, sizeof(pkti));
144
145         // Send the packet
146         ssize_t result = sendmsg(fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL);
147 #else
148         // Send the packet
149         ssize_t result = sendto(fd, data, len, MSG_DONTWAIT | MSG_NOSIGNAL, &dest->sa, SALEN(dest->sa));
150 #endif
151
152         if(result <= 0) {
153                 logger(mesh, MESHLINK_ERROR, "Error sending multicast packet: %s", strerror(errno));
154         }
155 }
156
157 static void send_mdns_packet(meshlink_handle_t *mesh, const discovery_address_t *addr) {
158         // Configure the socket to send the packet to the right interface
159         int fd;
160         uint8_t data[1024];
161         char *fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self);
162         const char *keys[] = {MESHLINK_MDNS_NAME_KEY, MESHLINK_MDNS_FINGERPRINT_KEY};
163         const char *values[] = {mesh->name, fingerprint};
164         size_t size = prepare_packet(data, sizeof data, fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, values, false);
165         free(fingerprint);
166
167         switch(addr->address.sa.sa_family) {
168         case AF_INET:
169                 fd = mesh->discovery.sockets[0].fd;
170 #ifdef IP_MULTICAST_IF
171                 {
172                         struct ip_mreqn mreq = {
173                                 .imr_address = addr->address.in.sin_addr,
174                                 .imr_ifindex = addr->index,
175                         };
176
177                         if(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) != 0) {
178                                 logger(mesh, MESHLINK_ERROR, "Could not set outgoing multicast interface on IPv4 socket");
179                                 return;
180                         }
181                 }
182
183 #endif
184
185                 send_mdns_packet_ipv4(mesh, fd, addr->index, &addr->address, &mdns_address_ipv4, data, size);
186                 break;
187
188         case AF_INET6:
189                 fd = mesh->discovery.sockets[1].fd;
190 #ifdef IPV6_MULTICAST_IF
191
192                 if(setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &addr->index, sizeof(addr->index)) != 0) {
193                         logger(mesh, MESHLINK_ERROR, "Could not set outgoing multicast interface on IPv6 socket");
194                         return;
195                 }
196
197 #endif
198
199                 send_mdns_packet_ipv6(mesh, fd, addr->index, &addr->address, &mdns_address_ipv6, data, size);
200                 break;
201
202         default:
203                 break;
204         }
205 }
206
207 static void mdns_io_handler(event_loop_t *loop, void *data, int flags) {
208         (void)flags;
209         meshlink_handle_t *mesh = loop->data;
210         io_t *io = data;
211         uint8_t buf[1024];
212         sockaddr_t sa;
213         socklen_t sl = sizeof(sa);
214
215         ssize_t len = recvfrom(io->fd, buf, sizeof(buf), MSG_DONTWAIT, &sa.sa, &sl);
216
217         if(len == -1) {
218                 if(!sockwouldblock(errno)) {
219                         logger(mesh, MESHLINK_ERROR, "Error reading from mDNS discovery socket: %s", strerror(errno));
220                         io_set(loop, io, 0);
221                 }
222
223                 return;
224         }
225
226         char *name = NULL;
227         uint16_t port = 0;
228         const char *keys[2] = {MESHLINK_MDNS_NAME_KEY, MESHLINK_MDNS_FINGERPRINT_KEY};
229         char *values[2] = {NULL, NULL};
230         bool response;
231
232         if(parse_packet(buf, len, &name, mesh->appname, "tcp", &port, 2, keys, values, &response)) {
233                 node_t *n = (node_t *)meshlink_get_node(mesh, values[0]);
234
235                 if(n) {
236                         if(n != mesh->self) {
237                                 logger(mesh, MESHLINK_INFO, "Node %s discovered on the local network.\n", n->name);
238                         }
239
240                         if(!response && n != mesh->self) {
241                                 // Send a unicast response back
242                                 char *fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self);
243                                 const char *response_values[] = {mesh->name, fingerprint};
244                                 size_t size = prepare_packet(buf, sizeof(buf), fingerprint, mesh->appname, "tcp", atoi(mesh->myport), 2, keys, response_values, true);
245                                 sendto(io->fd, buf, size, MSG_DONTWAIT | MSG_NOSIGNAL, &sa.sa, sl);
246                                 free(fingerprint);
247                         }
248
249                         switch(sa.sa.sa_family) {
250                         case AF_INET:
251                                 sa.in.sin_port = htons(port);
252                                 break;
253
254                         case AF_INET6:
255                                 sa.in6.sin6_port = htons(port);
256                                 break;
257
258                         default:
259                                 logger(mesh, MESHLINK_WARNING, "Could not resolve node %s to a known address family type.\n", n->name);
260                                 sa.sa.sa_family = AF_UNKNOWN;
261                                 break;
262                         }
263
264                         if(sa.sa.sa_family != AF_UNKNOWN) {
265                                 n->catta_address = sa;
266                                 n->last_connect_try = 0;
267                                 node_add_recent_address(mesh, n, &sa);
268
269                                 if(n->connection) {
270                                         n->connection->last_ping_time = -3600;
271                                 }
272
273                                 for list_each(outgoing_t, outgoing, mesh->outgoings) {
274                                         if(outgoing->node != n) {
275                                                 continue;
276                                         }
277
278                                         outgoing->timeout = 0;
279
280                                         if(outgoing->ev.cb) {
281                                                 timeout_set(&mesh->loop, &outgoing->ev, &(struct timespec) {
282                                                         0, 0
283                                                 });
284                                         }
285                                 }
286                         }
287                 }
288         }
289
290         free(name);
291
292         for(int i = 0; i < 2; i++) {
293                 free(values[i]);
294         }
295 }
296
297 static void iface_up(meshlink_handle_t *mesh, int index) {
298         int *p = bsearch(&index, mesh->discovery.ifaces, mesh->discovery.iface_count, sizeof(*p), iface_compare);
299
300         if(p) {
301                 return;
302         }
303
304         mesh->discovery.ifaces = xrealloc(mesh->discovery.ifaces, ++mesh->discovery.iface_count * sizeof(*p));
305         mesh->discovery.ifaces[mesh->discovery.iface_count - 1] = index;
306         qsort(mesh->discovery.ifaces, mesh->discovery.iface_count, sizeof(*p), iface_compare);
307
308         // Add multicast membership
309         struct ip_mreqn mreq4 = {
310                 .imr_multiaddr = mdns_address_ipv4.in.sin_addr,
311                 .imr_ifindex = index,
312         };
313         setsockopt(mesh->discovery.sockets[0].fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq4, sizeof(mreq4));
314         setsockopt(mesh->discovery.sockets[0].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq4, sizeof(mreq4));
315
316         struct ipv6_mreq mreq6 = {
317                 .ipv6mr_multiaddr = mdns_address_ipv6.in6.sin6_addr,
318                 .ipv6mr_interface = index,
319         };
320         setsockopt(mesh->discovery.sockets[1].fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
321         setsockopt(mesh->discovery.sockets[1].fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
322
323         // Send an announcement for all addresses associated with this interface
324         for(int i = 0; i < mesh->discovery.address_count; i++) {
325                 if(mesh->discovery.addresses[i].index == index) {
326                         send_mdns_packet(mesh, &mesh->discovery.addresses[i]);
327                 }
328         }
329
330         handle_network_change(mesh, true);
331 }
332
333 static void iface_down(meshlink_handle_t *const mesh, int index) {
334         int *p = bsearch(&index, mesh->discovery.ifaces, mesh->discovery.iface_count, sizeof(*p), iface_compare);
335
336         if(!p) {
337                 return;
338         }
339
340         // Drop multicast membership
341         struct ip_mreqn mreq4 = {
342                 .imr_multiaddr = mdns_address_ipv4.in.sin_addr,
343                 .imr_ifindex = index,
344         };
345         setsockopt(mesh->discovery.sockets[0].fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq4, sizeof(mreq4));
346
347         struct ipv6_mreq mreq6 = {
348                 .ipv6mr_multiaddr = mdns_address_ipv6.in6.sin6_addr,
349                 .ipv6mr_interface = index,
350         };
351         setsockopt(mesh->discovery.sockets[1].fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
352
353         memmove(p, p + 1, (mesh->discovery.ifaces + --mesh->discovery.iface_count - p) * sizeof(*p));
354
355         handle_network_change(mesh, mesh->discovery.iface_count);
356 }
357
358 static void addr_add(meshlink_handle_t *mesh, const discovery_address_t *addr) {
359         discovery_address_t *p = bsearch(addr, mesh->discovery.addresses, mesh->discovery.address_count, sizeof(*p), address_compare);
360
361         if(p) {
362                 return;
363         }
364
365         bool up = bsearch(&addr->index, mesh->discovery.ifaces, mesh->discovery.iface_count, sizeof(int), iface_compare);
366
367         mesh->discovery.addresses = xrealloc(mesh->discovery.addresses, ++mesh->discovery.address_count * sizeof(*p));
368         mesh->discovery.addresses[mesh->discovery.address_count - 1] = *addr;
369         mesh->discovery.addresses[mesh->discovery.address_count - 1].up = up;
370
371         if(up) {
372                 send_mdns_packet(mesh, &mesh->discovery.addresses[mesh->discovery.address_count - 1]);
373         }
374
375         qsort(mesh->discovery.addresses, mesh->discovery.address_count, sizeof(*p), address_compare);
376 }
377
378 static void addr_del(meshlink_handle_t *mesh, const discovery_address_t *addr) {
379         discovery_address_t *p = bsearch(addr, mesh->discovery.addresses, mesh->discovery.address_count, sizeof(*p), address_compare);
380
381         if(!p) {
382                 return;
383         }
384
385         memmove(p, p + 1, (mesh->discovery.addresses + --mesh->discovery.address_count - p) * sizeof(*p));
386 }
387
388 #if defined(__linux)
389 static void netlink_getlink(int fd) {
390         static const struct {
391                 struct nlmsghdr nlm;
392                 struct ifinfomsg ifi;
393         } msg = {
394                 .nlm.nlmsg_len = NLMSG_LENGTH(sizeof(msg.ifi)),
395                 .nlm.nlmsg_type = RTM_GETLINK,
396                 .nlm.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
397                 .nlm.nlmsg_seq = 1,
398                 .ifi.ifi_family = AF_UNSPEC,
399         };
400         send(fd, &msg, msg.nlm.nlmsg_len, 0);
401 }
402
403 static void netlink_getaddr(int fd) {
404         static const struct {
405                 struct nlmsghdr nlm;
406                 struct ifaddrmsg ifa;
407         } msg = {
408                 .nlm.nlmsg_len = NLMSG_LENGTH(sizeof(msg.ifa)),
409                 .nlm.nlmsg_type = RTM_GETADDR,
410                 .nlm.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
411                 .nlm.nlmsg_seq = 2,
412                 .ifa.ifa_family = AF_UNSPEC,
413         };
414         send(fd, &msg, msg.nlm.nlmsg_len, 0);
415 }
416
417 static void netlink_parse_link(meshlink_handle_t *mesh, const struct nlmsghdr *nlm) {
418         const struct ifinfomsg *ifi = (const struct ifinfomsg *)(nlm + 1);
419
420         if(ifi->ifi_flags & IFF_UP && ifi->ifi_flags & IFF_MULTICAST) {
421                 iface_up(mesh, ifi->ifi_index);
422         } else {
423                 iface_down(mesh, ifi->ifi_index);
424         }
425 }
426
427 static void netlink_parse_addr(meshlink_handle_t *mesh, const struct nlmsghdr *nlm) {
428         const struct ifaddrmsg *ifa = (const struct ifaddrmsg *)(nlm + 1);
429         const uint8_t *ptr = (const uint8_t *)(ifa + 1);
430         size_t len = nlm->nlmsg_len - (ptr - (const uint8_t *)nlm);
431
432         while(len >= sizeof(struct rtattr)) {
433                 const struct rtattr *rta = (const struct rtattr *)ptr;
434
435                 if(rta->rta_len <= 0 || rta->rta_len > len) {
436                         break;
437                 }
438
439                 if(rta->rta_type == IFA_ADDRESS) {
440                         discovery_address_t addr  = {
441                                 .index = ifa->ifa_index,
442                         };
443
444                         if(rta->rta_len == 8) {
445                                 addr.address.sa.sa_family = AF_INET;
446                                 memcpy(&addr.address.in.sin_addr, ptr + 4, 4);
447                                 addr.address.in.sin_port = 5353;
448                         } else if(rta->rta_len == 20) {
449                                 addr.address.sa.sa_family = AF_INET6;
450                                 memcpy(&addr.address.in6.sin6_addr, ptr + 4, 16);
451                                 addr.address.in6.sin6_port = 5353;
452                                 addr.address.in6.sin6_scope_id = ifa->ifa_index;
453                         } else {
454                                 addr.address.sa.sa_family = AF_UNKNOWN;
455                         }
456
457                         if(addr.address.sa.sa_family != AF_UNKNOWN) {
458                                 if(nlm->nlmsg_type == RTM_NEWADDR) {
459                                         addr_add(mesh, &addr);
460                                 } else {
461                                         addr_del(mesh, &addr);
462                                 }
463                         }
464                 }
465
466                 unsigned short rta_len = (rta->rta_len + 3) & ~3;
467                 ptr += rta_len;
468                 len -= rta_len;
469         }
470 }
471
472 static void netlink_parse(meshlink_handle_t *mesh, const void *data, size_t len) {
473         const uint8_t *ptr = data;
474
475         while(len >= sizeof(struct nlmsghdr)) {
476                 const struct nlmsghdr *nlm = (const struct nlmsghdr *)ptr;
477
478                 if(nlm->nlmsg_len > len) {
479                         break;
480                 }
481
482                 switch(nlm->nlmsg_type) {
483                 case RTM_NEWLINK:
484                 case RTM_DELLINK:
485                         netlink_parse_link(mesh, nlm);
486                         break;
487
488                 case RTM_NEWADDR:
489                 case RTM_DELADDR:
490                         netlink_parse_addr(mesh, nlm);
491                 }
492
493                 ptr += nlm->nlmsg_len;
494                 len -= nlm->nlmsg_len;
495         }
496 }
497
498 static void netlink_io_handler(event_loop_t *loop, void *data, int flags) {
499         (void)flags;
500         (void)data;
501         static time_t prev_update;
502         meshlink_handle_t *mesh = loop->data;
503
504         struct {
505                 struct nlmsghdr nlm;
506                 char data[16384];
507         } msg;
508
509         while(true) {
510                 ssize_t result = recv(mesh->discovery.pfroute_io.fd, &msg, sizeof(msg), MSG_DONTWAIT);
511
512                 if(result <= 0) {
513                         if(result == 0 || errno == EAGAIN || errno == EINTR) {
514                                 break;
515                         }
516
517                         logger(mesh, MESHLINK_ERROR, "Reading from Netlink socket failed: %s\n", strerror(errno));
518                         io_set(loop, &mesh->discovery.pfroute_io, 0);
519                 }
520
521                 if((size_t)result < sizeof(msg.nlm)) {
522                         logger(mesh, MESHLINK_ERROR, "Invalid Netlink message\n");
523                         break;
524                 }
525
526                 if(msg.nlm.nlmsg_type == NLMSG_DONE) {
527                         if(msg.nlm.nlmsg_seq == 1) {
528                                 // We just got the result of GETLINK, now send GETADDR.
529                                 netlink_getaddr(mesh->discovery.pfroute_io.fd);
530                         }
531                 } else {
532                         netlink_parse(mesh, &msg, result);
533
534                         if(loop->now.tv_sec > prev_update + 5) {
535                                 prev_update = loop->now.tv_sec;
536                                 handle_network_change(mesh, 1);
537                         }
538                 }
539         }
540 }
541 #elif defined(RTM_NEWADDR)
542 static void pfroute_io_handler(event_loop_t *loop, void *data, int flags) {
543         (void)flags;
544         (void)data;
545         static time_t prev_update;
546         meshlink_handle_t *mesh = loop->data;
547
548         struct {
549                 struct rt_msghdr rtm;
550                 char data[2048];
551         } msg;
552
553         while(true) {
554                 msg.rtm.rtm_version = 0;
555                 ssize_t result = recv(mesh->discovery.pfroute_io.fd, &msg, sizeof(msg), MSG_DONTWAIT);
556
557                 if(result <= 0) {
558                         if(result == 0 || errno == EAGAIN || errno == EINTR) {
559                                 break;
560                         }
561
562                         logger(mesh, MESHLINK_ERROR, "Reading from PFROUTE socket failed: %s\n", strerror(errno));
563                         io_set(loop, &mesh->discovery.pfroute_io, 0);
564                 }
565
566                 if(msg.rtm.rtm_version != RTM_VERSION) {
567                         logger(mesh, MESHLINK_ERROR, "Invalid PFROUTE message version\n");
568                         break;
569                 }
570
571                 switch(msg.rtm.rtm_type) {
572                 case RTM_IFINFO:
573                 case RTM_NEWADDR:
574                 case RTM_DELADDR:
575                         if(loop->now.tv_sec > prev_update + 5) {
576                                 prev_update = loop->now.tv_sec;
577                                 handle_network_change(mesh, 1);
578                         }
579
580                         break;
581
582                 default:
583                         break;
584                 }
585         }
586 }
587 #endif
588
589 bool discovery_start(meshlink_handle_t *mesh) {
590         logger(mesh, MESHLINK_DEBUG, "discovery_start called\n");
591
592         assert(mesh);
593
594         // Set up multicast sockets for mDNS
595         static const int one = 1;
596         static const int ttl = 255;
597
598         int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
599         sockaddr_t sa4 = {
600                 .in.sin_family = AF_INET,
601                 .in.sin_port = ntohs(5353),
602         };
603         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
604         bind(fd, &sa4.sa, SALEN(sa4.sa));
605         setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
606         setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
607         io_add(&mesh->loop, &mesh->discovery.sockets[0], mdns_io_handler, &mesh->discovery.sockets[0], fd, IO_READ);
608
609         sockaddr_t sa6 = {
610                 .in6.sin6_family = AF_INET6,
611                 .in6.sin6_port = ntohs(5353),
612         };
613         fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
614         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
615         setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
616         bind(fd, &sa6.sa, SALEN(sa6.sa));
617         setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
618         setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
619         io_add(&mesh->loop, &mesh->discovery.sockets[1], mdns_io_handler, &mesh->discovery.sockets[1], fd, IO_READ);
620
621 #if defined(__linux)
622         int sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
623
624         if(sock != -1) {
625                 struct sockaddr_nl sa;
626                 memset(&sa, 0, sizeof(sa));
627                 sa.nl_family = AF_NETLINK;
628                 sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
629
630                 if(bind(sock, (struct sockaddr *)&sa, sizeof(sa)) != -1) {
631                         io_add(&mesh->loop, &mesh->discovery.pfroute_io, netlink_io_handler, NULL, sock, IO_READ);
632                         netlink_getlink(sock);
633                 } else {
634                         logger(mesh, MESHLINK_WARNING, "Could not bind AF_NETLINK socket: %s", strerror(errno));
635                 }
636         } else {
637                 logger(mesh, MESHLINK_WARNING, "Could not open AF_NETLINK socket: %s", strerror(errno));
638         }
639
640 #elif defined(RTM_NEWADDR)
641         int sock = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
642
643         if(sock != -1) {
644                 io_add(&mesh->loop, &mesh->discovery.pfroute_io, pfroute_io_handler, NULL, sock, IO_READ);
645         } else {
646                 logger(mesh, MESHLINK_WARNING, "Could not open PF_ROUTE socket: %s", strerror(errno));
647         }
648
649 #endif
650
651         return true;
652 }
653
654 void discovery_stop(meshlink_handle_t *mesh) {
655         logger(mesh, MESHLINK_DEBUG, "discovery_stop called\n");
656
657         assert(mesh);
658
659         free(mesh->discovery.ifaces);
660         free(mesh->discovery.addresses);
661         mesh->discovery.ifaces = NULL;
662         mesh->discovery.addresses = NULL;
663         mesh->discovery.iface_count = 0;
664         mesh->discovery.address_count = 0;
665
666         if(mesh->discovery.pfroute_io.cb) {
667                 close(mesh->discovery.pfroute_io.fd);
668                 io_del(&mesh->loop, &mesh->discovery.pfroute_io);
669         }
670
671         for(int i = 0; i < 2; i++) {
672                 if(mesh->discovery.sockets[i].cb) {
673                         close(mesh->discovery.sockets[i].fd);
674                         io_del(&mesh->loop, &mesh->discovery.sockets[i]);
675                 }
676         }
677 }
678
679 void discovery_refresh(meshlink_handle_t *mesh) {
680         for(int i = 0; i < mesh->discovery.address_count; i++) {
681                 if(mesh->discovery.addresses[i].up) {
682                         send_mdns_packet(mesh, &mesh->discovery.addresses[i]);
683                 }
684         }
685 }