ubus: assume that the service iface can be NULL
[project/mdnsd.git] / interface.c
1 /*
2 * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
3 * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License version 2.1
7 * as published by the Free Software Foundation
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15 #define _GNU_SOURCE
16 #include <sys/socket.h>
17 #include <sys/ioctl.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/utsname.h>
21 #include <net/if.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <sys/types.h>
25
26 #include <ifaddrs.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <errno.h>
32
33 #include <libubox/usock.h>
34 #include <libubox/uloop.h>
35 #include <libubox/avl-cmp.h>
36 #include <libubox/utils.h>
37 #include "cache.h"
38 #include "interface.h"
39 #include "util.h"
40 #include "dns.h"
41 #include "announce.h"
42 #include "service.h"
43
44 static struct uloop_fd ufd[] = {
45 [SOCK_UC_IPV4] = { .fd = -1 },
46 [SOCK_UC_IPV6] = { .fd = -1 },
47 [SOCK_MC_IPV4] = { .fd = -1 },
48 [SOCK_MC_IPV6] = { .fd = -1 },
49 };
50
51 static int
52 interface_send_packet4(struct interface *iface, struct sockaddr_in *to, struct iovec *iov, int iov_len)
53 {
54 static size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1];
55 static struct sockaddr_in a = {};
56 static struct msghdr m = {
57 .msg_name = (struct sockaddr *) &a,
58 .msg_namelen = sizeof(a),
59 .msg_control = cmsg_data,
60 .msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)),
61 };
62 struct in_pktinfo *pkti;
63 struct cmsghdr *cmsg;
64 int fd;
65
66 a.sin_family = AF_INET;
67 a.sin_port = htons(MCAST_PORT);
68 m.msg_iov = iov;
69 m.msg_iovlen = iov_len;
70
71 memset(cmsg_data, 0, sizeof(cmsg_data));
72 cmsg = CMSG_FIRSTHDR(&m);
73 cmsg->cmsg_len = m.msg_controllen;
74 cmsg->cmsg_level = IPPROTO_IP;
75 cmsg->cmsg_type = IP_PKTINFO;
76
77 pkti = (struct in_pktinfo*) CMSG_DATA(cmsg);
78 pkti->ipi_ifindex = iface->ifindex;
79
80 fd = ufd[iface->type].fd;
81 if (interface_multicast(iface)) {
82 a.sin_addr.s_addr = inet_addr(MCAST_ADDR);
83 if (to)
84 fprintf(stderr, "Ignoring IPv4 address for multicast interface\n");
85 } else {
86 a.sin_addr.s_addr = to->sin_addr.s_addr;
87 }
88
89 return sendmsg(fd, &m, 0);
90 }
91
92 static int
93 interface_send_packet6(struct interface *iface, struct sockaddr_in6 *to, struct iovec *iov, int iov_len)
94 {
95 static size_t cmsg_data[( CMSG_SPACE(sizeof(struct in6_pktinfo)) / sizeof(size_t)) + 1];
96 static struct sockaddr_in6 a = {};
97 static struct msghdr m = {
98 .msg_name = (struct sockaddr *) &a,
99 .msg_namelen = sizeof(a),
100 .msg_control = cmsg_data,
101 .msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo)),
102 };
103 struct in6_pktinfo *pkti;
104 struct cmsghdr *cmsg;
105 int fd;
106
107 a.sin6_family = AF_INET6;
108 a.sin6_port = htons(MCAST_PORT);
109 a.sin6_scope_id = iface->ifindex;
110 m.msg_iov = iov;
111 m.msg_iovlen = iov_len;
112
113 memset(cmsg_data, 0, sizeof(cmsg_data));
114 cmsg = CMSG_FIRSTHDR(&m);
115 cmsg->cmsg_len = m.msg_controllen;
116 cmsg->cmsg_level = IPPROTO_IPV6;
117 cmsg->cmsg_type = IPV6_PKTINFO;
118
119 pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg);
120 pkti->ipi6_ifindex = iface->ifindex;
121
122 fd = ufd[iface->type].fd;
123 if (interface_multicast(iface)) {
124 inet_pton(AF_INET6, MCAST_ADDR6, &a.sin6_addr);
125 if (to)
126 fprintf(stderr, "Ignoring IPv6 address for multicast interface\n");
127 } else {
128 a.sin6_addr = to->sin6_addr;
129 }
130
131 return sendmsg(fd, &m, 0);
132 }
133
134 int
135 interface_send_packet(struct interface *iface, struct sockaddr *to, struct iovec *iov, int iov_len)
136 {
137 if (!interface_multicast(iface) && !to) {
138 fprintf(stderr, "No IP address specified for unicast interface\n");
139 errno = EINVAL;
140 return -1;
141 }
142
143 if (debug > 1) {
144 fprintf(stderr, "TX ipv%d: %s\n", interface_ipv6(iface) ? 6 : 4, iface->name);
145 fprintf(stderr, " multicast: %d\n", interface_multicast(iface));
146 }
147
148 if (interface_ipv6(iface))
149 return interface_send_packet6(iface, (struct sockaddr_in6 *)to, iov, iov_len);
150
151 return interface_send_packet4(iface, (struct sockaddr_in *)to, iov, iov_len);
152 }
153
154 static struct interface *interface_lookup(unsigned int ifindex, enum umdns_socket_type type)
155 {
156 struct interface *iface;
157
158 vlist_for_each_element(&interfaces, iface, node)
159 if (iface->ifindex == ifindex && iface->type == type)
160 return iface;
161
162 return NULL;
163 }
164
165 static void interface_free(struct interface *iface)
166 {
167 cache_cleanup(iface);
168 announce_free(iface);
169 free(iface->addrs.v4);
170 free(iface);
171 }
172
173 static int
174 interface_valid_src(void *ip1, void *mask, void *ip2, int len)
175 {
176 uint8_t *i1 = ip1;
177 uint8_t *i2 = ip2;
178 uint8_t *m = mask;
179 int i;
180
181 if (cfg_no_subnet)
182 return 0;
183
184 for (i = 0; i < len; i++, i1++, i2++, m++) {
185 if ((*i1 & *m) != (*i2 & *m))
186 return -1;
187 }
188
189 return 0;
190 }
191
192 static void
193 read_socket4(struct uloop_fd *u, unsigned int events)
194 {
195 enum umdns_socket_type type = (enum umdns_socket_type)(u - ufd);
196 struct interface *iface;
197 static uint8_t buffer[8 * 1024];
198 struct iovec iov[1];
199 char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo)) + CMSG_SPACE(sizeof(int)) + 1];
200 struct cmsghdr *cmsgptr;
201 struct msghdr msg;
202 socklen_t len;
203 struct sockaddr_in from;
204 int flags = 0;
205 uint8_t ttl = 0;
206 struct in_pktinfo *inp = NULL;
207 bool valid_src = false;
208
209 if (u->eof) {
210 uloop_end();
211 return;
212 }
213
214 iov[0].iov_base = buffer;
215 iov[0].iov_len = sizeof(buffer);
216
217 memset(&msg, 0, sizeof(msg));
218 msg.msg_name = (struct sockaddr *) &from;
219 msg.msg_namelen = sizeof(struct sockaddr_in);
220 msg.msg_iov = iov;
221 msg.msg_iovlen = 1;
222 msg.msg_control = &cmsg;
223 msg.msg_controllen = sizeof(cmsg);
224
225 len = recvmsg(u->fd, &msg, flags);
226 if (len == -1) {
227 perror("read failed");
228 return;
229 }
230 for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
231 void *c = CMSG_DATA(cmsgptr);
232
233 switch (cmsgptr->cmsg_type) {
234 case IP_PKTINFO:
235 inp = ((struct in_pktinfo *) c);
236 break;
237
238 case IP_TTL:
239 ttl = (uint8_t) *((int *) c);
240 break;
241
242 default:
243 fprintf(stderr, "unknown cmsg %x\n", cmsgptr->cmsg_type);
244 return;
245 }
246 }
247
248 if (!inp)
249 return;
250
251 iface = interface_lookup(inp->ipi_ifindex, type);
252 if (!iface)
253 return;
254
255 if (debug > 1) {
256 char buf[256];
257
258 fprintf(stderr, "RX ipv4: %s\n", iface->name);
259 fprintf(stderr, " multicast: %d\n", interface_multicast(iface));
260 inet_ntop(AF_INET, &from.sin_addr, buf, 256);
261 fprintf(stderr, " src %s:%d\n", buf, ntohs(from.sin_port));
262 inet_ntop(AF_INET, &inp->ipi_spec_dst, buf, 256);
263 fprintf(stderr, " dst %s\n", buf);
264 inet_ntop(AF_INET, &inp->ipi_addr, buf, 256);
265 fprintf(stderr, " real %s\n", buf);
266 fprintf(stderr, " ttl %u\n", ttl);
267 }
268
269 for (size_t i = 0; i < iface->addrs.n_addr; i++) {
270 if (!interface_valid_src((void *)&iface->addrs.v4[i].addr,
271 (void *)&iface->addrs.v4[i].mask,
272 (void *) &from.sin_addr, 4)) {
273 valid_src = true;
274 break;
275 }
276 }
277
278 if (!valid_src)
279 return;
280
281 dns_handle_packet(iface, (struct sockaddr *) &from, ntohs(from.sin_port), buffer, len);
282 }
283
284 static void
285 read_socket6(struct uloop_fd *u, unsigned int events)
286 {
287 enum umdns_socket_type type = (enum umdns_socket_type)(u - ufd);
288 struct interface *iface;
289 static uint8_t buffer[8 * 1024];
290 struct iovec iov[1];
291 char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)) + 1];
292 struct cmsghdr *cmsgptr;
293 struct msghdr msg;
294 socklen_t len;
295 struct sockaddr_in6 from;
296 int flags = 0;
297 int ttl = 0;
298 struct in6_pktinfo *inp = NULL;
299 bool valid_src = false;
300
301 if (u->eof) {
302 uloop_end();
303 return;
304 }
305
306 iov[0].iov_base = buffer;
307 iov[0].iov_len = sizeof(buffer);
308
309 memset(&msg, 0, sizeof(msg));
310 msg.msg_name = (struct sockaddr *) &from;
311 msg.msg_namelen = sizeof(struct sockaddr_in6);
312 msg.msg_iov = iov;
313 msg.msg_iovlen = 1;
314 msg.msg_control = &cmsg6;
315 msg.msg_controllen = sizeof(cmsg6);
316
317 len = recvmsg(u->fd, &msg, flags);
318 if (len == -1) {
319 perror("read failed");
320 return;
321 }
322 for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
323 void *c = CMSG_DATA(cmsgptr);
324
325 switch (cmsgptr->cmsg_type) {
326 case IPV6_PKTINFO:
327 inp = ((struct in6_pktinfo *) c);
328 break;
329
330 case IPV6_HOPLIMIT:
331 ttl = (uint8_t) *((int *) c);
332 break;
333
334 default:
335 fprintf(stderr, "unknown cmsg %x\n", cmsgptr->cmsg_type);
336 return;
337 }
338 }
339
340 if (!inp)
341 return;
342
343 iface = interface_lookup(inp->ipi6_ifindex, type);
344 if (!iface)
345 return;
346
347 if (debug > 1) {
348 char buf[256];
349
350 fprintf(stderr, "RX ipv6: %s\n", iface->name);
351 fprintf(stderr, " multicast: %d\n", interface_multicast(iface));
352 inet_ntop(AF_INET6, &from.sin6_addr, buf, 256);
353 fprintf(stderr, " src %s:%d\n", buf, ntohs(from.sin6_port));
354 inet_ntop(AF_INET6, &inp->ipi6_addr, buf, 256);
355 fprintf(stderr, " dst %s\n", buf);
356 fprintf(stderr, " ttl %u\n", ttl);
357 }
358
359 for (size_t i = 0; i < iface->addrs.n_addr; i++) {
360 if (!interface_valid_src((void *)&iface->addrs.v6[i].addr,
361 (void *)&iface->addrs.v6[i].mask,
362 (void *)&from.sin6_addr, 6)) {
363 valid_src = true;
364 break;
365 }
366 }
367
368 if (!valid_src)
369 return;
370
371 dns_handle_packet(iface, (struct sockaddr *) &from, ntohs(from.sin6_port), buffer, len);
372 }
373
374 static int
375 interface_mcast_setup4(struct interface *iface)
376 {
377 struct ip_mreqn mreq;
378 struct sockaddr_in sa = {};
379 int fd = ufd[SOCK_MC_IPV4].fd;
380
381 sa.sin_family = AF_INET;
382 sa.sin_port = htons(MCAST_PORT);
383 inet_pton(AF_INET, MCAST_ADDR, &sa.sin_addr);
384
385 memset(&mreq, 0, sizeof(mreq));
386 mreq.imr_multiaddr = sa.sin_addr;
387 mreq.imr_ifindex = iface->ifindex;
388 mreq.imr_address.s_addr = iface->addrs.v4[0].addr.s_addr;
389
390 /* Some network drivers have issues with dropping membership of
391 * mcast groups when the iface is down, but don't allow rejoining
392 * when it comes back up. This is an ugly workaround
393 * -- this was copied from avahi --
394 */
395 setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
396 setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
397
398 return 0;
399 }
400
401 static int
402 interface_mcast_setup6(struct interface *iface)
403 {
404 struct ipv6_mreq mreq;
405 struct sockaddr_in6 sa = {};
406 int fd = ufd[SOCK_MC_IPV6].fd;
407
408 sa.sin6_family = AF_INET6;
409 sa.sin6_port = htons(MCAST_PORT);
410 inet_pton(AF_INET6, MCAST_ADDR6, &sa.sin6_addr);
411
412 memset(&mreq, 0, sizeof(mreq));
413 mreq.ipv6mr_multiaddr = sa.sin6_addr;
414 mreq.ipv6mr_interface = iface->ifindex;
415
416 setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq));
417 setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
418
419 return 0;
420 }
421
422 static void interface_start(struct interface *iface)
423 {
424 if (iface->type & SOCKTYPE_BIT_UNICAST)
425 return;
426
427 if (iface->type & SOCKTYPE_BIT_IPV6)
428 interface_mcast_setup6(iface);
429 else
430 interface_mcast_setup4(iface);
431
432 dns_send_question(iface, NULL, C_DNS_SD, TYPE_PTR, 0);
433 announce_init(iface);
434 }
435
436 static bool
437 iface_equal(struct interface *if_old, struct interface *if_new)
438 {
439 size_t addr_size;
440
441 if (if_old->ifindex != if_new->ifindex ||
442 if_old->addrs.n_addr != if_new->addrs.n_addr)
443 return false;
444
445 if (if_old->type & SOCKTYPE_BIT_IPV6)
446 addr_size = sizeof(*if_old->addrs.v6);
447 else
448 addr_size = sizeof(*if_old->addrs.v4);
449 addr_size *= if_old->addrs.n_addr;
450 if (memcmp(if_old->addrs.v4, if_new->addrs.v4, addr_size) != 0)
451 return false;
452
453 return true;
454 }
455
456 static void
457 iface_update_cb(struct vlist_tree *tree, struct vlist_node *node_new,
458 struct vlist_node *node_old)
459 {
460 struct interface *if_old = container_of_safe(node_old, struct interface, node);
461 struct interface *if_new = container_of_safe(node_new, struct interface, node);
462
463 if (if_old && if_new) {
464 if (!iface_equal(if_old, if_new))
465 cache_cleanup(if_old);
466 free(if_old->addrs.v4);
467 if_old->addrs = if_new->addrs;
468 if_old->ifindex = if_new->ifindex;
469 free(if_new);
470 return;
471 }
472
473 if (if_old)
474 interface_free(if_old);
475
476 if (if_new)
477 interface_start(if_new);
478 }
479
480 static int interface_init_socket(enum umdns_socket_type type)
481 {
482 struct sockaddr_in6 local6 = {
483 .sin6_family = AF_INET6
484 };
485 struct sockaddr_in local = {
486 .sin_family = AF_INET
487 };
488 uint8_t ttl = 255;
489 int ittl = 255;
490 int yes = 1;
491 int no = 0;
492 int fd;
493 int af = (type & SOCKTYPE_BIT_IPV6) ? AF_INET6 : AF_INET;
494
495 if (ufd[type].fd >= 0)
496 return 0;
497
498 ufd[type].fd = fd = socket(af, SOCK_DGRAM, 0);
499 if (fd < 0)
500 return -1;
501
502 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
503 #ifdef SO_REUSEPORT
504 setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes));
505 #endif
506
507 switch (type) {
508 case SOCK_UC_IPV4:
509 case SOCK_UC_IPV6:
510 break;
511 case SOCK_MC_IPV4:
512 setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
513 setsockopt(fd, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl));
514 setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &no, sizeof(no));
515 local.sin_port = htons(MCAST_PORT);
516 break;
517 case SOCK_MC_IPV6:
518 setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
519 setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
520 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes));
521 setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &no, sizeof(no));
522 local6.sin6_port = htons(MCAST_PORT);
523 break;
524 }
525
526 if (type & SOCKTYPE_BIT_IPV6) {
527 ufd[type].cb = read_socket6;
528 if (bind(fd, (struct sockaddr *)&local6, sizeof(local6)) < 0)
529 goto error;
530
531 setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes));
532 setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes));
533 } else {
534 ufd[type].cb = read_socket4;
535 if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0)
536 goto error;
537
538 setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes));
539 setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes));
540 }
541
542 uloop_fd_add(&ufd[type], ULOOP_READ);
543
544 return 0;
545
546 error:
547 close(ufd[type].fd);
548 return -1;
549 }
550
551 static void
552 __interface_add(const char *name, enum umdns_socket_type type,
553 struct interface_addr_list *list)
554 {
555 struct interface *iface;
556 unsigned int ifindex;
557 char *id_buf;
558
559 if (interface_init_socket(type))
560 goto error;
561
562 ifindex = if_nametoindex(name);
563 if (!ifindex)
564 goto error;
565
566 iface = calloc_a(sizeof(*iface),
567 &id_buf, strlen(name) + 3);
568
569 sprintf(id_buf, "%d_%s", type, name);
570 iface->name = id_buf + 2;
571 iface->ifindex = ifindex;
572 iface->type = type;
573 iface->addrs = *list;
574
575 vlist_add(&interfaces, &iface->node, id_buf);
576 return;
577
578 error:
579 free(list->v4);
580 }
581
582 int interface_add(const char *name)
583 {
584 struct ifaddrs *ifap, *ifa;
585 struct interface_addr_list addr4 = {}, addr6 = {};
586
587 getifaddrs(&ifap);
588
589 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
590 if (strcmp(ifa->ifa_name, name))
591 continue;
592 if (ifa->ifa_addr->sa_family == AF_INET) {
593 struct sockaddr_in *sin;
594
595 if (cfg_proto && (cfg_proto != 4))
596 continue;
597
598 addr4.v4 = realloc(addr4.v4, (addr4.n_addr + 1) * sizeof(*addr4.v4));
599 sin = (struct sockaddr_in *) ifa->ifa_addr;
600 addr4.v4[addr4.n_addr].addr = sin->sin_addr;
601 sin = (struct sockaddr_in *) ifa->ifa_netmask;
602 addr4.v4[addr4.n_addr++].mask = sin->sin_addr;
603 }
604
605 if (ifa->ifa_addr->sa_family == AF_INET6) {
606 uint8_t ll_prefix[] = {0xfe, 0x80 };
607 struct sockaddr_in6 *sin6;
608
609 if (cfg_proto && (cfg_proto != 6))
610 continue;
611
612 sin6 = (struct sockaddr_in6 *) ifa->ifa_addr;
613 if (memcmp(&sin6->sin6_addr, &ll_prefix, 2))
614 continue;
615
616 addr6.v6 = realloc(addr6.v6, (addr6.n_addr + 1) * sizeof(*addr6.v6));
617 sin6 = (struct sockaddr_in6 *) ifa->ifa_addr;
618 addr6.v6[addr6.n_addr].addr = sin6->sin6_addr;
619 sin6 = (struct sockaddr_in6 *) ifa->ifa_netmask;
620 addr6.v6[addr6.n_addr++].mask = sin6->sin6_addr;
621 }
622 }
623
624 freeifaddrs(ifap);
625
626 if (addr4.n_addr) {
627 size_t addr_size = addr4.n_addr * sizeof(*addr4.v4);
628 void *addr_dup = malloc(addr_size);
629
630 memcpy(addr_dup, addr4.v4, addr_size);
631 __interface_add(name, SOCK_UC_IPV4, &addr4);
632 addr4.v4 = addr_dup;
633 __interface_add(name, SOCK_MC_IPV4, &addr4);
634 }
635
636 if (addr6.n_addr) {
637 size_t addr_size = addr6.n_addr * sizeof(*addr6.v6);
638 void *addr_dup = malloc(addr_size);
639
640 memcpy(addr_dup, addr6.v6, addr_size);
641 __interface_add(name, SOCK_UC_IPV6, &addr6);
642 addr6.v6 = addr_dup;
643 __interface_add(name, SOCK_MC_IPV6, &addr6);
644 }
645
646 return !addr4.n_addr && !addr6.n_addr;
647 }
648
649 void interface_shutdown(void)
650 {
651 struct interface *iface;
652
653 vlist_for_each_element(&interfaces, iface, node)
654 if (interface_multicast(iface)) {
655 dns_reply_a(iface, NULL, 0, NULL);
656 dns_reply_a_additional(iface, NULL, 0);
657 service_announce_services(iface, NULL, 0);
658 }
659
660 for (size_t i = 0; i < ARRAY_SIZE(ufd); i++) {
661 uloop_fd_delete(&ufd[i]);
662 close(ufd[i].fd);
663 ufd[i].fd = -1;
664 }
665 }
666
667 struct interface *interface_get(const char *name, enum umdns_socket_type type)
668 {
669 char id_buf[32];
670 snprintf(id_buf, sizeof(id_buf), "%d_%s", type, name);
671 struct interface *iface = vlist_find(&interfaces, id_buf, iface, node);
672 return iface;
673 }
674
675 VLIST_TREE(interfaces, avl_strcmp, iface_update_cb, true, false);