1ecb9d7467a91561d45371656be55dccc1cb7094
[collectd.git] / src / network.c
1 /**
2  * collectd - src/network.c
3  * Copyright (C) 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  **/
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netdb.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <syslog.h>
32 #include <errno.h>
33
34 #include "network.h"
35 #include "common.h"
36 #include "configfile.h"
37 #include "utils_debug.h"
38
39 /* 1500 - 40 - 8  =  Ethernet packet - IPv6 header - UDP header */
40 /* #define BUFF_SIZE 1452 */
41
42 #ifndef IPV6_ADD_MEMBERSHIP
43 # ifdef IPV6_JOIN_GROUP
44 #  define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
45 # else
46 #  error "Neither IP_ADD_MEMBERSHIP nor IPV6_JOIN_GROUP is defined"
47 # endif
48 #endif /* !IP_ADD_MEMBERSHIP */
49
50 #define BUFF_SIZE 4096
51
52 #ifdef HAVE_LIBRRD
53 extern int operating_mode;
54 #else
55 static int operating_mode = MODE_CLIENT;
56 #endif
57
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;
66
67 static sockent_t *socklist_head = NULL;
68
69 static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
70 {
71         char *ttl_str;
72         int   ttl_int;
73
74         ttl_str = cf_get_option ("TimeToLive", NULL);
75         if (ttl_str == NULL)
76                 return (-1);
77
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         }
84
85         DBG ("ttl = %i", ttl_int);
86
87         if (ai->ai_family == AF_INET)
88         {
89                 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
90                 int optname;
91
92                 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
93                         optname = IP_MULTICAST_TTL;
94                 else
95                         optname = IP_TTL;
96
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;
109
110                 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
111                         optname = IPV6_MULTICAST_HOPS;
112                 else
113                         optname = IPV6_UNICAST_HOPS;
114
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         }
122
123         return (0);
124 }
125
126 static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
127 {
128         int loop = 1;
129
130         DBG ("fd = %i; calling `bind'", se->fd);
131
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         }
137
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;
144
145                         DBG ("fd = %i; IPv4 multicast address found", se->fd);
146
147                         mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
148                         mreq.imr_interface.s_addr = htonl (INADDR_ANY);
149
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                         }
156
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;
172
173                         DBG ("fd = %i; IPv6 multicast address found", se->fd);
174
175                         memcpy (&mreq.ipv6mr_multiaddr,
176                                         &addr->sin6_addr,
177                                         sizeof (addr->sin6_addr));
178
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;
189
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                         }
196
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         }
205
206         return (0);
207 }
208
209 int network_create_socket (const char *node, const char *service)
210 {
211         sockent_t *socklist_tail;
212
213         struct addrinfo  ai_hints;
214         struct addrinfo *ai_list, *ai_ptr;
215         int              ai_return;
216
217         int num_added = 0;
218
219         DBG ("node = %s, service = %s", node, service);
220
221         if (operating_mode == MODE_LOCAL)
222                 return (-1);
223
224         socklist_tail = socklist_head;
225         while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
226                 socklist_tail = socklist_tail->next;
227
228         memset (&ai_hints, '\0', sizeof (ai_hints));
229         ai_hints.ai_flags    = 0;
230 #ifdef AI_PASSIVE
231         ai_hints.ai_flags |= AI_PASSIVE;
232 #endif
233 #ifdef AI_ADDRCONFIG
234         ai_hints.ai_flags |= AI_ADDRCONFIG;
235 #endif
236         ai_hints.ai_family   = PF_UNSPEC;
237         ai_hints.ai_socktype = SOCK_DGRAM;
238         ai_hints.ai_protocol = IPPROTO_UDP;
239
240         if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
241         {
242                 syslog (LOG_ERR, "getaddrinfo (%s, %s): %s",
243                                 node == NULL ? "(null)" : node,
244                                 service == NULL ? "(null)" : service,
245                                 ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
246                 return (-1);
247         }
248
249         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
250         {
251                 sockent_t *se;
252
253                 if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
254                 {
255                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
256                         continue;
257                 }
258
259                 if ((se->addr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage))) == NULL)
260                 {
261                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
262                         free (se);
263                         continue;
264                 }
265
266                 assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
267                 memset (se->addr, '\0', sizeof (struct sockaddr_storage));
268                 memcpy (se->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
269                 se->addrlen = ai_ptr->ai_addrlen;
270
271                 se->mode = operating_mode;
272                 se->fd   = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
273                 se->next = NULL;
274
275                 if (se->fd == -1)
276                 {
277                         syslog (LOG_ERR, "socket: %s", strerror (errno));
278                         free (se->addr);
279                         free (se);
280                         continue;
281                 }
282
283                 if (operating_mode == MODE_SERVER)
284                 {
285                         if (network_bind_socket (se, ai_ptr) != 0)
286                         {
287                                 free (se->addr);
288                                 free (se);
289                                 continue;
290                         }
291                 }
292                 else if (operating_mode == MODE_CLIENT)
293                 {
294                         network_set_ttl (se, ai_ptr);
295                 }
296
297                 if (socklist_tail == NULL)
298                 {
299                         socklist_head = se;
300                         socklist_tail = se;
301                 }
302                 else
303                 {
304                         socklist_tail->next = se;
305                         socklist_tail = se;
306                 }
307
308                 num_added++;
309
310                 /* We don't open more than one write-socket per node/service pair.. */
311                 if (operating_mode == MODE_CLIENT)
312                         break;
313         }
314
315         freeaddrinfo (ai_list);
316
317         return (num_added);
318 }
319
320 static int network_connect_default (void)
321 {
322         int ret;
323
324         if (socklist_head != NULL)
325                 return (0);
326
327         DBG ("socklist_head is NULL");
328
329         ret = 0;
330
331         if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
332                 ret++;
333
334         /* Don't use IPv4 and IPv6 in parallel by default.. */
335         if ((operating_mode == MODE_CLIENT) && (ret != 0))
336                 return (ret);
337
338         if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
339                 ret++;
340
341         if (ret == 0)
342                 ret = -1;
343
344         return (ret);
345 }
346
347 static int network_get_listen_socket (void)
348 {
349         int fd;
350         int max_fd;
351         int status;
352
353         fd_set readfds;
354         sockent_t *se;
355
356         if (socklist_head == NULL)
357                 network_connect_default ();
358
359         FD_ZERO (&readfds);
360         max_fd = -1;
361         for (se = socklist_head; se != NULL; se = se->next)
362         {
363                 if (se->mode != operating_mode)
364                         continue;
365
366                 FD_SET (se->fd, &readfds);
367                 if (se->fd >= max_fd)
368                         max_fd = se->fd + 1;
369         }
370
371         if (max_fd == -1)
372         {
373                 syslog (LOG_WARNING, "No listen sockets found!");
374                 return (-1);
375         }
376
377         status = select (max_fd, &readfds, NULL, NULL, NULL);
378
379         if (status == -1)
380         {
381                 if (errno != EINTR)
382                         syslog (LOG_ERR, "select: %s", strerror (errno));
383                 return (-1);
384         }
385
386         fd = -1;
387         for (se = socklist_head; se != NULL; se = se->next)
388         {
389                 if (se->mode != operating_mode)
390                         continue;
391
392                 if (FD_ISSET (se->fd, &readfds))
393                 {
394                         fd = se->fd;
395                         break;
396                 }
397         }
398
399         if (fd == -1)
400                 syslog (LOG_WARNING, "No socket ready..?");
401
402         DBG ("fd = %i", fd);
403         return (fd);
404 }
405
406 int network_receive (char **host, char **type, char **inst, char **value)
407 {
408         int fd;
409         char buffer[BUFF_SIZE];
410
411         struct sockaddr_storage addr;
412         socklen_t               addrlen;
413         int status;
414
415         char *fields[4];
416
417         assert (operating_mode == MODE_SERVER);
418
419         *host  = NULL;
420         *type  = NULL;
421         *inst  = NULL;
422         *value = NULL;
423
424         if ((fd = network_get_listen_socket ()) < 0)
425                 return (-1);
426
427         addrlen = sizeof (addr);
428         if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
429         {
430                 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
431                 return (-1);
432         }
433
434         if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
435         {
436                 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
437                 return (-1);
438         }
439
440         status = getnameinfo ((struct sockaddr *) &addr, addrlen,
441                         *host, BUFF_SIZE, NULL, 0, 0);
442         if (status != 0)
443         {
444                 free (*host); *host = NULL;
445                 syslog (LOG_ERR, "getnameinfo: %s",
446                                 status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
447                 return (-1);
448         }
449
450         if (strsplit (buffer, fields, 4) != 3)
451         {
452                 syslog (LOG_WARNING, "Invalid message from `%s'", *host);
453                 free (*host); *host = NULL;
454                 return (-1);
455         }
456
457         if ((*type = strdup (fields[0])) == NULL)
458         {
459                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
460                 free (*host); *host = NULL;
461                 return (-1);
462         }
463
464         if ((*inst = strdup (fields[1])) == NULL)
465         {
466                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
467                 free (*host); *host = NULL;
468                 free (*type); *type = NULL;
469                 return (-1);
470         }
471
472         if ((*value = strdup (fields[2])) == NULL)
473         {
474                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
475                 free (*host); *host = NULL;
476                 free (*type); *type = NULL;
477                 free (*inst); *inst = NULL;
478                 return (-1);
479         }
480
481         DBG ("host = %s, type = %s, inst = %s, value = %s",
482                         *host, *type, *inst, *value);
483
484         return (0);
485 }
486
487 int network_send (char *type, char *inst, char *value)
488 {
489         char buf[BUFF_SIZE];
490         int buflen;
491
492         sockent_t *se;
493
494         int ret;
495         int status;
496
497         DBG ("type = %s, inst = %s, value = %s", type, inst, value);
498
499         assert (operating_mode == MODE_CLIENT);
500
501         buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value);
502         if ((buflen >= BUFF_SIZE) || (buflen < 1))
503         {
504                 syslog (LOG_WARNING, "network_send: snprintf failed..");
505                 return (-1);
506         }
507         buf[buflen] = '\0';
508         buflen++;
509
510         if (socklist_head == NULL)
511                 network_connect_default ();
512
513         ret = 0;
514         for (se = socklist_head; se != NULL; se = se->next)
515         {
516                 if (se->mode != operating_mode)
517                         continue;
518
519                 DBG ("fd = %i", se->fd);
520
521                 while (1)
522                 {
523                         status = sendto (se->fd, buf, buflen, 0,
524                                         (struct sockaddr *) se->addr, se->addrlen);
525
526                         if (status == -1)
527                         {
528                                 if (errno == EINTR)
529                                 {
530                                         DBG ("sendto was interrupted");
531                                         continue;
532                                 }
533                                 else
534                                 {
535                                         syslog (LOG_ERR, "sendto: %s", strerror (errno));
536                                         ret = -1;
537                                         break;
538                                 }
539                         }
540                         else if (ret >= 0)
541                                 ret++;
542                         break;
543                 }
544         }
545
546         if (ret == 0)
547                 syslog (LOG_WARNING, "Message wasn't sent to anybody..");
548
549         return (ret);
550 }