diff --git a/src/network.c b/src/network.c
index 914235aaaba09a0b501c42db53839fb100e1da3c..22e911a87b8e4d4fe89d805abc9c9559ce2463c6 100644 (file)
--- a/src/network.c
+++ b/src/network.c
/**
* collectd - src/network.c
/**
* collectd - src/network.c
- * Copyright (C) 2006 Florian octo Forster
+ * Copyright (C) 2005,2006 Florian octo Forster
*
* 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
*
* 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
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include "network.h"
#include "common.h"
#include "network.h"
#include "common.h"
+#include "configfile.h"
+#include "utils_debug.h"
-/*
- * From RFC2365:
- *
- * The IPv4 Organization Local Scope -- 239.192.0.0/14
- *
- * 239.192.0.0/14 is defined to be the IPv4 Organization Local Scope, and is
- * the space from which an organization should allocate sub-ranges when
- * defining scopes for private use.
- *
- * Port 25826 is not assigned as of 2005-09-12
- */
+/* 1500 - 40 - 8 = Ethernet packet - IPv6 header - UDP header */
+/* #define BUFF_SIZE 1452 */
-#define IPV4_MCAST_GROUP "239.192.74.66"
-#define UDP_PORT 25826
+#ifndef IPV6_ADD_MEMBERSHIP
+# ifdef IPV6_JOIN_GROUP
+# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+# else
+# error "Neither IP_ADD_MEMBERSHIP nor IPV6_JOIN_GROUP is defined"
+# endif
+#endif /* !IP_ADD_MEMBERSHIP */
-/* 1500 - 40 - 8 = Ethernet packet - IPv6 header - UDP header */
-#define BUFF_SIZE 1452
+#define BUFF_SIZE 4096
+
+extern int operating_mode;
-typedef struct socklist
+typedef struct sockent
{
{
- int fd;
- struct socklist *next;
-} socklist_t;
+ int fd;
+ int mode;
+ struct sockaddr_storage *addr;
+ socklen_t addrlen;
+ struct sockent *next;
+} sockent_t;
-static socklist_t *listen_socks_head = NULL;
+static sockent_t *socklist_head = NULL;
-uint16_t get_port (void)
+static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
{
{
- char *port_str;
- int port_int;
- uint16_t ret;
+ char *ttl_str;
+ int ttl_int;
+
+ ttl_str = cf_get_option ("TimeToLive", NULL);
+ if (ttl_str == NULL)
+ return (-1);
- port_str = cf_get_option ("Port", NULL);
- port_int = 0;
+ ttl_int = atoi (ttl_str);
+ if ((ttl_int < 1) || (ttl_int > 255))
+ {
+ syslog (LOG_WARNING, "A TTL value of %i is invalid.", ttl_int);
+ return (-1);
+ }
- if (port_str != NULL)
- port_int = atoi (port_str);
+ DBG ("ttl = %i", ttl_int);
- if (port_int == 0)
- port_int = UDP_PORT;
+ if (ai->ai_family == AF_INET)
+ {
+ struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
+ int optname;
- ret = htons (port_int);
- return (ret);
+ if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
+ optname = IP_MULTICAST_TTL;
+ else
+ optname = IP_TTL;
+
+ if (setsockopt (se->fd, IPPROTO_IP, optname,
+ &ttl_int, sizeof (ttl_int)) == -1)
+ {
+ syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
+ return (-1);
+ }
+ }
+ 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;
+ int optname;
+
+ if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
+ optname = IPV6_MULTICAST_HOPS;
+ else
+ optname = IPV6_UNICAST_HOPS;
+
+ if (setsockopt (se->fd, IPPROTO_IPV6, optname,
+ &ttl_int, sizeof (ttl_int)) == -1)
+ {
+ syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
+{
+ int loop = 1;
+
+ DBG ("fd = %i; calling `bind'", se->fd);
+
+ if (bind (se->fd, ai->ai_addr, ai->ai_addrlen) == -1)
+ {
+ syslog (LOG_ERR, "bind: %s", strerror (errno));
+ 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)))
+ {
+ struct ip_mreq mreq;
+
+ DBG ("fd = %i; IPv4 multicast address found", se->fd);
+
+ mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+ mreq.imr_interface.s_addr = htonl (INADDR_ANY);
+
+ if (setsockopt (se->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &loop, sizeof (loop)) == -1)
+ {
+ syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
+ return (-1);
+ }
+
+ if (setsockopt (se->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreq, sizeof (mreq)) == -1)
+ {
+ syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
+ return (-1);
+ }
+ }
+ }
+ 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;
+
+ DBG ("fd = %i; IPv6 multicast address found", se->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 = 0;
+
+ if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &loop, sizeof (loop)) == -1)
+ {
+ syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
+ return (-1);
+ }
+
+ if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
+ &mreq, sizeof (mreq)) == -1)
+ {
+ syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
+ return (-1);
+ }
+ }
+ }
+
+ return (0);
}
}
-int network_create_listen_socket (const char *node, const char *service)
+int network_create_socket (const char *node, const char *service)
{
{
- socklist_t *socklist_tail;
+ sockent_t *socklist_tail;
struct addrinfo ai_hints;
struct addrinfo *ai_list, *ai_ptr;
struct addrinfo ai_hints;
struct addrinfo *ai_list, *ai_ptr;
int num_added = 0;
int num_added = 0;
- socklist_tail = listen_socks_head;
+ DBG ("node = %s, service = %s", node, service);
+
+ if (operating_mode == MODE_LOCAL || operating_mode == MODE_LOG)
+ {
+ syslog (LOG_WARNING, "network_create_socket: There is no point opening a socket when you are in mode `%s'.",
+ operating_mode == MODE_LOCAL ? "Local" : "Log");
+ return (-1);
+ }
+
+ socklist_tail = socklist_head;
while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
socklist_tail = socklist_tail->next;
memset (&ai_hints, '\0', sizeof (ai_hints));
while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
socklist_tail = socklist_tail->next;
memset (&ai_hints, '\0', sizeof (ai_hints));
- ai_hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
- ai_hints.ai_family = AF_UNSPEC;
+ ai_hints.ai_flags = 0;
+#ifdef AI_PASSIVE
+ ai_hints.ai_flags |= AI_PASSIVE;
+#endif
+#ifdef AI_ADDRCONFIG
+ ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+ ai_hints.ai_family = PF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = IPPROTO_UDP;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = IPPROTO_UDP;
for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
{
for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
{
- socklist_t *socklist_ent;
+ sockent_t *se;
- if ((socklist_ent = (socklist_t *) malloc (sizeof (socklist_t))) == NULL)
+ if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
{
syslog (LOG_EMERG, "malloc: %s", strerror (errno));
continue;
}
{
syslog (LOG_EMERG, "malloc: %s", strerror (errno));
continue;
}
- socklist_ent->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
- socklist_ent->next = NULL;
-
- if (socklist_ent->fd == -1)
+ if ((se->addr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage))) == NULL)
{
{
- syslog (LOG_ERR, "socket: %s", strerror (errno));
- free (socklist_ent);
+ syslog (LOG_EMERG, "malloc: %s", strerror (errno));
+ free (se);
continue;
}
continue;
}
- if (bind (socklist_ent->fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen) == -1)
+ assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
+ memset (se->addr, '\0', sizeof (struct sockaddr_storage));
+ memcpy (se->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+ se->addrlen = ai_ptr->ai_addrlen;
+
+ se->mode = operating_mode;
+ se->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+ se->next = NULL;
+
+ if (se->fd == -1)
{
{
- syslog (LOG_ERR, "bind: %s", strerror (errno));
- close (socklist_ent->fd);
- free (socklist_ent);
+ syslog (LOG_ERR, "socket: %s", strerror (errno));
+ free (se->addr);
+ free (se);
continue;
}
continue;
}
- if (ai_ptr->ai_family == AF_INET)
+ if (operating_mode == MODE_SERVER)
{
{
- struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
- if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
+ if (network_bind_socket (se, ai_ptr) != 0)
{
{
- struct ip_mreq mreq;
-
- mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
- mreq.imr_interface.s_addr = htonl (INADDR_ANY);
-
- if (setsockopt (socklist_ent->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- &mreq, sizeof (mreq)) == -1)
- {
- syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
- close (socklist_ent->fd);
- free (socklist_ent);
- continue;
- }
+ free (se->addr);
+ free (se);
+ continue;
}
}
}
}
- else if (ai_ptr->ai_family == AF_INET6)
+ else if (operating_mode == MODE_CLIENT)
{
{
- /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
- struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
- if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
- {
- struct ipv6_mreq mreq;
-
- memcpy (&mreq.ipv6mr_multiaddr,
- &addr->sin6_addr,
- sizeof (addr->sin6_addr));
-
- /* FIXME What do I need here? `netdevice(7)'
- * doesn't tell me either.. */
- mreq6.ipv6mr_interface = 0;
-
- if (setsockopt (socklist_ent->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
- &mreq, sizeof (mreq)) == -1)
- {
- syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
- close (socklist_ent->fd);
- free (socklist_ent);
- continue;
- }
- }
+ network_set_ttl (se, ai_ptr);
}
if (socklist_tail == NULL)
{
}
if (socklist_tail == NULL)
{
- listen_socks_head = socklist_tail = socklist_ent;
+ socklist_head = se;
+ socklist_tail = se;
}
else
{
}
else
{
- socklist_tail->next = socklist_ent;
- socklist_tail = socklist_ent;
+ socklist_tail->next = se;
+ socklist_tail = se;
}
}
+
num_added++;
num_added++;
+
+ /* We don't open more than one write-socket per node/service pair.. */
+ if (operating_mode == MODE_CLIENT)
+ break;
}
freeaddrinfo (ai_list);
}
freeaddrinfo (ai_list);
return (num_added);
}
return (num_added);
}
-int get_write_socket (void)
+static int network_connect_default (void)
{
{
- static int sd = -1;
+ int ret;
- if (sd != -1)
- return (sd);
+ if (socklist_head != NULL)
+ return (0);
- if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) == -1)
- {
- syslog (LOG_ERR, "socket: %s", strerror (errno));
- return (-1);
- }
+ DBG ("socklist_head is NULL");
- return (sd);
+ ret = 0;
+
+ if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
+ ret++;
+
+ /* Don't use IPv4 and IPv6 in parallel by default.. */
+ if ((operating_mode == MODE_CLIENT) && (ret != 0))
+ return (ret);
+
+ if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
+ ret++;
+
+ if (ret == 0)
+ ret = -1;
+
+ return (ret);
}
}
-char *addr_to_host (struct sockaddr_in *addr)
+static int network_get_listen_socket (void)
{
{
- char *host;
- struct hostent *he;
+ int fd;
+ int max_fd;
+ int status;
+
+ fd_set readfds;
+ sockent_t *se;
+
+ if (socklist_head == NULL)
+ network_connect_default ();
+
+ FD_ZERO (&readfds);
+ max_fd = -1;
+ for (se = socklist_head; se != NULL; se = se->next)
+ {
+ if (se->mode != operating_mode)
+ continue;
+
+ FD_SET (se->fd, &readfds);
+ if (se->fd >= max_fd)
+ max_fd = se->fd + 1;
+ }
+
+ if (max_fd == -1)
+ {
+ syslog (LOG_WARNING, "No listen sockets found!");
+ return (-1);
+ }
+
+ status = select (max_fd, &readfds, NULL, NULL, NULL);
- if ((he = gethostbyaddr ((char *) &addr->sin_addr, sizeof (addr->sin_addr), AF_INET)) != NULL)
+ if (status == -1)
{
{
- host = strdup (he->h_name);
+ if (errno != EINTR)
+ syslog (LOG_ERR, "select: %s", strerror (errno));
+ return (-1);
}
}
- else
+
+ fd = -1;
+ for (se = socklist_head; se != NULL; se = se->next)
{
{
- char *tmp = inet_ntoa (addr->sin_addr);
- host = strdup (tmp);
+ if (se->mode != operating_mode)
+ continue;
+
+ if (FD_ISSET (se->fd, &readfds))
+ {
+ fd = se->fd;
+ break;
+ }
}
}
- return (host);
+ if (fd == -1)
+ syslog (LOG_WARNING, "No socket ready..?");
+
+ DBG ("fd = %i", fd);
+ return (fd);
}
}
-int network_receive (char **host, char **type, char **instance, char **value)
+int network_receive (char **host, char **type, char **inst, char **value)
{
{
- int sd = get_read_socket ();
-
+ int fd;
char buffer[BUFF_SIZE];
char buffer[BUFF_SIZE];
- struct sockaddr_in addr;
- socklen_t addr_size;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int status;
char *fields[4];
char *fields[4];
- *host = NULL;
- *type = NULL;
- *instance = NULL;
- *value = NULL;
+ assert (operating_mode == MODE_SERVER);
- if (sd == -1)
- return (-1);
+ *host = NULL;
+ *type = NULL;
+ *inst = NULL;
+ *value = NULL;
- addr_size = sizeof (addr);
+ if ((fd = network_get_listen_socket ()) < 0)
+ return (-1);
- if (recvfrom (sd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addr_size) == -1)
+ addrlen = sizeof (addr);
+ if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
{
syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
return (-1);
}
{
syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
return (-1);
}
+ if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
+ {
+ syslog (LOG_EMERG, "malloc: %s", strerror (errno));
+ return (-1);
+ }
+
+ status = getnameinfo ((struct sockaddr *) &addr, addrlen,
+ *host, BUFF_SIZE, NULL, 0, 0);
+ if (status != 0)
+ {
+ free (*host); *host = NULL;
+ syslog (LOG_ERR, "getnameinfo: %s",
+ status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
+ return (-1);
+ }
+
if (strsplit (buffer, fields, 4) != 3)
if (strsplit (buffer, fields, 4) != 3)
+ {
+ syslog (LOG_WARNING, "Invalid message from `%s'", *host);
+ free (*host); *host = NULL;
+ return (-1);
+ }
+
+ if ((*type = strdup (fields[0])) == NULL)
+ {
+ syslog (LOG_EMERG, "strdup: %s", strerror (errno));
+ free (*host); *host = NULL;
return (-1);
return (-1);
+ }
- *host = addr_to_host (&addr);
- *type = strdup (fields[0]);
- *instance = strdup (fields[1]);
- *value = strdup (fields[2]);
+ if ((*inst = strdup (fields[1])) == NULL)
+ {
+ syslog (LOG_EMERG, "strdup: %s", strerror (errno));
+ free (*host); *host = NULL;
+ free (*type); *type = NULL;
+ return (-1);
+ }
- if (*host == NULL || *type == NULL || *instance == NULL || *value == NULL)
+ if ((*value = strdup (fields[2])) == NULL)
+ {
+ syslog (LOG_EMERG, "strdup: %s", strerror (errno));
+ free (*host); *host = NULL;
+ free (*type); *type = NULL;
+ free (*inst); *inst = NULL;
return (-1);
return (-1);
+ }
+
+ DBG ("host = %s, type = %s, inst = %s, value = %s",
+ *host, *type, *inst, *value);
return (0);
}
return (0);
}
-int network_send (char *type, char *instance, char *value)
+int network_send (char *type, char *inst, char *value)
{
{
- int sd = get_write_socket ();
- struct sockaddr_in addr;
-
char buf[BUFF_SIZE];
int buflen;
char buf[BUFF_SIZE];
int buflen;
- if (sd == -1)
- return (-1);
+ sockent_t *se;
- if ((buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, instance, value)) >= BUFF_SIZE)
+ int ret;
+ int status;
+
+ DBG ("type = %s, inst = %s, value = %s", type, inst, value);
+
+ assert (operating_mode == MODE_CLIENT);
+
+ buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value);
+ if ((buflen >= BUFF_SIZE) || (buflen < 1))
{
{
- syslog (LOG_WARNING, "network_send: Output truncated..");
+ syslog (LOG_WARNING, "network_send: snprintf failed..");
return (-1);
}
return (-1);
}
- buf[buflen++] = '\0';
+ buf[buflen] = '\0';
+ buflen++;
+
+ if (socklist_head == NULL)
+ network_connect_default ();
+
+ ret = 0;
+ for (se = socklist_head; se != NULL; se = se->next)
+ {
+ if (se->mode != operating_mode)
+ continue;
+
+ while (1)
+ {
+ status = sendto (se->fd, buf, buflen, 0,
+ (struct sockaddr *) se->addr, se->addrlen);
+
+ if (status == -1)
+ {
+ if (errno == EINTR)
+ {
+ DBG ("sendto was interrupted");
+ continue;
+ }
+ else
+ {
+ syslog (LOG_ERR, "sendto: %s", strerror (errno));
+ ret = -1;
+ break;
+ }
+ }
+ else if (ret >= 0)
+ ret++;
+ break;
+ }
+ }
- memset(&addr, '\0', sizeof (addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr (IPV4_MCAST_GROUP);
- addr.sin_port = get_port ();
+ if (ret == 0)
+ syslog (LOG_WARNING, "Message wasn't sent to anybody..");
- return (sendto (sd, buf, buflen, 0, (struct sockaddr *) &addr, sizeof (addr)));
+ return (ret);
}
}