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