Code

Merge branch 'collectd-3.11'
[collectd.git] / src / network.c
1 /**
2  * collectd - src/network.c
3  * Copyright (C) 2005,2006  Florian octo Forster
4  *
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.
9  *
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.
14  *
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
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  **/
23 #if HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netdb.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <syslog.h>
36 #include <errno.h>
38 #include "network.h"
39 #include "common.h"
40 #include "configfile.h"
41 #include "utils_debug.h"
43 /* 1500 - 40 - 8  =  Ethernet packet - IPv6 header - UDP header */
44 /* #define BUFF_SIZE 1452 */
46 #ifndef IPV6_ADD_MEMBERSHIP
47 # ifdef IPV6_JOIN_GROUP
48 #  define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
49 # else
50 #  error "Neither IP_ADD_MEMBERSHIP nor IPV6_JOIN_GROUP is defined"
51 # endif
52 #endif /* !IP_ADD_MEMBERSHIP */
54 #define BUFF_SIZE 4096
56 extern int operating_mode;
58 typedef struct sockent
59 {
60         int                      fd;
61         int                      mode;
62         struct sockaddr_storage *addr;
63         socklen_t                addrlen;
64         struct sockent          *next;
65 } sockent_t;
67 static sockent_t *socklist_head = NULL;
69 static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
70 {
71         char *ttl_str;
72         int   ttl_int;
74         ttl_str = cf_get_option ("TimeToLive", NULL);
75         if (ttl_str == NULL)
76                 return (-1);
78         ttl_int = atoi (ttl_str);
79         if ((ttl_int < 1) || (ttl_int > 255))
80         {
81                 syslog (LOG_WARNING, "A TTL value of %i is invalid.", ttl_int);
82                 return (-1);
83         }
85         DBG ("ttl = %i", ttl_int);
87         if (ai->ai_family == AF_INET)
88         {
89                 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
90                 int optname;
92                 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
93                         optname = IP_MULTICAST_TTL;
94                 else
95                         optname = IP_TTL;
97                 if (setsockopt (se->fd, IPPROTO_IP, optname,
98                                         &ttl_int, sizeof (ttl_int)) == -1)
99                 {
100                         syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
101                         return (-1);
102                 }
103         }
104         else if (ai->ai_family == AF_INET6)
105         {
106                 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
107                 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
108                 int optname;
110                 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
111                         optname = IPV6_MULTICAST_HOPS;
112                 else
113                         optname = IPV6_UNICAST_HOPS;
115                 if (setsockopt (se->fd, IPPROTO_IPV6, optname,
116                                         &ttl_int, sizeof (ttl_int)) == -1)
117                 {
118                         syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
119                         return (-1);
120                 }
121         }
123         return (0);
126 static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
128         int loop = 1;
130         DBG ("fd = %i; calling `bind'", se->fd);
132         if (bind (se->fd, ai->ai_addr, ai->ai_addrlen) == -1)
133         {
134                 syslog (LOG_ERR, "bind: %s", strerror (errno));
135                 return (-1);
136         }
138         if (ai->ai_family == AF_INET)
139         {
140                 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
141                 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
142                 {
143                         struct ip_mreq mreq;
145                         DBG ("fd = %i; IPv4 multicast address found", se->fd);
147                         mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
148                         mreq.imr_interface.s_addr = htonl (INADDR_ANY);
150                         if (setsockopt (se->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
151                                                 &loop, sizeof (loop)) == -1)
152                         {
153                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
154                                 return (-1);
155                         }
157                         if (setsockopt (se->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
158                                                 &mreq, sizeof (mreq)) == -1)
159                         {
160                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
161                                 return (-1);
162                         }
163                 }
164         }
165         else if (ai->ai_family == AF_INET6)
166         {
167                 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
168                 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
169                 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
170                 {
171                         struct ipv6_mreq mreq;
173                         DBG ("fd = %i; IPv6 multicast address found", se->fd);
175                         memcpy (&mreq.ipv6mr_multiaddr,
176                                         &addr->sin6_addr,
177                                         sizeof (addr->sin6_addr));
179                         /* http://developer.apple.com/documentation/Darwin/Reference/ManPages/man4/ip6.4.html
180                          * ipv6mr_interface may be set to zeroes to
181                          * choose the default multicast interface or to
182                          * the index of a particular multicast-capable
183                          * interface if the host is multihomed.
184                          * Membership is associ-associated with a
185                          * single interface; programs running on
186                          * multihomed hosts may need to join the same
187                          * group on more than one interface.*/
188                         mreq.ipv6mr_interface = 0;
190                         if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
191                                                 &loop, sizeof (loop)) == -1)
192                         {
193                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
194                                 return (-1);
195                         }
197                         if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
198                                                 &mreq, sizeof (mreq)) == -1)
199                         {
200                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
201                                 return (-1);
202                         }
203                 }
204         }
206         return (0);
209 int network_create_socket (const char *node, const char *service)
211         sockent_t *socklist_tail;
213         struct addrinfo  ai_hints;
214         struct addrinfo *ai_list, *ai_ptr;
215         int              ai_return;
217         int num_added = 0;
219         DBG ("node = %s, service = %s", node, service);
221         if (operating_mode == MODE_LOCAL || operating_mode == MODE_LOG)
222         {
223                 syslog (LOG_WARNING, "network_create_socket: There is no point opening a socket when you are in mode `%s'.",
224                                 operating_mode == MODE_LOCAL ? "Local" : "Log");
225                 return (-1);
226         }
228         socklist_tail = socklist_head;
229         while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
230                 socklist_tail = socklist_tail->next;
232         memset (&ai_hints, '\0', sizeof (ai_hints));
233         ai_hints.ai_flags    = 0;
234 #ifdef AI_PASSIVE
235         ai_hints.ai_flags |= AI_PASSIVE;
236 #endif
237 #ifdef AI_ADDRCONFIG
238         ai_hints.ai_flags |= AI_ADDRCONFIG;
239 #endif
240         ai_hints.ai_family   = PF_UNSPEC;
241         ai_hints.ai_socktype = SOCK_DGRAM;
242         ai_hints.ai_protocol = IPPROTO_UDP;
244         if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
245         {
246                 syslog (LOG_ERR, "getaddrinfo (%s, %s): %s",
247                                 node == NULL ? "(null)" : node,
248                                 service == NULL ? "(null)" : service,
249                                 ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
250                 return (-1);
251         }
253         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
254         {
255                 sockent_t *se;
257                 if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
258                 {
259                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
260                         continue;
261                 }
263                 if ((se->addr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage))) == NULL)
264                 {
265                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
266                         free (se);
267                         continue;
268                 }
270                 assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
271                 memset (se->addr, '\0', sizeof (struct sockaddr_storage));
272                 memcpy (se->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
273                 se->addrlen = ai_ptr->ai_addrlen;
275                 se->mode = operating_mode;
276                 se->fd   = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
277                 se->next = NULL;
279                 if (se->fd == -1)
280                 {
281                         syslog (LOG_ERR, "socket: %s", strerror (errno));
282                         free (se->addr);
283                         free (se);
284                         continue;
285                 }
287                 if (operating_mode == MODE_SERVER)
288                 {
289                         if (network_bind_socket (se, ai_ptr) != 0)
290                         {
291                                 close (se->fd);
292                                 free (se->addr);
293                                 free (se);
294                                 continue;
295                         }
296                 }
297                 else if (operating_mode == MODE_CLIENT)
298                 {
299                         network_set_ttl (se, ai_ptr);
300                 }
302                 if (socklist_tail == NULL)
303                 {
304                         socklist_head = se;
305                         socklist_tail = se;
306                 }
307                 else
308                 {
309                         socklist_tail->next = se;
310                         socklist_tail = se;
311                 }
313                 num_added++;
315                 /* We don't open more than one write-socket per node/service pair.. */
316                 if (operating_mode == MODE_CLIENT)
317                         break;
318         }
320         freeaddrinfo (ai_list);
322         return (num_added);
325 static int network_connect_default (void)
327         int ret;
329         if (socklist_head != NULL)
330                 return (0);
332         DBG ("socklist_head is NULL");
334         ret = 0;
336         if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
337                 ret++;
339         /* Don't use IPv4 and IPv6 in parallel by default.. */
340         if ((operating_mode == MODE_CLIENT) && (ret != 0))
341                 return (ret);
343         if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
344                 ret++;
346         if (ret == 0)
347                 ret = -1;
349         return (ret);
352 static int network_get_listen_socket (void)
354         int fd;
355         int max_fd;
356         int status;
358         fd_set readfds;
359         sockent_t *se;
361         if (socklist_head == NULL)
362                 network_connect_default ();
364         FD_ZERO (&readfds);
365         max_fd = -1;
366         for (se = socklist_head; se != NULL; se = se->next)
367         {
368                 if (se->mode != operating_mode)
369                         continue;
371                 FD_SET (se->fd, &readfds);
372                 if (se->fd >= max_fd)
373                         max_fd = se->fd + 1;
374         }
376         if (max_fd == -1)
377         {
378                 syslog (LOG_WARNING, "No listen sockets found!");
379                 return (-1);
380         }
382         status = select (max_fd, &readfds, NULL, NULL, NULL);
384         if (status == -1)
385         {
386                 if (errno != EINTR)
387                         syslog (LOG_ERR, "select: %s", strerror (errno));
388                 return (-1);
389         }
391         fd = -1;
392         for (se = socklist_head; se != NULL; se = se->next)
393         {
394                 if (se->mode != operating_mode)
395                         continue;
397                 if (FD_ISSET (se->fd, &readfds))
398                 {
399                         fd = se->fd;
400                         break;
401                 }
402         }
404         if (fd == -1)
405                 syslog (LOG_WARNING, "No socket ready..?");
407         DBG ("fd = %i", fd);
408         return (fd);
411 int network_receive (char **host, char **type, char **inst, char **value)
413         int fd;
414         char buffer[BUFF_SIZE];
416         struct sockaddr_storage addr;
417         socklen_t               addrlen;
418         int status;
420         char *fields[4];
422         assert (operating_mode == MODE_SERVER);
424         *host  = NULL;
425         *type  = NULL;
426         *inst  = NULL;
427         *value = NULL;
429         if ((fd = network_get_listen_socket ()) < 0)
430                 return (-1);
432         addrlen = sizeof (addr);
433         if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
434         {
435                 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
436                 return (-1);
437         }
439         if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
440         {
441                 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
442                 return (-1);
443         }
445         status = getnameinfo ((struct sockaddr *) &addr, addrlen,
446                         *host, BUFF_SIZE, NULL, 0, 0);
447         if (status != 0)
448         {
449                 free (*host); *host = NULL;
450                 syslog (LOG_ERR, "getnameinfo: %s",
451                                 status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
452                 return (-1);
453         }
455         if (strsplit (buffer, fields, 4) != 3)
456         {
457                 syslog (LOG_WARNING, "Invalid message from `%s'", *host);
458                 free (*host); *host = NULL;
459                 return (1);
460         }
462         if ((*type = strdup (fields[0])) == NULL)
463         {
464                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
465                 free (*host); *host = NULL;
466                 return (-1);
467         }
469         if ((*inst = strdup (fields[1])) == NULL)
470         {
471                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
472                 free (*host); *host = NULL;
473                 free (*type); *type = NULL;
474                 return (-1);
475         }
477         if ((*value = strdup (fields[2])) == NULL)
478         {
479                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
480                 free (*host); *host = NULL;
481                 free (*type); *type = NULL;
482                 free (*inst); *inst = NULL;
483                 return (-1);
484         }
486         DBG ("host = %s, type = %s, inst = %s, value = %s",
487                         *host, *type, *inst, *value);
489         return (0);
492 int network_send (char *type, char *inst, char *value)
494         char buf[BUFF_SIZE];
495         int buflen;
497         sockent_t *se;
499         int ret;
500         int status;
502         DBG ("type = %s, inst = %s, value = %s", type, inst, value);
504         assert (operating_mode == MODE_CLIENT);
506         buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value);
507         if ((buflen >= BUFF_SIZE) || (buflen < 1))
508         {
509                 syslog (LOG_WARNING, "network_send: snprintf failed..");
510                 return (-1);
511         }
512         buf[buflen] = '\0';
513         buflen++;
515         if (socklist_head == NULL)
516                 network_connect_default ();
518         ret = 0;
519         for (se = socklist_head; se != NULL; se = se->next)
520         {
521                 if (se->mode != operating_mode)
522                         continue;
524                 while (1)
525                 {
526                         status = sendto (se->fd, buf, buflen, 0,
527                                         (struct sockaddr *) se->addr, se->addrlen);
529                         if (status == -1)
530                         {
531                                 if (errno == EINTR)
532                                 {
533                                         DBG ("sendto was interrupted");
534                                         continue;
535                                 }
536                                 else
537                                 {
538                                         syslog (LOG_ERR, "sendto: %s", strerror (errno));
539                                         ret = -1;
540                                         break;
541                                 }
542                         }
543                         else if (ret >= 0)
544                                 ret++;
545                         break;
546                 }
547         }
549         if (ret == 0)
550                 syslog (LOG_WARNING, "Message wasn't sent to anybody..");
552         return (ret);