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