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); 676
This page was automatically generated by LXR 0.3.1. • OpenWrt