Merge branch 'master' into mpr_rework
[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-2015, 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 /**
43  * @file
44  */
45
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <stdio.h>
51
52 #include "common/common_types.h"
53 #include "common/isonumber.h"
54 #include "common/autobuf.h"
55 #include "core/oonf_cfg.h"
56 #include "core/oonf_logging.h"
57 #include "core/oonf_subsystem.h"
58 #include "subsystems/oonf_class.h"
59 #include "subsystems/oonf_layer2.h"
60 #include "subsystems/oonf_rfc5444.h"
61 #include "subsystems/oonf_timer.h"
62
63 #include "nhdp/nhdp.h"
64 #include "nhdp/nhdp_domain.h"
65 #include "nhdp/nhdp_interfaces.h"
66
67 #include "ff_dat_metric/ff_dat_metric.h"
68
69 /* Definitions */
70 #define LOG_FF_DAT _olsrv2_ffdat_subsystem.logging
71
72 /**
73  * Configuration settings of DATFF Metric
74  */
75 struct ff_dat_config {
76   /*! Interval between two updates of the metric */
77   uint64_t interval;
78
79   /*! length of history in 'interval sized' memory cells */
80   int32_t window;
81
82   /*! true if metric should include link speed */
83   bool ett;
84
85   /*! selects how loss should be scaled */
86   int loss_exponent;
87
88   /*! true if MIC factor should be applied to metric */
89   bool mic;
90
91 #ifdef COLLECT_RAW_DATA
92   /* filename to store raw data into */
93   char *rawdata_file;
94
95   /* true if metric should collect raw data */
96   bool rawdata_start;
97
98   /* time in milliseconds until measurement stops */
99   uint64_t rawdata_maxtime;
100
101   /* maxmimum number of measured packets until measurement stops */
102   int rawdata_maxpackets;
103 #endif
104 };
105
106 /**
107  * a single history memory cell, stores the metric
108  * data for a single update interval
109  */
110 struct link_datff_bucket {
111   /*! number of RFC5444 packets received in time interval */
112   int received;
113
114   /*! sum of received and lost RFC5444 packets in time interval */
115   int total;
116
117   /*! link speed scaled to "minimum speed = 1" */
118   int scaled_speed;
119 };
120
121 /**
122  * Additional data for a nhdp_link class for metric calculation
123  */
124 struct link_datff_data {
125   /*! timer for measuring lost hellos when no further packets are received */
126   struct oonf_timer_instance hello_lost_timer;
127
128   /*! back pointer to NHDP link */
129   struct nhdp_link *nhdp_link;
130
131   /*! current position in history ringbuffer */
132   int activePtr;
133
134   /*! number of missed hellos based on timeouts since last received packet */
135   int missed_hellos;
136
137   /*! last received packet sequence number */
138   uint16_t last_seq_nr;
139
140   /*! remember the last transmitted packet loss for hysteresis */
141   uint32_t last_packet_success_rate;
142
143   /*! last known hello interval */
144   uint64_t hello_interval;
145
146   /*! estimated number of neighbors of this link */
147   uint32_t link_neigborhood;
148
149   /*! history ringbuffer */
150   struct link_datff_bucket buckets[0];
151 };
152
153 /* prototypes */
154 static int _init(void);
155 static void _cleanup(void);
156
157 static void _cb_enable_metric(void);
158 static void _cb_disable_metric(void);
159
160 static void _cb_link_added(void *);
161 static void _cb_link_changed(void *);
162 static void _cb_link_removed(void *);
163
164 static void _cb_dat_sampling(struct oonf_timer_instance *);
165 static void _calculate_link_neighborhood(struct nhdp_link *lnk,
166     struct link_datff_data *ldata);
167 static int _calculate_dynamic_loss_exponent(int link_neigborhood);
168 static uint32_t _apply_packet_loss(struct nhdp_link *lnk,
169     struct link_datff_data *ldata,
170     uint32_t metric, uint32_t received, uint32_t total);
171
172 static void _cb_hello_lost(struct oonf_timer_instance *);
173
174 static enum rfc5444_result _cb_process_packet(
175       struct rfc5444_reader_tlvblock_context *context);
176
177 static void _reset_missed_hello_timer(struct link_datff_data *);
178
179 static const char *_link_to_string(
180     struct nhdp_metric_str *buf, uint32_t metric);
181 static const char *_path_to_string(
182     struct nhdp_metric_str *buf, uint32_t metric, uint8_t hopcount);
183 static const char *_int_link_to_string(struct nhdp_metric_str *,
184     struct nhdp_link *);
185
186 static int _cb_cfg_validate(const char *section_name,
187     struct cfg_named_section *, struct autobuf *);
188 static void _cb_cfg_changed(void);
189
190 /* plugin declaration */
191
192 /**
193  * loss scaling options
194  */
195 enum idx_loss_scaling {
196   /*! linear loss scaling */
197   IDX_LOSS_LINEAR,
198
199   /*! quadratic loss scaling */
200   IDX_LOSS_QUADRATIC,
201
202   /*! cubic loss scaling */
203   IDX_LOSS_CUBIC,
204
205   /*! dynamic loss scaling */
206   IDX_LOSS_DYNAMIC,
207 };
208 static const char *LOSS_SCALING[] = {
209   [IDX_LOSS_LINEAR]    = "linear",
210   [IDX_LOSS_QUADRATIC] = "quadratic",
211   [IDX_LOSS_CUBIC]     = "cubic",
212   [IDX_LOSS_DYNAMIC]   = "dynamic",
213 };
214
215 static struct cfg_schema_entry _datff_entries[] = {
216   CFG_MAP_CLOCK_MIN(ff_dat_config, interval, "interval", "1.0",
217       "Time interval between recalculations of metric", 100),
218   CFG_MAP_INT32_MINMAX(ff_dat_config, window, "window", "64",
219       "Number of intervals to calculate average metric", 0, false, 2, 65535),
220   CFG_MAP_BOOL(ff_dat_config, ett, "airtime", "true",
221       "Activates the handling of linkspeed within the metric, set to false to"
222       " downgrade to ETX metric"),
223   CFG_MAP_CHOICE(ff_dat_config, loss_exponent, "loss_exponent", "linear",
224       "scaling of the packet loss influence on the metric", LOSS_SCALING),
225   CFG_MAP_BOOL(ff_dat_config, mic, "mic", "false",
226       "Activates the MIC penalty-factor for link metrics"),
227 #ifdef COLLECT_RAW_DATA
228   CFG_MAP_STRING(ff_dat_config, rawdata_file, "raw_filename", "/tmp/olsrv2_dat_metric.txt",
229       "File to write recorded data into"),
230   CFG_MAP_BOOL(ff_dat_config, rawdata_start, "raw_start", "false",
231       "Set to true to activate rawdata measurement"),
232   CFG_MAP_CLOCK(ff_dat_config, rawdata_maxtime, "raw_maxtime", "3600000",
233       "Time until measurement stops"),
234   CFG_MAP_INT32_MINMAX(ff_dat_config, rawdata_maxpackets, "raw_maxpackets", "20000",
235       "Maximum number of packets to record", 0, false, 1, INT32_MAX),
236 #endif
237 };
238
239 /* Subsystem definition */
240 static struct cfg_schema_section _datff_section = {
241   .type = OONF_FF_DAT_METRIC_SUBSYSTEM,
242   .cb_validate = _cb_cfg_validate,
243   .cb_delta_handler = _cb_cfg_changed,
244   .entries = _datff_entries,
245   .entry_count = ARRAYSIZE(_datff_entries),
246 };
247
248 static struct ff_dat_config _datff_config;
249
250 static const char *_dependencies[] = {
251   OONF_CLASS_SUBSYSTEM,
252   OONF_LAYER2_SUBSYSTEM,
253   OONF_RFC5444_SUBSYSTEM,
254   OONF_TIMER_SUBSYSTEM,
255   OONF_NHDP_SUBSYSTEM,
256 };
257 static struct oonf_subsystem _olsrv2_ffdat_subsystem = {
258   .name = OONF_FF_DAT_METRIC_SUBSYSTEM,
259   .dependencies = _dependencies,
260   .dependencies_count = ARRAYSIZE(_dependencies),
261   .descr = "OLSRv2 Funkfeuer Directional Airtime Metric plugin",
262   .author = "Henning Rogge",
263
264   .cfg_section = &_datff_section,
265
266   .init = _init,
267   .cleanup = _cleanup,
268 };
269 DECLARE_OONF_PLUGIN(_olsrv2_ffdat_subsystem);
270
271 /* RFC5444 packet listener */
272 static struct oonf_rfc5444_protocol *_protocol;
273
274 static struct rfc5444_reader_tlvblock_consumer _packet_consumer = {
275   .order = RFC5444_LQ_PARSER_PRIORITY,
276   .default_msg_consumer = true,
277   .start_callback = _cb_process_packet,
278 };
279
280 /* storage extension and listeners */
281 static struct oonf_class_extension _link_extenstion = {
282   .ext_name = "datff linkmetric",
283   .class_name = NHDP_CLASS_LINK,
284   .size = sizeof(struct link_datff_data),
285
286   .cb_add = _cb_link_added,
287   .cb_change = _cb_link_changed,
288   .cb_remove = _cb_link_removed,
289 };
290
291 /* timer for sampling in RFC5444 packets */
292 static struct oonf_timer_class _sampling_timer_info = {
293   .name = "Sampling timer for DATFF-metric",
294   .callback = _cb_dat_sampling,
295   .periodic = true,
296 };
297
298 static struct oonf_timer_instance _sampling_timer = {
299   .class = &_sampling_timer_info,
300 };
301
302 /* timer class to measure interval between Hellos */
303 static struct oonf_timer_class _hello_lost_info = {
304   .name = "Hello lost timer for DATFF-metric",
305   .callback = _cb_hello_lost,
306 };
307
308 /* nhdp metric handler */
309 static struct nhdp_domain_metric _datff_handler = {
310   .name = OONF_FF_DAT_METRIC_SUBSYSTEM,
311
312   .metric_minimum = DATFF_LINKCOST_MINIMUM,
313   .metric_maximum = DATFF_LINKCOST_MAXIMUM,
314
315   .link_to_string = _link_to_string,
316   .path_to_string = _path_to_string,
317   .internal_link_to_string = _int_link_to_string,
318
319   .enable = _cb_enable_metric,
320   .disable = _cb_disable_metric,
321 };
322
323 /* Temporary buffer to sort incoming link speed for median calculation */
324 static int *_rx_sort_array = NULL;
325
326 /* rawdata collection */
327 #ifdef COLLECT_RAW_DATA
328 static struct autobuf _rawdata_buf;
329 static int _rawdata_fd = -1;
330 static uint64_t _rawdata_end = 0;
331 static int _rawdata_count = 0;
332 #endif
333
334 /**
335  * Initialize plugin
336  * @return -1 if an error happened, 0 otherwise
337  */
338 static int
339 _init(void) {
340   if (nhdp_domain_metric_add(&_datff_handler)) {
341     return -1;
342   }
343
344   oonf_timer_add(&_sampling_timer_info);
345   oonf_timer_add(&_hello_lost_info);
346
347   _protocol = oonf_rfc5444_get_default_protocol();
348
349   oonf_rfc5444_add_protocol_pktseqno(_protocol);
350 #ifdef COLLECT_RAW_DATA
351   abuf_init(&_rawdata_buf);
352 #endif
353   return 0;
354 }
355
356 /**
357  * Cleanup plugin
358  */
359 static void
360 _cleanup(void) {
361 #ifdef COLLECT_RAW_DATA
362   if (_rawdata_fd != -1) {
363     fsync(_rawdata_fd);
364     close(_rawdata_fd);
365   }
366   abuf_free(&_rawdata_buf);
367   free(_datff_config.rawdata_file);
368 #endif
369   /* free sorting array */
370   free (_rx_sort_array);
371
372   /* remove metric from core */
373   nhdp_domain_metric_remove(&_datff_handler);
374
375   oonf_rfc5444_remove_protocol_pktseqno(_protocol);
376   _protocol = NULL;
377
378   oonf_class_extension_remove(&_link_extenstion);
379
380   oonf_timer_stop(&_sampling_timer);
381
382   oonf_timer_remove(&_sampling_timer_info);
383   oonf_timer_remove(&_hello_lost_info);
384 }
385
386 static void
387 _cb_enable_metric(void) {
388   struct nhdp_link *lnk;
389
390   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
391     _cb_link_added(lnk);
392   }
393
394   rfc5444_reader_add_packet_consumer(&_protocol->reader, &_packet_consumer, NULL, 0);
395   oonf_timer_set(&_sampling_timer, _datff_config.interval);
396 }
397
398 static void
399 _cb_disable_metric(void) {
400   struct nhdp_link *lnk;
401
402   oonf_timer_stop(&_sampling_timer);
403   rfc5444_reader_remove_packet_consumer(&_protocol->reader, &_packet_consumer);
404
405   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
406     _cb_link_removed(lnk);
407   }
408 }
409
410 /**
411  * Callback triggered when a new nhdp link is added
412  * @param ptr nhdp link
413  */
414 static void
415 _cb_link_added(void *ptr) {
416   struct link_datff_data *data;
417   struct nhdp_link *lnk;
418   int i;
419
420   lnk = ptr;
421   data = oonf_class_get_extension(&_link_extenstion, lnk);
422
423   memset(data, 0, sizeof(*data));
424   data->activePtr = -1;
425   for (i = 0; i<_datff_config.window; i++) {
426     data->buckets[i].total = 1;
427     data->buckets[i].scaled_speed = 0;
428   }
429
430   /* start 'hello lost' timer for link */
431   data->hello_lost_timer.class = &_hello_lost_info;
432 }
433
434 /**
435  * Callback triggered when a new nhdp link is changed
436  * @param ptr nhdp link
437  */
438 static void
439 _cb_link_changed(void *ptr) {
440   struct link_datff_data *data;
441   struct nhdp_link *lnk;
442
443   lnk = ptr;
444   data = oonf_class_get_extension(&_link_extenstion, lnk);
445
446   if (lnk->itime_value > 0) {
447     data->hello_interval = lnk->itime_value;
448   }
449   else {
450     data->hello_interval = lnk->vtime_value;
451   }
452
453   _reset_missed_hello_timer(data);
454 }
455
456 /**
457  * Callback triggered when a nhdp link is removed from the database
458  * @param ptr nhdp link
459  */
460 static void
461 _cb_link_removed(void *ptr) {
462   struct link_datff_data *data;
463
464   data = oonf_class_get_extension(&_link_extenstion, ptr);
465
466   oonf_timer_stop(&data->hello_lost_timer);
467 }
468
469 static int
470 _int_comparator(const void *p1, const void *p2) {
471   const int *i1 = (int *)p1;
472   const int *i2 = (int *)p2;
473
474   if (*i1 > *i2) {
475     return 1;
476   }
477   else if (*i1 < *i2) {
478     return -1;
479   }
480   return 0;
481 }
482
483 static int
484 _get_median_rx_linkspeed(struct link_datff_data *ldata) {
485   int zero_count;
486   int window;
487   int i;
488
489   zero_count = 0;
490   for (i=0; i<_datff_config.window; i++) {
491     _rx_sort_array[i] = ldata->buckets[i].scaled_speed;
492     if (_rx_sort_array[i] == 0) {
493       zero_count++;
494     }
495   }
496
497   window = _datff_config.window - zero_count;
498   if (window == 0) {
499     return 1;
500   }
501
502   qsort(_rx_sort_array, _datff_config.window, sizeof(int), _int_comparator);
503
504   return _rx_sort_array[zero_count + window/2];
505 }
506
507 /**
508  * Retrieves the speed of a nhdp link, scaled to the minimum link speed
509  * of this metric.
510  * @param lnk nhdp link
511  * @return scaled link speed, 1 if could not be retrieved.
512  */
513 static int
514 _get_scaled_rx_linkspeed(struct nhdp_link *lnk) {
515   struct os_interface *os_if;
516   const struct oonf_layer2_data *l2data;
517   int rate;
518
519   if (!_datff_config.ett) {
520     /* ETT feature is switched off */
521     return 1;
522   }
523
524   /* get local interface data  */
525   os_if = nhdp_interface_get_if_listener(lnk->local_if)->data;
526
527   l2data = oonf_layer2_neigh_query(
528       os_if->name, &lnk->remote_mac, OONF_LAYER2_NEIGH_RX_BITRATE);
529   if (!l2data) {
530     return 1;
531   }
532
533   /* round up */
534   rate = (oonf_layer2_get_value(l2data) + DATFF_LINKSPEED_MINIMUM - 1) / DATFF_LINKSPEED_MINIMUM;
535   if (rate < 1) {
536     return 1;
537   }
538   if (rate > DATFF_LINKSPEED_RANGE) {
539     return DATFF_LINKSPEED_RANGE;
540   }
541   return rate;
542 }
543
544 static void
545 _reset_missed_hello_timer(struct link_datff_data *);
546
547 /**
548  * Timer callback to sample new metric values into bucket
549  * @param ptr nhdp link
550  */
551 static void
552 _cb_dat_sampling(struct oonf_timer_instance *ptr __attribute__((unused))) {
553   struct rfc7181_metric_field encoded_metric;
554   struct link_datff_data *ldata;
555   struct nhdp_link *lnk;
556   uint32_t total, received;
557   uint64_t metric;
558   uint32_t metric_value;
559   int rx_bitrate;
560   int i;
561
562 #ifdef OONF_LOG_DEBUG_INFO
563   struct nhdp_laddr *laddr;
564   struct netaddr_str nbuf;
565 #endif
566
567   OONF_DEBUG(LOG_FF_DAT, "Calculate Metric from sampled data");
568
569   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
570     ldata = oonf_class_get_extension(&_link_extenstion, lnk);
571
572     if (ldata->activePtr == -1) {
573       /* still no data for this link */
574       continue;
575     }
576
577     /* initialize counter */
578     total = 0;
579     received = 0;
580
581     /* calculate metric */
582     for (i=0; i<_datff_config.window; i++) {
583       received += ldata->buckets[i].received;
584       total += ldata->buckets[i].total;
585     }
586
587     if (ldata->missed_hellos > 0) {
588       int32_t interval;
589
590       interval = ldata->missed_hellos * ldata->hello_interval / 1000;
591       if (interval > _datff_config.window) {
592         received = 0;
593       }
594       else {
595         received = (received * (_datff_config.window - interval)) / _datff_config.window;
596       }
597     }
598
599     /* update link speed */
600     ldata->buckets[ldata->activePtr].scaled_speed = _get_scaled_rx_linkspeed(lnk);
601 #ifdef COLLECT_RAW_DATA
602     if (_rawdata_fd != -1) {
603       if (0 > write(_rawdata_fd, abuf_getptr(&_rawdata_buf), abuf_getlen(&_rawdata_buf))) {
604         close (_rawdata_fd);
605         _rawdata_fd = -1;
606       }
607       else {
608         fsync(_rawdata_fd);
609         abuf_clear(&_rawdata_buf);
610       }
611     }
612 #endif
613
614     OONF_DEBUG(LOG_FF_DAT, "Query incoming linkspeed for link %s: %"PRIu64,
615         netaddr_to_string(&nbuf, &lnk->if_addr),
616         (uint64_t)(ldata->buckets[ldata->activePtr].scaled_speed) * DATFF_LINKSPEED_MINIMUM);
617
618     /* get median scaled link speed and apply it to metric */
619     rx_bitrate = _get_median_rx_linkspeed(ldata);
620     if (rx_bitrate > DATFF_LINKSPEED_RANGE) {
621       metric = 1;
622     }
623     else {
624       metric = DATFF_LINKSPEED_RANGE / rx_bitrate;
625     }
626
627     /* calculate frame loss, use discrete values */
628     if (total == 0 || received == 0
629         || received * DATFF_FRAME_SUCCESS_RANGE <= total) {
630       metric *= DATFF_FRAME_SUCCESS_RANGE;
631     }
632     else {
633       metric = _apply_packet_loss(lnk, ldata, metric, received, total);
634     }
635
636     /* convert into something that can be transmitted over the network */
637     if (metric > RFC7181_METRIC_MAX) {
638       /* give the metric an upper bound */
639       metric_value = RFC7181_METRIC_MAX;
640     }
641     else if (metric < RFC7181_METRIC_MIN) {
642       metric_value = RFC7181_METRIC_MIN;
643     }
644     else if(!rfc7181_metric_encode(&encoded_metric, metric)) {
645       metric_value = rfc7181_metric_decode(&encoded_metric);
646     }
647     else {
648       /* metric encoding failed */
649       OONF_DEBUG(LOG_FF_DAT, "Metric encoding failed for %"PRIu64, metric);
650       metric_value = RFC7181_METRIC_MAX;
651     }
652
653     /* set metric for incoming link */
654     nhdp_domain_set_incoming_metric(
655         &_datff_handler, lnk, metric_value);
656
657     OONF_DEBUG(LOG_FF_DAT, "New sampling rate for link %s (%s):"
658         " %d/%d = %u (speed=%"PRIu64 ")\n",
659         netaddr_to_string(&nbuf, &avl_first_element(&lnk->_addresses, laddr, _link_node)->link_addr),
660         nhdp_interface_get_name(lnk->local_if),
661         received, total, metric_value, (uint64_t)(rx_bitrate) * DATFF_LINKSPEED_MINIMUM);
662
663     /* update rolling buffer */
664     ldata->activePtr++;
665     if (ldata->activePtr >= _datff_config.window) {
666       ldata->activePtr = 0;
667     }
668     ldata->buckets[ldata->activePtr].received = 0;
669     ldata->buckets[ldata->activePtr].total = 0;
670   }
671 }
672
673 /**
674  * Calculate how many neigbors a link has
675  * @param lnk nhdp link
676  * @param data ff data link data
677  */
678 static void
679 _calculate_link_neighborhood(struct nhdp_link *lnk, struct link_datff_data *data) {
680   struct nhdp_l2hop *l2hop;
681   struct nhdp_laddr *laddr;
682   int count;
683
684   /* local link neighbors */
685   count = lnk->local_if->_link_originators.count;
686
687   /* links twohop neighbors */
688   avl_for_each_element(&lnk->_2hop, l2hop, _link_node) {
689     if (l2hop->same_interface
690         && !avl_find_element(&lnk->local_if->_link_addresses, &l2hop->twohop_addr, laddr, _if_node)) {
691       count ++;
692     }
693   }
694
695   data->link_neigborhood = count;
696 }
697
698 /**
699  * Calculate the loss exponentiation based on the link neigborhood size
700  * @param link_neigborhood link neighborhood count
701  * @return loss exponent
702  */
703 static int
704 _calculate_dynamic_loss_exponent(int link_neigborhood) {
705   if (link_neigborhood < 4) {
706     return 1;
707   }
708   if (link_neigborhood < 9) {
709     return 2;
710   }
711   if (link_neigborhood < 15) {
712     return 3;
713   }
714   return 4;
715 }
716
717 /**
718  * Select discrete packet loss values and apply a hysteresis
719  * @param lnk nhdp link
720  * @param ldata link data object
721  * @param metric metric based on linkspeed
722  * @param received received packets
723  * @param total total packets
724  * @return metric including linkspeed and packet loss
725  */
726 static uint32_t
727 _apply_packet_loss(struct nhdp_link *lnk,
728     struct link_datff_data *ldata, uint32_t metric,
729     uint32_t received, uint32_t total) {
730   int64_t success_scaled_by_1000;
731   int64_t last_scaled_by_1000;
732   int loss_exponent;
733
734   last_scaled_by_1000 = (int64_t)ldata->last_packet_success_rate * 1000ll;
735   success_scaled_by_1000 = ((int64_t)DATFF_FRAME_SUCCESS_RANGE * 1000ll) * received / total;
736
737   if (success_scaled_by_1000 >= last_scaled_by_1000 - 750
738       && success_scaled_by_1000 <= last_scaled_by_1000 + 750) {
739     /* keep old loss rate */
740     success_scaled_by_1000 = last_scaled_by_1000;
741   }
742   else {
743     /* remember new loss rate */
744     ldata->last_packet_success_rate = success_scaled_by_1000/1000;
745   }
746
747   _calculate_link_neighborhood(lnk, ldata);
748
749   switch (_datff_config.loss_exponent) {
750     case IDX_LOSS_LINEAR:
751       loss_exponent = 1;
752       break;
753     case IDX_LOSS_QUADRATIC:
754       loss_exponent = 2;
755       break;
756     case IDX_LOSS_CUBIC:
757       loss_exponent = 3;
758       break;
759     case IDX_LOSS_DYNAMIC:
760       loss_exponent = _calculate_dynamic_loss_exponent(ldata->link_neigborhood);
761       break;
762     default:
763       loss_exponent = 1;
764       break;
765   }
766
767   while (loss_exponent) {
768     metric = ((int64_t)metric * (int64_t)DATFF_FRAME_SUCCESS_RANGE * 1000ll + 500ll) / success_scaled_by_1000;
769     loss_exponent--;
770   }
771
772   if (_datff_config.mic) {
773     metric = metric * (int64_t)ldata->link_neigborhood;
774   }
775   return metric;
776 }
777
778 /**
779  * Callback triggered when the next hellos should have been received
780  * @param ptr timer instance that fired
781  */
782 static void
783 _cb_hello_lost(struct oonf_timer_instance *ptr) {
784   struct link_datff_data *ldata;
785
786   ldata = container_of(ptr, struct link_datff_data, hello_lost_timer);
787
788   if (ldata->activePtr != -1) {
789     ldata->missed_hellos++;
790
791     oonf_timer_set(&ldata->hello_lost_timer, ldata->hello_interval);
792
793     OONF_DEBUG(LOG_FF_DAT, "Missed Hello: %d", ldata->missed_hellos);
794   }
795 }
796
797 /**
798  * Callback to process all in RFC5444 packets for metric calculation. The
799  * Callback ignores all unicast packets.
800  * @param consumer
801  * @param context
802  * @return
803  */
804 static enum rfc5444_result
805 _cb_process_packet(struct rfc5444_reader_tlvblock_context *context) {
806   struct link_datff_data *ldata;
807   struct nhdp_interface *interf;
808   struct nhdp_laddr *laddr;
809   struct nhdp_link *lnk;
810   int total;
811
812   if (!_protocol->input_is_multicast) {
813     /* silently ignore unicasts */
814     return RFC5444_OKAY;
815   }
816
817   if (!context->has_pktseqno) {
818     struct netaddr_str buf;
819
820     OONF_WARN(LOG_FF_DAT, "Neighbor %s does not send packet sequence numbers, cannot collect datff data!",
821         netaddr_socket_to_string(&buf, _protocol->input_socket));
822     return RFC5444_OKAY;
823   }
824
825   /* get interface and link */
826   interf = nhdp_interface_get(_protocol->input_interface->name);
827   if (interf == NULL) {
828     /* silently ignore unknown interface */
829     return RFC5444_OKAY;
830   }
831
832   laddr = nhdp_interface_get_link_addr(interf, _protocol->input_address);
833   if (laddr == NULL) {
834     /* silently ignore unknown link*/
835     return RFC5444_OKAY;
836   }
837
838 #ifdef COLLECT_RAW_DATA
839   if (_rawdata_fd != -1)
840   {
841     uint64_t now;
842
843     now = oonf_clock_getNow();
844     _rawdata_count++;
845
846     if (now > _rawdata_end || _rawdata_count > _datff_config.rawdata_maxpackets) {
847       if (0 <= write(_rawdata_fd, abuf_getptr(&_rawdata_buf), abuf_getlen(&_rawdata_buf))) {
848         fsync(_rawdata_fd);
849       }
850       close(_rawdata_fd);
851       _rawdata_fd = -1;
852     }
853     else {
854       struct isonumber_str timebuf;
855       struct netaddr_str neighbuf;
856
857       abuf_appendf(&_rawdata_buf, "%s %s %u %d\n",
858           oonf_clock_toIntervalString(&timebuf, now),
859           netaddr_to_string(&neighbuf, &laddr->link_addr),
860           context->pkt_seqno,
861           _get_scaled_rx_linkspeed(laddr->link));
862     }
863   }
864 #endif
865
866   /* get link and its dat data */
867   lnk = laddr->link;
868   ldata = oonf_class_get_extension(&_link_extenstion, lnk);
869
870   if (ldata->activePtr == -1) {
871     ldata->activePtr = 0;
872     ldata->buckets[0].received = 1;
873     ldata->buckets[0].total = 1;
874     ldata->last_seq_nr = context->pkt_seqno;
875
876     return RFC5444_OKAY;
877   }
878
879   total = (int)(context->pkt_seqno) - (int)(ldata->last_seq_nr);
880   if (total < 0) {
881     total += 65536;
882   }
883
884   ldata->buckets[ldata->activePtr].received++;
885   ldata->buckets[ldata->activePtr].total += total;
886   ldata->last_seq_nr = context->pkt_seqno;
887
888   _reset_missed_hello_timer(ldata);
889
890   return RFC5444_OKAY;
891 }
892
893 static void
894 _reset_missed_hello_timer(struct link_datff_data *data) {
895   oonf_timer_set(&data->hello_lost_timer, (data->hello_interval * 3) / 2);
896
897   data->missed_hellos = 0;
898 }
899
900 /**
901  * Convert DATFF metric into string representation
902  * @param buf pointer to output buffer
903  * @param metric metric value
904  * @return pointer to output string
905  */
906 static const char *
907 _link_to_string(struct nhdp_metric_str *buf, uint32_t metric) {
908   uint64_t value;
909
910   if (metric < DATFF_LINKCOST_MINIMUM) {
911     value = (uint32_t)DATFF_LINKSPEED_MINIMUM
912         * (uint32_t)DATFF_LINKSPEED_RANGE;
913   }
914   else if (metric > DATFF_LINKCOST_MAXIMUM) {
915     strscpy(buf->buf, "infinite", sizeof(*buf));
916     return buf->buf;
917   }
918   else {
919     value = (uint32_t)(DATFF_LINKSPEED_MINIMUM) * (uint32_t)(DATFF_LINKSPEED_RANGE) / metric;
920   }
921   isonumber_from_u64((struct isonumber_str *)buf,
922       value, "bit/s", 0, true, false);
923   return buf->buf;
924 }
925
926 /**
927  * Convert DATFF path metric into string representation
928  * @param buf pointer to output buffer
929  * @param metric path metric value
930  * @return pointer to output string
931  */
932 static const char *
933 _path_to_string(struct nhdp_metric_str *buf, uint32_t metric, uint8_t hopcount) {
934   struct nhdp_metric_str mbuf;
935
936   if (hopcount == 0) {
937     /* prevent division by zero */
938     hopcount = 1;
939   }
940   snprintf(buf->buf, sizeof(*buf), "%s (%u hops)",
941       _link_to_string(&mbuf, metric / hopcount), hopcount);
942   return buf->buf;
943 }
944
945 static const char *
946 _int_link_to_string(struct nhdp_metric_str *buf, struct nhdp_link *lnk) {
947   struct link_datff_data *ldata;
948   int64_t received = 0, total = 0;
949   int i;
950
951   ldata = oonf_class_get_extension(&_link_extenstion, lnk);
952
953   for (i=0; i<_datff_config.window; i++) {
954     received += ldata->buckets[i].received;
955     total += ldata->buckets[i].total;
956   }
957
958   snprintf(buf->buf, sizeof(*buf), "p_recv=%"PRId64",p_total=%"PRId64","
959       "speed=%"PRId64",success=%u,missed_hello=%d,lastseq=%u,lneigh=%d",
960       received, total, (int64_t)_get_median_rx_linkspeed(ldata) * (int64_t)1024,
961       ldata->last_packet_success_rate, ldata->missed_hellos,
962       ldata->last_seq_nr, ldata->link_neigborhood);
963   return buf->buf;
964 }
965
966 /**
967  * Callback triggered when configuration changes
968  */
969 static void
970 _cb_cfg_changed(void) {
971   bool first;
972
973   first = _datff_config.window == 0;
974
975   if (cfg_schema_tobin(&_datff_config, _datff_section.post,
976       _datff_entries, ARRAYSIZE(_datff_entries))) {
977     OONF_WARN(LOG_FF_DAT, "Cannot convert configuration for "
978         OONF_FF_DAT_METRIC_SUBSYSTEM);
979     return;
980   }
981
982   if (first) {
983     _link_extenstion.size +=
984         sizeof(struct link_datff_bucket) * _datff_config.window;
985
986     if (oonf_class_extension_add(&_link_extenstion)) {
987       return;
988     }
989
990     _rx_sort_array = calloc(_datff_config.window, sizeof(int));
991   }
992
993   /* start/change sampling timer */
994   oonf_timer_set(&_sampling_timer, _datff_config.interval);
995
996 #ifdef COLLECT_RAW_DATA
997   if (_rawdata_fd != -1) {
998     fsync(_rawdata_fd);
999     close(_rawdata_fd);
1000     _rawdata_end = 0;
1001     _rawdata_count = 0;
1002   }
1003
1004   if (_datff_config.rawdata_start) {
1005     _rawdata_fd = open(_datff_config.rawdata_file, O_CREAT | O_TRUNC | O_WRONLY,
1006                 S_IRUSR|S_IWUSR);
1007     if (_rawdata_fd != -1) {
1008       abuf_clear(&_rawdata_buf);
1009       abuf_appendf(&_rawdata_buf, "Time: %s\n", oonf_log_get_walltime());
1010       _rawdata_end = oonf_clock_get_absolute(_datff_config.rawdata_maxtime);
1011     }
1012   }
1013 #endif
1014 }
1015
1016 /**
1017  * Callback triggered to check validity of configuration section
1018  * @param section_name name of section
1019  * @param named configuration data of section
1020  * @param out output buffer for error messages
1021  * @return 0 if data is okay, -1 if an error happened
1022  */
1023 static int
1024 _cb_cfg_validate(const char *section_name,
1025     struct cfg_named_section *named, struct autobuf *out) {
1026   struct ff_dat_config cfg;
1027
1028   /* clear temporary buffer */
1029   memset(&cfg, 0, sizeof(cfg));
1030
1031   /* convert configuration to binary */
1032   if (cfg_schema_tobin(&cfg, named,
1033       _datff_entries, ARRAYSIZE(_datff_entries))) {
1034     OONF_WARN(LOG_FF_DAT, "Could not convert "
1035         OONF_FF_DAT_METRIC_SUBSYSTEM " plugin configuration");
1036     return -1;
1037   }
1038
1039   if (_datff_config.window != 0 && cfg.window != _datff_config.window) {
1040     cfg_append_printable_line(out, "%s: DATff window cannot be changed during runtime",
1041         section_name);
1042     return -1;
1043   }
1044   return 0;
1045 }