2 * collectd - src/libcollectdclient/network.c
3 * Copyright (C) 2005-2015 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>
39 #include <sys/socket.h>
40 #include <sys/types.h>
43 #include <netinet/in.h>
50 #include "collectd/network.h"
51 #include "collectd/network_buffer.h"
56 struct lcc_network_s {
57 lcc_server_t *servers;
65 lcc_security_level_t security_level;
73 lcc_network_buffer_t *buffer;
81 static int server_close_socket(lcc_server_t *srv) /* {{{ */
96 } /* }}} int server_close_socket */
98 static void int_server_destroy(lcc_server_t *srv) /* {{{ */
105 server_close_socket(srv);
115 int_server_destroy(next);
116 } /* }}} void int_server_destroy */
118 static int server_open_socket(lcc_server_t *srv) /* {{{ */
120 struct addrinfo *ai_list;
127 server_close_socket(srv);
129 struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
130 .ai_flags = AI_ADDRCONFIG,
131 .ai_socktype = SOCK_DGRAM};
133 status = getaddrinfo(srv->node, srv->service, &ai_hints, &ai_list);
136 assert(ai_list != NULL);
138 for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL;
139 ai_ptr = ai_ptr->ai_next) {
141 socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
145 if (ai_ptr->ai_family == AF_INET) {
146 struct sockaddr_in *addr = (struct sockaddr_in *)ai_ptr->ai_addr;
149 if (IN_MULTICAST(ntohl(addr->sin_addr.s_addr)))
150 optname = IP_MULTICAST_TTL;
155 setsockopt(srv->fd, IPPROTO_IP, optname, &srv->ttl, sizeof(srv->ttl));
156 } else if (ai_ptr->ai_family == AF_INET6) {
158 * http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
159 struct sockaddr_in6 *addr = (struct sockaddr_in6 *)ai_ptr->ai_addr;
162 if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr))
163 optname = IPV6_MULTICAST_HOPS;
165 optname = IPV6_UNICAST_HOPS;
167 status = setsockopt(srv->fd, IPPROTO_IPV6, optname, &srv->ttl,
171 /* setsockopt failed. */
177 srv->sa = malloc(ai_ptr->ai_addrlen);
178 if (srv->sa == NULL) {
184 memcpy(srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
185 srv->sa_len = ai_ptr->ai_addrlen;
189 freeaddrinfo(ai_list);
194 } /* }}} int server_open_socket */
196 static int server_send_buffer(lcc_server_t *srv) /* {{{ */
198 char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT] = {0};
203 status = server_open_socket(srv);
208 buffer_size = sizeof(buffer);
210 status = lcc_network_buffer_finalize(srv->buffer);
212 lcc_network_buffer_initialize(srv->buffer);
216 status = lcc_network_buffer_get(srv->buffer, buffer, &buffer_size);
217 lcc_network_buffer_initialize(srv->buffer);
222 if (buffer_size > sizeof(buffer))
223 buffer_size = sizeof(buffer);
226 assert(srv->fd >= 0);
227 assert(srv->sa != NULL);
228 status = (int)sendto(srv->fd, buffer, buffer_size, /* flags = */ 0, srv->sa,
230 if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
239 } /* }}} int server_send_buffer */
241 static int server_value_add(lcc_server_t *srv, /* {{{ */
242 const lcc_value_list_t *vl) {
245 status = lcc_network_buffer_add_value(srv->buffer, vl);
249 server_send_buffer(srv);
250 return lcc_network_buffer_add_value(srv->buffer, vl);
251 } /* }}} int server_value_add */
256 lcc_network_t *lcc_network_create(void) /* {{{ */
260 net = calloc(1, sizeof(*net));
267 } /* }}} lcc_network_t *lcc_network_create */
269 void lcc_network_destroy(lcc_network_t *net) /* {{{ */
273 int_server_destroy(net->servers);
275 } /* }}} void lcc_network_destroy */
277 lcc_server_t *lcc_server_create(lcc_network_t *net, /* {{{ */
278 const char *node, const char *service) {
281 if ((net == NULL) || (node == NULL))
284 service = NET_DEFAULT_PORT;
286 srv = calloc(1, sizeof(*srv));
291 srv->security_level = NONE;
292 srv->username = NULL;
293 srv->password = NULL;
296 srv->node = strdup(node);
297 if (srv->node == NULL) {
302 srv->service = strdup(service);
303 if (srv->service == NULL) {
309 srv->buffer = lcc_network_buffer_create(/* size = */ 0);
310 if (srv->buffer == NULL) {
317 if (net->servers == NULL) {
320 lcc_server_t *last = net->servers;
322 while (last->next != NULL)
329 } /* }}} lcc_server_t *lcc_server_create */
331 int lcc_server_destroy(lcc_network_t *net, lcc_server_t *srv) /* {{{ */
333 if ((net == NULL) || (srv == NULL))
336 if (net->servers == srv) {
337 net->servers = srv->next;
340 lcc_server_t *prev = net->servers;
342 while ((prev != NULL) && (prev->next != srv))
348 prev->next = srv->next;
352 int_server_destroy(srv);
355 } /* }}} int lcc_server_destroy */
357 int lcc_server_set_ttl(lcc_server_t *srv, uint8_t ttl) /* {{{ */
365 } /* }}} int lcc_server_set_ttl */
367 int lcc_server_set_interface(lcc_server_t *srv, char const *interface) /* {{{ */
369 unsigned int if_index;
372 if ((srv == NULL) || (interface == NULL))
375 if_index = if_nametoindex(interface);
380 if (srv->sa->sa_family == AF_INET) {
381 struct sockaddr_in *addr = (struct sockaddr_in *)srv->sa;
383 if (IN_MULTICAST(ntohl(addr->sin_addr.s_addr))) {
384 #if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
385 /* If possible, use the "ip_mreqn" structure which has
386 * an "interface index" member. Using the interface
387 * index is preferred here, because of its similarity
388 * to the way IPv6 handles this. Unfortunately, it
389 * appears not to be portable. */
390 struct ip_mreqn mreq = {.imr_multiaddr.s_addr = addr->sin_addr.s_addr,
391 .imr_address.s_addr = ntohl(INADDR_ANY),
392 .imr_ifindex = (int)if_index};
394 struct ip_mreq mreq = {.imr_multiaddr.s_addr = addr->sin_addr.s_addr,
395 .imr_interface.s_addr = ntohl(INADDR_ANY)};
399 setsockopt(srv->fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq));
408 if (srv->sa->sa_family == AF_INET6) {
409 struct sockaddr_in6 *addr = (struct sockaddr_in6 *)srv->sa;
411 if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr)) {
412 status = setsockopt(srv->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index,
421 /* else: Not a multicast interface. */
422 #if defined(SO_BINDTODEVICE)
423 status = setsockopt(srv->fd, SOL_SOCKET, SO_BINDTODEVICE, interface,
424 (socklen_t)(strlen(interface) + 1));
430 } /* }}} int lcc_server_set_interface */
432 int lcc_server_set_security_level(lcc_server_t *srv, /* {{{ */
433 lcc_security_level_t level,
434 const char *username, const char *password) {
435 return lcc_network_buffer_set_security_level(srv->buffer, level, username,
437 } /* }}} int lcc_server_set_security_level */
439 int lcc_network_values_send(lcc_network_t *net, /* {{{ */
440 const lcc_value_list_t *vl) {
441 if ((net == NULL) || (vl == NULL))
444 for (lcc_server_t *srv = net->servers; srv != NULL; srv = srv->next)
445 server_value_add(srv, vl);
448 } /* }}} int lcc_network_values_send */