Rename "subsystems" directory to "base"
[oonf.git] / src / generic / remotecontrol / remotecontrol.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 <stdlib.h>
47
48 #include <oonf/libcommon/autobuf.h>
49 #include <oonf/libcommon/avl.h>
50 #include <oonf/libcommon/avl_comp.h>
51 #include <oonf/oonf.h>
52 #include <oonf/libcommon/list.h>
53 #include <oonf/libcommon/netaddr.h>
54 #include <oonf/libcommon/string.h>
55 #include <oonf/libconfig/cfg_cmd.h>
56 #include <oonf/libconfig/cfg_db.h>
57 #include <oonf/libconfig/cfg_schema.h>
58 #include <oonf/libcore/oonf_cfg.h>
59 #include <oonf/libcore/oonf_logging.h>
60 #include <oonf/libcore/oonf_subsystem.h>
61 #include <oonf/base/oonf_class.h>
62 #include <oonf/base/oonf_telnet.h>
63 #include <oonf/base/oonf_timer.h>
64 #include <oonf/base/os_routing.h>
65
66 #include <oonf/generic/remotecontrol/remotecontrol.h>
67
68 /* Definitions */
69 #define LOG_REMOTECONTROL _oonf_remotecontrol_subsystem.logging
70
71 /**
72  * Remote control configuration
73  */
74 struct _remotecontrol_cfg {
75   /*! access control list for telnet plugin */
76   struct netaddr_acl acl;
77 };
78
79 /**
80  * Remote control session for telnet command
81  */
82 struct _remotecontrol_session {
83   /*! hook into list of sessions */
84   struct list_entity node;
85
86   /*! telnet cleanup hooks */
87   struct oonf_telnet_cleanup cleanup;
88
89   /*! logging mask for telnet command */
90   uint8_t mask[LOG_MAXIMUM_SOURCES];
91
92   /*! route object for routing queries */
93   struct os_route route;
94 };
95
96 /* prototypes */
97 static int _init(void);
98 static void _cleanup(void);
99
100 static enum oonf_telnet_result _cb_handle_route(struct oonf_telnet_data *data);
101 static enum oonf_telnet_result _cb_handle_log(struct oonf_telnet_data *data);
102 static enum oonf_telnet_result _cb_handle_config(struct oonf_telnet_data *data);
103 static enum oonf_telnet_result _update_logfilter(
104   struct oonf_telnet_data *data, uint8_t *mask, const char *current, bool value);
105
106 static enum oonf_telnet_result _start_logging(struct oonf_telnet_data *data, struct _remotecontrol_session *rc_session);
107 static void _stop_logging(struct oonf_telnet_data *data);
108
109 static void _cb_print_log(struct oonf_log_handler_entry *, struct oonf_log_parameters *);
110
111 static void _cb_route_finished(struct os_route *, int error);
112 static void _cb_route_get(struct os_route *filter, struct os_route *route);
113
114 static void _cb_config_changed(void);
115 static struct _remotecontrol_session *_get_remotecontrol_session(struct oonf_telnet_data *data);
116 static void _cb_handle_session_cleanup(struct oonf_telnet_cleanup *cleanup);
117
118 /* configuration */
119 static struct cfg_schema_entry _remotecontrol_entries[] = {
120   CFG_MAP_ACL(_remotecontrol_cfg, acl, "acl", ACL_LOCALHOST_ONLY, "acl for remote control commands"),
121 };
122
123 static struct cfg_schema_section _remotecontrol_section = {
124   .type = OONF_REMOTECONTROL_SUBSYSTEM,
125   .cb_delta_handler = _cb_config_changed,
126   .entries = _remotecontrol_entries,
127   .entry_count = ARRAYSIZE(_remotecontrol_entries),
128 };
129
130 static struct _remotecontrol_cfg _remotecontrol_config;
131
132 /* plugin declaration */
133 static const char *_dependencies[] = {
134   OONF_CLASS_SUBSYSTEM,
135   OONF_TELNET_SUBSYSTEM,
136   OONF_TIMER_SUBSYSTEM,
137   OONF_OS_ROUTING_SUBSYSTEM,
138 };
139
140 static struct oonf_subsystem _oonf_remotecontrol_subsystem = {
141   .name = OONF_REMOTECONTROL_SUBSYSTEM,
142   .dependencies = _dependencies,
143   .dependencies_count = ARRAYSIZE(_dependencies),
144   .descr = "OONF remote control and debug plugin",
145   .author = "Henning Rogge",
146
147   .cfg_section = &_remotecontrol_section,
148
149   .init = _init,
150   .cleanup = _cleanup,
151 };
152 DECLARE_OONF_PLUGIN(_oonf_remotecontrol_subsystem);
153
154 /* command callbacks and names */
155 static struct oonf_telnet_command _telnet_cmds[] = {
156   TELNET_CMD("log", _cb_handle_log,
157     "\"log\":      continuous output of logging to this console\n"
158     "\"log show\": show configured logging option for debuginfo output\n"
159     "\"log add <severity> <source1> <source2> ...\": Add one or more sources of a defined severity for logging\n"
160     "\"log remove <severity> <source1> <source2> ...\": Remove one or more sources of a defined severity for logging\n",
161     .acl = &_remotecontrol_config.acl),
162   TELNET_CMD("config", _cb_handle_config,
163     "\"config commit\":                                   Commit changed configuration\n"
164     "\"config revert\":                                   Revert to active configuration\n"
165     "\"config schema\":                                   Display all allowed section types of configuration\n"
166     "\"config schema <section_type>\":                    Display all allowed entries of one configuration section\n"
167     "\"config schema <section_type.key>\":                Display help text for configuration entry\n"
168     "\"config load <SOURCE>\":                            Load configuration from a SOURCE\n"
169     "\"config save <TARGET>\":                            Save configuration to a TARGET\n"
170     "\"config set <section_type>.\":                      Add an unnamed section to the configuration\n"
171     "\"config set <section_type>.<key>=<value>\":         Add a key/value pair to an unnamed section\n"
172     "\"config set <section_type>[<name>].\":              Add a named section to the configuration\n"
173     "\"config set <section_type>[<name>].<key>=<value>\": Add a key/value pair to a named section\n"
174     "\"config remove <section_type>.\":                   Remove all sections of a certain type\n"
175     "\"config remove <section_type>.<key>\":              Remove a key in an unnamed section\n"
176     "\"config remove <section_type>[<name>].\":           Remove a named section\n"
177     "\"config remove <section_type>[<name>].<key>\":      Remove a key in a named section\n"
178     "\"config get\":                                      Show all section types in database\n"
179     "\"config get <section_type>.\":                      Show all named sections of a certain type\n"
180     "\"config get <section_type>.<key>\":                 Show the value(s) of a key in an unnamed section\n"
181     "\"config get <section_type>[<name>].<key>\":         Show the value(s) of a key in a named section\n"
182     "\"config query <section_type>.<key>\":               Show the value(s) of a key in an unnamed section, show default value if no data available\n"
183     "\"config query <section_type>[<name>].<key>\":       Show the value(s) of a key in a named section, show default value if no data available\n",
184     .acl = &_remotecontrol_config.acl),
185   TELNET_CMD("route", _cb_handle_route,
186     "\"route add [src-ip <src-ip>] [gw <gateway ip>] dst <destination prefix> [src-prefix <src-prefix]\n"
187     "            [table <table-id>] [proto <protocol-id>] [metric <metric>] if <if-name>\n"
188     "                                                     Set a route in the kernel routing table\n"
189     "\"route del [src-ip <src-ip>] [gw <gateway ip>] dst <destination prefix> [src-prefix <src-prefix]\n"
190     "            [table <table-id>] [proto <protocol-id>] [metric <metric>] if <if-name>\n"
191     "                                                     Remove a route in the kernel routing table\n"
192     "\"route get [src-ip <src-ip>] [gw <gateway ip>] [dst <destination prefix>] [src-prefix]\n"
193     "               [table <table-id>] [proto <protocol-id>] [metric <metric>] [if <if-name>] [ipv6]\n"
194     "                                                     Lists all known kernel routes matching a set of data\n",
195     .acl = &_remotecontrol_config.acl),
196 };
197
198 /* list of telnet sessions with logging mask data */
199 static struct list_entity _remote_sessions;
200
201 /**
202  * Initialize remotecontrol plugin
203  * @return always returns 0 (cannot fail)
204  */
205 static int
206 _init(void) {
207   size_t i;
208
209   netaddr_acl_add(&_remotecontrol_config.acl);
210   list_init_head(&_remote_sessions);
211
212   for (i = 0; i < ARRAYSIZE(_telnet_cmds); i++) {
213     oonf_telnet_add(&_telnet_cmds[i]);
214   }
215
216   return 0;
217 }
218
219 /**
220  * Free all resources of remotecontrol plugin
221  */
222 static void
223 _cleanup(void) {
224   struct _remotecontrol_session *session, *it;
225   size_t i;
226
227   /* shutdown all running logging streams */
228   list_for_each_element_safe(&_remote_sessions, session, node, it) {
229     oonf_telnet_stop(session->cleanup.data, false);
230   }
231
232   for (i = 0; i < ARRAYSIZE(_telnet_cmds); i++) {
233     oonf_telnet_remove(&_telnet_cmds[i]);
234   }
235
236   netaddr_acl_remove(&_remotecontrol_config.acl);
237 }
238
239 /**
240  * Update the remotecontrol logging filter
241  * @param data pointer to telnet data
242  * @param mask pointer to logging mask to manipulate
243  * @param param parameters of log add/log remove command
244  * @param value true if new source should be added, false
245  *    if it should be removed
246  * @return telnet result constant
247  */
248 static enum oonf_telnet_result
249 _update_logfilter(struct oonf_telnet_data *data, uint8_t *mask, const char *param, bool value) {
250   const char *next;
251   enum oonf_log_source src;
252   enum oonf_log_severity sev;
253
254   OONF_FOR_ALL_LOGSEVERITIES(sev) {
255     if ((next = str_hasnextword(param, LOG_SEVERITY_NAMES[sev])) != NULL) {
256       break;
257     }
258   }
259   if (sev == LOG_SEVERITY_MAX) {
260     abuf_appendf(data->out, "Error, unknown severity level: %s\n", param);
261     return TELNET_RESULT_ACTIVE;
262   }
263
264   param = next;
265   while (param && *param) {
266     for (src = 0; src < oonf_log_get_sourcecount(); src++) {
267       if ((next = str_hasnextword(param, LOG_SOURCE_NAMES[src])) != NULL) {
268         if (value) {
269           oonf_log_mask_set(mask, src, sev);
270         }
271         else {
272           oonf_log_mask_reset(mask, src, sev);
273         }
274         break;
275       }
276     }
277     if (src == oonf_log_get_sourcecount()) {
278       abuf_appendf(data->out, "Error, unknown logging source: %s\n", param);
279       return TELNET_RESULT_ACTIVE;
280     }
281     param = next;
282   }
283
284   oonf_log_updatemask();
285   return TELNET_RESULT_ACTIVE;
286 }
287
288 /**
289  * Log handler for telnet output
290  * @param h logging handler
291  * @param param logging parameter set
292  */
293 static void
294 _cb_print_log(struct oonf_log_handler_entry *h __attribute__((unused)), struct oonf_log_parameters *param) {
295   struct oonf_telnet_data *data = h->custom;
296
297   abuf_puts(data->out, param->buffer);
298   abuf_puts(data->out, "\n");
299
300   /* This might trigger logging output in oonf_socket_stream ! */
301   oonf_telnet_flush_session(data);
302 }
303
304 /**
305  * Stop handler for continous logging output
306  * @param session telnet session data
307  */
308 static void
309 _stop_logging(struct oonf_telnet_data *session) {
310   struct oonf_log_handler_entry *log_handler;
311
312   log_handler = session->stop_data[0];
313
314   oonf_log_removehandler(log_handler);
315   free(log_handler);
316
317   session->stop_handler = NULL;
318 }
319
320 /**
321  * Activate logging handler for telnet output
322  * @param data pointer to telnet data
323  * @param rc_session pointer to remotecontrol session
324  * @return telnet result code
325  */
326 static enum oonf_telnet_result
327 _start_logging(struct oonf_telnet_data *data, struct _remotecontrol_session *rc_session) {
328   struct oonf_log_handler_entry *log_handler;
329
330   log_handler = calloc(1, sizeof(*log_handler));
331   if (log_handler == NULL) {
332     return TELNET_RESULT_INTERNAL_ERROR;
333   }
334
335   oonf_log_mask_copy(log_handler->user_bitmask, rc_session->mask);
336   log_handler->custom = data;
337   log_handler->handler = _cb_print_log;
338
339   oonf_log_addhandler(log_handler);
340
341   data->stop_handler = _stop_logging;
342   data->stop_data[0] = log_handler;
343
344   return TELNET_RESULT_CONTINOUS;
345 }
346
347 /**
348  * Handle resource command
349  * @param data pointer to telnet data
350  * @return telnet result constant
351  */
352 static enum oonf_telnet_result
353 _cb_handle_log(struct oonf_telnet_data *data) {
354   struct _remotecontrol_session *rc_session;
355   const char *next;
356   enum oonf_log_source src;
357
358   rc_session = _get_remotecontrol_session(data);
359   if (rc_session == NULL) {
360     return TELNET_RESULT_INTERNAL_ERROR;
361   }
362
363   if (data->parameter == NULL) {
364     if (data->stop_handler) {
365       abuf_puts(data->out, "Error, you cannot stack continuous output commands\n");
366       return TELNET_RESULT_ACTIVE;
367     }
368
369     return _start_logging(data, rc_session);
370   }
371
372   if (strcasecmp(data->parameter, "show") == 0) {
373     abuf_appendf(data->out, "%*s %*s %*s %*s\n", (int)oonf_log_get_max_sourcetextlen(), "",
374       (int)oonf_log_get_max_severitytextlen(), LOG_SEVERITY_NAMES[LOG_SEVERITY_DEBUG],
375       (int)oonf_log_get_max_severitytextlen(), LOG_SEVERITY_NAMES[LOG_SEVERITY_INFO],
376       (int)oonf_log_get_max_severitytextlen(), LOG_SEVERITY_NAMES[LOG_SEVERITY_WARN]);
377
378     for (src = 0; src < oonf_log_get_sourcecount(); src++) {
379       abuf_appendf(data->out, "%*s %*s %*s %*s\n", (int)oonf_log_get_max_sourcetextlen(), LOG_SOURCE_NAMES[src],
380         (int)oonf_log_get_max_severitytextlen(),
381         oonf_log_mask_test(rc_session->mask, src, LOG_SEVERITY_DEBUG) ? "*" : "",
382         (int)oonf_log_get_max_severitytextlen(),
383         oonf_log_mask_test(rc_session->mask, src, LOG_SEVERITY_INFO) ? "*" : "",
384         (int)oonf_log_get_max_severitytextlen(),
385         oonf_log_mask_test(rc_session->mask, src, LOG_SEVERITY_WARN) ? "*" : "");
386     }
387     return TELNET_RESULT_ACTIVE;
388   }
389
390   if ((next = str_hasnextword(data->parameter, "add")) != NULL) {
391     return _update_logfilter(data, rc_session->mask, next, true);
392   }
393   if ((next = str_hasnextword(data->parameter, "remove")) != NULL) {
394     return _update_logfilter(data, rc_session->mask, next, false);
395   }
396
397   abuf_appendf(data->out, "Error, unknown subcommand for %s: %s", data->command, data->parameter);
398   return TELNET_RESULT_ACTIVE;
399 }
400
401 /**
402  * Handle config command
403  * @param data pointer to telnet data
404  * @return telnet result constant
405  */
406 static enum oonf_telnet_result
407 _cb_handle_config(struct oonf_telnet_data *data) {
408   const char *next = NULL;
409   int result = 0;
410
411   if (data->parameter == NULL || *data->parameter == 0) {
412     abuf_puts(data->out, "Error, 'config' needs a parameter\n");
413     return TELNET_RESULT_ACTIVE;
414   }
415
416   if ((next = str_hasnextword(data->parameter, "commit"))) {
417     if (cfg_schema_validate(oonf_cfg_get_rawdb(), false, true, data->out) == 0) {
418       oonf_cfg_trigger_commit();
419     }
420   }
421   else if ((next = str_hasnextword(data->parameter, "rollback"))) {
422     result = oonf_cfg_rollback();
423   }
424   else if ((next = str_hasnextword(data->parameter, "get"))) {
425     result = cfg_cmd_handle_get(oonf_cfg_get_instance(), oonf_cfg_get_rawdb(), next, data->out);
426   }
427   else if ((next = str_hasnextword(data->parameter, "query"))) {
428     result = cfg_cmd_handle_query(oonf_cfg_get_instance(), oonf_cfg_get_rawdb(), next, data->out);
429   }
430   else if ((next = str_hasnextword(data->parameter, "load"))) {
431     result = cfg_cmd_handle_load(oonf_cfg_get_instance(), oonf_cfg_get_rawdb(), next, data->out);
432   }
433   else if ((next = str_hasnextword(data->parameter, "remove"))) {
434     result = cfg_cmd_handle_remove(oonf_cfg_get_instance(), oonf_cfg_get_rawdb(), next, data->out);
435   }
436   else if ((next = str_hasnextword(data->parameter, "save"))) {
437     result = cfg_cmd_handle_save(oonf_cfg_get_instance(), oonf_cfg_get_rawdb(), next, data->out);
438   }
439   else if ((next = str_hasnextword(data->parameter, "schema"))) {
440     result = cfg_cmd_handle_schema(oonf_cfg_get_rawdb(), next, data->out);
441   }
442   else if ((next = str_hasnextword(data->parameter, "set"))) {
443     result = cfg_cmd_handle_set(oonf_cfg_get_instance(), oonf_cfg_get_rawdb(), next, data->out);
444   }
445   else {
446     abuf_appendf(data->out, "Error, unknown subcommand for %s: %s", data->command, data->parameter);
447   }
448
449   if (result) {
450     abuf_puts(data->out, "Command returned an error");
451   }
452   return TELNET_RESULT_ACTIVE;
453 }
454
455 /**
456  * Handle interrupt from user console during route output
457  * @param data telnet session data
458  */
459 static void
460 _cb_route_stophandler(struct oonf_telnet_data *data) {
461   struct _remotecontrol_session *session;
462
463   session = data->stop_data[0];
464   os_routing_interrupt(&session->route);
465 }
466
467 /**
468  * Handle end of incoming route data
469  * @param rt OS route data
470  * @param error error code, 0 if 0 error
471  */
472 static void
473 _cb_route_finished(struct os_route *rt, int error) {
474   struct _remotecontrol_session *session;
475
476   session = container_of(rt, struct _remotecontrol_session, route);
477
478   if (error) {
479     abuf_appendf(session->cleanup.data->out, "Command failed: %s (%d)\n", strerror(error), error);
480   }
481   else {
482     abuf_puts(session->cleanup.data->out, "Command successful\n");
483   }
484
485   oonf_telnet_stop(session->cleanup.data, false);
486 }
487
488 /**
489  * Handle incoming route data
490  * @param filter pointer to filter for route data
491  * @param route pointer to route data
492  */
493 static void
494 _cb_route_get(struct os_route *filter, struct os_route *route) {
495   struct _remotecontrol_session *session;
496   struct autobuf *out;
497   struct netaddr_str buf;
498   char if_buf[IF_NAMESIZE];
499
500   session = container_of(filter, struct _remotecontrol_session, route);
501   out = session->cleanup.data->out;
502
503   if (netaddr_get_address_family(&route->p.key.dst) != AF_UNSPEC) {
504     abuf_appendf(out, "%s ", netaddr_to_string(&buf, &route->p.key.dst));
505   }
506   if (netaddr_get_address_family(&route->p.gw) != AF_UNSPEC) {
507     abuf_appendf(out, "via %s ", netaddr_to_string(&buf, &route->p.gw));
508   }
509   if (netaddr_get_address_family(&route->p.src_ip) != AF_UNSPEC) {
510     abuf_appendf(out, "src-ip %s ", netaddr_to_string(&buf, &route->p.src_ip));
511   }
512   if (netaddr_get_address_family(&route->p.key.src) != AF_UNSPEC) {
513     abuf_appendf(out, "src-prefix %s ", netaddr_to_string(&buf, &route->p.key.src));
514   }
515   if (netaddr_get_address_family(&route->p.key.dst) == AF_UNSPEC &&
516       netaddr_get_address_family(&route->p.gw) == AF_UNSPEC &&
517       netaddr_get_address_family(&route->p.src_ip) == AF_UNSPEC) {
518     abuf_appendf(out, "%s ", route->p.family == AF_INET ? "ipv4" : "ipv6");
519   }
520
521   if (route->p.if_index) {
522     abuf_appendf(out, "dev %s (%d) ", if_indextoname(route->p.if_index, if_buf), route->p.if_index);
523   }
524   if (route->p.protocol != RTPROT_UNSPEC) {
525     abuf_appendf(out, "prot %d ", route->p.protocol);
526   }
527   if (route->p.metric != -1) {
528     abuf_appendf(out, "metric %d ", route->p.metric);
529   }
530   if (route->p.table != RT_TABLE_UNSPEC) {
531     abuf_appendf(out, "table %d ", route->p.table);
532   }
533   abuf_puts(out, "\n");
534   oonf_telnet_flush_session(session->cleanup.data);
535 }
536
537 /**
538  * Handle the route command
539  * @param data pointer to telnet data
540  * @return telnet result constant
541  */
542 static enum oonf_telnet_result
543 _cb_handle_route(struct oonf_telnet_data *data) {
544   bool add = false, del = false, get = false;
545   const char *ptr = NULL, *next = NULL;
546   struct _remotecontrol_session *session;
547   struct netaddr_str buf;
548   struct os_route route;
549   int result;
550
551   os_routing_init_wildcard_route(&route);
552
553   if ((next = str_hasnextword(data->parameter, "add")) != NULL) {
554     add = true;
555   }
556   else if ((next = str_hasnextword(data->parameter, "del")) != NULL) {
557     del = true;
558   }
559   else if ((next = str_hasnextword(data->parameter, "get")) != NULL) {
560     get = true;
561   }
562
563   if (add || del || get) {
564     ptr = next;
565     while (ptr && *ptr) {
566       if ((next = str_hasnextword(ptr, "src-ip"))) {
567         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
568         if (netaddr_from_string(&route.p.src_ip, buf.buf) != 0 ||
569             (netaddr_get_address_family(&route.p.src_ip) != AF_INET &&
570               netaddr_get_address_family(&route.p.src_ip) != AF_INET6)) {
571           abuf_appendf(data->out, "Error, illegal source: %s", buf.buf);
572           return TELNET_RESULT_ACTIVE;
573         }
574         route.p.family = netaddr_get_address_family(&route.p.src_ip);
575       }
576       else if ((next = str_hasnextword(ptr, "gw"))) {
577         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
578         if (netaddr_from_string(&route.p.gw, buf.buf) != 0 || (netaddr_get_address_family(&route.p.gw) != AF_INET &&
579                                                                 netaddr_get_address_family(&route.p.gw) != AF_INET6)) {
580           abuf_appendf(data->out, "Error, illegal gateway: %s", buf.buf);
581           return TELNET_RESULT_ACTIVE;
582         }
583         route.p.family = netaddr_get_address_family(&route.p.gw);
584       }
585       else if ((next = str_hasnextword(ptr, "dst"))) {
586         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
587         if (netaddr_from_string(&route.p.key.dst, buf.buf) != 0 ||
588             (netaddr_get_address_family(&route.p.key.dst) != AF_INET &&
589               netaddr_get_address_family(&route.p.key.dst) != AF_INET6)) {
590           abuf_appendf(data->out, "Error, illegal destination: %s", buf.buf);
591           return TELNET_RESULT_ACTIVE;
592         }
593         route.p.family = netaddr_get_address_family(&route.p.key.dst);
594       }
595       else if ((next = str_hasnextword(ptr, "src-prefix"))) {
596         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
597         if (netaddr_from_string(&route.p.key.src, buf.buf) != 0 ||
598             (netaddr_get_address_family(&route.p.key.src) != AF_INET &&
599               netaddr_get_address_family(&route.p.key.src) != AF_INET6)) {
600           abuf_appendf(data->out, "Error, illegal source-prefix: %s", buf.buf);
601           return TELNET_RESULT_ACTIVE;
602         }
603         route.p.family = netaddr_get_address_family(&route.p.key.src);
604       }
605       else if ((next = str_hasnextword(ptr, "table"))) {
606         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
607         route.p.table = atoi(buf.buf);
608       }
609       else if ((next = str_hasnextword(ptr, "proto"))) {
610         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
611         route.p.protocol = atoi(buf.buf);
612       }
613       else if ((next = str_hasnextword(ptr, "metric"))) {
614         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
615         route.p.table = atoi(buf.buf);
616       }
617       else if ((next = str_hasnextword(ptr, "if"))) {
618         ptr = str_cpynextword(buf.buf, next, sizeof(buf));
619         route.p.if_index = if_nametoindex(buf.buf);
620       }
621       else if ((next = str_hasnextword(ptr, "ipv6"))) {
622         route.p.family = AF_INET6;
623         ptr = next;
624       }
625       else {
626         abuf_appendf(data->out, "Cannot parse remainder of parameter string: %s", ptr);
627         return TELNET_RESULT_ACTIVE;
628       }
629     }
630     if ((add || del) && route.p.if_index == 0) {
631       abuf_appendf(data->out, "Missing or unknown interface");
632       return TELNET_RESULT_ACTIVE;
633     }
634     if ((add || del) && netaddr_get_address_family(&route.p.key.dst) == AF_UNSPEC) {
635       abuf_appendf(data->out, "Error, IPv4 or IPv6 destination mandatory for add/del");
636       return TELNET_RESULT_ACTIVE;
637     }
638     if ((netaddr_get_address_family(&route.p.src_ip) != AF_UNSPEC &&
639           netaddr_get_address_family(&route.p.src_ip) != route.p.family) ||
640         (netaddr_get_address_family(&route.p.gw) != AF_UNSPEC &&
641           netaddr_get_address_family(&route.p.gw) != route.p.family) ||
642         (netaddr_get_address_family(&route.p.key.dst) != AF_UNSPEC &&
643           netaddr_get_address_family(&route.p.key.dst) != route.p.family)) {
644       abuf_appendf(data->out, "Error, IP address types do not match");
645       return TELNET_RESULT_ACTIVE;
646     }
647
648     if (route.p.family == AF_UNSPEC) {
649       route.p.family = AF_INET;
650     }
651
652     /* allocate permanent route datastructure for continous output */
653     session = _get_remotecontrol_session(data);
654     if (session == NULL) {
655       return TELNET_RESULT_INTERNAL_ERROR;
656     }
657     memcpy(&session->route, &route, sizeof(route));
658
659     session->route.cb_finished = _cb_route_finished;
660     session->route.cb_get = _cb_route_get;
661
662     if (add || del) {
663       result = os_routing_set(&session->route, add, true);
664     }
665     else {
666       result = os_routing_query(&session->route);
667     }
668
669     if (result) {
670       abuf_puts(data->out, "Error while preparing netlink command");
671       return TELNET_RESULT_ACTIVE;
672     }
673
674     data->stop_handler = _cb_route_stophandler;
675     data->stop_data[0] = session;
676     return TELNET_RESULT_CONTINOUS;
677   }
678   abuf_appendf(data->out, "Error, unknown subcommand for %s: %s", data->command, data->parameter);
679   return TELNET_RESULT_ACTIVE;
680 }
681
682 /**
683  * Update configuration of remotecontrol plugin
684  */
685 static void
686 _cb_config_changed(void) {
687   if (cfg_schema_tobin(&_remotecontrol_config, _remotecontrol_section.post, _remotecontrol_entries,
688         ARRAYSIZE(_remotecontrol_entries))) {
689     OONF_WARN(LOG_REMOTECONTROL, "Could not convert remotecontrol config to bin");
690     return;
691   }
692 }
693
694 /**
695  * Look for remotecontrol session of telnet data. Create one if
696  * necessary
697  * @param data pointer to telnet data
698  * @return remotecontrol session, NULL if an error happened
699  */
700 static struct _remotecontrol_session *
701 _get_remotecontrol_session(struct oonf_telnet_data *data) {
702   struct _remotecontrol_session *cl;
703
704   list_for_each_element(&_remote_sessions, cl, node) {
705     if (cl->cleanup.data == data) {
706       return cl;
707     }
708   }
709
710   /* create new telnet */
711   cl = calloc(1, sizeof(*cl));
712   if (cl == NULL) {
713     OONF_WARN(LOG_REMOTECONTROL, "Not enough memory for remotecontrol session");
714     return NULL;
715   }
716
717   cl->cleanup.cleanup_handler = _cb_handle_session_cleanup;
718   cl->cleanup.custom = cl;
719   oonf_telnet_add_cleanup(data, &cl->cleanup);
720
721   /* copy global mask */
722   oonf_log_mask_copy(cl->mask, log_global_mask);
723
724   /* add to remote telnet list */
725   list_add_tail(&_remote_sessions, &cl->node);
726
727   return cl;
728 }
729
730 /**
731  * Cleanup remotecontrol session if telnet session is over
732  * @param cleanup pointer to telnet cleanup handler
733  */
734 static void
735 _cb_handle_session_cleanup(struct oonf_telnet_cleanup *cleanup) {
736   struct _remotecontrol_session *session;
737
738   session = cleanup->custom;
739   list_remove(&session->node);
740   free(session);
741 }