From: Florian Forster Date: Sat, 17 Nov 2012 07:25:01 +0000 (+0100) Subject: Merge branch 'ff/netlib' X-Git-Tag: collectd-5.2.0~18 X-Git-Url: https://git.octo.it/?p=collectd.git;a=commitdiff_plain;h=3faf514fd9b869cadda0f895e14e5036313c7781;hp=28460d4f235bb0272c98c830d14b9d7a73f812b7 Merge branch 'ff/netlib' Conflicts: src/collectdctl.c src/libcollectdclient/client.c src/libcollectdclient/collectd/client.h --- diff --git a/configure.in b/configure.in index 4a159767..7c4ec3a2 100644 --- a/configure.in +++ b/configure.in @@ -5102,7 +5102,7 @@ AC_SUBST(LCC_VERSION_PATCH) AC_SUBST(LCC_VERSION_EXTRA) AC_SUBST(LCC_VERSION_STRING) -AC_CONFIG_FILES(src/libcollectdclient/lcc_features.h) +AC_CONFIG_FILES(src/libcollectdclient/collectd/lcc_features.h) AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libcollectdclient/Makefile src/libcollectdclient/libcollectdclient.pc src/liboconfig/Makefile bindings/Makefile bindings/java/Makefile) diff --git a/src/Makefile.am b/src/Makefile.am index 58ab17e5..3532e7b2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,7 @@ AM_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"' AM_CPPFLAGS += -DPKGDATADIR='"${pkgdatadir}"' sbin_PROGRAMS = collectd collectdmon -bin_PROGRAMS = collectd-nagios collectdctl +bin_PROGRAMS = collectd-nagios collectdctl collectd-tg collectd_SOURCES = collectd.c collectd.h \ common.c common.h \ @@ -113,6 +113,18 @@ endif collectdctl_LDADD += libcollectdclient/libcollectdclient.la collectdctl_DEPENDENCIES = libcollectdclient/libcollectdclient.la +collectd_tg_SOURCES = collectd-tg.c \ + utils_heap.c utils_heap.h +collectd_tg_LDADD = +if BUILD_WITH_LIBSOCKET +collectd_tg_LDADD += -lsocket +endif +if BUILD_AIX +collectd_tg_LDADD += -lm +endif +collectd_tg_LDADD += libcollectdclient/libcollectdclient.la +collectd_tg_DEPENDENCIES = libcollectdclient/libcollectdclient.la + pkglib_LTLIBRARIES = diff --git a/src/collectd-nagios.c b/src/collectd-nagios.c index 88a53023..e31d95ca 100644 --- a/src/collectd-nagios.c +++ b/src/collectd-nagios.c @@ -66,7 +66,7 @@ # endif #endif /* NAN_ZERO_ZERO */ -#include "libcollectdclient/client.h" +#include "libcollectdclient/collectd/client.h" #define RET_OKAY 0 #define RET_WARNING 1 diff --git a/src/collectd-tg.c b/src/collectd-tg.c new file mode 100644 index 00000000..e5b2d1f1 --- /dev/null +++ b/src/collectd-tg.c @@ -0,0 +1,405 @@ +/** + * collectd-td - collectd traffic generator + * Copyright (C) 2010 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian Forster + **/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef _ISOC99_SOURCE +# define _ISOC99_SOURCE +#endif + +#ifndef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200809L +#endif + +#ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 700 +#endif + +#if !__GNUC__ +# define __attribute__(x) /**/ +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "utils_heap.h" + +#include "libcollectdclient/collectd/client.h" +#include "libcollectdclient/collectd/network.h" +#include "libcollectdclient/collectd/network_buffer.h" + +#define DEF_NUM_HOSTS 1000 +#define DEF_NUM_PLUGINS 20 +#define DEF_NUM_VALUES 100000 +#define DEF_INTERVAL 10 + +static int conf_num_hosts = DEF_NUM_HOSTS; +static int conf_num_plugins = DEF_NUM_PLUGINS; +static int conf_num_values = DEF_NUM_VALUES; +static int conf_interval = DEF_INTERVAL; +static const char *conf_destination = NET_DEFAULT_V6_ADDR; +static const char *conf_service = NET_DEFAULT_PORT; + +static lcc_network_t *net; + +static c_heap_t *values_heap = NULL; + +static struct sigaction sigint_action; +static struct sigaction sigterm_action; + +static _Bool loop = 1; + +__attribute__((noreturn)) +static void exit_usage (int exit_status) /* {{{ */ +{ + fprintf ((exit_status == EXIT_FAILURE) ? stderr : stdout, + "collectd-tg -- collectd traffic generator\n" + "\n" + " Usage: collectd-ng [OPTION]\n" + "\n" + " Valid options:\n" + " -n Number of value lists. (Default: %i)\n" + " -H Number of hosts to emulate. (Default: %i)\n" + " -p Number of plugins to emulate. (Default: %i)\n" + " -i Interval of each value in seconds. (Default: %i)\n" + " -d Destination address of the network packets.\n" + " (Default: %s)\n" + " -D Destination port of the network packets.\n" + " (Default: %s)\n" + " -h Print usage information (this output).\n" + "\n" + "Copyright (C) 2010 Florian Forster\n" + "Licensed under the GNU General Public License, version 2 (GPLv2)\n", + DEF_NUM_VALUES, DEF_NUM_HOSTS, DEF_NUM_PLUGINS, + DEF_INTERVAL, + NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT); + exit (exit_status); +} /* }}} void exit_usage */ + +static void signal_handler (int signal) /* {{{ */ +{ + loop = 0; +} /* }}} void signal_handler */ + +static int compare_time (const void *v0, const void *v1) /* {{{ */ +{ + const lcc_value_list_t *vl0 = v0; + const lcc_value_list_t *vl1 = v1; + + if (vl0->time < vl1->time) + return (-1); + else if (vl0->time > vl1->time) + return (1); + else + return (0); +} /* }}} int compare_time */ + +static int get_boundet_random (int min, int max) /* {{{ */ +{ + int range; + + if (min >= max) + return (-1); + if (min == (max - 1)) + return (min); + + range = max - min; + + return (min + ((int) (((double) range) * ((double) random ()) / (((double) RAND_MAX) + 1.0)))); +} /* }}} int get_boundet_random */ + +static lcc_value_list_t *create_value_list (void) /* {{{ */ +{ + lcc_value_list_t *vl; + int host_num; + + vl = malloc (sizeof (*vl)); + if (vl == NULL) + { + fprintf (stderr, "malloc failed.\n"); + return (NULL); + } + memset (vl, 0, sizeof (*vl)); + + vl->values = calloc (/* nmemb = */ 1, sizeof (*vl->values)); + if (vl->values == NULL) + { + fprintf (stderr, "calloc failed.\n"); + free (vl); + return (NULL); + } + + vl->values_types = calloc (/* nmemb = */ 1, sizeof (*vl->values_types)); + if (vl->values_types == NULL) + { + fprintf (stderr, "calloc failed.\n"); + free (vl->values); + free (vl); + return (NULL); + } + + vl->values_len = 1; + + host_num = get_boundet_random (0, conf_num_hosts); + + vl->interval = conf_interval; + vl->time = time (NULL) + (host_num % vl->interval) + 1; + + if (get_boundet_random (0, 2) == 0) + vl->values_types[0] = LCC_TYPE_GAUGE; + else + vl->values_types[0] = LCC_TYPE_DERIVE; + + snprintf (vl->identifier.host, sizeof (vl->identifier.host), + "host%04i", host_num); + snprintf (vl->identifier.plugin, sizeof (vl->identifier.plugin), + "plugin%03i", get_boundet_random (0, conf_num_plugins)); + strncpy (vl->identifier.type, + (vl->values_types[0] == LCC_TYPE_GAUGE) ? "gauge" : "derive", + sizeof (vl->identifier.type)); + snprintf (vl->identifier.type_instance, sizeof (vl->identifier.type_instance), + "ti%li", random ()); + + return (vl); +} /* }}} int create_value_list */ + +static void destroy_value_list (lcc_value_list_t *vl) /* {{{ */ +{ + if (vl == NULL) + return; + + free (vl->values); + free (vl->values_types); + free (vl); +} /* }}} void destroy_value_list */ + +static int send_value (lcc_value_list_t *vl) /* {{{ */ +{ + int status; + + if (vl->values_types[0] == LCC_TYPE_GAUGE) + vl->values[0].gauge = 100.0 * ((gauge_t) random ()) / (((gauge_t) RAND_MAX) + 1.0); + else + vl->values[0].derive += get_boundet_random (0, 100); + + status = lcc_network_values_send (net, vl); + if (status != 0) + fprintf (stderr, "lcc_network_values_send failed with status %i.\n", status); + + vl->time += vl->interval; + + return (0); +} /* }}} int send_value */ + +static int get_integer_opt (const char *str, int *ret_value) /* {{{ */ +{ + char *endptr; + int tmp; + + errno = 0; + endptr = NULL; + tmp = (int) strtol (str, &endptr, /* base = */ 0); + if (errno != 0) + { + fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n", + str, strerror (errno)); + exit (EXIT_FAILURE); + } + else if (endptr == str) + { + fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str); + exit (EXIT_FAILURE); + } + else if (*endptr != 0) + { + fprintf (stderr, "Garbage after end of value: \"%s\"\n", str); + exit (EXIT_FAILURE); + } + + *ret_value = tmp; + return (0); +} /* }}} int get_integer_opt */ + +static int read_options (int argc, char **argv) /* {{{ */ +{ + int opt; + + while ((opt = getopt (argc, argv, "n:H:p:i:d:D:h")) != -1) + { + switch (opt) + { + case 'n': + get_integer_opt (optarg, &conf_num_values); + break; + + case 'H': + get_integer_opt (optarg, &conf_num_hosts); + break; + + case 'p': + get_integer_opt (optarg, &conf_num_plugins); + break; + + case 'i': + get_integer_opt (optarg, &conf_interval); + break; + + case 'd': + conf_destination = optarg; + break; + + case 'D': + conf_service = optarg; + break; + + case 'h': + exit_usage (EXIT_SUCCESS); + + default: + exit_usage (EXIT_FAILURE); + } /* switch (opt) */ + } /* while (getopt) */ + + return (0); +} /* }}} int read_options */ + +int main (int argc, char **argv) /* {{{ */ +{ + int i; + time_t last_time; + int values_sent = 0; + + read_options (argc, argv); + + sigint_action.sa_handler = signal_handler; + sigaction (SIGINT, &sigint_action, /* old = */ NULL); + + sigterm_action.sa_handler = signal_handler; + sigaction (SIGTERM, &sigterm_action, /* old = */ NULL); + + + values_heap = c_heap_create (compare_time); + if (values_heap == NULL) + { + fprintf (stderr, "c_heap_create failed.\n"); + exit (EXIT_FAILURE); + } + + net = lcc_network_create (); + if (net == NULL) + { + fprintf (stderr, "lcc_network_create failed.\n"); + exit (EXIT_FAILURE); + } + else + { + lcc_server_t *srv; + + srv = lcc_server_create (net, conf_destination, conf_service); + if (srv == NULL) + { + fprintf (stderr, "lcc_server_create failed.\n"); + exit (EXIT_FAILURE); + } + + lcc_server_set_ttl (srv, 42); +#if 0 + lcc_server_set_security_level (srv, ENCRYPT, + "admin", "password1"); +#endif + } + + fprintf (stdout, "Creating %i values ... ", conf_num_values); + fflush (stdout); + for (i = 0; i < conf_num_values; i++) + { + lcc_value_list_t *vl; + + vl = create_value_list (); + if (vl == NULL) + { + fprintf (stderr, "create_value_list failed.\n"); + exit (EXIT_FAILURE); + } + + c_heap_insert (values_heap, vl); + } + fprintf (stdout, "done\n"); + + last_time = 0; + while (loop) + { + lcc_value_list_t *vl = c_heap_get_root (values_heap); + + if (vl == NULL) + break; + + if (vl->time != last_time) + { + printf ("%i values have been sent.\n", values_sent); + + /* Check if we need to sleep */ + time_t now = time (NULL); + + while (now < vl->time) + { + /* 1 / 100 second */ + struct timespec ts = { 0, 10000000 }; + nanosleep (&ts, /* remaining = */ NULL); + now = time (NULL); + + if (!loop) + break; + } + last_time = vl->time; + } + + send_value (vl); + values_sent++; + + c_heap_insert (values_heap, vl); + } + + fprintf (stdout, "Shutting down.\n"); + fflush (stdout); + + while (42) + { + lcc_value_list_t *vl = c_heap_get_root (values_heap); + if (vl == NULL) + break; + destroy_value_list (vl); + } + c_heap_destroy (values_heap); + + lcc_network_destroy (net); + exit (EXIT_SUCCESS); + return (0); +} /* }}} int main */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/collectdctl.c b/src/collectdctl.c index 61b7fc55..0b8d0c1e 100644 --- a/src/collectdctl.c +++ b/src/collectdctl.c @@ -65,7 +65,7 @@ # endif #endif /* NAN_ZERO_ZERO */ -#include "libcollectdclient/client.h" +#include "libcollectdclient/collectd/client.h" #define DEFAULT_SOCK LOCALSTATEDIR"/run/"PACKAGE_NAME"-unixsock" diff --git a/src/libcollectdclient/Makefile.am b/src/libcollectdclient/Makefile.am index 60441452..baf7a21b 100644 --- a/src/libcollectdclient/Makefile.am +++ b/src/libcollectdclient/Makefile.am @@ -4,11 +4,18 @@ if COMPILER_IS_GCC AM_CFLAGS = -Wall -Werror endif -pkginclude_HEADERS = client.h lcc_features.h +pkginclude_HEADERS = collectd/client.h collectd/network_buffer.h collectd/lcc_features.h lib_LTLIBRARIES = libcollectdclient.la nodist_pkgconfig_DATA = libcollectdclient.pc -BUILT_SOURCES = lcc_features.h +BUILT_SOURCES = collectd/lcc_features.h -libcollectdclient_la_SOURCES = client.c +libcollectdclient_la_SOURCES = client.c network.c network_buffer.c +libcollectdclient_la_CPPFLAGS = $(AM_CPPFLAGS) libcollectdclient_la_LDFLAGS = -version-info 0:0:0 +libcollectdclient_la_LIBADD = +if BUILD_WITH_LIBGCRYPT +libcollectdclient_la_CPPFLAGS += $(GCRYPT_CPPFLAGS) +libcollectdclient_la_LDFLAGS += $(GCRYPT_LDFLAGS) +libcollectdclient_la_LIBADD += $(GCRYPT_LIBS) +endif diff --git a/src/libcollectdclient/client.c b/src/libcollectdclient/client.c index 2adf6d48..39875903 100644 --- a/src/libcollectdclient/client.c +++ b/src/libcollectdclient/client.c @@ -27,7 +27,7 @@ # define __attribute__(x) /**/ #endif -#include "lcc_features.h" +#include "collectd/lcc_features.h" #include #include @@ -41,7 +41,7 @@ #include #include -#include "client.h" +#include "collectd/client.h" /* NI_MAXHOST has been obsoleted by RFC 3493 which is a reason for SunOS 5.11 * to no longer define it. We'll use the old, RFC 2553 value here. */ @@ -228,53 +228,6 @@ static void lcc_chomp (char *str) /* {{{ */ } } /* }}} void lcc_chomp */ -static int lcc_identifier_cmp (const void *a, const void *b) -{ - const lcc_identifier_t *ident_a, *ident_b; - - int status; - - ident_a = a; - ident_b = b; - - status = strcasecmp (ident_a->host, ident_b->host); - if (status != 0) - return (status); - - status = strcmp (ident_a->plugin, ident_b->plugin); - if (status != 0) - return (status); - - if ((*ident_a->plugin_instance != '\0') || (*ident_b->plugin_instance != '\0')) - { - if (*ident_a->plugin_instance == '\0') - return (-1); - else if (*ident_b->plugin_instance == '\0') - return (1); - - status = strcmp (ident_a->plugin_instance, ident_b->plugin_instance); - if (status != 0) - return (status); - } - - status = strcmp (ident_a->type, ident_b->type); - if (status != 0) - return (status); - - if ((*ident_a->type_instance != '\0') || (*ident_b->type_instance != '\0')) - { - if (*ident_a->type_instance == '\0') - return (-1); - else if (*ident_b->type_instance == '\0') - return (1); - - status = strcmp (ident_a->type_instance, ident_b->type_instance); - if (status != 0) - return (status); - } - return (0); -} /* }}} int lcc_identifier_cmp */ - static void lcc_response_free (lcc_response_t *res) /* {{{ */ { size_t i; @@ -1105,6 +1058,35 @@ int lcc_string_to_identifier (lcc_connection_t *c, /* {{{ */ return (0); } /* }}} int lcc_string_to_identifier */ +int lcc_identifier_compare (const lcc_identifier_t *i0, /* {{{ */ + const lcc_identifier_t *i1) +{ + int status; + + if ((i0 == NULL) && (i1 == NULL)) + return (0); + else if (i0 == NULL) + return (-1); + else if (i1 == NULL) + return (1); + +#define CMP_FIELD(f) do { \ + status = strcmp (i0->f, i1->f); \ + if (status != 0) \ + return (status); \ +} while (0); + + CMP_FIELD (host); + CMP_FIELD (plugin); + CMP_FIELD (plugin_instance); + CMP_FIELD (type); + CMP_FIELD (type_instance); + +#undef CMP_FIELD + + return (0); +} /* }}} int lcc_identifier_compare */ + int lcc_sort_identifiers (lcc_connection_t *c, /* {{{ */ lcc_identifier_t *idents, size_t idents_num) { @@ -1114,7 +1096,8 @@ int lcc_sort_identifiers (lcc_connection_t *c, /* {{{ */ return (-1); } - qsort (idents, idents_num, sizeof (*idents), lcc_identifier_cmp); + qsort (idents, idents_num, sizeof (*idents), + (void *) lcc_identifier_compare); return (0); } /* }}} int lcc_sort_identifiers */ diff --git a/src/libcollectdclient/client.h b/src/libcollectdclient/client.h deleted file mode 100644 index 36b1d4d2..00000000 --- a/src/libcollectdclient/client.h +++ /dev/null @@ -1,125 +0,0 @@ -/** - * libcollectdclient - src/libcollectdclient/client.h - * Copyright (C) 2008 Florian octo Forster - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; only version 2 of the License is applicable. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: - * Florian octo Forster - **/ - -#ifndef LIBCOLLECTD_COLLECTDCLIENT_H -#define LIBCOLLECTD_COLLECTDCLIENT_H 1 - -#include "lcc_features.h" - -/* - * Includes (for data types) - */ -#if HAVE_STDINT_H -# include -#endif -#include -#include - -/* - * Defines - */ -#define LCC_NAME_LEN 64 -#define LCC_DEFAULT_PORT "25826" - -/* - * Types - */ -#define LCC_TYPE_COUNTER 0 -#define LCC_TYPE_GAUGE 1 -#define LCC_TYPE_DERIVE 2 -#define LCC_TYPE_ABSOLUTE 3 - -LCC_BEGIN_DECLS - -typedef uint64_t counter_t; -typedef double gauge_t; -typedef uint64_t derive_t; -typedef uint64_t absolute_t; - -union value_u -{ - counter_t counter; - gauge_t gauge; - derive_t derive; - absolute_t absolute; -}; -typedef union value_u value_t; - -struct lcc_identifier_s -{ - char host[LCC_NAME_LEN]; - char plugin[LCC_NAME_LEN]; - char plugin_instance[LCC_NAME_LEN]; - char type[LCC_NAME_LEN]; - char type_instance[LCC_NAME_LEN]; -}; -typedef struct lcc_identifier_s lcc_identifier_t; -#define LCC_IDENTIFIER_INIT { "localhost", "", "", "", "" } - -struct lcc_value_list_s -{ - value_t *values; - int *values_types; - size_t values_len; - time_t time; - int interval; - lcc_identifier_t identifier; -}; -typedef struct lcc_value_list_s lcc_value_list_t; -#define LCC_VALUE_LIST_INIT { NULL, NULL, 0, 0, 0, LCC_IDENTIFIER_INIT } - -struct lcc_connection_s; -typedef struct lcc_connection_s lcc_connection_t; - -/* - * Functions - */ -int lcc_connect (const char *address, lcc_connection_t **ret_con); -int lcc_disconnect (lcc_connection_t *c); -#define LCC_DESTROY(c) do { lcc_disconnect (c); (c) = NULL; } while (0) - -int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, - size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names); - -int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl); - -int lcc_flush (lcc_connection_t *c, const char *plugin, - lcc_identifier_t *ident, int timeout); - -int lcc_listval (lcc_connection_t *c, - lcc_identifier_t **ret_ident, size_t *ret_ident_num); - -/* TODO: putnotif */ - -const char *lcc_strerror (lcc_connection_t *c); - -int lcc_identifier_to_string (lcc_connection_t *c, - char *string, size_t string_size, const lcc_identifier_t *ident); -int lcc_string_to_identifier (lcc_connection_t *c, - lcc_identifier_t *ident, const char *string); - -int lcc_sort_identifiers (lcc_connection_t *c, - lcc_identifier_t *idents, size_t idents_num); - -LCC_END_DECLS - -/* vim: set sw=2 sts=2 et : */ -#endif /* LIBCOLLECTD_COLLECTDCLIENT_H */ diff --git a/src/libcollectdclient/collectd/client.h b/src/libcollectdclient/collectd/client.h new file mode 100644 index 00000000..b2354ff4 --- /dev/null +++ b/src/libcollectdclient/collectd/client.h @@ -0,0 +1,130 @@ +/** + * libcollectdclient - src/libcollectdclient/client.h + * Copyright (C) 2008 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#ifndef LIBCOLLECTD_COLLECTDCLIENT_H +#define LIBCOLLECTD_COLLECTDCLIENT_H 1 + +#include "lcc_features.h" + +/* + * Includes (for data types) + */ +#if HAVE_STDINT_H +# include +#endif +#include +#include + +/* + * Defines + */ +#define LCC_NAME_LEN 64 +#define LCC_DEFAULT_PORT "25826" + +/* + * Types + */ +#define LCC_TYPE_COUNTER 0 +#define LCC_TYPE_GAUGE 1 +#define LCC_TYPE_DERIVE 2 +#define LCC_TYPE_ABSOLUTE 3 + +LCC_BEGIN_DECLS + +typedef uint64_t counter_t; +typedef double gauge_t; +typedef uint64_t derive_t; +typedef uint64_t absolute_t; + +union value_u +{ + counter_t counter; + gauge_t gauge; + derive_t derive; + absolute_t absolute; +}; +typedef union value_u value_t; + +struct lcc_identifier_s +{ + char host[LCC_NAME_LEN]; + char plugin[LCC_NAME_LEN]; + char plugin_instance[LCC_NAME_LEN]; + char type[LCC_NAME_LEN]; + char type_instance[LCC_NAME_LEN]; +}; +typedef struct lcc_identifier_s lcc_identifier_t; +#define LCC_IDENTIFIER_INIT { "localhost", "", "", "", "" } + +struct lcc_value_list_s +{ + value_t *values; + int *values_types; + size_t values_len; + time_t time; + int interval; + lcc_identifier_t identifier; +}; +typedef struct lcc_value_list_s lcc_value_list_t; +#define LCC_VALUE_LIST_INIT { NULL, NULL, 0, 0, 0, LCC_IDENTIFIER_INIT } + +struct lcc_connection_s; +typedef struct lcc_connection_s lcc_connection_t; + +/* + * Functions + */ +int lcc_connect (const char *address, lcc_connection_t **ret_con); +int lcc_disconnect (lcc_connection_t *c); +#define LCC_DESTROY(c) do { lcc_disconnect (c); (c) = NULL; } while (0) + +int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, + size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names); + +int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl); + +int lcc_flush (lcc_connection_t *c, const char *plugin, + lcc_identifier_t *ident, int timeout); + +int lcc_listval (lcc_connection_t *c, + lcc_identifier_t **ret_ident, size_t *ret_ident_num); + +/* TODO: putnotif */ + +const char *lcc_strerror (lcc_connection_t *c); + +int lcc_identifier_to_string (lcc_connection_t *c, + char *string, size_t string_size, const lcc_identifier_t *ident); +int lcc_string_to_identifier (lcc_connection_t *c, + lcc_identifier_t *ident, const char *string); + +/* Compares the identifiers "i0" and "i1" and returns less than zero or greater + * than zero if "i0" is smaller than or greater than "i1", respectively. If + * "i0" and "i1" are identical, zero is returned. */ +int lcc_identifier_compare (const lcc_identifier_t *i0, + const lcc_identifier_t *i1); +int lcc_sort_identifiers (lcc_connection_t *c, + lcc_identifier_t *idents, size_t idents_num); + +LCC_END_DECLS + +/* vim: set sw=2 sts=2 et : */ +#endif /* LIBCOLLECTD_COLLECTDCLIENT_H */ diff --git a/src/libcollectdclient/collectd/lcc_features.h.in b/src/libcollectdclient/collectd/lcc_features.h.in new file mode 100644 index 00000000..3916a179 --- /dev/null +++ b/src/libcollectdclient/collectd/lcc_features.h.in @@ -0,0 +1,62 @@ +/** + * libcollectdclient - src/libcollectdclient/lcc_features.h + * Copyright (C) 2009 Sebastian Harl + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Sebastian tokkee Harl + **/ + +#ifndef LIBCOLLECTD_LCC_FEATURES_H +#define LIBCOLLECTD_LCC_FEATURES_H 1 + +#ifdef __cplusplus +# define LCC_BEGIN_DECLS extern "C" { +# define LCC_END_DECLS } +#else +# define LCC_BEGIN_DECLS +# define LCC_END_DECLS +#endif + +#define LCC_API_VERSION 0 + +#define LCC_VERSION_MAJOR @LCC_VERSION_MAJOR@ +#define LCC_VERSION_MINOR @LCC_VERSION_MINOR@ +#define LCC_VERSION_PATCH @LCC_VERSION_PATCH@ + +#define LCC_VERSION_EXTRA "@LCC_VERSION_EXTRA@" + +#define LCC_VERSION_STRING "@LCC_VERSION_STRING@" + +#define LCC_VERSION_ENCODE(major, minor, patch) \ + ((major) * 10000 + (minor) * 100 + (patch)) + +#define LCC_VERSION \ + LCC_VERSION_ENCODE(LCC_VERSION_MAJOR, LCC_VERSION_MINOR, LCC_VERSION_PATCH) + +LCC_BEGIN_DECLS + +unsigned int lcc_version (void); + +const char *lcc_version_string (void); + +const char *lcc_version_extra (void); + +LCC_END_DECLS + +#endif /* ! LIBCOLLECTD_LCC_FEATURES_H */ + +/* vim: set sw=4 ts=4 tw=78 noexpandtab : */ + diff --git a/src/libcollectdclient/collectd/network.h b/src/libcollectdclient/collectd/network.h new file mode 100644 index 00000000..ff29b9a9 --- /dev/null +++ b/src/libcollectdclient/collectd/network.h @@ -0,0 +1,79 @@ +/** + * collectd - src/libcollectdclient/network.h + * Copyright (C) 2005-2010 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; only version 2.1 of the License is + * applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#ifndef LIBCOLLECTDCLIENT_NETWORK_H +#define LIBCOLLECTDCLIENT_NETWORK_H 1 + +#include +#include + +#include "client.h" + +#define NET_DEFAULT_V4_ADDR "239.192.74.66" +#define NET_DEFAULT_V6_ADDR "ff18::efc0:4a42" +#define NET_DEFAULT_PORT "25826" + +struct lcc_network_s; +typedef struct lcc_network_s lcc_network_t; + +struct lcc_server_s; +typedef struct lcc_server_s lcc_server_t; + +enum lcc_security_level_e +{ + NONE, + SIGN, + ENCRYPT +}; +typedef enum lcc_security_level_e lcc_security_level_t; + +/* + * Create / destroy object + */ +lcc_network_t *lcc_network_create (void); +void lcc_network_destroy (lcc_network_t *net); + +/* + * Add servers + */ +lcc_server_t *lcc_server_create (lcc_network_t *net, + const char *node, const char *service); +int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv); + +/* Configure servers */ +int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl); +int lcc_server_set_security_level (lcc_server_t *srv, + lcc_security_level_t level, + const char *username, const char *password); + +/* + * Send data + */ +int lcc_network_values_send (lcc_network_t *net, + const lcc_value_list_t *vl); +#if 0 +int lcc_network_notification_send (lcc_network_t *net, + const lcc_notification_t *notif); +#endif + +/* vim: set sw=2 sts=2 et : */ +#endif /* LIBCOLLECTDCLIENT_NETWORK_H */ diff --git a/src/libcollectdclient/collectd/network_buffer.h b/src/libcollectdclient/collectd/network_buffer.h new file mode 100644 index 00000000..76ef1bf1 --- /dev/null +++ b/src/libcollectdclient/collectd/network_buffer.h @@ -0,0 +1,53 @@ +/** + * collectd - src/libcollectdclient/network_buffer.h + * Copyright (C) 2010 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; only version 2.1 of the License is + * applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#ifndef LIBCOLLECTDCLIENT_NETWORK_BUFFER_H +#define LIBCOLLECTDCLIENT_NETWORK_BUFFER_H 1 + +/* FIXME */ +#include "client.h" +#include "network.h" + +/* Ethernet frame - (IPv6 header + UDP header) */ +#define LCC_NETWORK_BUFFER_SIZE_DEFAULT 1452 + +struct lcc_network_buffer_s; +typedef struct lcc_network_buffer_s lcc_network_buffer_t; + +lcc_network_buffer_t *lcc_network_buffer_create (size_t size); +void lcc_network_buffer_destroy (lcc_network_buffer_t *nb); + +int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, + lcc_security_level_t level, + const char *user, const char *password); + +int lcc_network_buffer_initialize (lcc_network_buffer_t *nb); +int lcc_network_buffer_finalize (lcc_network_buffer_t *nb); + +int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, + const lcc_value_list_t *vl); + +int lcc_network_buffer_get (lcc_network_buffer_t *nb, + void *buffer, size_t *buffer_size); + +#endif /* LIBCOLLECTDCLIENT_NETWORK_BUFFER_H */ +/* vim: set sw=2 sts=2 et : */ diff --git a/src/libcollectdclient/lcc_features.h.in b/src/libcollectdclient/lcc_features.h.in deleted file mode 100644 index 3916a179..00000000 --- a/src/libcollectdclient/lcc_features.h.in +++ /dev/null @@ -1,62 +0,0 @@ -/** - * libcollectdclient - src/libcollectdclient/lcc_features.h - * Copyright (C) 2009 Sebastian Harl - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; only version 2 of the License is applicable. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: - * Sebastian tokkee Harl - **/ - -#ifndef LIBCOLLECTD_LCC_FEATURES_H -#define LIBCOLLECTD_LCC_FEATURES_H 1 - -#ifdef __cplusplus -# define LCC_BEGIN_DECLS extern "C" { -# define LCC_END_DECLS } -#else -# define LCC_BEGIN_DECLS -# define LCC_END_DECLS -#endif - -#define LCC_API_VERSION 0 - -#define LCC_VERSION_MAJOR @LCC_VERSION_MAJOR@ -#define LCC_VERSION_MINOR @LCC_VERSION_MINOR@ -#define LCC_VERSION_PATCH @LCC_VERSION_PATCH@ - -#define LCC_VERSION_EXTRA "@LCC_VERSION_EXTRA@" - -#define LCC_VERSION_STRING "@LCC_VERSION_STRING@" - -#define LCC_VERSION_ENCODE(major, minor, patch) \ - ((major) * 10000 + (minor) * 100 + (patch)) - -#define LCC_VERSION \ - LCC_VERSION_ENCODE(LCC_VERSION_MAJOR, LCC_VERSION_MINOR, LCC_VERSION_PATCH) - -LCC_BEGIN_DECLS - -unsigned int lcc_version (void); - -const char *lcc_version_string (void); - -const char *lcc_version_extra (void); - -LCC_END_DECLS - -#endif /* ! LIBCOLLECTD_LCC_FEATURES_H */ - -/* vim: set sw=4 ts=4 tw=78 noexpandtab : */ - diff --git a/src/libcollectdclient/network.c b/src/libcollectdclient/network.c new file mode 100644 index 00000000..fe1d4201 --- /dev/null +++ b/src/libcollectdclient/network.c @@ -0,0 +1,394 @@ +/** + * collectd - src/libcollectdclient/network.h + * Copyright (C) 2005-2010 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; only version 2.1 of the License is + * applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "collectd/network.h" +#include "collectd/network_buffer.h" + +/* + * Private data types + */ +struct lcc_network_s +{ + lcc_server_t *servers; +}; + +struct lcc_server_s +{ + char *node; + char *service; + + int ttl; + lcc_security_level_t security_level; + char *username; + char *password; + + int fd; + struct sockaddr *sa; + socklen_t sa_len; + + lcc_network_buffer_t *buffer; + + lcc_server_t *next; +}; + +/* + * Private functions + */ +static int server_close_socket (lcc_server_t *srv) /* {{{ */ +{ + if (srv == NULL) + return (EINVAL); + + if (srv->fd < 0) + return (0); + + close (srv->fd); + free (srv->sa); + srv->sa = NULL; + srv->sa_len = 0; + + return (0); +} /* }}} int server_close_socket */ + +static void int_server_destroy (lcc_server_t *srv) /* {{{ */ +{ + lcc_server_t *next; + + if (srv == NULL) + return; + + server_close_socket (srv); + + next = srv->next; + + if (srv->fd >= 0) + { + close (srv->fd); + srv->fd = -1; + } + + free (srv->node); + free (srv->service); + free (srv->username); + free (srv->password); + free (srv); + + int_server_destroy (next); +} /* }}} void int_server_destroy */ + +static int server_open_socket (lcc_server_t *srv) /* {{{ */ +{ + struct addrinfo ai_hints = { 0 }; + struct addrinfo *ai_list = NULL; + struct addrinfo *ai_ptr; + int status; + + if (srv == NULL) + return (EINVAL); + + if (srv->fd >= 0) + server_close_socket (srv); + +#ifdef AI_ADDRCONFIG + ai_hints.ai_flags |= AI_ADDRCONFIG; +#endif + ai_hints.ai_family = AF_UNSPEC; + ai_hints.ai_socktype = SOCK_DGRAM; + + status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list); + if (status != 0) + return (status); + assert (ai_list != NULL); + + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) + { + srv->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); + if (srv->fd < 0) + continue; + + if (ai_ptr->ai_family == AF_INET) + { + + struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr; + int optname; + + if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr))) + optname = IP_MULTICAST_TTL; + else + optname = IP_TTL; + + setsockopt (srv->fd, IPPROTO_IP, optname, + &srv->ttl, + sizeof (srv->ttl)); + } + else if (ai_ptr->ai_family == AF_INET6) + { + /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */ + struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr; + int optname; + + if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr)) + optname = IPV6_MULTICAST_HOPS; + else + optname = IPV6_UNICAST_HOPS; + + setsockopt (srv->fd, IPPROTO_IPV6, optname, + &srv->ttl, + sizeof (srv->ttl)); + } + + srv->sa = malloc (ai_ptr->ai_addrlen); + if (srv->sa == NULL) + { + close (srv->fd); + srv->fd = -1; + continue; + } + + memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen); + srv->sa_len = ai_ptr->ai_addrlen; + break; + } + + freeaddrinfo (ai_list); + + if (srv->fd < 0) + return (-1); + return (0); +} /* }}} int server_open_socket */ + +static int server_send_buffer (lcc_server_t *srv) /* {{{ */ +{ + char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT]; + size_t buffer_size; + int status; + + if (srv->fd < 0) + { + status = server_open_socket (srv); + if (status != 0) + return (status); + } + + memset (buffer, 0, sizeof (buffer)); + buffer_size = sizeof (buffer); + + lcc_network_buffer_finalize (srv->buffer); + status = lcc_network_buffer_get (srv->buffer, buffer, &buffer_size); + lcc_network_buffer_initialize (srv->buffer); + + if (status != 0) + return (status); + + if (buffer_size > sizeof (buffer)) + buffer_size = sizeof (buffer); + + while (42) + { + assert (srv->fd >= 0); + assert (srv->sa != NULL); + status = (int) sendto (srv->fd, buffer, buffer_size, /* flags = */ 0, + srv->sa, srv->sa_len); + if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN))) + continue; + + break; + } + + if (status < 0) + return (status); + return (0); +} /* }}} int server_send_buffer */ + +static int server_value_add (lcc_server_t *srv, /* {{{ */ + const lcc_value_list_t *vl) +{ + int status; + + status = lcc_network_buffer_add_value (srv->buffer, vl); + if (status == 0) + return (0); + + server_send_buffer (srv); + return (lcc_network_buffer_add_value (srv->buffer, vl)); +} /* }}} int server_value_add */ + +/* + * Public functions + */ +lcc_network_t *lcc_network_create (void) /* {{{ */ +{ + lcc_network_t *net; + + net = malloc (sizeof (*net)); + if (net == NULL) + return (NULL); + memset (net, 0, sizeof (*net)); + + net->servers = NULL; + + return (net); +} /* }}} lcc_network_t *lcc_network_create */ + +void lcc_network_destroy (lcc_network_t *net) /* {{{ */ +{ + if (net == NULL) + return; + int_server_destroy (net->servers); + free (net); +} /* }}} void lcc_network_destroy */ + +lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */ + const char *node, const char *service) +{ + lcc_server_t *srv; + + if ((net == NULL) || (node == NULL)) + return (NULL); + if (service == NULL) + service = NET_DEFAULT_PORT; + + srv = malloc (sizeof (*srv)); + if (srv == NULL) + return (NULL); + memset (srv, 0, sizeof (*srv)); + + srv->fd = -1; + srv->security_level = NONE; + srv->username = NULL; + srv->password = NULL; + srv->next = NULL; + + srv->node = strdup (node); + if (srv->node == NULL) + { + free (srv); + return (NULL); + } + + srv->service = strdup (service); + if (srv->service == NULL) + { + free (srv->node); + free (srv); + return (NULL); + } + + srv->buffer = lcc_network_buffer_create (/* size = */ 0); + if (srv->buffer == NULL) + { + free (srv->service); + free (srv->node); + free (srv); + return (NULL); + } + + if (net->servers == NULL) + { + net->servers = srv; + } + else + { + lcc_server_t *last = net->servers; + + while (last->next != NULL) + last = last->next; + + last->next = srv; + } + + return (srv); +} /* }}} lcc_server_t *lcc_server_create */ + +int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */ +{ + if ((net == NULL) || (srv == NULL)) + return (EINVAL); + + if (net->servers == srv) + { + net->servers = srv->next; + srv->next = NULL; + } + else + { + lcc_server_t *prev = net->servers; + + while ((prev != NULL) && (prev->next != srv)) + prev = prev->next; + + if (prev == NULL) + return (ENOENT); + + prev->next = srv->next; + srv->next = NULL; + } + + int_server_destroy (srv); + + return (0); +} /* }}} int lcc_server_destroy */ + +int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */ +{ + if (srv == NULL) + return (EINVAL); + + srv->ttl = (int) ttl; + + return (0); +} /* }}} int lcc_server_set_ttl */ + +int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */ + lcc_security_level_t level, + const char *username, const char *password) +{ + return (lcc_network_buffer_set_security_level (srv->buffer, + level, username, password)); +} /* }}} int lcc_server_set_security_level */ + +int lcc_network_values_send (lcc_network_t *net, /* {{{ */ + const lcc_value_list_t *vl) +{ + lcc_server_t *srv; + + if ((net == NULL) || (vl == NULL)) + return (EINVAL); + + for (srv = net->servers; srv != NULL; srv = srv->next) + server_value_add (srv, vl); + + return (0); +} /* }}} int lcc_network_values_send */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/libcollectdclient/network_buffer.c b/src/libcollectdclient/network_buffer.c new file mode 100644 index 00000000..e7b43ef7 --- /dev/null +++ b/src/libcollectdclient/network_buffer.c @@ -0,0 +1,788 @@ +/** + * collectd - src/libcollectdclient/network_buffer.c + * Copyright (C) 2010 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; only version 2.1 of the License is + * applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include /* htons */ + +#include + +#if HAVE_LIBGCRYPT +#include +GCRY_THREAD_OPTION_PTHREAD_IMPL; +#endif + +#include "collectd/network_buffer.h" + +#define TYPE_HOST 0x0000 +#define TYPE_TIME 0x0001 +#define TYPE_PLUGIN 0x0002 +#define TYPE_PLUGIN_INSTANCE 0x0003 +#define TYPE_TYPE 0x0004 +#define TYPE_TYPE_INSTANCE 0x0005 +#define TYPE_VALUES 0x0006 +#define TYPE_INTERVAL 0x0007 + +/* Types to transmit notifications */ +#define TYPE_MESSAGE 0x0100 +#define TYPE_SEVERITY 0x0101 + +#define TYPE_SIGN_SHA256 0x0200 +#define TYPE_ENCR_AES256 0x0210 + +#define PART_SIGNATURE_SHA256_SIZE 36 +#define PART_ENCRYPTION_AES256_SIZE 42 + +#define ADD_GENERIC(nb,srcptr,size) do { \ + assert ((size) <= (nb)->free); \ + memcpy ((nb)->ptr, (srcptr), (size)); \ + (nb)->ptr += (size); \ + (nb)->free -= (size); \ +} while (0) + +#define ADD_STATIC(nb,var) \ + ADD_GENERIC(nb,&(var),sizeof(var)); + +/* + * Data types + */ +struct lcc_network_buffer_s +{ + char *buffer; + size_t size; + + lcc_value_list_t state; + char *ptr; + size_t free; + + lcc_security_level_t seclevel; + char *username; + char *password; + + gcry_cipher_hd_t encr_cypher; + size_t encr_header_len; + char encr_iv[16]; +}; + +#define SSTRNCPY(dst,src,sz) do { \ + strncpy ((dst), (src), (sz)); \ + (dst)[(sz) - 1] = 0; \ +} while (0) + +/* + * Private functions + */ +static _Bool have_gcrypt (void) /* {{{ */ +{ + static _Bool result = 0; + static _Bool need_init = 1; + + if (!need_init) + return (result); + need_init = 0; + + gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); + + if (!gcry_check_version (GCRYPT_VERSION)) + return (0); + + gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0); + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); + + result = 1; + return (1); +} /* }}} _Bool have_gcrypt */ + +static uint64_t htonll (uint64_t val) /* {{{ */ +{ + static int config = 0; + + uint32_t hi; + uint32_t lo; + + if (config == 0) + { + uint16_t h = 0x1234; + uint16_t n = htons (h); + + if (h == n) + config = 1; + else + config = 2; + } + + if (config == 1) + return (val); + + hi = (uint32_t) (val >> 32); + lo = (uint32_t) (val & 0x00000000FFFFFFFF); + + hi = htonl (hi); + lo = htonl (lo); + + return ((((uint64_t) lo) << 32) | ((uint64_t) hi)); +} /* }}} uint64_t htonll */ + +static double htond (double val) /* {{{ */ +{ + static int config = 0; + + union { uint8_t byte[8]; double floating; } in; + union { uint8_t byte[8]; double floating; } out; + + if (config == 0) + { + double d = 8.642135e130; + uint8_t c[8]; + + memcpy (c, &d, 8); + + if ((c[0] == 0x2f) && (c[1] == 0x25) + && (c[2] == 0xc0) && (c[3] == 0xc7) + && (c[4] == 0x43) && (c[5] == 0x2b) + && (c[6] == 0x1f) && (c[7] == 0x5b)) + config = 1; /* need nothing */ + else if ((c[7] == 0x2f) && (c[6] == 0x25) + && (c[5] == 0xc0) && (c[4] == 0xc7) + && (c[3] == 0x43) && (c[2] == 0x2b) + && (c[1] == 0x1f) && (c[0] == 0x5b)) + config = 2; /* endian flip */ + else if ((c[4] == 0x2f) && (c[5] == 0x25) + && (c[6] == 0xc0) && (c[7] == 0xc7) + && (c[0] == 0x43) && (c[1] == 0x2b) + && (c[2] == 0x1f) && (c[3] == 0x5b)) + config = 3; /* int swap */ + else + config = 4; + } + + if (isnan (val)) + { + out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00; + out.byte[4] = out.byte[5] = 0x00; + out.byte[6] = 0xf8; + out.byte[7] = 0x7f; + return (out.floating); + } + else if (config == 1) + return (val); + else if (config == 2) + { + in.floating = val; + out.byte[0] = in.byte[7]; + out.byte[1] = in.byte[6]; + out.byte[2] = in.byte[5]; + out.byte[3] = in.byte[4]; + out.byte[4] = in.byte[3]; + out.byte[5] = in.byte[2]; + out.byte[6] = in.byte[1]; + out.byte[7] = in.byte[0]; + return (out.floating); + } + else if (config == 3) + { + in.floating = val; + out.byte[0] = in.byte[4]; + out.byte[1] = in.byte[5]; + out.byte[2] = in.byte[6]; + out.byte[3] = in.byte[7]; + out.byte[4] = in.byte[0]; + out.byte[5] = in.byte[1]; + out.byte[6] = in.byte[2]; + out.byte[7] = in.byte[3]; + return (out.floating); + } + else + { + /* If in doubt, just copy the value back to the caller. */ + return (val); + } +} /* }}} double htond */ + +static int nb_add_values (char **ret_buffer, /* {{{ */ + size_t *ret_buffer_len, + const lcc_value_list_t *vl) +{ + char *packet_ptr; + size_t packet_len; + + uint16_t pkg_type; + uint16_t pkg_length; + uint16_t pkg_num_values; + uint8_t pkg_values_types[vl->values_len]; + value_t pkg_values[vl->values_len]; + + size_t offset; + size_t i; + + packet_len = sizeof (pkg_type) + sizeof (pkg_length) + + sizeof (pkg_num_values) + + sizeof (pkg_values_types) + + sizeof (pkg_values); + + if (*ret_buffer_len < packet_len) + return (ENOMEM); + + pkg_type = htons (TYPE_VALUES); + pkg_length = htons ((uint16_t) packet_len); + pkg_num_values = htons ((uint16_t) vl->values_len); + + for (i = 0; i < vl->values_len; i++) + { + pkg_values_types[i] = (uint8_t) vl->values_types[i]; + switch (vl->values_types[i]) + { + case LCC_TYPE_COUNTER: + pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter); + break; + + case LCC_TYPE_GAUGE: + pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge); + break; + + case LCC_TYPE_DERIVE: + pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive); + break; + + case LCC_TYPE_ABSOLUTE: + pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute); + break; + + default: + return (EINVAL); + } /* switch (vl->values_types[i]) */ + } /* for (vl->values_len) */ + + /* + * Use `memcpy' to write everything to the buffer, because the pointer + * may be unaligned and some architectures, such as SPARC, can't handle + * that. + */ + packet_ptr = *ret_buffer; + offset = 0; + memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type)); + offset += sizeof (pkg_type); + memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length)); + offset += sizeof (pkg_length); + memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values)); + offset += sizeof (pkg_num_values); + memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types)); + offset += sizeof (pkg_values_types); + memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values)); + offset += sizeof (pkg_values); + + assert (offset == packet_len); + + *ret_buffer = packet_ptr + packet_len; + *ret_buffer_len -= packet_len; + return (0); +} /* }}} int nb_add_values */ + +static int nb_add_number (char **ret_buffer, /* {{{ */ + size_t *ret_buffer_len, + uint16_t type, uint64_t value) +{ + char *packet_ptr; + size_t packet_len; + + uint16_t pkg_type; + uint16_t pkg_length; + uint64_t pkg_value; + + size_t offset; + + packet_len = sizeof (pkg_type) + + sizeof (pkg_length) + + sizeof (pkg_value); + + if (*ret_buffer_len < packet_len) + return (ENOMEM); + + pkg_type = htons (type); + pkg_length = htons ((uint16_t) packet_len); + pkg_value = htonll (value); + + packet_ptr = *ret_buffer; + offset = 0; + memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type)); + offset += sizeof (pkg_type); + memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length)); + offset += sizeof (pkg_length); + memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value)); + offset += sizeof (pkg_value); + + assert (offset == packet_len); + + *ret_buffer = packet_ptr + packet_len; + *ret_buffer_len -= packet_len; + return (0); +} /* }}} int nb_add_number */ + +static int nb_add_string (char **ret_buffer, /* {{{ */ + size_t *ret_buffer_len, + uint16_t type, const char *str, size_t str_len) +{ + char *packet_ptr; + size_t packet_len; + + uint16_t pkg_type; + uint16_t pkg_length; + + size_t offset; + + packet_len = sizeof (pkg_type) + + sizeof (pkg_length) + + str_len + 1; + if (*ret_buffer_len < packet_len) + return (ENOMEM); + + pkg_type = htons (type); + pkg_length = htons ((uint16_t) packet_len); + + packet_ptr = *ret_buffer; + offset = 0; + memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type)); + offset += sizeof (pkg_type); + memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length)); + offset += sizeof (pkg_length); + memcpy (packet_ptr + offset, str, str_len); + offset += str_len; + memset (packet_ptr + offset, 0, 1); + offset += 1; + + assert (offset == packet_len); + + *ret_buffer = packet_ptr + packet_len; + *ret_buffer_len -= packet_len; + return (0); +} /* }}} int nb_add_string */ + +static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */ + const lcc_value_list_t *vl) +{ + char *buffer = nb->ptr; + size_t buffer_size = nb->free; + + const lcc_identifier_t *ident_src; + lcc_identifier_t *ident_dst; + + ident_src = &vl->identifier; + ident_dst = &nb->state.identifier; + + if (strcmp (ident_dst->host, ident_src->host) != 0) + { + if (nb_add_string (&buffer, &buffer_size, TYPE_HOST, + ident_src->host, strlen (ident_src->host)) != 0) + return (-1); + SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host)); + } + + if (strcmp (ident_dst->plugin, ident_src->plugin) != 0) + { + if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN, + ident_src->plugin, strlen (ident_src->plugin)) != 0) + return (-1); + SSTRNCPY (ident_dst->plugin, ident_src->plugin, + sizeof (ident_dst->plugin)); + } + + if (strcmp (ident_dst->plugin_instance, + ident_src->plugin_instance) != 0) + { + if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE, + ident_src->plugin_instance, + strlen (ident_src->plugin_instance)) != 0) + return (-1); + SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance, + sizeof (ident_dst->plugin_instance)); + } + + if (strcmp (ident_dst->type, ident_src->type) != 0) + { + if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE, + ident_src->type, strlen (ident_src->type)) != 0) + return (-1); + SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type)); + } + + if (strcmp (ident_dst->type_instance, + ident_src->type_instance) != 0) + { + if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE, + ident_src->type_instance, + strlen (ident_src->type_instance)) != 0) + return (-1); + SSTRNCPY (ident_dst->type_instance, ident_src->type_instance, + sizeof (ident_dst->type_instance)); + } + + if (nb->state.time != vl->time) + { + if (nb_add_number (&buffer, &buffer_size, TYPE_TIME, + (uint64_t) vl->time)) + return (-1); + nb->state.time = vl->time; + } + + if (nb->state.interval != vl->interval) + { + if (nb_add_number (&buffer, &buffer_size, TYPE_INTERVAL, + (uint64_t) vl->interval)) + return (-1); + nb->state.interval = vl->interval; + } + + if (nb_add_values (&buffer, &buffer_size, vl) != 0) + return (-1); + + nb->ptr = buffer; + nb->free = buffer_size; + return (0); +} /* }}} int nb_add_value_list */ + +static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */ +{ + char *buffer; + size_t buffer_size; + + gcry_md_hd_t hd; + gcry_error_t err; + unsigned char *hash; + const size_t hash_length = 32; + + /* The type, length and username have already been filled in by + * "lcc_network_buffer_initialize". All we do here is calculate the hash over + * the username and the data and add the hash value to the buffer. */ + + buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE; + assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE)); + buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE); + + hd = NULL; + err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); + if (err != 0) + return (-1); + + assert (nb->password != NULL); + err = gcry_md_setkey (hd, nb->password, strlen (nb->password)); + if (err != 0) + { + gcry_md_close (hd); + return (-1); + } + + gcry_md_write (hd, buffer, buffer_size); + hash = gcry_md_read (hd, GCRY_MD_SHA256); + if (hash == NULL) + { + gcry_md_close (hd); + return (-1); + } + + assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE); + memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length); + + gcry_md_close (hd); + return (0); +} /* }}} int nb_add_signature */ + +static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */ +{ + size_t package_length; + char *encr_ptr; /* pointer to data being encrypted */ + size_t encr_size; + + char *hash_ptr; /* pointer to data being hashed */ + size_t hash_size; + char hash[20]; + + uint16_t pkg_length; + gcry_error_t err; + + /* Fill in the package length */ + package_length = nb->size - nb->free; + pkg_length = htons ((uint16_t) package_length); + memcpy (nb->buffer + 2, &pkg_length, sizeof (pkg_length)); + + /* Calculate what to hash */ + hash_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE; + hash_size = package_length - nb->encr_header_len; + + /* Calculate what to encrypt */ + encr_ptr = hash_ptr - sizeof (hash); + encr_size = hash_size + sizeof (hash); + + /* Calculate the SHA-1 hash */ + gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash_ptr, hash_size); + memcpy (encr_ptr, hash, sizeof (hash)); + + if (nb->encr_cypher == NULL) + { + unsigned char password_hash[32]; + + err = gcry_cipher_open (&nb->encr_cypher, + GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0); + if (err != 0) + return (-1); + + /* Calculate our 256bit key used for AES */ + gcry_md_hash_buffer (GCRY_MD_SHA256, password_hash, + nb->password, strlen (nb->password)); + + err = gcry_cipher_setkey (nb->encr_cypher, + password_hash, sizeof (password_hash)); + if (err != 0) + { + gcry_cipher_close (nb->encr_cypher); + nb->encr_cypher = NULL; + return (-1); + } + } + else /* if (nb->encr_cypher != NULL) */ + { + gcry_cipher_reset (nb->encr_cypher); + } + + /* Set the initialization vector */ + err = gcry_cipher_setiv (nb->encr_cypher, + nb->encr_iv, sizeof (nb->encr_iv)); + if (err != 0) + { + gcry_cipher_close (nb->encr_cypher); + nb->encr_cypher = NULL; + return (-1); + } + + /* Encrypt the buffer in-place */ + err = gcry_cipher_encrypt (nb->encr_cypher, + encr_ptr, encr_size, + /* in = */ NULL, /* in len = */ 0); + if (err != 0) + { + gcry_cipher_close (nb->encr_cypher); + nb->encr_cypher = NULL; + return (-1); + } + + return (0); +} /* }}} int nb_add_encryption */ + +/* + * Public functions + */ +lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */ +{ + lcc_network_buffer_t *nb; + + if (size == 0) + size = LCC_NETWORK_BUFFER_SIZE_DEFAULT; + + if (size < 128) + { + errno = EINVAL; + return (NULL); + } + + nb = malloc (sizeof (*nb)); + if (nb == NULL) + return (NULL); + memset (nb, 0, sizeof (*nb)); + + nb->size = size; + nb->buffer = malloc (nb->size); + if (nb->buffer == NULL) + { + free (nb); + return (NULL); + } + memset (nb->buffer, 0, nb->size); + + nb->ptr = nb->buffer; + nb->free = nb->size; + + nb->seclevel = NONE; + nb->username = NULL; + nb->password = NULL; + + return (nb); +} /* }}} lcc_network_buffer_t *lcc_network_buffer_create */ + +void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */ +{ + if (nb == NULL) + return; + + free (nb->buffer); + free (nb); +} /* }}} void lcc_network_buffer_destroy */ + +int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */ + lcc_security_level_t level, + const char *username, const char *password) +{ + char *username_copy; + char *password_copy; + + if (level == NONE) + { + free (nb->username); + free (nb->password); + nb->username = NULL; + nb->password = NULL; + nb->seclevel = NONE; + lcc_network_buffer_initialize (nb); + return (0); + } + + if (!have_gcrypt ()) + return (ENOTSUP); + + username_copy = strdup (username); + password_copy = strdup (password); + if ((username_copy == NULL) || (password_copy == NULL)) + { + free (username_copy); + free (password_copy); + return (ENOMEM); + } + + free (nb->username); + free (nb->password); + nb->username = username_copy; + nb->password = password_copy; + nb->seclevel = level; + + lcc_network_buffer_initialize (nb); + return (0); +} /* }}} int lcc_network_buffer_set_security_level */ + +int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */ +{ + if (nb == NULL) + return (EINVAL); + + memset (nb->buffer, 0, nb->size); + memset (&nb->state, 0, sizeof (nb->state)); + nb->ptr = nb->buffer; + nb->free = nb->size; + + if (nb->seclevel == SIGN) + { + size_t username_len; + uint16_t pkg_type = htons (TYPE_SIGN_SHA256); + uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE; + + assert (nb->username != NULL); + username_len = strlen (nb->username); + pkg_length = htons (pkg_length + ((uint16_t) username_len)); + + /* Fill in everything but the hash value here. */ + memcpy (nb->ptr, &pkg_type, sizeof (pkg_type)); + memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length)); + nb->ptr += PART_SIGNATURE_SHA256_SIZE; + nb->free -= PART_SIGNATURE_SHA256_SIZE; + + memcpy (nb->ptr, nb->username, username_len); + nb->ptr += username_len; + nb->free -= username_len; + } + else if (nb->seclevel == ENCRYPT) + { + size_t username_length = strlen (nb->username); + uint16_t pkg_type = htons (TYPE_ENCR_AES256); + uint16_t pkg_length = 0; /* Filled in in finalize. */ + uint16_t pkg_user_len = htons ((uint16_t) username_length); + char hash[20]; + + nb->encr_header_len = username_length; + nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE; + + gcry_randomize ((void *) &nb->encr_iv, sizeof (nb->encr_iv), + GCRY_STRONG_RANDOM); + + /* Filled in in finalize. */ + memset (hash, 0, sizeof (hash)); + + ADD_STATIC (nb, pkg_type); + ADD_STATIC (nb, pkg_length); + ADD_STATIC (nb, pkg_user_len); + ADD_GENERIC (nb, nb->username, username_length); + ADD_GENERIC (nb, nb->encr_iv, sizeof (nb->encr_iv)); + ADD_GENERIC (nb, hash, sizeof (hash)); + assert ((nb->encr_header_len + nb->free) == nb->size); + } + + return (0); +} /* }}} int lcc_network_buffer_initialize */ + +int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */ +{ + if (nb == NULL) + return (EINVAL); + + if (nb->seclevel == SIGN) + nb_add_signature (nb); + else if (nb->seclevel == ENCRYPT) + nb_add_encryption (nb); + + return (0); +} /* }}} int lcc_network_buffer_finalize */ + +int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */ + const lcc_value_list_t *vl) +{ + int status; + + if ((nb == NULL) || (vl == NULL)) + return (EINVAL); + + status = nb_add_value_list (nb, vl); + return (status); +} /* }}} int lcc_network_buffer_add_value */ + +int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */ + void *buffer, size_t *buffer_size) +{ + size_t sz_required; + size_t sz_available; + + if ((nb == NULL) || (buffer_size == NULL)) + return (EINVAL); + + assert (nb->size >= nb->free); + sz_required = nb->size - nb->free; + sz_available = *buffer_size; + + *buffer_size = sz_required; + if (buffer != NULL) + memcpy (buffer, nb->buffer, + (sz_available < sz_required) ? sz_available : sz_required); + + return (0); +} /* }}} int lcc_network_buffer_get */ + +/* vim: set sw=2 sts=2 et fdm=marker : */