Make connect_to parameter for DLEP router useful again
[oonf.git] / src-plugins / generic / dlep / router / dlep_router_interface.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, 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 /**
43  * @file
44  */
45
46 #include <errno.h>
47 #include <unistd.h>
48
49 #include "common/avl.h"
50 #include "common/avl_comp.h"
51 #include "common/common_types.h"
52 #include "common/netaddr.h"
53
54 #include "subsystems/oonf_class.h"
55 #include "subsystems/oonf_layer2.h"
56 #include "subsystems/oonf_packet_socket.h"
57 #include "subsystems/oonf_timer.h"
58 #include "subsystems/os_interface.h"
59
60 #include "dlep/dlep_extension.h"
61 #include "dlep/dlep_iana.h"
62 #include "dlep/dlep_interface.h"
63 #include "dlep/dlep_session.h"
64 #include "dlep/dlep_writer.h"
65
66 #include "dlep/router/dlep_router.h"
67 #include "dlep/router/dlep_router_interface.h"
68
69 #include "dlep/ext_base_ip/ip.h"
70 #include "dlep/ext_base_metric/metric.h"
71 #include "dlep/ext_base_proto/proto_router.h"
72 #include "dlep/ext_l1_statistics/l1_statistics.h"
73 #include "dlep/ext_l2_statistics/l2_statistics.h"
74 #include "dlep/ext_radio_attributes/radio_attributes.h"
75 #include "dlep/router/dlep_router_internal.h"
76 #include "dlep/router/dlep_router_session.h"
77
78 static void _connect_to_setup(struct dlep_router_if *router_if);
79 static void _check_connect_to(struct dlep_router_if *router_if);
80 static void _cleanup_interface(struct dlep_router_if *interface);
81 static int _connect_to_if_changed(struct os_interface_listener *);
82 static void _cb_check_connect_to_status(struct oonf_timer_instance *);
83
84 static struct oonf_class _router_if_class = {
85   .name = "DLEP router interface",
86   .size = sizeof(struct dlep_router_if),
87 };
88
89 static bool _shutting_down;
90
91 static struct oonf_layer2_origin _l2_origin = {
92   .name = "dlep router",
93   .proactive = true,
94   .priority = OONF_LAYER2_ORIGIN_RELIABLE,
95 };
96
97 static struct oonf_layer2_origin _l2_default_origin = {
98   .name = "dlep router defaults",
99   .proactive = false,
100   .priority = OONF_LAYER2_ORIGIN_UNRELIABLE,
101 };
102
103 static struct oonf_timer_class _connect_to_watchdog_class = {
104   .name = "connect_to watchdog",
105   .callback = _cb_check_connect_to_status,
106 };
107
108 /**
109  * Initialize dlep router interface framework. This will also
110  * initialize the dlep router session framework.
111  */
112 void
113 dlep_router_interface_init(void) {
114   oonf_class_add(&_router_if_class);
115
116   dlep_extension_init();
117   dlep_session_init();
118   dlep_router_session_init();
119   dlep_base_proto_router_init();
120   dlep_base_metric_init();
121   dlep_base_ip_init();
122   dlep_l1_statistics_init();
123   dlep_l2_statistics_init();
124   dlep_radio_attributes_init();
125
126   _shutting_down = false;
127
128   oonf_layer2_origin_add(&_l2_origin);
129   oonf_timer_add(&_connect_to_watchdog_class);
130 }
131
132 /**
133  * Cleanup dlep router interface framework. This will also cleanup
134  * all dlep router sessions.
135  */
136 void
137 dlep_router_interface_cleanup(void) {
138   struct dlep_router_if *interf, *it;
139
140   avl_for_each_element_safe(dlep_if_get_tree(false), interf, interf._node, it) {
141     dlep_router_remove_interface(interf);
142   }
143
144   oonf_class_remove(&_router_if_class);
145
146   dlep_base_ip_cleanup();
147   dlep_router_session_cleanup();
148   dlep_extension_cleanup();
149   oonf_layer2_origin_remove(&_l2_origin);
150   oonf_timer_remove(&_connect_to_watchdog_class);
151 }
152
153 /**
154  * Get a dlep router interface by layer2 interface name
155  * @param l2_ifname interface name
156  * @return dlep router interface, NULL if not found
157  */
158 struct dlep_router_if *
159 dlep_router_get_by_layer2_if(const char *l2_ifname) {
160   struct dlep_router_if *interf;
161
162   return avl_find_element(dlep_if_get_tree(false), l2_ifname, interf, interf._node);
163 }
164
165 /**
166  * Get a dlep router interface by dlep datapath name
167  * @param ifname interface name
168  * @return dlep router interface, NULL if not found
169  */
170 struct dlep_router_if *
171 dlep_router_get_by_datapath_if(const char *ifname) {
172   struct dlep_router_if *interf;
173
174   avl_for_each_element(dlep_if_get_tree(false), interf, interf._node) {
175     if (strcmp(interf->interf.udp_config.interface, ifname) == 0) {
176       return interf;
177     }
178   }
179   return NULL;
180 }
181
182 /**
183  * Add a new dlep interface or get existing one with same name.
184  * @param ifname interface name
185  * @return dlep router interface, NULL if allocation failed
186  */
187 struct dlep_router_if *
188 dlep_router_add_interface(const char *ifname) {
189   struct dlep_router_if *interface;
190
191   interface = dlep_router_get_by_layer2_if(ifname);
192   if (interface) {
193     OONF_DEBUG(LOG_DLEP_ROUTER, "use existing instance for %s", ifname);
194     return interface;
195   }
196
197   interface = oonf_class_malloc(&_router_if_class);
198   if (!interface) {
199     return NULL;
200   }
201
202   if (dlep_if_add(&interface->interf, ifname, &_l2_origin, &_l2_default_origin, _connect_to_if_changed, LOG_DLEP_ROUTER, false)) {
203     oonf_class_free(&_router_if_class, interface);
204     return NULL;
205   }
206
207   /* prepare timer */
208   interface->_connect_to_watchdog.class = &_connect_to_watchdog_class;
209
210   OONF_DEBUG(LOG_DLEP_ROUTER, "Add session %s", ifname);
211   return interface;
212 }
213
214 /**
215  * Remove dlep router interface
216  * @param interface dlep router interface
217  */
218 void
219 dlep_router_remove_interface(struct dlep_router_if *interface) {
220   /* close all sessions */
221   _cleanup_interface(interface);
222
223   /* cleanup generic interface */
224   dlep_if_remove(&interface->interf);
225
226   /* remove session */
227   free(interface->interf.session.cfg.peer_type);
228   oonf_class_free(&_router_if_class, interface);
229 }
230
231 /**
232  * Apply new settings to dlep router interface. This will close all
233  * existing dlep sessions.
234  * @param interf dlep router interface
235  */
236 void
237 dlep_router_apply_interface_settings(struct dlep_router_if *interf) {
238   struct dlep_extension *ext;
239
240   oonf_packet_apply_managed(&interf->interf.udp, &interf->interf.udp_config);
241
242   _cleanup_interface(interf);
243
244   if (!netaddr_is_unspec(&interf->connect_to_addr)) {
245     _connect_to_setup(interf);
246   }
247   else {
248     oonf_timer_stop(&interf->_connect_to_watchdog);
249   }
250
251   avl_for_each_element(dlep_extension_get_tree(), ext, _node) {
252     if (ext->cb_session_apply_router) {
253       ext->cb_session_apply_router(&interf->interf.session);
254     }
255   }
256 }
257
258 /**
259  * Send all active sessions a Peer Terminate signal
260  */
261 void
262 dlep_router_terminate_all_sessions(void) {
263   struct dlep_router_if *interf;
264   struct dlep_router_session *router_session;
265
266   _shutting_down = true;
267
268   avl_for_each_element(dlep_if_get_tree(false), interf, interf._node) {
269     avl_for_each_element(&interf->interf.session_tree, router_session, _node) {
270       dlep_session_terminate(&router_session->session);
271     }
272   }
273 }
274
275 /**
276 * open a direct TCP connection for this interface
277 * @param router_if router interface
278 */
279 static void
280 _connect_to_setup(struct dlep_router_if *router_if) {
281   struct os_interface *os_if;
282   const struct os_interface_ip *result;
283   union netaddr_socket local;
284 #ifdef OONF_LOG_DEBUG_INFO
285   struct netaddr_str nbuf;
286 #endif
287
288   os_if = router_if->interf.session.l2_listener.data;
289
290   OONF_DEBUG(LOG_DLEP_ROUTER, "Connect directly to [%s]:%d", netaddr_to_string(&nbuf, &router_if->connect_to_addr),
291       router_if->connect_to_port);
292
293   /* start watchdog */
294   oonf_timer_set(&router_if->_connect_to_watchdog, 1000);
295
296   result = os_interface_get_prefix_from_dst(&router_if->connect_to_addr, os_if);
297   if (result) {
298     /* initialize local and remote socket */
299     netaddr_socket_init(&local, &result->address, 0, os_if->index);
300     netaddr_socket_init(&router_if->connect_to, &router_if->connect_to_addr, router_if->connect_to_port, os_if->index);
301
302     dlep_router_add_session(router_if, &local, &router_if->connect_to);
303   }
304 }
305
306 /**
307  * Close all existing dlep sessions of a dlep interface
308  * @param interface dlep router interface
309  */
310 static void
311 _cleanup_interface(struct dlep_router_if *interface) {
312   struct dlep_router_session *stream, *it;
313
314   /* close TCP connection and socket */
315   avl_for_each_element_safe(&interface->interf.session_tree, stream, _node, it) {
316     dlep_router_remove_session(stream);
317   }
318 }
319
320 /**
321  * check if connect_to session is up and running. If not, restart it.
322  * @param router_if router interface
323  */
324 static void
325 _check_connect_to(struct dlep_router_if *router_if) {
326   struct dlep_router_session *connect_to_session;
327
328   if (netaddr_is_unspec(&router_if->connect_to_addr)) {
329     /* do not connect */
330     return;
331   }
332
333   connect_to_session = dlep_router_get_session(router_if, &router_if->connect_to);
334   if (connect_to_session->session._peer_state == DLEP_PEER_NOT_CONNECTED
335     || connect_to_session->session._peer_state == DLEP_PEER_TERMINATED) {
336     /* cleanup not working session */
337     dlep_router_remove_session(connect_to_session);
338     connect_to_session = NULL;
339   }
340
341   if (!connect_to_session) {
342     _connect_to_setup(router_if);
343   }
344   return;
345 }
346
347 /**
348 * Interface listener to (re-)establish connect_to session if it failed.
349 * @param interf interface listener that triggered
350 * @return always 0
351 */
352 static int
353 _connect_to_if_changed(struct os_interface_listener *interf) {
354   struct dlep_router_if *router_if;
355
356   router_if = container_of(interf, struct dlep_router_if, interf.session.l2_listener);
357   _check_connect_to(router_if);
358   return 0;
359 }
360
361 /**
362  * Timer callback to watch connect_to session status
363  * @param instance watchdog timer instance
364  */
365 static void
366 _cb_check_connect_to_status(struct oonf_timer_instance *instance) {
367   struct dlep_router_if *router_if;
368
369   router_if = container_of(instance, struct dlep_router_if, _connect_to_watchdog);
370   _check_connect_to(router_if);
371 }