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