7c6328db0af1697cdcbb57eec826fcf782e3b455
[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, 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  * @return
571  */
572 void
573 nhdp_domain_delayed_mpr_recalculation(struct nhdp_domain *domain, struct nhdp_neighbor *neigh __attribute__((unused))) {
574   if (!domain) {
575     list_for_each_element(&_domain_list, domain, _node) {
576       nhdp_domain_delayed_mpr_recalculation(domain, neigh);
577     }
578     nhdp_domain_delayed_mpr_recalculation(&_flooding_domain, neigh);
579     return;
580   }
581
582   domain->_mpr_outdated = true;
583 }
584
585 /**
586  * @return true if this node is selected as a MPR by any other node
587  */
588 bool
589 nhdp_domain_node_is_mpr(void) {
590   return _node_is_selected_as_mpr;
591 }
592
593 /**
594  *
595  * @param mprtypes destination buffer for mpr types
596  * @param mprtypes_size size of destination buffer
597  * @param tlv pointer to mprtypes TLV, might be NULL
598  * @return number of bytes written into destination buffer
599  */
600 size_t
601 nhdp_domain_process_mprtypes_tlv(uint8_t *mprtypes, size_t mprtypes_size, struct rfc5444_reader_tlvblock_entry *tlv) {
602   struct nhdp_domain *domain;
603   size_t count;
604
605   if (!tlv) {
606     domain = list_first_element(&_domain_list, domain, _node);
607     mprtypes[0] = domain->ext;
608
609     return 1;
610   }
611
612   memset(mprtypes, 255, mprtypes_size);
613
614   count = 0;
615   list_for_each_element(&_domain_list, domain, _node) {
616     mprtypes[count++] = domain->ext;
617     if (count >= mprtypes_size) {
618       break;
619     }
620   }
621   return count;
622 }
623
624 /**
625  * Process an in MPR tlv for a NHDP link
626  * @param mprtypes list of extensions for MPR
627  * @param mprtypes_size length of mprtypes array
628  * @param neigh NHDP neighbor
629  * @param tlv MPR tlv context
630  */
631 void
632 nhdp_domain_process_mpr_tlv(
633   uint8_t *mprtypes, size_t mprtypes_size, struct nhdp_link *lnk, struct rfc5444_reader_tlvblock_entry *tlv) {
634   struct nhdp_domain *domain;
635   struct nhdp_neighbor *neigh;
636   size_t bit_idx, byte_idx;
637   size_t i;
638
639   lnk->local_is_flooding_mpr = false;
640   list_for_each_element(&_domain_list, domain, _node) {
641     nhdp_domain_get_neighbordata(domain, lnk->neigh)->local_is_mpr = false;
642   }
643
644   if (!tlv) {
645     return;
646   }
647
648   /* set flooding MPR flag */
649   lnk->local_is_flooding_mpr = (tlv->single_value[0] & RFC7181_MPR_FLOODING) != 0;
650   OONF_DEBUG(LOG_NHDP_R, "Flooding MPR for neighbor: %s", lnk->local_is_flooding_mpr ? "true" : "false");
651
652   /* set routing MPR flags */
653   for (i = 0; i < mprtypes_size; i++) {
654     domain = nhdp_domain_get_by_ext(mprtypes[i]);
655     if (domain == NULL) {
656       continue;
657     }
658     bit_idx = (i + 1) & 7;
659     byte_idx = (i + 1) >> 3;
660
661     if (byte_idx >= tlv->length) {
662       continue;
663     }
664
665     nhdp_domain_get_neighbordata(domain, lnk->neigh)->local_is_mpr =
666       (tlv->single_value[byte_idx] & (1 << bit_idx)) != 0;
667
668     OONF_DEBUG(LOG_NHDP_R, "Routing MPR for neighbor in domain %u: %s", domain->ext,
669       nhdp_domain_get_neighbordata(domain, lnk->neigh)->local_is_mpr ? "true" : "false");
670   }
671
672   _node_is_selected_as_mpr = false;
673   list_for_each_element(&_domain_list, domain, _node) {
674     list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
675       if (nhdp_domain_get_neighbordata(domain, neigh)->local_is_mpr) {
676         _node_is_selected_as_mpr = true;
677         return;
678       }
679     }
680   }
681 }
682
683 /**
684  * Process an in Willingness tlv and put values into
685  * temporary storage in MPR handler object. Call
686  * nhdp_domain_store_willingness to permanently store them later.
687  * @param mprtypes list of extensions for MPR
688  * @param mprtypes_size length of mprtypes array
689  * @param tlv Willingness tlv context
690  */
691 void
692 nhdp_domain_process_willingness_tlv(
693   uint8_t *mprtypes, size_t mprtypes_size, struct rfc5444_reader_tlvblock_entry *tlv) {
694   struct nhdp_domain *domain;
695   size_t idx, i;
696   uint8_t value;
697
698   _flooding_domain._tmp_willingness = RFC7181_WILLINGNESS_NEVER;
699   list_for_each_element(&_domain_list, domain, _node) {
700     domain->_tmp_willingness = RFC7181_WILLINGNESS_NEVER;
701   }
702
703   if (!tlv) {
704     return;
705   }
706
707   /* copy flooding willingness */
708   _flooding_domain._tmp_willingness = tlv->single_value[0] & RFC7181_WILLINGNESS_MASK;
709   OONF_DEBUG(LOG_NHDP_R, "Received flooding willingness: %u", _flooding_domain._tmp_willingness);
710
711   for (i = 0; i < mprtypes_size; i++) {
712     domain = nhdp_domain_get_by_ext(mprtypes[i]);
713     if (domain == NULL) {
714       continue;
715     }
716
717     idx = (i + 1) / 2;
718     if (idx >= tlv->length) {
719       continue;
720     }
721
722     value = tlv->single_value[idx];
723     if ((domain->index & 1) == 0) {
724       value >>= RFC7181_WILLINGNESS_SHIFT;
725     }
726     else {
727       value &= RFC7181_WILLINGNESS_MASK;
728     }
729
730     domain->_tmp_willingness = value;
731
732     OONF_DEBUG(LOG_NHDP_R, "Received routing willingness for domain %u: %u", domain->ext, domain->_tmp_willingness);
733   }
734 }
735
736 /**
737  * Stores the willingness data processed by
738  * nhdp_domain_process_willingness_tlv() into a neighbor object
739  * @param neigh NHDP neighbor
740  */
741 void
742 nhdp_domain_store_willingness(struct nhdp_link *lnk) {
743   struct nhdp_neighbor_domaindata *neighdata;
744   struct nhdp_domain *domain;
745
746   lnk->flooding_willingness = _flooding_domain._tmp_willingness;
747   OONF_DEBUG(LOG_NHDP_R, "Set flooding willingness: %u", lnk->flooding_willingness);
748
749   list_for_each_element(&_domain_list, domain, _node) {
750     neighdata = nhdp_domain_get_neighbordata(domain, lnk->neigh);
751     neighdata->willingness = domain->_tmp_willingness;
752     OONF_DEBUG(LOG_NHDP_R, "Set routing willingness for domain %u: %u", domain->ext, neighdata->willingness);
753   }
754 }
755
756 /**
757  * Generate MPRTYPES tlv value
758  * @param mprtypes pointer to destination buffer for value
759  * @param mprtypes_size length of destination buffer
760  * @return number of bytes written into buffer
761  */
762 size_t
763 nhdp_domain_encode_mprtypes_tlvvalue(uint8_t *mprtypes, size_t mprtypes_size) {
764   struct nhdp_domain *domain;
765   size_t count;
766
767   count = 0;
768   list_for_each_element(&_domain_list, domain, _node) {
769     mprtypes[count++] = domain->ext;
770
771     if (count >= mprtypes_size) {
772       break;
773     }
774   }
775
776   return count;
777 }
778
779 /**
780  * Calculates the tlvvalue of a MPR tlv
781  *
782  * @param tlvvalue destination for value of MPR tlv
783  * @param tlvsize length of tlv value
784  * @param neigh pointer to NHDP neighbor for MPR tlv
785  * @return length of tlvvalue, 0 if an error happened
786  */
787 size_t
788 nhdp_domain_encode_mpr_tlvvalue(uint8_t *tlvvalue, size_t tlvsize, struct nhdp_link *lnk) {
789   struct nhdp_domain *domain;
790   size_t bit_idx, byte_idx, len;
791
792   memset(tlvvalue, 0, tlvsize);
793   len = 0;
794   /* set flooding MPR flag */
795   if (lnk->neigh_is_flooding_mpr) {
796     tlvvalue[0] |= RFC7181_MPR_FLOODING;
797   }
798
799   OONF_DEBUG(LOG_NHDP_W, "Set flooding MPR: %s", lnk->neigh_is_flooding_mpr ? "true" : "false");
800
801   list_for_each_element(&_domain_list, domain, _node) {
802     bit_idx = (domain->index + 1) & 7;
803     byte_idx = (domain->index + 1) >> 3;
804
805     if (byte_idx >= tlvsize) {
806       return 0;
807     }
808     if (byte_idx + 1 > len) {
809       len = byte_idx + 1;
810     }
811
812     if (nhdp_domain_get_neighbordata(domain, lnk->neigh)->neigh_is_mpr) {
813       tlvvalue[byte_idx] |= (1 << bit_idx);
814     }
815
816     OONF_DEBUG(LOG_NHDP_W, "Set routing MPR for domain %u: %s", domain->ext,
817       nhdp_domain_get_neighbordata(domain, lnk->neigh)->neigh_is_mpr ? "true" : "false");
818   }
819   return len;
820 }
821
822 /**
823  * Calculates the tlvvalue of a Willingness tlv
824  * @param tlvvalue destination array
825  * @param tlvsize length of destination array
826  * @return length of tlvvalue, 0 if an error happened
827  */
828 size_t
829 nhdp_domain_encode_willingness_tlvvalue(uint8_t *tlvvalue, size_t tlvsize) {
830   struct nhdp_domain *domain;
831   uint8_t value;
832   size_t idx, len;
833
834   memset(tlvvalue, 0, tlvsize);
835   len = 0;
836
837   /* set flooding willingness */
838   tlvvalue[0] = _flooding_domain.local_willingness;
839   OONF_DEBUG(LOG_NHDP_W, "Set flooding willingness: %u", _flooding_domain.local_willingness);
840
841   /* set routing willingness */
842   list_for_each_element(&_domain_list, domain, _node) {
843     idx = (domain->index + 1) / 2;
844     if (idx >= tlvsize) {
845       return -1;
846     }
847     if (idx + 1 > len) {
848       len = idx + 1;
849     }
850
851     value = domain->local_willingness & RFC7181_WILLINGNESS_MASK;
852
853     if ((domain->index & 1) == 0) {
854       value <<= RFC7181_WILLINGNESS_SHIFT;
855     }
856
857     OONF_DEBUG(LOG_NHDP_W,
858       "Set routing willingness for domain %u: %x"
859       " (%" PRINTF_SIZE_T_SPECIFIER ")",
860       domain->ext, value, idx);
861
862     tlvvalue[idx] |= value;
863   }
864
865   return len;
866 }
867
868 /**
869  * Sets a new flodding MPR algorithm
870  * @param mpr_name name of MPR algorithm
871  * @param willingness of MPR algorithm
872  */
873 void
874 nhdp_domain_set_flooding_mpr(const char *mpr_name, uint8_t willingness) {
875   _apply_mpr(&_flooding_domain, mpr_name, willingness);
876 }
877
878 /**
879  * @return the virtual flooding domain
880  */
881 const struct nhdp_domain *
882 nhdp_domain_get_flooding_domain(void) {
883   return &_flooding_domain;
884 }
885 /**
886  * Sets the incoming metric of a link. This is the only function external
887  * code should use to commit the calculated metric values to the nhdp db.
888  * @param metric NHDP domain metric
889  * @param lnk NHDP link
890  * @param metric_in incoming metric value for NHDP link
891  * @return true if metric changed, false otherwise
892  */
893 bool
894 nhdp_domain_set_incoming_metric(struct nhdp_domain_metric *metric, struct nhdp_link *lnk, uint32_t metric_in) {
895   struct nhdp_domain_metric_postprocessor *processor;
896   struct nhdp_link_domaindata *linkdata;
897   struct nhdp_domain *domain;
898   uint32_t new_metric;
899   bool changed;
900
901   changed = false;
902
903   list_for_each_element(&_domain_list, domain, _node) {
904     if (domain->metric == metric) {
905       linkdata = nhdp_domain_get_linkdata(domain, lnk);
906       new_metric = metric_in;
907
908       list_for_each_element(&_domain_metric_postprocessor_list, processor, _node) {
909         new_metric = processor->process_in_metric(domain, lnk, new_metric);
910       }
911
912       if (linkdata->metric.in != new_metric) {
913         changed = true;
914         linkdata->last_metric_change = oonf_clock_getNow();
915       }
916       linkdata->metric.in = new_metric;
917     }
918   }
919   return changed;
920 }
921
922 /**
923  * @return list of domains
924  */
925 struct list_entity *
926 nhdp_domain_get_list(void) {
927   return &_domain_list;
928 }
929
930 /**
931  * @return list of event listeners for domain metric/mpr triggers
932  */
933 struct list_entity *
934 nhdp_domain_get_listener_list(void) {
935   return &_domain_listener_list;
936 }
937
938 static bool
939 _recalculate_flooding_mpr_set(void) {
940   struct nhdp_link *lnk;
941
942   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
943     lnk->_neigh_was_flooding_mpr = lnk->neigh_is_flooding_mpr;
944   }
945
946   _flooding_domain.mpr->update_flooding_mpr(&_flooding_domain);
947
948   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
949     if (lnk->_neigh_was_flooding_mpr != lnk->neigh_is_flooding_mpr) {
950       OONF_DEBUG(LOG_NHDP, "Flooding domain MPR set changed");
951       return true;
952     }
953   }
954   return false;
955 }
956
957 /**
958  * Recalculate the MPR set of a NHDP domain
959  * @param domain nhdp domain
960  * @return true if the MPR set changed
961  */
962 static bool
963 _recalculate_routing_mpr_set(struct nhdp_domain *domain) {
964   struct nhdp_neighbor_domaindata *neighdata;
965   struct nhdp_neighbor *neigh;
966
967   if (!domain->mpr->update_routing_mpr) {
968     return false;
969   }
970
971   /* remember old MPR set */
972   list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
973     neighdata = nhdp_domain_get_neighbordata(domain, neigh);
974     neighdata->_neigh_was_mpr = neighdata->neigh_is_mpr;
975   }
976
977   /* update MPR set */
978   domain->mpr->update_routing_mpr(domain);
979
980   /* check for changes */
981   list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
982     neighdata = nhdp_domain_get_neighbordata(domain, neigh);
983     if (neighdata->_neigh_was_mpr != neighdata->neigh_is_mpr) {
984       OONF_DEBUG(LOG_NHDP, "Domain ext %u MPR set changed", domain->ext);
985       return true;
986     }
987   }
988   return false;
989 }
990
991 /**
992  * Recalculate the 'best link/metric' values of a neighbor
993  * and check for two-hop outgoing link metric changes
994  * @param domain NHDP domain
995  * @param neigh NHDP neighbor
996  * @param neighdata NHDP neighbor domaindata
997  * @return true neighbor metric or the two-hop link metrics changed
998  */
999 static bool
1000 _recalculate_neighbor_metric(struct nhdp_domain *domain, struct nhdp_neighbor *neigh) {
1001   struct nhdp_link *lnk;
1002   struct nhdp_link_domaindata *linkdata;
1003   struct nhdp_l2hop *l2hop;
1004   struct nhdp_l2hop_domaindata *l2hopdata;
1005   struct nhdp_neighbor_domaindata *neighdata;
1006   bool changed;
1007 #ifdef OONF_LOG_INFO
1008   struct netaddr_str nbuf;
1009 #endif
1010
1011   neighdata = nhdp_domain_get_neighbordata(domain, neigh);
1012   changed = false;
1013
1014   /* reset metric */
1015   neighdata->metric.in = RFC7181_METRIC_INFINITE;
1016   neighdata->metric.out = RFC7181_METRIC_INFINITE;
1017
1018   /* reset best link */
1019   neighdata->best_out_link = NULL;
1020   neighdata->best_link_ifindex = 0;
1021
1022   OONF_INFO(LOG_NHDP, "Recalculate neighbor %s metrics (ext %u): old_outgoing=%u",
1023     netaddr_to_string(&nbuf, &neigh->originator), domain->ext, neighdata->best_out_link_metric);
1024
1025   /* get best metric */
1026   list_for_each_element(&neigh->_links, lnk, _neigh_node) {
1027     if (lnk->status != NHDP_LINK_SYMMETRIC) {
1028       continue;
1029     }
1030
1031     linkdata = nhdp_domain_get_linkdata(domain, lnk);
1032     if (linkdata->metric.out < neighdata->metric.out) {
1033       OONF_DEBUG(LOG_NHDP, "Link on if %s has better outgoing metric: %u", lnk->local_if->os_if_listener.data->name,
1034         linkdata->metric.out);
1035
1036       neighdata->metric.out = linkdata->metric.out;
1037       neighdata->best_out_link = lnk;
1038     }
1039     if (linkdata->metric.in < neighdata->metric.in) {
1040       OONF_DEBUG(LOG_NHDP, "Link on if %s has better incoming metric: %u", lnk->local_if->os_if_listener.data->name,
1041         linkdata->metric.in);
1042       neighdata->metric.in = linkdata->metric.in;
1043     }
1044
1045     /* check for changes in outgoing 2-hop metrics */
1046     avl_for_each_element(&lnk->_2hop, l2hop, _link_node) {
1047       l2hopdata = nhdp_domain_get_l2hopdata(domain, l2hop);
1048
1049       changed |= l2hopdata->metric.out != l2hopdata->_last_used_outgoing_metric;
1050       l2hopdata->_last_used_outgoing_metric = l2hopdata->metric.out;
1051     }
1052   }
1053
1054   if (neighdata->best_out_link != NULL) {
1055     linkdata = nhdp_domain_get_linkdata(domain, neighdata->best_out_link);
1056
1057     OONF_INFO(LOG_NHDP, "Best link: if=%s, link=%s, in=%u, out=%u",
1058       nhdp_interface_get_if_listener(neighdata->best_out_link->local_if)->data->name,
1059       netaddr_to_string(&nbuf, &neighdata->best_out_link->if_addr), linkdata->metric.in, linkdata->metric.out);
1060     neighdata->best_link_ifindex = nhdp_interface_get_if_listener(neighdata->best_out_link->local_if)->data->index;
1061
1062     changed |= neighdata->best_out_link_metric != linkdata->metric.out;
1063     neighdata->best_out_link_metric = linkdata->metric.out;
1064   }
1065
1066   return changed;
1067 }
1068
1069 /**
1070  * Add a new domain to the NHDP system
1071  * @param ext TLV extension type used for new domain
1072  * @return pointer to new domain, NULL, if out of memory or
1073  *   maximum number of domains has been reached.
1074  */
1075 struct nhdp_domain *
1076 nhdp_domain_add(uint8_t ext) {
1077   struct nhdp_domain *domain;
1078   int i;
1079
1080   domain = nhdp_domain_get_by_ext(ext);
1081   if (domain) {
1082     return domain;
1083   }
1084
1085   if (_domain_counter == NHDP_MAXIMUM_DOMAINS) {
1086     OONF_WARN(LOG_NHDP, "Maximum number of NHDP domains reached: %d", NHDP_MAXIMUM_DOMAINS);
1087     return NULL;
1088   }
1089
1090   /* initialize new domain */
1091   domain = oonf_class_malloc(&_domain_class);
1092   if (domain == NULL) {
1093     return NULL;
1094   }
1095
1096   domain->ext = ext;
1097   domain->index = _domain_counter++;
1098   domain->metric = &_no_metric;
1099   domain->mpr = &_everyone_mprs;
1100
1101   domain->mpr->_refcount++;
1102   domain->metric->_refcount++;
1103
1104   /* initialize metric TLVs */
1105   for (i = 0; i < 4; i++) {
1106     domain->_metric_addrtlvs[i].type = RFC7181_ADDRTLV_LINK_METRIC;
1107     domain->_metric_addrtlvs[i].exttype = domain->ext;
1108
1109     rfc5444_writer_register_addrtlvtype(&_protocol->writer, &domain->_metric_addrtlvs[i], -1);
1110   }
1111
1112   /* add to domain list */
1113   list_add_tail(&_domain_list, &domain->_node);
1114
1115   oonf_class_event(&_domain_class, domain, OONF_OBJECT_ADDED);
1116   return domain;
1117 }
1118
1119 /**
1120  * Configure a NHDP domain to a metric and a MPR algorithm
1121  * @param ext TLV extension type used for new domain
1122  * @param metric_name name of the metric algorithm to be used,
1123  *   might be CFG_DOMAIN_NO_METRIC (for hopcount metric)
1124  *   or CFG_DOMAIN_ANY_METRIC (for a metric the NHDP core should
1125  *   choose).
1126  * @param mpr_name name of the MPR algorithm to be used,
1127  *   might be CFG_DOMAIN_NO_MPR (every node is MPR)
1128  *   or CFG_DOMAIN_ANY_MPR (for a MPR the NHDP core should
1129  *   choose).
1130  * @param willingness routing willingness for domain
1131  * @return pointer to configured domain, NULL, if out of memory or
1132  *   maximum number of domains has been reached.
1133  */
1134 struct nhdp_domain *
1135 nhdp_domain_configure(uint8_t ext, const char *metric_name, const char *mpr_name, uint8_t willingness) {
1136   struct nhdp_domain *domain;
1137
1138   domain = nhdp_domain_add(ext);
1139   if (domain == NULL) {
1140     return NULL;
1141   }
1142
1143   OONF_DEBUG(LOG_NHDP, "Configure domain %u to metric=%s", domain->index, metric_name);
1144   _apply_metric(domain, metric_name);
1145
1146   OONF_DEBUG(LOG_NHDP, "Configure domain %u to mpr=%s, willingness=%u", domain->index, mpr_name, willingness);
1147   _apply_mpr(domain, mpr_name, willingness);
1148
1149   oonf_class_event(&_domain_class, domain, OONF_OBJECT_CHANGED);
1150
1151   return domain;
1152 }
1153
1154 /**
1155  * Apply a new metric algorithm to a NHDP domain
1156  * @param domain pointer to NHDP domain
1157  * @param metric_name name of the metric algorithm to be used,
1158  *   might be CFG_DOMAIN_NO_METRIC (for hopcount metric)
1159  *   or CFG_DOMAIN_ANY_METRIC (for a metric the NHDP core should
1160  *   choose).
1161  */
1162 static void
1163 _apply_metric(struct nhdp_domain *domain, const char *metric_name) {
1164   struct nhdp_domain_metric *metric;
1165
1166   /* check if we have to remove the old metric first */
1167   if (strcasecmp(domain->metric_name, metric_name) == 0) {
1168     /* nothing to do, we already have the right metric */
1169     return;
1170   }
1171
1172   if (domain->metric != &_no_metric) {
1173     _remove_metric(domain);
1174   }
1175
1176   /* Handle wildcard metric name first */
1177   if (strcasecmp(metric_name, CFG_DOMAIN_ANY_METRIC_MPR) == 0 && !avl_is_empty(&_domain_metrics)) {
1178     metric_name = avl_first_element(&_domain_metrics, metric, _node)->name;
1179   }
1180
1181   /* look for metric implementation */
1182   metric = avl_find_element(&_domain_metrics, metric_name, metric, _node);
1183   if (metric == NULL) {
1184     metric = &_no_metric;
1185   }
1186
1187   /* copy new metric name */
1188   strscpy(domain->metric_name, metric->name, sizeof(domain->metric_name));
1189
1190   /* link domain and metric */
1191   domain->metric->_refcount--;
1192   domain->metric = metric;
1193
1194   /* activate metric */
1195   if (metric->_refcount == 0 && metric->enable) {
1196     metric->enable();
1197   }
1198   metric->_refcount++;
1199 }
1200
1201 /**
1202  * Reset the metric of a NHDP domain to hopcount
1203  * @param domain pointer to NHDP domain
1204  */
1205 static void
1206 _remove_metric(struct nhdp_domain *domain) {
1207   domain->metric->_refcount--;
1208   if (!domain->metric->_refcount && domain->metric->disable) {
1209     domain->metric->disable();
1210   }
1211   strscpy(domain->metric_name, CFG_DOMAIN_NO_METRIC_MPR, sizeof(domain->metric_name));
1212   domain->metric = &_no_metric;
1213   domain->metric->_refcount++;
1214 }
1215
1216 /**
1217  * Apply a new MPR algorithm to a NHDP domain
1218  * @param domain pointer to NHDP domain
1219  * @param mpr_name name of the MPR algorithm to be used,
1220  *   might be CFG_DOMAIN_NO_MPR (every node is MPR)
1221  *   or CFG_DOMAIN_ANY_MPR (for a MPR the NHDP core should
1222  *   choose).
1223  * @param willingness routing willingness for domain
1224  */
1225 static void
1226 _apply_mpr(struct nhdp_domain *domain, const char *mpr_name, uint8_t willingness) {
1227   struct nhdp_domain_mpr *mpr;
1228
1229   domain->local_willingness = willingness;
1230
1231   /* check if we have to remove the old mpr first */
1232   if (strcasecmp(domain->mpr_name, mpr_name) == 0) {
1233     /* nothing else to do, we already have the right MPR */
1234     return;
1235   }
1236   if (domain->mpr != &_everyone_mprs) {
1237     /* replace old MPR algorithm with "everyone MPR" */
1238     _remove_mpr(domain);
1239   }
1240
1241   /* Handle wildcard mpr name first */
1242   if (strcasecmp(mpr_name, CFG_DOMAIN_ANY_METRIC_MPR) == 0 && !avl_is_empty(&_domain_mprs)) {
1243     mpr_name = avl_first_element(&_domain_mprs, mpr, _node)->name;
1244   }
1245
1246   /* look for mpr implementation */
1247   mpr = avl_find_element(&_domain_mprs, mpr_name, mpr, _node);
1248   if (mpr == NULL) {
1249     mpr = &_everyone_mprs;
1250   }
1251
1252   /* copy new metric name */
1253   strscpy(domain->mpr_name, mpr->name, sizeof(domain->mpr_name));
1254
1255   /* link domain and mpr */
1256   domain->mpr->_refcount--;
1257   domain->mpr = mpr;
1258
1259   /* activate mpr */
1260   if (mpr->_refcount == 0 && mpr->enable) {
1261     mpr->enable();
1262   }
1263   mpr->_refcount++;
1264 }
1265
1266 /**
1267  * Reset the MPR of a NHDP domain to 'everyone is MPR'
1268  * @param domain pointer to NHDP domain
1269  */
1270 static void
1271 _remove_mpr(struct nhdp_domain *domain) {
1272   domain->mpr->_refcount--;
1273   if (!domain->mpr->_refcount && domain->mpr->disable) {
1274     domain->mpr->disable();
1275   }
1276   strscpy(domain->mpr_name, CFG_DOMAIN_NO_METRIC_MPR, sizeof(domain->mpr_name));
1277   domain->mpr = &_everyone_mprs;
1278   domain->mpr->_refcount++;
1279 }
1280
1281 static void
1282 _cb_update_everyone_routing_mpr(struct nhdp_domain *domain) {
1283   struct nhdp_neighbor *neigh;
1284   struct nhdp_neighbor_domaindata *domaindata;
1285
1286   list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
1287     if (domain->mpr == &_everyone_mprs) {
1288       domaindata = nhdp_domain_get_neighbordata(domain, neigh);
1289       domaindata->neigh_is_mpr = domaindata->willingness > RFC7181_WILLINGNESS_NEVER;
1290     }
1291   }
1292 }
1293
1294 static void
1295 _cb_update_everyone_flooding_mpr(struct nhdp_domain *domain __attribute__((unused))) {
1296   struct nhdp_link *lnk;
1297
1298   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
1299     lnk->neigh_is_flooding_mpr = lnk->flooding_willingness > RFC7181_WILLINGNESS_NEVER;
1300   }
1301 }
1302
1303 /**
1304  * Default implementation to convert a link metric value into text
1305  * @param buf pointer to metric output buffer
1306  * @param metric link metric value
1307  * @return pointer to string representation of linkmetric value
1308  */
1309 static const char *
1310 _link_to_string(struct nhdp_metric_str *buf, uint32_t metric) {
1311   snprintf(buf->buf, sizeof(*buf), "0x%x", metric);
1312
1313   return buf->buf;
1314 }
1315
1316 /**
1317  * Default implementation to convert a path metric value into text
1318  * @param buf pointer to metric output buffer
1319  * @param metric path metric value
1320  * @param hopcount hopcount of path
1321  * @return pointer to string representation of path metric value
1322  */
1323 static const char *
1324 _path_to_string(struct nhdp_metric_str *buf, uint32_t metric, uint8_t hopcount __attribute((unused))) {
1325   snprintf(buf->buf, sizeof(*buf), "0x%x", metric);
1326
1327   return buf->buf;
1328 }
1329
1330 static const char *
1331 _int_to_string(struct nhdp_metric_str *buf, struct nhdp_link *lnk __attribute__((unused))) {
1332   strscpy(buf->buf, "-", sizeof(*buf));
1333   return buf->buf;
1334 }