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>
51 #define AI_ADDRCONFIG 0
54 #include "collectd/network.h"
55 #include "collectd/network_buffer.h"
60 struct lcc_network_s {
61 lcc_server_t *servers;
69 lcc_security_level_t security_level;
77 lcc_network_buffer_t *buffer;
85 static int server_close_socket(lcc_server_t *srv) /* {{{ */
100 } /* }}} int server_close_socket */
102 static void int_server_destroy(lcc_server_t *srv) /* {{{ */
109 server_close_socket(srv);
119 int_server_destroy(next);
120 } /* }}} void int_server_destroy */
122 static int server_open_socket(lcc_server_t *srv) /* {{{ */
124 struct addrinfo *ai_list;
131 server_close_socket(srv);
133 struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
134 .ai_flags = AI_ADDRCONFIG,
135 .ai_socktype = SOCK_DGRAM};
137 status = getaddrinfo(srv->node, srv->service, &ai_hints, &ai_list);
140 assert(ai_list != NULL);
142 for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL;
143 ai_ptr = ai_ptr->ai_next) {
145 socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
149 if (ai_ptr->ai_family == AF_INET) {
150 struct sockaddr_in *addr = (struct sockaddr_in *)ai_ptr->ai_addr;
153 if (IN_MULTICAST(ntohl(addr->sin_addr.s_addr)))
154 optname = IP_MULTICAST_TTL;
159 setsockopt(srv->fd, IPPROTO_IP, optname, &srv->ttl, sizeof(srv->ttl));
160 } else if (ai_ptr->ai_family == AF_INET6) {
162 * http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
163 struct sockaddr_in6 *addr = (struct sockaddr_in6 *)ai_ptr->ai_addr;
166 if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr))
167 optname = IPV6_MULTICAST_HOPS;
169 optname = IPV6_UNICAST_HOPS;
171 status = setsockopt(srv->fd, IPPROTO_IPV6, optname, &srv->ttl,
175 /* setsockopt failed. */
181 srv->sa = malloc(ai_ptr->ai_addrlen);
182 if (srv->sa == NULL) {
188 memcpy(srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
189 srv->sa_len = ai_ptr->ai_addrlen;
193 freeaddrinfo(ai_list);
198 } /* }}} int server_open_socket */
200 static int server_send_buffer(lcc_server_t *srv) /* {{{ */
202 char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT] = {0};
207 status = server_open_socket(srv);
212 buffer_size = sizeof(buffer);
214 status = lcc_network_buffer_finalize(srv->buffer);
216 lcc_network_buffer_initialize(srv->buffer);
220 status = lcc_network_buffer_get(srv->buffer, buffer, &buffer_size);
221 lcc_network_buffer_initialize(srv->buffer);
226 if (buffer_size > sizeof(buffer))
227 buffer_size = sizeof(buffer);
230 assert(srv->fd >= 0);
231 assert(srv->sa != NULL);
232 status = (int)sendto(srv->fd, buffer, buffer_size, /* flags = */ 0, srv->sa,
234 if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
243 } /* }}} int server_send_buffer */
245 static int server_value_add(lcc_server_t *srv, /* {{{ */
246 const lcc_value_list_t *vl) {
249 status = lcc_network_buffer_add_value(srv->buffer, vl);
253 server_send_buffer(srv);
254 return lcc_network_buffer_add_value(srv->buffer, vl);
255 } /* }}} int server_value_add */
260 lcc_network_t *lcc_network_create(void) /* {{{ */
264 net = calloc(1, sizeof(*net));
271 } /* }}} lcc_network_t *lcc_network_create */
273 void lcc_network_destroy(lcc_network_t *net) /* {{{ */
277 int_server_destroy(net->servers);
279 } /* }}} void lcc_network_destroy */
281 lcc_server_t *lcc_server_create(lcc_network_t *net, /* {{{ */
282 const char *node, const char *service) {
285 if ((net == NULL) || (node == NULL))
288 service = NET_DEFAULT_PORT;
290 srv = calloc(1, sizeof(*srv));
295 srv->security_level = NONE;
296 srv->username = NULL;
297 srv->password = NULL;
300 srv->node = strdup(node);
301 if (srv->node == NULL) {
306 srv->service = strdup(service);
307 if (srv->service == NULL) {
313 srv->buffer = lcc_network_buffer_create(/* size = */ 0);
314 if (srv->buffer == NULL) {
321 if (net->servers == NULL) {
324 lcc_server_t *last = net->servers;
326 while (last->next != NULL)
333 } /* }}} lcc_server_t *lcc_server_create */
335 int lcc_server_destroy(lcc_network_t *net, lcc_server_t *srv) /* {{{ */
337 if ((net == NULL) || (srv == NULL))
340 if (net->servers == srv) {
341 net->servers = srv->next;
344 lcc_server_t *prev = net->servers;
346 while ((prev != NULL) && (prev->next != srv))
352 prev->next = srv->next;
356 int_server_destroy(srv);
359 } /* }}} int lcc_server_destroy */
361 int lcc_server_set_ttl(lcc_server_t *srv, uint8_t ttl) /* {{{ */
369 } /* }}} int lcc_server_set_ttl */
371 int lcc_server_set_interface(lcc_server_t *srv, char const *iface) /* {{{ */
373 unsigned int if_index;
376 if ((srv == NULL) || (iface == NULL))
379 if_index = if_nametoindex(iface);
384 if (srv->sa->sa_family == AF_INET) {
385 struct sockaddr_in *addr = (struct sockaddr_in *)srv->sa;
387 if (IN_MULTICAST(ntohl(addr->sin_addr.s_addr))) {
388 #if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
389 /* If possible, use the "ip_mreqn" structure which has
390 * an "interface index" member. Using the interface
391 * index is preferred here, because of its similarity
392 * to the way IPv6 handles this. Unfortunately, it
393 * appears not to be portable. */
394 struct ip_mreqn mreq = {.imr_multiaddr.s_addr = addr->sin_addr.s_addr,
395 .imr_address.s_addr = ntohl(INADDR_ANY),
396 .imr_ifindex = (int)if_index};
398 struct ip_mreq mreq = {.imr_multiaddr.s_addr = addr->sin_addr.s_addr,
399 .imr_interface.s_addr = ntohl(INADDR_ANY)};
403 setsockopt(srv->fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq));
412 if (srv->sa->sa_family == AF_INET6) {
413 struct sockaddr_in6 *addr = (struct sockaddr_in6 *)srv->sa;
415 if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr)) {
416 status = setsockopt(srv->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index,
425 /* else: Not a multicast interface. */
426 #if defined(SO_BINDTODEVICE)
427 status = setsockopt(srv->fd, SOL_SOCKET, SO_BINDTODEVICE, iface,
428 (socklen_t)(strlen(iface) + 1));
434 } /* }}} int lcc_server_set_interface */
436 int lcc_server_set_security_level(lcc_server_t *srv, /* {{{ */
437 lcc_security_level_t level,
438 const char *username, const char *password) {
439 return lcc_network_buffer_set_security_level(srv->buffer, level, username,
441 } /* }}} int lcc_server_set_security_level */
443 int lcc_network_values_send(lcc_network_t *net, /* {{{ */
444 const lcc_value_list_t *vl) {
445 if ((net == NULL) || (vl == NULL))
448 for (lcc_server_t *srv = net->servers; srv != NULL; srv = srv->next)
449 server_value_add(srv, vl);
452 } /* }}} int lcc_network_values_send */