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