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