Reworking layer2 subsystem
[oonf.git] / src-plugins / layer2_viewer / layer2_viewer.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2013, 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 <stdio.h>
43
44 #include "common/common_types.h"
45 #include "common/autobuf.h"
46 #include "common/netaddr.h"
47 #include "common/netaddr_acl.h"
48 #include "common/string.h"
49 #include "common/template.h"
50
51 #include "config/cfg_schema.h"
52 #include "core/oonf_logging.h"
53 #include "core/oonf_plugins.h"
54 #include "subsystems/oonf_clock.h"
55 #include "subsystems/oonf_interface.h"
56 #include "subsystems/oonf_layer2.h"
57 #include "subsystems/oonf_telnet.h"
58
59 #include "layer2_viewer/layer2_viewer.h"
60
61 /* keys for template engine */
62 #define KEY_neighbor "neighbor"
63 #define KEY_radio "radio"
64 #define KEY_ifindex "ifindex"
65 #define KEY_ifid    "ifid"
66 #define KEY_interface "interface"
67 #define KEY_lastseen "lastseen"
68
69 /* definitions */
70 struct _l2viewer_config {
71   struct netaddr_acl acl;
72 };
73
74 /* prototypes */
75 static int _init(void);
76 static void _cleanup(void);
77
78 static void _cb_config_changed(void);
79
80 static enum oonf_telnet_result _cb_handle_layer2(struct oonf_telnet_data *data);
81
82 /* configuration */
83 static struct cfg_schema_entry _layer2_entries[] = {
84   CFG_MAP_ACL(_l2viewer_config, acl, "acl", "default_accept", "acl for layer2 telnet command"),
85 };
86
87 static struct cfg_schema_section _layer2_section = {
88   .type = OONF_PLUGIN_GET_NAME(),
89   .cb_delta_handler = _cb_config_changed,
90   .entries = _layer2_entries,
91   .entry_count = ARRAYSIZE(_layer2_entries),
92 };
93
94 static struct _l2viewer_config _config;
95
96 /* plugin declaration */
97 struct oonf_subsystem oonf_layer2_viewer_subsystem = {
98   .name = OONF_PLUGIN_GET_NAME(),
99   .descr = "OONFD layer2 viewer plugin",
100   .author = "Henning Rogge",
101
102   .cfg_section = &_layer2_section,
103
104   .init = _init,
105   .cleanup = _cleanup,
106 };
107 DECLARE_OONF_PLUGIN(oonf_layer2_viewer_subsystem);
108
109 /* telnet command */
110 static struct oonf_telnet_command _telnet_cmd =
111   TELNET_CMD("layer2", _cb_handle_layer2,
112       "\"layer2 net\": show data of all known WLAN networks\n"
113       "\"layer2 net list\": show a table of all known active WLAN networks\n"
114       "\"layer2 net "JSON_TEMPLATE_FORMAT"\": show a json output of all known active WLAN networks\n"
115       "\"layer2 net <template>\": show a table of all known active WLAN networks\n"
116       "     (use net_full/net_inactive to output all/inactive networks)\n"
117       "\"layer2 neigh\": show data of all known WLAN neighbors\n"
118       "\"layer2 neigh list\": show a table of all known WLAN neighbors\n"
119       "\"layer2 neigh "JSON_TEMPLATE_FORMAT"\": show a json output of all known WLAN neighbors\n"
120       "\"layer2 neigh <template>\": show a table of all known WLAN neighbors\n"
121       "     (use neigh_full/neigh_inactive to output all/inactive neighbors)\n",
122       .acl = &_config.acl);
123
124 /* template buffers */
125 static struct {
126   struct netaddr_str neigh_addr;
127   struct netaddr_str radio_addr;
128   char ifindex[10];
129   char interface[IF_NAMESIZE];
130   struct human_readable_str lastseen;
131   char if_id[33];
132   struct human_readable_str network[OONF_LAYER2_NET_COUNT];
133   struct human_readable_str neighbor[OONF_LAYER2_NEIGH_COUNT];
134 } _template_buf;
135
136 static struct abuf_template_data _template_neigh_data[5 + OONF_LAYER2_NEIGH_COUNT] = {
137   { .key = KEY_neighbor, .value = _template_buf.neigh_addr.buf, .string = true},
138   { .key = KEY_radio, .value = _template_buf.radio_addr.buf, .string = true},
139   { .key = KEY_ifindex, .value = _template_buf.ifindex },
140   { .key = KEY_interface, .value = _template_buf.interface, .string = true },
141   { .key = KEY_lastseen, .value = _template_buf.lastseen.buf },
142 };
143
144 static struct abuf_template_data _template_net_data[5 + OONF_LAYER2_NET_COUNT] = {
145   { .key = KEY_radio, .value = _template_buf.radio_addr.buf, .string = true},
146   { .key = KEY_ifindex, .value = _template_buf.ifindex },
147   { .key = KEY_interface, .value = _template_buf.interface, .string = true },
148   { .key = KEY_lastseen, .value = _template_buf.lastseen.buf },
149   { .key = KEY_ifid, .value = _template_buf.if_id, .string = true},
150 };
151
152 struct _command_params {
153   const char *sub;
154   const char *tmpl_full;
155   const char *tmpl_table;
156   const char *headline_table;
157
158   /* set by runtime */
159   const char *template;
160 };
161
162 struct _command_params _net_params = {
163   .sub = "net",
164
165   .tmpl_full =
166       "Radio MAC:    %" KEY_radio     "%\n"
167       "If-Index:     %" KEY_ifindex   "%\n"
168       "Interface:    %" KEY_interface "%\n"
169       "Interf. ID:   %" KEY_ifid      "%\n"
170       "Last seen:    %" KEY_lastseen  "% seconds ago\n"
171       "Frequency:    %" OONF_LAYER2_NET_FREQUENCY_KEY "%\n"
172       "Max. Bitrate: %" OONF_LAYER2_NET_MAX_BITRATE_KEY "%\n"
173       "\n",
174   .tmpl_table =
175       "%"KEY_interface"%\t%"KEY_radio"%\n",
176
177   .headline_table = "If\tRadio\n",
178 };
179
180 struct _command_params _neigh_params = {
181   .sub = "neigh",
182
183   .tmpl_full =
184       "Neighbor MAC: %" KEY_neighbor  "%\n"
185       "Radio MAC:    %" KEY_radio     "%\n"
186       "If-Index:     %" KEY_ifindex   "%\n"
187       "Interface:    %" KEY_interface "%\n"
188       "Last seen:    %" KEY_lastseen  "% seconds ago\n"
189       "Signal:       %" OONF_LAYER2_NEIGH_SIGNAL_KEY    "% dBm\n"
190       "Rx bitrate:   %" OONF_LAYER2_NEIGH_RX_BITRATE_KEY "%\n"
191       "Rx bytes:     %" OONF_LAYER2_NEIGH_RX_BYTES_KEY   "%\n"
192       "Rx frames:    %" OONF_LAYER2_NEIGH_RX_FRAMES_KEY "%\n"
193       "Tx bitrate:   %" OONF_LAYER2_NEIGH_TX_BITRATE_KEY "%\n"
194       "Tx bytes:     %" OONF_LAYER2_NEIGH_TX_BYTES_KEY   "%\n"
195       "Tx frames:    %" OONF_LAYER2_NEIGH_TX_FRAMES_KEY "%\n"
196       "Tx retries:   %" OONF_LAYER2_NEIGH_TX_RETRIES_KEY "%\n"
197       "Tx failed:    %" OONF_LAYER2_NEIGH_TX_FAILED_KEY  "%\n"
198       "\n",
199   .tmpl_table =
200       "%"KEY_interface"%\t%"KEY_radio"%\t%"KEY_neighbor"%\n",
201
202   .headline_table = "  If\tRadio\tNeighbor\n",
203 };
204
205 /**
206  * Constructor of plugin
207  * @return 0 if initialization was successful, -1 otherwise
208  */
209 static int
210 _init(void) {
211   int i;
212
213   oonf_telnet_add(&_telnet_cmd);
214
215   /* initialize templates */
216   for (i=0; i<OONF_LAYER2_NET_COUNT; i++) {
217     _template_net_data[5+i].key = oonf_layer2_metadata_net[i].key;
218     _template_net_data[5+i].string = false;
219     _template_net_data[5+i].value = _template_buf.network[i].buf;
220   }
221   for (i=0; i<OONF_LAYER2_NEIGH_COUNT; i++) {
222     _template_neigh_data[5+i].key = oonf_layer2_metadata_neigh[i].key;
223     _template_neigh_data[5+i].string = false;
224     _template_neigh_data[5+i].value = _template_buf.neighbor[i].buf;
225   }
226   return 0;
227 }
228
229 /**
230  * Disable plugin
231  */
232 static void
233 _cleanup(void) {
234   oonf_telnet_remove(&_telnet_cmd);
235 }
236
237 static int
238 _print_value(struct human_readable_str *dst, struct oonf_layer2_data *data,
239     const struct oonf_layer2_metadata *meta, bool raw) {
240   int64_t value;
241
242   value = oonf_layer2_get_value(data);
243   if (str_get_human_readable_s64(dst, value,
244       meta->unit, meta->fraction, meta->binary, raw)) {
245     return 0;
246   }
247   return -1;
248 }
249
250 /**
251  * Print the data of a layer2 addr to the telnet stream
252  * @param out pointer to output stream
253  * @param net pointer to layer2 addr data
254  * @return -1 if an error happened, 0 otherwise
255  */
256 static int
257 _init_network_template_value(struct oonf_layer2_net *net, bool raw) {
258   int i;
259
260   memset (&_template_buf, 0, sizeof(_template_buf));
261
262   if (NULL == netaddr_to_string(&_template_buf.radio_addr, &net->addr))
263     return -1;
264
265   if (net->if_index) {
266     sprintf(_template_buf.ifindex, "%u", net->if_index);
267     if_indextoname(net->if_index, _template_buf.interface);
268   }
269
270   if (net->if_ident[0]) {
271     strscpy(_template_buf.if_id, net->if_ident, sizeof(_template_buf.if_id));
272   }
273
274   if (net->last_seen) {
275     int64_t relative;
276
277     relative = oonf_clock_get_relative(net->last_seen);
278     if (NULL == oonf_clock_toIntervalString(&_template_buf.lastseen, -relative)) {
279       return -1;
280     }
281   }
282
283
284   for (i=0; i<OONF_LAYER2_NET_COUNT; i++) {
285     if (oonf_layer2_has_value(&net->data[i])) {
286       if (_print_value(&_template_buf.network[i], &net->data[i],
287           &oonf_layer2_metadata_net[i],raw)) {
288         return -1;
289       }
290     }
291   }
292   return 0;
293 }
294
295 /**
296  * Print the data of a layer2 neighbor to the telnet stream
297  * @param out pointer to output stream
298  * @param net pointer to layer2 neighbor data
299  * @return -1 if an error happened, 0 otherwise
300  */
301 static int
302 _init_neighbor_template_value(struct oonf_layer2_neigh *neigh, bool raw) {
303   int i;
304
305   if (NULL == netaddr_to_string(&_template_buf.neigh_addr, &neigh->addr))
306     return -1;
307
308   if (NULL == netaddr_to_string(&_template_buf.radio_addr, &neigh->network->addr))
309     return -1;
310
311   if (neigh->network->if_index) {
312     sprintf(_template_buf.ifindex, "%u", neigh->network->if_index);
313     if_indextoname(neigh->network->if_index, _template_buf.interface);
314   }
315
316   if (neigh->last_seen) {
317     int64_t relative;
318
319     relative = oonf_clock_get_relative(neigh->last_seen);
320     if (NULL == oonf_clock_toIntervalString(&_template_buf.lastseen, -relative)) {
321       return -1;
322     }
323   }
324
325   for (i=0; i<OONF_LAYER2_NEIGH_COUNT; i++) {
326     if (oonf_layer2_has_value(&neigh->data[i])) {
327       if (_print_value(&_template_buf.neighbor[i], &neigh->data[i],
328           &oonf_layer2_metadata_neigh[i], raw)) {
329         return -1;
330       }
331     }
332   }
333   return 0;
334 }
335
336 /**
337  * Parse a group of subcommands to support filtered/nonfiltered output with
338  * full, list, json and custom template mode
339  * @param out output buffer
340  * @param cmd command stream
341  * @param params pointer to subcommand description
342  * @return true if one of the subcommands was found, false otherwise
343  */
344 static bool
345 _parse_mode(struct autobuf *out, const char *cmd, struct _command_params *params) {
346   const char *next;
347
348   if ((next = str_hasnextword(cmd, params->sub)) == NULL) {
349     return false;
350   }
351
352   if (strcasecmp(next, "list") == 0) {
353     abuf_puts(out, params->headline_table);
354     params->template = params->tmpl_table;
355   }
356   else if (strcasecmp(next, JSON_TEMPLATE_FORMAT) == 0) {
357     params->template = NULL;
358   }
359   else if (*next == 0) {
360     params->template = params->tmpl_full;
361   }
362   else {
363     params->template = next;
364   }
365   return true;
366 }
367
368 /**
369  * Implementation of 'layer2' telnet command
370  * @param data pointer to telnet data
371  * @return return code for telnet server
372  */
373 static enum oonf_telnet_result
374 _cb_handle_layer2(struct oonf_telnet_data *data) {
375   struct oonf_layer2_net *net;
376   struct oonf_layer2_neigh *neigh;
377   struct abuf_template_storage *tmpl_storage = NULL;
378
379   if (data->parameter == NULL || *data->parameter == 0) {
380     abuf_puts(data->out, "Error, 'layer2' needs a parameter\n");
381     return TELNET_RESULT_ACTIVE;
382   }
383
384   if (_parse_mode(data->out, data->parameter, &_net_params)) {
385     if (_net_params.template) {
386       tmpl_storage = abuf_template_init(
387         _template_net_data, ARRAYSIZE(_template_net_data), _net_params.template);
388       if (tmpl_storage == NULL) {
389         return TELNET_RESULT_INTERNAL_ERROR;
390       }
391     }
392
393     avl_for_each_element(&oonf_layer2_net_tree, net, _node) {
394       if (_init_network_template_value(net, _net_params.template == NULL)) {
395         free(tmpl_storage);
396         return TELNET_RESULT_INTERNAL_ERROR;
397       }
398       if (_net_params.template) {
399         abuf_add_template(data->out, _net_params.template, tmpl_storage);
400       }
401       else {
402         abuf_add_json(data->out, "",
403             _template_net_data, ARRAYSIZE(_template_net_data));
404       }
405     }
406   }
407   else if (_parse_mode(data->out, data->parameter, &_neigh_params)) {
408     if (_neigh_params.template) {
409       tmpl_storage = abuf_template_init(
410         _template_neigh_data, ARRAYSIZE(_template_neigh_data), _neigh_params.template);
411       if (tmpl_storage == NULL) {
412         return TELNET_RESULT_INTERNAL_ERROR;
413       }
414     }
415
416     avl_for_each_element(&oonf_layer2_net_tree, net, _node) {
417       avl_for_each_element(&net->neighbors, neigh, _node) {
418         if (_init_neighbor_template_value(neigh, _neigh_params.template == NULL)) {
419           free(tmpl_storage);
420           return TELNET_RESULT_INTERNAL_ERROR;
421         }
422
423         if (_neigh_params.template) {
424           abuf_add_template(data->out, _neigh_params.template, tmpl_storage);
425         }
426         else {
427           abuf_add_json(data->out, "", _template_neigh_data, ARRAYSIZE(_template_neigh_data));
428         }
429       }
430     }
431   }
432   else {
433     abuf_appendf(data->out, "Error, unknown parameters for %s command: %s\n",
434         data->command, data->parameter);
435   }
436
437   free(tmpl_storage);
438   return TELNET_RESULT_ACTIVE;
439 }
440
441 /**
442  * Update configuration of layer2-viewer plugin
443  */
444 static void
445 _cb_config_changed(void) {
446   if (cfg_schema_tobin(&_config, _layer2_section.post,
447       _layer2_entries, ARRAYSIZE(_layer2_entries))) {
448     OONF_WARN(LOG_LAYER2_VIEWER, "Could not convert layer2_listener config to bin");
449     return;
450   }
451 }