X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fnetwork.c;h=eb074e9d66153eff21dee68a31d9c9ec5816b457;hb=9d095a155631f4cf973b9c5a489f81ffbef5263b;hp=1b453753a4d0204f930afce574624a039d3e313e;hpb=f9d51dc8fabe44f3cd17499713d63b518f0e1853;p=collectd.git diff --git a/src/network.c b/src/network.c index 1b453753..eb074e9d 100644 --- a/src/network.c +++ b/src/network.c @@ -4,17 +4,18 @@ * Copyright (C) 2009 Aman Gupta * * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; only version 2 of the License is applicable. + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; only version 2.1 of the License is + * applicable. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: * Florian octo Forster @@ -51,6 +52,9 @@ #if HAVE_POLL_H # include #endif +#if HAVE_NET_IF_H +# include +#endif #if HAVE_LIBGCRYPT # include @@ -253,6 +257,7 @@ typedef struct receive_list_entry_s receive_list_entry_t; * Private variables */ static int network_config_ttl = 0; +static int network_config_interface_idx = 0; static size_t network_config_packet_size = 1024; static int network_config_forward = 0; static int network_config_stats = 0; @@ -1582,6 +1587,91 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai) return (0); } /* int network_set_ttl */ +static int network_set_interface (const sockent_t *se, const struct addrinfo *ai) /* {{{ */ +{ + DEBUG ("network plugin: network_set_interface: interface index = %i;", + network_config_interface_idx); + + 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 KERNEL_LINUX + struct ip_mreqn mreq; +#else + struct ip_mreq mreq; +#endif + + mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr; +#if KERNEL_LINUX + mreq.imr_address.s_addr = ntohl (INADDR_ANY); + mreq.imr_ifindex = network_config_interface_idx; +#else + 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, + &network_config_interface_idx, + sizeof (network_config_interface_idx)) == -1) + { + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); + return (-1); + } + + return (0); + } + } + +#if KERNEL_LINUX + if (network_config_interface_idx != 0) + { + char interface_name[IFNAMSIZ]; + + if (if_indextoname (network_config_interface_idx, 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 + + return (0); +} /* }}} network_set_interface */ + static int network_bind_socket (int fd, const struct addrinfo *ai) { int loop = 0; @@ -1611,12 +1701,21 @@ 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 KERNEL_LINUX + 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 KERNEL_LINUX + mreq.imr_address.s_addr = ntohl (INADDR_ANY); + mreq.imr_ifindex = network_config_interface_idx; +#else + mreq.imr_interface.s_addr = ntohl (INADDR_ANY); +#endif if (setsockopt (fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof (loop)) == -1) @@ -1637,6 +1736,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) @@ -1662,7 +1763,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 = network_config_interface_idx; if (setsockopt (fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof (loop)) == -1) @@ -1683,8 +1784,35 @@ static int network_bind_socket (int fd, const struct addrinfo *ai) sizeof (errbuf))); return (-1); } + + return (0); + } + } + +#if KERNEL_LINUX + /* 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 (network_config_interface_idx != 0) + { + char interface_name[IFNAMSIZ]; + + if (if_indextoname (network_config_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 return (0); } /* int network_bind_socket */ @@ -1889,6 +2017,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.. */ @@ -2600,6 +2729,21 @@ 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) /* {{{ */ +{ + 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); + } + + network_config_interface_idx = 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; @@ -2841,6 +2985,8 @@ static int network_config (oconfig_item_t *ci) /* {{{ */ network_config_add_server (child); else if (strcasecmp ("TimeToLive", child->key) == 0) network_config_set_ttl (child); + else if (strcasecmp ("Interface", child->key) == 0) + network_config_set_interface (child); else if (strcasecmp ("MaxPacketSize", child->key) == 0) network_config_set_buffer_size (child); else if (strcasecmp ("Forward", child->key) == 0)