Rename "subsystems" directory to "base"
[oonf.git] / src / nhdp / hysteresis_olsrv1 / hysteresis_olsrv1.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /**
43  * @file
44  */
45
46 #include <stdio.h>
47
48 #include <oonf/libcommon/autobuf.h>
49 #include <oonf/oonf.h>
50 #include <oonf/libcore/oonf_cfg.h>
51 #include <oonf/libcore/oonf_logging.h>
52 #include <oonf/libcore/oonf_subsystem.h>
53 #include <oonf/base/oonf_class.h>
54 #include <oonf/base/oonf_rfc5444.h>
55 #include <oonf/base/oonf_timer.h>
56
57 #include <oonf/nhdp/nhdp/nhdp_hysteresis.h>
58 #include <oonf/nhdp/nhdp/nhdp_interfaces.h>
59
60 #include <oonf/nhdp/hysteresis_olsrv1/hysteresis_olsrv1.h>
61
62 /* definitions and constants */
63 #define LOG_HYSTERESIS_OLSRV1 _olsrv2_hysteresis_olsrv1_subsystem.logging
64
65 /**
66  * hysteresis plugin configuration
67  */
68 struct _config {
69   /*! hysteresis threshold to accept a link (multiplied by 1000) */
70   int accept;
71
72   /*! hysteresis threshold to reject a link (multiplied by 1000) */
73   int reject;
74
75   /*! alpha factor for exponential aging (multiplied by 1000) */
76   int scaling;
77 };
78
79 /**
80  * extension of nhdp_link class for hysteresis calculation
81  */
82 struct link_hysteresis_data {
83   /*! timer until the next NHDP Hello should arrive */
84   struct oonf_timer_instance interval_timer;
85
86   /*! back pointer to NHDP link */
87   struct nhdp_link *nhdp_link;
88
89   /*! itime time delivered by neighbors Hello */
90   uint64_t itime;
91
92   /*! current hysteresis quality of this link */
93   int32_t quality;
94
95   /*! true if the link is considered pending */
96   bool pending;
97
98   /*! true if the link is considered lost */
99   bool lost;
100 };
101
102 /* prototypes */
103 static int _init(void);
104 static void _cleanup(void);
105
106 static void _update_hysteresis(struct nhdp_link *, struct link_hysteresis_data *, bool);
107
108 static void _cb_link_added(void *);
109 static void _cb_link_removed(void *);
110
111 static void _cb_update_hysteresis(struct nhdp_link *, struct rfc5444_reader_tlvblock_context *context);
112 static bool _cb_is_pending(struct nhdp_link *);
113 static bool _cb_is_lost(struct nhdp_link *);
114 static const char *_cb_to_string(struct nhdp_hysteresis_str *, struct nhdp_link *);
115
116 static void _cb_timer_hello_lost(struct oonf_timer_instance *);
117 static void _cb_cfg_changed(void);
118 static int _cb_cfg_validate(const char *section_name, struct cfg_named_section *, struct autobuf *);
119
120 /* configuration options */
121 static struct cfg_schema_entry _hysteresis_entries[] = {
122   CFG_MAP_INT32_MINMAX(_config, accept, "accept", "0.7", "link quality to consider a link up", 3, 0, 1000),
123   CFG_MAP_INT32_MINMAX(_config, reject, "reject", "0.3", "link quality to consider a link down", 3, 0, 1000),
124   CFG_MAP_INT32_MINMAX(
125     _config, scaling, "scaling", "0.25", "exponential aging to control speed of link hysteresis", 3, 1, 1000),
126 };
127
128 static struct cfg_schema_section _hysteresis_section = {
129   .type = OONF_HYSTERESIS_OLSRV1_SUBSYSTEM,
130   .cb_delta_handler = _cb_cfg_changed,
131   .cb_validate = _cb_cfg_validate,
132   .entries = _hysteresis_entries,
133   .entry_count = ARRAYSIZE(_hysteresis_entries),
134 };
135
136 static struct _config _hysteresis_config;
137
138 /* plugin declaration */
139 static const char *_dependencies[] = {
140   OONF_CLASS_SUBSYSTEM,
141   OONF_RFC5444_SUBSYSTEM,
142   OONF_TIMER_SUBSYSTEM,
143   OONF_NHDP_SUBSYSTEM,
144 };
145 static struct oonf_subsystem _olsrv2_hysteresis_olsrv1_subsystem = {
146   .name = OONF_HYSTERESIS_OLSRV1_SUBSYSTEM,
147   .dependencies = _dependencies,
148   .dependencies_count = ARRAYSIZE(_dependencies),
149   .descr = "OONFD2 olsrv1-style hysteresis plugin",
150   .author = "Henning Rogge",
151
152   .cfg_section = &_hysteresis_section,
153
154   .init = _init,
155   .cleanup = _cleanup,
156 };
157 DECLARE_OONF_PLUGIN(_olsrv2_hysteresis_olsrv1_subsystem);
158
159 /* storage extension for nhdp_link */
160 static struct oonf_class_extension _link_extenstion = {
161   .ext_name = OONF_HYSTERESIS_OLSRV1_SUBSYSTEM,
162   .class_name = NHDP_CLASS_LINK,
163   .size = sizeof(struct link_hysteresis_data),
164   .cb_add = _cb_link_added,
165   .cb_remove = _cb_link_removed,
166 };
167
168 /* timer class to measure itime between Hellos */
169 static struct oonf_timer_class _hello_timer_info = {
170   .name = "Hello itime timeout for hysteresis",
171   .callback = _cb_timer_hello_lost,
172 };
173
174 /* hysteresis handler */
175 static struct nhdp_hysteresis_handler _hysteresis_handler = {
176   .name = "hysteresis_olsrv1",
177   .update_hysteresis = _cb_update_hysteresis,
178   .is_pending = _cb_is_pending,
179   .is_lost = _cb_is_lost,
180   .to_string = _cb_to_string,
181 };
182
183 /**
184  * Initialize plugin
185  * @return -1 if an error happened, 0 otherwise
186  */
187 static int
188 _init(void) {
189   if (oonf_class_is_extension_registered(&_link_extenstion)) {
190     struct nhdp_link *lnk;
191
192     /* add all custom extensions for link */
193     list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
194       _cb_link_added(lnk);
195     }
196   }
197   else if (oonf_class_extension_add(&_link_extenstion)) {
198     return -1;
199   }
200
201   nhdp_hysteresis_set_handler(&_hysteresis_handler);
202   return 0;
203 }
204
205 /**
206  * Cleanup plugin
207  */
208 static void
209 _cleanup(void) {
210   struct nhdp_link *lnk;
211
212   /* remove all custom extensions for link */
213   list_for_each_element(nhdp_db_get_link_list(), lnk, _global_node) {
214     _cb_link_removed(lnk);
215   }
216
217   nhdp_hysteresis_set_handler(NULL);
218   oonf_class_extension_remove(&_link_extenstion);
219 }
220
221 /**
222  * Update the quality value of a link
223  * @param lnk pointer to nhdp link
224  * @param data pointer to link hysteresis data
225  * @param lost true if hello was lost, false if hello was received
226  */
227 static void
228 _update_hysteresis(struct nhdp_link *lnk, struct link_hysteresis_data *data, bool lost) {
229   /* calculate exponential aging */
230   data->quality = data->quality * (1000 - _hysteresis_config.scaling);
231   data->quality = (data->quality + 999) / 1000;
232   if (!lost) {
233     data->quality += _hysteresis_config.scaling;
234   }
235
236   if (!data->pending && !data->lost) {
237     if (data->quality < _hysteresis_config.reject) {
238       data->lost = true;
239       nhdp_db_link_update_status(lnk);
240     }
241   }
242   else {
243     if (data->quality > _hysteresis_config.accept) {
244       data->pending = false;
245       data->lost = false;
246       nhdp_db_link_update_status(lnk);
247     }
248   }
249 }
250
251 /**
252  * Callback triggered when a new nhdp link is added
253  * @param ptr nhdp link
254  */
255 static void
256 _cb_link_added(void *ptr) {
257   struct link_hysteresis_data *data;
258   data = oonf_class_get_extension(&_link_extenstion, ptr);
259
260   memset(data, 0, sizeof(*data));
261   data->pending = true;
262   data->nhdp_link = ptr;
263
264   data->interval_timer.class = &_hello_timer_info;
265 }
266
267 /**
268  * Callback triggered when a nhdp link will be removed
269  * @param ptr nhdp link
270  */
271 static void
272 _cb_link_removed(void *ptr) {
273   struct link_hysteresis_data *data;
274   data = oonf_class_get_extension(&_link_extenstion, ptr);
275
276   oonf_timer_stop(&data->interval_timer);
277 }
278
279 /**
280  * Callback for hysteresis handler which is triggered to
281  * update the hysteresis when a HELLO is received.
282  * @param lnk nhdp link
283  * @param context RFC5444 tlvblock reader context
284  */
285 static void
286 _cb_update_hysteresis(struct nhdp_link *lnk, struct rfc5444_reader_tlvblock_context *context __attribute__((unused))) {
287   struct link_hysteresis_data *data;
288
289   data = oonf_class_get_extension(&_link_extenstion, lnk);
290
291   /* update hysteresis because of received hello */
292   _update_hysteresis(lnk, data, false);
293
294   /* store itime */
295   data->itime = lnk->itime_value;
296
297   /* first timer gets a delay */
298   if (data->itime == 0) {
299     data->itime = lnk->vtime_value;
300   }
301   oonf_timer_set(&data->interval_timer, (data->itime * 3) / 2);
302 }
303
304 /**
305  * Callback for hysteresis handler to check if link is pending
306  * @param lnk nhdp link
307  * @return true if link is pending, false otherwise
308  */
309 static bool
310 _cb_is_pending(struct nhdp_link *lnk) {
311   struct link_hysteresis_data *data;
312
313   data = oonf_class_get_extension(&_link_extenstion, lnk);
314   return data->pending;
315 }
316
317 /**
318  * Callback for hysteresis handler to check if link is lost
319  * @param lnk nhdp link
320  * @return true if link is lost, false otherwise
321  */
322 static bool
323 _cb_is_lost(struct nhdp_link *lnk) {
324   struct link_hysteresis_data *data;
325
326   data = oonf_class_get_extension(&_link_extenstion, lnk);
327   return data->lost;
328 }
329
330 /**
331  * Callback for hysteresis handler to get a human readable
332  * from of the current hysteresis data.
333  * @param buf output buffer
334  * @param lnk nhdp link
335  * @return pointer to output buffer
336  */
337 static const char *
338 _cb_to_string(struct nhdp_hysteresis_str *buf, struct nhdp_link *lnk) {
339   struct isonumber_str fbuf;
340   struct link_hysteresis_data *data;
341
342   data = oonf_class_get_extension(&_link_extenstion, lnk);
343
344   snprintf(buf->buf, sizeof(*buf), "quality=%s", isonumber_from_s64(&fbuf, data->quality, "", 3, true));
345
346   return buf->buf;
347 }
348
349 /**
350  * Timer callback triggered when Hello was lost
351  * @param ptr timer instance that fired
352  */
353 static void
354 _cb_timer_hello_lost(struct oonf_timer_instance *ptr) {
355   struct link_hysteresis_data *data;
356
357   data = container_of(ptr, struct link_hysteresis_data, interval_timer);
358
359   /* update hysteresis because of lost Hello */
360   _update_hysteresis(data->nhdp_link, data, true);
361
362   /* reactivate timer */
363   oonf_timer_set(&data->interval_timer, data->itime);
364 }
365
366 /**
367  * Callback triggered when configuration changes
368  */
369 static void
370 _cb_cfg_changed(void) {
371   if (cfg_schema_tobin(
372         &_hysteresis_config, _hysteresis_section.post, _hysteresis_entries, ARRAYSIZE(_hysteresis_entries))) {
373     OONF_WARN(LOG_HYSTERESIS_OLSRV1, "Could not convert " OONF_HYSTERESIS_OLSRV1_SUBSYSTEM " plugin configuration");
374   }
375 }
376
377 /**
378  * Callback triggered to check validity of configuration section
379  * @param section_name name of section
380  * @param named configuration data of section
381  * @param out output buffer for error messages
382  * @return 0 if data is okay, -1 if an error happened
383  */
384 static int
385 _cb_cfg_validate(const char *section_name, struct cfg_named_section *named, struct autobuf *out) {
386   struct _config cfg;
387   struct isonumber_str buf1, buf2;
388
389   if (cfg_schema_tobin(&cfg, named, _hysteresis_entries, ARRAYSIZE(_hysteresis_entries))) {
390     cfg_append_printable_line(out, "Could not parse hysteresis configuration in section %s", section_name);
391     return -1;
392   }
393
394   if (cfg.accept <= cfg.reject) {
395     cfg_append_printable_line(out, "hysteresis accept (%s) is not smaller than reject (%s) value",
396       isonumber_from_s64(&buf1, cfg.accept, "", 3, true), isonumber_from_s64(&buf2, cfg.reject, "", 3, true));
397     return -1;
398   }
399   return 0;
400 }