2 * collectd - src/network.c
3 * Copyright (C) 2006 Florian octo Forster
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * Florian octo Forster <octo at verplant.org>
26 #include <sys/types.h>
27 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
36 #include "configfile.h"
37 #include "utils_debug.h"
39 /* 1500 - 40 - 8 = Ethernet packet - IPv6 header - UDP header */
40 /* #define BUFF_SIZE 1452 */
42 #define BUFF_SIZE 4096
45 extern int operating_mode;
47 static int operating_mode = MODE_CLIENT;
50 typedef struct sockent
54 struct sockaddr_storage *addr;
59 static sockent_t *socklist_head = NULL;
61 static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
68 ttl_str = cf_get_option ("MulticastTTL", NULL);
71 ttl_int = atoi (ttl_str);
72 if ((ttl_int < 1) || (ttl_int > 255))
73 ttl_int = NET_DEFAULT_MC_TTL;
75 DBG ("fd = %i; calling `bind'", se->fd);
77 if (bind (se->fd, ai->ai_addr, ai->ai_addrlen) == -1)
79 syslog (LOG_ERR, "bind: %s", strerror (errno));
83 if (ai->ai_family == AF_INET)
85 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
86 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
90 DBG ("fd = %i; IPv4 multicast address found", se->fd);
92 mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
93 mreq.imr_interface.s_addr = htonl (INADDR_ANY);
95 if (setsockopt (se->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
96 &loop, sizeof (loop)) == -1)
98 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
102 /* IP_MULTICAST_TTL */
103 if (setsockopt (se->fd, IPPROTO_IP, IP_MULTICAST_TTL,
104 &ttl_int, sizeof (ttl_int)) == -1)
106 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
110 if (setsockopt (se->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
111 &mreq, sizeof (mreq)) == -1)
113 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
118 else if (ai->ai_family == AF_INET6)
120 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
121 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
122 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
124 struct ipv6_mreq mreq;
126 DBG ("fd = %i; IPv6 multicast address found", se->fd);
128 memcpy (&mreq.ipv6mr_multiaddr,
130 sizeof (addr->sin6_addr));
132 /* http://developer.apple.com/documentation/Darwin/Reference/ManPages/man4/ip6.4.html
133 * ipv6mr_interface may be set to zeroes to
134 * choose the default multicast interface or to
135 * the index of a particular multicast-capable
136 * interface if the host is multihomed.
137 * Membership is associ-associated with a
138 * single interface; programs running on
139 * multihomed hosts may need to join the same
140 * group on more than one interface.*/
141 mreq.ipv6mr_interface = 0;
143 if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
144 &loop, sizeof (loop)) == -1)
146 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
150 if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
151 &ttl_int, sizeof (ttl_int)) == -1)
153 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
157 if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
158 &mreq, sizeof (mreq)) == -1)
160 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
169 int network_create_socket (const char *node, const char *service)
171 sockent_t *socklist_tail;
173 struct addrinfo ai_hints;
174 struct addrinfo *ai_list, *ai_ptr;
179 DBG ("node = %s, service = %s", node, service);
181 if (operating_mode == MODE_LOCAL)
184 socklist_tail = socklist_head;
185 while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
186 socklist_tail = socklist_tail->next;
188 memset (&ai_hints, '\0', sizeof (ai_hints));
189 ai_hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
190 ai_hints.ai_family = PF_UNSPEC;
191 ai_hints.ai_socktype = SOCK_DGRAM;
192 ai_hints.ai_protocol = IPPROTO_UDP;
194 if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
196 syslog (LOG_ERR, "getaddrinfo (%s, %s): %s",
197 node == NULL ? "(null)" : node,
198 service == NULL ? "(null)" : service,
199 ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
203 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
207 if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
209 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
213 if ((se->addr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage))) == NULL)
215 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
220 assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
221 memset (se->addr, '\0', sizeof (struct sockaddr_storage));
222 memcpy (se->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
223 se->addrlen = ai_ptr->ai_addrlen;
225 se->mode = operating_mode;
226 se->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
231 syslog (LOG_ERR, "socket: %s", strerror (errno));
237 if (operating_mode == MODE_SERVER)
238 if (network_bind_socket (se, ai_ptr) != 0)
245 if (socklist_tail == NULL)
252 socklist_tail->next = se;
258 /* We don't open more than one write-socket per node/service pair.. */
259 if (operating_mode == MODE_CLIENT)
263 freeaddrinfo (ai_list);
268 static int network_connect_default (void)
272 if (socklist_head != NULL)
275 DBG ("socklist_head is NULL");
279 if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
282 /* Don't use IPv4 and IPv6 in parallel by default.. */
283 if ((operating_mode == MODE_CLIENT) && (ret != 0))
286 if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
295 static int network_get_listen_socket (void)
304 if (socklist_head == NULL)
305 network_connect_default ();
309 for (se = socklist_head; se != NULL; se = se->next)
311 if (se->mode != operating_mode)
314 FD_SET (se->fd, &readfds);
315 if (se->fd >= max_fd)
321 syslog (LOG_WARNING, "No listen sockets found!");
325 status = select (max_fd, &readfds, NULL, NULL, NULL);
330 syslog (LOG_ERR, "select: %s", strerror (errno));
335 for (se = socklist_head; se != NULL; se = se->next)
337 if (se->mode != operating_mode)
340 if (FD_ISSET (se->fd, &readfds))
348 syslog (LOG_WARNING, "No socket ready..?");
354 int network_receive (char **host, char **type, char **inst, char **value)
357 char buffer[BUFF_SIZE];
359 struct sockaddr_storage addr;
365 assert (operating_mode == MODE_SERVER);
372 if ((fd = network_get_listen_socket ()) < 0)
375 addrlen = sizeof (addr);
376 if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
378 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
382 if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
384 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
388 status = getnameinfo ((struct sockaddr *) &addr, addrlen,
389 *host, BUFF_SIZE, NULL, 0, 0);
392 free (*host); *host = NULL;
393 syslog (LOG_ERR, "getnameinfo: %s",
394 status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
398 if (strsplit (buffer, fields, 4) != 3)
400 syslog (LOG_WARNING, "Invalid message from `%s'", *host);
401 free (*host); *host = NULL;
405 if ((*type = strdup (fields[0])) == NULL)
407 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
408 free (*host); *host = NULL;
412 if ((*inst = strdup (fields[1])) == NULL)
414 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
415 free (*host); *host = NULL;
416 free (*type); *type = NULL;
420 if ((*value = strdup (fields[2])) == NULL)
422 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
423 free (*host); *host = NULL;
424 free (*type); *type = NULL;
425 free (*inst); *inst = NULL;
429 DBG ("host = %s, type = %s, inst = %s, value = %s",
430 *host, *type, *inst, *value);
435 int network_send (char *type, char *inst, char *value)
445 DBG ("type = %s, inst = %s, value = %s", type, inst, value);
447 assert (operating_mode == MODE_CLIENT);
449 buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value);
450 if ((buflen >= BUFF_SIZE) || (buflen < 1))
452 syslog (LOG_WARNING, "network_send: snprintf failed..");
458 if (socklist_head == NULL)
459 network_connect_default ();
462 for (se = socklist_head; se != NULL; se = se->next)
464 if (se->mode != operating_mode)
467 DBG ("fd = %i", se->fd);
471 status = sendto (se->fd, buf, buflen, 0,
472 (struct sockaddr *) se->addr, se->addrlen);
478 DBG ("sendto was interrupted");
483 syslog (LOG_ERR, "sendto: %s", strerror (errno));
495 syslog (LOG_WARNING, "Message wasn't sent to anybody..");