3572bd3043fcb4006c7ce926bebc686f3b3a4e65
[oonf.git] / src-plugins / olsrv2 / lan_import / lan_import.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 "common/autobuf.h"
47 #include "common/avl.h"
48 #include "common/avl_comp.h"
49 #include "common/common_types.h"
50 #include "common/list.h"
51 #include "common/netaddr.h"
52 #include "common/netaddr_acl.h"
53
54 #include "core/oonf_logging.h"
55 #include "core/oonf_subsystem.h"
56 #include "subsystems/oonf_class.h"
57 #include "subsystems/oonf_clock.h"
58 #include "subsystems/oonf_timer.h"
59
60 #include "olsrv2/olsrv2.h"
61 #include "olsrv2/olsrv2_lan.h"
62 #include "olsrv2/olsrv2_routing.h"
63
64 #include "lan_import/lan_import.h"
65
66 /* definitions */
67 #define LOG_LAN_IMPORT _import_subsystem.logging
68
69 /**
70  * configuration of one LAN import instance
71  */
72 struct _import_entry {
73   /*! name of the lan import */
74   char name[16];
75
76   /*! domain of the lan import */
77   int32_t domain;
78
79   /*! address filter */
80   struct netaddr_acl filter;
81
82   /*! filter by prefix length, -1 to ignore */
83   int32_t prefix_length;
84
85   /*! filter by interface name, length null to ignore*/
86   char ifname[IF_NAMESIZE];
87
88   /*! filter by routing table id, 0 to ignore */
89   int32_t table;
90
91   /*! filter by routing protocol id, 0 to ignore */
92   int32_t protocol;
93
94   /*! filter by routing metric, 0 to ignore */
95   int32_t distance;
96
97   /*! set the routing metric to a specific value */
98   int32_t routing_metric;
99
100   /*! double the metric every time interval, 0 to disable */
101   uint64_t metric_aging;
102
103   /*! list of lan entries imported by this filter */
104   struct avl_tree imported_lan_tree;
105
106   /*! tree of all configured lan import */
107   struct avl_node _node;
108 };
109
110 struct _imported_lan {
111   struct os_route_key key;
112
113   struct _import_entry *import;
114
115   /*! timer to age metric value */
116   struct oonf_timer_instance _aging_timer;
117
118   /*! node for list of imported lan entries */
119   struct avl_node _node;
120 };
121
122 /* prototypes */
123 static int _init(void);
124 static void _initiate_shutdown(void);
125 static void _cleanup(void);
126
127 static struct _import_entry *_get_import(const char *name);
128 static void _destroy_import(struct _import_entry *);
129
130 static struct _imported_lan *_add_lan(
131   struct _import_entry *, struct os_route_key *key, uint32_t metric, uint8_t distance);
132 static void _destroy_lan(struct _imported_lan *);
133
134 static void _cb_query(struct os_route *filter, struct os_route *route);
135 static void _cb_query_finished(struct os_route *, int error);
136
137 static bool _is_allowed_to_import(const struct os_route *route);
138 static void _cb_rt_event(const struct os_route *, bool);
139
140 static void _cb_metric_aging(struct oonf_timer_instance *entry);
141
142 static void _cb_cfg_changed(void);
143
144 /* plugin declaration */
145 static struct cfg_schema_entry _import_entries[] = {
146   CFG_MAP_INT32_MINMAX(
147     _import_entry, domain, "domain", "-1", "Routing domain extension for filter, -1 for all domains", 0, -1, 255),
148   CFG_MAP_ACL(_import_entry, filter, "matches", ACL_DEFAULT_ACCEPT,
149     "Ip addresses the filter should be applied to"
150     " (the plugin will never import loopback, linklocal or multicast IPs)"),
151   CFG_MAP_INT32_MINMAX(_import_entry, prefix_length, "prefix_length", "-1",
152     "Prefix length the filter should be applied to, -1 for any prefix length", 0, -1, 128),
153   CFG_MAP_STRING_ARRAY(
154     _import_entry, ifname, "interface", "", "Interface name of matching routes, empty if all interfaces", IF_NAMESIZE),
155   CFG_MAP_INT32_MINMAX(
156     _import_entry, table, "table", "-1", "Routing table of matching routes, 0 for matching all tables", 0, -1, 255),
157   CFG_MAP_INT32_MINMAX(
158     _import_entry, protocol, "protocol", "-1", "Routing protocol of matching routes, 0 for all protocols", 0, -1, 255),
159   CFG_MAP_INT32_MINMAX(
160     _import_entry, distance, "metric", "-1", "Metric of matching routes, 0 for all metrics", 0, -1, INT32_MAX),
161   CFG_MAP_INT32_MINMAX(_import_entry, routing_metric, "routing_metric", "1",
162     "Set the routing metric of an imported route to a specific value", false, RFC7181_METRIC_MIN, RFC7181_METRIC_MAX),
163   CFG_MAP_CLOCK(_import_entry, metric_aging, "metric_aging", "0",
164     "Double the routing metric value every time interval, 0 to disable"),
165 };
166
167 static struct cfg_schema_section _import_section = {
168   .type = OONF_LAN_IMPORT_SUBSYSTEM,
169
170   /*
171    * this MUST NOT be CFG_SSMODE_NAMED_WITH_DEFAULT, otherwise it will
172    * activate without user interaction
173    */
174   .mode = CFG_SSMODE_NAMED,
175
176   .cb_delta_handler = _cb_cfg_changed,
177
178   .entries = _import_entries,
179   .entry_count = ARRAYSIZE(_import_entries),
180 };
181
182 static const char *_dependencies[] = {
183   OONF_CLASS_SUBSYSTEM,
184   OONF_CLOCK_SUBSYSTEM,
185   OONF_TIMER_SUBSYSTEM,
186   OONF_OLSRV2_SUBSYSTEM,
187   OONF_OS_ROUTING_SUBSYSTEM,
188 };
189 static struct oonf_subsystem _import_subsystem = {
190   .name = OONF_LAN_IMPORT_SUBSYSTEM,
191   .dependencies = _dependencies,
192   .dependencies_count = ARRAYSIZE(_dependencies),
193   .descr = "OLSRv2 lan-import plugin",
194   .author = "Henning Rogge",
195
196   .cfg_section = &_import_section,
197
198   .init = _init,
199   .cleanup = _cleanup,
200   .initiate_shutdown = _initiate_shutdown,
201 };
202 DECLARE_OONF_PLUGIN(_import_subsystem);
203
204 /* class definition for filters */
205 static struct oonf_class _import_class = {
206   .name = "lan import filter",
207   .size = sizeof(struct _import_entry),
208 };
209
210 /* class definition for imported lans */
211 static struct oonf_class _lan_import_class = {
212   .name = "lan import entry",
213   .size = sizeof(struct _imported_lan),
214 };
215
216 /* callback filter for dijkstra */
217 static struct os_route_listener _routing_listener = {
218   .cb_get = _cb_rt_event,
219 };
220
221 /* tree of lan importers */
222 static struct avl_tree _import_tree;
223
224 static struct oonf_timer_class _aging_timer_class = {
225   .name = "lan import metric aging",
226   .callback = _cb_metric_aging,
227   .periodic = true,
228 };
229
230 /* wildcard route for first query */
231 static struct os_route _unicast_query;
232
233 /**
234  * Initialize plugin
235  * @return always returns 0 (cannot fail)
236  */
237 static int
238 _init(void) {
239   avl_init(&_import_tree, avl_comp_strcasecmp, false);
240   oonf_class_add(&_import_class);
241   oonf_class_add(&_lan_import_class);
242   os_routing_listener_add(&_routing_listener);
243   oonf_timer_add(&_aging_timer_class);
244
245   /* initialize wildcard query */
246   os_routing_init_wildcard_route(&_unicast_query);
247   _unicast_query.cb_get = _cb_query;
248   _unicast_query.cb_finished = _cb_query_finished;
249   _unicast_query.p.type = OS_ROUTE_UNICAST;
250   return 0;
251 }
252
253 static void
254 _initiate_shutdown(void) {
255   /* we are not interested in listening to all the routing cleanup */
256   os_routing_listener_remove(&_routing_listener);
257 }
258
259 /**
260  * Cleanup plugin
261  */
262 static void
263 _cleanup(void) {
264   struct _import_entry *import, *import_it;
265
266   avl_for_each_element_safe(&_import_tree, import, _node, import_it) {
267     _destroy_import(import);
268   }
269
270   oonf_timer_remove(&_aging_timer_class);
271   oonf_class_remove(&_lan_import_class);
272   oonf_class_remove(&_import_class);
273 }
274
275 /**
276  * Wrapper for cb_get for wildcard query
277  * @param filter unused filter
278  * @param route route found by wildcard query
279  */
280 static void
281 _cb_query(struct os_route *filter __attribute__((unused)), struct os_route *route) {
282   _cb_rt_event(route, true);
283 }
284
285 /**
286  * Dummy cb_finished callback for wildcard query
287  * @param route route that was finished
288  * @param error error code
289  */
290 static void
291 _cb_query_finished(struct os_route *route __attribute__((unused)), int error __attribute__((unused))) {}
292
293 /**
294  * Checks if importing the route is prevented because of safety issues
295  * @param route route data
296  * @return true if is okay to import, false otherwise
297  */
298 static bool
299 _is_allowed_to_import(const struct os_route *route) {
300   struct nhdp_domain *domain;
301   const struct olsrv2_routing_domain *rtparam;
302   struct os_interface *interf;
303
304   list_for_each_element(nhdp_domain_get_list(), domain, _node) {
305     rtparam = olsrv2_routing_get_parameters(domain);
306     if (rtparam->protocol == route->p.protocol && rtparam->table == route->p.table) {
307       /* do never set a LAN for a route tagged with an olsrv2 protocol */
308       OONF_DEBUG(LOG_LAN_IMPORT, "Matches olsrv2 protocol, do not import!");
309       return false;
310     }
311   }
312
313   interf = os_interface_get_data_by_ifindex(route->p.if_index);
314   if (interf != NULL && interf->flags.mesh) {
315     /* don't import routes from mesh interface */
316     return false;
317   }
318   return true;
319 }
320
321 /**
322  * Callback for route listener
323  * @param route routing data
324  * @param set true if route was set, false otherwise
325  */
326 static void
327 _cb_rt_event(const struct os_route *route, bool set) {
328   struct _import_entry *import;
329   struct _imported_lan *lan;
330   char ifname[IF_NAMESIZE];
331   struct os_route_key ssprefix;
332   int metric;
333
334 #ifdef OONF_LOG_DEBUG_INFO
335   struct os_route_str rbuf;
336 #endif
337
338   if (netaddr_is_in_subnet(&NETADDR_IPV4_MULTICAST, &route->p.key.dst) ||
339       netaddr_is_in_subnet(&NETADDR_IPV4_LINKLOCAL, &route->p.key.dst) ||
340       netaddr_is_in_subnet(&NETADDR_IPV4_LOOPBACK_NET, &route->p.key.dst) ||
341       netaddr_is_in_subnet(&NETADDR_IPV6_MULTICAST, &route->p.key.dst) ||
342       netaddr_is_in_subnet(&NETADDR_IPV6_LINKLOCAL, &route->p.key.dst) ||
343       netaddr_is_in_subnet(&NETADDR_IPV6_LOOPBACK, &route->p.key.dst)) {
344     /* ignore multicast, linklocal and loopback */
345     return;
346   }
347   if (route->p.type != OS_ROUTE_UNICAST) {
348     /* return all non-unicast type routes */
349     return;
350   }
351
352   OONF_DEBUG(
353     LOG_LAN_IMPORT, "Received route event (%s): %s", set ? "set" : "remove", os_routing_to_string(&rbuf, &route->p));
354
355   if (!_is_allowed_to_import(route)) {
356     return;
357   }
358
359   avl_for_each_element(&_import_tree, import, _node) {
360     OONF_DEBUG(LOG_LAN_IMPORT, "Check for import: %s", import->name);
361
362     /* check prefix length */
363     if (import->prefix_length != -1 && import->prefix_length != netaddr_get_prefix_length(&route->p.key.dst)) {
364       OONF_DEBUG(LOG_LAN_IMPORT, "Bad prefix length");
365       continue;
366     }
367
368     /* check if destination matches */
369     if (!netaddr_acl_check_accept(&import->filter, &route->p.key.dst)) {
370       OONF_DEBUG(LOG_LAN_IMPORT, "Bad prefix");
371       continue;
372     }
373
374     /* check routing table */
375     if (import->table != -1 && import->table != route->p.table) {
376       OONF_DEBUG(LOG_LAN_IMPORT, "Bad routing table");
377       continue;
378     }
379
380     /* check protocol */
381     if (import->protocol != -1 && import->protocol != route->p.protocol) {
382       OONF_DEBUG(LOG_LAN_IMPORT, "Bad protocol");
383       continue;
384     }
385
386     /* check metric */
387     if (import->distance != -1 && import->distance != route->p.metric) {
388       OONF_DEBUG(LOG_LAN_IMPORT, "Bad distance");
389       continue;
390     }
391
392     /* check interface name */
393     if (import->ifname[0]) {
394       if_indextoname(route->p.if_index, ifname);
395       if (strcmp(import->ifname, ifname) != 0) {
396         OONF_DEBUG(LOG_LAN_IMPORT, "Bad interface");
397         continue;
398       }
399     }
400
401     memcpy(&ssprefix.dst, &route->p.key.dst, sizeof(struct netaddr));
402     memcpy(&ssprefix.src, &route->p.key.src, sizeof(struct netaddr));
403
404     if (set) {
405       metric = route->p.metric;
406       if (metric < 1) {
407         metric = 1;
408       }
409       if (metric > 255) {
410         metric = 255;
411       }
412
413       OONF_DEBUG(LOG_LAN_IMPORT, "Add lan...");
414       lan = _add_lan(import, &ssprefix, import->routing_metric, metric);
415       if (lan && import->metric_aging) {
416         oonf_timer_set(&lan->_aging_timer, import->metric_aging);
417       }
418     }
419     else {
420       OONF_DEBUG(LOG_LAN_IMPORT, "Remove lan...");
421       lan = avl_find_element(&import->imported_lan_tree, &ssprefix, lan, _node);
422       if (lan) {
423         _destroy_lan(lan);
424       }
425     }
426   }
427 }
428
429 /**
430  * Lookups a lan importer or create a new one
431  * @param name name of lan importer
432  * @return pointer to lan importer or NULL if out of memory
433  */
434 static struct _import_entry *
435 _get_import(const char *name) {
436   struct _import_entry *import;
437
438   import = avl_find_element(&_import_tree, name, import, _node);
439   if (import) {
440     return import;
441   }
442
443   import = oonf_class_malloc(&_import_class);
444   if (import == NULL) {
445     return NULL;
446   }
447
448   /* copy key and add to tree */
449   strscpy(import->name, name, sizeof(import->name));
450   import->_node.key = import->name;
451   avl_insert(&_import_tree, &import->_node);
452
453   avl_init(&import->imported_lan_tree, os_routing_avl_cmp_route_key, false);
454
455   return import;
456 }
457
458 /**
459  * Free all resources associated with a route modifier
460  * @param import import entry
461  */
462 static void
463 _destroy_import(struct _import_entry *import) {
464   avl_remove(&_import_tree, &import->_node);
465   netaddr_acl_remove(&import->filter);
466   oonf_class_free(&_import_class, import);
467 }
468
469 static struct _imported_lan *
470 _add_lan(struct _import_entry *import, struct os_route_key *key, uint32_t metric, uint8_t distance) {
471   struct nhdp_domain *domain;
472   struct _imported_lan *lan;
473
474   lan = avl_find_element(&import->imported_lan_tree, key, lan, _node);
475   if (lan) {
476     return lan;
477   }
478
479   lan = oonf_class_malloc(&_lan_import_class);
480   if (!lan) {
481     return NULL;
482   }
483
484   memcpy(&lan->key, key, sizeof(*key));
485   lan->_node.key = &lan->key;
486   avl_insert(&import->imported_lan_tree, &lan->_node);
487
488   lan->import = import;
489   lan->_aging_timer.class = &_aging_timer_class;
490
491   list_for_each_element(nhdp_domain_get_list(), domain, _node) {
492     if (import->domain == -1 || import->domain == domain->ext) {
493       olsrv2_lan_add(domain, key, metric, distance);
494     }
495   }
496
497   return lan;
498 }
499
500 static void
501 _destroy_lan(struct _imported_lan *lan) {
502   struct nhdp_domain *domain;
503
504   list_for_each_element(nhdp_domain_get_list(), domain, _node) {
505     if (lan->import->domain == -1 || lan->import->domain == domain->ext) {
506       olsrv2_lan_remove(domain, &lan->key);
507     }
508   }
509
510   avl_remove(&lan->import->imported_lan_tree, &lan->_node);
511   oonf_class_free(&_lan_import_class, lan);
512 }
513
514 static void
515 _cb_metric_aging(struct oonf_timer_instance *entry) {
516   struct olsrv2_lan_entry *lan_entry;
517   struct nhdp_domain *domain;
518   struct olsrv2_lan_domaindata *landata;
519   struct _imported_lan *lan;
520
521   lan = container_of(entry, struct _imported_lan, _aging_timer);
522   lan_entry = olsrv2_lan_get(&lan->key);
523   if (lan_entry) {
524     list_for_each_element(nhdp_domain_get_list(), domain, _node) {
525       if (lan->import->domain == -1 || lan->import->domain == domain->ext) {
526         landata = olsrv2_lan_get_domaindata(domain, lan_entry);
527         if (landata->outgoing_metric >= RFC7181_METRIC_MAX / 2) {
528           landata->outgoing_metric = RFC7181_METRIC_MAX;
529           oonf_timer_stop(entry);
530         }
531         else {
532           landata->outgoing_metric *= 2;
533         }
534       }
535     }
536   }
537 }
538
539 /**
540  * Configuration changed
541  */
542 static void
543 _cb_cfg_changed(void) {
544   struct _import_entry *import;
545
546   /* get existing modifier */
547   import = _get_import(_import_section.section_name);
548   if (!import) {
549     /* out of memory */
550     return;
551   }
552
553   if (_import_section.post == NULL) {
554     /* section was removed */
555     _destroy_import(import);
556     return;
557   }
558
559   if (cfg_schema_tobin(import, _import_section.post, _import_entries, ARRAYSIZE(_import_entries))) {
560     OONF_WARN(LOG_LAN_IMPORT, "Could not convert configuration data of section '%s'", _import_section.section_name);
561
562     if (_import_section.pre == NULL) {
563       _destroy_import(import);
564     }
565     return;
566   }
567
568   cfg_get_phy_if(import->ifname, import->ifname);
569
570   /* trigger wildcard query */
571   if (!os_routing_is_in_progress(&_unicast_query)) {
572     os_routing_query(&_unicast_query);
573   }
574 }