/**
* 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
* Florian octo Forster <octo at verplant.org>
**/
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <errno.h>
-#include <assert.h>
#include "network.h"
#include "common.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
- */
-
-#define IPV4_MCAST_GROUP "239.192.74.66"
-#define UDP_PORT 25826
+#include "configfile.h"
+#include "utils_debug.h"
/* 1500 - 40 - 8 = Ethernet packet - IPv6 header - UDP header */
-#define BUFF_SIZE 1452
+/* #define BUFF_SIZE 1452 */
+
+#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 */
#define BUFF_SIZE 4096
-#ifdef HAVE_LIBRRD
extern int operating_mode;
-#else
-static int operating_mode = MODE_CLIENT;
-#endif
typedef struct sockent
{
int fd;
+ int mode;
struct sockaddr_storage *addr;
socklen_t addrlen;
struct sockent *next;
static sockent_t *socklist_head = NULL;
-static int network_bind_socket (int fd, const struct addrinfo *ai, const sockent_t *se)
+static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
+{
+ char *ttl_str;
+ int ttl_int;
+
+ ttl_str = cf_get_option ("TimeToLive", NULL);
+ if (ttl_str == NULL)
+ return (-1);
+
+ 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);
+ }
+
+ DBG ("ttl = %i", ttl_int);
+
+ if (ai->ai_family == AF_INET)
+ {
+ struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
+ int optname;
+
+ 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;
- if (bind (fd, ai->ai_addr, ai->ai_addrlen) == -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);
{
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);
{
struct ipv6_mreq mreq;
+ DBG ("fd = %i; IPv6 multicast address found", se->fd);
+
memcpy (&mreq.ipv6mr_multiaddr,
&addr->sin6_addr,
sizeof (addr->sin6_addr));
* single interface; programs running on
* multihomed hosts may need to join the same
* group on more than one interface.*/
- mreq6.ipv6mr_interface = 0;
+ mreq.ipv6mr_interface = 0;
if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
&loop, sizeof (loop)) == -1)
DBG ("node = %s, service = %s", node, service);
- if (operating_mode == MODE_LOCAL)
+ 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));
- ai_hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ 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; /* XXX is this right here?!? */
+ ai_hints.ai_protocol = IPPROTO_UDP;
if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
{
continue;
}
- assert (sizeof (struct sockaddr_storage) >= ai_ptr->addrlen);
+ 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->addrlen);
- se->addrlen = ai_ptr->addrlen;
+ 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 (operating_mode == MODE_SERVER)
- if (network_bind_socket (se->fd, ai_ptr, se->addr) != 0)
+ {
+ if (network_bind_socket (se, ai_ptr) != 0)
{
+ close (se->fd);
free (se->addr);
free (se);
continue;
}
+ }
+ else if (operating_mode == MODE_CLIENT)
+ {
+ network_set_ttl (se, ai_ptr);
+ }
if (socklist_tail == NULL)
{
- socklist_head = socklist_tail = se;
+ socklist_head = se;
+ socklist_tail = se;
}
else
{
return (num_added);
}
+static int network_connect_default (void)
+{
+ int ret;
+
+ if (socklist_head != NULL)
+ return (0);
+
+ DBG ("socklist_head is NULL");
+
+ 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);
+}
+
static int network_get_listen_socket (void)
{
- int fd;
- int max_fd;
+ int fd;
+ int max_fd;
+ int status;
fd_set readfds;
sockent_t *se;
- while (1)
+ if (socklist_head == NULL)
+ network_connect_default ();
+
+ FD_ZERO (&readfds);
+ max_fd = -1;
+ for (se = socklist_head; se != NULL; se = se->next)
{
- FD_ZERO (&readfds);
- max_fd = -1;
- for (se = socklist_head; se != NULL; se = se->next)
- {
- FD_SET (se->fd, &readfds);
- if (se->fd >= max_fd)
- max_fd = se->fd + 1;
- }
+ if (se->mode != operating_mode)
+ continue;
- if (max_fd == -1)
- {
- syslog (LOG_WARNING, "No listen sockets found!");
- return (-1);
- }
+ FD_SET (se->fd, &readfds);
+ if (se->fd >= max_fd)
+ max_fd = se->fd + 1;
+ }
- status = select (max_fd, &readfds, NULL, NULL, NULL);
+ if (max_fd == -1)
+ {
+ syslog (LOG_WARNING, "No listen sockets found!");
+ return (-1);
+ }
- if ((status == -1) && (errno == EINTR))
- continue;
- else if (status == -1)
- {
+ status = select (max_fd, &readfds, NULL, NULL, NULL);
+
+ if (status == -1)
+ {
+ if (errno != EINTR)
syslog (LOG_ERR, "select: %s", strerror (errno));
- return (-1);
- }
- else
- break;
- } /* while (true) */
+ return (-1);
+ }
fd = -1;
for (se = socklist_head; se != NULL; se = se->next)
+ {
+ if (se->mode != operating_mode)
+ continue;
+
if (FD_ISSET (se->fd, &readfds))
{
fd = se->fd;
break;
}
+ }
if (fd == -1)
syslog (LOG_WARNING, "No socket ready..?");
char buffer[BUFF_SIZE];
struct sockaddr_storage addr;
+ socklen_t addrlen;
int status;
char *fields[4];
if ((fd = network_get_listen_socket ()) < 0)
return (-1);
- if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, sizeof (addr)) == -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);
return (-1);
}
- status = getnameinfo ((struct sockaddr *) &addr, sizeof (addr),
+ status = getnameinfo ((struct sockaddr *) &addr, addrlen,
*host, BUFF_SIZE, NULL, 0, 0);
if (status != 0)
{
{
syslog (LOG_WARNING, "Invalid message from `%s'", *host);
free (*host); *host = NULL;
- return (-1);
+ return (1);
}
if ((*type = strdup (fields[0])) == NULL)
{
- syslog (LOG_EMERG, "strdup: %s", strerror ());
+ syslog (LOG_EMERG, "strdup: %s", strerror (errno));
free (*host); *host = NULL;
return (-1);
}
if ((*inst = strdup (fields[1])) == NULL)
{
- syslog (LOG_EMERG, "strdup: %s", strerror ());
+ syslog (LOG_EMERG, "strdup: %s", strerror (errno));
free (*host); *host = NULL;
free (*type); *type = NULL;
return (-1);
if ((*value = strdup (fields[2])) == NULL)
{
- syslog (LOG_EMERG, "strdup: %s", strerror ());
+ syslog (LOG_EMERG, "strdup: %s", strerror (errno));
free (*host); *host = NULL;
free (*type); *type = NULL;
free (*inst); *inst = NULL;
buf[buflen] = '\0';
buflen++;
+ if (socklist_head == NULL)
+ network_connect_default ();
+
ret = 0;
for (se = socklist_head; se != NULL; se = se->next)
{
- DBG ("fd = %i", se->fd);
+ if (se->mode != operating_mode)
+ continue;
while (1)
{
else
{
syslog (LOG_ERR, "sendto: %s", strerror (errno));
+ ret = -1;
break;
}
}