Fixes for OLSRd2 build process
[oonf.git] / src-plugins / nhdp / ff_dat_metric / ff_dat_metric.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2013, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <stdio.h>
47
48 #include "common/common_types.h"
49 #include "common/isonumber.h"
50 #include "common/autobuf.h"
51 #include "core/oonf_cfg.h"
52 #include "core/oonf_logging.h"
53 #include "core/oonf_subsystem.h"
54 #include "subsystems/oonf_class.h"
55 #include "subsystems/oonf_layer2.h"
56 #include "subsystems/oonf_rfc5444.h"
57 #include "subsystems/oonf_timer.h"
58
59 #include "nhdp/nhdp.h"
60 #include "nhdp/nhdp_domain.h"
61 #include "nhdp/nhdp_interfaces.h"
62
63 #include "ff_dat_metric/ff_dat_metric.h"
64
65 /* Definitions */
66 #define LOG_FF_DAT _olsrv2_ffdat_subsystem.logging
67
68 /* prototypes */
69 static int _init(void);
70 static void _cleanup(void);
71
72 static void _cb_enable_metric(void);
73 static void _cb_disable_metric(void);
74
75 static void _cb_link_added(void *);
76 static void _cb_link_changed(void *);
77 static void _cb_link_removed(void *);
78
79 static void _cb_dat_sampling(void *);
80 static void _cb_hello_lost(void *);
81
82 static enum rfc5444_result _cb_process_packet(
83       struct rfc5444_reader_tlvblock_context *context);
84
85 static const char *_to_string(
86     struct nhdp_metric_str *buf, uint32_t metric);
87 static const char *_int_to_string(struct nhdp_metric_str *,
88     struct nhdp_domain *, struct nhdp_link *);
89
90 static int _cb_cfg_validate(const char *section_name,
91     struct cfg_named_section *, struct autobuf *);
92 static void _cb_cfg_changed(void);
93
94 /* plugin declaration */
95 static struct cfg_schema_entry _datff_entries[] = {
96   CFG_MAP_CLOCK_MIN(ff_dat_config, interval, "interval", "1.0",
97       "Time interval between recalculations of metric", 100),
98   CFG_MAP_INT32_MINMAX(ff_dat_config, window, "window", "64",
99       "Number of intervals to calculate average metric", 0, false, 2, 65535),
100   CFG_MAP_BOOL(ff_dat_config, ett, "ett", "true",
101       "Activates the handling of linkspeed within the metric, set to false to"
102       " downgrade to ETX metric"),
103
104 #ifdef COLLECT_RAW_DATA
105   CFG_MAP_STRING(ff_dat_config, rawdata_file, "raw_filename", "/tmp/olsrv2_dat_metric.txt",
106       "File to write recorded data into"),
107   CFG_MAP_BOOL(ff_dat_config, rawdata_start, "raw_start", "false",
108       "Set to true to activate rawdata measurement"),
109   CFG_MAP_CLOCK(ff_dat_config, rawdata_maxtime, "raw_maxtime", "3600000",
110       "Time until measurement stops"),
111   CFG_MAP_INT32_MINMAX(ff_dat_config, rawdata_maxpackets, "raw_maxpackets", "20000",
112       "Maximum number of packets to record", 0, false, 1, INT32_MAX),
113 #endif
114 };
115
116 /* Subsystem definition */
117 static struct cfg_schema_section _datff_section = {
118   .type = OONF_FF_DAT_METRIC_SUBSYSTEM,
119   .cb_validate = _cb_cfg_validate,
120   .cb_delta_handler = _cb_cfg_changed,
121   .entries = _datff_entries,
122   .entry_count = ARRAYSIZE(_datff_entries),
123 };
124
125 static struct ff_dat_config _datff_config;
126
127 static const char *_dependencies[] = {
128   OONF_CLASS_SUBSYSTEM,
129   OONF_LAYER2_SUBSYSTEM,
130   OONF_RFC5444_SUBSYSTEM,
131   OONF_TIMER_SUBSYSTEM,
132   OONF_NHDP_SUBSYSTEM,
133 };
134 static struct oonf_subsystem _olsrv2_ffdat_subsystem = {
135   .name = OONF_FF_DAT_METRIC_SUBSYSTEM,
136   .dependencies = _dependencies,
137   .dependencies_count = ARRAYSIZE(_dependencies),
138   .descr = "OLSRv2 Funkfeuer Directional Airtime Metric plugin",
139   .author = "Henning Rogge",
140
141   .cfg_section = &_datff_section,
142
143   .init = _init,
144   .cleanup = _cleanup,
145 };
146 DECLARE_OONF_PLUGIN(_olsrv2_ffdat_subsystem);
147
148 /* RFC5444 packet listener */
149 static struct oonf_rfc5444_protocol *_protocol;
150
151 static struct rfc5444_reader_tlvblock_consumer _packet_consumer = {
152   .order = RFC5444_LQ_PARSER_PRIORITY,
153   .default_msg_consumer = true,
154   .start_callback = _cb_process_packet,
155 };
156
157 /* storage extension and listeners */
158 static struct oonf_class_extension _link_extenstion = {
159   .ext_name = "datff linkmetric",
160   .class_name = NHDP_CLASS_LINK,
161   .size = sizeof(struct link_datff_data),
162
163   .cb_add = _cb_link_added,
164   .cb_change = _cb_link_changed,
165   .cb_remove = _cb_link_removed,
166 };
167
168 /* timer for sampling in RFC5444 packets */
169 static struct oonf_timer_class _sampling_timer_info = {
170   .name = "Sampling timer for DATFF-metric",
171   .callback = _cb_dat_sampling,
172   .periodic = true,
173 };
174
175 static struct oonf_timer_instance _sampling_timer = {
176   .class = &_sampling_timer_info,
177 };
178
179 /* timer class to measure interval between Hellos */
180 static struct oonf_timer_class _hello_lost_info = {
181   .name = "Hello lost timer for DATFF-metric",
182   .callback = _cb_hello_lost,
183 };
184
185 /* nhdp metric handler */
186 static struct nhdp_domain_metric _datff_handler = {
187   .name = OONF_FF_DAT_METRIC_SUBSYSTEM,
188
189   .metric_minimum = DATFF_LINKCOST_MINIMUM,
190   .metric_maximum = DATFF_LINKCOST_MAXIMUM,
191
192   .incoming_link_start = DATFF_LINKCOST_START,
193
194   .to_string = _to_string,
195   .internal_to_string = _int_to_string,
196
197   .enable = _cb_enable_metric,
198   .disable = _cb_disable_metric,
199 };
200
201 /* Temporary buffer to sort incoming link speed for median calculation */
202 static int *_rx_sort_array = NULL;
203
204 /* rawdata collection */
205 #ifdef COLLECT_RAW_DATA
206 static struct autobuf _rawdata_buf;
207 static int _rawdata_fd = -1;
208 static uint64_t _rawdata_end = 0;
209 static int _rawdata_count = 0;
210 #endif
211
212 /**
213  * Initialize plugin
214  * @return -1 if an error happened, 0 otherwise
215  */
216 static int
217 _init(void) {
218   if (nhdp_domain_metric_add(&_datff_handler)) {
219     return -1;
220   }
221
222   oonf_timer_add(&_sampling_timer_info);
223   oonf_timer_add(&_hello_lost_info);
224
225   _protocol = oonf_rfc5444_add_protocol(RFC5444_PROTOCOL, true);
226
227   oonf_rfc5444_add_protocol_pktseqno(_protocol);
228 #ifdef COLLECT_RAW_DATA
229   abuf_init(&_rawdata_buf);
230 #endif
231   return 0;
232 }
233
234 /**
235  * Cleanup plugin
236  */
237 static void
238 _cleanup(void) {
239 #ifdef COLLECT_RAW_DATA
240   if (_rawdata_fd != -1) {
241     fsync(_rawdata_fd);
242     close(_rawdata_fd);
243   }
244   abuf_free(&_rawdata_buf);
245   free(_datff_config.rawdata_file);
246 #endif
247   /* free sorting array */
248   free (_rx_sort_array);
249
250   /* remove metric from core */
251   nhdp_domain_metric_remove(&_datff_handler);
252
253   oonf_rfc5444_remove_protocol_pktseqno(_protocol);
254   oonf_rfc5444_remove_protocol(_protocol);
255
256   oonf_class_extension_remove(&_link_extenstion);
257
258   oonf_timer_stop(&_sampling_timer);
259
260   oonf_timer_remove(&_sampling_timer_info);
261   oonf_timer_remove(&_hello_lost_info);
262 }
263
264 static void
265 _cb_enable_metric(void) {
266   struct nhdp_link *lnk;
267
268   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
269     _cb_link_added(lnk);
270   }
271
272   rfc5444_reader_add_packet_consumer(&_protocol->reader, &_packet_consumer, NULL, 0);
273   oonf_timer_set(&_sampling_timer, _datff_config.interval);
274 }
275
276 static void
277 _cb_disable_metric(void) {
278   struct nhdp_link *lnk;
279
280   oonf_timer_stop(&_sampling_timer);
281   rfc5444_reader_remove_packet_consumer(&_protocol->reader, &_packet_consumer);
282
283   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
284     _cb_link_removed(lnk);
285   }
286 }
287
288 /**
289  * Callback triggered when a new nhdp link is added
290  * @param ptr nhdp link
291  */
292 static void
293 _cb_link_added(void *ptr) {
294   struct link_datff_data *data;
295   struct nhdp_link *lnk;
296   int i;
297
298   lnk = ptr;
299   data = oonf_class_get_extension(&_link_extenstion, lnk);
300
301   memset(data, 0, sizeof(*data));
302   data->activePtr = -1;
303   for (i = 0; i<_datff_config.window; i++) {
304     data->buckets[i].total = 1;
305     data->buckets[i].scaled_speed = 0;
306   }
307
308   /* start 'hello lost' timer for link */
309   data->hello_lost_timer.class = &_hello_lost_info;
310   data->hello_lost_timer.cb_context = ptr;
311 }
312
313 /**
314  * Callback triggered when a new nhdp link is changed
315  * @param ptr nhdp link
316  */
317 static void
318 _cb_link_changed(void *ptr) {
319   struct link_datff_data *data;
320   struct nhdp_link *lnk;
321
322   lnk = ptr;
323   data = oonf_class_get_extension(&_link_extenstion, lnk);
324
325   if (lnk->itime_value > 0) {
326     data->hello_interval = lnk->itime_value;
327   }
328   else {
329     data->hello_interval = lnk->vtime_value;
330   }
331
332   oonf_timer_set(&data->hello_lost_timer, (data->hello_interval * 3) / 2);
333
334   data->missed_hellos = 0;
335 }
336
337 /**
338  * Callback triggered when a nhdp link is removed from the database
339  * @param ptr nhdp link
340  */
341 static void
342 _cb_link_removed(void *ptr) {
343   struct link_datff_data *data;
344
345   data = oonf_class_get_extension(&_link_extenstion, ptr);
346
347   oonf_timer_stop(&data->hello_lost_timer);
348 }
349
350 static int
351 _int_comparator(const void *p1, const void *p2) {
352   const int *i1 = (int *)p1;
353   const int *i2 = (int *)p2;
354
355   if (*i1 > *i2) {
356     return 1;
357   }
358   else if (*i1 < *i2) {
359     return -1;
360   }
361   return 0;
362 }
363
364 static int
365 _get_median_rx_linkspeed(struct link_datff_data *ldata) {
366   int zero_count;
367   int window;
368   int i;
369
370   zero_count = 0;
371   for (i=0; i<_datff_config.window; i++) {
372     _rx_sort_array[i] = ldata->buckets[i].scaled_speed;
373     if (_rx_sort_array[i] == 0) {
374       zero_count++;
375     }
376   }
377
378   window = _datff_config.window - zero_count;
379   if (window == 0) {
380     return 1;
381   }
382
383   qsort(_rx_sort_array, _datff_config.window, sizeof(int), _int_comparator);
384
385   return _rx_sort_array[zero_count + window/2];
386 }
387
388 /**
389  * Retrieves the speed of a nhdp link, scaled to the minimum link speed
390  * of this metric.
391  * @param lnk nhdp link
392  * @return scaled link speed, 1 if could not be retrieved.
393  */
394 static int
395 _get_scaled_rx_linkspeed(struct nhdp_link *lnk) {
396   // const struct oonf_linkconfig_data *linkdata;
397   struct oonf_interface_data *ifdata;
398   const struct oonf_layer2_data *l2data;
399   int rate;
400
401   if (!_datff_config.ett) {
402     /* ETT feature is switched off */
403     return 1;
404   }
405
406   /* get local interface data  */
407   ifdata = oonf_interface_get_data(nhdp_interface_get_name(lnk->local_if), NULL);
408   if (!ifdata) {
409     return 1;
410   }
411
412   l2data = oonf_layer2_neigh_query(
413       ifdata->name, &lnk->remote_mac, OONF_LAYER2_NEIGH_RX_BITRATE);
414   if (!l2data) {
415     return 1;
416   }
417
418   /* round up */
419   rate = (oonf_layer2_get_value(l2data) + DATFF_LINKSPEED_MINIMUM - 1) / DATFF_LINKSPEED_MINIMUM;
420   if (rate < 1) {
421     return 1;
422   }
423   if (rate > DATFF_LINKSPEED_RANGE) {
424     return DATFF_LINKSPEED_RANGE;
425   }
426   return rate;
427 }
428
429 /**
430  * Timer callback to sample new metric values into bucket
431  * @param ptr nhdp link
432  */
433 static void
434 _cb_dat_sampling(void *ptr __attribute__((unused))) {
435   struct rfc7181_metric_field encoded_metric;
436   struct link_datff_data *ldata;
437   struct nhdp_link *lnk;
438   uint32_t total, received, loss_cost_multiplier;
439   uint64_t metric;
440   uint32_t metric_value;
441   int rx_bitrate;
442   int i;
443   bool change_happened;
444
445 #ifdef OONF_LOG_DEBUG_INFO
446   struct nhdp_laddr *laddr;
447   struct netaddr_str nbuf;
448 #endif
449
450   OONF_DEBUG(LOG_FF_DAT, "Calculate Metric from sampled data");
451
452   change_happened = false;
453   if (!_datff_handler.domain) {
454     /* metric not used */
455     return;
456   }
457
458   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
459     ldata = oonf_class_get_extension(&_link_extenstion, lnk);
460
461     if (ldata->activePtr == -1) {
462       /* still no data for this link */
463       continue;
464     }
465
466     /* initialize counter */
467     total = 0;
468     received = 0;
469     loss_cost_multiplier = 1;
470
471     /* calculate metric */
472     for (i=0; i<_datff_config.window; i++) {
473       received += ldata->buckets[i].received;
474       total += ldata->buckets[i].total;
475     }
476
477     if (ldata->missed_hellos > 0) {
478       int32_t interval;
479
480       interval = ldata->missed_hellos * ldata->hello_interval / 1000;
481       if (interval > _datff_config.window) {
482         received = 0;
483       }
484       else {
485         received = (received * (_datff_config.window - interval)) / _datff_config.window;
486       }
487     }
488
489     if (total == 0 || received == 0) {
490       nhdp_domain_set_incoming_metric(_datff_handler.domain, lnk, RFC7181_METRIC_MAX);
491       continue;
492     }
493
494     /* update link speed */
495     ldata->buckets[ldata->activePtr].scaled_speed = _get_scaled_rx_linkspeed(lnk);
496 #ifdef COLLECT_RAW_DATA
497     if (_rawdata_fd != -1) {
498       if (0 > write(_rawdata_fd, abuf_getptr(&_rawdata_buf), abuf_getlen(&_rawdata_buf))) {
499         close (_rawdata_fd);
500         _rawdata_fd = -1;
501       }
502       else {
503         fsync(_rawdata_fd);
504         abuf_clear(&_rawdata_buf);
505       }
506     }
507 #endif
508
509     OONF_DEBUG(LOG_FF_DAT, "Query incoming linkspeed for link %s: %"PRIu64,
510         netaddr_to_string(&nbuf, &lnk->if_addr),
511         (uint64_t)(ldata->buckets[ldata->activePtr].scaled_speed) * DATFF_LINKSPEED_MINIMUM);
512
513     /* get median scaled link speed and apply it to metric */
514     rx_bitrate = _get_median_rx_linkspeed(ldata);
515     if (rx_bitrate > DATFF_LINKSPEED_RANGE) {
516       metric = 1;
517     }
518     else {
519       metric = DATFF_LINKSPEED_RANGE / rx_bitrate;
520     }
521
522     /* calculate frame loss, use discrete values */
523     if (received < total) {
524       loss_cost_multiplier = DATFF_FRAME_SUCCESS_RANGE - (DATFF_FRAME_SUCCESS_RANGE * received) / total;
525       metric *= loss_cost_multiplier;
526     }
527
528     /* convert into something that can be transmitted over the network */
529     if (metric > RFC7181_METRIC_MAX) {
530       /* give the metric an upper bound */
531       metric_value = RFC7181_METRIC_MAX;
532     }
533     else if (metric < RFC7181_METRIC_MIN) {
534       metric_value = RFC7181_METRIC_MIN;
535     }
536     else if(!rfc7181_metric_encode(&encoded_metric, metric)) {
537       metric_value = rfc7181_metric_decode(&encoded_metric);
538     }
539     else {
540       /* metric encoding failed */
541       OONF_DEBUG(LOG_FF_DAT, "Metric encoding failed for %"PRIu64, metric);
542       metric_value = RFC7181_METRIC_MAX;
543     }
544
545     /* set metric for incoming link */
546     change_happened |= nhdp_domain_set_incoming_metric(_datff_handler.domain, lnk, metric_value);
547
548     OONF_DEBUG(LOG_FF_DAT, "New sampling rate for link %s (%s):"
549         " %d/%d (%d) = %u (speed=%"PRIu64 ")\n",
550         netaddr_to_string(&nbuf, &avl_first_element(&lnk->_addresses, laddr, _link_node)->link_addr),
551         nhdp_interface_get_name(lnk->local_if),
552         received, total, loss_cost_multiplier,
553         metric_value, (uint64_t)(rx_bitrate) * DATFF_LINKSPEED_MINIMUM);
554
555     /* update rolling buffer */
556     ldata->activePtr++;
557     if (ldata->activePtr >= _datff_config.window) {
558       ldata->activePtr = 0;
559     }
560     ldata->buckets[ldata->activePtr].received = 0;
561     ldata->buckets[ldata->activePtr].total = 0;
562   }
563
564   /* update neighbor metrics */
565   if (change_happened) {
566     nhdp_domain_neighborhood_changed();
567   }
568 }
569
570 /**
571  * Callback triggered when the next hellos should have been received
572  * @param ptr nhdp link
573  */
574 static void
575 _cb_hello_lost(void *ptr) {
576   struct link_datff_data *ldata;
577   struct nhdp_link *lnk;
578
579   lnk = ptr;
580   ldata = oonf_class_get_extension(&_link_extenstion, lnk);
581
582   if (ldata->activePtr != -1) {
583     ldata->missed_hellos++;
584
585     oonf_timer_set(&ldata->hello_lost_timer, ldata->hello_interval);
586
587     OONF_DEBUG(LOG_FF_DAT, "Missed Hello: %d", ldata->missed_hellos);
588   }
589 }
590
591 /**
592  * Callback to process all in RFC5444 packets for metric calculation. The
593  * Callback ignores all unicast packets.
594  * @param consumer
595  * @param context
596  * @return
597  */
598 static enum rfc5444_result
599 _cb_process_packet(struct rfc5444_reader_tlvblock_context *context) {
600   struct link_datff_data *ldata;
601   struct nhdp_interface *interf;
602   struct nhdp_laddr *laddr;
603   struct nhdp_link *lnk;
604   int total;
605
606   if (!_protocol->input_is_multicast) {
607     /* silently ignore unicasts */
608     return RFC5444_OKAY;
609   }
610
611   if (!context->has_pktseqno) {
612     struct netaddr_str buf;
613
614     OONF_WARN(LOG_FF_DAT, "Neighbor %s does not send packet sequence numbers, cannot collect datff data!",
615         netaddr_socket_to_string(&buf, _protocol->input_socket));
616     return RFC5444_OKAY;
617   }
618
619   /* get interface and link */
620   interf = nhdp_interface_get(_protocol->input_interface->name);
621   if (interf == NULL) {
622     /* silently ignore unknown interface */
623     return RFC5444_OKAY;
624   }
625
626   laddr = nhdp_interface_get_link_addr(interf, _protocol->input_address);
627   if (laddr == NULL) {
628     /* silently ignore unknown link*/
629     return RFC5444_OKAY;
630   }
631
632 #ifdef COLLECT_RAW_DATA
633   if (_rawdata_fd != -1)
634   {
635     uint64_t now;
636
637     now = oonf_clock_getNow();
638     _rawdata_count++;
639
640     if (now > _rawdata_end || _rawdata_count > _datff_config.rawdata_maxpackets) {
641       if (0 <= write(_rawdata_fd, abuf_getptr(&_rawdata_buf), abuf_getlen(&_rawdata_buf))) {
642         fsync(_rawdata_fd);
643       }
644       close(_rawdata_fd);
645       _rawdata_fd = -1;
646     }
647     else {
648       struct isonumber_str timebuf;
649       struct netaddr_str neighbuf;
650
651       abuf_appendf(&_rawdata_buf, "%s %s %u %d\n",
652           oonf_clock_toIntervalString(&timebuf, now),
653           netaddr_to_string(&neighbuf, &laddr->link_addr),
654           context->pkt_seqno,
655           _get_scaled_rx_linkspeed(laddr->link));
656     }
657   }
658 #endif
659
660   /* get link and its dat data */
661   lnk = laddr->link;
662   ldata = oonf_class_get_extension(&_link_extenstion, lnk);
663
664   if (ldata->activePtr == -1) {
665     ldata->activePtr = 0;
666     ldata->buckets[0].received = 1;
667     ldata->buckets[0].total = 1;
668     ldata->last_seq_nr = context->pkt_seqno;
669
670     return RFC5444_OKAY;
671   }
672
673   total = (int)(context->pkt_seqno) - (int)(ldata->last_seq_nr);
674   if (total < 0) {
675     total += 65536;
676   }
677
678   ldata->buckets[ldata->activePtr].received++;
679   ldata->buckets[ldata->activePtr].total += total;
680   ldata->last_seq_nr = context->pkt_seqno;
681
682   return RFC5444_OKAY;
683 }
684
685 /**
686  * Convert DATFF metric into string representation
687  * @param buf pointer to output buffer
688  * @param metric metric value
689  * @return pointer to output string
690  */
691 static const char *
692 _to_string(struct nhdp_metric_str *buf, uint32_t metric) {
693   if (metric < DATFF_LINKCOST_MINIMUM) {
694     strscpy(buf->buf, "unknown", sizeof (*buf));
695   }
696   else if (metric > DATFF_LINKCOST_MAXIMUM) {
697     strscpy(buf->buf, "infinite", sizeof(*buf));
698   }
699   else {
700     isonumber_from_u64((struct isonumber_str *)buf,
701         (uint32_t)(DATFF_LINKSPEED_MINIMUM) * (uint32_t)(DATFF_LINKSPEED_RANGE) / metric,
702         "bit/s", 0, true, false);
703   }
704   return buf->buf;
705 }
706
707 static const char *
708 _int_to_string(struct nhdp_metric_str *buf,
709     struct nhdp_domain *domain __attribute__((unused)),
710     struct nhdp_link *lnk) {
711   struct link_datff_data *ldata;
712   int64_t received = 0, total = 0;
713   int i;
714
715   ldata = oonf_class_get_extension(&_link_extenstion, lnk);
716
717   for (i=0; i<_datff_config.window; i++) {
718     received += ldata->buckets[i].received;
719     total += ldata->buckets[i].total;
720   }
721
722   snprintf(buf->buf, sizeof(*buf), "%"PRId64"/%"PRId64",%d kbit/s(missed=%d,lastseq=%u)",
723       received, total, _get_median_rx_linkspeed(ldata),
724       ldata->missed_hellos, ldata->last_seq_nr);
725
726   return buf->buf;
727 }
728
729 /**
730  * Callback triggered when configuration changes
731  */
732 static void
733 _cb_cfg_changed(void) {
734   bool first;
735
736   first = _datff_config.window == 0;
737
738   if (cfg_schema_tobin(&_datff_config, _datff_section.post,
739       _datff_entries, ARRAYSIZE(_datff_entries))) {
740     OONF_WARN(LOG_FF_DAT, "Cannot convert configuration for "
741         OONF_FF_DAT_METRIC_SUBSYSTEM);
742     return;
743   }
744
745   if (first) {
746     _link_extenstion.size +=
747         sizeof(struct link_datff_bucket) * _datff_config.window;
748
749     if (oonf_class_extension_add(&_link_extenstion)) {
750       return;
751     }
752
753     _rx_sort_array = calloc(_datff_config.window, sizeof(int));
754   }
755
756   /* start/change sampling timer */
757   if (_datff_handler.domain) {
758     oonf_timer_set(&_sampling_timer, _datff_config.interval);
759   }
760
761 #ifdef COLLECT_RAW_DATA
762   if (_rawdata_fd != -1) {
763     fsync(_rawdata_fd);
764     close(_rawdata_fd);
765     _rawdata_end = 0;
766     _rawdata_count = 0;
767   }
768
769   if (_datff_config.rawdata_start) {
770     _rawdata_fd = open(_datff_config.rawdata_file, O_CREAT | O_TRUNC | O_WRONLY,
771                 S_IRUSR|S_IWUSR);
772     if (_rawdata_fd != -1) {
773       abuf_clear(&_rawdata_buf);
774       abuf_appendf(&_rawdata_buf, "Time: %s\n", oonf_log_get_walltime());
775       _rawdata_end = oonf_clock_get_absolute(_datff_config.rawdata_maxtime);
776     }
777   }
778 #endif
779 }
780
781 /**
782  * Callback triggered to check validity of configuration section
783  * @param section_name name of section
784  * @param named configuration data of section
785  * @param out output buffer for error messages
786  * @return 0 if data is okay, -1 if an error happened
787  */
788 static int
789 _cb_cfg_validate(const char *section_name,
790     struct cfg_named_section *named, struct autobuf *out) {
791   struct ff_dat_config cfg;
792
793   /* clear temporary buffer */
794   memset(&cfg, 0, sizeof(cfg));
795
796   /* convert configuration to binary */
797   if (cfg_schema_tobin(&cfg, named,
798       _datff_entries, ARRAYSIZE(_datff_entries))) {
799     cfg_append_printable_line(out, "Could not parse hysteresis configuration in section %s",
800         section_name);
801     return -1;
802   }
803
804   if (_datff_config.window != 0 && cfg.window != _datff_config.window) {
805     cfg_append_printable_line(out, "%s: DATff window cannot be changed during runtime",
806         section_name);
807     return -1;
808   }
809   return 0;
810 }