Merge branch 'master' into mpr_rework
[oonf.git] / src-plugins / nhdp / constant_metric / constant_metric.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /**
43  * @file
44  */
45
46 #include <errno.h>
47 #include <stdio.h>
48
49 #include "common/common_types.h"
50 #include "common/autobuf.h"
51 #include "common/avl.h"
52 #include "common/avl_comp.h"
53 #include "config/cfg_schema.h"
54 #include "config/cfg_validate.h"
55 #include "core/oonf_cfg.h"
56 #include "core/oonf_logging.h"
57 #include "core/oonf_subsystem.h"
58 #include "subsystems/oonf_class.h"
59 #include "subsystems/oonf_rfc5444.h"
60 #include "subsystems/oonf_timer.h"
61 #include "subsystems/os_interface.h"
62
63 #include "nhdp/nhdp.h"
64 #include "nhdp/nhdp_domain.h"
65 #include "nhdp/nhdp_interfaces.h"
66
67 #include "constant_metric/constant_metric.h"
68
69 /* constants and definitions */
70 #define LOG_CONSTANT_METRIC _olsrv2_constant_metric_subsystem.logging
71
72 /**
73  * Session data for a configured constant metric
74  */
75 struct _linkcost {
76   /*! hook into tree of configured metrics */
77   struct avl_node _node;
78
79   /*! name of interface */
80   char if_name[IF_NAMESIZE];
81
82   /*! neighbor IP the metric is restricted to, NULL if interface generic */
83   struct netaddr neighbor;
84
85   /*! configured metric */
86   uint32_t cost;
87 };
88
89 /* prototypes */
90 static int _init(void);
91 static void _cleanup(void);
92
93 static void _cb_link_added(void *);
94 static void _cb_set_linkcost(struct oonf_timer_instance *);
95
96 static int _avlcmp_linkcost(const void *, const void *);
97
98 static int _cb_validate_link(const struct cfg_schema_entry *entry,
99       const char *section_name, const char *value, struct autobuf *out);
100 static void _cb_cfg_changed(void);
101
102 /* plugin declaration */
103 static struct cfg_schema_entry _constant_entries[] = {
104   _CFG_VALIDATE("link", "", "Defines the static cost to the link to a neighbor."
105       " Value consists of the originator address (or '-' for all neighbors)"
106       " followed by the link cost",
107       .cb_validate = _cb_validate_link, .list = true),
108 };
109
110 static struct cfg_schema_section _constant_section = {
111   .type = OONF_CONSTANT_METRIC_SUBSYSTEM,
112   .mode = CFG_SSMODE_NAMED_WITH_DEFAULT,
113   .def_name = OS_INTERFACE_ANY,
114   .cb_delta_handler = _cb_cfg_changed,
115   .entries = _constant_entries,
116   .entry_count = ARRAYSIZE(_constant_entries),
117 };
118
119 static const char *_dependencies[] = {
120   OONF_CLASS_SUBSYSTEM,
121   OONF_TIMER_SUBSYSTEM,
122   OONF_OS_INTERFACE_SUBSYSTEM,
123   OONF_NHDP_SUBSYSTEM,
124 };
125 static struct oonf_subsystem _olsrv2_constant_metric_subsystem = {
126   .name = OONF_CONSTANT_METRIC_SUBSYSTEM,
127   .dependencies = _dependencies,
128   .dependencies_count = ARRAYSIZE(_dependencies),
129   .descr = "OLSRv2 Constant Metric plugin",
130   .author = "Henning Rogge",
131
132   .cfg_section = &_constant_section,
133
134   .init = _init,
135   .cleanup = _cleanup,
136 };
137 DECLARE_OONF_PLUGIN(_olsrv2_constant_metric_subsystem);
138
139 /* timer for handling new NHDP neighbors */
140 static struct oonf_timer_class _setup_timer_info = {
141   .name = "Delayed update of constant NHDP neighbor linkcosts",
142   .callback = _cb_set_linkcost,
143   .periodic = false,
144 };
145
146 static struct oonf_timer_instance _setup_timer = {
147   .class = &_setup_timer_info,
148 };
149
150 /* nhdp metric handler */
151 static struct nhdp_domain_metric _constant_metric_handler = {
152   .name = OONF_CONSTANT_METRIC_SUBSYSTEM,
153 };
154
155 /* NHDP link listeners */
156 static struct oonf_class_extension _link_extenstion = {
157   .ext_name = "constant linkmetric",
158   .class_name = NHDP_CLASS_LINK,
159
160   .cb_add = _cb_link_added,
161 };
162
163 /* storage for settings */
164 static struct oonf_class _linkcost_class = {
165   .name = "Constant linkcost storage",
166   .size = sizeof(struct _linkcost),
167 };
168
169 static struct avl_tree _linkcost_tree;
170
171 /**
172  * Initialize plugin
173  * @return -1 if an error happened, 0 otherwise
174  */
175 static int
176 _init(void) {
177   if (nhdp_domain_metric_add(&_constant_metric_handler)) {
178     return -1;
179   }
180
181   if (oonf_class_extension_add(&_link_extenstion)) {
182     nhdp_domain_metric_remove(&_constant_metric_handler);
183     return -1;
184   }
185
186   oonf_timer_add(&_setup_timer_info);
187   oonf_class_add(&_linkcost_class);
188   avl_init(&_linkcost_tree, _avlcmp_linkcost, false);
189   return 0;
190 }
191
192 /**
193  * Cleanup plugin
194  */
195 static void
196 _cleanup(void) {
197   struct _linkcost *lk, *lk_it;
198
199   avl_for_each_element_safe(&_linkcost_tree, lk, _node, lk_it) {
200     avl_remove(&_linkcost_tree, &lk->_node);
201     oonf_class_free(&_linkcost_class, lk);
202   }
203
204   oonf_timer_stop(&_setup_timer);
205   oonf_timer_remove(&_setup_timer_info);
206
207   oonf_class_remove(&_linkcost_class);
208
209   oonf_class_extension_remove(&_link_extenstion);
210   nhdp_domain_metric_remove(&_constant_metric_handler);
211 }
212
213 /**
214  * Callback triggered when a new nhdp link is added
215  * @param ptr nhdp link
216  */
217 static void
218 _cb_link_added(void *ptr __attribute__((unused))) {
219   oonf_timer_set(&_setup_timer, 1);
220 }
221
222 /**
223  * Get the linkcost object for an interface/neighbor combination
224  * @param ifname name of the interface
225  * @param originator IP of the neighbor
226  * @return linkcost object, NULL if not found
227  */
228 static struct _linkcost *
229 _get_linkcost(const char *ifname, const struct netaddr *originator) {
230   struct _linkcost key;
231   struct _linkcost *entry;
232
233   strscpy(key.if_name, ifname, IF_NAMESIZE);
234   memcpy(&key.neighbor, originator, sizeof(struct netaddr));
235
236   return avl_find_element(&_linkcost_tree, &key, entry, _node);
237 }
238
239 /**
240  * Timer callback for delayed setting of new metric values into db
241  * @param ptr timer instance that fired
242  */
243 static void
244 _cb_set_linkcost(struct oonf_timer_instance *ptr __attribute__((unused))) {
245   struct nhdp_link *lnk;
246   struct _linkcost *entry;
247 #ifdef OONF_LOG_DEBUG_INFO
248   struct netaddr_str nbuf;
249 #endif
250
251   OONF_DEBUG(LOG_CONSTANT_METRIC, "Start setting constant linkcosts");
252   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
253     const char *ifname;
254
255     ifname = nhdp_interface_get_name(lnk->local_if);
256     OONF_DEBUG(LOG_CONSTANT_METRIC, "Look for constant metric if=%s originator=%s",
257         ifname, netaddr_to_string(&nbuf, &lnk->neigh->originator));
258
259     if (netaddr_get_address_family(&lnk->neigh->originator) == AF_UNSPEC) {
260       continue;
261     }
262
263     entry = _get_linkcost(ifname, &lnk->neigh->originator);
264     if (entry == NULL && nhdp_db_link_is_dualstack(lnk)) {
265       entry = _get_linkcost(ifname, &lnk->dualstack_partner->neigh->originator);
266     }
267     if (entry == NULL) {
268       entry = _get_linkcost(OS_INTERFACE_ANY, &lnk->neigh->originator);
269     }
270     if (entry == NULL && nhdp_db_link_is_dualstack(lnk)) {
271       entry = _get_linkcost(OS_INTERFACE_ANY,
272           &lnk->dualstack_partner->neigh->originator);
273     }
274     if (entry == NULL)  {
275       entry = _get_linkcost(ifname, &NETADDR_UNSPEC);
276     }
277     if (entry == NULL) {
278       entry = _get_linkcost(OS_INTERFACE_ANY, &NETADDR_UNSPEC);
279     }
280     if (entry) {
281       OONF_DEBUG(LOG_CONSTANT_METRIC, "Found metric value %u", entry->cost);
282       nhdp_domain_set_incoming_metric(&_constant_metric_handler, lnk, entry->cost);
283       continue;
284     }
285     else {
286       nhdp_domain_set_incoming_metric(&_constant_metric_handler, lnk, RFC7181_METRIC_INFINITE);
287     }
288   }
289 }
290
291 /**
292  * compare two linkcosts with each other by comparing
293  * interface name and neighbor IP
294  * @param ptr1
295  * @param ptr2
296  * @return
297  */
298 static int
299 _avlcmp_linkcost(const void *ptr1, const void *ptr2) {
300   const struct _linkcost *lk1, *lk2;
301   int result;
302
303   lk1 = ptr1;
304   lk2 = ptr2;
305
306   result = avl_comp_strcasecmp(&lk1->if_name, &lk2->if_name);
307   if (result == 0) {
308     result = avl_comp_netaddr(&lk1->neighbor, &lk2->neighbor);
309   }
310   return result;
311 }
312
313 /**
314  * Validate configuration parameter for a constant metric
315  * @param entry
316  * @param section_name
317  * @param value
318  * @param out
319  * @return
320  */
321 static int
322 _cb_validate_link(const struct cfg_schema_entry *entry,
323       const char *section_name, const char *value, struct autobuf *out) {
324   struct isonumber_str sbuf;
325   struct netaddr_str nbuf;
326   const char *ptr;
327   int8_t af[] = { AF_INET, AF_INET6, AF_UNSPEC };
328
329   /* test if first word is a network address readable number */
330   ptr = str_cpynextword(nbuf.buf, value, sizeof(nbuf));
331   if (cfg_validate_netaddr(out, section_name, entry->key.entry, nbuf.buf,
332       false, af, ARRAYSIZE(af))) {
333     return -1;
334   }
335
336   /* test if second word is a human readable number */
337   ptr = str_cpynextword(sbuf.buf, ptr, sizeof(sbuf));
338   if (cfg_validate_int(out, section_name, entry->key.entry, sbuf.buf,
339       RFC7181_METRIC_MIN, RFC7181_METRIC_MAX, 4, 0, false)) {
340     return -1;
341   }
342
343   if (ptr) {
344     cfg_append_printable_line(out, "Value '%s' for entry '%s'"
345         " in section %s should have only an address and a link cost",
346         value, entry->key.entry, section_name);
347     return -1;
348   }
349   return 0;
350 }
351
352 /**
353  * Callback triggered when configuration changes
354  */
355 static void
356 _cb_cfg_changed(void) {
357   struct _linkcost *lk, *lk_it;
358   struct netaddr_str nbuf;
359   const char *ptr, *cost_ptr;
360   const struct const_strarray *array;
361   int64_t cost;
362
363   /* remove old entries for this interface */
364   avl_for_each_element_safe(&_linkcost_tree, lk, _node, lk_it) {
365     if (strcasecmp(lk->if_name, _constant_section.section_name) == 0) {
366       avl_remove(&_linkcost_tree, &lk->_node);
367       oonf_class_free(&_linkcost_class, lk);
368     }
369   }
370
371   array = cfg_db_get_schema_entry_value(
372       _constant_section.post, &_constant_entries[0]);
373   if (!array) {
374     OONF_WARN(LOG_CONSTANT_METRIC, "No link defined for static cost");
375     return;
376   }
377
378   strarray_for_each_element(array, ptr) {
379     lk = oonf_class_malloc(&_linkcost_class);
380     if (lk) {
381       cost_ptr = str_cpynextword(nbuf.buf, ptr, sizeof(nbuf));
382
383       strscpy(lk->if_name, _constant_section.section_name, IF_NAMESIZE);
384       if (netaddr_from_string(&lk->neighbor, nbuf.buf)) {
385         oonf_class_free(&_linkcost_class, lk);
386         continue;
387       }
388       if (isonumber_to_s64(&cost, cost_ptr, 0, false)) {
389         oonf_class_free(&_linkcost_class, lk);
390         continue;
391       }
392
393       lk->cost = cost;
394
395       lk->_node.key = lk;
396       avl_insert(&_linkcost_tree, &lk->_node);
397
398       OONF_DEBUG(LOG_CONSTANT_METRIC, "Add entry (%s/%s: %s)",
399           lk->if_name, nbuf.buf, cost_ptr);
400     }
401   }
402
403   /* delay updating linkcosts */
404   oonf_timer_set(&_setup_timer, 1);
405 }