+ return (0);
+ }
+ } else if (ai->ai_family == AF_INET6) {
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)ai->ai_addr;
+
+ if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr)) {
+ if (setsockopt(se->data.client.fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ &se->interface, sizeof(se->interface)) != 0) {
+ char errbuf[1024];
+ ERROR("network plugin: setsockopt (ipv6-multicast-if): %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return (-1);
+ }
+
+ return (0);
+ }
+ }
+
+ /* else: Not a multicast interface. */
+ if (se->interface != 0) {
+#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && \
+ defined(SO_BINDTODEVICE)
+ char interface_name[IFNAMSIZ];
+
+ if (if_indextoname(se->interface, interface_name) == NULL)
+ return (-1);
+
+ DEBUG("network plugin: Binding socket to interface %s", interface_name);
+
+ if (setsockopt(se->data.client.fd, SOL_SOCKET, SO_BINDTODEVICE,
+ interface_name, sizeof(interface_name)) == -1) {
+ char errbuf[1024];
+ ERROR("network plugin: setsockopt (bind-if): %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return (-1);
+ }
+/* #endif HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */
+
+#else
+ WARNING("network plugin: Cannot set the interface on a unicast "
+ "socket because "
+#if !defined(SO_BINDTODEVICE)
+ "the \"SO_BINDTODEVICE\" socket option "
+#else
+ "the \"if_indextoname\" function "
+#endif
+ "is not available on your system.");
+#endif
+ }
+
+ return (0);
+} /* }}} network_set_interface */
+
+static int network_bind_socket(int fd, const struct addrinfo *ai,
+ const int interface_idx) {
+#if KERNEL_SOLARIS
+ char loop = 0;
+#else
+ int loop = 0;
+#endif
+ int yes = 1;
+
+ /* allow multiple sockets to use the same PORT number */
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
+ char errbuf[1024];
+ ERROR("network plugin: setsockopt (reuseaddr): %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return (-1);
+ }
+
+ DEBUG("fd = %i; calling `bind'", fd);
+
+ if (bind(fd, ai->ai_addr, ai->ai_addrlen) == -1) {
+ char errbuf[1024];
+ ERROR("bind: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+ return (-1);
+ }
+
+ if (ai->ai_family == AF_INET) {
+ struct sockaddr_in *addr = (struct sockaddr_in *)ai->ai_addr;
+ if (IN_MULTICAST(ntohl(addr->sin_addr.s_addr))) {
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+ struct ip_mreqn mreq;
+#else
+ struct ip_mreq mreq;
+#endif
+
+ DEBUG("fd = %i; IPv4 multicast address found", fd);
+
+ mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+ /* Set the interface using the interface index if
+ * possible (available). Unfortunately, the struct
+ * ip_mreqn is not portable. */
+ mreq.imr_address.s_addr = ntohl(INADDR_ANY);
+ mreq.imr_ifindex = interface_idx;
+#else
+ mreq.imr_interface.s_addr = ntohl(INADDR_ANY);
+#endif
+
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) ==
+ -1) {
+ char errbuf[1024];
+ ERROR("network plugin: setsockopt (multicast-loop): %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return (-1);
+ }
+
+ if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) ==
+ -1) {
+ char errbuf[1024];
+ ERROR("network plugin: setsockopt (add-membership): %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return (-1);
+ }
+
+ return (0);
+ }
+ } else if (ai->ai_family == AF_INET6) {
+ /* Useful example:
+ * http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)ai->ai_addr;
+ if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr)) {
+ struct ipv6_mreq mreq;
+
+ DEBUG("fd = %i; IPv6 multicast address found", fd);
+
+ memcpy(&mreq.ipv6mr_multiaddr, &addr->sin6_addr, sizeof(addr->sin6_addr));
+
+ /* http://developer.apple.com/documentation/Darwin/Reference/ManPages/man4/ip6.4.html
+ * ipv6mr_interface may be set to zeroes to
+ * choose the default multicast interface or to
+ * the index of a particular multicast-capable
+ * interface if the host is multihomed.
+ * Membership is associ-associated with a
+ * single interface; programs running on
+ * multihomed hosts may need to join the same
+ * group on more than one interface.*/
+ mreq.ipv6mr_interface = interface_idx;
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop,
+ sizeof(loop)) == -1) {
+ char errbuf[1024];
+ ERROR("network plugin: setsockopt (ipv6-multicast-loop): %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return (-1);
+ }
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ char errbuf[1024];
+ ERROR("network plugin: setsockopt (ipv6-add-membership): %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return (-1);
+ }
+
+ return (0);
+ }
+ }
+
+#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && \
+ defined(SO_BINDTODEVICE)
+ /* if a specific interface was set, bind the socket to it. But to avoid
+ * possible problems with multicast routing, only do that for non-multicast
+ * addresses */
+ if (interface_idx != 0) {
+ char interface_name[IFNAMSIZ];
+
+ if (if_indextoname(interface_idx, interface_name) == NULL)
+ return (-1);
+
+ DEBUG("fd = %i; Binding socket to interface %s", fd, interface_name);
+
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, interface_name,
+ sizeof(interface_name)) == -1) {
+ char errbuf[1024];
+ ERROR("network plugin: setsockopt (bind-if): %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return (-1);
+ }
+ }
+#endif /* HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */
+
+ return (0);
+} /* int network_bind_socket */