07eaf14ebd80f73393c3e4f4c418a8ebb99e76f0
[oonf.git] / src-api / core / olsr_interface.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2013, 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 #include <netinet/in.h>
43
44 #include "common/common_types.h"
45 #include "common/avl.h"
46 #include "common/avl_comp.h"
47 #include "common/list.h"
48 #include "common/netaddr.h"
49 #include "common/string.h"
50
51 #include "core/olsr_interface.h"
52 #include "core/olsr_logging.h"
53 #include "core/olsr_timer.h"
54 #include "core/olsr_subsystem.h"
55 #include "core/os_net.h"
56 #include "core/os_system.h"
57 #include "core/os_routing.h"
58
59 /* timeinterval to delay change in interface to trigger actions */
60 #define OLSR_INTERFACE_CHANGE_INTERVAL 100
61
62 /* prototypes */
63 static int _init(void);
64 static void _cleanup(void);
65
66 static struct olsr_interface *_interface_add(const char *, bool mesh);
67 static void _interface_remove(struct olsr_interface *interf, bool mesh);
68 static void _cb_change_handler(void *);
69 static void _trigger_change_timer(struct olsr_interface *);
70
71 /* global tree of known interfaces */
72 struct avl_tree olsr_interface_tree;
73
74 /* subsystem definition */
75 struct oonf_subsystem oonf_interface_subsystem = {
76   .init = _init,
77   .cleanup = _cleanup,
78 };
79
80 static struct list_entity _interface_listener;
81 static struct olsr_timer_info _change_timer_info = {
82   .name = "Interface change",
83   .callback = _cb_change_handler,
84 };
85
86 static struct os_system_if_listener _iflistener = {
87   .if_changed = olsr_interface_trigger_change,
88 };
89
90 /**
91  * Initialize interface subsystem
92  * @return always returns 0
93  */
94 static int
95 _init(void) {
96   olsr_timer_add(&_change_timer_info);
97
98   avl_init(&olsr_interface_tree, avl_comp_strcasecmp, false);
99   list_init_head(&_interface_listener);
100
101   os_system_iflistener_add(&_iflistener);
102   return 0;
103 }
104
105 /**
106  * Cleanup interface subsystem
107  */
108 static void
109 _cleanup(void) {
110   struct olsr_interface_listener *listener, *l_it;
111
112   list_for_each_element_safe(&_interface_listener, listener, _node, l_it) {
113     olsr_interface_remove_listener(listener);
114   }
115
116   os_system_iflistener_remove(&_iflistener);
117   olsr_timer_remove(&_change_timer_info);
118 }
119
120 /**
121  * Add a listener to a specific interface
122  * @param listener initialized listener object
123  * @return -1 if an error happened, 0 otherwise
124  */
125 int
126 olsr_interface_add_listener(
127     struct olsr_interface_listener *listener) {
128   if (list_is_node_added(&listener->_node)) {
129     return 0;
130   }
131
132   if (listener->name) {
133     listener->interface = _interface_add(listener->name, listener->mesh);
134     if (listener->interface == NULL) {
135       return -1;
136     }
137   }
138
139   list_add_tail(&_interface_listener, &listener->_node);
140   return 0;
141 }
142
143 /**
144  * Removes a listener to an interface object
145  * @param listener pointer to listener object
146  */
147 void
148 olsr_interface_remove_listener(
149     struct olsr_interface_listener *listener) {
150   if (!list_is_node_added(&listener->_node)) {
151     return;
152   }
153
154   if (listener->interface) {
155     _interface_remove(listener->interface, listener->mesh);
156   }
157
158   list_remove(&listener->_node);
159 }
160
161 /**
162  * Trigger a potential change in the interface settings. Normally
163  * called by os_system code
164  * @param name interface name
165  * @param down true if interface is going down
166  */
167 void
168 olsr_interface_trigger_change(const char *name, bool down) {
169   struct olsr_interface *interf;
170
171   OLSR_DEBUG(LOG_INTERFACE, "Change of interface %s was triggered", name);
172
173   interf = avl_find_element(&olsr_interface_tree, name, interf, _node);
174   if (interf == NULL) {
175     return;
176   }
177
178   if (down) {
179     interf->data.up = false;
180   }
181
182   olsr_interface_trigger_handler(interf);
183 }
184
185 /**
186  * Trigger the interface change handler after a short waiting period
187  * to accumulate multiple change events.
188  * @param interf pointer to olsr interface
189  */
190 void
191 olsr_interface_trigger_handler(struct olsr_interface *interf) {
192   /* trigger interface reload in 100 ms */
193   _trigger_change_timer(interf);
194 }
195
196 /**
197  * @param name interface name
198  * @return pointer to olsr interface data, NULL if not found
199  */
200 struct olsr_interface_data *
201 olsr_interface_get_data(const char *name) {
202   struct olsr_interface *interf;
203
204   interf = avl_find_element(&olsr_interface_tree, name, interf, _node);
205   if (interf == NULL) {
206     return NULL;
207   }
208
209   return &interf->data;
210 }
211
212 /**
213  * Find an IP address of an interface fitting to a specified prefix.
214  * Destination will only be overwritten if address was found.
215  * @param dst pointer to target address buffer
216  * @param prefix specified prefix of address
217  * @param ifdata interface data
218  * @return 0 if an address was found, -1 otherwise
219  */
220 int
221 olsr_interface_find_address(struct netaddr *dst,
222     struct netaddr *prefix, struct olsr_interface_data *ifdata) {
223   size_t i;
224
225   for (i=0; i<ifdata->addrcount; i++) {
226     if (netaddr_is_in_subnet(prefix, &ifdata->addresses[i])) {
227       memcpy(dst, &ifdata->addresses[i], sizeof(*dst));
228       return 0;
229     }
230   }
231   return -1;
232 }
233
234 /**
235  * Add an interface to the listener system
236  * @param if_index index of interface
237  * @param mesh true if interface is used for mesh traffic
238  * @return pointer to interface struct, NULL if an error happened
239  */
240 static struct olsr_interface *
241 _interface_add(const char *name, bool mesh) {
242   struct olsr_interface *interf;
243
244   interf = avl_find_element(&olsr_interface_tree, name, interf, _node);
245   if (!interf) {
246     /* allocate new interface */
247     interf = calloc(1, sizeof(*interf));
248     if (interf == NULL) {
249       OLSR_WARN(LOG_INTERFACE, "Not enough memory for interface structure");
250       return NULL;
251     }
252
253     /* hookup */
254     strscpy(interf->data.name, name, sizeof(interf->data.name));
255     interf->_node.key = interf->data.name;
256     avl_insert(&olsr_interface_tree, &interf->_node);
257
258     interf->data.index = if_nametoindex(name);
259
260     interf->_change_timer.info = &_change_timer_info;
261     interf->_change_timer.cb_context = interf;
262
263     /* initialize data of interface */
264     os_net_update_interface(&interf->data, name);
265   }
266
267   /* update reference counters */
268   interf->usage_counter++;
269   if(mesh) {
270     if (interf->mesh_counter == 0) {
271       os_routing_init_mesh_if(interf);
272     }
273     interf->mesh_counter++;
274   }
275
276   /* trigger update */
277   if (interf->usage_counter == 1) {
278     /* new interface */
279     _cb_change_handler(interf);
280   }
281   else {
282     /* existing one, delay update */
283     _trigger_change_timer(interf);
284   }
285
286   return interf;
287 }
288
289 /**
290  * Remove an interface from the listener system. If multiple listeners
291  * share an interface, this will only decrease the reference counter.
292  * @param interf pointer to olsr_interface
293  */
294 static void
295 _interface_remove(struct olsr_interface *interf, bool mesh) {
296   /* handle mesh interface flag */
297   if (mesh) {
298     interf->mesh_counter--;
299
300     if (interf->mesh_counter < 1) {
301       /* no mesh interface anymore, remove routing settings */
302       os_routing_cleanup_mesh_if(interf);
303     }
304   }
305
306   interf->usage_counter--;
307   if (interf->usage_counter > 0) {
308     return;
309   }
310
311   if (interf->data.addresses) {
312     free(interf->data.addresses);
313   }
314   avl_remove(&olsr_interface_tree, &interf->_node);
315
316   olsr_timer_stop(&interf->_change_timer);
317   free(interf);
318 }
319
320 /**
321  * Timer callback to handle potential change of data of an interface
322  * @param ptr pointer to interface object
323  */
324 static void
325 _cb_change_handler(void *ptr) {
326   struct olsr_interface_data old_data, new_data;
327   struct olsr_interface_listener *listener, *l_it;
328   struct olsr_interface *interf;
329
330   interf = ptr;
331
332   OLSR_DEBUG(LOG_INTERFACE, "Change of interface %s in progress", interf->data.name);
333
334   /* read interface data */
335   memset(&new_data, 0, sizeof(new_data));
336   if (os_net_update_interface(&new_data, interf->data.name)) {
337     /* an error happened, try again */
338     _trigger_change_timer(interf);
339     return;
340   }
341
342   /* something changed ?
343   if (memcmp(&interf->data, &new_data, sizeof(new_data)) == 0) {
344     return;
345   }
346 */
347   /* copy data to interface object, but remember the old data */
348   memcpy(&old_data, &interf->data, sizeof(old_data));
349   memcpy(&interf->data, &new_data, sizeof(interf->data));
350
351   /* call listeners */
352   list_for_each_element_safe(&_interface_listener, listener, _node, l_it) {
353     if (listener->process != NULL
354         && (listener->name == NULL
355             || strcasecmp(listener->name, interf->data.name) == 0)) {
356       listener->interface = interf;
357       listener->old = &old_data;
358       listener->process(listener);
359       listener->interface = NULL;
360       listener->old = NULL;
361     }
362   }
363
364   if (old_data.addresses) {
365     free (old_data.addresses);
366   }
367 }
368
369 /**
370  * Activate the change timer of an interface object
371  * @param interf pointer to interface object
372  */
373 static void
374 _trigger_change_timer(struct olsr_interface *interf) {
375   olsr_timer_set(&interf->_change_timer, OLSR_INTERFACE_CHANGE_INTERVAL);
376 }