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