Prevent division by zero through (malformed) RLQ value
[oonf.git] / src / 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 <errno.h>
47 #include <fcntl.h>
48 #include <stdio.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
51
52 #include <oonf/libcommon/autobuf.h>
53 #include <oonf/oonf.h>
54 #include <oonf/libcommon/isonumber.h>
55 #include <oonf/libcore/oonf_cfg.h>
56 #include <oonf/libcore/oonf_logging.h>
57 #include <oonf/libcore/oonf_subsystem.h>
58 #include <oonf/base/oonf_class.h>
59 #include <oonf/base/oonf_layer2.h>
60 #include <oonf/base/oonf_rfc5444.h>
61 #include <oonf/base/oonf_timer.h>
62
63 #include <oonf/nhdp/nhdp/nhdp.h>
64 #include <oonf/nhdp/nhdp/nhdp_domain.h>
65 #include <oonf/nhdp/nhdp/nhdp_interfaces.h>
66
67 #include <oonf/nhdp/ff_dat_metric/ff_dat_metric.h>
68
69 /* Definitions */
70 enum
71 {
72   DAT_SAMPLING_COUNT = 32,
73 };
74
75 /**
76  * Configuration settings of DATFF Metric
77  */
78 struct ff_dat_if_config {
79   /*! true if metric should include link speed */
80   bool ett;
81
82   /*! selects how loss should be scaled */
83   int loss_exponent;
84
85   /*! true if MIC factor should be applied to metric */
86   bool mic;
87
88   /*! true if metric should include unicast into calculation */
89   bool accept_unicast;
90
91   /*! timer for sampling interface data */
92   struct oonf_timer_instance _sampling_timer;
93
94   /*! true if we registered the interface */
95   bool registered;
96 };
97
98 /**
99  * a single history memory cell, stores the metric
100  * data for a single update interval
101  */
102 struct link_datff_bucket {
103   /*! number of RFC5444 packets received in time interval */
104   uint32_t received;
105
106   /*! sum of received and lost RFC5444 packets in time interval */
107   uint32_t total;
108
109   /*! link speed in bit/s */
110   int64_t raw_speed;
111 };
112
113 /**
114  * Additional data for a nhdp_link class for metric calculation
115  */
116 struct link_datff_data {
117   /*! timer for measuring lost hellos when no further packets are received */
118   struct oonf_timer_instance hello_lost_timer;
119
120   /*! true if buckets contain data */
121   bool contains_data;
122
123   /*! number of missed hellos based on timeouts since last received packet */
124   uint32_t missed_hellos;
125
126   /*! current position in history ringbuffer */
127   uint16_t activePtr;
128
129   /*! last received packet sequence number */
130   uint16_t last_seq_nr;
131
132   /*!
133    * remember the last transmitted packet success for hysteresis
134    * (scaled by 1000*DATFF_FRAME_SUCCESS_RANGE)
135    */
136   int64_t last_packet_loss_rate;
137
138   /*! last known hello interval */
139   uint64_t hello_interval;
140
141   /*! estimated number of neighbors of this link */
142   uint32_t link_neigborhood;
143
144   /*! history ringbuffer */
145   struct link_datff_bucket buckets[DAT_SAMPLING_COUNT];
146 };
147
148 /* prototypes */
149 static void _early_cfg_init(void);
150 static int _init(void);
151 static void _cleanup(void);
152
153 static void _cb_enable_metric(void);
154 static void _cb_disable_metric(void);
155
156 static void _cb_link_added(void *);
157 static void _cb_link_changed(void *);
158 static void _cb_link_removed(void *);
159
160 static void _cb_nhdpif_added(void *);
161 static void _cb_nhdpif_removed(void *);
162
163 static void _cb_dat_sampling(struct oonf_timer_instance *);
164 static void _calculate_link_neighborhood(struct nhdp_link *lnk, struct link_datff_data *ldata);
165 static int _calculate_dynamic_loss_exponent(int link_neigborhood);
166
167 static int64_t _get_raw_rx_linkspeed(struct nhdp_link *lnk);
168 static int _get_median_rx_linkspeed(struct link_datff_data *ldata);
169
170 static int64_t _get_bitrate_cost_factor(struct link_datff_data *ldata, struct nhdp_link *lnk, int64_t *bitrate);
171 static int64_t _get_lossrate_cost_factor(struct ff_dat_if_config *ifconfig, struct nhdp_link *lnk,
172   struct link_datff_data *ldata, uint32_t received, uint32_t total);
173 static int64_t _get_throughput_cost_factor(struct ff_dat_if_config *ifconfig, struct nhdp_link *lnk,
174   struct link_datff_data *ldata, uint32_t received, uint32_t total);
175 static int64_t _get_mic_cost_factor(struct ff_dat_if_config *ifconfig, struct nhdp_link *lnk,
176   struct link_datff_data *ldata);
177
178 static uint64_t _shape_metric(uint64_t metric, const char *ifname, struct netaddr *link_id);
179
180 static void _cb_hello_lost(struct oonf_timer_instance *);
181
182 static bool _shall_process_packet(struct nhdp_interface *, struct ff_dat_if_config *ifconfig);
183
184 static enum rfc5444_result _cb_process_packet(struct rfc5444_reader_tlvblock_context *context);
185
186 static void _reset_missed_hello_timer(struct link_datff_data *);
187
188 #if 0
189 // TODO: implement
190 static enum nhdp_metric_result _get_dat_metric(struct nhdp_domain *domain, uint32_t *metric, struct oonf_layer2_neigh *neigh);
191 #endif
192
193 static const char *_link_to_string(struct nhdp_metric_str *buf, uint32_t metric);
194 static const char *_path_to_string(struct nhdp_metric_str *buf, uint32_t metric, uint8_t hopcount);
195 static const char *_int_link_to_string(struct nhdp_metric_str *, struct nhdp_link *);
196
197 static void _cb_cfg_changed(void);
198
199 /* plugin declaration */
200
201 /**
202  * loss scaling options
203  */
204 enum idx_loss_scaling
205 {
206   /*! linear loss scaling */
207   IDX_LOSS_LINEAR,
208
209   /*! quadratic loss scaling */
210   IDX_LOSS_QUADRATIC,
211
212   /*! cubic loss scaling */
213   IDX_LOSS_CUBIC,
214
215   /*! dynamic loss scaling */
216   IDX_LOSS_DYNAMIC,
217 };
218 static const char *LOSS_SCALING[] = {
219   [IDX_LOSS_LINEAR] = "linear",
220   [IDX_LOSS_QUADRATIC] = "quadratic",
221   [IDX_LOSS_CUBIC] = "cubic",
222   [IDX_LOSS_DYNAMIC] = "dynamic",
223 };
224
225 static struct cfg_schema_entry _datff_entries[] = {
226   CFG_MAP_BOOL(ff_dat_if_config, ett, "ffdat_airtime", "true",
227     "Activates the handling of linkspeed within the metric, set to false to"
228     " downgrade to ETX metric"),
229   CFG_MAP_CHOICE(ff_dat_if_config, loss_exponent, "ffdat_loss_exponent", "linear",
230     "scaling of the packet loss influence on the metric", LOSS_SCALING),
231   CFG_MAP_BOOL(ff_dat_if_config, mic, "ffdat_mic", "false", "Activates the MIC penalty-factor for link metrics"),
232   CFG_MAP_BOOL(ff_dat_if_config, accept_unicast, "ffdat_unicast", "false", "Include unicast into metric calculation"),
233 };
234
235 /* Subsystem definition */
236 static struct cfg_schema_section _datff_section = {
237   CFG_OSIF_SCHEMA_INTERFACE_SECTION_INIT,
238
239   .cb_delta_handler = _cb_cfg_changed,
240   .entries = _datff_entries,
241   .entry_count = ARRAYSIZE(_datff_entries),
242 };
243
244 static const char *_dependencies[] = {
245   OONF_CLASS_SUBSYSTEM,
246   OONF_LAYER2_SUBSYSTEM,
247   OONF_RFC5444_SUBSYSTEM,
248   OONF_TIMER_SUBSYSTEM,
249   OONF_NHDP_SUBSYSTEM,
250   OONF_OS_INTERFACE_SUBSYSTEM,
251 };
252 static struct oonf_subsystem _nhdp_ffdat_subsystem = {
253   .name = OONF_FF_DAT_METRIC_SUBSYSTEM,
254   .dependencies = _dependencies,
255   .dependencies_count = ARRAYSIZE(_dependencies),
256   .descr = "NHDP Funkfeuer Directional Airtime Metric plugin",
257   .author = "Henning Rogge",
258
259   .cfg_section = &_datff_section,
260
261   .early_cfg_init = _early_cfg_init,
262   .init = _init,
263   .cleanup = _cleanup,
264 };
265 DECLARE_OONF_PLUGIN(_nhdp_ffdat_subsystem);
266
267 /* RFC5444 packet listener */
268 static struct oonf_rfc5444_protocol *_protocol;
269
270 static struct rfc5444_reader_tlvblock_consumer _packet_consumer = {
271   .order = RFC5444_LQ_PARSER_PRIORITY,
272   .default_msg_consumer = true,
273   .start_callback = _cb_process_packet,
274 };
275
276 /* storage extension and listeners */
277 static struct oonf_class_extension _link_extenstion = {
278   .ext_name = "datff linkmetric",
279   .class_name = NHDP_CLASS_LINK,
280   .size = sizeof(struct link_datff_data),
281
282   .cb_add = _cb_link_added,
283   .cb_change = _cb_link_changed,
284   .cb_remove = _cb_link_removed,
285 };
286
287 static struct oonf_class_extension _nhdpif_extenstion = {
288   .ext_name = "datff linkmetric",
289   .class_name = NHDP_CLASS_INTERFACE,
290   .size = sizeof(struct ff_dat_if_config),
291
292   .cb_add = _cb_nhdpif_added,
293   .cb_remove = _cb_nhdpif_removed,
294 };
295
296 /* timer for sampling in RFC5444 packets */
297 static struct oonf_timer_class _sampling_timer_info = {
298   .name = "Sampling timer for DATFF-metric",
299   .callback = _cb_dat_sampling,
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 /* layer2 originator for BC loss */
309 static struct oonf_layer2_origin _ffdat_origin = {
310   .name = "ffdat measured data",
311   .proactive = true,
312   /* not as reliable as we would like because we are measuring the broadcast loss */
313   .priority = OONF_LAYER2_ORIGIN_RELIABLE - 1,
314 };
315
316 /* nhdp metric handler */
317 static const enum oonf_layer2_neighbor_index _required_l2neigh[] = {
318   OONF_LAYER2_NEIGH_RX_BITRATE,
319 };
320
321 static struct nhdp_domain_metric _datff_handler = {
322   .name = OONF_FF_DAT_METRIC_SUBSYSTEM,
323
324   .metric_minimum = DATFF_LINKCOST_MINIMUM,
325   .metric_maximum = DATFF_LINKCOST_MAXIMUM,
326
327   .link_to_string = _link_to_string,
328   .path_to_string = _path_to_string,
329   .internal_link_to_string = _int_link_to_string,
330
331   .enable = _cb_enable_metric,
332   .disable = _cb_disable_metric,
333
334   .required_l2neigh_data = _required_l2neigh,
335   .required_l2neigh_count = ARRAYSIZE(_required_l2neigh),
336
337 #if 0
338 // TODO: implement
339   .cb_get_metric = _get_dat_metric,
340 #endif
341 };
342
343 /* Temporary buffer to sort incoming link speed for median calculation */
344 static int _rx_sort_array[DAT_SAMPLING_COUNT] = { 0 };
345
346 /* ff_dat has multiple logging targets */
347 enum oonf_log_source LOG_FF_DAT;
348 enum oonf_log_source LOG_FF_DAT_RAW;
349
350 /**
351  * Initialize additional logging sources for ffdat
352  */
353 static void
354 _early_cfg_init(void) {
355   LOG_FF_DAT = _nhdp_ffdat_subsystem.logging;
356   LOG_FF_DAT_RAW = oonf_log_register_source(OONF_FF_DAT_METRIC_SUBSYSTEM "_raw");
357 }
358
359 /**
360  * Initialize plugin
361  * @return -1 if an error happened, 0 otherwise
362  */
363 static int
364 _init(void) {
365   if (nhdp_domain_metric_add(&_datff_handler)) {
366     return -1;
367   }
368
369   if (oonf_class_extension_add(&_nhdpif_extenstion)) {
370     nhdp_domain_metric_remove(&_datff_handler);
371     return -1;
372   }
373   if (oonf_class_extension_add(&_link_extenstion)) {
374     oonf_class_extension_remove(&_link_extenstion);
375     nhdp_domain_metric_remove(&_datff_handler);
376     return -1;
377   }
378   oonf_timer_add(&_sampling_timer_info);
379   oonf_timer_add(&_hello_lost_info);
380
381   _protocol = oonf_rfc5444_get_default_protocol();
382   oonf_rfc5444_add_protocol_pktseqno(_protocol);
383
384   oonf_layer2_origin_add(&_ffdat_origin);
385   return 0;
386 }
387
388 /**
389  * Cleanup plugin
390  */
391 static void
392 _cleanup(void) {
393   struct nhdp_interface *nhdp_if, *nhdp_if_it;
394   struct ff_dat_if_config *ifconfig;
395
396   avl_for_each_element_safe(nhdp_interface_get_tree(), nhdp_if, _node, nhdp_if_it) {
397     ifconfig = oonf_class_get_extension(&_nhdpif_extenstion, nhdp_if);
398     if (ifconfig->registered) {
399       nhdp_interface_remove(nhdp_if);
400     }
401   }
402
403   /* remove metric from core */
404   nhdp_domain_metric_remove(&_datff_handler);
405
406   oonf_rfc5444_remove_protocol_pktseqno(_protocol);
407   _protocol = NULL;
408
409   oonf_class_extension_remove(&_link_extenstion);
410   oonf_class_extension_remove(&_nhdpif_extenstion);
411
412   oonf_timer_remove(&_sampling_timer_info);
413   oonf_timer_remove(&_hello_lost_info);
414
415   oonf_layer2_origin_remove(&_ffdat_origin);
416 }
417
418 /**
419  * Enable metric calculation
420  */
421 static void
422 _cb_enable_metric(void) {
423   struct nhdp_interface *nhdpif;
424   struct nhdp_link *lnk;
425
426   avl_for_each_element(nhdp_interface_get_tree(), nhdpif, _node) {
427     _cb_nhdpif_added(nhdpif);
428   }
429   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
430     _cb_link_added(lnk);
431   }
432
433   rfc5444_reader_add_packet_consumer(&_protocol->reader, &_packet_consumer, NULL, 0);
434 }
435
436 /**
437  * Disable metric calculation
438  */
439 static void
440 _cb_disable_metric(void) {
441   struct nhdp_interface *nhdpif;
442   struct nhdp_link *lnk;
443
444   rfc5444_reader_remove_packet_consumer(&_protocol->reader, &_packet_consumer);
445
446   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
447     _cb_link_removed(lnk);
448   }
449   avl_for_each_element(nhdp_interface_get_tree(), nhdpif, _node) {
450     _cb_nhdpif_removed(nhdpif);
451   }
452 }
453
454 /**
455  * Callback triggered when a new nhdp link is added
456  * @param ptr nhdp link
457  */
458 static void
459 _cb_link_added(void *ptr) {
460   struct link_datff_data *data;
461   struct nhdp_link *lnk;
462   size_t i;
463
464   lnk = ptr;
465   data = oonf_class_get_extension(&_link_extenstion, lnk);
466
467   memset(data, 0, sizeof(*data));
468   // data->contains_data = false;
469
470   for (i = 0; i < ARRAYSIZE(data->buckets); i++) {
471     data->buckets[i].total = 1;
472     // data->buckets[i].scaled_speed = 0;
473   }
474
475   /* initialize 'hello lost' timer for link */
476   data->hello_lost_timer.class = &_hello_lost_info;
477
478   if (lnk->itime_value > 0) {
479     data->hello_interval = lnk->itime_value;
480   }
481   else {
482     data->hello_interval = lnk->vtime_value;
483   }
484
485   /* start timer */
486   _reset_missed_hello_timer(data);
487
488   /* maximum value possible for loss rate */
489   data->last_packet_loss_rate = 8000ll;
490 }
491
492 /**
493  * Callback triggered when a new nhdp link is changed
494  * @param ptr nhdp link
495  */
496 static void
497 _cb_link_changed(void *ptr) {
498   struct link_datff_data *data;
499   struct nhdp_link *lnk;
500
501   lnk = ptr;
502   data = oonf_class_get_extension(&_link_extenstion, lnk);
503
504   if (lnk->itime_value > 0) {
505     data->hello_interval = lnk->itime_value;
506   }
507   else {
508     data->hello_interval = lnk->vtime_value;
509   }
510
511   _reset_missed_hello_timer(data);
512 }
513
514 /**
515  * Callback triggered when a nhdp link is removed from the database
516  * @param ptr nhdp link
517  */
518 static void
519 _cb_link_removed(void *ptr) {
520   struct link_datff_data *data;
521
522   data = oonf_class_get_extension(&_link_extenstion, ptr);
523
524   oonf_timer_stop(&data->hello_lost_timer);
525 }
526
527 /**
528  * Callback triggered when a NHDP interface has been added
529  * @param ptr NHDP interface instance
530  */
531 static void
532 _cb_nhdpif_added(void *ptr) {
533   struct ff_dat_if_config *ifconfig;
534
535   ifconfig = oonf_class_get_extension(&_nhdpif_extenstion, ptr);
536
537   ifconfig->_sampling_timer.class = &_sampling_timer_info;
538 }
539
540 /**
541  * Callback triggered when a NHDP interface is removed
542  * @param ptr NHDP interface instance
543  */
544 static void
545 _cb_nhdpif_removed(void *ptr) {
546   struct ff_dat_if_config *ifconfig;
547
548   ifconfig = oonf_class_get_extension(&_nhdpif_extenstion, ptr);
549
550   if (ifconfig->_sampling_timer.class) {
551     oonf_timer_stop(&ifconfig->_sampling_timer);
552     ifconfig->_sampling_timer.class = NULL;
553   }
554 }
555
556 /**
557  * Helper for sorting datarate array
558  * @param p1 pointer to integer 1
559  * @param p2 pointer to integer 2
560  * @return <0, 0 >0 (standard comparator output)
561  */
562 static int
563 _int_comparator(const void *p1, const void *p2) {
564   const int *i1 = (int *)p1;
565   const int *i2 = (int *)p2;
566
567   if (*i1 > *i2) {
568     return 1;
569   }
570   else if (*i1 < *i2) {
571     return -1;
572   }
573   return 0;
574 }
575
576 /**
577  * Get the median of all recorded link speeds by sorting
578  * @param ldata linkdata
579  * @return median linkspeed
580  */
581 static int
582 _get_median_rx_linkspeed(struct link_datff_data *ldata) {
583   int zero_count;
584   size_t window;
585   size_t i;
586
587   zero_count = 0;
588   for (i = 0; i < ARRAYSIZE(ldata->buckets); i++) {
589     _rx_sort_array[i] = ldata->buckets[i].raw_speed;
590     if (_rx_sort_array[i] <= 0) {
591       zero_count++;
592     }
593   }
594
595   window = ARRAYSIZE(ldata->buckets) - zero_count;
596   if (window == 0) {
597     return -1;
598   }
599
600   qsort(_rx_sort_array, ARRAYSIZE(ldata->buckets), sizeof(int), _int_comparator);
601
602   return _rx_sort_array[zero_count + window / 2];
603 }
604
605 /**
606  * Get the rx bitrate from the l2 database. Lookup by MAC address of neighbor,
607  * if this fails, look up by IP address.
608  * @param lnk NHDP link instance
609  * @return -1 if no data was available, rx_bitrate otherwise
610  */
611 static int64_t
612 _get_raw_rx_linkspeed(struct nhdp_link *lnk) {
613   struct os_interface *os_if;
614   struct oonf_layer2_net *l2net;
615   struct oonf_layer2_neigh *l2neigh;
616   const struct oonf_layer2_data *rx_bitrate_entry;
617
618     /* get local interface data  */
619   os_if = nhdp_interface_get_if_listener(lnk->local_if)->data;
620
621   rx_bitrate_entry = oonf_layer2_neigh_query(os_if->name, &lnk->remote_mac, OONF_LAYER2_NEIGH_RX_BITRATE, true);
622   if (rx_bitrate_entry) {
623     return oonf_layer2_data_get_int64(rx_bitrate_entry, 1, 0);
624   }
625
626   l2net = oonf_layer2_net_get(os_if->name);
627   if (!l2net) {
628     /* no layer2 data available for this interface */
629     return -1;
630   }
631
632   /* search for an entry in the l2 database which reports the remote link IP */
633   avl_for_each_element(&l2net->neighbors, l2neigh, _node) {
634     if (oonf_layer2_neigh_get_remote_ip(l2neigh, &lnk->if_addr)) {
635       rx_bitrate_entry = &l2neigh->data[OONF_LAYER2_NEIGH_RX_BITRATE];
636       if (oonf_layer2_data_has_value(rx_bitrate_entry)) {
637         return oonf_layer2_data_get_int64(rx_bitrate_entry, 1, 1);
638       }
639     }
640   }
641
642   /* no data available */
643   return -1;
644 }
645
646 static int64_t
647 _scale_linkspeed(struct nhdp_link *lnk __attribute__((unused)), int64_t raw_rx_rate) {
648 #ifdef OONF_LOG_DEBUG_INFO
649   struct netaddr_str nbuf;
650 #endif
651   int64_t rx_rate;
652
653   /* round up */
654   rx_rate = raw_rx_rate / DATFF_LINKSPEED_MINIMUM;
655   if (raw_rx_rate % DATFF_LINKSPEED_MINIMUM > 0) {
656     rx_rate++;
657   }
658   if (rx_rate < 1) {
659     OONF_DEBUG(LOG_FF_DAT, "Datarate for link %s (%s) too small: %" PRId64 " / %" PRId64,
660       netaddr_to_string(&nbuf, &lnk->if_addr), nhdp_interface_get_name(lnk->local_if), rx_rate, raw_rx_rate);
661     return 1;
662   }
663   if (rx_rate > DATFF_LINKSPEED_RANGE) {
664     OONF_DEBUG(LOG_FF_DAT, "Datarate for link %s (%s) too large: %" PRId64 " / %" PRId64,
665       netaddr_to_string(&nbuf, &lnk->if_addr), nhdp_interface_get_name(lnk->local_if), rx_rate, raw_rx_rate);
666
667     return DATFF_LINKSPEED_RANGE;
668   }
669   return rx_rate;
670 }
671
672 static int64_t
673 _get_bitrate_cost_factor(struct link_datff_data *ldata, struct nhdp_link *lnk, int64_t *bitrate) {
674   int rx_bitrate;
675   struct netaddr_str nbuf;
676
677   /* get median scaled link speed and apply it to metric */
678   rx_bitrate = _get_median_rx_linkspeed(ldata);
679   if (rx_bitrate == -1) {
680     return -1;
681   }
682   *bitrate = rx_bitrate;
683   rx_bitrate = _scale_linkspeed(lnk, rx_bitrate);
684   if (rx_bitrate < 0) {
685     return -1;
686   }
687   if (rx_bitrate > DATFF_LINKSPEED_RANGE) {
688     OONF_WARN(LOG_FF_DAT, "Metric overflow for link %s (if %s): %d", netaddr_to_string(&nbuf, &lnk->if_addr),
689       nhdp_interface_get_name(lnk->local_if), rx_bitrate);
690     return (1000ll * DATFF_LINKSPEED_RANGE);
691   }
692   return (1000ll * DATFF_LINKSPEED_RANGE) / rx_bitrate;
693 }
694
695 /**
696  * Select discrete packet loss values and apply a hysteresis
697  * @param lnk nhdp link
698  * @param ldata link data object
699  * @param metric metric based on linkspeed
700  * @param received received packets
701  * @param total total packets
702  * @return packet loss factor multiplied by 1000
703  */
704 static int64_t
705 _get_lossrate_cost_factor(struct ff_dat_if_config *ifconfig, struct nhdp_link *lnk,
706   struct link_datff_data *ldata, uint32_t received, uint32_t total) {
707   struct oonf_layer2_data *rx_bc_loss_entry, *rx_rlq_entry;
708   int64_t success_scaled_by_1000, probed_bc_success_by_1000, loss_by_1000;
709   int loss_exponent, success_datapoint_count;
710
711   success_datapoint_count = 0;
712   success_scaled_by_1000 = 0ll;
713   probed_bc_success_by_1000 = 0ll;
714
715   /* success based on received multicast frames */
716   if (total != 0 && received * DATFF_FRAME_SUCCESS_RANGE > total) {
717     probed_bc_success_by_1000 = (1000ll * received) / total;
718     success_scaled_by_1000 += probed_bc_success_by_1000;
719     success_datapoint_count++;
720   }
721
722   /* success based on layer2 broadcast loss */
723   rx_bc_loss_entry = oonf_layer2_neigh_query(nhdp_interface_get_name(lnk->local_if),
724     &lnk->remote_mac, OONF_LAYER2_NEIGH_RX_BC_LOSS, false);
725   if (rx_bc_loss_entry && oonf_layer2_data_get_origin(rx_bc_loss_entry) != &_ffdat_origin) {
726     success_scaled_by_1000 += (1000ll - oonf_layer2_data_get_int64(rx_bc_loss_entry, 1000, 0));
727     success_datapoint_count++;
728   }
729   else if (probed_bc_success_by_1000 > 0) {
730     rx_bc_loss_entry = oonf_layer2_neigh_add_path(nhdp_interface_get_name(lnk->local_if),
731         &lnk->remote_mac, OONF_LAYER2_NEIGH_RX_BC_LOSS);
732     if (rx_bc_loss_entry) {
733       oonf_layer2_data_set_int64(rx_bc_loss_entry, &_ffdat_origin, NULL, 1000ll - probed_bc_success_by_1000, 1000);
734     }
735   }
736
737   /* RLQ handling */
738   rx_rlq_entry = oonf_layer2_neigh_query(nhdp_interface_get_name(lnk->local_if),
739     &lnk->remote_mac, OONF_LAYER2_NEIGH_RX_RLQ, false);
740   if (rx_rlq_entry && oonf_layer2_data_get_origin(rx_rlq_entry) != &_ffdat_origin) {
741     success_scaled_by_1000 += oonf_layer2_data_get_int64(rx_rlq_entry, 1000, 0);
742     success_datapoint_count++;
743   }
744   else if (probed_bc_success_by_1000 > 0) {
745     rx_rlq_entry = oonf_layer2_neigh_add_path(nhdp_interface_get_name(lnk->local_if),
746         &lnk->remote_mac, OONF_LAYER2_NEIGH_RX_RLQ);
747     if (rx_rlq_entry) {
748       oonf_layer2_data_set_int64(rx_rlq_entry, &_ffdat_origin, NULL, probed_bc_success_by_1000, 1000);
749     }
750   }
751
752   /* make sure we have someone meaningful */
753   if (success_datapoint_count == 0) {
754     /* send 8.000 as cost */
755     return 1000ll * DATFF_FRAME_SUCCESS_RANGE;
756   }
757
758   /* calculate mean success if necessary */
759   success_scaled_by_1000 /= success_datapoint_count;
760
761   switch (ifconfig->loss_exponent) {
762     case IDX_LOSS_LINEAR:
763       loss_exponent = 1;
764       break;
765     case IDX_LOSS_QUADRATIC:
766       loss_exponent = 2;
767       break;
768     case IDX_LOSS_CUBIC:
769       loss_exponent = 3;
770       break;
771     case IDX_LOSS_DYNAMIC:
772       loss_exponent = _calculate_dynamic_loss_exponent(ldata->link_neigborhood);
773       break;
774     default:
775       loss_exponent = 1;
776       break;
777   }
778
779   while (loss_exponent > 1) {
780     success_scaled_by_1000 *= success_scaled_by_1000;
781     success_scaled_by_1000 /= 1000ll;
782     loss_exponent--;
783   }
784
785   if (success_scaled_by_1000 == 0) {
786     return 1000ll * DATFF_FRAME_SUCCESS_RANGE;
787   }
788
789   loss_by_1000 =  (1000ll * 1000ll) / success_scaled_by_1000;
790
791   /* hysteresis */
792   if (loss_by_1000 > 1000ll && loss_by_1000 < 1000ll * DATFF_FRAME_SUCCESS_RANGE
793       && loss_by_1000 >= ldata->last_packet_loss_rate - 100
794       && loss_by_1000 <= ldata->last_packet_loss_rate + 100) {
795     /* keep old loss rate */
796     loss_by_1000 = ldata->last_packet_loss_rate;
797   }
798   else {
799     /* remember new loss rate */
800     ldata->last_packet_loss_rate = loss_by_1000;
801   }
802
803   return loss_by_1000;
804 }
805
806 static int64_t
807 _get_throughput_cost_factor(struct ff_dat_if_config *ifconfig, struct nhdp_link *lnk,
808   struct link_datff_data *ldata, uint32_t received, uint32_t total) {
809   struct oonf_layer2_data *rx_throughput_entry;
810   int64_t scaled_rx_throughput;
811   int64_t bitrate_cost, bitrate;
812   int64_t loss_cost;
813   int64_t throughput_cost, throughput_count, throughput;
814 #ifdef OONF_LOG_DEBUG_INFO
815   struct netaddr_str nbuf;
816 #endif
817
818   throughput_count = 0ll;
819   throughput_cost  = 0ll;
820   bitrate          = 0ll;
821   throughput       = 0ll;
822
823   bitrate_cost = _get_bitrate_cost_factor(ldata, lnk, &bitrate);
824   loss_cost = _get_lossrate_cost_factor(ifconfig, lnk, ldata, received, total);
825
826   if (bitrate_cost > 0 && loss_cost > 0) {
827     throughput_cost += (bitrate_cost * loss_cost / 1000ll);
828     throughput_count++;
829   }
830
831   rx_throughput_entry = oonf_layer2_neigh_query(nhdp_interface_get_name(lnk->local_if),
832     &lnk->remote_mac, OONF_LAYER2_NEIGH_RX_THROUGHPUT, true);
833   if (rx_throughput_entry && oonf_layer2_data_get_origin(rx_throughput_entry) != &_ffdat_origin) {
834     throughput = oonf_layer2_data_get_int64(rx_throughput_entry, 1, 0);
835     scaled_rx_throughput = _scale_linkspeed(lnk, throughput);
836     throughput_cost += (1000ll * DATFF_LINKSPEED_RANGE) / scaled_rx_throughput;
837     throughput_count++;
838   }
839   else if (loss_cost > 0 && bitrate_cost > 0) {
840     rx_throughput_entry = oonf_layer2_neigh_add_path(nhdp_interface_get_name(lnk->local_if),
841         &lnk->remote_mac, OONF_LAYER2_NEIGH_RX_THROUGHPUT);
842     if (rx_throughput_entry) {
843       oonf_layer2_data_set_int64(rx_throughput_entry, &_ffdat_origin, NULL, bitrate * 1000ll / loss_cost, 1);
844     }
845   }
846
847   if (throughput_count == 0 && loss_cost > 0) {
848     /* fall back to ETX */
849     throughput_cost = DATFF_LINKSPEED_RANGE * loss_cost;
850     throughput_count++;
851   }
852
853   OONF_DEBUG(LOG_FF_DAT,
854       "throughput cost for link %s (%s): %d/%d, loss=%"PRId64", bitrate=%"PRId64", throughput=%"PRId64", throughput_cost=%"PRId64"\n",
855       netaddr_to_string(&nbuf, &lnk->if_addr), nhdp_interface_get_name(lnk->local_if),
856       received, total, loss_cost, bitrate_cost, throughput, throughput_cost);
857
858   if (throughput_count == 0) {
859     return 1000ll * DATFF_LINKSPEED_RANGE * DATFF_FRAME_SUCCESS_RANGE;
860   }
861   throughput_cost /= throughput_count;
862   return throughput_cost;
863 }
864
865 static int64_t
866 _get_mic_cost_factor(struct ff_dat_if_config *ifconfig, struct nhdp_link *lnk,
867   struct link_datff_data *ldata) {
868   if (ifconfig->mic && ldata->link_neigborhood > 1) {
869     _calculate_link_neighborhood(lnk, ldata);
870
871     return ldata->link_neigborhood * 1000ll;
872   }
873   return 1000ll;
874 }
875
876 static uint64_t
877 _shape_metric(uint64_t metric, const char *ifname, struct netaddr *link_id) {
878   struct rfc7181_metric_field encoded_metric;
879   struct netaddr_str nbuf;
880
881   /* convert into something that can be transmitted over the network */
882   if (metric > RFC7181_METRIC_MAX) {
883     /* give the metric an upper bound */
884     OONF_INFO(LOG_FF_DAT, "Metric overflow for link %s (if %s): %" PRIu64, netaddr_to_string(&nbuf, link_id),
885       ifname, metric);
886     return RFC7181_METRIC_MAX;
887   }
888   else if (metric < RFC7181_METRIC_MIN) {
889     OONF_WARN(LOG_FF_DAT, "Metric underflow for link %s (if %s): %" PRIu64, netaddr_to_string(&nbuf, link_id),
890       ifname, metric);
891     return RFC7181_METRIC_MIN;
892   }
893   else if (!rfc7181_metric_encode(&encoded_metric, metric)) {
894     return rfc7181_metric_decode(&encoded_metric);
895   }
896   else {
897     /* metric encoding failed */
898     OONF_WARN(LOG_FF_DAT, "Metric encoding failed for link %s (if %s): %" PRIu64,
899       netaddr_to_string(&nbuf, link_id), ifname, metric);
900     return RFC7181_METRIC_MAX;
901   }
902 }
903
904 /**
905  * Timer callback to sample new metric values into bucket
906  * @param ptr nhdp link
907  */
908 static void
909 _cb_dat_sampling(struct oonf_timer_instance *ptr) {
910   struct ff_dat_if_config *ifconfig;
911   struct link_datff_data *ldata;
912   struct nhdp_interface *nhdp_if;
913   struct nhdp_link *lnk;
914   uint32_t total, received;
915   uint64_t mic_cost, throughput_cost, dat_metric;
916   uint32_t metric_value;
917   uint32_t missing_intervals;
918   size_t i;
919 #ifdef OONF_LOG_DEBUG_INFO
920   struct netaddr_str nbuf;
921 #endif
922
923   ifconfig = container_of(ptr, struct ff_dat_if_config, _sampling_timer);
924
925   OONF_DEBUG(LOG_FF_DAT, "Calculate Metric from sampled data");
926
927   nhdp_if = oonf_class_get_base(&_nhdpif_extenstion, ifconfig);
928   list_for_each_element(&nhdp_if->_links, lnk, _if_node) {
929     ldata = oonf_class_get_extension(&_link_extenstion, lnk);
930     if (!ldata->contains_data) {
931       /* still no data for this link */
932       continue;
933     }
934
935     /* initialize counter */
936     total = 0;
937     received = 0;
938
939     /* calculate metric */
940     for (i = 0; i < ARRAYSIZE(ldata->buckets); i++) {
941       received += ldata->buckets[i].received;
942       total += ldata->buckets[i].total;
943     }
944
945     if (ldata->missed_hellos > 0) {
946       missing_intervals = (ldata->missed_hellos * ldata->hello_interval) / lnk->local_if->refresh_interval;
947       if (missing_intervals > ARRAYSIZE(ldata->buckets)) {
948         received = 0;
949       }
950       else {
951         received = (received * (ARRAYSIZE(ldata->buckets) - missing_intervals)) / ARRAYSIZE(ldata->buckets);
952       }
953     }
954
955     /* update link speed */
956     ldata->buckets[ldata->activePtr].raw_speed = _get_raw_rx_linkspeed(lnk);
957
958     OONF_DEBUG(LOG_FF_DAT, "Query incoming linkspeed for link %s: %" PRId64, netaddr_to_string(&nbuf, &lnk->if_addr),
959       ldata->buckets[ldata->activePtr].raw_speed);
960
961     /* calculate cost components of metric */
962     throughput_cost = _get_throughput_cost_factor(ifconfig, lnk, ldata, received, total);
963     mic_cost = _get_mic_cost_factor(ifconfig, lnk, ldata);
964
965     /* calculate total metric (not multiplied by 1000) */
966     dat_metric = (throughput_cost * mic_cost) / 1000000ll;
967
968     /* shape metric into transmittable format */
969     metric_value = _shape_metric(dat_metric, nhdp_interface_get_name(lnk->local_if), &lnk->if_addr);
970
971     /* set metric for incoming link */
972     nhdp_domain_set_incoming_metric(&_datff_handler, lnk, metric_value);
973
974     OONF_DEBUG(LOG_FF_DAT,
975       "New sampling rate for link %s (%s): %d/%d = %u\n",
976       netaddr_to_string(&nbuf, &lnk->if_addr), nhdp_interface_get_name(lnk->local_if),
977       received, total, metric_value);
978
979     /* update rolling buffer */
980     ldata->activePtr++;
981     if (ldata->activePtr >= ARRAYSIZE(ldata->buckets)) {
982       ldata->activePtr = 0;
983     }
984     ldata->buckets[ldata->activePtr].received = 0;
985     ldata->buckets[ldata->activePtr].total = 0;
986   }
987   oonf_timer_set(&ifconfig->_sampling_timer, nhdp_if->refresh_interval);
988 }
989
990 /**
991  * Calculate how many neighbors a link has
992  * @param lnk nhdp link
993  * @param data ff data link data
994  */
995 static void
996 _calculate_link_neighborhood(struct nhdp_link *lnk, struct link_datff_data *data) {
997   struct nhdp_l2hop *l2hop;
998   struct nhdp_laddr *laddr;
999   int count;
1000
1001   /* local link neighbors */
1002   count = lnk->local_if->_link_originators.count;
1003
1004   /* links twohop neighbors */
1005   avl_for_each_element(&lnk->_2hop, l2hop, _link_node) {
1006     if (l2hop->same_interface &&
1007         !avl_find_element(&lnk->local_if->_link_addresses, &l2hop->twohop_addr, laddr, _if_node)) {
1008       count++;
1009     }
1010   }
1011
1012   data->link_neigborhood = count;
1013 }
1014
1015 /**
1016  * Calculate the loss exponentiation based on the link neigborhood size
1017  * @param link_neigborhood link neighborhood count
1018  * @return loss exponent
1019  */
1020 static int
1021 _calculate_dynamic_loss_exponent(int link_neigborhood) {
1022   if (link_neigborhood < 4) {
1023     return 1;
1024   }
1025   if (link_neigborhood < 9) {
1026     return 2;
1027   }
1028   if (link_neigborhood < 15) {
1029     return 3;
1030   }
1031   return 4;
1032 }
1033
1034 /**
1035  * Callback triggered when the next hellos should have been received
1036  * @param ptr timer instance that fired
1037  */
1038 static void
1039 _cb_hello_lost(struct oonf_timer_instance *ptr) {
1040   struct link_datff_data *ldata;
1041
1042   ldata = container_of(ptr, struct link_datff_data, hello_lost_timer);
1043
1044   if (ldata->contains_data) {
1045     ldata->missed_hellos++;
1046
1047     oonf_timer_set(&ldata->hello_lost_timer, ldata->hello_interval);
1048
1049     OONF_DEBUG(LOG_FF_DAT, "Missed Hello: %d", ldata->missed_hellos);
1050   }
1051 }
1052
1053 /**
1054  * Check if an incoming packet should be used for metric calculation
1055  * @return true to process packet, false otherwise
1056  */
1057 static bool
1058 _shall_process_packet(struct nhdp_interface *nhdpif, struct ff_dat_if_config *ifconfig) {
1059   struct os_interface_listener *if_listener;
1060   struct oonf_layer2_data *l2data;
1061   struct oonf_layer2_net *l2net;
1062
1063   if (_protocol->input.is_multicast) {
1064     /* accept multicast */
1065     return true;
1066   }
1067
1068   if_listener = nhdp_interface_get_if_listener(nhdpif);
1069   if (if_listener && if_listener->data && if_listener->data->flags.unicast_only) {
1070     /* accept unicast for unicast-only interfaces */
1071     return true;
1072   }
1073
1074   l2net = oonf_layer2_net_get(if_listener->name);
1075   if (l2net) {
1076     /* accept for unicast-only interfaces marked in layer2-data */
1077     l2data = &l2net->data[OONF_LAYER2_NET_RX_ONLY_UNICAST];
1078
1079     if (oonf_layer2_data_get_boolean(l2data, false)) {
1080       return true;
1081     }
1082   }
1083
1084   /* default to configuration */
1085   return ifconfig->accept_unicast;
1086 }
1087
1088 /**
1089  * Callback to process all in RFC5444 packets for metric calculation. The
1090  * Callback ignores all unicast packets.
1091  * @param context RFC5444 context of the incoming packet
1092  * @return RFC5444 API result
1093  */
1094 static enum rfc5444_result
1095 _cb_process_packet(struct rfc5444_reader_tlvblock_context *context) {
1096   struct ff_dat_if_config *ifconfig;
1097   struct link_datff_data *ldata;
1098   struct nhdp_interface *interf;
1099   struct nhdp_laddr *laddr;
1100   struct nhdp_link *lnk;
1101   int total;
1102
1103 #ifdef OONF_LOG_DEBUG_INFO
1104   struct netaddr_str nbuf;
1105   struct isonumber_str timebuf;
1106 #endif
1107
1108   if (!context->has_pktseqno) {
1109     struct netaddr_str buf;
1110
1111     OONF_WARN(LOG_FF_DAT, "Neighbor %s does not send packet sequence numbers, cannot collect datff data!",
1112       netaddr_socket_to_string(&buf, _protocol->input.src_socket));
1113     return RFC5444_OKAY;
1114   }
1115
1116   /* get interface and link */
1117   interf = nhdp_interface_get(_protocol->input.interface->name);
1118   if (interf == NULL) {
1119     /* silently ignore unknown interface */
1120     return RFC5444_OKAY;
1121   }
1122
1123   ifconfig = oonf_class_get_extension(&_nhdpif_extenstion, interf);
1124   if (!_shall_process_packet(interf, ifconfig)) {
1125     /* silently ignore unicasts */
1126     return RFC5444_OKAY;
1127   }
1128
1129   laddr = nhdp_interface_get_link_addr(interf, _protocol->input.src_address);
1130   if (laddr == NULL) {
1131     /* silently ignore unknown link*/
1132     return RFC5444_OKAY;
1133   }
1134
1135   /* log raw metric data */
1136   OONF_DEBUG(LOG_FF_DAT_RAW, "%s %s %u %"PRId64"\n",
1137              oonf_clock_toIntervalString(&timebuf, oonf_clock_getNow()),
1138               netaddr_to_string(&nbuf, &laddr->link_addr), context->pkt_seqno,
1139              _get_raw_rx_linkspeed(laddr->link));
1140
1141   /* get link and its dat data */
1142   lnk = laddr->link;
1143   ldata = oonf_class_get_extension(&_link_extenstion, lnk);
1144
1145   if (!ldata->contains_data) {
1146     ldata->contains_data = true;
1147     ldata->activePtr = 0;
1148     ldata->buckets[0].received = 1;
1149     ldata->buckets[0].total = 1;
1150     ldata->last_seq_nr = context->pkt_seqno;
1151
1152     return RFC5444_OKAY;
1153   }
1154
1155   if (context->pkt_seqno >= ldata->last_seq_nr) {
1156     total = context->pkt_seqno - ldata->last_seq_nr;
1157   }
1158   else {
1159     total = ((uint32_t)(context->pkt_seqno) + 65536) - (uint32_t)(ldata->last_seq_nr);
1160   }
1161
1162   ldata->buckets[ldata->activePtr].received++;
1163   ldata->buckets[ldata->activePtr].total += total;
1164   ldata->last_seq_nr = context->pkt_seqno;
1165
1166   _reset_missed_hello_timer(ldata);
1167
1168   return RFC5444_OKAY;
1169 }
1170
1171 /**
1172  * A Hello was received, handle data and timer changes
1173  * @param data link metric data
1174  */
1175 static void
1176 _reset_missed_hello_timer(struct link_datff_data *data) {
1177   oonf_timer_set(&data->hello_lost_timer, (data->hello_interval * 3) / 2);
1178
1179   data->missed_hellos = 0;
1180 }
1181
1182 #if 0
1183 // TODO: implement !
1184 static enum nhdp_metric_result
1185 _get_dat_metric(struct nhdp_domain *domain, uint32_t *metric, struct oonf_layer2_neigh *neigh) {
1186   uint32_t metric;
1187
1188   _get_
1189 }
1190 #endif
1191
1192 /**
1193  * Convert DATFF metric into string representation
1194  * @param buf pointer to output buffer
1195  * @param metric metric value
1196  * @return pointer to output string
1197  */
1198 static const char *
1199 _link_to_string(struct nhdp_metric_str *buf, uint32_t metric) {
1200   uint64_t value;
1201
1202   if (metric < DATFF_LINKCOST_MINIMUM) {
1203     value = (uint32_t)DATFF_LINKSPEED_MINIMUM * (uint32_t)DATFF_LINKSPEED_RANGE;
1204   }
1205   else if (metric > DATFF_LINKCOST_MAXIMUM) {
1206     strscpy(buf->buf, "infinite", sizeof(*buf));
1207     return buf->buf;
1208   }
1209   else {
1210     value = (uint32_t)(DATFF_LINKSPEED_MINIMUM) * (uint32_t)(DATFF_LINKSPEED_RANGE) / metric;
1211   }
1212   isonumber_from_u64((struct isonumber_str *)buf, value, "bit/s", 1, false);
1213   return buf->buf;
1214 }
1215
1216 /**
1217  * Convert DATFF path metric into string representation
1218  * @param buf pointer to output buffer
1219  * @param metric path metric value
1220  * @return pointer to output string
1221  */
1222 static const char *
1223 _path_to_string(struct nhdp_metric_str *buf, uint32_t metric, uint8_t hopcount) {
1224   struct nhdp_metric_str mbuf;
1225
1226   if (hopcount == 0) {
1227     /* prevent division by zero */
1228     hopcount = 1;
1229   }
1230   snprintf(buf->buf, sizeof(*buf), "%s (%u hops)", _link_to_string(&mbuf, metric / hopcount), hopcount);
1231   return buf->buf;
1232 }
1233
1234 /**
1235  * Internal link metric to string processing
1236  * @param buf output buffer
1237  * @param lnk nhdp link
1238  * @return pointer to output buffer
1239  */
1240 static const char *
1241 _int_link_to_string(struct nhdp_metric_str *buf, struct nhdp_link *lnk) {
1242   struct link_datff_data *ldata;
1243   int64_t received = 0, total = 0;
1244   size_t i;
1245
1246   ldata = oonf_class_get_extension(&_link_extenstion, lnk);
1247
1248   for (i = 0; i < ARRAYSIZE(ldata->buckets); i++) {
1249     received += ldata->buckets[i].received;
1250     total += ldata->buckets[i].total;
1251   }
1252
1253   snprintf(buf->buf, sizeof(*buf),
1254     "p_recv=%" PRId64 ",p_total=%" PRId64 ","
1255     "speed=%d,success=%" PRId64 ",missed_hello=%d,lastseq=%u,lneigh=%d",
1256     received, total, _get_median_rx_linkspeed(ldata), ldata->last_packet_loss_rate,
1257     ldata->missed_hellos, ldata->last_seq_nr, ldata->link_neigborhood);
1258   return buf->buf;
1259 }
1260
1261 /**
1262  * Callback triggered when configuration changes
1263  */
1264 static void
1265 _cb_cfg_changed(void) {
1266   struct ff_dat_if_config *ifconfig = NULL;
1267   struct nhdp_interface *nhdp_if;
1268   const char *ifname;
1269   char ifbuf[IF_NAMESIZE];
1270
1271   ifname = cfg_get_phy_if(ifbuf, _datff_section.section_name);
1272
1273   if (_datff_section.pre == NULL) {
1274     /* increase nhdp_interface refcount */
1275     nhdp_if = nhdp_interface_add(ifname);
1276   }
1277   else {
1278     /* get interface */
1279     nhdp_if = nhdp_interface_get(ifname);
1280   }
1281
1282   if (nhdp_if) {
1283     /* get block domain extension */
1284     ifconfig = oonf_class_get_extension(&_nhdpif_extenstion, nhdp_if);
1285     ifconfig->registered = true;
1286   }
1287
1288   if (_datff_section.post == NULL) {
1289     /* section was removed */
1290     if (nhdp_if != NULL) {
1291       ifconfig->registered = false;
1292
1293       /* decrease nhdp_interface refcount */
1294       nhdp_interface_remove(nhdp_if);
1295     }
1296
1297     nhdp_if = NULL;
1298   }
1299
1300   if (!nhdp_if) {
1301     return;
1302   }
1303
1304   if (cfg_schema_tobin(ifconfig, _datff_section.post, _datff_entries, ARRAYSIZE(_datff_entries))) {
1305     OONF_WARN(LOG_FF_DAT, "Cannot convert configuration for " OONF_FF_DAT_METRIC_SUBSYSTEM);
1306     return;
1307   }
1308
1309   /* start/change sampling timer */
1310   oonf_timer_set(&ifconfig->_sampling_timer, 1000);
1311 }