+ struct ipv6_mreq mreq;
+ int ttl = 255;
+ int yes = 1;
+ int no = 0;
+ struct sockaddr_in6 sa = { 0 };
+ int fd = iface->fd.fd;
+
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = htons(MCAST_PORT);
+ inet_pton(AF_INET6, MCAST_ADDR6, &sa.sin6_addr);
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.ipv6mr_multiaddr = sa.sin6_addr;
+ mreq.ipv6mr_interface = iface->ifindex;
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0)
+ fprintf(stderr, "ioctl failed: IPV6_MULTICAST_HOPS\n");
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0)
+ fprintf(stderr, "ioctl failed: IPV6_UNICAST_HOPS\n");
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
+ fprintf(stderr, "ioctl failed: SO_REUSEADDR\n");
+
+ setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq));
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+ fprintf(stderr, "failed to join multicast group: %s\n", strerror(errno));
+ close(fd);
+ fd = -1;
+ return -1;
+ }
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0)
+ fprintf(stderr, "ioctl failed: IPV6_RECVPKTINFO\n");
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0)
+ fprintf(stderr, "ioctl failed: IPV6_RECVHOPLIMIT\n");
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &no, sizeof(no)) < 0)
+ fprintf(stderr, "ioctl failed: IPV6_MULTICAST_LOOP\n");
+
+ return 0;
+}
+
+static void
+reconnect_socket(struct uloop_timeout *timeout)
+{
+ struct interface *iface = container_of(timeout, struct interface, reconnect);
+ char mcast_addr[16];
+ int type = 0;
+
+ if (iface->v6) {
+ snprintf(mcast_addr, sizeof(mcast_addr), "%s%%%s", iface->mcast_addr, iface->name);
+ type = USOCK_IPV6ONLY;
+ } else {
+ snprintf(mcast_addr, sizeof(mcast_addr), "%s", iface->mcast_addr);
+ type = USOCK_IPV4ONLY;
+ }
+
+ iface->fd.fd = usock(USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK | type, mcast_addr, "5353");
+ if (iface->fd.fd < 0) {
+ fprintf(stderr, "failed to add listener %s: %s\n", mcast_addr, strerror(errno));
+ goto retry;
+ }
+
+ if (!iface->v6 && interface_socket_setup4(iface)) {
+ iface->fd.fd = -1;
+ goto retry;
+ }
+
+ if (iface->v6 && interface_socket_setup6(iface)) {
+ iface->fd.fd = -1;
+ goto retry;
+ }
+
+ uloop_fd_add(&iface->fd, ULOOP_READ);
+ dns_send_question(iface, "_services._dns-sd._udp.local", TYPE_PTR);
+ announce_init(iface);
+ return;
+
+retry:
+ uloop_timeout_set(timeout, 1000);
+}
+
+
+static void interface_start(struct interface *iface)
+{
+ iface->fd.cb = read_socket;
+ iface->reconnect.cb = reconnect_socket;
+ uloop_timeout_set(&iface->reconnect, 100);
+}
+
+static void
+iface_update_cb(struct vlist_tree *tree, struct vlist_node *node_new,
+ struct vlist_node *node_old)
+{
+ struct interface *iface;
+
+ if (node_old) {
+ iface = container_of(node_old, struct interface, node);
+ interface_free(iface);
+ }
+
+ if (node_new) {
+ iface = container_of(node_new, struct interface, node);
+ interface_start(iface);
+ }
+}
+
+static int
+get_iface_ipv4(struct interface *iface)
+{
+ struct sockaddr_in *sin;