route get/set working, remotecontrol plugin updated
[oonf.git] / lib / remotecontrol / src / remotecontrol.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2009, 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 <stdlib.h>
43
44 #include "common/common_types.h"
45 #include "common/autobuf.h"
46 #include "common/avl.h"
47 #include "common/avl_comp.h"
48 #include "common/list.h"
49 #include "common/netaddr.h"
50 #include "common/string.h"
51
52 #include "config/cfg_cmd.h"
53 #include "config/cfg_db.h"
54 #include "config/cfg_schema.h"
55
56 #include "olsr_cfg.h"
57 #include "olsr_logging.h"
58 #include "olsr_memcookie.h"
59 #include "olsr_plugins.h"
60 #include "olsr_telnet.h"
61 #include "olsr_timer.h"
62 #include "olsr.h"
63 #include "os_routing.h"
64
65 /* variable definitions */
66 struct _remotecontrol_cfg {
67   struct olsr_netaddr_acl acl;
68 };
69
70 struct _remotecontrol_session {
71   struct list_entity node;
72   struct olsr_telnet_cleanup cleanup;
73
74   struct log_handler_mask_entry *mask;
75
76   struct os_route route;
77 };
78
79 /* prototypes */
80 static int _cb_plugin_load(void);
81 static int _cb_plugin_unload(void);
82 static int _cb_plugin_enable(void);
83 static int _cb_plugin_disable(void);
84
85 static enum olsr_telnet_result _cb_handle_resource(struct olsr_telnet_data *data);
86 static enum olsr_telnet_result _cb_handle_route(struct olsr_telnet_data *data);
87 static enum olsr_telnet_result _cb_handle_log(struct olsr_telnet_data *data);
88 static enum olsr_telnet_result _cb_handle_config(struct olsr_telnet_data *data);
89 static enum olsr_telnet_result _update_logfilter(struct olsr_telnet_data *data,
90     struct log_handler_mask_entry *mask, const char *current, bool value);
91
92 static int _print_memory(struct autobuf *buf);
93 static int _print_timer(struct autobuf *buf);
94
95 static enum olsr_telnet_result _start_logging(struct olsr_telnet_data *data,
96     struct _remotecontrol_session *rc_session);
97 static void _stop_logging(struct olsr_telnet_data *data);
98
99 static void _cb_print_log(struct log_handler_entry *,
100     struct log_parameters *);
101
102 static void _cb_route_finished(struct os_route *, int error);
103 static void _cb_route_get(struct os_route *filter, struct os_route *route);
104
105 static void _cb_config_changed(void);
106 static struct _remotecontrol_session *
107     _get_remotecontrol_session(struct olsr_telnet_data *data);
108 static void _cb_handle_session_cleanup(struct olsr_telnet_cleanup *cleanup);
109
110
111
112 /* plugin declaration */
113 OLSR_PLUGIN7 {
114   .descr = "OLSRD remote control and debug plugin",
115   .author = "Henning Rogge",
116
117   .load = _cb_plugin_load,
118   .unload = _cb_plugin_unload,
119   .enable = _cb_plugin_enable,
120   .disable = _cb_plugin_disable,
121
122   .deactivate = true,
123 };
124
125 /* configuration */
126 static struct cfg_schema_section _remotecontrol_section = {
127   .type = "remotecontrol",
128   .cb_delta_handler = _cb_config_changed,
129 };
130
131 static struct cfg_schema_entry _remotecontrol_entries[] = {
132   CFG_MAP_ACL(_remotecontrol_cfg, acl, "acl", "+127.0.0.1\0+::1\0default_reject", "acl for remote control commands"),
133 };
134
135 static struct _remotecontrol_cfg _remotecontrol_config;
136
137 /* command callbacks and names */
138 static struct olsr_telnet_command _telnet_cmds[] = {
139   TELNET_CMD("resources", _cb_handle_resource,
140       "\"resources memory\": display information about memory usage\n"
141       "\"resources timer\": display information about active timers\n",
142       .acl = &_remotecontrol_config.acl),
143   TELNET_CMD("log", _cb_handle_log,
144       "\"log\":      continuous output of logging to this console\n"
145       "\"log show\": show configured logging option for debuginfo output\n"
146       "\"log add <severity> <source1> <source2> ...\": Add one or more sources of a defined severity for logging\n"
147       "\"log remove <severity> <source1> <source2> ...\": Remove one or more sources of a defined severity for logging\n",
148       .acl = &_remotecontrol_config.acl),
149   TELNET_CMD("config", _cb_handle_config,
150       "\"config commit\":                                   Commit changed configuration\n"
151       "\"config revert\":                                   Revert to active configuration\n"
152       "\"config schema\":                                   Display all allowed section types of configuration\n"
153       "\"config schema <section_type>\":                    Display all allowed entries of one configuration section\n"
154       "\"config schema <section_type.key>\":                Display help text for configuration entry\n"
155       "\"config load <SOURCE>\":                            Load configuration from a SOURCE\n"
156       "\"config save <TARGET>\":                            Save configuration to a TARGET\n"
157       "\"config set <section_type>.\":                      Add an unnamed section to the configuration\n"
158       "\"config set <section_type>.<key>=<value>\":         Add a key/value pair to an unnamed section\n"
159       "\"config set <section_type>[<name>].\":              Add a named section to the configuration\n"
160       "\"config set <section_type>[<name>].<key>=<value>\": Add a key/value pair to a named section\n"
161       "\"config remove <section_type>.\":                   Remove all sections of a certain type\n"
162       "\"config remove <section_type>.<key>\":              Remove a key in an unnamed section\n"
163       "\"config remove <section_type>[<name>].\":           Remove a named section\n"
164       "\"config remove <section_type>[<name>].<key>\":      Remove a key in a named section\n"
165       "\"config get\":                                      Show all section types in database\n"
166       "\"config get <section_type>.\":                      Show all named sections of a certain type\n"
167       "\"config get <section_type>.<key>\":                 Show the value(s) of a key in an unnamed section\n"
168       "\"config get <section_type>[<name>].<key>\":         Show the value(s) of a key in a named section\n"
169       "\"config format <FORMAT>\":                          Set the format for loading/saving data\n"
170       "\"config format AUTO\":                              Set the format to automatic detection\n",
171       .acl = &_remotecontrol_config.acl),
172   TELNET_CMD("route", _cb_handle_route,
173       "\"route add [src <src-ip>] [gw <gateway ip>] dst <destination prefix> [table <table-id>]\n"
174       "            [proto <protocol-id>] [metric <metric>] if <if-name>\n"
175       "                                                     Set a route in the kernel routing table\n"
176       "\"route del [src <src-ip>] [gw <gateway ip>] dst <destination prefix> [table <table-id>]\n"
177       "               [proto <protocol-id>] [metric <metric>] if <if-name>\n"
178       "                                                     Remove a route in the kernel routing table\n"
179       "\"route get [src <src-ip>] [gw <gateway ip>] [dst <destination prefix>] [table <table-id>]\n"
180       "               [proto <protocol-id>] [metric <metric>] [if <if-name>] [ipv6]\n"
181       "                                                     Lists all known kernel routes matching a set of data\n",
182       .acl = &_remotecontrol_config.acl),
183 };
184
185 /* variables for log access */
186 static int _log_source_maxlen, _log_severity_maxlen;
187
188 /* list of telnet sessions with logging mask data */
189 static struct list_entity _remote_sessions;
190
191 /**
192  * Initialize remotecontrol plugin
193  * @return always returns 0 (cannot fail)
194  */
195 static int
196 _cb_plugin_load(void)
197 {
198   cfg_schema_add_section(olsr_cfg_get_schema(), &_remotecontrol_section,
199       _remotecontrol_entries, ARRAYSIZE(_remotecontrol_entries));
200   olsr_acl_add(&_remotecontrol_config.acl);
201
202   return 0;
203 }
204
205 /**
206  * Free all resources of remotecontrol plugin
207  * @return always returns 0 (cannot fail)
208  */
209 static int
210 _cb_plugin_unload(void)
211 {
212   olsr_acl_remove(&_remotecontrol_config.acl);
213   cfg_schema_remove_section(olsr_cfg_get_schema(), &_remotecontrol_section);
214   return 0;
215 }
216
217 /**
218  * Enable remotecontrol plugin
219  * @return always returns 0 (cannot fail)
220  */
221 static int
222 _cb_plugin_enable(void)
223 {
224   size_t i;
225   enum log_source src;
226   enum log_severity sev;
227
228   list_init_head(&_remote_sessions);
229
230   /* calculate maximum length of log source names */
231   _log_source_maxlen = 0;
232   for (src=1; src<olsr_log_get_sourcecount(); src++) {
233     int len = strlen(LOG_SOURCE_NAMES[src]);
234
235     if (len > _log_source_maxlen) {
236       _log_source_maxlen = len;
237     }
238   }
239
240   /* calculate maximum length of log severity names */
241   _log_severity_maxlen = 0;
242   for (sev=1; sev<LOG_SEVERITY_COUNT; sev++) {
243     int len = strlen(LOG_SEVERITY_NAMES[sev]);
244
245     if (len > _log_severity_maxlen) {
246       _log_severity_maxlen = len;
247     }
248   }
249
250   for (i=0; i<ARRAYSIZE(_telnet_cmds); i++) {
251     olsr_telnet_add(&_telnet_cmds[i]);
252   }
253
254   return 0;
255 }
256
257 /**
258  * Deactivate remotecontrol plugin
259  * @return always returns 0 (cannot fail)
260  */
261 static int
262 _cb_plugin_disable(void)
263 {
264   struct _remotecontrol_session *session, *it;
265   size_t i;
266
267   for (i=0; i<ARRAYSIZE(_telnet_cmds); i++) {
268     olsr_telnet_remove(&_telnet_cmds[i]);
269   }
270
271   /* shutdown all running logging streams */
272   list_for_each_element_safe(&_remote_sessions, session, node, it) {
273     olsr_telnet_stop(session->cleanup.data);
274   }
275
276   return 0;
277 }
278
279 /**
280  * Print current resources known to memory manager
281  * @param buf output buffer
282  * @return -1 if an error happened, 0 otherwise
283  */
284 static int
285 _print_memory(struct autobuf *buf) {
286   struct olsr_memcookie_info *c, *iterator;
287
288   OLSR_FOR_ALL_COOKIES(c, iterator) {
289     if (abuf_appendf(buf, "%-25s (MEMORY) size: %lu usage: %u freelist: %u\n",
290         c->ci_name, (unsigned long)c->ci_size, c->ci_usage, c->ci_free_list_usage) < 0) {
291       return -1;
292     }
293   }
294   return 0;
295 }
296
297 /**
298  * Print current resources known to timer scheduler
299  * @param buf output buffer
300  * @return -1 if an error happened, 0 otherwise
301  */
302 static int
303 _print_timer(struct autobuf *buf) {
304   struct olsr_timer_info *t, *iterator;
305
306   OLSR_FOR_ALL_TIMERS(t, iterator) {
307     if (abuf_appendf(buf, "%-25s (TIMER) usage: %u changes: %u\n",
308         t->name, t->usage, t->changes) < 0) {
309       return -1;
310     }
311   }
312   return 0;
313 }
314
315 /**
316  * Handle resource command
317  * @param data pointer to telnet data
318  * @return telnet result constant
319  */
320 static enum olsr_telnet_result
321 _cb_handle_resource(struct olsr_telnet_data *data) {
322   if (data->parameter == NULL || strcasecmp(data->parameter, "memory") == 0) {
323     if (abuf_puts(data->out, "Memory cookies:\n") < 0) {
324       return TELNET_RESULT_INTERNAL_ERROR;
325     }
326
327     if (_print_memory(data->out)) {
328       return TELNET_RESULT_INTERNAL_ERROR;
329     }
330   }
331
332   if (data->parameter == NULL || strcasecmp(data->parameter, "timer") == 0) {
333     if (abuf_puts(data->out, "\nTimer cookies:\n") < 0) {
334       return TELNET_RESULT_INTERNAL_ERROR;
335     }
336
337     if (_print_timer(data->out)) {
338       return TELNET_RESULT_INTERNAL_ERROR;
339     }
340   }
341   return TELNET_RESULT_ACTIVE;
342 }
343
344 /**
345  * Update the remotecontrol logging filter
346  * @param data pointer to telnet data
347  * @param mask pointer to logging mask to manipulate
348  * @param param parameters of log add/log remove command
349  * @param value true if new source should be added, false
350  *    if it should be removed
351  * @return telnet result constant
352  */
353 static enum olsr_telnet_result
354 _update_logfilter(struct olsr_telnet_data *data,
355     struct log_handler_mask_entry *mask, const char *param, bool value) {
356   const char *next;
357   enum log_source src;
358   enum log_severity sev;
359
360   for (sev = 0; sev < LOG_SEVERITY_COUNT; sev++) {
361     if ((next = str_hasnextword(param, LOG_SEVERITY_NAMES[sev])) != NULL) {
362       break;
363     }
364   }
365   if (sev == LOG_SEVERITY_COUNT) {
366     abuf_appendf(data->out, "Error, unknown severity level: %s\n", param);
367     return TELNET_RESULT_ACTIVE;
368   }
369
370   param = next;
371   while (param && *param) {
372     for (src = 0; src < olsr_log_get_sourcecount(); src++) {
373       if ((next = str_hasnextword(param, LOG_SOURCE_NAMES[src])) != NULL) {
374         mask[src].log_for_severity[sev] = value;
375         value = false;
376         break;
377       }
378     }
379     if (src == olsr_log_get_sourcecount()) {
380       abuf_appendf(data->out, "Error, unknown logging source: %s\n", param);
381       return TELNET_RESULT_ACTIVE;
382     }
383     param = next;
384   }
385
386   olsr_log_updatemask();
387   return TELNET_RESULT_ACTIVE;
388 }
389
390 /**
391  * Log handler for telnet output
392  * @param entry logging handler
393  * @param param logging parameter set
394  */
395 static void
396 _cb_print_log(struct log_handler_entry *h __attribute__((unused)),
397     struct log_parameters *param) {
398   struct olsr_telnet_data *data = h->custom;
399
400   abuf_puts(data->out, param->buffer);
401   abuf_puts(data->out, "\n");
402
403   /* This might trigger logging output in olsr_socket_stream ! */
404   olsr_telnet_flush_session(data);
405 }
406
407 /**
408  * Stop handler for continous logging output
409  * @param telnet pointer ot telnet telnet
410  */
411 static void
412 _stop_logging(struct olsr_telnet_data *session) {
413   struct log_handler_entry *log_handler;
414
415   log_handler = session->stop_data[0];
416
417   olsr_log_removehandler(log_handler);
418   free (log_handler);
419
420   session->stop_handler = NULL;
421 }
422
423 /**
424  * Activate logging handler for telnet output
425  * @param data pointer to telnet data
426  * @param rc_session pointer to remotecontrol session
427  * @return telnet result code
428  */
429 static enum olsr_telnet_result
430 _start_logging(struct olsr_telnet_data *data,
431     struct _remotecontrol_session *rc_session) {
432   struct log_handler_entry *log_handler;
433
434   log_handler = calloc(1, sizeof(*log_handler));
435   if (log_handler == NULL) {
436     return TELNET_RESULT_INTERNAL_ERROR;
437   }
438
439   log_handler->bitmask = rc_session->mask;
440   log_handler->custom = data;
441   log_handler->handler = _cb_print_log;
442
443   data->stop_handler = _stop_logging;
444   data->stop_data[0] = log_handler;
445
446   return TELNET_RESULT_CONTINOUS;
447 }
448
449 /**
450  * Handle resource command
451  * @param data pointer to telnet data
452  * @return telnet result constant
453  */
454 static enum olsr_telnet_result
455 _cb_handle_log(struct olsr_telnet_data *data) {
456   struct _remotecontrol_session *rc_session;
457   const char *next;
458   enum log_source src;
459
460   rc_session = _get_remotecontrol_session(data);
461   if (rc_session == NULL) {
462     return TELNET_RESULT_INTERNAL_ERROR;
463   }
464
465   if (data->parameter == NULL) {
466     if (data->stop_handler) {
467       abuf_puts(data->out, "Error, you cannot stack continuous output commands\n");
468       return TELNET_RESULT_ACTIVE;
469     }
470
471     return _start_logging(data, rc_session);
472   }
473
474   if (strcasecmp(data->parameter, "show") == 0) {
475     abuf_appendf(data->out, "%*s %6s %6s %6s\n",
476         _log_source_maxlen, "",
477         LOG_SEVERITY_NAMES[SEVERITY_DEBUG],
478         LOG_SEVERITY_NAMES[SEVERITY_INFO],
479         LOG_SEVERITY_NAMES[SEVERITY_WARN]);
480
481     for (src=0; src<olsr_log_get_sourcecount(); src++) {
482       abuf_appendf(data->out, "%*s %*s %*s %*s\n",
483         _log_source_maxlen, LOG_SOURCE_NAMES[src],
484         (int)sizeof(LOG_SEVERITY_NAMES[SEVERITY_DEBUG]),
485         rc_session->mask[src].log_for_severity[SEVERITY_DEBUG] ? "*" : "",
486         (int)sizeof(LOG_SEVERITY_NAMES[SEVERITY_INFO]),
487         rc_session->mask[src].log_for_severity[SEVERITY_INFO] ? "*" : "",
488         (int)sizeof(LOG_SEVERITY_NAMES[SEVERITY_WARN]),
489         rc_session->mask[src].log_for_severity[SEVERITY_WARN] ? "*" : "");
490     }
491     return TELNET_RESULT_ACTIVE;
492   }
493
494   if ((next = str_hasnextword(data->parameter, "add")) != NULL) {
495     return _update_logfilter(data, rc_session->mask, next, true);
496   }
497   if ((next = str_hasnextword(data->parameter, "remove")) != NULL) {
498     return _update_logfilter(data, rc_session->mask, next, false);
499   }
500
501   abuf_appendf(data->out, "Error, unknown subcommand for %s: %s",
502       data->command, data->parameter);
503   return TELNET_RESULT_ACTIVE;
504 }
505
506 /**
507  * Handle config command
508  * @param data pointer to telnet data
509  * @return telnet result constant
510  */
511 static enum olsr_telnet_result
512 _cb_handle_config(struct olsr_telnet_data *data) {
513   const char *next = NULL;
514
515   if (data->parameter == NULL || *data->parameter == 0) {
516     abuf_puts(data->out, "Error, 'config' needs a parameter\n");
517     return TELNET_RESULT_ACTIVE;
518   }
519
520   if ((next = str_hasnextword(data->parameter, "commit"))) {
521     if (!cfg_schema_validate(olsr_cfg_get_rawdb(),
522         false, true, data->out)) {
523       olsr_cfg_trigger_commit();
524     }
525   }
526   else if ((next = str_hasnextword(data->parameter, "rollback"))) {
527     olsr_cfg_rollback();
528   }
529   else if ((next = str_hasnextword(data->parameter, "format"))) {
530     cfg_cmd_handle_format(olsr_cfg_get_instance(), next);
531   }
532   else if ((next = str_hasnextword(data->parameter, "get"))) {
533     cfg_cmd_handle_get(olsr_cfg_get_instance(),
534         olsr_cfg_get_rawdb(), next, data->out);
535   }
536   else if ((next = str_hasnextword(data->parameter, "load"))) {
537     cfg_cmd_handle_load(olsr_cfg_get_instance(),
538         olsr_cfg_get_rawdb(), next, data->out);
539   }
540   else if ((next = str_hasnextword(data->parameter, "remove"))) {
541     cfg_cmd_handle_remove(olsr_cfg_get_instance(),
542         olsr_cfg_get_rawdb(), next, data->out);
543   }
544   else if ((next = str_hasnextword(data->parameter, "save"))) {
545     cfg_cmd_handle_save(olsr_cfg_get_instance(),
546         olsr_cfg_get_rawdb(), next, data->out);
547   }
548   else if ((next = str_hasnextword(data->parameter, "schema"))) {
549     cfg_cmd_handle_schema(
550         olsr_cfg_get_rawdb(), next, data->out);
551   }
552   else if ((next = str_hasnextword(data->parameter, "set"))) {
553     cfg_cmd_handle_set(olsr_cfg_get_instance(),
554         olsr_cfg_get_rawdb(), next, data->out);
555   }
556   else {
557     abuf_appendf(data->out, "Error, unknown subcommand for %s: %s",
558         data->command, data->parameter);
559   }
560
561   return TELNET_RESULT_ACTIVE;
562 }
563
564 static void
565 _stop_route_feedback(struct olsr_telnet_data *session) {
566   struct os_route *route;
567
568   route = session->stop_data[0];
569   if (route->cb_finished) {
570     route->cb_finished(route, -1);
571   }
572 }
573
574 static void
575 _cb_route_finished(struct os_route *rt, int error) {
576   struct _remotecontrol_session *session;
577
578   session = container_of(rt, struct _remotecontrol_session, route);
579
580   if (error) {
581     abuf_appendf(session->cleanup.data->out, "Command failed: %s (%d)\n",
582         strerror(error), error);
583   }
584   else {
585     abuf_puts(session->cleanup.data->out, "Command successful\n");
586   }
587   olsr_telnet_flush_session(session->cleanup.data);
588   olsr_telnet_stop(session->cleanup.data);
589 }
590
591 static void
592 _cb_route_get(struct os_route *filter, struct os_route *route) {
593   struct _remotecontrol_session *session;
594   struct autobuf *out;
595   struct netaddr_str buf;
596   char if_buf[IF_NAMESIZE];
597
598   session = container_of(filter, struct _remotecontrol_session, route);
599   out = session->cleanup.data->out;
600
601   if (route->dst.type != AF_UNSPEC) {
602     abuf_appendf(out, "%s ", netaddr_to_string(&buf, &route->dst));
603   }
604   if (route->gw.type != AF_UNSPEC) {
605     abuf_appendf(out, "via %s ", netaddr_to_string(&buf, &route->gw));
606   }
607   if (route->src.type != AF_UNSPEC) {
608     abuf_appendf(out, "src %s ", netaddr_to_string(&buf, &route->src));
609   }
610   if (route->dst.type == AF_UNSPEC
611       && route->gw.type == AF_UNSPEC
612       && route->src.type == AF_UNSPEC) {
613     abuf_appendf(out, "%s ", route->family == AF_INET ? "ipv4" : "ipv6");
614   }
615
616   if (route->if_index) {
617     abuf_appendf(out, "dev %s (%d) ",
618         if_indextoname(route->if_index, if_buf), route->if_index);
619   }
620   if (route->protocol != RTPROT_UNSPEC) {
621     abuf_appendf(out, "prot %d ", route->protocol);
622   }
623   if (route->metric != -1) {
624     abuf_appendf(out, "metric %d ", route->metric);
625   }
626   if (route->table != RT_TABLE_UNSPEC) {
627     abuf_appendf(out, "table %d ", route->table);
628   }
629   abuf_puts(out, "\n");
630   olsr_telnet_flush_session(session->cleanup.data);
631 }
632
633 /**
634  * Handle the route command
635  * @param data pointer to telnet data
636  * @return telnet result constant
637  */
638 static enum olsr_telnet_result
639 _cb_handle_route(struct olsr_telnet_data *data) {
640   bool add  = false, del = false, get = false;
641   const char *ptr = NULL, *next = NULL;
642   struct _remotecontrol_session *session;
643   struct netaddr_str buf;
644   struct os_route route;
645   int result;
646
647   memcpy(&route, &OS_ROUTE_WILDCARD, sizeof(route));
648
649   if ((next = str_hasnextword(data->parameter, "add")) != NULL) {
650     add = true;
651   }
652   else if ((next = str_hasnextword(data->parameter, "del")) != NULL) {
653     del = true;
654   }
655   else if ((next = str_hasnextword(data->parameter, "get")) != NULL) {
656     get = true;
657   }
658
659   if (add || del || get) {
660     ptr = next;
661     while (ptr && *ptr) {
662       if ((next = str_hasnextword(ptr, "src"))) {
663         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
664         if (netaddr_from_string(&route.src, buf.buf) != 0
665             || (route.src.type != AF_INET && route.src.type != AF_INET6)) {
666           abuf_appendf(data->out, "Error, illegal source: %s", buf.buf);
667           return TELNET_RESULT_ACTIVE;
668         }
669         route.family = route.src.type;
670       }
671       else if ((next = str_hasnextword(ptr, "gw"))) {
672         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
673         if (netaddr_from_string(&route.gw, buf.buf) != 0
674             || (route.gw.type != AF_INET && route.gw.type != AF_INET6)) {
675           abuf_appendf(data->out, "Error, illegal gateway: %s", buf.buf);
676           return TELNET_RESULT_ACTIVE;
677         }
678         route.family = route.gw.type;
679       }
680       else if ((next = str_hasnextword(ptr, "dst"))) {
681         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
682         if (netaddr_from_string(&route.dst, buf.buf) != 0
683             || (route.dst.type != AF_INET && route.dst.type != AF_INET6)) {
684           abuf_appendf(data->out, "Error, illegal destination: %s", buf.buf);
685           return TELNET_RESULT_ACTIVE;
686         }
687         route.family = route.dst.type;
688       }
689       else if ((next = str_hasnextword(ptr, "table"))) {
690         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
691         route.table = atoi(buf.buf);
692       }
693       else if ((next = str_hasnextword(ptr, "proto"))) {
694         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
695         route.protocol = atoi(buf.buf);
696       }
697       else if ((next = str_hasnextword(ptr, "metric"))) {
698         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
699         route.table = atoi(buf.buf);
700       }
701       else if ((next = str_hasnextword(ptr, "if"))) {
702         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
703         route.if_index = if_nametoindex(buf.buf);
704       }
705       else if ((next = str_hasnextword(ptr, "ipv6"))) {
706         route.family = AF_INET6;
707         ptr = next;
708       }
709       else {
710         abuf_appendf(data->out, "Cannot parse remainder of parameter string: %s", ptr);
711         return TELNET_RESULT_ACTIVE;
712       }
713     }
714     if ((add||del) && route.if_index == 0) {
715       abuf_appendf(data->out, "Missing or unknown interface");
716       return TELNET_RESULT_ACTIVE;
717     }
718     if ((add||del) && route.dst.type == AF_UNSPEC) {
719       abuf_appendf(data->out, "Error, IPv4 or IPv6 destination mandatory for add/del");
720       return TELNET_RESULT_ACTIVE;
721     }
722     if ((route.src.type != AF_UNSPEC && route.src.type != route.family)
723         || (route.gw.type != AF_UNSPEC && route.gw.type != route.family)
724         || (route.dst.type != AF_UNSPEC && route.dst.type != route.family)) {
725       abuf_appendf(data->out, "Error, IP address types do not match");
726       return TELNET_RESULT_ACTIVE;
727     }
728
729     if (route.family == AF_UNSPEC) {
730       route.family = AF_INET;
731     }
732
733     /* allocate permanent route datastructure for continous output */
734     session = _get_remotecontrol_session(data);
735     if (session == NULL) {
736       OLSR_WARN_OOM(LOG_PLUGINS);
737       return TELNET_RESULT_INTERNAL_ERROR;
738     }
739     memcpy(&session->route, &route, sizeof(route));
740
741     session->route.cb_finished = _cb_route_finished;
742     session->route.cb_get = _cb_route_get;
743
744     if (add || del) {
745       result = os_routing_set(&session->route, add, true);
746     }
747     else {
748       result = os_routing_get(&session->route);
749     }
750
751     if (result) {
752       abuf_puts(data->out, "Error while preparing netlink command");
753       return TELNET_RESULT_ACTIVE;
754     }
755
756     data->stop_handler = _stop_route_feedback;
757     data->stop_data[0] = session;
758     return TELNET_RESULT_CONTINOUS;
759   }
760   abuf_appendf(data->out, "Error, unknown subcommand for %s: %s",
761       data->command, data->parameter);
762   return TELNET_RESULT_ACTIVE;
763 }
764
765 /**
766  * Update configuration of remotecontrol plugin
767  */
768 static void
769 _cb_config_changed(void) {
770   if (cfg_schema_tobin(&_remotecontrol_config, _remotecontrol_section.post,
771       _remotecontrol_entries, ARRAYSIZE(_remotecontrol_entries))) {
772     OLSR_WARN(LOG_CONFIG, "Could not convert remotecontrol config to bin");
773     return;
774   }
775 }
776
777 /**
778  * Look for remotecontrol session of telnet data. Create one if
779  * necessary
780  * @param data pointer to telnet data
781  * @return remotecontrol session, NULL if an error happened
782  */
783 static struct _remotecontrol_session *
784 _get_remotecontrol_session(struct olsr_telnet_data *data) {
785   struct _remotecontrol_session *cl;
786
787   list_for_each_element(&_remote_sessions, cl, node) {
788     if (cl->cleanup.data == data) {
789       return cl;
790     }
791   }
792
793   /* create new telnet */
794   cl = calloc(1, sizeof(*cl));
795   if (cl == NULL) {
796     OLSR_WARN_OOM(LOG_PLUGINS);
797     return NULL;
798   }
799
800   cl->cleanup.cleanup_handler = _cb_handle_session_cleanup;
801   cl->cleanup.custom = cl;
802   olsr_telnet_add_cleanup(data, &cl->cleanup);
803
804   /* copy global mask */
805   cl->mask = olsr_log_allocate_mask();
806   olsr_log_copy_mask(cl->mask, log_global_mask);
807
808   /* add to remote telnet list */
809   list_add_tail(&_remote_sessions, &cl->node);
810
811   return cl;
812 }
813
814 /**
815  * Cleanup remotecontrol session if telnet session is over
816  * @param cleanup pointer to telnet cleanup handler
817  */
818 static void
819 _cb_handle_session_cleanup(struct olsr_telnet_cleanup *cleanup) {
820   struct _remotecontrol_session *session;
821
822   session = cleanup->custom;
823   list_remove(&session->node);
824   olsr_log_free_mask(session->mask);
825   free(session);
826 }