X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fnetwork.c;h=74325a29483af445415cf89256f7be664721c559;hb=593ea76dca367478b92e17f91e663fe7c67b4df9;hp=a3ec5d94a5d9530308465091085eeacd1af99fac;hpb=07be522384e753b7921213a0dc96a50336c79a66;p=collectd.git diff --git a/src/network.c b/src/network.c index a3ec5d94..74325a29 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 { @@ -1590,7 +1594,110 @@ 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)) != 0) + { + 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)) != 0) + { + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); + return (-1); + } + + return (0); + } + } + + /* else: Not a multicast interface. */ +#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 */ + +#else + WARNING ("network plugin: Cannot set the interface on a unicast " + "socket because " +# if !defined(SO_BINDTODEVICE) + "the 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) { int loop = 0; int yes = 1; @@ -1619,12 +1726,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) @@ -1645,6 +1764,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) @@ -1670,7 +1791,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) @@ -1691,8 +1812,35 @@ 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 */ @@ -1709,6 +1857,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) @@ -1857,7 +2006,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); @@ -1897,6 +2046,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.. */ @@ -2608,6 +2758,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; @@ -2719,6 +2888,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); @@ -2797,6 +2970,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);