688970d5ba4ce116b367df047637887c448f0cca
[oonf.git] / src / nhdp / nhdp / nhdp_domain.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 <stdio.h>
47
48 #include <oonf/libcommon/avl.h>
49 #include <oonf/libcommon/avl_comp.h>
50 #include <oonf/oonf.h>
51 #include <oonf/libcommon/list.h>
52 #include <oonf/libcommon/netaddr.h>
53 #include <oonf/libcore/oonf_cfg.h>
54 #include <oonf/libcore/oonf_logging.h>
55 #include <oonf/subsystems/oonf_class.h>
56 #include <oonf/subsystems/oonf_rfc5444.h>
57
58 #include <oonf/nhdp/nhdp/nhdp.h>
59 #include <oonf/nhdp/nhdp/nhdp_db.h>
60 #include <oonf/nhdp/nhdp/nhdp_domain.h>
61 #include <oonf/nhdp/nhdp/nhdp_interfaces.h>
62 #include <oonf/nhdp/nhdp/nhdp_internal.h>
63
64 static void _apply_metric(struct nhdp_domain *domain, const char *metric_name);
65 static void _remove_metric(struct nhdp_domain *);
66 static void _apply_mpr(struct nhdp_domain *domain, const char *mpr_name, uint8_t willingness);
67 static void _remove_mpr(struct nhdp_domain *);
68
69 static void _cb_update_everyone_routing_mpr(struct nhdp_domain *domain);
70 static void _cb_update_everyone_flooding_mpr(struct nhdp_domain *domain);
71
72 static bool _recalculate_neighbor_metric(struct nhdp_domain *domain, struct nhdp_neighbor *neigh);
73 static bool _recalculate_routing_mpr_set(struct nhdp_domain *domain);
74 static bool _recalculate_flooding_mpr_set(void);
75
76 static const char *_link_to_string(struct nhdp_metric_str *, uint32_t);
77 static const char *_path_to_string(struct nhdp_metric_str *, uint32_t, uint8_t);
78 static const char *_int_to_string(struct nhdp_metric_str *, struct nhdp_link *);
79
80 /* domain class */
81 static struct oonf_class _domain_class = {
82   .name = NHDP_CLASS_DOMAIN,
83   .size = sizeof(struct nhdp_domain),
84 };
85
86 /* default metric handler (hopcount) */
87 static struct nhdp_domain_metric _no_metric = {
88   .name = "Hopcount metric",
89
90   .incoming_link_start = RFC7181_METRIC_MAX,
91   .outgoing_link_start = RFC7181_METRIC_MAX,
92   .incoming_2hop_start = RFC7181_METRIC_MAX,
93   .outgoing_2hop_start = RFC7181_METRIC_MAX,
94
95   .link_to_string = _link_to_string,
96   .path_to_string = _path_to_string,
97   .internal_link_to_string = _int_to_string,
98
99   .no_default_handling = true,
100 };
101
102 /* default MPR handler (no MPR handling) */
103 static struct nhdp_domain_mpr _everyone_mprs = {
104   .name = "Everyone MPR",
105
106   .update_flooding_mpr = _cb_update_everyone_flooding_mpr,
107   .update_routing_mpr = _cb_update_everyone_routing_mpr,
108 };
109
110 /* non-default routing domains registered to NHDP */
111 static struct list_entity _domain_list;
112 static struct list_entity _domain_listener_list;
113 static struct list_entity _domain_metric_postprocessor_list;
114
115 static size_t _domain_counter = 0;
116
117 /* tree of known routing metrics/mpr-algorithms */
118 static struct avl_tree _domain_metrics;
119 static struct avl_tree _domain_mprs;
120
121 /* flooding domain */
122 static struct nhdp_domain _flooding_domain;
123
124 /* NHDP RFC5444 protocol */
125 static struct oonf_rfc5444_protocol *_protocol;
126
127 /* remember if node is MPR or not */
128 static bool _node_is_selected_as_mpr = false;
129
130 /**
131  * Initialize nhdp metric core
132  * @param p pointer to rfc5444 protocol
133  */
134 void
135 nhdp_domain_init(struct oonf_rfc5444_protocol *p) {
136   _protocol = p;
137
138   oonf_class_add(&_domain_class);
139   list_init_head(&_domain_list);
140   list_init_head(&_domain_listener_list);
141   list_init_head(&_domain_metric_postprocessor_list);
142
143   avl_init(&_domain_metrics, avl_comp_strcasecmp, false);
144   avl_init(&_domain_mprs, avl_comp_strcasecmp, false);
145
146   /* initialize flooding domain */
147   _flooding_domain.metric = &_no_metric;
148   _flooding_domain.mpr = &_everyone_mprs;
149
150   _flooding_domain.mpr->_refcount++;
151   _flooding_domain.metric->_refcount++;
152 }
153
154 /**
155  * cleanup allocated resources for nhdp metric core
156  */
157 void
158 nhdp_domain_cleanup(void) {
159   struct nhdp_domain *domain, *d_it;
160   struct nhdp_domain_listener *listener, *l_it;
161   struct nhdp_domain_metric_postprocessor *processor, *p_it;
162   int i;
163
164   list_for_each_element_safe(&_domain_list, domain, _node, d_it) {
165     /* free allocated TLVs */
166     for (i = 0; i < 4; i++) {
167       rfc5444_writer_unregister_addrtlvtype(&_protocol->writer, &domain->_metric_addrtlvs[i]);
168     }
169
170     /* remove domain */
171     list_remove(&domain->_node);
172     oonf_class_free(&_domain_class, domain);
173   }
174
175   list_for_each_element_safe(&_domain_metric_postprocessor_list, processor, _node, p_it) {
176     nhdp_domain_metric_postprocessor_remove(processor);
177   }
178   list_for_each_element_safe(&_domain_listener_list, listener, _node, l_it) {
179     nhdp_domain_listener_remove(listener);
180   }
181   oonf_class_remove(&_domain_class);
182 }
183
184 /**
185  * @return number of registered nhdp domains
186  */
187 size_t
188 nhdp_domain_get_count(void) {
189   return _domain_counter;
190 }
191
192 /**
193  * Add a new metric handler to nhdp
194  * @param metric pointer to NHDP link metric
195  * @return 0 if successful, -1 if metric was already registered
196  */
197 int
198 nhdp_domain_metric_add(struct nhdp_domain_metric *metric) {
199   /* initialize key */
200   metric->_node.key = metric->name;
201
202   /* insert default values if not set */
203   if (metric->incoming_link_start == 0) {
204     metric->incoming_link_start = RFC7181_METRIC_MAX;
205   }
206   if (metric->outgoing_link_start == 0) {
207     metric->outgoing_link_start = RFC7181_METRIC_INFINITE;
208   }
209   if (metric->incoming_2hop_start == 0) {
210     metric->incoming_2hop_start = RFC7181_METRIC_INFINITE;
211   }
212   if (metric->outgoing_2hop_start == 0) {
213     metric->outgoing_2hop_start = RFC7181_METRIC_INFINITE;
214   }
215
216   /* initialize to_string method if empty */
217   if (metric->link_to_string == NULL) {
218     metric->link_to_string = _link_to_string;
219   }
220   if (metric->path_to_string == NULL) {
221     metric->path_to_string = _path_to_string;
222   }
223
224   if (metric->internal_link_to_string == NULL) {
225     metric->internal_link_to_string = _int_to_string;
226   }
227
228   /* hook into tree */
229   return avl_insert(&_domain_metrics, &metric->_node);
230 }
231
232 /**
233  * Remove a metric handler from the nhdp metric core
234  * @param metric pointer to metric handler
235  */
236 void
237 nhdp_domain_metric_remove(struct nhdp_domain_metric *metric) {
238   struct nhdp_domain *domain;
239
240   list_for_each_element(&_domain_list, domain, _node) {
241     if (domain->metric == metric) {
242       _remove_metric(domain);
243       break;
244     }
245   }
246
247   avl_remove(&_domain_metrics, &metric->_node);
248 }
249
250 /**
251  * Add a new mpr handler to nhdp
252  * @param mpr pointer to mpr handler
253  * @return 0 if successful, -1 if metric is already registered
254  */
255 int
256 nhdp_domain_mpr_add(struct nhdp_domain_mpr *mpr) {
257   struct nhdp_domain *domain;
258
259   /* initialize key */
260   mpr->_node.key = mpr->name;
261
262   if (avl_insert(&_domain_mprs, &mpr->_node)) {
263     return -1;
264   }
265
266   list_for_each_element(&_domain_list, domain, _node) {
267     if (domain->mpr == &_everyone_mprs) {
268       _apply_mpr(domain, domain->mpr_name, domain->local_willingness);
269     }
270   }
271   if (_flooding_domain.mpr == &_everyone_mprs) {
272     _apply_mpr(&_flooding_domain, _flooding_domain.mpr_name, _flooding_domain.local_willingness);
273   }
274   return 0;
275 }
276
277 /**
278  * Remove a metric handler from the nhdp metric core
279  * @param mpr pointer to mpr handler
280  */
281 void
282 nhdp_domain_mpr_remove(struct nhdp_domain_mpr *mpr) {
283   struct nhdp_domain *domain;
284
285   list_for_each_element(&_domain_list, domain, _node) {
286     if (domain->mpr == mpr) {
287       _remove_mpr(domain);
288       break;
289     }
290   }
291
292   avl_remove(&_domain_mprs, &mpr->_node);
293 }
294
295 /**
296  * Adds a listener to the NHDP domain system
297  * @param listener pointer to NHDP domain listener
298  */
299 void
300 nhdp_domain_listener_add(struct nhdp_domain_listener *listener) {
301   list_add_tail(&_domain_listener_list, &listener->_node);
302 }
303
304 /**
305  * Removes a listener from the NHDP domain system
306  * @param listener pointer to NHDP domain listener
307  */
308 void
309 nhdp_domain_listener_remove(struct nhdp_domain_listener *listener) {
310   if (list_is_node_added(&listener->_node)) {
311     list_remove(&listener->_node);
312   }
313 }
314
315 void
316 nhdp_domain_metric_postprocessor_add(struct nhdp_domain_metric_postprocessor *processor) {
317   list_add_tail(&_domain_metric_postprocessor_list, &processor->_node);
318 }
319
320 void
321 nhdp_domain_metric_postprocessor_remove(struct nhdp_domain_metric_postprocessor *processor) {
322   if (list_is_node_added(&processor->_node)) {
323     list_remove(&processor->_node);
324   }
325 }
326
327 /**
328  * @param ext TLV extension value of MPR/Linkmetrics
329  * @return NHDP domain registered to this extension, NULL if not found
330  */
331 struct nhdp_domain *
332 nhdp_domain_get_by_ext(uint8_t ext) {
333   struct nhdp_domain *d;
334
335   list_for_each_element(&_domain_list, d, _node) {
336     if (d->ext == ext) {
337       return d;
338     }
339   }
340   return NULL;
341 }
342
343 /**
344  * Initialize the domain data of a new NHDP link
345  * @param lnk NHDP link
346  */
347 void
348 nhdp_domain_init_link(struct nhdp_link *lnk) {
349   struct nhdp_domain *domain;
350   struct nhdp_link_domaindata *data;
351   int i;
352
353   /* initialize flooding MPR settings */
354   lnk->flooding_willingness = RFC7181_WILLINGNESS_NEVER;
355   lnk->local_is_flooding_mpr = false;
356   lnk->neigh_is_flooding_mpr = false;
357
358   /* initialize metrics */
359   for (i = 0; i < NHDP_MAXIMUM_DOMAINS; i++) {
360     lnk->_domaindata[i].metric.in = RFC7181_METRIC_INFINITE;
361     lnk->_domaindata[i].metric.out = RFC7181_METRIC_INFINITE;
362     lnk->_domaindata[i].last_metric_change = oonf_clock_getNow();
363   }
364   list_for_each_element(&_domain_list, domain, _node) {
365     data = nhdp_domain_get_linkdata(domain, lnk);
366
367     if (domain->metric->no_default_handling) {
368       data->metric.in = domain->metric->incoming_link_start;
369       data->metric.out = domain->metric->outgoing_link_start;
370     }
371   }
372 }
373
374 /**
375  * Initialize the domain data of a new NHDP twohop neighbor
376  * @param l2hop NHDP twohop neighbor
377  */
378 void
379 nhdp_domain_init_l2hop(struct nhdp_l2hop *l2hop) {
380   struct nhdp_domain *domain;
381   struct nhdp_l2hop_domaindata *data;
382   int i;
383
384   /* initialize metrics */
385   for (i = 0; i < NHDP_MAXIMUM_DOMAINS; i++) {
386     l2hop->_domaindata[i].metric.in = RFC7181_METRIC_INFINITE;
387     l2hop->_domaindata[i].metric.out = RFC7181_METRIC_INFINITE;
388   }
389
390   list_for_each_element(&_domain_list, domain, _node) {
391     data = nhdp_domain_get_l2hopdata(domain, l2hop);
392
393     if (domain->metric->no_default_handling) {
394       data->metric.in = domain->metric->incoming_2hop_start;
395       data->metric.out = domain->metric->outgoing_2hop_start;
396     }
397   }
398 }
399
400 /**
401  * Initialize the domain data of a new NHDP neighbor
402  * @param neigh NHDP neighbor
403  */
404 void
405 nhdp_domain_init_neighbor(struct nhdp_neighbor *neigh) {
406   struct nhdp_domain *domain;
407   struct nhdp_neighbor_domaindata *data;
408   int i;
409
410   for (i = 0; i < NHDP_MAXIMUM_DOMAINS; i++) {
411     neigh->_domaindata[i].metric.in = RFC7181_METRIC_INFINITE;
412     neigh->_domaindata[i].metric.out = RFC7181_METRIC_INFINITE;
413
414     neigh->_domaindata[i].best_out_link = NULL;
415     neigh->_domaindata[i].best_out_link_metric = RFC7181_METRIC_INFINITE;
416     neigh->_domaindata[i].willingness = RFC7181_WILLINGNESS_NEVER;
417
418     neigh->_domaindata[i].local_is_mpr = false;
419     neigh->_domaindata[i].neigh_is_mpr = false;
420   }
421
422   /* initialize metrics and mprs */
423   list_for_each_element(&_domain_list, domain, _node) {
424     data = nhdp_domain_get_neighbordata(domain, neigh);
425
426     if (domain->metric->no_default_handling) {
427       data->metric.in = domain->metric->incoming_link_start;
428       data->metric.out = domain->metric->outgoing_link_start;
429     }
430   }
431 }
432
433 /**
434  * Process an in linkmetric tlv for a nhdp link
435  * @param domain pointer to NHDP domain
436  * @param lnk pointer to nhdp link
437  * @param value pointer to value of metric tlv,
438  *   must have a length of at least 2
439  */
440 void
441 nhdp_domain_process_metric_linktlv(struct nhdp_domain *domain, struct nhdp_link *lnk, const uint8_t *value) {
442   struct rfc7181_metric_field metric_field;
443   uint32_t metric;
444
445   memcpy(&metric_field, value, sizeof(metric_field));
446   metric = rfc7181_metric_decode(&metric_field);
447
448   if (rfc7181_metric_has_flag(&metric_field, RFC7181_LINKMETRIC_INCOMING_LINK)) {
449     nhdp_domain_get_linkdata(domain, lnk)->metric.out = metric;
450   }
451   if (rfc7181_metric_has_flag(&metric_field, RFC7181_LINKMETRIC_INCOMING_NEIGH)) {
452     nhdp_domain_get_neighbordata(domain, lnk->neigh)->metric.out = metric;
453   }
454 }
455
456 /**
457  * Process an in linkmetric tlv for a nhdp twohop neighbor
458  * @param domain pointer to NHDP domain
459  * @param l2hop pointer to nhdp twohop neighbor
460  * @param value value of metric tlv
461  */
462 void
463 nhdp_domain_process_metric_2hoptlv(struct nhdp_domain *domain, struct nhdp_l2hop *l2hop, const uint8_t *value) {
464   struct rfc7181_metric_field metric_field;
465   struct nhdp_l2hop_domaindata *data;
466   uint32_t metric;
467
468   memcpy(&metric_field, value, sizeof(metric_field));
469   metric = rfc7181_metric_decode(&metric_field);
470
471   data = nhdp_domain_get_l2hopdata(domain, l2hop);
472   if (rfc7181_metric_has_flag(&metric_field, RFC7181_LINKMETRIC_INCOMING_NEIGH)) {
473     data->metric.in = metric;
474   }
475   if (rfc7181_metric_has_flag(&metric_field, RFC7181_LINKMETRIC_OUTGOING_NEIGH)) {
476     data->metric.out = metric;
477   }
478 }
479
480 /**
481  * This will trigger a metric recalculation
482  * @param domain NHDP domain of metric change, NULL for all domains
483  * return true if metric changed, false otherwise
484  */
485 static bool
486 _recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh, bool trigger) {
487   struct nhdp_domain_listener *listener;
488   bool changed_metric;
489
490   changed_metric = false;
491
492   if (trigger) {
493     OONF_DEBUG(LOG_NHDP, "Recalculating metrics set for domain %d", domain ? domain->index : -1);
494   }
495
496   if (!domain) {
497     list_for_each_element(&_domain_list, domain, _node) {
498       changed_metric |= _recalculate_metrics(domain, neigh, false);
499     }
500     domain = NULL;
501   }
502   else if (!neigh) {
503     list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
504       changed_metric |= _recalculate_neighbor_metric(domain, neigh);
505     }
506   }
507   else {
508     changed_metric |= _recalculate_neighbor_metric(domain, neigh);
509   }
510
511   if (trigger && changed_metric) {
512     list_for_each_element(&_domain_listener_list, listener, _node) {
513       /* trigger domain listeners */
514       if (listener->metric_update) {
515         listener->metric_update(domain);
516       }
517     }
518   }
519
520   if (trigger) {
521     OONF_INFO(
522       LOG_NHDP, "Metrics changed for domain %d: %s", domain ? domain->index : -1, changed_metric ? "true" : "false");
523   }
524   return changed_metric;
525 }
526
527 bool
528 nhdp_domain_recalculate_metrics(struct nhdp_domain *domain, struct nhdp_neighbor *neigh) {
529   return _recalculate_metrics(domain, neigh, true);
530 }
531
532 static void
533 _fire_mpr_changed(struct nhdp_domain *domain) {
534   struct nhdp_domain_listener *listener;
535   list_for_each_element(&_domain_listener_list, listener, _node) {
536     /* trigger domain listeners */
537     if (listener->mpr_update) {
538       listener->mpr_update(domain);
539     }
540   }
541 }
542
543 void
544 nhdp_domain_recalculate_mpr(void) {
545   struct nhdp_domain *domain;
546
547   list_for_each_element(&_domain_list, domain, _node) {
548     if (domain->_mpr_outdated) {
549       if (_recalculate_routing_mpr_set(domain)) {
550         domain->mpr->update_routing_mpr(domain);
551         _fire_mpr_changed(domain);
552       }
553       domain->_mpr_outdated = false;
554     }
555   }
556   if (_flooding_domain._mpr_outdated) {
557     if (_recalculate_flooding_mpr_set()) {
558       _flooding_domain.mpr->update_flooding_mpr(&_flooding_domain);
559       _fire_mpr_changed(&_flooding_domain);
560     }
561     _flooding_domain._mpr_outdated = false;
562   }
563 }
564
565 /**
566  * This marks a MPR domain as 'to be recalculated' as soon as a Hello is sent
567  * @param domain NHDP domain
568  * @param neigh neighbor that triggered the recalculation,
569  *   NULL for unspecified neighbor
570  */
571 void
572 nhdp_domain_delayed_mpr_recalculation(struct nhdp_domain *domain, struct nhdp_neighbor *neigh __attribute__((unused))) {
573   if (!domain) {
574     list_for_each_element(&_domain_list, domain, _node) {
575       nhdp_domain_delayed_mpr_recalculation(domain, neigh);
576     }
577     nhdp_domain_delayed_mpr_recalculation(&_flooding_domain, neigh);
578     return;
579   }
580
581   domain->_mpr_outdated = true;
582 }
583
584 /**
585  * @return true if this node is selected as a MPR by any other node
586  */
587 bool
588 nhdp_domain_node_is_mpr(void) {
589   return _node_is_selected_as_mpr;
590 }
591
592 /**
593  *
594  * @param mprtypes destination buffer for mpr types
595  * @param mprtypes_size size of destination buffer
596  * @param tlv pointer to mprtypes TLV, might be NULL
597  * @return number of bytes written into destination buffer
598  */
599 size_t
600 nhdp_domain_process_mprtypes_tlv(uint8_t *mprtypes, size_t mprtypes_size, struct rfc5444_reader_tlvblock_entry *tlv) {
601   struct nhdp_domain *domain;
602   size_t count;
603
604   if (!tlv) {
605     domain = list_first_element(&_domain_list, domain, _node);
606     mprtypes[0] = domain->ext;
607
608     return 1;
609   }
610
611   memset(mprtypes, 255, mprtypes_size);
612
613   count = 0;
614   list_for_each_element(&_domain_list, domain, _node) {
615     mprtypes[count++] = domain->ext;
616     if (count >= mprtypes_size) {
617       break;
618     }
619   }
620   return count;
621 }
622
623 /**
624  * Process an in MPR tlv for a NHDP link
625  * @param mprtypes list of extensions for MPR
626  * @param mprtypes_size length of mprtypes array
627  * @param lnk NHDP link
628  * @param tlv MPR tlv context
629  */
630 void
631 nhdp_domain_process_mpr_tlv(
632   uint8_t *mprtypes, size_t mprtypes_size, struct nhdp_link *lnk, struct rfc5444_reader_tlvblock_entry *tlv) {
633   struct nhdp_domain *domain;
634   struct nhdp_neighbor *neigh;
635   size_t bit_idx, byte_idx;
636   size_t i;
637
638   lnk->local_is_flooding_mpr = false;
639   list_for_each_element(&_domain_list, domain, _node) {
640     nhdp_domain_get_neighbordata(domain, lnk->neigh)->local_is_mpr = false;
641   }
642
643   if (!tlv) {
644     return;
645   }
646
647   /* set flooding MPR flag */
648   lnk->local_is_flooding_mpr = (tlv->single_value[0] & RFC7181_MPR_FLOODING) != 0;
649   OONF_DEBUG(LOG_NHDP_R, "Flooding MPR for neighbor: %s", lnk->local_is_flooding_mpr ? "true" : "false");
650
651   /* set routing MPR flags */
652   for (i = 0; i < mprtypes_size; i++) {
653     domain = nhdp_domain_get_by_ext(mprtypes[i]);
654     if (domain == NULL) {
655       continue;
656     }
657     bit_idx = (i + 1) & 7;
658     byte_idx = (i + 1) >> 3;
659
660     if (byte_idx >= tlv->length) {
661       continue;
662     }
663
664     nhdp_domain_get_neighbordata(domain, lnk->neigh)->local_is_mpr =
665       (tlv->single_value[byte_idx] & (1 << bit_idx)) != 0;
666
667     OONF_DEBUG(LOG_NHDP_R, "Routing MPR for neighbor in domain %u: %s", domain->ext,
668       nhdp_domain_get_neighbordata(domain, lnk->neigh)->local_is_mpr ? "true" : "false");
669   }
670
671   _node_is_selected_as_mpr = false;
672   list_for_each_element(&_domain_list, domain, _node) {
673     list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
674       if (nhdp_domain_get_neighbordata(domain, neigh)->local_is_mpr) {
675         _node_is_selected_as_mpr = true;
676         return;
677       }
678     }
679   }
680 }
681
682 /**
683  * Process an in Willingness tlv and put values into
684  * temporary storage in MPR handler object. Call
685  * nhdp_domain_store_willingness to permanently store them later.
686  * @param mprtypes list of extensions for MPR
687  * @param mprtypes_size length of mprtypes array
688  * @param tlv Willingness tlv context
689  */
690 void
691 nhdp_domain_process_willingness_tlv(
692   uint8_t *mprtypes, size_t mprtypes_size, struct rfc5444_reader_tlvblock_entry *tlv) {
693   struct nhdp_domain *domain;
694   size_t idx, i;
695   uint8_t value;
696
697   _flooding_domain._tmp_willingness = RFC7181_WILLINGNESS_NEVER;
698   list_for_each_element(&_domain_list, domain, _node) {
699     domain->_tmp_willingness = RFC7181_WILLINGNESS_NEVER;
700   }
701
702   if (!tlv) {
703     return;
704   }
705
706   /* copy flooding willingness */
707   _flooding_domain._tmp_willingness = tlv->single_value[0] & RFC7181_WILLINGNESS_MASK;
708   OONF_DEBUG(LOG_NHDP_R, "Received flooding willingness: %u", _flooding_domain._tmp_willingness);
709
710   for (i = 0; i < mprtypes_size; i++) {
711     domain = nhdp_domain_get_by_ext(mprtypes[i]);
712     if (domain == NULL) {
713       continue;
714     }
715
716     idx = (i + 1) / 2;
717     if (idx >= tlv->length) {
718       continue;
719     }
720
721     value = tlv->single_value[idx];
722     if ((domain->index & 1) == 0) {
723       value >>= RFC7181_WILLINGNESS_SHIFT;
724     }
725     else {
726       value &= RFC7181_WILLINGNESS_MASK;
727     }
728
729     domain->_tmp_willingness = value;
730
731     OONF_DEBUG(LOG_NHDP_R, "Received routing willingness for domain %u: %u", domain->ext, domain->_tmp_willingness);
732   }
733 }
734
735 /**
736  * Stores the willingness data processed by
737  * nhdp_domain_process_willingness_tlv() into a neighbor object
738  * @param lnk NHDP link
739  */
740 void
741 nhdp_domain_store_willingness(struct nhdp_link *lnk) {
742   struct nhdp_neighbor_domaindata *neighdata;
743   struct nhdp_domain *domain;
744
745   lnk->flooding_willingness = _flooding_domain._tmp_willingness;
746   OONF_DEBUG(LOG_NHDP_R, "Set flooding willingness: %u", lnk->flooding_willingness);
747
748   list_for_each_element(&_domain_list, domain, _node) {
749     neighdata = nhdp_domain_get_neighbordata(domain, lnk->neigh);
750     neighdata->willingness = domain->_tmp_willingness;
751     OONF_DEBUG(LOG_NHDP_R, "Set routing willingness for domain %u: %u", domain->ext, neighdata->willingness);
752   }
753 }
754
755 /**
756  * Generate MPRTYPES tlv value
757  * @param mprtypes pointer to destination buffer for value
758  * @param mprtypes_size length of destination buffer
759  * @return number of bytes written into buffer
760  */
761 size_t
762 nhdp_domain_encode_mprtypes_tlvvalue(uint8_t *mprtypes, size_t mprtypes_size) {
763   struct nhdp_domain *domain;
764   size_t count;
765
766   count = 0;
767   list_for_each_element(&_domain_list, domain, _node) {
768     mprtypes[count++] = domain->ext;
769
770     if (count >= mprtypes_size) {
771       break;
772     }
773   }
774
775   return count;
776 }
777
778 /**
779  * Calculates the tlvvalue of a MPR tlv
780  *
781  * @param tlvvalue destination for value of MPR tlv
782  * @param tlvsize length of tlv value
783  * @param lnk pointer to NHDP link for MPR tlv
784  * @return length of tlvvalue, 0 if an error happened
785  */
786 size_t
787 nhdp_domain_encode_mpr_tlvvalue(uint8_t *tlvvalue, size_t tlvsize, struct nhdp_link *lnk) {
788   struct nhdp_domain *domain;
789   size_t bit_idx, byte_idx, len;
790
791   memset(tlvvalue, 0, tlvsize);
792   len = 0;
793   /* set flooding MPR flag */
794   if (lnk->neigh_is_flooding_mpr) {
795     tlvvalue[0] |= RFC7181_MPR_FLOODING;
796   }
797
798   OONF_DEBUG(LOG_NHDP_W, "Set flooding MPR: %s", lnk->neigh_is_flooding_mpr ? "true" : "false");
799
800   list_for_each_element(&_domain_list, domain, _node) {
801     bit_idx = (domain->index + 1) & 7;
802     byte_idx = (domain->index + 1) >> 3;
803
804     if (byte_idx >= tlvsize) {
805       return 0;
806     }
807     if (byte_idx + 1 > len) {
808       len = byte_idx + 1;
809     }
810
811     if (nhdp_domain_get_neighbordata(domain, lnk->neigh)->neigh_is_mpr) {
812       tlvvalue[byte_idx] |= (1 << bit_idx);
813     }
814
815     OONF_DEBUG(LOG_NHDP_W, "Set routing MPR for domain %u: %s", domain->ext,
816       nhdp_domain_get_neighbordata(domain, lnk->neigh)->neigh_is_mpr ? "true" : "false");
817   }
818   return len;
819 }
820
821 /**
822  * Calculates the tlvvalue of a Willingness tlv
823  * @param tlvvalue destination array
824  * @param tlvsize length of destination array
825  * @return length of tlvvalue, 0 if an error happened
826  */
827 size_t
828 nhdp_domain_encode_willingness_tlvvalue(uint8_t *tlvvalue, size_t tlvsize) {
829   struct nhdp_domain *domain;
830   uint8_t value;
831   size_t idx, len;
832
833   memset(tlvvalue, 0, tlvsize);
834   len = 0;
835
836   /* set flooding willingness */
837   tlvvalue[0] = _flooding_domain.local_willingness;
838   OONF_DEBUG(LOG_NHDP_W, "Set flooding willingness: %u", _flooding_domain.local_willingness);
839
840   /* set routing willingness */
841   list_for_each_element(&_domain_list, domain, _node) {
842     idx = (domain->index + 1) / 2;
843     if (idx >= tlvsize) {
844       return -1;
845     }
846     if (idx + 1 > len) {
847       len = idx + 1;
848     }
849
850     value = domain->local_willingness & RFC7181_WILLINGNESS_MASK;
851
852     if ((domain->index & 1) == 0) {
853       value <<= RFC7181_WILLINGNESS_SHIFT;
854     }
855
856     OONF_DEBUG(LOG_NHDP_W,
857       "Set routing willingness for domain %u: %x"
858       " (%" PRINTF_SIZE_T_SPECIFIER ")",
859       domain->ext, value, idx);
860
861     tlvvalue[idx] |= value;
862   }
863
864   return len;
865 }
866
867 /**
868  * Sets a new flodding MPR algorithm
869  * @param mpr_name name of MPR algorithm
870  * @param willingness of MPR algorithm
871  */
872 void
873 nhdp_domain_set_flooding_mpr(const char *mpr_name, uint8_t willingness) {
874   _apply_mpr(&_flooding_domain, mpr_name, willingness);
875 }
876
877 /**
878  * @return the virtual flooding domain
879  */
880 const struct nhdp_domain *
881 nhdp_domain_get_flooding_domain(void) {
882   return &_flooding_domain;
883 }
884 /**
885  * Sets the incoming metric of a link. This is the only function external
886  * code should use to commit the calculated metric values to the nhdp db.
887  * @param metric NHDP domain metric
888  * @param lnk NHDP link
889  * @param metric_in incoming metric value for NHDP link
890  * @return true if metric changed, false otherwise
891  */
892 bool
893 nhdp_domain_set_incoming_metric(struct nhdp_domain_metric *metric, struct nhdp_link *lnk, uint32_t metric_in) {
894   struct nhdp_domain_metric_postprocessor *processor;
895   struct nhdp_link_domaindata *linkdata;
896   struct nhdp_domain *domain;
897   uint32_t new_metric;
898   bool changed;
899
900   changed = false;
901
902   list_for_each_element(&_domain_list, domain, _node) {
903     if (domain->metric == metric) {
904       linkdata = nhdp_domain_get_linkdata(domain, lnk);
905       new_metric = metric_in;
906
907       list_for_each_element(&_domain_metric_postprocessor_list, processor, _node) {
908         new_metric = processor->process_in_metric(domain, lnk, new_metric);
909       }
910
911       if (linkdata->metric.in != new_metric) {
912         changed = true;
913         linkdata->last_metric_change = oonf_clock_getNow();
914       }
915       linkdata->metric.in = new_metric;
916     }
917   }
918   return changed;
919 }
920
921 /**
922  * @return list of domains
923  */
924 struct list_entity *
925 nhdp_domain_get_list(void) {
926   return &_domain_list;
927 }
928
929 /**
930  * @return list of event listeners for domain metric/mpr triggers
931  */
932 struct list_entity *
933 nhdp_domain_get_listener_list(void) {
934   return &_domain_listener_list;
935 }
936
937 static bool
938 _recalculate_flooding_mpr_set(void) {
939   struct nhdp_link *lnk;
940
941   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
942     lnk->_neigh_was_flooding_mpr = lnk->neigh_is_flooding_mpr;
943   }
944
945   _flooding_domain.mpr->update_flooding_mpr(&_flooding_domain);
946
947   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
948     if (lnk->_neigh_was_flooding_mpr != lnk->neigh_is_flooding_mpr) {
949       OONF_DEBUG(LOG_NHDP, "Flooding domain MPR set changed");
950       return true;
951     }
952   }
953   return false;
954 }
955
956 /**
957  * Recalculate the MPR set of a NHDP domain
958  * @param domain nhdp domain
959  * @return true if the MPR set changed
960  */
961 static bool
962 _recalculate_routing_mpr_set(struct nhdp_domain *domain) {
963   struct nhdp_neighbor_domaindata *neighdata;
964   struct nhdp_neighbor *neigh;
965
966   if (!domain->mpr->update_routing_mpr) {
967     return false;
968   }
969
970   /* remember old MPR set */
971   list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
972     neighdata = nhdp_domain_get_neighbordata(domain, neigh);
973     neighdata->_neigh_was_mpr = neighdata->neigh_is_mpr;
974   }
975
976   /* update MPR set */
977   domain->mpr->update_routing_mpr(domain);
978
979   /* check for changes */
980   list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
981     neighdata = nhdp_domain_get_neighbordata(domain, neigh);
982     if (neighdata->_neigh_was_mpr != neighdata->neigh_is_mpr) {
983       OONF_DEBUG(LOG_NHDP, "Domain ext %u MPR set changed", domain->ext);
984       return true;
985     }
986   }
987   return false;
988 }
989
990 /**
991  * Recalculate the 'best link/metric' values of a neighbor
992  * and check for two-hop outgoing link metric changes
993  * @param domain NHDP domain
994  * @param neigh NHDP neighbor
995  * @return true neighbor metric or the two-hop link metrics changed
996  */
997 static bool
998 _recalculate_neighbor_metric(struct nhdp_domain *domain, struct nhdp_neighbor *neigh) {
999   struct nhdp_link *lnk;
1000   struct nhdp_link_domaindata *linkdata;
1001   struct nhdp_l2hop *l2hop;
1002   struct nhdp_l2hop_domaindata *l2hopdata;
1003   struct nhdp_neighbor_domaindata *neighdata;
1004   bool changed;
1005 #ifdef OONF_LOG_INFO
1006   struct netaddr_str nbuf;
1007 #endif
1008
1009   neighdata = nhdp_domain_get_neighbordata(domain, neigh);
1010   changed = false;
1011
1012   /* reset metric */
1013   neighdata->metric.in = RFC7181_METRIC_INFINITE;
1014   neighdata->metric.out = RFC7181_METRIC_INFINITE;
1015
1016   /* reset best link */
1017   neighdata->best_out_link = NULL;
1018   neighdata->best_link_ifindex = 0;
1019
1020   OONF_INFO(LOG_NHDP, "Recalculate neighbor %s metrics (ext %u): old_outgoing=%u",
1021     netaddr_to_string(&nbuf, &neigh->originator), domain->ext, neighdata->best_out_link_metric);
1022
1023   /* get best metric */
1024   list_for_each_element(&neigh->_links, lnk, _neigh_node) {
1025     if (lnk->status != NHDP_LINK_SYMMETRIC) {
1026       continue;
1027     }
1028
1029     linkdata = nhdp_domain_get_linkdata(domain, lnk);
1030     if (linkdata->metric.out < neighdata->metric.out) {
1031       OONF_DEBUG(LOG_NHDP, "Link on if %s has better outgoing metric: %u", lnk->local_if->os_if_listener.data->name,
1032         linkdata->metric.out);
1033
1034       neighdata->metric.out = linkdata->metric.out;
1035       neighdata->best_out_link = lnk;
1036     }
1037     if (linkdata->metric.in < neighdata->metric.in) {
1038       OONF_DEBUG(LOG_NHDP, "Link on if %s has better incoming metric: %u", lnk->local_if->os_if_listener.data->name,
1039         linkdata->metric.in);
1040       neighdata->metric.in = linkdata->metric.in;
1041     }
1042
1043     /* check for changes in outgoing 2-hop metrics */
1044     avl_for_each_element(&lnk->_2hop, l2hop, _link_node) {
1045       l2hopdata = nhdp_domain_get_l2hopdata(domain, l2hop);
1046
1047       changed |= l2hopdata->metric.out != l2hopdata->_last_used_outgoing_metric;
1048       l2hopdata->_last_used_outgoing_metric = l2hopdata->metric.out;
1049     }
1050   }
1051
1052   if (neighdata->best_out_link != NULL) {
1053     linkdata = nhdp_domain_get_linkdata(domain, neighdata->best_out_link);
1054
1055     OONF_INFO(LOG_NHDP, "Best link: if=%s, link=%s, in=%u, out=%u",
1056       nhdp_interface_get_if_listener(neighdata->best_out_link->local_if)->data->name,
1057       netaddr_to_string(&nbuf, &neighdata->best_out_link->if_addr), linkdata->metric.in, linkdata->metric.out);
1058     neighdata->best_link_ifindex = nhdp_interface_get_if_listener(neighdata->best_out_link->local_if)->data->index;
1059
1060     changed |= neighdata->best_out_link_metric != linkdata->metric.out;
1061     neighdata->best_out_link_metric = linkdata->metric.out;
1062   }
1063
1064   return changed;
1065 }
1066
1067 /**
1068  * Add a new domain to the NHDP system
1069  * @param ext TLV extension type used for new domain
1070  * @return pointer to new domain, NULL, if out of memory or
1071  *   maximum number of domains has been reached.
1072  */
1073 struct nhdp_domain *
1074 nhdp_domain_add(uint8_t ext) {
1075   struct nhdp_domain *domain;
1076   int i;
1077
1078   domain = nhdp_domain_get_by_ext(ext);
1079   if (domain) {
1080     return domain;
1081   }
1082
1083   if (_domain_counter == NHDP_MAXIMUM_DOMAINS) {
1084     OONF_WARN(LOG_NHDP, "Maximum number of NHDP domains reached: %d", NHDP_MAXIMUM_DOMAINS);
1085     return NULL;
1086   }
1087
1088   /* initialize new domain */
1089   domain = oonf_class_malloc(&_domain_class);
1090   if (domain == NULL) {
1091     return NULL;
1092   }
1093
1094   domain->ext = ext;
1095   domain->index = _domain_counter++;
1096   domain->metric = &_no_metric;
1097   domain->mpr = &_everyone_mprs;
1098
1099   domain->mpr->_refcount++;
1100   domain->metric->_refcount++;
1101
1102   /* initialize metric TLVs */
1103   for (i = 0; i < 4; i++) {
1104     domain->_metric_addrtlvs[i].type = RFC7181_ADDRTLV_LINK_METRIC;
1105     domain->_metric_addrtlvs[i].exttype = domain->ext;
1106
1107     rfc5444_writer_register_addrtlvtype(&_protocol->writer, &domain->_metric_addrtlvs[i], -1);
1108   }
1109
1110   /* add to domain list */
1111   list_add_tail(&_domain_list, &domain->_node);
1112
1113   oonf_class_event(&_domain_class, domain, OONF_OBJECT_ADDED);
1114   return domain;
1115 }
1116
1117 /**
1118  * Configure a NHDP domain to a metric and a MPR algorithm
1119  * @param ext TLV extension type used for new domain
1120  * @param metric_name name of the metric algorithm to be used,
1121  *   might be CFG_DOMAIN_NO_METRIC (for hopcount metric)
1122  *   or CFG_DOMAIN_ANY_METRIC (for a metric the NHDP core should
1123  *   choose).
1124  * @param mpr_name name of the MPR algorithm to be used,
1125  *   might be CFG_DOMAIN_NO_MPR (every node is MPR)
1126  *   or CFG_DOMAIN_ANY_MPR (for a MPR the NHDP core should
1127  *   choose).
1128  * @param willingness routing willingness for domain
1129  * @return pointer to configured domain, NULL, if out of memory or
1130  *   maximum number of domains has been reached.
1131  */
1132 struct nhdp_domain *
1133 nhdp_domain_configure(uint8_t ext, const char *metric_name, const char *mpr_name, uint8_t willingness) {
1134   struct nhdp_domain *domain;
1135
1136   domain = nhdp_domain_add(ext);
1137   if (domain == NULL) {
1138     return NULL;
1139   }
1140
1141   OONF_DEBUG(LOG_NHDP, "Configure domain %u to metric=%s", domain->index, metric_name);
1142   _apply_metric(domain, metric_name);
1143
1144   OONF_DEBUG(LOG_NHDP, "Configure domain %u to mpr=%s, willingness=%u", domain->index, mpr_name, willingness);
1145   _apply_mpr(domain, mpr_name, willingness);
1146
1147   oonf_class_event(&_domain_class, domain, OONF_OBJECT_CHANGED);
1148
1149   return domain;
1150 }
1151
1152 /**
1153  * Apply a new metric algorithm to a NHDP domain
1154  * @param domain pointer to NHDP domain
1155  * @param metric_name name of the metric algorithm to be used,
1156  *   might be CFG_DOMAIN_NO_METRIC (for hopcount metric)
1157  *   or CFG_DOMAIN_ANY_METRIC (for a metric the NHDP core should
1158  *   choose).
1159  */
1160 static void
1161 _apply_metric(struct nhdp_domain *domain, const char *metric_name) {
1162   struct nhdp_domain_metric *metric;
1163
1164   /* check if we have to remove the old metric first */
1165   if (strcasecmp(domain->metric_name, metric_name) == 0) {
1166     /* nothing to do, we already have the right metric */
1167     return;
1168   }
1169
1170   if (domain->metric != &_no_metric) {
1171     _remove_metric(domain);
1172   }
1173
1174   /* Handle wildcard metric name first */
1175   if (strcasecmp(metric_name, CFG_DOMAIN_ANY_METRIC_MPR) == 0 && !avl_is_empty(&_domain_metrics)) {
1176     metric_name = avl_first_element(&_domain_metrics, metric, _node)->name;
1177   }
1178
1179   /* look for metric implementation */
1180   metric = avl_find_element(&_domain_metrics, metric_name, metric, _node);
1181   if (metric == NULL) {
1182     metric = &_no_metric;
1183   }
1184
1185   /* copy new metric name */
1186   strscpy(domain->metric_name, metric->name, sizeof(domain->metric_name));
1187
1188   /* link domain and metric */
1189   domain->metric->_refcount--;
1190   domain->metric = metric;
1191
1192   /* activate metric */
1193   if (metric->_refcount == 0 && metric->enable) {
1194     metric->enable();
1195   }
1196   metric->_refcount++;
1197 }
1198
1199 /**
1200  * Reset the metric of a NHDP domain to hopcount
1201  * @param domain pointer to NHDP domain
1202  */
1203 static void
1204 _remove_metric(struct nhdp_domain *domain) {
1205   domain->metric->_refcount--;
1206   if (!domain->metric->_refcount && domain->metric->disable) {
1207     domain->metric->disable();
1208   }
1209   strscpy(domain->metric_name, CFG_DOMAIN_NO_METRIC_MPR, sizeof(domain->metric_name));
1210   domain->metric = &_no_metric;
1211   domain->metric->_refcount++;
1212 }
1213
1214 /**
1215  * Apply a new MPR algorithm to a NHDP domain
1216  * @param domain pointer to NHDP domain
1217  * @param mpr_name name of the MPR algorithm to be used,
1218  *   might be CFG_DOMAIN_NO_MPR (every node is MPR)
1219  *   or CFG_DOMAIN_ANY_MPR (for a MPR the NHDP core should
1220  *   choose).
1221  * @param willingness routing willingness for domain
1222  */
1223 static void
1224 _apply_mpr(struct nhdp_domain *domain, const char *mpr_name, uint8_t willingness) {
1225   struct nhdp_domain_mpr *mpr;
1226
1227   domain->local_willingness = willingness;
1228
1229   /* check if we have to remove the old mpr first */
1230   if (strcasecmp(domain->mpr_name, mpr_name) == 0) {
1231     /* nothing else to do, we already have the right MPR */
1232     return;
1233   }
1234   if (domain->mpr != &_everyone_mprs) {
1235     /* replace old MPR algorithm with "everyone MPR" */
1236     _remove_mpr(domain);
1237   }
1238
1239   /* Handle wildcard mpr name first */
1240   if (strcasecmp(mpr_name, CFG_DOMAIN_ANY_METRIC_MPR) == 0 && !avl_is_empty(&_domain_mprs)) {
1241     mpr_name = avl_first_element(&_domain_mprs, mpr, _node)->name;
1242   }
1243
1244   /* look for mpr implementation */
1245   mpr = avl_find_element(&_domain_mprs, mpr_name, mpr, _node);
1246   if (mpr == NULL) {
1247     mpr = &_everyone_mprs;
1248   }
1249
1250   /* copy new metric name */
1251   strscpy(domain->mpr_name, mpr->name, sizeof(domain->mpr_name));
1252
1253   /* link domain and mpr */
1254   domain->mpr->_refcount--;
1255   domain->mpr = mpr;
1256
1257   /* activate mpr */
1258   if (mpr->_refcount == 0 && mpr->enable) {
1259     mpr->enable();
1260   }
1261   mpr->_refcount++;
1262 }
1263
1264 /**
1265  * Reset the MPR of a NHDP domain to 'everyone is MPR'
1266  * @param domain pointer to NHDP domain
1267  */
1268 static void
1269 _remove_mpr(struct nhdp_domain *domain) {
1270   domain->mpr->_refcount--;
1271   if (!domain->mpr->_refcount && domain->mpr->disable) {
1272     domain->mpr->disable();
1273   }
1274   strscpy(domain->mpr_name, CFG_DOMAIN_NO_METRIC_MPR, sizeof(domain->mpr_name));
1275   domain->mpr = &_everyone_mprs;
1276   domain->mpr->_refcount++;
1277 }
1278
1279 static void
1280 _cb_update_everyone_routing_mpr(struct nhdp_domain *domain) {
1281   struct nhdp_neighbor *neigh;
1282   struct nhdp_neighbor_domaindata *domaindata;
1283
1284   list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
1285     if (domain->mpr == &_everyone_mprs) {
1286       domaindata = nhdp_domain_get_neighbordata(domain, neigh);
1287       domaindata->neigh_is_mpr = domaindata->willingness > RFC7181_WILLINGNESS_NEVER;
1288     }
1289   }
1290 }
1291
1292 static void
1293 _cb_update_everyone_flooding_mpr(struct nhdp_domain *domain __attribute__((unused))) {
1294   struct nhdp_link *lnk;
1295
1296   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
1297     lnk->neigh_is_flooding_mpr = lnk->flooding_willingness > RFC7181_WILLINGNESS_NEVER;
1298   }
1299 }
1300
1301 /**
1302  * Default implementation to convert a link metric value into text
1303  * @param buf pointer to metric output buffer
1304  * @param metric link metric value
1305  * @return pointer to string representation of linkmetric value
1306  */
1307 static const char *
1308 _link_to_string(struct nhdp_metric_str *buf, uint32_t metric) {
1309   snprintf(buf->buf, sizeof(*buf), "0x%x", metric);
1310
1311   return buf->buf;
1312 }
1313
1314 /**
1315  * Default implementation to convert a path metric value into text
1316  * @param buf pointer to metric output buffer
1317  * @param metric path metric value
1318  * @param hopcount hopcount of path
1319  * @return pointer to string representation of path metric value
1320  */
1321 static const char *
1322 _path_to_string(struct nhdp_metric_str *buf, uint32_t metric, uint8_t hopcount __attribute((unused))) {
1323   snprintf(buf->buf, sizeof(*buf), "0x%x", metric);
1324
1325   return buf->buf;
1326 }
1327
1328 static const char *
1329 _int_to_string(struct nhdp_metric_str *buf, struct nhdp_link *lnk __attribute__((unused))) {
1330   strscpy(buf->buf, "-", sizeof(*buf));
1331   return buf->buf;
1332 }