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