X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fnetwork.c;h=9cb8c38e071344a24fde6aa3909b99f619fe0cc9;hb=b6d95bf9f5713c62286851c1782ac95b2eb2fa21;hp=8615753807445755cab2b4a49c87b73e9e4ea893;hpb=3bd6fcdfd20002eee1f1803460728449c0c98f86;p=collectd.git diff --git a/src/network.c b/src/network.c index 86157538..9cb8c38e 100644 --- a/src/network.c +++ b/src/network.c @@ -52,6 +52,9 @@ #if HAVE_POLL_H # include #endif +#if HAVE_NET_IF_H +# include +#endif #if HAVE_LIBGCRYPT # include @@ -118,6 +121,7 @@ typedef struct sockent char *node; char *service; + int interface; union { @@ -1583,7 +1587,98 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai) return (0); } /* int network_set_ttl */ -static int network_bind_socket (int fd, const struct addrinfo *ai) +static int network_set_interface (const sockent_t *se, const struct addrinfo *ai) /* {{{ */ +{ + DEBUG ("network plugin: network_set_interface: interface index = %i;", + se->interface); + + assert (se->type == SOCKENT_TYPE_CLIENT); + + 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 + /* If possible, use the "ip_mreqn" structure which has + * an "interface index" member. Using the interface + * index is preferred here, because of its similarity + * to the way IPv6 handles this. Unfortunately, it + * appears not to be portable. */ + struct ip_mreqn mreq; + + memset (&mreq, 0, sizeof (mreq)); + mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr; + mreq.imr_address.s_addr = ntohl (INADDR_ANY); + mreq.imr_ifindex = se->interface; +#else + struct ip_mreq mreq; + + memset (&mreq, 0, sizeof (mreq)); + mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr; + mreq.imr_interface.s_addr = ntohl (INADDR_ANY); +#endif + + if (setsockopt (se->data.client.fd, IPPROTO_IP, IP_MULTICAST_IF, + &mreq, sizeof (mreq)) == -1) + { + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + 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)) == -1) + { + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); + return (-1); + } + + return (0); + } + } + +#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE) + if (se->interface != 0) + { + 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 ("setsockopt: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + } +#endif /* HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */ + + return (0); +} /* }}} network_set_interface */ + +static int network_bind_socket (int fd, const struct addrinfo *ai, const int interface_idx) { int loop = 0; int yes = 1; @@ -1612,12 +1707,24 @@ static int network_bind_socket (int fd, const struct addrinfo *ai) 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; - mreq.imr_interface.s_addr = htonl (INADDR_ANY); +#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) @@ -1638,6 +1745,8 @@ static int network_bind_socket (int fd, const struct addrinfo *ai) sizeof (errbuf))); return (-1); } + + return (0); } } else if (ai->ai_family == AF_INET6) @@ -1663,7 +1772,7 @@ static int network_bind_socket (int fd, const struct addrinfo *ai) * single interface; programs running on * multihomed hosts may need to join the same * group on more than one interface.*/ - mreq.ipv6mr_interface = 0; + mreq.ipv6mr_interface = interface_idx; if (setsockopt (fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof (loop)) == -1) @@ -1684,9 +1793,36 @@ static int network_bind_socket (int fd, const struct addrinfo *ai) 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 ("setsockopt: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + } +#endif /* HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */ + return (0); } /* int network_bind_socket */ @@ -1702,6 +1838,7 @@ static int sockent_init (sockent_t *se, int type) /* {{{ */ se->type = SOCKENT_TYPE_CLIENT; se->node = NULL; se->service = NULL; + se->interface = 0; se->next = NULL; if (type == SOCKENT_TYPE_SERVER) @@ -1850,7 +1987,7 @@ static int sockent_open (sockent_t *se) /* {{{ */ continue; } - status = network_bind_socket (*tmp, ai_ptr); + status = network_bind_socket (*tmp, ai_ptr, se->interface); if (status != 0) { close (*tmp); @@ -1890,6 +2027,7 @@ static int sockent_open (sockent_t *se) /* {{{ */ se->data.client.addrlen = ai_ptr->ai_addrlen; network_set_ttl (se, ai_ptr); + network_set_interface (se, ai_ptr); /* We don't open more than one write-socket per * node/service pair.. */ @@ -2601,6 +2739,25 @@ static int network_config_set_ttl (const oconfig_item_t *ci) /* {{{ */ return (0); } /* }}} int network_config_set_ttl */ +static int network_config_set_interface (const oconfig_item_t *ci, /* {{{ */ + int *interface) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("network plugin: The `Interface' config option needs exactly " + "one string argument."); + return (-1); + } + + if (interface == NULL) + return (-1); + + *interface = if_nametoindex (ci->values[0].value.string); + + return (0); +} /* }}} int network_config_set_interface */ + static int network_config_set_buffer_size (const oconfig_item_t *ci) /* {{{ */ { int tmp; @@ -2712,6 +2869,10 @@ static int network_config_add_listen (const oconfig_item_t *ci) /* {{{ */ &se->data.server.security_level); else #endif /* HAVE_LIBGCRYPT */ + if (strcasecmp ("Interface", child->key) == 0) + network_config_set_interface (child, + &se->interface); + else { WARNING ("network plugin: Option `%s' is not allowed here.", child->key); @@ -2790,6 +2951,10 @@ static int network_config_add_server (const oconfig_item_t *ci) /* {{{ */ &se->data.client.security_level); else #endif /* HAVE_LIBGCRYPT */ + if (strcasecmp ("Interface", child->key) == 0) + network_config_set_interface (child, + &se->interface); + else { WARNING ("network plugin: Option `%s' is not allowed here.", child->key);