The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
collectd.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: c584ec6440eca43d302d3a8a6c9d5fa9745d7a5f $
19 * @file collectd.c
20 * @brief Helper functions to enabled radsniff to talk to collectd
21 *
22 * @copyright 2013 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24#include <assert.h>
25#include <ctype.h>
26
27#ifdef HAVE_COLLECTDC_H
28#include <collectd/client.h>
29#include <freeradius-devel/util/syserror.h>
30#include "radsniff.h"
31
32/** Copy a 64bit unsigned integer into a double
33 *
34 */
35/*
36static void _copy_uint64_to_double(UNUSED rs_t *conf, rs_stats_value_tmpl_t *tmpl)
37{
38 assert(tmpl->src);
39 assert(tmpl->dst);
40
41 *((double *) tmpl->dst) = *((uint64_t *) tmpl->src);
42}
43*/
44
45/*
46static void _copy_uint64_to_uint64(UNUSED rs_t *conf, rs_stats_value_tmpl_t *tmpl)
47{
48 assert(tmpl->src);
49 assert(tmpl->dst);
50
51 *((uint64_t *) tmpl->dst) = *((uint64_t *) tmpl->src);
52}
53*/
54
55static void _copy_double_to_double(UNUSED rs_t *conf, rs_stats_value_tmpl_t *tmpl)
56{
57 assert(tmpl->src);
58 assert(tmpl->dst);
59
60 *((double *) tmpl->dst) = *((double*) tmpl->src);
61}
62
63
64/** Allocates a stats template which describes a single gauge/counter
65 *
66 * This is just intended to simplify allocating a fairly complex memory structure
67 * src and dst pointers must be set
68 *
69 * @param ctx Context to allocate collectd struct in.
70 * @param conf Radsniff configuration.
71 * @param plugin_instance usually the type of packet (in our case).
72 * @param type string, the name of a collection of stats e.g. exchange
73 * @param type_instance the name of the counter/gauge within the collection e.g. latency.
74 * @param stats structure to derive statistics from.
75 * @param values Value templates used to populate lcc_value_list.
76 * @return
77 * - New #rs_stats_tmpl_t on success.
78 * - NULL on failure.
79 */
80static rs_stats_tmpl_t *rs_stats_collectd_init(TALLOC_CTX *ctx, rs_t *conf,
81 char const *plugin_instance,
82 char const *type, char const *type_instance,
83 void *stats,
84 rs_stats_value_tmpl_t const *values)
85{
86 static char hostname[255];
87 static char fqdn[LCC_NAME_LEN];
88
89 size_t len;
90 int i;
91 char *p;
92
93 rs_stats_tmpl_t *tmpl;
94 lcc_value_list_t *value;
95
96 assert(conf);
97 assert(type);
98 assert(type_instance);
99
100 for (len = 0; values[len].src; len++) {} ;
101 assert(len > 0);
102
103 /*
104 * Initialise hostname once so we don't call gethostname every time
105 */
106 if (*fqdn == '\0') {
107 int ret;
108 struct addrinfo hints, *info = NULL;
109
110 if (gethostname(hostname, sizeof(hostname)) < 0) {
111 ERROR("Error getting hostname: %s", fr_syserror(errno));
112
113 return NULL;
114 }
115
116 memset(&hints, 0, sizeof hints);
117 hints.ai_family = AF_UNSPEC; /*either IPV4 or IPV6*/
118 hints.ai_socktype = SOCK_STREAM;
119 hints.ai_flags = AI_CANONNAME;
120
121 if ((ret = getaddrinfo(hostname, "radius", &hints, &info)) != 0) {
122 ERROR("Error getting hostname: %s", gai_strerror(ret));
123 return NULL;
124 }
125
126 strlcpy(fqdn, info->ai_canonname, sizeof(fqdn));
127
128 freeaddrinfo(info);
129 }
130
131 tmpl = talloc_zero(ctx, rs_stats_tmpl_t);
132 if (!tmpl) {
133 return NULL;
134 }
135
136 tmpl->value_tmpl = talloc_zero_array(tmpl, rs_stats_value_tmpl_t, len);
137 if (!tmpl->value_tmpl) {
138 goto error;
139 }
140
141 tmpl->stats = stats;
142
143 value = talloc_zero(tmpl, lcc_value_list_t);
144 if (!value) {
145 goto error;
146 }
147 tmpl->value = value;
148
149 value->interval = conf->stats.interval;
150 value->values_len = len;
151
152 value->values_types = talloc_zero_array(value, int, len);
153 if (!value->values_types) {
154 goto error;
155 }
156
157 value->values = talloc_zero_array(value, value_t, len);
158 if (!value->values) {
159 goto error;
160 }
161
162 for (i = 0; i < (int) len; i++) {
163 assert(values[i].src);
164 assert(values[i].cb);
165
166 tmpl->value_tmpl[i] = values[i];
167 switch (tmpl->value_tmpl[i].type) {
168 case LCC_TYPE_COUNTER:
169 tmpl->value_tmpl[i].dst = &value->values[i].counter;
170 break;
171
172 case LCC_TYPE_GAUGE:
173 tmpl->value_tmpl[i].dst = &value->values[i].gauge;
174 break;
175
176 case LCC_TYPE_DERIVE:
177 tmpl->value_tmpl[i].dst = &value->values[i].derive;
178 break;
179
180 case LCC_TYPE_ABSOLUTE:
181 tmpl->value_tmpl[i].dst = &value->values[i].absolute;
182 break;
183
184 default:
185 assert(0);
186 }
187 value->values_types[i] = tmpl->value_tmpl[i].type;
188 }
189
190 /*
191 * These should be OK as is
192 */
193 strlcpy(value->identifier.host, fqdn, sizeof(value->identifier.host));
194
195 /*
196 * Plugin is ASCII only and no '/'
197 */
198 fr_snprint(value->identifier.plugin, sizeof(value->identifier.plugin),
199 conf->stats.prefix, strlen(conf->stats.prefix), '\0');
200 for (p = value->identifier.plugin; *p; ++p) {
201 if ((*p == '-') || (*p == '/'))*p = '_';
202 }
203
204 /*
205 * Plugin instance is ASCII only (assuming printable only) and no '/'
206 */
207 fr_snprint(value->identifier.plugin_instance, sizeof(value->identifier.plugin_instance),
208 plugin_instance, strlen(plugin_instance), '\0');
209 for (p = value->identifier.plugin_instance; *p; ++p) {
210 if ((*p == '-') || (*p == '/')) *p = '_';
211 }
212
213 /*
214 * Type is ASCII only (assuming printable only) and no '/' or '-'
215 */
216 fr_snprint(value->identifier.type, sizeof(value->identifier.type),
217 type, strlen(type), '\0');
218 for (p = value->identifier.type; *p; ++p) {
219 if ((*p == '-') || (*p == '/')) *p = '_';
220 }
221
222 fr_snprint(value->identifier.type_instance, sizeof(value->identifier.type_instance),
223 type_instance, strlen(type_instance), '\0');
224 for (p = value->identifier.type_instance; *p; ++p) {
225 if ((*p == '-') || (*p == '/')) *p = '_';
226 }
227
228
229 return tmpl;
230
231error:
232 talloc_free(tmpl);
233 return NULL;
234}
235
236
237/** Setup stats templates for latency
238 *
239 */
240rs_stats_tmpl_t *rs_stats_collectd_init_latency(TALLOC_CTX *ctx, rs_stats_tmpl_t **out, rs_t *conf,
241 char const *type, rs_latency_t *stats, fr_radius_packet_code_t code)
242{
243 rs_stats_tmpl_t **tmpl = out, *last;
244 char *p;
245 char buffer[LCC_NAME_LEN];
246 rs_stats_value_tmpl_t rtx[(RS_RETRANSMIT_MAX + 1) + 1 + 1]; // RTX bins + 0 bin + lost + NULL
247 int i;
248
249 /* not static so were thread safe */
250 rs_stats_value_tmpl_t const _packet_count[] = {
251 { &stats->interval.received, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
252 { &stats->interval.linked, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
253 { &stats->interval.unlinked, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
254 { &stats->interval.reused, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
255 { NULL, 0, NULL, NULL }
256 };
257
258 rs_stats_value_tmpl_t const _latency[] = {
259 { &stats->latency_smoothed, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
260 { &stats->interval.latency_average, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
261 { &stats->interval.latency_high, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
262 { &stats->interval.latency_low, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
263 { NULL, 0, NULL, NULL }
264 };
265
266#define INIT_STATS(_ti, _v) do {\
267 strlcpy(buffer, fr_radius_packet_name[code], sizeof(buffer)); \
268 for (p = buffer; *p; ++p) *p = tolower((uint8_t) *p);\
269 last = *tmpl = rs_stats_collectd_init(ctx, conf, type, _ti, buffer, stats, _v);\
270 if (!*tmpl) {\
271 TALLOC_FREE(*out);\
272 return NULL;\
273 }\
274 tmpl = &(*tmpl)->next;\
275 ctx = *tmpl;\
276 } while (0)
277
278
279 INIT_STATS("radius_count", _packet_count);
280 INIT_STATS("radius_latency", _latency);
281
282 for (i = 0; i < (RS_RETRANSMIT_MAX + 1); i++) {
283 rtx[i].src = &stats->interval.rt[i];
284 rtx[i].type = LCC_TYPE_GAUGE;
285 rtx[i].cb = _copy_double_to_double;
286 rtx[i].dst = NULL;
287 }
288
289 rtx[i].src = &stats->interval.lost;
290 rtx[i].type = LCC_TYPE_GAUGE;
291 rtx[i].cb = _copy_double_to_double;
292 rtx[i].dst = NULL;
293
294 memset(&rtx[++i], 0, sizeof(rs_stats_value_tmpl_t));
295
296 INIT_STATS("radius_rtx", rtx);
297
298 return last;
299}
300
301/** Refresh and send the stats to the collectd server
302 *
303 */
304void rs_stats_collectd_do_stats(rs_t *conf, rs_stats_tmpl_t *tmpls, struct timeval *now)
305{
306 rs_stats_tmpl_t *tmpl = tmpls;
307 char identifier[6 * LCC_NAME_LEN];
308 int i;
309
310 while (tmpl) {
311 /*
312 * Refresh the value of whatever were sending
313 */
314 for (i = 0; i < (int) tmpl->value->values_len; i++) {
315 tmpl->value_tmpl[i].cb(conf, &tmpl->value_tmpl[i]);
316 }
317
318 tmpl->value->time = now->tv_sec;
319
320 lcc_identifier_to_string(conf->stats.handle, identifier, sizeof(identifier), &tmpl->value->identifier);
321
322 if (lcc_putval(conf->stats.handle, tmpl->value) < 0) {
323 char const *error;
324
325 error = lcc_strerror(conf->stats.handle);
326 ERROR("Failed PUTVAL \"%s\" interval=%i %" PRIu64 " : %s",
327 identifier,
328 (int) tmpl->value->interval,
329 (uint64_t) tmpl->value->time,
330 error ? error : "unknown error");
331 }
332
333 tmpl = tmpl->next;
334 }
335}
336
337/** Connect to a collectd server for stats output
338 *
339 * @param[in,out] conf radsniff configuration, we write the generated handle here.
340 * @return
341 * - 0 on success
342 * - -1 on failure.
343 */
344int rs_stats_collectd_open(rs_t *conf)
345{
346 assert(conf->stats.collectd);
347
348 /*
349 * Tear down stale connections gracefully.
350 */
351 rs_stats_collectd_close(conf);
352
353 /*
354 * There's no way to get the error from the connection handle
355 * because it's freed on failure, before lcc returns.
356 */
357 if (lcc_connect(conf->stats.collectd, &conf->stats.handle) < 0) {
358 ERROR("Failed opening connection to collectd: %s", fr_syserror(errno));
359 return -1;
360 }
361 DEBUG2("Connected to \"%s\"", conf->stats.collectd);
362
363 assert(conf->stats.handle);
364 return 0;
365}
366
367/** Close connection
368 *
369 * @param[in,out] conf radsniff configuration.
370 * @return
371 * - 0 on success.
372 * - -1 on failure.
373 */
374int rs_stats_collectd_close(rs_t *conf)
375{
376 int ret = 0;
377
378 assert(conf->stats.collectd);
379
380 if (conf->stats.handle) {
381 ret = lcc_disconnect(conf->stats.handle);
382 conf->stats.handle = NULL;
383 }
384
385 return ret;
386}
387#endif
static int const char char buffer[256]
Definition acutest.h:576
#define UNUSED
Definition build.h:315
fr_radius_packet_code_t
RADIUS packet codes.
Definition defs.h:31
#define ERROR(fmt,...)
Definition dhcpclient.c:41
Test enumeration values.
Definition dict_test.h:92
int getaddrinfo(char const *hostname, char const *servname, struct addrinfo const *hints, struct addrinfo **res)
char const * gai_strerror(int ecode)
void freeaddrinfo(struct addrinfo *ai)
talloc_free(reap)
size_t fr_snprint(char *out, size_t outlen, char const *in, ssize_t inlen, char quote)
Escape any non printable or non-UTF8 characters in the input string.
Definition print.c:227
#define DEBUG2(fmt,...)
Definition radclient.h:43
static rs_t * conf
Definition radsniff.c:53
Structures and prototypes for the RADIUS sniffer.
struct rs::@1 stats
#define RS_RETRANSMIT_MAX
Maximum number of times we expect to see a packet retransmitted.
Definition radsniff.h:44
double latency_smoothed
Smoothed moving average.
Definition radsniff.h:115
struct rs_latency_t::@0 interval
Definition radsniff.h:259
Stats for a single interval.
Definition radsniff.h:112
static char const * hostname(char *buf, size_t buflen, uint32_t ipaddr)
Definition radwho.c:133
fr_aka_sim_id_type_t type
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
static size_t char ** out
Definition value.h:997