2c109bde51871e893a3841e6768a1d9bb3f46b8c
[oonf.git] / src-api / core / olsr_interface.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2012, 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 "common/common_types.h"
43 #include "common/avl.h"
44 #include "common/avl_comp.h"
45 #include "common/list.h"
46 #include "common/netaddr.h"
47
48 #include "olsr_interface.h"
49 #include "olsr_logging.h"
50 #include "olsr_timer.h"
51 #include "olsr.h"
52 #include "os_net.h"
53 #include "os_system.h"
54 #include "os_routing.h"
55
56 /* timeinterval to delay change in interface to trigger actions */
57 #define OLSR_INTERFACE_CHANGE_INTERVAL 100
58
59 static struct olsr_interface *_interface_add(const char *name, bool mesh);
60 static void _interface_remove(const char *name, bool mesh);
61 static void _cb_change_handler(void *);
62 static void _trigger_change_timer(struct olsr_interface *);
63
64 /* global tree of known interfaces */
65 struct avl_tree olsr_interface_tree;
66
67 /* remember state of subsystem */
68 OLSR_SUBSYSTEM_STATE(_interface_state);
69
70 static struct list_entity _interface_listener;
71 static struct olsr_timer_info _change_timer_info = {
72   .name = "Interface change",
73   .callback = _cb_change_handler,
74 };
75
76 /**
77  * Initialize interface subsystem
78  * @return -1 if an error happened, 0 otherwise
79  */
80 int
81 olsr_interface_init(void) {
82   if (olsr_subsystem_is_initialized(&_interface_state))
83     return 0;
84
85   olsr_timer_add(&_change_timer_info);
86
87   avl_init(&olsr_interface_tree, avl_comp_strcasecmp, false, NULL);
88   list_init_head(&_interface_listener);
89
90   olsr_subsystem_init(&_interface_state);
91   return 0;
92 }
93
94 /**
95  * Cleanup interface subsystem
96  */
97 void
98 olsr_interface_cleanup(void) {
99   struct olsr_interface_listener *listener, *l_it;
100
101   if (olsr_subsystem_cleanup(&_interface_state))
102     return;
103
104   list_for_each_element_safe(&_interface_listener, listener, node, l_it) {
105     olsr_interface_remove_listener(listener);
106   }
107
108   olsr_timer_remove(&_change_timer_info);
109 }
110
111 /**
112  * Add a listener to a specific interface
113  * @param listener initialized listener object
114  * @return pointer to olsr_interface struct, NULL if an error happened
115  */
116 struct olsr_interface *
117 olsr_interface_add_listener(
118     struct olsr_interface_listener *listener) {
119   struct olsr_interface *interf;
120
121   interf = _interface_add(listener->name, listener->mesh);
122   if (interf != NULL) {
123     list_add_tail(&_interface_listener, &listener->node);
124   }
125
126   return interf;
127 }
128
129 /**
130  * Removes a listener to an interface object
131  * @param listener pointer to listener object
132  */
133 void
134 olsr_interface_remove_listener(
135     struct olsr_interface_listener *listener) {
136   if (list_is_node_added(&listener->node)) {
137     list_remove(&listener->node);
138     _interface_remove(listener->name, listener->mesh);
139   }
140 }
141
142
143 /**
144  * Trigger a potential change in the interface settings. Normally
145  * called by os_system code
146  * @param name pointer to name of interface
147  */
148 void
149 olsr_interface_trigger_change(const char *name) {
150   struct olsr_interface *interf;
151
152   interf = avl_find_element(&olsr_interface_tree, name, interf, node);
153   if (interf == NULL) {
154     return;
155   }
156
157   /* trigger interface reload in 100 ms */
158   _trigger_change_timer(interf);
159 }
160
161 /**
162  * Add an interface to the listener system
163  * @param name pointer to interface name
164  * @param mesh true if interface is used for mesh traffic
165  * @return pointer to interface struct, NULL if an error happened
166  */
167 static struct olsr_interface *
168 _interface_add(const char *name, bool mesh) {
169   struct olsr_interface *interf;
170
171   interf = avl_find_element(&olsr_interface_tree, name, interf, node);
172   if (!interf) {
173     /* allocate new interface */
174     interf = calloc(1, sizeof(*interf));
175     if (interf == NULL) {
176       OLSR_WARN_OOM(LOG_INTERFACE);
177       return NULL;
178     }
179
180     /* hookup */
181     interf->name = strdup(name);
182     if (interf->name == NULL) {
183       OLSR_WARN_OOM(LOG_INTERFACE);
184       free(interf);
185       return NULL;
186     }
187
188     interf->node.key = interf->name;
189     avl_insert(&olsr_interface_tree, &interf->node);
190
191     interf->change_timer.info = &_change_timer_info;
192     interf->change_timer.cb_context = interf;
193
194     /* grab data of interface */
195     os_net_update_interface(&interf->data, interf->name);
196   }
197
198   /* update reference counters */
199   interf->usage_counter++;
200   if(mesh) {
201     if (interf->mesh_counter == 0) {
202       os_routing_init_mesh_if(interf);
203     }
204     interf->mesh_counter++;
205   }
206
207   /* trigger update */
208   _trigger_change_timer(interf);
209
210   return interf;
211 }
212
213 /**
214  * Remove an interface from the listener system. If multiple listeners
215  * share an interface, this will only decrease the reference counter.
216  * @param name pointer to interface name
217  * @param mesh true if interface is used for mesh traffic
218  */
219 static void
220 _interface_remove(const char *name, bool mesh) {
221   struct olsr_interface *interf;
222
223   interf = avl_find_element(&olsr_interface_tree, name, interf, node);
224   if (!interf) {
225     return;
226   }
227
228   interf->usage_counter--;
229   if (mesh) {
230     interf->mesh_counter--;
231
232     if (interf->mesh_counter < 1) {
233       os_routing_cleanup_mesh_if(interf);
234     }
235   }
236
237   if (interf->usage_counter > 0) {
238     return;
239   }
240
241   avl_remove(&olsr_interface_tree, &interf->node);
242
243   free((void *)interf->name);
244   free(interf);
245 }
246
247
248 /**
249  * Timer callback to handle potential change of data of an interface
250  * @param ptr pointer to interface object
251  */
252 static void
253 _cb_change_handler(void *ptr) {
254   struct olsr_interface_data old_data, new_data;
255   struct olsr_interface_listener *listener, *l_it;
256   struct olsr_interface *interf;
257
258   interf = ptr;
259
260   /* read interface data */
261   if (os_net_update_interface(&interf->data, interf->name)) {
262     /* an error happened, try again */
263     _trigger_change_timer(interf);
264     return;
265   }
266
267   /* something changed ? */
268   if (memcmp(&interf->data, &new_data, sizeof(new_data)) == 0) {
269     return;
270   }
271
272   /* copy data to interface object, but remember the old data */
273   memcpy(&old_data, &interf->data, sizeof(old_data));
274   memcpy(&interf->data, &new_data, sizeof(interf->data));
275
276   /* call listeners */
277   list_for_each_element_safe(&_interface_listener, listener, node, l_it) {
278     if (listener->name == NULL || strcmp(listener->name, interf->name) == 0) {
279       listener->process(interf, &old_data);
280     }
281   }
282 }
283
284 /**
285  * Activate the change timer of an interface object
286  * @param interf pointer to interface object
287  */
288 static void
289 _trigger_change_timer(struct olsr_interface *interf) {
290   olsr_timer_set(&interf->change_timer, OLSR_INTERFACE_CHANGE_INTERVAL);
291 }