The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
stats.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library 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 GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Map internal data structures to statistics
18 *
19 * @file src/lib/util/stats.c
20 *
21 * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
22 */
23RCSID("$Id: 9aa3c64e83f4569f6b5d9e3557fb0983559f9d72 $")
24
25#include <freeradius-devel/util/stats.h>
26
27/** Convert a statistics structure to #fr_pair_t
28 *
29 * @param[in] ctx talloc ctx
30 * @param[out] out where the output pairs will be stored
31 * @param[in] inst data structure defining this instance of the statistics
32 */
34{
35 size_t i;
36 fr_stats_link_t const *def = inst->def;
37 uint8_t const *in = inst->stats;
39
40 parent = fr_pair_afrom_da(ctx, *(def->root_p));
41 if (!parent) return -1;
42
43 for (i = 0; i < def->num_elements; i++) {
44 uint8_t const *field;
45
46 vp = fr_pair_afrom_da(parent, *(def->entry[i].da_p));
47 if (!vp) goto fail;
48
49 field = ((uint8_t const *) in) + def->entry[i].offset;
50
51 /*
52 * Strings of length 0 are "const char *".
53 *
54 * Everything else is an in-line field.
55 */
56 switch (def->entry[i].type) {
57 case FR_TYPE_STRING:
58 if (!def->entry[i].size) {
59 char const *str;
60
61 memcpy(&str, field, sizeof(str));
62
63 if (fr_pair_value_strdup(vp, str, false) < 0) goto fail;
64 break;
65 }
67
68 case FR_TYPE_OCTETS:
69 case FR_TYPE_UINT16:
70 case FR_TYPE_UINT32:
71 case FR_TYPE_UINT64:
74 fr_assert(def->entry[i].size > 0);
75
76 if (fr_value_box_from_network(vp, &vp->data, def->entry[i].type, vp->da,
77 &FR_DBUFF_TMP(field, def->entry[i].size), def->entry[i].size, false) < 0) {
78 goto fail;
79 }
80 break;
81
82 case FR_TYPE_DATE:
83 memcpy(&vp->vp_date, field, sizeof(vp->vp_date));
84 break;
85
87 memcpy(&vp->vp_time_delta, field, sizeof(vp->vp_time_delta));
88 break;
89
90 default:
91 fr_strerror_printf("Unsupported data type '%s'", fr_type_to_str(def->entry[i].type));
92 return -1;
93 }
94
95 if (fr_pair_append(&parent->vp_group, vp) < 0) goto fail;
96 }
97
98 if (fr_pair_append(out, parent) < 0) {
99 fail:
101 return -1;
102 }
103
104 return 0;
105}
106
107
108/** Convert a statistics structure to #fr_pair_t
109 *
110 * @param[in] ctx talloc ctx
111 * @param[out] inst where the output pairs will be stored
112 * @param[in] list pairs where we read the pairs from
113 */
115{
116 size_t i;
117 fr_stats_link_t const *def = inst->def;
118 uint8_t *out = inst->stats;
119 fr_pair_t const *parent, *vp;
120
121 parent = fr_pair_find_by_da(list, NULL, *(def->root_p));
122 if (!parent) return -1;
123
124 memset(out, 0, inst->def->size);
125
126 for (i = 0; i < def->num_elements; i++) {
127 uint8_t *field;
128
129 vp = fr_pair_find_by_da(&parent->vp_group, NULL, *(def->entry[i].da_p));
130 if (!vp) continue;
131
132 field = ((uint8_t *) out) + def->entry[i].offset;
133
134 /*
135 * Strings of length 0 are "const char *".
136 *
137 * Everything else is an in-line field.
138 */
139 switch (def->entry[i].type) {
140 case FR_TYPE_STRING:
141 if (!def->entry[i].size) {
142 char const *str;
143
144 str = talloc_bstrndup(ctx, vp->vp_strvalue, vp->vp_length);
145 if (!str) return -1;
146
147 memcpy(&field, &str, sizeof(str));
148 break;
149 }
151
152 case FR_TYPE_OCTETS:
153 if (vp->vp_length <= def->entry[i].size) {
154 memcpy(field, vp->vp_ptr, vp->vp_length);
155 } else {
156 memcpy(field, vp->vp_ptr, def->entry[i].size);
157 }
158 break;
159
160#undef COPY
161#define COPY(_type, _field) \
162 case _type: \
163 memcpy(field, &vp->vp_ ## _field, sizeof(vp->vp_ ##_field)); \
164 break
165
166 COPY(FR_TYPE_UINT16, uint16);
167 COPY(FR_TYPE_UINT32, uint32);
168 COPY(FR_TYPE_UINT64, uint64);
169
170 COPY(FR_TYPE_IPV4_ADDR, ipv4addr); /* struct in_addr, and not vp_ip */
171 COPY(FR_TYPE_IPV6_ADDR, ipv6addr); /* struct in6_addr, and not vp_ip */
172
173 COPY(FR_TYPE_DATE, date);
174 COPY(FR_TYPE_TIME_DELTA, time_delta);
175
176 default:
177 fr_strerror_printf("Unsupported data type '%s'", fr_type_to_str(def->entry[i].type));
178 return -1;
179 }
180 }
181
182 return 0;
183}
184
185
186#undef add
187#define add(_type, _out, _in) \
188do { \
189 _type _a, _b, _sum; \
190 memcpy(&_a, &_out, sizeof(_a)); \
191 memcpy(&_b, &_in, sizeof(_b)); \
192 _sum = _a + _b; \
193 memcpy(&_out, &_sum, sizeof(_sum)); \
194} while (0)
195
196/** Merge to statistics structures
197 *
198 * @todo - ensure that the struct magic is the same as the def magic
199 *
200 */
201static int stats_merge_internal(fr_stats_link_t const *def, void *out, void const *in)
202{
203 size_t i;
204
205 for (i = 0; i < def->num_elements; i++) {
206 uint8_t const *field_in;
207 uint8_t const *field_out;
208
209 field_in = ((uint8_t const *) in) + def->entry[i].offset;
210 field_out = ((uint8_t const *) out) + def->entry[i].offset;
211
212 switch (def->entry[i].type) {
213 case FR_TYPE_UINT16:
214 add(uint16_t, field_out, field_in);
215 break;
216
217 case FR_TYPE_UINT32:
218 add(uint32_t, field_out, field_in);
219 break;
220
221 case FR_TYPE_UINT64:
222 add(uint64_t, field_out, field_in);
223 break;
224
225 default:
226 break;
227 }
228 }
229
230 return 0;
231}
232
233/** Public API for merging two statistics structures
234 *
235 * @param[out] out where the merged stats are written to
236 * @param[in] in source stats to merge into out
237 * @return
238 * - 0 on success
239 * - <0 on the two instances are not compatible.
240 */
242{
243 if (out->def != in->def) {
244 fr_strerror_printf("Cannot merge stats into structure %s from different structure %s",
245 out->def->name, in->def->name);
246 return -1;
247 }
248
249 return stats_merge_internal(out->def, out->stats, in->stats);
250}
251
252/** Public API for merging two value-boxes based on their enums
253 *
254 * @param[in,out] dst where the merged stats are written to
255 * @param[in] src source stats to merge into dst
256 * @return
257 * - 0 on success
258 * - <0 on the two boxes are not compatible, or we cannot merge the given data type
259 */
261{
262 if (dst->type != src->type) {
263 fr_strerror_const("Cannot merge two different data types");
264 return -1;
265 }
266
267 if (dst->enumv != src->enumv) {
268 fr_strerror_const("Cannot merge two different fields");
269 return -1;
270 }
271
272 if (!dst->enumv) {
273 fr_strerror_const("Unable to determine how to merge the fields");
274 return -1;
275 }
276
277 /*
278 * @todo - distinguish counter from gauge. This also means that we will need to update the
279 * dictionary.stats to set the "counter" flag.
280 */
281
282 switch (dst->type) {
283 case FR_TYPE_UINT16:
284 dst->vb_uint16 += src->vb_uint16;
285 break;
286
287 case FR_TYPE_UINT32:
288 dst->vb_uint32 += src->vb_uint32;
289 break;
290
291 case FR_TYPE_UINT64:
292 dst->vb_uint64 += src->vb_uint64;
293 break;
294
295 default:
296 fr_strerror_const("Cannot merge non-integer data types");
297 return -1;
298 }
299
300 return 0;
301}
302
303
304/** Initialize an iterator over a structure
305 *
306 * @param[in] inst data structure defining this instance of the statistics
307 * @param[out] iter the initialized iterator
308 */
310{
311 iter->inst = inst;
312 iter->current = 0;
313}
314
315/** Go to the next entry in a structure
316 *
317 * @param[in] iter the iterator
318 * @return
319 * - true for continue the iteration
320 * - false for the iteration is done
321 */
323{
324 if (iter->current < iter->inst->def->num_elements) {
325 iter->current++;
326 return true;
327 }
328
329 return false;
330}
331
332
333/** Convert the statistic at an index to a value-box
334 *
335 * @param[in] ctx the talloc context
336 * @param[out] out the value-box to return
337 * @param[in] inst data structure defining this instance of the statistics
338 * @param[in] index the field index of the structure to use
339 * @return
340 * - 0 for success, and *out is non-NULL
341 * - <0 for error (memory allocation failed, index is invalid, etc), and *out is NULL
342 */
343int fr_stats_index_to_value_box(TALLOC_CTX *ctx, fr_value_box_t **out, fr_stats_instance_t const *inst, unsigned int index)
344{
345 size_t len;
346 fr_value_box_t *box;
347 uint8_t const *field;
348 fr_dict_attr_t const *da;
349
350 if (index >= inst->def->num_elements) {
351 fail:
352 *out = NULL;
353 return -1;
354 }
355
356 da = *(inst->def->entry[index].da_p);
357 fr_assert(da != NULL);
358
359 box = fr_value_box_alloc(ctx, inst->def->entry[index].type, da);
360 if (!box) goto fail;
361
362 field = ((uint8_t const *) inst->stats) + inst->def->entry[index].offset;
363
364 switch (box->type) {
365 case FR_TYPE_STRING:
366 if (!inst->def->entry[index].size) {
367 char const *str;
368
369 memcpy(&str, field, sizeof(str));
370 len = strlen(str);
371 } else {
372 uint8_t const *end;
373
374 /*
375 * Find the trailing NUL within the fixed-size field.
376 */
377 end = memchr(field, '\0', inst->def->entry[index].size);
378 len = (size_t) (end - field);
379 }
380 break;
381
382 case FR_TYPE_OCTETS:
383 case FR_TYPE_UINT16:
384 case FR_TYPE_UINT32:
385 case FR_TYPE_UINT64:
388 len = inst->def->entry[index].size;
389 break;
390
391 default:
392 fr_strerror_printf("Unsupported data type '%s'", fr_type_to_str(box->type));
393 goto fail_free;
394 }
395
396 if (fr_value_box_from_memory(box, box, box->type, da, field, len) < 0) {
397 fail_free:
398 talloc_free(box);
399 goto fail;
400 }
401
402 *out = box;
403 return 0;
404}
405
406
407/** Convert the statistic at the current iterator to a value-box
408 *
409 * @param[in] ctx the talloc context
410 * @param[out] out the value-box to return
411 * @param[in] iter the iterator, which points to the current entry.
412 * @return
413 * - 0 for success, and *out is non-NULL
414 * - <0 for error (memory allocation failed, index is invalid, etc), and *out is NULL
415 */
417{
418 fr_assert(iter->inst);
419 fr_assert(iter->current < iter->inst->def->num_elements);
420
421 return fr_stats_index_to_value_box(ctx, out, iter->inst, iter->current);
422}
423
424
425/** Convert the statistic of a given name to a value-box
426 *
427 * @param[in] ctx the talloc context
428 * @param[out] out the value-box to return
429 * @param[in] inst data structure defining this instance of the statistics
430 * @param[in] name the field name in the structure
431 * @return
432 * - 0 for success, and *out is non-NULL
433 * - <0 for error (memory allocation failed, name is invalid, etc), and *out is NULL
434 */
435int fr_stats_name_to_value_box(TALLOC_CTX *ctx, fr_value_box_t **out, fr_stats_instance_t const *inst, char const *name)
436{
437 fr_dict_attr_t const *da;
438
439 da = fr_dict_attr_by_name(NULL, *inst->def->root_p, name);
440 if (!da) return -1;
441
442 return fr_stats_index_to_value_box(ctx, out, inst, da->attr);
443}
#define RCSID(id)
Definition build.h:487
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:324
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:524
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3532
static fr_slen_t in
Definition dict.h:884
talloc_free(reap)
fr_dict_attr_t const ** root_p
point to the autoload definition of the DA for this structure
Definition stats.h:46
size_t size
size of this field
Definition stats.h:41
fr_type_t type
cached for locality and simplicity
Definition stats.h:39
size_t offset
from the start of the struct
Definition stats.h:40
fr_stats_link_entry_t entry[]
the field entries, in offset order
Definition stats.h:50
size_t num_elements
number of elements in the table. Note no "NULL" terminator
Definition stats.h:49
unsigned int current
Definition stats.h:77
fr_dict_attr_t const ** da_p
point to the autoload definition of the DA for this field
Definition stats.h:38
fr_stats_instance_t const * inst
Definition stats.h:76
Generic statistics structure.
Definition stats.h:65
Iterator for a statistics structure.
Definition stats.h:75
unsigned short uint16_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_OCTETS
Raw octets.
unsigned int uint32_t
unsigned char uint8_t
unsigned long int size_t
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition pair.c:2645
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:703
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1348
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:289
#define fr_assert(_expr)
Definition rad_assert.h:38
static char const * name
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
eap_type_t type
The preferred EAP-Type of this instance of the EAP-SIM/AKA/AKA' state machine.
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:586
static fr_slen_t parent
Definition pair.h:859
#define add(_type, _out, _in)
Definition stats.c:187
void fr_stats_iter_init(fr_stats_instance_t const *inst, fr_stats_iter_t *iter)
Initialize an iterator over a structure.
Definition stats.c:309
#define COPY(_type, _field)
int fr_stats_iter_to_value_box(TALLOC_CTX *ctx, fr_value_box_t **out, fr_stats_iter_t *iter)
Convert the statistic at the current iterator to a value-box.
Definition stats.c:416
bool fr_stats_iter_next(fr_stats_iter_t *iter)
Go to the next entry in a structure.
Definition stats.c:322
int fr_stats_index_to_value_box(TALLOC_CTX *ctx, fr_value_box_t **out, fr_stats_instance_t const *inst, unsigned int index)
Convert the statistic at an index to a value-box.
Definition stats.c:343
static int stats_merge_internal(fr_stats_link_t const *def, void *out, void const *in)
Merge to statistics structures.
Definition stats.c:201
int fr_stats_merge_instance(fr_stats_instance_t *out, fr_stats_instance_t const *in)
Public API for merging two statistics structures.
Definition stats.c:241
int fr_stats_from_pairs(TALLOC_CTX *ctx, fr_stats_instance_t *inst, fr_pair_list_t const *list)
Convert a statistics structure to fr_pair_t.
Definition stats.c:114
int fr_stats_merge_value_box(fr_value_box_t *dst, fr_value_box_t const *src)
Public API for merging two value-boxes based on their enums.
Definition stats.c:260
int fr_stats_to_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_stats_instance_t const *inst)
Convert a statistics structure to fr_pair_t.
Definition stats.c:33
int fr_stats_name_to_value_box(TALLOC_CTX *ctx, fr_value_box_t **out, fr_stats_instance_t const *inst, char const *name)
Convert the statistic of a given name to a value-box.
Definition stats.c:435
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:455
ssize_t fr_value_box_from_network(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t type, fr_dict_attr_t const *enumv, fr_dbuff_t *dbuff, size_t len, bool tainted)
Decode a fr_value_box_t from serialized binary data.
Definition value.c:1914
ssize_t fr_value_box_from_memory(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t type, fr_dict_attr_t const *enumv, void const *src, size_t len)
Decode a fr_value_box_t from a C type in memory.
Definition value.c:2439
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:645
static size_t char ** out
Definition value.h:1025