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