4821a32c737397d15adcb655dd29cdc50092ae77
[oonf.git] / src / nhdp / neighbor_probing / neighbor_probing.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 <oonf/libcommon/avl.h>
47 #include <oonf/oonf.h>
48 #include <oonf/libcommon/list.h>
49 #include <oonf/libconfig/cfg_schema.h>
50 #include <oonf/libcore/oonf_logging.h>
51 #include <oonf/libcore/oonf_subsystem.h>
52 #include <oonf/subsystems/oonf_class.h>
53 #include <oonf/subsystems/oonf_clock.h>
54 #include <oonf/subsystems/oonf_layer2.h>
55 #include <oonf/subsystems/oonf_rfc5444.h>
56 #include <oonf/subsystems/oonf_timer.h>
57 #include <oonf/subsystems/os_interface.h>
58
59 #include <oonf/nhdp/nhdp/nhdp_db.h>
60 #include <oonf/nhdp/nhdp/nhdp_interfaces.h>
61
62 #include <oonf/nhdp/neighbor_probing/neighbor_probing.h>
63
64 /* definitions and constants */
65 #define LOG_PROBING _olsrv2_neighbor_probing_subsystem.logging
66
67 /**
68  * Configuration of neighbor probing plugin
69  */
70 struct _config {
71   /*! Interval between two link probes */
72   uint64_t interval;
73
74   /*! size of probe */
75   int32_t probe_size;
76
77   /*! true to probe all DLEP interfaces */
78   bool probe_dlep;
79 };
80
81 /**
82  * NHDP link extension for neighbor probing plugin
83  */
84 struct _probing_link_data {
85   /*! absolute timestamp of last check if probing is necessary */
86   uint64_t last_probe_check;
87
88   /**
89    * number of bytes that had been sent to neighbor during last
90    * probe check.
91    */
92   uint64_t last_tx_traffic;
93
94   /**
95    * pointer to RFC5444 target allocated for link neighbor
96    */
97   struct oonf_rfc5444_target *target;
98 };
99
100 /* prototypes */
101 static int _init(void);
102 static void _cleanup(void);
103 static void _cb_link_removed(void *);
104 static void _cb_probe_link(struct oonf_timer_instance *);
105 static int _cb_addMessageHeader(struct rfc5444_writer *writer, struct rfc5444_writer_message *msg);
106 static void _cb_addMessageTLVs(struct rfc5444_writer *);
107 static void _cb_cfg_changed(void);
108
109 /* plugin declaration */
110 static struct cfg_schema_entry _probing_entries[] = {
111   CFG_MAP_CLOCK_MIN(_config, interval, "interval", "0.2", "Time interval between link probing", 100),
112   CFG_MAP_INT32_MINMAX(_config, probe_size, "size", "512", "Number of bytes used for neighbor probe", 0, 1, 1500),
113   CFG_MAP_BOOL(_config, probe_dlep, "probe_dlep", "true",
114     "Probe DLEP interfaces in addition to wireless interfaces"
115     " if they don't support the 'need probing' flag"),
116 };
117
118 static struct cfg_schema_section _probing_section = {
119   .type = OONF_NEIGHBOR_PROBING_SUBSYSTEM,
120   .cb_delta_handler = _cb_cfg_changed,
121   .entries = _probing_entries,
122   .entry_count = ARRAYSIZE(_probing_entries),
123 };
124
125 static const char *_dependencies[] = {
126   OONF_CLASS_SUBSYSTEM,
127   OONF_CLOCK_SUBSYSTEM,
128   OONF_LAYER2_SUBSYSTEM,
129   OONF_RFC5444_SUBSYSTEM,
130   OONF_TIMER_SUBSYSTEM,
131   OONF_OS_INTERFACE_SUBSYSTEM,
132   OONF_NHDP_SUBSYSTEM,
133 };
134 static struct oonf_subsystem _olsrv2_neighbor_probing_subsystem = {
135   .name = OONF_NEIGHBOR_PROBING_SUBSYSTEM,
136   .dependencies = _dependencies,
137   .dependencies_count = ARRAYSIZE(_dependencies),
138   .descr = "OLSRv2 Neighbor Probing plugin",
139   .author = "Henning Rogge",
140
141   .cfg_section = &_probing_section,
142
143   .init = _init,
144   .cleanup = _cleanup,
145 };
146 DECLARE_OONF_PLUGIN(_olsrv2_neighbor_probing_subsystem);
147
148 static struct _config _probe_config;
149
150 /* storage extension and listeners */
151 static struct oonf_class_extension _link_extenstion = {
152   .ext_name = "probing linkmetric",
153   .class_name = NHDP_CLASS_LINK,
154   .size = sizeof(struct _probing_link_data),
155   .cb_remove = _cb_link_removed,
156 };
157
158 /* timer class to measure interval between probes */
159 static struct oonf_timer_class _probe_info = {
160   .name = "Link probing timer",
161   .callback = _cb_probe_link,
162   .periodic = true,
163 };
164
165 static struct oonf_timer_instance _probe_timer = {
166   .class = &_probe_info,
167 };
168
169 /* rfc5444 message handing for probing */
170 static struct oonf_rfc5444_protocol *_protocol;
171 static struct rfc5444_writer_message *_probing_message;
172
173 static struct rfc5444_writer_content_provider _probing_msg_provider = {
174   .msg_type = RFC5444_MSGTYPE_PROBING,
175   .addMessageTLVs = _cb_addMessageTLVs,
176 };
177
178 /**
179  * Initialize plugin
180  * @return -1 if an error happened, 0 otherwise
181  */
182 static int
183 _init(void) {
184   _protocol = oonf_rfc5444_get_default_protocol();
185
186   if (oonf_class_extension_add(&_link_extenstion)) {
187     return -1;
188   }
189
190   _probing_message = rfc5444_writer_register_message(&_protocol->writer, RFC5444_MSGTYPE_PROBING, true);
191   if (_probing_message == NULL) {
192     oonf_rfc5444_remove_protocol(_protocol);
193     oonf_class_extension_remove(&_link_extenstion);
194     OONF_WARN(LOG_PROBING, "Could not register Probing message");
195     return -1;
196   }
197
198   _probing_message->addMessageHeader = _cb_addMessageHeader;
199
200   if (rfc5444_writer_register_msgcontentprovider(&_protocol->writer, &_probing_msg_provider, NULL, 0)) {
201     OONF_WARN(LOG_PROBING, "Count not register Probing msg contentprovider");
202     rfc5444_writer_unregister_message(&_protocol->writer, _probing_message);
203     oonf_rfc5444_remove_protocol(_protocol);
204     oonf_class_extension_remove(&_link_extenstion);
205     return -1;
206   }
207
208   oonf_timer_add(&_probe_info);
209   return 0;
210 }
211
212 /**
213  * Cleanup plugin
214  */
215 static void
216 _cleanup(void) {
217   rfc5444_writer_unregister_content_provider(&_protocol->writer, &_probing_msg_provider, NULL, 0);
218   rfc5444_writer_unregister_message(&_protocol->writer, _probing_message);
219   _protocol = NULL;
220   oonf_timer_remove(&_probe_info);
221   oonf_class_extension_remove(&_link_extenstion);
222 }
223
224 /**
225  * Callback when link is removed to cleanup plugin data
226  * @param ptr NHDP link instance to be removed
227  */
228 static void
229 _cb_link_removed(void *ptr) {
230   struct _probing_link_data *ldata;
231
232   ldata = oonf_class_get_extension(&_link_extenstion, ptr);
233   if (ldata->target) {
234     oonf_rfc5444_remove_target(ldata->target);
235   }
236 }
237
238 /**
239  * Check if a certain interface should be probed
240  * @param net layer2 network instance
241  * @return true if interface should be probed, false otherwise
242  */
243 static bool
244 _check_if_type(struct oonf_layer2_net *net) {
245   struct oonf_layer2_data *l2data;
246   bool value;
247
248   l2data = &net->data[OONF_LAYER2_NET_MCS_BY_PROBING];
249   if (!oonf_layer2_data_read_boolean(&value, l2data)) {
250     /* we got a direct setting reported for the interface for probing */
251     return value;
252   }
253   if (net->if_dlep) {
254     /* use configuration for DLEP that does not report if probing is necessary */
255     return _probe_config.probe_dlep;
256   }
257
258   return net->if_type == OONF_LAYER2_TYPE_WIRELESS;
259 }
260
261 /**
262  * Callback for triggering a new neighbor probe
263  * @param ptr timer instance that fired
264  */
265 static void
266 _cb_probe_link(struct oonf_timer_instance *ptr __attribute__((unused))) {
267   struct nhdp_link *lnk, *best_lnk;
268   struct _probing_link_data *ldata, *best_ldata;
269   struct nhdp_interface *nhdp_if;
270
271   struct os_interface_listener *if_listener;
272   struct oonf_layer2_net *l2net;
273   struct oonf_layer2_neigh *l2neigh;
274
275   uint64_t points, best_points;
276   uint64_t last_tx_packets;
277
278 #ifdef OONF_LOG_DEBUG_INFO
279   struct netaddr_str nbuf;
280 #endif
281
282   best_ldata = NULL;
283   best_points = 0;
284
285   OONF_DEBUG(LOG_PROBING, "Start looking for probe candidate");
286
287   l2neigh = NULL;
288
289   avl_for_each_element(nhdp_interface_get_tree(), nhdp_if, _node) {
290     if_listener = nhdp_interface_get_if_listener(nhdp_if);
291
292     l2net = oonf_layer2_net_get(if_listener->data->name);
293     if (!l2net) {
294       continue;
295     }
296
297     if (!_check_if_type(l2net)) {
298       OONF_DEBUG(LOG_PROBING, "Drop interface %s (not wireless)", if_listener->data->name);
299       continue;
300     }
301
302     OONF_DEBUG(LOG_PROBING, "Start looking for probe candidate in interface '%s'", if_listener->data->name);
303
304     list_for_each_element(&nhdp_if->_links, lnk, _if_node) {
305       if (lnk->status != NHDP_LINK_SYMMETRIC) {
306         /* only probe symmetric neighbors */
307         continue;
308       }
309
310       /* get layer2 data */
311       l2neigh = oonf_layer2_neigh_get(l2net, &lnk->remote_mac);
312       if (l2neigh == NULL || !oonf_layer2_data_has_value(&l2neigh->data[OONF_LAYER2_NEIGH_RX_BITRATE]) ||
313           !oonf_layer2_data_has_value(&l2neigh->data[OONF_LAYER2_NEIGH_TX_FRAMES])) {
314         OONF_DEBUG(LOG_PROBING, "Drop link %s (missing l2 data)", netaddr_to_string(&nbuf, &lnk->remote_mac));
315         continue;
316       }
317
318       /* get link extension for probing */
319       ldata = oonf_class_get_extension(&_link_extenstion, lnk);
320
321       /* fix tx-packets */
322       last_tx_packets = ldata->last_tx_traffic;
323       ldata->last_tx_traffic = oonf_layer2_data_get_int64(&l2neigh->data[OONF_LAYER2_NEIGH_TX_FRAMES], 0);
324
325       /* check if link had traffic since last probe check */
326       if (last_tx_packets != ldata->last_tx_traffic) {
327         /* advance timestamp */
328         ldata->last_probe_check = oonf_clock_getNow();
329         OONF_DEBUG(LOG_PROBING, "Drop link %s (already has unicast traffic)", netaddr_to_string(&nbuf, &l2neigh->key.addr));
330         continue;
331       }
332
333       points = oonf_clock_getNow() - ldata->last_probe_check;
334
335       OONF_DEBUG(LOG_PROBING, "Link %s has %" PRIu64 " points", netaddr_to_string(&nbuf, &lnk->if_addr), points);
336
337       if (points > best_points) {
338         best_points = points;
339         best_lnk = lnk;
340         best_ldata = ldata;
341       }
342     }
343   }
344
345   if (best_ldata != NULL) {
346     best_ldata->last_probe_check = oonf_clock_getNow();
347
348     if (best_ldata->target == NULL && netaddr_get_address_family(&best_lnk->if_addr) != AF_UNSPEC) {
349       best_ldata->target = oonf_rfc5444_add_target(best_lnk->local_if->rfc5444_if.interface, &best_lnk->if_addr);
350     }
351
352     if (best_ldata->target) {
353       OONF_DEBUG(LOG_PROBING, "Send probing to %s", netaddr_to_string(&nbuf, &best_ldata->target->dst));
354
355       oonf_rfc5444_send_if(best_ldata->target, RFC5444_MSGTYPE_PROBING);
356     }
357   }
358 }
359
360 static int
361 _cb_addMessageHeader(struct rfc5444_writer *writer, struct rfc5444_writer_message *msg) {
362   rfc5444_writer_set_msg_header(writer, msg, false, false, false, false);
363   return RFC5444_OKAY;
364 }
365
366 static void
367 _cb_addMessageTLVs(struct rfc5444_writer *writer) {
368   uint8_t data[1500];
369
370   memset(data, 0, _probe_config.probe_size);
371   rfc5444_writer_add_messagetlv(writer, RFC5444_MSGTLV_PROBING, 0, data, _probe_config.probe_size);
372 }
373
374 /**
375  * Callback triggered when configuration changes
376  */
377 static void
378 _cb_cfg_changed(void) {
379   if (cfg_schema_tobin(&_probe_config, _probing_section.post, _probing_entries, ARRAYSIZE(_probing_entries))) {
380     OONF_WARN(LOG_PROBING, "Cannot convert configuration for " OONF_NEIGHBOR_PROBING_SUBSYSTEM);
381     return;
382   }
383
384   oonf_timer_set(&_probe_timer, _probe_config.interval);
385 }