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