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 #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 */
50 #define BUFF_SIZE 4096
52 extern int operating_mode;
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;
63 static sockent_t *socklist_head = NULL;
65 static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
66 {
67 char *ttl_str;
68 int ttl_int;
70 ttl_str = cf_get_option ("TimeToLive", NULL);
71 if (ttl_str == NULL)
72 return (-1);
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 }
81 DBG ("ttl = %i", ttl_int);
83 if (ai->ai_family == AF_INET)
84 {
85 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
86 int optname;
88 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
89 optname = IP_MULTICAST_TTL;
90 else
91 optname = IP_TTL;
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;
106 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
107 optname = IPV6_MULTICAST_HOPS;
108 else
109 optname = IPV6_UNICAST_HOPS;
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 }
119 return (0);
120 }
122 static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
123 {
124 int loop = 1;
126 DBG ("fd = %i; calling `bind'", se->fd);
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 }
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;
141 DBG ("fd = %i; IPv4 multicast address found", se->fd);
143 mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
144 mreq.imr_interface.s_addr = htonl (INADDR_ANY);
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 }
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;
169 DBG ("fd = %i; IPv6 multicast address found", se->fd);
171 memcpy (&mreq.ipv6mr_multiaddr,
172 &addr->sin6_addr,
173 sizeof (addr->sin6_addr));
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;
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 }
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 }
202 return (0);
203 }
205 int network_create_socket (const char *node, const char *service)
206 {
207 sockent_t *socklist_tail;
209 struct addrinfo ai_hints;
210 struct addrinfo *ai_list, *ai_ptr;
211 int ai_return;
213 int num_added = 0;
215 DBG ("node = %s, service = %s", node, service);
217 if (operating_mode == MODE_LOCAL || operating_mode == MODE_LOG)
218 return (-1);
220 socklist_tail = socklist_head;
221 while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
222 socklist_tail = socklist_tail->next;
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;
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 }
245 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
246 {
247 sockent_t *se;
249 if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
250 {
251 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
252 continue;
253 }
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 }
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;
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;
271 if (se->fd == -1)
272 {
273 syslog (LOG_ERR, "socket: %s", strerror (errno));
274 free (se->addr);
275 free (se);
276 continue;
277 }
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 }
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 }
304 num_added++;
306 /* We don't open more than one write-socket per node/service pair.. */
307 if (operating_mode == MODE_CLIENT)
308 break;
309 }
311 freeaddrinfo (ai_list);
313 return (num_added);
314 }
316 static int network_connect_default (void)
317 {
318 int ret;
320 if (socklist_head != NULL)
321 return (0);
323 DBG ("socklist_head is NULL");
325 ret = 0;
327 if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
328 ret++;
330 /* Don't use IPv4 and IPv6 in parallel by default.. */
331 if ((operating_mode == MODE_CLIENT) && (ret != 0))
332 return (ret);
334 if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
335 ret++;
337 if (ret == 0)
338 ret = -1;
340 return (ret);
341 }
343 static int network_get_listen_socket (void)
344 {
345 int fd;
346 int max_fd;
347 int status;
349 fd_set readfds;
350 sockent_t *se;
352 if (socklist_head == NULL)
353 network_connect_default ();
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;
362 FD_SET (se->fd, &readfds);
363 if (se->fd >= max_fd)
364 max_fd = se->fd + 1;
365 }
367 if (max_fd == -1)
368 {
369 syslog (LOG_WARNING, "No listen sockets found!");
370 return (-1);
371 }
373 status = select (max_fd, &readfds, NULL, NULL, NULL);
375 if (status == -1)
376 {
377 if (errno != EINTR)
378 syslog (LOG_ERR, "select: %s", strerror (errno));
379 return (-1);
380 }
382 fd = -1;
383 for (se = socklist_head; se != NULL; se = se->next)
384 {
385 if (se->mode != operating_mode)
386 continue;
388 if (FD_ISSET (se->fd, &readfds))
389 {
390 fd = se->fd;
391 break;
392 }
393 }
395 if (fd == -1)
396 syslog (LOG_WARNING, "No socket ready..?");
398 DBG ("fd = %i", fd);
399 return (fd);
400 }
402 int network_receive (char **host, char **type, char **inst, char **value)
403 {
404 int fd;
405 char buffer[BUFF_SIZE];
407 struct sockaddr_storage addr;
408 socklen_t addrlen;
409 int status;
411 char *fields[4];
413 assert (operating_mode == MODE_SERVER);
415 *host = NULL;
416 *type = NULL;
417 *inst = NULL;
418 *value = NULL;
420 if ((fd = network_get_listen_socket ()) < 0)
421 return (-1);
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 }
430 if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
431 {
432 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
433 return (-1);
434 }
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 }
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 }
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 }
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 }
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 }
477 DBG ("host = %s, type = %s, inst = %s, value = %s",
478 *host, *type, *inst, *value);
480 return (0);
481 }
483 int network_send (char *type, char *inst, char *value)
484 {
485 char buf[BUFF_SIZE];
486 int buflen;
488 sockent_t *se;
490 int ret;
491 int status;
493 DBG ("type = %s, inst = %s, value = %s", type, inst, value);
495 assert (operating_mode == MODE_CLIENT);
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++;
506 if (socklist_head == NULL)
507 network_connect_default ();
509 ret = 0;
510 for (se = socklist_head; se != NULL; se = se->next)
511 {
512 if (se->mode != operating_mode)
513 continue;
515 DBG ("fd = %i", se->fd);
517 while (1)
518 {
519 status = sendto (se->fd, buf, buflen, 0,
520 (struct sockaddr *) se->addr, se->addrlen);
522 if (status == -1)
523 {
524 if (errno == EINTR)
525 {
526 DBG ("sendto was interrupted");
527 continue;
528 }
529 else
530 {
531 syslog (LOG_ERR, "sendto: %s", strerror (errno));
532 ret = -1;
533 break;
534 }
535 }
536 else if (ret >= 0)
537 ret++;
538 break;
539 }
540 }
542 if (ret == 0)
543 syslog (LOG_WARNING, "Message wasn't sent to anybody..");
545 return (ret);
546 }