546f5e2d142ad46d03548a5410d5c139ba361018
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 <netdb.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <syslog.h>
32 #include <errno.h>
33 #include <assert.h>
35 #include "network.h"
36 #include "common.h"
37 #include "utils_debug.h"
39 /*
40 * From RFC2365:
41 *
42 * The IPv4 Organization Local Scope -- 239.192.0.0/14
43 *
44 * 239.192.0.0/14 is defined to be the IPv4 Organization Local Scope, and is
45 * the space from which an organization should allocate sub-ranges when
46 * defining scopes for private use.
47 *
48 * Port 25826 is not assigned as of 2005-09-12
49 */
51 #define IPV4_MCAST_GROUP "239.192.74.66"
52 #define UDP_PORT 25826
54 /* 1500 - 40 - 8 = Ethernet packet - IPv6 header - UDP header */
55 /* #define BUFF_SIZE 1452 */
57 #define BUFF_SIZE 4096
59 #ifdef HAVE_LIBRRD
60 extern int operating_mode;
61 #else
62 static int operating_mode = MODE_CLIENT;
63 #endif
65 typedef struct sockent
66 {
67 int fd;
68 struct sockaddr_storage *addr;
69 socklen_t addrlen;
70 struct sockent *next;
71 } sockent_t;
73 static sockent_t *socklist_head = NULL;
75 static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
76 {
77 int loop = 1;
79 if (bind (se->fd, ai->ai_addr, ai->ai_addrlen) == -1)
80 {
81 syslog (LOG_ERR, "bind: %s", strerror (errno));
82 return (-1);
83 }
85 if (ai->ai_family == AF_INET)
86 {
87 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
88 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
89 {
90 struct ip_mreq mreq;
92 mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
93 mreq.imr_interface.s_addr = htonl (INADDR_ANY);
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 }
102 if (setsockopt (se->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
103 &mreq, sizeof (mreq)) == -1)
104 {
105 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
106 return (-1);
107 }
108 }
109 }
110 else if (ai->ai_family == AF_INET6)
111 {
112 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
113 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
114 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
115 {
116 struct ipv6_mreq mreq;
118 memcpy (&mreq.ipv6mr_multiaddr,
119 &addr->sin6_addr,
120 sizeof (addr->sin6_addr));
122 /* http://developer.apple.com/documentation/Darwin/Reference/ManPages/man4/ip6.4.html
123 * ipv6mr_interface may be set to zeroes to
124 * choose the default multicast interface or to
125 * the index of a particular multicast-capable
126 * interface if the host is multihomed.
127 * Membership is associ-associated with a
128 * single interface; programs running on
129 * multihomed hosts may need to join the same
130 * group on more than one interface.*/
131 mreq.ipv6mr_interface = 0;
133 if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
134 &loop, sizeof (loop)) == -1)
135 {
136 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
137 return (-1);
138 }
140 if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
141 &mreq, sizeof (mreq)) == -1)
142 {
143 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
144 return (-1);
145 }
146 }
147 }
149 return (0);
150 }
152 int network_create_socket (const char *node, const char *service)
153 {
154 sockent_t *socklist_tail;
156 struct addrinfo ai_hints;
157 struct addrinfo *ai_list, *ai_ptr;
158 int ai_return;
160 int num_added = 0;
162 DBG ("node = %s, service = %s", node, service);
164 if (operating_mode == MODE_LOCAL)
165 return (-1);
167 socklist_tail = socklist_head;
168 while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
169 socklist_tail = socklist_tail->next;
171 memset (&ai_hints, '\0', sizeof (ai_hints));
172 ai_hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
173 ai_hints.ai_family = PF_UNSPEC;
174 ai_hints.ai_socktype = SOCK_DGRAM;
175 ai_hints.ai_protocol = IPPROTO_UDP; /* XXX is this right here?!? */
177 if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
178 {
179 syslog (LOG_ERR, "getaddrinfo (%s, %s): %s",
180 node == NULL ? "(null)" : node,
181 service == NULL ? "(null)" : service,
182 ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
183 return (-1);
184 }
186 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
187 {
188 sockent_t *se;
190 if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
191 {
192 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
193 continue;
194 }
196 if ((se->addr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage))) == NULL)
197 {
198 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
199 free (se);
200 continue;
201 }
203 assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
204 memset (se->addr, '\0', sizeof (struct sockaddr_storage));
205 memcpy (se->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
206 se->addrlen = ai_ptr->ai_addrlen;
208 se->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
209 se->next = NULL;
211 if (se->fd == -1)
212 {
213 syslog (LOG_ERR, "socket: %s", strerror (errno));
214 free (se->addr);
215 free (se);
216 continue;
217 }
219 if (operating_mode == MODE_SERVER)
220 if (network_bind_socket (se, ai_ptr) != 0)
221 {
222 free (se->addr);
223 free (se);
224 continue;
225 }
227 if (socklist_tail == NULL)
228 {
229 socklist_head = socklist_tail = se;
230 }
231 else
232 {
233 socklist_tail->next = se;
234 socklist_tail = se;
235 }
237 num_added++;
239 /* We don't open more than one write-socket per node/service pair.. */
240 if (operating_mode == MODE_CLIENT)
241 break;
242 }
244 freeaddrinfo (ai_list);
246 return (num_added);
247 }
249 static int network_get_listen_socket (void)
250 {
251 int fd;
252 int max_fd;
253 int status;
255 fd_set readfds;
256 sockent_t *se;
258 while (1)
259 {
260 FD_ZERO (&readfds);
261 max_fd = -1;
262 for (se = socklist_head; se != NULL; se = se->next)
263 {
264 FD_SET (se->fd, &readfds);
265 if (se->fd >= max_fd)
266 max_fd = se->fd + 1;
267 }
269 if (max_fd == -1)
270 {
271 syslog (LOG_WARNING, "No listen sockets found!");
272 return (-1);
273 }
275 status = select (max_fd, &readfds, NULL, NULL, NULL);
277 if ((status == -1) && (errno == EINTR))
278 continue;
279 else if (status == -1)
280 {
281 syslog (LOG_ERR, "select: %s", strerror (errno));
282 return (-1);
283 }
284 else
285 break;
286 } /* while (true) */
288 fd = -1;
289 for (se = socklist_head; se != NULL; se = se->next)
290 if (FD_ISSET (se->fd, &readfds))
291 {
292 fd = se->fd;
293 break;
294 }
296 if (fd == -1)
297 syslog (LOG_WARNING, "No socket ready..?");
299 DBG ("fd = %i", fd);
300 return (fd);
301 }
303 int network_receive (char **host, char **type, char **inst, char **value)
304 {
305 int fd;
306 char buffer[BUFF_SIZE];
308 struct sockaddr_storage addr;
309 socklen_t addrlen;
310 int status;
312 char *fields[4];
314 assert (operating_mode == MODE_SERVER);
316 *host = NULL;
317 *type = NULL;
318 *inst = NULL;
319 *value = NULL;
321 if ((fd = network_get_listen_socket ()) < 0)
322 return (-1);
324 if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
325 {
326 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
327 return (-1);
328 }
330 if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
331 {
332 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
333 return (-1);
334 }
336 status = getnameinfo ((struct sockaddr *) &addr, addrlen,
337 *host, BUFF_SIZE, NULL, 0, 0);
338 if (status != 0)
339 {
340 free (*host); *host = NULL;
341 syslog (LOG_ERR, "getnameinfo: %s",
342 status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
343 return (-1);
344 }
346 if (strsplit (buffer, fields, 4) != 3)
347 {
348 syslog (LOG_WARNING, "Invalid message from `%s'", *host);
349 free (*host); *host = NULL;
350 return (-1);
351 }
353 if ((*type = strdup (fields[0])) == NULL)
354 {
355 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
356 free (*host); *host = NULL;
357 return (-1);
358 }
360 if ((*inst = strdup (fields[1])) == NULL)
361 {
362 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
363 free (*host); *host = NULL;
364 free (*type); *type = NULL;
365 return (-1);
366 }
368 if ((*value = strdup (fields[2])) == NULL)
369 {
370 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
371 free (*host); *host = NULL;
372 free (*type); *type = NULL;
373 free (*inst); *inst = NULL;
374 return (-1);
375 }
377 DBG ("host = %s, type = %s, inst = %s, value = %s",
378 *host, *type, *inst, *value);
380 return (0);
381 }
383 int network_send (char *type, char *inst, char *value)
384 {
385 char buf[BUFF_SIZE];
386 int buflen;
388 sockent_t *se;
390 int ret;
391 int status;
393 DBG ("type = %s, inst = %s, value = %s", type, inst, value);
395 assert (operating_mode == MODE_CLIENT);
397 buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value);
398 if ((buflen >= BUFF_SIZE) || (buflen < 1))
399 {
400 syslog (LOG_WARNING, "network_send: snprintf failed..");
401 return (-1);
402 }
403 buf[buflen] = '\0';
404 buflen++;
406 ret = 0;
407 for (se = socklist_head; se != NULL; se = se->next)
408 {
409 DBG ("fd = %i", se->fd);
411 while (1)
412 {
413 status = sendto (se->fd, buf, buflen, 0,
414 (struct sockaddr *) se->addr, se->addrlen);
416 if (status == -1)
417 {
418 if (errno == EINTR)
419 {
420 DBG ("sendto was interrupted");
421 continue;
422 }
423 else
424 {
425 syslog (LOG_ERR, "sendto: %s", strerror (errno));
426 break;
427 }
428 }
429 else if (ret >= 0)
430 ret++;
431 break;
432 }
433 }
435 if (ret == 0)
436 syslog (LOG_WARNING, "Message wasn't sent to anybody..");
438 return (ret);
439 }