f42693e1c494f90359b1877c4148c8e4a5fef45e
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"
38 /*
39 * From RFC2365:
40 *
41 * The IPv4 Organization Local Scope -- 239.192.0.0/14
42 *
43 * 239.192.0.0/14 is defined to be the IPv4 Organization Local Scope, and is
44 * the space from which an organization should allocate sub-ranges when
45 * defining scopes for private use.
46 *
47 * Port 25826 is not assigned as of 2005-09-12
48 */
50 #define IPV4_MCAST_GROUP "239.192.74.66"
51 #define UDP_PORT 25826
53 /* 1500 - 40 - 8 = Ethernet packet - IPv6 header - UDP header */
54 #define BUFF_SIZE 1452
56 #define BUFF_SIZE 4096
58 #ifdef HAVE_LIBRRD
59 extern int operating_mode;
60 #else
61 static int operating_mode = MODE_CLIENT;
62 #endif
64 typedef struct sockent
65 {
66 int fd;
67 struct sockaddr_storage *addr;
68 socklen_t addrlen;
69 struct sockent *next;
70 } sockent_t;
72 static sockent_t *socklist_head = NULL;
74 static int network_bind_socket (int fd, const struct addrinfo *ai, const sockent_t *se)
75 {
76 int loop = 1;
78 if (bind (fd, ai->ai_addr, ai->ai_addrlen) == -1)
79 {
80 syslog (LOG_ERR, "bind: %s", strerror (errno));
81 return (-1);
82 }
84 if (ai->ai_family == AF_INET)
85 {
86 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
87 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
88 {
89 struct ip_mreq mreq;
91 mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
92 mreq.imr_interface.s_addr = htonl (INADDR_ANY);
94 if (setsockopt (se->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
95 &loop, sizeof (loop)) == -1)
96 {
97 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
98 return (-1);
99 }
101 if (setsockopt (se->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
102 &mreq, sizeof (mreq)) == -1)
103 {
104 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
105 return (-1);
106 }
107 }
108 }
109 else if (ai->ai_family == AF_INET6)
110 {
111 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
112 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
113 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
114 {
115 struct ipv6_mreq mreq;
117 memcpy (&mreq.ipv6mr_multiaddr,
118 &addr->sin6_addr,
119 sizeof (addr->sin6_addr));
121 /* http://developer.apple.com/documentation/Darwin/Reference/ManPages/man4/ip6.4.html
122 * ipv6mr_interface may be set to zeroes to
123 * choose the default multicast interface or to
124 * the index of a particular multicast-capable
125 * interface if the host is multihomed.
126 * Membership is associ-associated with a
127 * single interface; programs running on
128 * multihomed hosts may need to join the same
129 * group on more than one interface.*/
130 mreq6.ipv6mr_interface = 0;
132 if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
133 &loop, sizeof (loop)) == -1)
134 {
135 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
136 return (-1);
137 }
139 if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
140 &mreq, sizeof (mreq)) == -1)
141 {
142 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
143 return (-1);
144 }
145 }
146 }
148 return (0);
149 }
151 int network_create_socket (const char *node, const char *service)
152 {
153 sockent_t *socklist_tail;
155 struct addrinfo ai_hints;
156 struct addrinfo *ai_list, *ai_ptr;
157 int ai_return;
159 int num_added = 0;
161 DBG ("node = %s, service = %s", node, service);
163 if (operating_mode == MODE_LOCAL)
164 return (-1);
166 socklist_tail = socklist_head;
167 while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
168 socklist_tail = socklist_tail->next;
170 memset (&ai_hints, '\0', sizeof (ai_hints));
171 ai_hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
172 ai_hints.ai_family = PF_UNSPEC;
173 ai_hints.ai_socktype = SOCK_DGRAM;
174 ai_hints.ai_protocol = IPPROTO_UDP; /* XXX is this right here?!? */
176 if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
177 {
178 syslog (LOG_ERR, "getaddrinfo (%s, %s): %s",
179 node == NULL ? "(null)" : node,
180 service == NULL ? "(null)" : service,
181 ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
182 return (-1);
183 }
185 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
186 {
187 sockent_t *se;
189 if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
190 {
191 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
192 continue;
193 }
195 if ((se->addr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage))) == NULL)
196 {
197 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
198 free (se);
199 continue;
200 }
202 assert (sizeof (struct sockaddr_storage) >= ai_ptr->addrlen);
203 memset (se->addr, '\0', sizeof (struct sockaddr_storage));
204 memcpy (se->addr, ai_ptr->ai_addr, ai_ptr->addrlen);
205 se->addrlen = ai_ptr->addrlen;
207 se->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
208 se->next = NULL;
210 if (se->fd == -1)
211 {
212 syslog (LOG_ERR, "socket: %s", strerror (errno));
213 free (se->addr);
214 free (se);
215 continue;
216 }
218 if (operating_mode == MODE_SERVER)
219 if (network_bind_socket (se->fd, ai_ptr, se->addr) != 0)
220 {
221 free (se->addr);
222 free (se);
223 continue;
224 }
226 if (socklist_tail == NULL)
227 {
228 socklist_head = socklist_tail = se;
229 }
230 else
231 {
232 socklist_tail->next = se;
233 socklist_tail = se;
234 }
236 num_added++;
238 /* We don't open more than one write-socket per node/service pair.. */
239 if (operating_mode == MODE_CLIENT)
240 break;
241 }
243 freeaddrinfo (ai_list);
245 return (num_added);
246 }
248 static int network_get_listen_socket (void)
249 {
250 int fd;
251 int max_fd;
253 fd_set readfds;
254 sockent_t *se;
256 while (1)
257 {
258 FD_ZERO (&readfds);
259 max_fd = -1;
260 for (se = socklist_head; se != NULL; se = se->next)
261 {
262 FD_SET (se->fd, &readfds);
263 if (se->fd >= max_fd)
264 max_fd = se->fd + 1;
265 }
267 if (max_fd == -1)
268 {
269 syslog (LOG_WARNING, "No listen sockets found!");
270 return (-1);
271 }
273 status = select (max_fd, &readfds, NULL, NULL, NULL);
275 if ((status == -1) && (errno == EINTR))
276 continue;
277 else if (status == -1)
278 {
279 syslog (LOG_ERR, "select: %s", strerror (errno));
280 return (-1);
281 }
282 else
283 break;
284 } /* while (true) */
286 fd = -1;
287 for (se = socklist_head; se != NULL; se = se->next)
288 if (FD_ISSET (se->fd, &readfds))
289 {
290 fd = se->fd;
291 break;
292 }
294 if (fd == -1)
295 syslog (LOG_WARNING, "No socket ready..?");
297 DBG ("fd = %i", fd);
298 return (fd);
299 }
301 int network_receive (char **host, char **type, char **inst, char **value)
302 {
303 int fd;
304 char buffer[BUFF_SIZE];
306 struct sockaddr_storage addr;
307 int status;
309 char *fields[4];
311 assert (operating_mode == MODE_SERVER);
313 *host = NULL;
314 *type = NULL;
315 *inst = NULL;
316 *value = NULL;
318 if ((fd = network_get_listen_socket ()) < 0)
319 return (-1);
321 if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, sizeof (addr)) == -1)
322 {
323 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
324 return (-1);
325 }
327 if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
328 {
329 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
330 return (-1);
331 }
333 status = getnameinfo ((struct sockaddr *) &addr, sizeof (addr),
334 *host, BUFF_SIZE, NULL, 0, 0);
335 if (status != 0)
336 {
337 free (*host); *host = NULL;
338 syslog (LOG_ERR, "getnameinfo: %s",
339 status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
340 return (-1);
341 }
343 if (strsplit (buffer, fields, 4) != 3)
344 {
345 syslog (LOG_WARNING, "Invalid message from `%s'", *host);
346 free (*host); *host = NULL;
347 return (-1);
348 }
350 if ((*type = strdup (fields[0])) == NULL)
351 {
352 syslog (LOG_EMERG, "strdup: %s", strerror ());
353 free (*host); *host = NULL;
354 return (-1);
355 }
357 if ((*inst = strdup (fields[1])) == NULL)
358 {
359 syslog (LOG_EMERG, "strdup: %s", strerror ());
360 free (*host); *host = NULL;
361 free (*type); *type = NULL;
362 return (-1);
363 }
365 if ((*value = strdup (fields[2])) == NULL)
366 {
367 syslog (LOG_EMERG, "strdup: %s", strerror ());
368 free (*host); *host = NULL;
369 free (*type); *type = NULL;
370 free (*inst); *inst = NULL;
371 return (-1);
372 }
374 DBG ("host = %s, type = %s, inst = %s, value = %s",
375 *host, *type, *inst, *value);
377 return (0);
378 }
380 int network_send (char *type, char *inst, char *value)
381 {
382 char buf[BUFF_SIZE];
383 int buflen;
385 sockent_t *se;
387 int ret;
388 int status;
390 DBG ("type = %s, inst = %s, value = %s", type, inst, value);
392 assert (operating_mode == MODE_CLIENT);
394 buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value);
395 if ((buflen >= BUFF_SIZE) || (buflen < 1))
396 {
397 syslog (LOG_WARNING, "network_send: snprintf failed..");
398 return (-1);
399 }
400 buf[buflen] = '\0';
401 buflen++;
403 ret = 0;
404 for (se = socklist_head; se != NULL; se = se->next)
405 {
406 DBG ("fd = %i", se->fd);
408 while (1)
409 {
410 status = sendto (se->fd, buf, buflen, 0,
411 (struct sockaddr *) se->addr, se->addrlen);
413 if (status == -1)
414 {
415 if (errno == EINTR)
416 {
417 DBG ("sendto was interrupted");
418 continue;
419 }
420 else
421 {
422 syslog (LOG_ERR, "sendto: %s", strerror (errno));
423 break;
424 }
425 }
426 else if (ret >= 0)
427 ret++;
428 break;
429 }
430 }
432 if (ret == 0)
433 syslog (LOG_WARNING, "Message wasn't sent to anybody..");
435 return (ret);
436 }