Added the `MulticastTTL' option so users can set a TTL different to `1'.
[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 #define BUFF_SIZE 4096
43
44 #ifdef HAVE_LIBRRD
45 extern int operating_mode;
46 #else
47 static int operating_mode = MODE_CLIENT;
48 #endif
49
50 typedef struct sockent
51 {
52         int                      fd;
53         int                      mode;
54         struct sockaddr_storage *addr;
55         socklen_t                addrlen;
56         struct sockent          *next;
57 } sockent_t;
58
59 static sockent_t *socklist_head = NULL;
60
61 static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
62 {
63         int loop = 1;
64
65         char *ttl_str;
66         int   ttl_int;
67
68         ttl_str = cf_get_option ("MulticastTTL", NULL);
69         ttl_int = 0;
70         if (ttl_str != NULL)
71                 ttl_int = atoi (ttl_str);
72         if ((ttl_int < 1) || (ttl_int > 255))
73                 ttl_int = NET_DEFAULT_MC_TTL;
74
75         DBG ("fd = %i; calling `bind'", se->fd);
76
77         if (bind (se->fd, ai->ai_addr, ai->ai_addrlen) == -1)
78         {
79                 syslog (LOG_ERR, "bind: %s", strerror (errno));
80                 return (-1);
81         }
82
83         if (ai->ai_family == AF_INET)
84         {
85                 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
86                 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
87                 {
88                         struct ip_mreq mreq;
89
90                         DBG ("fd = %i; IPv4 multicast address found", se->fd);
91
92                         mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
93                         mreq.imr_interface.s_addr = htonl (INADDR_ANY);
94
95                         if (setsockopt (se->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
96                                                 &loop, sizeof (loop)) == -1)
97                         {
98                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
99                                 return (-1);
100                         }
101
102                         /* IP_MULTICAST_TTL */
103                         if (setsockopt (se->fd, IPPROTO_IP, IP_MULTICAST_TTL,
104                                                 &ttl_int, sizeof (ttl_int)) == -1)
105                         {
106                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
107                                 return (-1);
108                         }
109
110                         if (setsockopt (se->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
111                                                 &mreq, sizeof (mreq)) == -1)
112                         {
113                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
114                                 return (-1);
115                         }
116                 }
117         }
118         else if (ai->ai_family == AF_INET6)
119         {
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))
123                 {
124                         struct ipv6_mreq mreq;
125
126                         DBG ("fd = %i; IPv6 multicast address found", se->fd);
127
128                         memcpy (&mreq.ipv6mr_multiaddr,
129                                         &addr->sin6_addr,
130                                         sizeof (addr->sin6_addr));
131
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;
142
143                         if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
144                                                 &loop, sizeof (loop)) == -1)
145                         {
146                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
147                                 return (-1);
148                         }
149
150                         if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
151                                                 &ttl_int, sizeof (ttl_int)) == -1)
152                         {
153                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
154                                 return (-1);
155                         }
156
157                         if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
158                                                 &mreq, sizeof (mreq)) == -1)
159                         {
160                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
161                                 return (-1);
162                         }
163                 }
164         }
165
166         return (0);
167 }
168
169 int network_create_socket (const char *node, const char *service)
170 {
171         sockent_t *socklist_tail;
172
173         struct addrinfo  ai_hints;
174         struct addrinfo *ai_list, *ai_ptr;
175         int              ai_return;
176
177         int num_added = 0;
178
179         DBG ("node = %s, service = %s", node, service);
180
181         if (operating_mode == MODE_LOCAL)
182                 return (-1);
183
184         socklist_tail = socklist_head;
185         while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
186                 socklist_tail = socklist_tail->next;
187
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;
193
194         if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
195         {
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));
200                 return (-1);
201         }
202
203         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
204         {
205                 sockent_t *se;
206
207                 if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
208                 {
209                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
210                         continue;
211                 }
212
213                 if ((se->addr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage))) == NULL)
214                 {
215                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
216                         free (se);
217                         continue;
218                 }
219
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;
224
225                 se->mode = operating_mode;
226                 se->fd   = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
227                 se->next = NULL;
228
229                 if (se->fd == -1)
230                 {
231                         syslog (LOG_ERR, "socket: %s", strerror (errno));
232                         free (se->addr);
233                         free (se);
234                         continue;
235                 }
236
237                 if (operating_mode == MODE_SERVER)
238                         if (network_bind_socket (se, ai_ptr) != 0)
239                         {
240                                 free (se->addr);
241                                 free (se);
242                                 continue;
243                         }
244
245                 if (socklist_tail == NULL)
246                 {
247                         socklist_head = se;
248                         socklist_tail = se;
249                 }
250                 else
251                 {
252                         socklist_tail->next = se;
253                         socklist_tail = se;
254                 }
255
256                 num_added++;
257
258                 /* We don't open more than one write-socket per node/service pair.. */
259                 if (operating_mode == MODE_CLIENT)
260                         break;
261         }
262
263         freeaddrinfo (ai_list);
264
265         return (num_added);
266 }
267
268 static int network_connect_default (void)
269 {
270         int ret;
271
272         if (socklist_head != NULL)
273                 return (0);
274
275         DBG ("socklist_head is NULL");
276
277         ret = 0;
278
279         if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
280                 ret++;
281
282         /* Don't use IPv4 and IPv6 in parallel by default.. */
283         if ((operating_mode == MODE_CLIENT) && (ret != 0))
284                 return (ret);
285
286         if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
287                 ret++;
288
289         if (ret == 0)
290                 ret = -1;
291
292         return (ret);
293 }
294
295 static int network_get_listen_socket (void)
296 {
297         int fd;
298         int max_fd;
299         int status;
300
301         fd_set readfds;
302         sockent_t *se;
303
304         if (socklist_head == NULL)
305                 network_connect_default ();
306
307         FD_ZERO (&readfds);
308         max_fd = -1;
309         for (se = socklist_head; se != NULL; se = se->next)
310         {
311                 if (se->mode != operating_mode)
312                         continue;
313
314                 FD_SET (se->fd, &readfds);
315                 if (se->fd >= max_fd)
316                         max_fd = se->fd + 1;
317         }
318
319         if (max_fd == -1)
320         {
321                 syslog (LOG_WARNING, "No listen sockets found!");
322                 return (-1);
323         }
324
325         status = select (max_fd, &readfds, NULL, NULL, NULL);
326
327         if (status == -1)
328         {
329                 if (errno != EINTR)
330                         syslog (LOG_ERR, "select: %s", strerror (errno));
331                 return (-1);
332         }
333
334         fd = -1;
335         for (se = socklist_head; se != NULL; se = se->next)
336         {
337                 if (se->mode != operating_mode)
338                         continue;
339
340                 if (FD_ISSET (se->fd, &readfds))
341                 {
342                         fd = se->fd;
343                         break;
344                 }
345         }
346
347         if (fd == -1)
348                 syslog (LOG_WARNING, "No socket ready..?");
349
350         DBG ("fd = %i", fd);
351         return (fd);
352 }
353
354 int network_receive (char **host, char **type, char **inst, char **value)
355 {
356         int fd;
357         char buffer[BUFF_SIZE];
358
359         struct sockaddr_storage addr;
360         socklen_t               addrlen;
361         int status;
362
363         char *fields[4];
364
365         assert (operating_mode == MODE_SERVER);
366
367         *host  = NULL;
368         *type  = NULL;
369         *inst  = NULL;
370         *value = NULL;
371
372         if ((fd = network_get_listen_socket ()) < 0)
373                 return (-1);
374
375         addrlen = sizeof (addr);
376         if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
377         {
378                 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
379                 return (-1);
380         }
381
382         if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
383         {
384                 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
385                 return (-1);
386         }
387
388         status = getnameinfo ((struct sockaddr *) &addr, addrlen,
389                         *host, BUFF_SIZE, NULL, 0, 0);
390         if (status != 0)
391         {
392                 free (*host); *host = NULL;
393                 syslog (LOG_ERR, "getnameinfo: %s",
394                                 status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
395                 return (-1);
396         }
397
398         if (strsplit (buffer, fields, 4) != 3)
399         {
400                 syslog (LOG_WARNING, "Invalid message from `%s'", *host);
401                 free (*host); *host = NULL;
402                 return (-1);
403         }
404
405         if ((*type = strdup (fields[0])) == NULL)
406         {
407                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
408                 free (*host); *host = NULL;
409                 return (-1);
410         }
411
412         if ((*inst = strdup (fields[1])) == NULL)
413         {
414                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
415                 free (*host); *host = NULL;
416                 free (*type); *type = NULL;
417                 return (-1);
418         }
419
420         if ((*value = strdup (fields[2])) == NULL)
421         {
422                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
423                 free (*host); *host = NULL;
424                 free (*type); *type = NULL;
425                 free (*inst); *inst = NULL;
426                 return (-1);
427         }
428
429         DBG ("host = %s, type = %s, inst = %s, value = %s",
430                         *host, *type, *inst, *value);
431
432         return (0);
433 }
434
435 int network_send (char *type, char *inst, char *value)
436 {
437         char buf[BUFF_SIZE];
438         int buflen;
439
440         sockent_t *se;
441
442         int ret;
443         int status;
444
445         DBG ("type = %s, inst = %s, value = %s", type, inst, value);
446
447         assert (operating_mode == MODE_CLIENT);
448
449         buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value);
450         if ((buflen >= BUFF_SIZE) || (buflen < 1))
451         {
452                 syslog (LOG_WARNING, "network_send: snprintf failed..");
453                 return (-1);
454         }
455         buf[buflen] = '\0';
456         buflen++;
457
458         if (socklist_head == NULL)
459                 network_connect_default ();
460
461         ret = 0;
462         for (se = socklist_head; se != NULL; se = se->next)
463         {
464                 if (se->mode != operating_mode)
465                         continue;
466
467                 DBG ("fd = %i", se->fd);
468
469                 while (1)
470                 {
471                         status = sendto (se->fd, buf, buflen, 0,
472                                         (struct sockaddr *) se->addr, se->addrlen);
473
474                         if (status == -1)
475                         {
476                                 if (errno == EINTR)
477                                 {
478                                         DBG ("sendto was interrupted");
479                                         continue;
480                                 }
481                                 else
482                                 {
483                                         syslog (LOG_ERR, "sendto: %s", strerror (errno));
484                                         ret = -1;
485                                         break;
486                                 }
487                         }
488                         else if (ret >= 0)
489                                 ret++;
490                         break;
491                 }
492         }
493
494         if (ret == 0)
495                 syslog (LOG_WARNING, "Message wasn't sent to anybody..");
496
497         return (ret);
498 }