Merged `branches/log-mode' to `trunk'
[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 extern int operating_mode;
53
54 typedef struct sockent
55 {
56         int                      fd;
57         int                      mode;
58         struct sockaddr_storage *addr;
59         socklen_t                addrlen;
60         struct sockent          *next;
61 } sockent_t;
62
63 static sockent_t *socklist_head = NULL;
64
65 static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
66 {
67         char *ttl_str;
68         int   ttl_int;
69
70         ttl_str = cf_get_option ("TimeToLive", NULL);
71         if (ttl_str == NULL)
72                 return (-1);
73
74         ttl_int = atoi (ttl_str);
75         if ((ttl_int < 1) || (ttl_int > 255))
76         {
77                 syslog (LOG_WARNING, "A TTL value of %i is invalid.", ttl_int);
78                 return (-1);
79         }
80
81         DBG ("ttl = %i", ttl_int);
82
83         if (ai->ai_family == AF_INET)
84         {
85                 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
86                 int optname;
87
88                 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
89                         optname = IP_MULTICAST_TTL;
90                 else
91                         optname = IP_TTL;
92
93                 if (setsockopt (se->fd, IPPROTO_IP, optname,
94                                         &ttl_int, sizeof (ttl_int)) == -1)
95                 {
96                         syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
97                         return (-1);
98                 }
99         }
100         else if (ai->ai_family == AF_INET6)
101         {
102                 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
103                 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
104                 int optname;
105
106                 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
107                         optname = IPV6_MULTICAST_HOPS;
108                 else
109                         optname = IPV6_UNICAST_HOPS;
110
111                 if (setsockopt (se->fd, IPPROTO_IPV6, optname,
112                                         &ttl_int, sizeof (ttl_int)) == -1)
113                 {
114                         syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
115                         return (-1);
116                 }
117         }
118
119         return (0);
120 }
121
122 static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
123 {
124         int loop = 1;
125
126         DBG ("fd = %i; calling `bind'", se->fd);
127
128         if (bind (se->fd, ai->ai_addr, ai->ai_addrlen) == -1)
129         {
130                 syslog (LOG_ERR, "bind: %s", strerror (errno));
131                 return (-1);
132         }
133
134         if (ai->ai_family == AF_INET)
135         {
136                 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
137                 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
138                 {
139                         struct ip_mreq mreq;
140
141                         DBG ("fd = %i; IPv4 multicast address found", se->fd);
142
143                         mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
144                         mreq.imr_interface.s_addr = htonl (INADDR_ANY);
145
146                         if (setsockopt (se->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
147                                                 &loop, sizeof (loop)) == -1)
148                         {
149                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
150                                 return (-1);
151                         }
152
153                         if (setsockopt (se->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
154                                                 &mreq, sizeof (mreq)) == -1)
155                         {
156                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
157                                 return (-1);
158                         }
159                 }
160         }
161         else if (ai->ai_family == AF_INET6)
162         {
163                 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
164                 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
165                 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
166                 {
167                         struct ipv6_mreq mreq;
168
169                         DBG ("fd = %i; IPv6 multicast address found", se->fd);
170
171                         memcpy (&mreq.ipv6mr_multiaddr,
172                                         &addr->sin6_addr,
173                                         sizeof (addr->sin6_addr));
174
175                         /* http://developer.apple.com/documentation/Darwin/Reference/ManPages/man4/ip6.4.html
176                          * ipv6mr_interface may be set to zeroes to
177                          * choose the default multicast interface or to
178                          * the index of a particular multicast-capable
179                          * interface if the host is multihomed.
180                          * Membership is associ-associated with a
181                          * single interface; programs running on
182                          * multihomed hosts may need to join the same
183                          * group on more than one interface.*/
184                         mreq.ipv6mr_interface = 0;
185
186                         if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
187                                                 &loop, sizeof (loop)) == -1)
188                         {
189                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
190                                 return (-1);
191                         }
192
193                         if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
194                                                 &mreq, sizeof (mreq)) == -1)
195                         {
196                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
197                                 return (-1);
198                         }
199                 }
200         }
201
202         return (0);
203 }
204
205 int network_create_socket (const char *node, const char *service)
206 {
207         sockent_t *socklist_tail;
208
209         struct addrinfo  ai_hints;
210         struct addrinfo *ai_list, *ai_ptr;
211         int              ai_return;
212
213         int num_added = 0;
214
215         DBG ("node = %s, service = %s", node, service);
216
217         if (operating_mode == MODE_LOCAL || operating_mode == MODE_LOG)
218                 return (-1);
219
220         socklist_tail = socklist_head;
221         while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
222                 socklist_tail = socklist_tail->next;
223
224         memset (&ai_hints, '\0', sizeof (ai_hints));
225         ai_hints.ai_flags    = 0;
226 #ifdef AI_PASSIVE
227         ai_hints.ai_flags |= AI_PASSIVE;
228 #endif
229 #ifdef AI_ADDRCONFIG
230         ai_hints.ai_flags |= AI_ADDRCONFIG;
231 #endif
232         ai_hints.ai_family   = PF_UNSPEC;
233         ai_hints.ai_socktype = SOCK_DGRAM;
234         ai_hints.ai_protocol = IPPROTO_UDP;
235
236         if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
237         {
238                 syslog (LOG_ERR, "getaddrinfo (%s, %s): %s",
239                                 node == NULL ? "(null)" : node,
240                                 service == NULL ? "(null)" : service,
241                                 ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
242                 return (-1);
243         }
244
245         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
246         {
247                 sockent_t *se;
248
249                 if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
250                 {
251                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
252                         continue;
253                 }
254
255                 if ((se->addr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage))) == NULL)
256                 {
257                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
258                         free (se);
259                         continue;
260                 }
261
262                 assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
263                 memset (se->addr, '\0', sizeof (struct sockaddr_storage));
264                 memcpy (se->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
265                 se->addrlen = ai_ptr->ai_addrlen;
266
267                 se->mode = operating_mode;
268                 se->fd   = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
269                 se->next = NULL;
270
271                 if (se->fd == -1)
272                 {
273                         syslog (LOG_ERR, "socket: %s", strerror (errno));
274                         free (se->addr);
275                         free (se);
276                         continue;
277                 }
278
279                 if (operating_mode == MODE_SERVER)
280                 {
281                         if (network_bind_socket (se, ai_ptr) != 0)
282                         {
283                                 free (se->addr);
284                                 free (se);
285                                 continue;
286                         }
287                 }
288                 else if (operating_mode == MODE_CLIENT)
289                 {
290                         network_set_ttl (se, ai_ptr);
291                 }
292
293                 if (socklist_tail == NULL)
294                 {
295                         socklist_head = se;
296                         socklist_tail = se;
297                 }
298                 else
299                 {
300                         socklist_tail->next = se;
301                         socklist_tail = se;
302                 }
303
304                 num_added++;
305
306                 /* We don't open more than one write-socket per node/service pair.. */
307                 if (operating_mode == MODE_CLIENT)
308                         break;
309         }
310
311         freeaddrinfo (ai_list);
312
313         return (num_added);
314 }
315
316 static int network_connect_default (void)
317 {
318         int ret;
319
320         if (socklist_head != NULL)
321                 return (0);
322
323         DBG ("socklist_head is NULL");
324
325         ret = 0;
326
327         if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
328                 ret++;
329
330         /* Don't use IPv4 and IPv6 in parallel by default.. */
331         if ((operating_mode == MODE_CLIENT) && (ret != 0))
332                 return (ret);
333
334         if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
335                 ret++;
336
337         if (ret == 0)
338                 ret = -1;
339
340         return (ret);
341 }
342
343 static int network_get_listen_socket (void)
344 {
345         int fd;
346         int max_fd;
347         int status;
348
349         fd_set readfds;
350         sockent_t *se;
351
352         if (socklist_head == NULL)
353                 network_connect_default ();
354
355         FD_ZERO (&readfds);
356         max_fd = -1;
357         for (se = socklist_head; se != NULL; se = se->next)
358         {
359                 if (se->mode != operating_mode)
360                         continue;
361
362                 FD_SET (se->fd, &readfds);
363                 if (se->fd >= max_fd)
364                         max_fd = se->fd + 1;
365         }
366
367         if (max_fd == -1)
368         {
369                 syslog (LOG_WARNING, "No listen sockets found!");
370                 return (-1);
371         }
372
373         status = select (max_fd, &readfds, NULL, NULL, NULL);
374
375         if (status == -1)
376         {
377                 if (errno != EINTR)
378                         syslog (LOG_ERR, "select: %s", strerror (errno));
379                 return (-1);
380         }
381
382         fd = -1;
383         for (se = socklist_head; se != NULL; se = se->next)
384         {
385                 if (se->mode != operating_mode)
386                         continue;
387
388                 if (FD_ISSET (se->fd, &readfds))
389                 {
390                         fd = se->fd;
391                         break;
392                 }
393         }
394
395         if (fd == -1)
396                 syslog (LOG_WARNING, "No socket ready..?");
397
398         DBG ("fd = %i", fd);
399         return (fd);
400 }
401
402 int network_receive (char **host, char **type, char **inst, char **value)
403 {
404         int fd;
405         char buffer[BUFF_SIZE];
406
407         struct sockaddr_storage addr;
408         socklen_t               addrlen;
409         int status;
410
411         char *fields[4];
412
413         assert (operating_mode == MODE_SERVER);
414
415         *host  = NULL;
416         *type  = NULL;
417         *inst  = NULL;
418         *value = NULL;
419
420         if ((fd = network_get_listen_socket ()) < 0)
421                 return (-1);
422
423         addrlen = sizeof (addr);
424         if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
425         {
426                 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
427                 return (-1);
428         }
429
430         if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
431         {
432                 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
433                 return (-1);
434         }
435
436         status = getnameinfo ((struct sockaddr *) &addr, addrlen,
437                         *host, BUFF_SIZE, NULL, 0, 0);
438         if (status != 0)
439         {
440                 free (*host); *host = NULL;
441                 syslog (LOG_ERR, "getnameinfo: %s",
442                                 status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
443                 return (-1);
444         }
445
446         if (strsplit (buffer, fields, 4) != 3)
447         {
448                 syslog (LOG_WARNING, "Invalid message from `%s'", *host);
449                 free (*host); *host = NULL;
450                 return (-1);
451         }
452
453         if ((*type = strdup (fields[0])) == NULL)
454         {
455                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
456                 free (*host); *host = NULL;
457                 return (-1);
458         }
459
460         if ((*inst = strdup (fields[1])) == NULL)
461         {
462                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
463                 free (*host); *host = NULL;
464                 free (*type); *type = NULL;
465                 return (-1);
466         }
467
468         if ((*value = strdup (fields[2])) == NULL)
469         {
470                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
471                 free (*host); *host = NULL;
472                 free (*type); *type = NULL;
473                 free (*inst); *inst = NULL;
474                 return (-1);
475         }
476
477         DBG ("host = %s, type = %s, inst = %s, value = %s",
478                         *host, *type, *inst, *value);
479
480         return (0);
481 }
482
483 int network_send (char *type, char *inst, char *value)
484 {
485         char buf[BUFF_SIZE];
486         int buflen;
487
488         sockent_t *se;
489
490         int ret;
491         int status;
492
493         DBG ("type = %s, inst = %s, value = %s", type, inst, value);
494
495         assert (operating_mode == MODE_CLIENT);
496
497         buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value);
498         if ((buflen >= BUFF_SIZE) || (buflen < 1))
499         {
500                 syslog (LOG_WARNING, "network_send: snprintf failed..");
501                 return (-1);
502         }
503         buf[buflen] = '\0';
504         buflen++;
505
506         if (socklist_head == NULL)
507                 network_connect_default ();
508
509         ret = 0;
510         for (se = socklist_head; se != NULL; se = se->next)
511         {
512                 if (se->mode != operating_mode)
513                         continue;
514
515                 while (1)
516                 {
517                         status = sendto (se->fd, buf, buflen, 0,
518                                         (struct sockaddr *) se->addr, se->addrlen);
519
520                         if (status == -1)
521                         {
522                                 if (errno == EINTR)
523                                 {
524                                         DBG ("sendto was interrupted");
525                                         continue;
526                                 }
527                                 else
528                                 {
529                                         syslog (LOG_ERR, "sendto: %s", strerror (errno));
530                                         ret = -1;
531                                         break;
532                                 }
533                         }
534                         else if (ret >= 0)
535                                 ret++;
536                         break;
537                 }
538         }
539
540         if (ret == 0)
541                 syslog (LOG_WARNING, "Message wasn't sent to anybody..");
542
543         return (ret);
544 }