]> git.tuebingen.mpg.de Git - paraslash.git/blobdiff - udp_recv.c
Allow to specify UDPv4/6 multicast interface
[paraslash.git] / udp_recv.c
index bdc25411ef7a5c17cdde8509ab9e6e8de01db10c..9ea35d8d53ed2db68a54bfd1bc5527866d4f3061 100644 (file)
@@ -217,19 +217,89 @@ static void *udp_recv_parse_config(int argc, char **argv)
        return NULL;
 }
 
+/*
+ * Perform AF-independent joining of multicast receive addresses.
+ *
+ * \param fd   Bound socket descriptor.
+ * \param iface        The receiving multicast interface, or NULL for the default.
+ *
+ * \return Zero if okay, negative on error.
+ */
+static int mcast_receiver_setup(int fd, const char *iface)
+{
+       struct sockaddr_storage ss;
+       socklen_t sslen = sizeof(ss);
+       int id = iface == NULL ? 0 : if_nametoindex(iface);
+
+       if (getsockname(fd, (struct sockaddr *)&ss, &sslen) < 0)
+               goto err;
+
+       if (iface != NULL && id == 0)
+               PARA_WARNING_LOG("could not resolve interface %s, using default", iface);
+
+       switch (ss.ss_family) {
+       case AF_INET:
+               if (IN_MULTICAST(htonl(((struct sockaddr_in *)&ss)->sin_addr.s_addr))) {
+#ifdef HAVE_IP_MREQN
+                       struct ip_mreqn m4;
+
+                       m4.imr_address.s_addr   = INADDR_ANY;
+                       m4.imr_ifindex          = id;
+#else
+                       struct ip_mreq m4;
+
+                       m4.imr_interface.s_addr = INADDR_ANY;
+                       if (id != 0)
+                               PARA_ERROR_LOG("Setting IPv4 receiver mcast interface not supported.");
+
+#endif
+                       m4.imr_multiaddr        = ((struct sockaddr_in *)&ss)->sin_addr;
+
+                       if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &m4, sizeof(m4)) < 0)
+                               break;
+               }
+               return 0;
+       case AF_INET6:
+               if (IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)&ss)->sin6_addr)) {
+                       struct ipv6_mreq m6;
+
+                       memset(&m6, 0, sizeof(m6));
+                       memcpy(&m6.ipv6mr_multiaddr, &((struct sockaddr_in6 *)&ss)->sin6_addr, 16);
+                       m6.ipv6mr_interface = id;
+                       if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &m6, sizeof(m6)) < 0)
+                               break;
+               }
+               return 0;
+       default:
+               PARA_ERROR_LOG("address family %d not supported", ss.ss_family);
+               return -E_ADDRESS_LOOKUP;
+       }
+err:
+       return -ERRNO_TO_PARA_ERROR(errno);
+}
+
 static int udp_recv_open(struct receiver_node *rn)
 {
        struct private_udp_recv_data *purd;
        struct udp_recv_args_info *c = rn->conf;
+       char  *iface = c->iface_given ? c->iface_arg : NULL;
        int ret;
 
        rn->buf = para_calloc(UDP_RECV_CHUNK_SIZE);
        rn->private_data = para_calloc(sizeof(struct private_udp_recv_data));
        purd = rn->private_data;
-       ret = create_udp_recv_socket(c->host_arg, c->port_arg);
+
+       ret = makesock(AF_UNSPEC, IPPROTO_UDP, 1, c->host_arg, c->port_arg);
        if (ret < 0)
                goto err;
        purd->fd = ret;
+
+       ret = mcast_receiver_setup(purd->fd, iface);
+       if (ret < 0) {
+               close(purd->fd);
+               return ret;
+       }
+
        ret = mark_fd_nonblocking(purd->fd);
        if (ret < 0)
                goto err;