X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fnetwork.c;h=22e911a87b8e4d4fe89d805abc9c9559ce2463c6;hb=44d73d6556833bcfbc4678a01731aafee95c3caf;hp=914235aaaba09a0b501c42db53839fb100e1da3c;hpb=75af7e101561fecd3041dc2b4e8dec1dfdaf4c47;p=collectd.git diff --git a/src/network.c b/src/network.c index 914235aa..22e911a8 100644 --- a/src/network.c +++ b/src/network.c @@ -1,6 +1,6 @@ /** * 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 @@ -23,9 +23,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -33,55 +33,178 @@ #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; @@ -89,13 +212,28 @@ int network_create_listen_socket (const char *node, const char *service) 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)); - 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; @@ -110,89 +248,68 @@ int network_create_listen_socket (const char *node, const char *service) 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; } - 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; } - 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; } - 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) { - listen_socks_head = socklist_tail = socklist_ent; + socklist_head = se; + socklist_tail = se; } else { - socklist_tail->next = socklist_ent; - socklist_tail = socklist_ent; + socklist_tail->next = se; + socklist_tail = se; } + num_added++; + + /* We don't open more than one write-socket per node/service pair.. */ + if (operating_mode == MODE_CLIENT) + break; } freeaddrinfo (ai_list); @@ -200,103 +317,232 @@ int network_create_listen_socket (const char *node, const char *service) 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]; - struct sockaddr_in addr; - socklen_t addr_size; + struct sockaddr_storage addr; + socklen_t addrlen; + int status; 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); } + 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) + { + 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); + } - *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); + } + + DBG ("host = %s, type = %s, inst = %s, value = %s", + *host, *type, *inst, *value); 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; - 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); } - 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); }