ee14dfdbcc0400cba34af2de7b99df09f0c98df0
[oonf.git] / src / nhdp / nhdp / nhdp.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 <oonf/nhdp/nhdp/nhdp.h>
47 #include <oonf/oonf.h>
48 #include <oonf/libconfig/cfg_schema.h>
49 #include <oonf/libcore/oonf_logging.h>
50 #include <oonf/libcore/oonf_subsystem.h>
51 #include <oonf/nhdp/nhdp/nhdp_domain.h>
52 #include <oonf/nhdp/nhdp/nhdp_hysteresis.h>
53 #include <oonf/nhdp/nhdp/nhdp_interfaces.h>
54 #include <oonf/nhdp/nhdp/nhdp_reader.h>
55 #include <oonf/nhdp/nhdp/nhdp_writer.h>
56 #include <oonf/subsystems/oonf_class.h>
57 #include <oonf/subsystems/oonf_rfc5444.h>
58 #include <oonf/subsystems/os_interface.h>
59
60 /* definitions */
61
62 /**
63  * Parameters of a NHDP domain
64  */
65 struct _domain_parameters {
66   /*! name of metric algorithm */
67   char metric_name[NHDP_DOMAIN_METRIC_MAXLEN];
68
69   /*! name of mpr algorithm for routing */
70   char mpr_name[NHDP_DOMAIN_MPR_MAXLEN];
71
72   /*! routing willingness */
73   int32_t mpr_willingness;
74 };
75
76 /**
77  * generic paramters for all domains
78  */
79 struct _generic_parameters {
80   /*! name of MPR algorithm for flooding */
81   char flooding_mpr_name[NHDP_DOMAIN_MPR_MAXLEN];
82
83   /*! routing willingness */
84   int32_t mpr_willingness;
85 };
86
87 /* prototypes */
88 static void _early_cfg_init(void);
89 static int _init(void);
90 static void _initiate_shutdown(void);
91 static void _cleanup(void);
92
93 static bool _forwarding_selector(struct rfc5444_writer_target *rfc5444_target);
94
95 static void _cb_cfg_domain_changed(void);
96 static void _cb_cfg_interface_changed(void);
97 static void _cb_cfg_nhdp_changed(void);
98 static int _cb_validate_domain_section(const char *section_name, struct cfg_named_section *, struct autobuf *);
99
100 /* subsystem definition */
101 static struct cfg_schema_entry _nhdp_entries[] = {
102   CFG_MAP_STRING_ARRAY(_generic_parameters, flooding_mpr_name, "mpr", "*",
103     "ID of the mpr algorithm used for flooding RFC5444 messages. '" CFG_DOMAIN_NO_METRIC_MPR "'"
104     " means no mpr algorithm (everyone is MPR), '" CFG_DOMAIN_ANY_METRIC_MPR "' means"
105     " any metric that is loaded (with fallback on '" CFG_DOMAIN_NO_METRIC_MPR "').",
106     NHDP_DOMAIN_MPR_MAXLEN),
107   CFG_MAP_INT32_MINMAX(_generic_parameters, mpr_willingness, "willingness", RFC7181_WILLINGNESS_DEFAULT_STRING,
108     "Flooding willingness for MPR calculation", 0, RFC7181_WILLINGNESS_MIN, RFC7181_WILLINGNESS_MAX),
109 };
110
111 static struct cfg_schema_section _nhdp_section = {
112   CFG_NHDP_SCHEMA_NHDP_SECTION_INIT,
113
114   .cb_delta_handler = _cb_cfg_nhdp_changed,
115   .entries = _nhdp_entries,
116   .entry_count = ARRAYSIZE(_nhdp_entries),
117 };
118
119 static struct cfg_schema_entry _interface_entries[] = {
120   CFG_MAP_ACL_V46(nhdp_interface, ifaddr_filter, "ifaddr_filter", "-127.0.0.0/8\0-::1\0" ACL_DEFAULT_ACCEPT,
121     "Filter for ip interface addresses that should be included in HELLO messages"),
122   CFG_MAP_CLOCK_MIN(
123     nhdp_interface, validity_time, "hello_validity", "20.0", "Validity time for NHDP Hello Messages", 100),
124   CFG_MAP_CLOCK_MIN(
125     nhdp_interface, refresh_interval, "hello_interval", "2.0", "Time interval between two NHDP Hello Messages", 100),
126 };
127
128 static struct cfg_schema_section _interface_section = {
129   CFG_OSIF_SCHEMA_INTERFACE_SECTION_INIT,
130
131   .cb_delta_handler = _cb_cfg_interface_changed,
132   .entries = _interface_entries,
133   .entry_count = ARRAYSIZE(_interface_entries),
134   .next_section = &_nhdp_section,
135 };
136
137 static struct cfg_schema_entry _domain_entries[] = {
138   CFG_MAP_STRING_ARRAY(_domain_parameters, metric_name, "metric", CFG_DOMAIN_ANY_METRIC_MPR,
139     "ID of the routing metric used for this domain. '" CFG_DOMAIN_NO_METRIC_MPR "'"
140     " means no metric (hopcount!), '" CFG_DOMAIN_ANY_METRIC_MPR "' means any metric"
141     " that is loaded (with fallback on '" CFG_DOMAIN_NO_METRIC_MPR "').",
142     NHDP_DOMAIN_METRIC_MAXLEN),
143   CFG_MAP_STRING_ARRAY(_domain_parameters, mpr_name, "mpr", CFG_DOMAIN_ANY_METRIC_MPR,
144     "ID of the mpr algorithm used for reducing the routing (mpr-)set of this domain."
145     " '" CFG_DOMAIN_NO_METRIC_MPR "'"
146     " means no mpr algorithm (everyone is MPR), '" CFG_DOMAIN_ANY_METRIC_MPR "' means"
147     " any metric that is loaded (with fallback on '" CFG_DOMAIN_NO_METRIC_MPR "').",
148     NHDP_DOMAIN_MPR_MAXLEN),
149   CFG_MAP_INT32_MINMAX(_domain_parameters, mpr_willingness, "willingness", RFC7181_WILLINGNESS_DEFAULT_STRING,
150     "Routing willingness used for MPR calculation", 0, RFC7181_WILLINGNESS_MIN, RFC7181_WILLINGNESS_MAX),
151 };
152
153 static struct cfg_schema_section _domain_section = {
154   CFG_NHDP_SCHEMA_DOMAIN_SECTION_INIT,
155
156   .cb_delta_handler = _cb_cfg_domain_changed,
157   .cb_validate = _cb_validate_domain_section,
158
159   .entries = _domain_entries,
160   .entry_count = ARRAYSIZE(_domain_entries),
161   .next_section = &_interface_section,
162 };
163
164 static const char *_dependencies[] = {
165   OONF_CLOCK_SUBSYSTEM,
166   OONF_CLASS_SUBSYSTEM,
167   OONF_RFC5444_SUBSYSTEM,
168   OONF_TIMER_SUBSYSTEM,
169   OONF_OS_INTERFACE_SUBSYSTEM,
170 };
171 static struct oonf_subsystem nhdp_subsystem = {
172   .name = OONF_NHDP_SUBSYSTEM,
173   .dependencies = _dependencies,
174   .dependencies_count = ARRAYSIZE(_dependencies),
175   .early_cfg_init = _early_cfg_init,
176   .init = _init,
177   .cleanup = _cleanup,
178   .initiate_shutdown = _initiate_shutdown,
179   .cfg_section = &_domain_section,
180 };
181 DECLARE_OONF_PLUGIN(nhdp_subsystem);
182
183 /* other global variables */
184 static struct oonf_rfc5444_protocol *_protocol;
185
186 /* NHDP originator address, might be undefined */
187 static struct netaddr _originator_v4, _originator_v6;
188
189 /* Additional logging sources, not static because used by other source files! */
190 enum oonf_log_source LOG_NHDP;
191 enum oonf_log_source LOG_NHDP_R;
192 enum oonf_log_source LOG_NHDP_W;
193
194 /**
195  * Initialize additional logging sources for NHDP
196  */
197 static void
198 _early_cfg_init(void) {
199   LOG_NHDP = nhdp_subsystem.logging;
200   LOG_NHDP_R = oonf_log_register_source(OONF_NHDP_SUBSYSTEM "_r");
201   LOG_NHDP_W = oonf_log_register_source(OONF_NHDP_SUBSYSTEM "_w");
202 }
203
204 /**
205  * Initialize NHDP subsystem
206  * @return 0 if initialized, -1 if an error happened
207  */
208 static int
209 _init(void) {
210   _protocol = oonf_rfc5444_get_default_protocol();
211   if (nhdp_writer_init(_protocol)) {
212     return -1;
213   }
214
215   nhdp_db_init();
216   nhdp_reader_init(_protocol);
217   nhdp_interfaces_init(_protocol);
218   nhdp_domain_init(_protocol);
219
220   return 0;
221 }
222
223 /**
224  * Begin shutdown by deactivating reader and writer
225  */
226 static void
227 _initiate_shutdown(void) {
228   nhdp_writer_cleanup();
229   nhdp_reader_cleanup();
230 }
231
232 /**
233  * Cleanup NHDP subsystem
234  */
235 static void
236 _cleanup(void) {
237   nhdp_db_cleanup();
238   nhdp_interfaces_cleanup();
239   nhdp_domain_cleanup();
240 }
241
242 /**
243  * Sets the originator address used by NHDP to a new value.
244  * @param addr NHDP originator.
245  */
246 void
247 nhdp_set_originator(const struct netaddr *addr) {
248 #ifdef OONF_LOG_DEBUG_INFO
249   struct netaddr_str buf;
250 #endif
251
252   OONF_DEBUG(LOG_NHDP, "Set originator to %s", netaddr_to_string(&buf, addr));
253   if (netaddr_get_address_family(addr) == AF_INET) {
254     memcpy(&_originator_v4, addr, sizeof(*addr));
255   }
256   else if (netaddr_get_address_family(addr) == AF_INET6) {
257     memcpy(&_originator_v6, addr, sizeof(*addr));
258   }
259 }
260
261 /**
262  * Remove the originator currently set
263  * @param af_type address family type of the originator
264  *   (AF_INET or AF_INET6)
265  */
266 void
267 nhdp_reset_originator(int af_type) {
268   if (af_type == AF_INET) {
269     netaddr_invalidate(&_originator_v4);
270   }
271   else if (af_type == AF_INET6) {
272     netaddr_invalidate(&_originator_v6);
273   }
274 }
275
276 /**
277  * @param af_type address family type of the originator
278  *   (AF_INET or AF_INET6)
279  * @return current NHDP originator
280  */
281 const struct netaddr *
282 nhdp_get_originator(int af_type) {
283   if (af_type == AF_INET) {
284     return &_originator_v4;
285   }
286   else if (af_type == AF_INET6) {
287     return &_originator_v6;
288   }
289   return NULL;
290 }
291
292 /**
293  * default implementation for rfc5444 flooding target selection to
294  * handle dualstack correctly.
295  * @param writer rfc5444 protocol to flood messages
296  * @param rfc5444_target rfc5444 target to flood message
297  * @param ptr custom pointer for message flooding callback
298  * @return true if message should be flooded
299  */
300 bool
301 nhdp_flooding_selector(struct rfc5444_writer *writer __attribute__((unused)),
302   struct rfc5444_writer_target *rfc5444_target, void *ptr __attribute__((unused))) {
303   return _forwarding_selector(rfc5444_target);
304 }
305
306 /**
307  * default implementation for rfc5444 forwarding selector to
308  * hangle dualstack correctly
309  * @param rfc5444_target rfc5444 target to flood message to
310  * @param context reader context of the message to be forwarded
311  * @return true if target corresponds to selection
312  */
313 bool
314 nhdp_forwarding_selector(struct rfc5444_writer_target *rfc5444_target,
315   struct rfc5444_reader_tlvblock_context *context __attribute__((unused))) {
316   return _forwarding_selector(rfc5444_target);
317 }
318
319 /**
320  * default implementation for rfc5444 forwarding selector to
321  * hangle dualstack correctly
322  * @param rfc5444_target rfc5444 target
323  * @return true if target corresponds to selection
324  */
325 static bool
326 _forwarding_selector(struct rfc5444_writer_target *rfc5444_target) {
327   struct oonf_rfc5444_target *target;
328   struct nhdp_interface *interf;
329   bool is_ipv4, flood;
330 #ifdef OONF_LOG_DEBUG_INFO
331   struct netaddr_str buf;
332 #endif
333   target = container_of(rfc5444_target, struct oonf_rfc5444_target, rfc5444_target);
334
335   /* test if this is the ipv4 multicast target */
336   is_ipv4 = target == target->interface->multicast4;
337
338   /* only forward to multicast targets */
339   if (!is_ipv4 && target != target->interface->multicast6) {
340     return false;
341   }
342
343   /* get NHDP interface for target */
344   interf = nhdp_interface_get(target->interface->name);
345   if (interf == NULL) {
346     OONF_DEBUG(LOG_NHDP,
347       "Do not flood message type"
348       " to interface %s: its unknown to NHDP",
349       target->interface->name);
350     return NULL;
351   }
352
353   /* lookup flooding cache in NHDP interface */
354   if (is_ipv4) {
355     flood = interf->use_ipv4_for_flooding || interf->dualstack_af_type == AF_INET;
356   }
357   else {
358     flood = interf->use_ipv6_for_flooding || interf->dualstack_af_type == AF_INET6;
359   }
360
361   OONF_DEBUG(LOG_NHDP, "Flooding to target %s: %s", netaddr_to_string(&buf, &target->dst), flood ? "yes" : "no");
362
363   return flood;
364 }
365
366 /**
367  * Configuration of a NHDP domain changed
368  */
369 static void
370 _cb_cfg_domain_changed(void) {
371   struct _domain_parameters param;
372   int ext;
373
374   OONF_INFO(LOG_NHDP, "Received domain cfg change for name '%s': %s %s", _domain_section.section_name,
375     _domain_section.pre != NULL ? "pre" : "-", _domain_section.post != NULL ? "post" : "-");
376
377   ext = strtol(_domain_section.section_name, NULL, 10);
378
379   if (cfg_schema_tobin(&param, _domain_section.post, _domain_entries, ARRAYSIZE(_domain_entries))) {
380     OONF_WARN(LOG_NHDP, "Cannot convert NHDP domain configuration.");
381     return;
382   }
383
384   nhdp_domain_configure(ext, param.metric_name, param.mpr_name, param.mpr_willingness);
385 }
386
387 /**
388  * Configuration has changed, handle the changes
389  */
390 static void
391 _cb_cfg_interface_changed(void) {
392   struct nhdp_interface *nhdp_if;
393   const char *ifname;
394   char ifbuf[IF_NAMESIZE];
395
396   ifname = cfg_get_phy_if(ifbuf, _interface_section.section_name);
397   OONF_DEBUG(LOG_NHDP, "Configuration of NHDP interface %s changed", _interface_section.section_name);
398
399   if (_interface_section.pre == NULL) {
400     /* increase nhdp_interface refcount */
401     nhdp_if = nhdp_interface_add(ifname);
402   }
403   else {
404     /* get interface */
405     nhdp_if = nhdp_interface_get(ifname);
406   }
407
408   if (nhdp_if) {
409     /* get block domain extension */
410     nhdp_if->registered = true;
411   }
412
413   if (_interface_section.post == NULL) {
414     /* section was removed */
415     if (nhdp_if != NULL) {
416       nhdp_if->registered = false;
417
418       /* decrease nhdp_interface refcount */
419       nhdp_interface_remove(nhdp_if);
420     }
421
422     nhdp_if = NULL;
423   }
424
425   if (!nhdp_if) {
426     return;
427   }
428
429   if (cfg_schema_tobin(nhdp_if, _interface_section.post, _interface_entries, ARRAYSIZE(_interface_entries))) {
430     OONF_WARN(LOG_NHDP, "Cannot convert NHDP configuration for interface.");
431     return;
432   }
433
434   /* apply new settings to interface */
435   nhdp_interface_apply_settings(nhdp_if);
436 }
437
438 static void
439 _cb_cfg_nhdp_changed(void) {
440   struct _generic_parameters param;
441   if (cfg_schema_tobin(&param, _nhdp_section.post, _nhdp_entries, ARRAYSIZE(_nhdp_entries))) {
442     OONF_WARN(LOG_NHDP, "Cannot convert NHDP configuration.");
443     return;
444   }
445
446   nhdp_domain_set_flooding_mpr(param.flooding_mpr_name, param.mpr_willingness);
447 }
448
449 /**
450  * Validate that the name of the domain section is valid
451  * @param section_name name of section including type
452  * @param named cfg named section
453  * @param out output buffer for errors
454  * @return -1 if invalid, 0 otherwise
455  */
456 static int
457 _cb_validate_domain_section(const char *section_name, struct cfg_named_section *named, struct autobuf *out) {
458   char *error = NULL;
459   int ext;
460
461   if (!named->name) {
462     /* default name should be okay */
463     return 0;
464   }
465
466   ext = strtol(named->name, &error, 10);
467   if (error != NULL && *error != 0) {
468     /* illegal domain name */
469     abuf_appendf(out, "name of section '%s' must be a number between 0 and 255", section_name);
470     return -1;
471   }
472
473   if (ext < 0 || ext > 255) {
474     /* name out of range */
475     abuf_appendf(out, "name of section '%s' must be a number between 0 and 255", section_name);
476     return -1;
477   }
478   return 0;
479 }