2 * collectd - src/libcollectdclient/network.c
3 * Copyright (C) 2005-2014 Florian Forster
4 * Copyright (C) 2010 Max Henkel
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
25 * Florian Forster <octo at collectd.org>
26 * Max Henkel <henkel at gmx.at>
49 #include <sys/types.h>
50 #include <sys/socket.h>
54 # include <netinet/in.h>
63 #include "collectd/network.h"
64 #include "collectd/network_buffer.h"
71 lcc_server_t *servers;
80 lcc_security_level_t security_level;
88 lcc_network_buffer_t *buffer;
96 static int server_close_socket (lcc_server_t *srv) /* {{{ */
110 } /* }}} int server_close_socket */
112 static void int_server_destroy (lcc_server_t *srv) /* {{{ */
119 server_close_socket (srv);
131 free (srv->username);
132 free (srv->password);
135 int_server_destroy (next);
136 } /* }}} void int_server_destroy */
138 static int server_open_socket (lcc_server_t *srv) /* {{{ */
140 struct addrinfo ai_hints = { 0 };
141 struct addrinfo *ai_list = NULL;
142 struct addrinfo *ai_ptr;
149 server_close_socket (srv);
152 ai_hints.ai_flags |= AI_ADDRCONFIG;
154 ai_hints.ai_family = AF_UNSPEC;
155 ai_hints.ai_socktype = SOCK_DGRAM;
157 status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list);
160 assert (ai_list != NULL);
162 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
164 srv->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
168 if (ai_ptr->ai_family == AF_INET)
171 struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
174 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
175 optname = IP_MULTICAST_TTL;
179 setsockopt (srv->fd, IPPROTO_IP, optname,
183 else if (ai_ptr->ai_family == AF_INET6)
185 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
186 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
189 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
190 optname = IPV6_MULTICAST_HOPS;
192 optname = IPV6_UNICAST_HOPS;
194 setsockopt (srv->fd, IPPROTO_IPV6, optname,
199 srv->sa = malloc (ai_ptr->ai_addrlen);
207 memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
208 srv->sa_len = ai_ptr->ai_addrlen;
212 freeaddrinfo (ai_list);
217 } /* }}} int server_open_socket */
219 static int server_send_buffer (lcc_server_t *srv) /* {{{ */
221 char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
227 status = server_open_socket (srv);
232 memset (buffer, 0, sizeof (buffer));
233 buffer_size = sizeof (buffer);
235 lcc_network_buffer_finalize (srv->buffer);
236 status = lcc_network_buffer_get (srv->buffer, buffer, &buffer_size);
237 lcc_network_buffer_initialize (srv->buffer);
242 if (buffer_size > sizeof (buffer))
243 buffer_size = sizeof (buffer);
247 assert (srv->fd >= 0);
248 assert (srv->sa != NULL);
249 status = (int) sendto (srv->fd, buffer, buffer_size, /* flags = */ 0,
250 srv->sa, srv->sa_len);
251 if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
260 } /* }}} int server_send_buffer */
262 static int server_value_add (lcc_server_t *srv, /* {{{ */
263 const lcc_value_list_t *vl)
267 status = lcc_network_buffer_add_value (srv->buffer, vl);
271 server_send_buffer (srv);
272 return (lcc_network_buffer_add_value (srv->buffer, vl));
273 } /* }}} int server_value_add */
278 lcc_network_t *lcc_network_create (void) /* {{{ */
282 net = malloc (sizeof (*net));
285 memset (net, 0, sizeof (*net));
290 } /* }}} lcc_network_t *lcc_network_create */
292 void lcc_network_destroy (lcc_network_t *net) /* {{{ */
296 int_server_destroy (net->servers);
298 } /* }}} void lcc_network_destroy */
300 lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */
301 const char *node, const char *service)
305 if ((net == NULL) || (node == NULL))
308 service = NET_DEFAULT_PORT;
310 srv = malloc (sizeof (*srv));
313 memset (srv, 0, sizeof (*srv));
316 srv->security_level = NONE;
317 srv->username = NULL;
318 srv->password = NULL;
321 srv->node = strdup (node);
322 if (srv->node == NULL)
328 srv->service = strdup (service);
329 if (srv->service == NULL)
336 srv->buffer = lcc_network_buffer_create (/* size = */ 0);
337 if (srv->buffer == NULL)
345 if (net->servers == NULL)
351 lcc_server_t *last = net->servers;
353 while (last->next != NULL)
360 } /* }}} lcc_server_t *lcc_server_create */
362 int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */
364 if ((net == NULL) || (srv == NULL))
367 if (net->servers == srv)
369 net->servers = srv->next;
374 lcc_server_t *prev = net->servers;
376 while ((prev != NULL) && (prev->next != srv))
382 prev->next = srv->next;
386 int_server_destroy (srv);
389 } /* }}} int lcc_server_destroy */
391 int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
396 srv->ttl = (int) ttl;
399 } /* }}} int lcc_server_set_ttl */
402 static unsigned if_nametoindex (LPCSTR pszIfName) /* {{{ */
404 IP_ADAPTER_ADDRESSES *pAddrList;
405 IP_ADAPTER_ADDRESSES *pAddrPtr;
409 unsigned dwIndex = 0;
411 dwSize = (ULONG) sizeof (bBuffer);
412 pAddrList = (IP_ADAPTER_ADDRESSES *) &bBuffer[0];
414 dwStatus = GetAdaptersAddresses (
415 /* Family = */ AF_UNSPEC,
416 /* Flags = */ GAA_FLAG_SKIP_ANYCAST,
417 /* Reserved = */ NULL,
418 /* AdapterAddresses = */ pAddrList,
419 /* SizePointer = */ &dwSize);
420 if (dwStatus != ERROR_SUCCESS)
423 for (pAddrPtr = pAddrList;
425 pAddrPtr = pAddrPtr->Next)
427 if (strcmp (pszIfName, pAddrPtr->AdapterName) != 0)
430 dwIndex = (unsigned) pAddrPtr->IfIndex;
435 } /* }}} unsigned if_nametoindex */
438 int lcc_server_set_interface (lcc_server_t *srv, char const *ifname) /* {{{ */
443 if ((srv == NULL) || (ifname == NULL))
446 if_index = if_nametoindex (ifname);
451 if (srv->sa->sa_family == AF_INET)
453 struct sockaddr_in *addr = (struct sockaddr_in *) srv->sa;
455 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
457 #if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
458 /* If possible, use the "ip_mreqn" structure which has
459 * an "interface index" member. Using the interface
460 * index is preferred here, because of its similarity
461 * to the way IPv6 handles this. Unfortunately, it
462 * appears not to be portable. */
463 struct ip_mreqn mreq;
465 memset (&mreq, 0, sizeof (mreq));
466 mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
467 mreq.imr_address.s_addr = ntohl (INADDR_ANY);
468 mreq.imr_ifindex = if_index;
472 memset (&mreq, 0, sizeof (mreq));
473 mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
474 mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
477 status = setsockopt (srv->fd, IPPROTO_IP, IP_MULTICAST_IF,
478 (void *) &mreq, sizeof (mreq));
487 if (srv->sa->sa_family == AF_INET6)
489 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) srv->sa;
491 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
493 status = setsockopt (srv->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
494 (void *) &if_index, sizeof (if_index));
502 /* else: Not a multicast interface. */
503 #if defined(SO_BINDTODEVICE)
504 status = setsockopt (srv->fd, SOL_SOCKET, SO_BINDTODEVICE,
505 ifname, strlen (ifname) + 1);
511 } /* }}} int lcc_server_set_interface */
513 int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
514 lcc_security_level_t level,
515 const char *username, const char *password)
517 return (lcc_network_buffer_set_security_level (srv->buffer,
518 level, username, password));
519 } /* }}} int lcc_server_set_security_level */
521 int lcc_network_values_send (lcc_network_t *net, /* {{{ */
522 const lcc_value_list_t *vl)
526 if ((net == NULL) || (vl == NULL))
529 for (srv = net->servers; srv != NULL; srv = srv->next)
530 server_value_add (srv, vl);
533 } /* }}} int lcc_network_values_send */
535 /* vim: set sw=2 sts=2 et fdm=marker : */