Fix compilation issue with "no-debug" logging
[oonf.git] / src / olsrv2 / netjsoninfo / netjsoninfo.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/autobuf.h>
47 #include <oonf/oonf.h>
48 #include <oonf/libcommon/json.h>
49
50 #include <oonf/libcore/oonf_logging.h>
51 #include <oonf/libcore/oonf_subsystem.h>
52 #include <oonf/base/oonf_clock.h>
53 #include <oonf/base/oonf_telnet.h>
54
55 #include <oonf/nhdp/nhdp/nhdp.h>
56 #include <oonf/nhdp/nhdp/nhdp_db.h>
57 #include <oonf/nhdp/nhdp/nhdp_domain.h>
58 #include <oonf/nhdp/nhdp/nhdp_interfaces.h>
59 #include <oonf/olsrv2/olsrv2/olsrv2.h>
60 #include <oonf/olsrv2/olsrv2/olsrv2_lan.h>
61 #include <oonf/olsrv2/olsrv2/olsrv2_originator.h>
62 #include <oonf/olsrv2/olsrv2/olsrv2_routing.h>
63 #include <oonf/olsrv2/olsrv2/olsrv2_tc.h>
64
65 #include <oonf/olsrv2/netjsoninfo/netjsoninfo.h>
66
67 /* definitions */
68 #define LOG_NETJSONINFO olsrv2_netjsoninfo.logging
69
70 /*! name of filter command */
71 #define JSON_NAME_FILTER "filter"
72
73 /*! name of graph command/json-object */
74 #define JSON_NAME_GRAPH "graph"
75
76 /*! name of route command/json-object */
77 #define JSON_NAME_ROUTE "route"
78
79 /*! name of domain command/json-object */
80 #define JSON_NAME_DOMAIN "domain"
81
82 /*! Text buffer for a domain id string */
83 struct domain_id_str {
84   /*! string buffer */
85   char buf[16];
86 };
87
88 /*! Text buffer for a node id string */
89 struct _node_id_str {
90   /*! string buffer */
91   char buf[256];
92 };
93
94 /*! types of nodes known to olsrv2 netjson graph */
95 enum netjson_node_type
96 {
97   /*! the local node itself */
98   NETJSON_NODE_LOCAL,
99
100   /*! attached network prefix of the local node */
101   NETJSON_NODE_LAN,
102
103   /*! a remote OLSRv2 router */
104   NETJSON_NODE_ROUTERS,
105
106   /*! attached network prefix of a remote router */
107   NETJSON_NODE_ATTACHED,
108 };
109
110 /*! types of edges known to olsrv2 netjson graph */
111 enum netjson_edge_type
112 {
113   /*! outgoing edge of the local router */
114   NETJSON_EDGE_LOCAL,
115
116   /*! edge to attached prefix of the local router */
117   NETJSON_EDGE_LAN,
118
119   /*! edge from or between remote routers */
120   NETJSON_EDGE_ROUTERS,
121
122   /*! edge to attached prefix of a remote router */
123   NETJSON_EDGE_ATTACHED,
124 };
125
126 /* prototypes */
127 static int _init(void);
128 static void _cleanup(void);
129
130 static void _print_graph(struct json_session *session, struct nhdp_domain *domain, int af_type);
131 static void _create_graph_json(struct json_session *session, const char *filter);
132 static void _print_routing_tree(struct json_session *session, struct nhdp_domain *domain, int af_type);
133 static void _create_route_json(struct json_session *session, const char *filter);
134 static void _create_domain_json(struct json_session *session);
135 static void _create_error_json(struct json_session *session, const char *message, const char *parameter);
136 static enum oonf_telnet_result _cb_netjsoninfo(struct oonf_telnet_data *con);
137 static void _print_json_string(struct json_session *session, const char *key, const char *value);
138 static void _print_json_number(struct json_session *session, const char *key, uint64_t value);
139 static void _print_json_netaddr(struct json_session *session, const char *key, const struct netaddr *addr);
140
141 /* telnet command of this plugin */
142 static struct oonf_telnet_command _telnet_commands[] = {
143   TELNET_CMD(OONF_NETJSONINFO_SUBSYSTEM, _cb_netjsoninfo,
144     "The command has three main commands (route, graph, domain) and a"
145     " 'filter' prefix for route/graph. You can use any combination of the"
146     " three main commands (space separated) to generate a NetworkCollection"
147     " with the information of the main commands for all known domains.\n"
148     "> netjsoninfo route graph\n"
149     "The filter prefix use an id (which can be queried by 'domain') to output"
150     " a single domain of route/graph without the NetworkCollection object"
151     " around it. The domain_id's are ipv4_<domain_number> and ipv6_<domain_number>.\n"
152     "> netjsoninfo filter route ipv4_0\n"),
153 };
154
155 /* plugin declaration */
156 static const char *_dependencies[] = {
157   OONF_NHDP_SUBSYSTEM,
158   OONF_OLSRV2_SUBSYSTEM,
159   OONF_TELNET_SUBSYSTEM,
160 };
161 static struct oonf_subsystem olsrv2_netjsoninfo = {
162   .name = OONF_NETJSONINFO_SUBSYSTEM,
163   .dependencies = _dependencies,
164   .dependencies_count = ARRAYSIZE(_dependencies),
165   .descr = "OLSRv2 JSON for networks generator plugin",
166   .author = "Henning Rogge",
167   .init = _init,
168   .cleanup = _cleanup,
169 };
170 DECLARE_OONF_PLUGIN(olsrv2_netjsoninfo);
171
172 /**
173  * Initialize plugin
174  * @return always returns 0
175  */
176 static int
177 _init(void) {
178   oonf_telnet_add(&_telnet_commands[0]);
179   return 0;
180 }
181
182 /**
183  * Cleanup plugin
184  */
185 static void
186 _cleanup(void) {
187   oonf_telnet_remove(&_telnet_commands[0]);
188 }
189
190 /**
191  * @param af_type address family type
192  * @return originator of the other type (IPv4 for IPv6)
193  */
194 static int
195 _get_other_af_type(int af_type) {
196   switch (af_type) {
197     case AF_INET:
198       return AF_INET6;
199       break;
200     case AF_INET6:
201       return AF_INET;
202       break;
203     default:
204       return 0;
205   }
206 }
207
208 /**
209  * Create a domain id string
210  * @param buf output buffer
211  * @param domain nhdp domain
212  * @param af_type address family type
213  * @return pointer to output buffer
214  */
215 static const char *
216 _create_domain_id(struct domain_id_str *buf, struct nhdp_domain *domain, int af_type) {
217   snprintf(buf->buf, sizeof(*buf), "%s_%u", af_type == AF_INET ? "ipv4" : "ipv6", domain->ext);
218   return buf->buf;
219 }
220
221 /**
222  * Create a node id string
223  * @param buf output buffer
224  * @param originator originator address
225  * @param addr secondary address, might be NULL
226  * @return pointer to output buffer
227  */
228 static const char *
229 _get_node_id(struct _node_id_str *buf, const struct netaddr *originator, const struct netaddr *addr) {
230   struct netaddr_str nbuf1, nbuf2;
231
232   netaddr_to_string(&nbuf1, originator);
233
234   if (!addr) {
235     snprintf(buf->buf, sizeof(*buf), "id_%s", nbuf1.buf);
236   }
237   else {
238     netaddr_to_string(&nbuf2, addr);
239
240     snprintf(buf->buf, sizeof(*buf), "id_%s_%s", nbuf1.buf, nbuf2.buf);
241   }
242   return buf->buf;
243 }
244
245 /**
246  * Create a node id string for the local router
247  * @param buf output buffer
248  * @param af_family address family
249  * @return pointer to output buffer
250  */
251 static const char *
252 _get_node_id_me(struct _node_id_str *buf, int af_family) {
253   return _get_node_id(buf, olsrv2_originator_get(af_family), NULL);
254 }
255
256 /**
257  * Create a node id string for a remote router
258  * @param buf output buffer
259  * @param node tc node
260  * @return pointer to output buffer
261  */
262 static const char *
263 _get_tc_node_id(struct _node_id_str *buf, const struct olsrv2_tc_node *node) {
264   return _get_node_id(buf, &node->target.prefix.dst, NULL);
265 }
266
267 /**
268  * Create a node id for a remote endpoint (attached prefix)
269  * @param buf output buffer
270  * @param attachment tc attachment
271  * @return pointer to output buffer
272  */
273 static const char *
274 _get_tc_endpoint_id(struct _node_id_str *buf, const struct olsrv2_tc_attachment *attachment) {
275   return _get_node_id(buf, &attachment->src->target.prefix.dst, &attachment->dst->target.prefix.dst);
276 }
277
278 /**
279  * Create a node id for a locally attached network
280  * @param buf output buffer
281  * @param lan locally attached network
282  * @return pointer to output buffer
283  */
284 static const char *
285 _get_tc_lan_id(struct _node_id_str *buf, const struct olsrv2_lan_entry *lan) {
286   int af_family;
287
288   af_family = netaddr_get_address_family(&lan->prefix.dst);
289   return _get_node_id(buf, olsrv2_originator_get(af_family), &lan->prefix.dst);
290 }
291
292 /**
293  * Create a node id for a NHDP neighbor
294  * @param buf output buffer
295  * @param neigh NHDP neighbor
296  * @return pointer to output buffer
297  */
298 static const char *
299 _get_nhdp_neighbor_id(struct _node_id_str *buf, const struct nhdp_neighbor *neigh) {
300   return _get_node_id(buf, &neigh->originator, NULL);
301 }
302
303 /**
304  * Print the JSON output for a graph node
305  * @param session json session
306  * @param id node address
307  * @param originator node originator address
308  * @param dualstack node dualstack originator address
309  * @param type netjson node type
310  */
311 static void
312 _print_graph_node(struct json_session *session, const struct _node_id_str *id, const char *label,
313   const struct netaddr *originator, const struct netaddr *dualstack, enum netjson_node_type type) {
314   struct _node_id_str originator_id, dualstack_id;
315
316   ;
317
318   json_start_object(session, NULL);
319
320   _print_json_string(session, "id", id->buf);
321   _print_json_string(session, "label", label);
322
323   json_start_object(session, "properties");
324   if (originator) {
325     _print_json_string(session, "router_id", _get_node_id(&originator_id, originator, NULL));
326     _print_json_netaddr(session, "router_addr", originator);
327   }
328   if (dualstack) {
329     _print_json_string(session, "dualstack_id", _get_node_id(&dualstack_id, dualstack, NULL));
330     _print_json_netaddr(session, "dualstack_addr", dualstack);
331   }
332
333   switch (type) {
334     case NETJSON_NODE_LOCAL:
335       _print_json_string(session, "type", "local");
336       break;
337     case NETJSON_NODE_LAN:
338       _print_json_string(session, "type", "lan");
339       break;
340     case NETJSON_NODE_ROUTERS:
341       _print_json_string(session, "type", "node");
342       break;
343     case NETJSON_NODE_ATTACHED:
344       _print_json_string(session, "type", "attached");
345       break;
346     default:
347       _print_json_string(session, "type", "unknown");
348       break;
349   }
350   json_end_object(session);
351
352   json_end_object(session);
353 }
354
355 /**
356  * Print the JSON node element for the local node
357  * @param session json session
358  * @param af_family address family
359  */
360 static void
361 _print_graph_node_me(struct json_session *session, int af_family) {
362   struct _node_id_str ebuf1;
363   struct netaddr_str nbuf1;
364   const struct netaddr *dualstack;
365
366   _get_node_id_me(&ebuf1, af_family);
367   netaddr_to_string(&nbuf1, olsrv2_originator_get(af_family));
368
369   dualstack = olsrv2_originator_get(_get_other_af_type(af_family));
370   _print_graph_node(session, &ebuf1, nbuf1.buf, olsrv2_originator_get(af_family), dualstack, NETJSON_NODE_LOCAL);
371 }
372
373 /**
374  * Print the JSON node element for a tc node
375  * @param session json session
376  * @param node tc node
377  */
378 static void
379 _print_graph_node_tc(struct json_session *session, const struct olsrv2_tc_node *node) {
380   const struct netaddr *dualstack;
381   struct _node_id_str ebuf;
382   struct netaddr_str nbuf1;
383   struct nhdp_neighbor *neigh;
384
385   _get_tc_node_id(&ebuf, node);
386   netaddr_to_string(&nbuf1, &node->target.prefix.dst);
387
388   dualstack = NULL;
389   neigh = nhdp_db_neighbor_get_by_originator(&node->target.prefix.dst);
390   if (neigh && neigh->dualstack_partner) {
391     dualstack = &neigh->dualstack_partner->originator;
392   }
393   _print_graph_node(session, &ebuf, nbuf1.buf, &node->target.prefix.dst, dualstack, NETJSON_NODE_ROUTERS);
394 }
395
396 /**
397  * Print the JSON node element for a tc attachment
398  * @param session json session
399  * @param attachment tc attachment
400  */
401 static void
402 _print_graph_node_attached(struct json_session *session, const struct olsrv2_tc_attachment *attachment) {
403   struct _node_id_str ebuf;
404   struct netaddr_str nbuf1, nbuf2;
405
406   char labelbuf[256];
407
408   _get_tc_endpoint_id(&ebuf, attachment);
409   netaddr_to_string(&nbuf1, &attachment->src->target.prefix.dst);
410   netaddr_to_string(&nbuf2, &attachment->dst->target.prefix.dst);
411
412   snprintf(labelbuf, sizeof(labelbuf), "%s - %s", nbuf1.buf, nbuf2.buf);
413
414   _print_graph_node(session, &ebuf, labelbuf, &attachment->src->target.prefix.dst, NULL, NETJSON_NODE_ATTACHED);
415 }
416
417 /**
418  * Print the JSON node element for a locally attached network
419  * @param session json session
420  * @param lan locally attached network
421  */
422 static void
423 _print_graph_node_lan(struct json_session *session, const struct olsrv2_lan_entry *lan) {
424   const struct netaddr *originator;
425   struct netaddr_str nbuf1, nbuf2;
426   struct _node_id_str ebuf;
427   int af_type;
428
429   char labelbuf[256];
430
431   af_type = netaddr_get_address_family(&lan->prefix.dst);
432   originator = olsrv2_originator_get(af_type);
433
434   _get_tc_lan_id(&ebuf, lan);
435   netaddr_to_string(&nbuf1, originator);
436   netaddr_to_string(&nbuf2, &lan->prefix.dst);
437
438   snprintf(labelbuf, sizeof(labelbuf), "%s - %s", nbuf1.buf, nbuf2.buf);
439
440   _print_graph_node(session, &ebuf, labelbuf, originator, NULL, NETJSON_NODE_LAN);
441 }
442
443 /**
444  * Print the NHDP links for a JSON link element.
445  * @param session json session
446  * @param domain nhdp domain
447  * @param neigh nhdp neighbor
448  * @param outgoing neighbor link is on outgoing dijkstra tree
449  */
450 static void
451 _print_edge_links(
452   struct json_session *session, struct nhdp_domain *domain, struct nhdp_neighbor *neigh, bool outgoing) {
453   struct nhdp_link *lnk;
454   struct nhdp_link *best_link;
455   struct nhdp_metric_str mbuf;
456   int32_t cost;
457   int af_type;
458
459   af_type = netaddr_get_address_family(&neigh->originator);
460
461   best_link = nhdp_domain_get_neighbordata(domain, neigh)->best_out_link;
462
463   json_start_array(session, "links");
464
465   list_for_each_element(&neigh->_links, lnk, _neigh_node) {
466     if (netaddr_get_address_family(&lnk->if_addr) != af_type) {
467       continue;
468     }
469
470     json_start_object(session, NULL);
471
472     _print_json_string(session, "interface", nhdp_interface_get_name(lnk->local_if));
473
474     _print_json_netaddr(session, "source_addr", nhdp_interface_get_socket_address(lnk->local_if, af_type));
475     _print_json_netaddr(session, "target_addr", &lnk->if_addr);
476
477     cost = nhdp_domain_get_linkdata(domain, lnk)->metric.out;
478     _print_json_number(session, "cost", cost);
479     _print_json_string(session, "cost_text", nhdp_domain_get_link_metric_value(&mbuf, domain, cost));
480
481     cost = nhdp_domain_get_linkdata(domain, lnk)->metric.in;
482     _print_json_number(session, "in_cost", cost);
483     _print_json_string(session, "in_text", nhdp_domain_get_link_metric_value(&mbuf, domain, cost));
484
485     _print_json_string(session, "outgoing_tree", json_getbool(outgoing && best_link == lnk));
486
487     json_end_object(session);
488   }
489
490   json_end_array(session);
491 }
492
493 /**
494  * Print a JSON graph edge
495  * @param session json session
496  * @param domain nhdp domain
497  * @param src source id
498  * @param dst destination id
499  * @param src_addr source IP address
500  * @param dst_addr destination IP address
501  * @param out outgoing metric
502  * @param in incoming metric, 0 for no metric
503  * @param hopcount outgoing hopcount, 0 for no hopcount
504  * @param outgoing_tree true if part of outgoing tree
505  * @param type edge type
506  * @param neigh reference to NHDP neighbor of edge,
507  *   NULL if no direct neighbor edge
508  */
509 static void
510 _print_graph_edge(struct json_session *session, struct nhdp_domain *domain, const struct _node_id_str *src,
511   const struct _node_id_str *dst, const struct netaddr *src_addr, const struct netaddr *dst_addr, uint32_t out,
512   uint32_t in, uint8_t hopcount, bool outgoing_tree, enum netjson_edge_type type, struct nhdp_neighbor *neigh) {
513   struct nhdp_metric_str mbuf;
514
515   if (out > RFC7181_METRIC_MAX) {
516     return;
517   }
518
519   json_start_object(session, NULL);
520   _print_json_string(session, "source", src->buf);
521   _print_json_string(session, "target", dst->buf);
522
523   _print_json_number(session, "cost", out);
524   _print_json_string(session, "cost_text", nhdp_domain_get_link_metric_value(&mbuf, domain, out));
525
526   json_start_object(session, "properties");
527   if (in >= RFC7181_METRIC_MIN && in <= RFC7181_METRIC_MAX) {
528     _print_json_number(session, "in_cost", in);
529     _print_json_string(session, "in_text", nhdp_domain_get_link_metric_value(&mbuf, domain, in));
530   }
531   _print_json_string(session, "outgoing_tree", json_getbool(outgoing_tree));
532
533   if (src_addr) {
534     _print_json_netaddr(session, "source_addr", src_addr);
535   }
536   if (dst_addr) {
537     _print_json_netaddr(session, "target_addr", dst_addr);
538   }
539   if (hopcount) {
540     _print_json_number(session, "hopcount", hopcount);
541   }
542
543   switch (type) {
544     case NETJSON_EDGE_LOCAL:
545       _print_json_string(session, "type", "local");
546       break;
547     case NETJSON_EDGE_LAN:
548       _print_json_string(session, "type", "lan");
549       break;
550     case NETJSON_EDGE_ROUTERS:
551       _print_json_string(session, "type", "node");
552       break;
553     case NETJSON_EDGE_ATTACHED:
554       _print_json_string(session, "type", "attached");
555       break;
556     default:
557       _print_json_string(session, "type", "unknown");
558       break;
559   }
560
561   if (neigh) {
562     _print_edge_links(session, domain, neigh, outgoing_tree);
563   }
564   json_end_object(session);
565   json_end_object(session);
566 }
567
568 /**
569  * Print the JSON graph object
570  * @param session json session
571  * @param domain NHDP domain
572  * @param af_type address family type
573  */
574 static void
575 _print_graph(struct json_session *session, struct nhdp_domain *domain, int af_type) {
576   struct os_route_key routekey;
577   const struct netaddr *originator, *dualstack;
578   struct nhdp_neighbor *neigh;
579   struct olsrv2_tc_node *node;
580   struct olsrv2_tc_edge *edge;
581   struct olsrv2_tc_attachment *attached;
582   struct olsrv2_lan_entry *lan;
583   struct avl_tree *rt_tree;
584   struct olsrv2_routing_entry *rt_entry;
585   struct domain_id_str dbuf;
586   struct _node_id_str node_id1, node_id2;
587   int other_af;
588
589   bool outgoing;
590
591   originator = olsrv2_originator_get(af_type);
592   if (netaddr_is_unspec(originator)) {
593     return;
594   }
595
596   /* get "other" originator */
597   other_af = _get_other_af_type(af_type);
598
599   /* get dualstack originator */
600   dualstack = olsrv2_originator_get(AF_INET6);
601
602   json_start_object(session, NULL);
603
604   _print_json_string(session, "type", "NetworkGraph");
605   _print_json_string(session, "protocol", "olsrv2");
606   _print_json_string(session, "version", oonf_log_get_libdata()->version);
607   _print_json_string(session, "revision", oonf_log_get_libdata()->git_commit);
608
609   _print_json_string(session, "router_id", _get_node_id_me(&node_id1, af_type));
610
611   _print_json_string(session, "metric", domain->metric->name);
612   _print_json_string(session, "topology_id", _create_domain_id(&dbuf, domain, af_type));
613
614   json_start_object(session, "properties");
615   _print_json_netaddr(session, "router_addr", originator);
616   if (dualstack) {
617     _print_json_string(session, "dualstack_id", _get_node_id_me(&node_id1, other_af));
618     _print_json_string(session, "dualstack_topology", _create_domain_id(&dbuf, domain, other_af));
619     _print_json_netaddr(session, "dualstack_addr", dualstack);
620   }
621   json_end_object(session);
622
623   json_start_array(session, "nodes");
624
625   /* local node */
626   _print_graph_node_me(session, af_type);
627
628   /* locally attached networks */
629   avl_for_each_element(olsrv2_lan_get_tree(), lan, _node) {
630     if (netaddr_get_address_family(&lan->prefix.dst) == af_type && olsrv2_lan_get_domaindata(domain, lan)->active) {
631       _print_graph_node_lan(session, lan);
632     }
633   }
634
635   /* originators of all other nodes */
636   avl_for_each_element(olsrv2_tc_get_tree(), node, _originator_node) {
637     if (netaddr_get_address_family(&node->target.prefix.dst) == af_type) {
638       if (netaddr_cmp(&node->target.prefix.dst, originator) == 0) {
639         continue;
640       }
641
642       _print_graph_node_tc(session, node);
643
644       /* attached networks */
645       avl_for_each_element(&node->_attached_networks, attached, _src_node) {
646         _print_graph_node_attached(session, attached);
647       }
648     }
649   }
650   json_end_array(session);
651
652   json_start_array(session, "links");
653
654   rt_tree = olsrv2_routing_get_tree(domain);
655
656   /* print local links to neighbors */
657   _get_node_id_me(&node_id1, af_type);
658
659   avl_for_each_element(nhdp_db_get_neigh_originator_tree(), neigh, _originator_node) {
660     if (netaddr_get_address_family(&neigh->originator) == af_type && neigh->symmetric > 0) {
661       os_routing_init_sourcespec_prefix(&routekey, &neigh->originator);
662
663       rt_entry = avl_find_element(rt_tree, &routekey, rt_entry, _node);
664       outgoing = rt_entry != NULL && netaddr_cmp(&rt_entry->last_originator, originator) == 0;
665
666       _get_nhdp_neighbor_id(&node_id2, neigh);
667
668       _print_graph_edge(session, domain, &node_id1, &node_id2, originator, &neigh->originator,
669         nhdp_domain_get_neighbordata(domain, neigh)->metric.out, nhdp_domain_get_neighbordata(domain, neigh)->metric.in,
670         0, outgoing, NETJSON_EDGE_LOCAL, neigh);
671
672       _print_graph_edge(session, domain, &node_id2, &node_id1, &neigh->originator, originator,
673         nhdp_domain_get_neighbordata(domain, neigh)->metric.in, nhdp_domain_get_neighbordata(domain, neigh)->metric.out,
674         0, false, NETJSON_EDGE_ROUTERS, NULL);
675     }
676   }
677
678   /* print local endpoints */
679   avl_for_each_element(olsrv2_lan_get_tree(), lan, _node) {
680     if (netaddr_get_address_family(&lan->prefix.dst) == af_type && olsrv2_lan_get_domaindata(domain, lan)->active) {
681       rt_entry = avl_find_element(rt_tree, &lan->prefix, rt_entry, _node);
682       outgoing = rt_entry == NULL;
683
684       _get_tc_lan_id(&node_id2, lan);
685
686       _print_graph_edge(session, domain, &node_id1, &node_id2, originator, &lan->prefix.dst,
687         olsrv2_lan_get_domaindata(domain, lan)->outgoing_metric, 0, olsrv2_lan_get_domaindata(domain, lan)->distance,
688         outgoing, NETJSON_EDGE_LAN, NULL);
689     }
690   }
691
692   /* print remote node links to neighbors */
693   avl_for_each_element(olsrv2_tc_get_tree(), node, _originator_node) {
694     if (netaddr_get_address_family(&node->target.prefix.dst) == af_type) {
695       _get_tc_node_id(&node_id1, node);
696
697       avl_for_each_element(&node->_edges, edge, _node) {
698         if (!edge->virtual) {
699           if (netaddr_cmp(&edge->dst->target.prefix.dst, originator) == 0) {
700             /* we already have this information from NHDP */
701             continue;
702           }
703
704           rt_entry = avl_find_element(rt_tree, &edge->dst->target.prefix, rt_entry, _node);
705           outgoing = rt_entry != NULL && netaddr_cmp(&rt_entry->last_originator, &node->target.prefix.dst) == 0;
706
707           _get_tc_node_id(&node_id2, edge->dst);
708
709           _print_graph_edge(session, domain, &node_id1, &node_id2, &node->target.prefix.dst,
710             &edge->dst->target.prefix.dst, edge->cost[domain->index], edge->inverse->cost[domain->index], 0, outgoing,
711             NETJSON_EDGE_ROUTERS, NULL);
712         }
713       }
714     }
715   }
716
717   /* print remote nodes neighbors */
718   avl_for_each_element(olsrv2_tc_get_tree(), node, _originator_node) {
719     if (netaddr_get_address_family(&node->target.prefix.dst) == af_type) {
720       _get_tc_node_id(&node_id1, node);
721
722       avl_for_each_element(&node->_attached_networks, attached, _src_node) {
723         rt_entry = avl_find_element(rt_tree, &attached->dst->target.prefix, rt_entry, _node);
724         outgoing = rt_entry != NULL && netaddr_cmp(&rt_entry->originator, &node->target.prefix.dst) == 0;
725
726         _get_tc_endpoint_id(&node_id2, attached);
727
728         _print_graph_edge(session, domain, &node_id1, &node_id2, &node->target.prefix.dst,
729           &attached->dst->target.prefix.dst, attached->cost[domain->index], 0, attached->distance[domain->index],
730           outgoing, NETJSON_EDGE_ATTACHED, NULL);
731       }
732     }
733   }
734   json_end_array(session);
735
736   json_end_object(session);
737 }
738
739 /**
740  * Print all JSON graph objects
741  * @param session json session
742  * @param filter domain filter
743  */
744 static void
745 _create_graph_json(struct json_session *session, const char *filter) {
746   struct nhdp_domain *domain;
747   struct domain_id_str dbuf;
748
749   list_for_each_element(nhdp_domain_get_list(), domain, _node) {
750     if (filter == NULL || strcmp(_create_domain_id(&dbuf, domain, AF_INET), filter) == 0) {
751       _print_graph(session, domain, AF_INET);
752     }
753     if (filter == NULL || strcmp(_create_domain_id(&dbuf, domain, AF_INET6), filter) == 0) {
754       _print_graph(session, domain, AF_INET6);
755     }
756   }
757 }
758
759 /**
760  * Print the JSON routing tree
761  * @param session json session
762  * @param domain NHDP domain
763  * @param af_type address family
764  */
765 static void
766 _print_routing_tree(struct json_session *session, struct nhdp_domain *domain, int af_type) {
767   struct olsrv2_routing_entry *rtentry;
768   const struct netaddr *originator;
769   char ibuf[IF_NAMESIZE];
770   struct nhdp_metric_str mbuf;
771   struct domain_id_str dbuf;
772   struct _node_id_str idbuf;
773
774   originator = olsrv2_originator_get(af_type);
775   if (netaddr_get_address_family(originator) != af_type) {
776     return;
777   }
778
779   json_start_object(session, NULL);
780
781   _print_json_string(session, "type", "NetworkRoutes");
782   _print_json_string(session, "protocol", "olsrv2");
783   _print_json_string(session, "version", oonf_log_get_libdata()->version);
784   _print_json_string(session, "revision", oonf_log_get_libdata()->git_commit);
785
786   _get_node_id_me(&idbuf, af_type);
787   _print_json_string(session, "router_id", idbuf.buf);
788   _print_json_string(session, "metric", domain->metric->name);
789   _print_json_string(session, "topology_id", _create_domain_id(&dbuf, domain, af_type));
790
791   json_start_object(session, "properties");
792   _print_json_netaddr(session, "router_addr", originator);
793   json_end_object(session);
794
795   json_start_array(session, JSON_NAME_ROUTE);
796
797   avl_for_each_element(olsrv2_routing_get_tree(domain), rtentry, _node) {
798     if (rtentry->route.p.family == af_type) {
799       json_start_object(session, NULL);
800
801       _print_json_netaddr(session, "destination", &rtentry->route.p.key.dst);
802
803       if (netaddr_get_prefix_length(&rtentry->route.p.key.src) > 0) {
804         _print_json_netaddr(session, "source", &rtentry->route.p.key.src);
805       }
806
807       _get_node_id(&idbuf, &rtentry->next_originator, NULL);
808       _print_json_netaddr(session, "next", &rtentry->route.p.gw);
809
810       _print_json_string(session, "device", if_indextoname(rtentry->route.p.if_index, ibuf));
811       _print_json_number(session, "cost", rtentry->path_cost);
812       _print_json_string(
813         session, "cost_text", nhdp_domain_get_path_metric_value(&mbuf, domain, rtentry->path_cost, rtentry->path_hops));
814
815       json_start_object(session, "properties");
816       if (!netaddr_is_unspec(&rtentry->originator)) {
817         _get_node_id(&idbuf, &rtentry->originator, NULL);
818         _print_json_string(session, "destination_id", idbuf.buf);
819       }
820       _print_json_string(session, "next_router_id", idbuf.buf);
821       _print_json_netaddr(session, "next_router_addr", &rtentry->next_originator);
822
823       _print_json_number(session, "hops", rtentry->path_hops);
824
825       _get_node_id(&idbuf, &rtentry->last_originator, NULL);
826       _print_json_string(session, "last_router_id", idbuf.buf);
827       _print_json_netaddr(session, "last_router_addr", &rtentry->last_originator);
828       json_end_object(session);
829
830       json_end_object(session);
831     }
832   }
833
834   json_end_array(session);
835   json_end_object(session);
836 }
837
838 /**
839  * Print all JSON routes
840  * @param session json session
841  * @param filter filter value to select domain
842  */
843 static void
844 _create_route_json(struct json_session *session, const char *filter) {
845   struct nhdp_domain *domain;
846   struct domain_id_str dbuf;
847
848   list_for_each_element(nhdp_domain_get_list(), domain, _node) {
849     if (filter == NULL || strcmp(_create_domain_id(&dbuf, domain, AF_INET), filter) == 0) {
850       _print_routing_tree(session, domain, AF_INET);
851     }
852     if (filter == NULL || strcmp(_create_domain_id(&dbuf, domain, AF_INET6), filter) == 0) {
853       _print_routing_tree(session, domain, AF_INET6);
854     }
855   }
856 }
857
858 static void
859 _create_domain_json(struct json_session *session) {
860   const struct netaddr *originator_v4, *originator_v6;
861   struct nhdp_domain *domain;
862   struct domain_id_str dbuf;
863
864   originator_v4 = olsrv2_originator_get(AF_INET);
865   originator_v6 = olsrv2_originator_get(AF_INET6);
866
867   json_start_object(session, NULL);
868
869   _print_json_string(session, "type", "NetworkDomain");
870   _print_json_string(session, "protocol", "olsrv2");
871   _print_json_string(session, "version", oonf_log_get_libdata()->version);
872   _print_json_string(session, "revision", oonf_log_get_libdata()->git_commit);
873
874   json_start_array(session, JSON_NAME_DOMAIN);
875
876   list_for_each_element(nhdp_domain_get_list(), domain, _node) {
877     if (!netaddr_is_unspec(originator_v4)) {
878       json_start_object(session, NULL);
879
880       _print_json_string(session, "id", _create_domain_id(&dbuf, domain, AF_INET));
881       _print_json_number(session, "number", domain->ext);
882       _print_json_netaddr(session, "router_id", originator_v4);
883       _print_json_string(session, "metric", domain->metric->name);
884       _print_json_string(session, "mpr", domain->mpr->name);
885
886       json_end_object(session);
887     }
888
889     if (!netaddr_is_unspec(originator_v6)) {
890       json_start_object(session, NULL);
891
892       _print_json_string(session, "id", _create_domain_id(&dbuf, domain, AF_INET6));
893       _print_json_number(session, "number", domain->ext);
894       _print_json_netaddr(session, "router_id", originator_v6);
895       _print_json_string(session, "metric", domain->metric->name);
896       _print_json_string(session, "mpr", domain->mpr->name);
897
898       json_end_object(session);
899     }
900   }
901
902   json_end_array(session);
903   json_end_object(session);
904 }
905
906 /**
907  * Print a JSON error
908  * @param session json session
909  * @param message error message
910  * @param parameter error parameter
911  */
912 static void
913 _create_error_json(struct json_session *session, const char *message, const char *parameter) {
914   json_start_object(session, NULL);
915
916   _print_json_string(session, "type", "Error");
917   _print_json_string(session, "message", message);
918   _print_json_string(session, "parameter", parameter);
919
920   json_end_object(session);
921 }
922
923 static const char *
924 _handle_netjson_object(struct json_session *session, const char *parameter, bool filter, bool *error) {
925   const char *ptr;
926
927   if ((ptr = str_hasnextword(parameter, JSON_NAME_GRAPH))) {
928     _create_graph_json(session, filter ? ptr : NULL);
929   }
930   else if ((ptr = str_hasnextword(parameter, JSON_NAME_ROUTE))) {
931     _create_route_json(session, filter ? ptr : NULL);
932   }
933   else if (!filter && (ptr = str_hasnextword(parameter, JSON_NAME_DOMAIN))) {
934     _create_domain_json(session);
935   }
936   else {
937     ptr = str_skipnextword(parameter);
938     *error = true;
939   }
940   return ptr;
941 }
942
943 static void
944 _handle_filter(struct json_session *session, const char *parameter) {
945   bool error = false;
946
947   _handle_netjson_object(session, parameter, true, &error);
948   if (error) {
949     _create_error_json(session, "Could not parse sub-command for netjsoninfo", parameter);
950   }
951 }
952
953 static void
954 _handle_collection(struct json_session *session, const char *parameter) {
955   const char *next;
956   bool error;
957
958   json_start_object(session, NULL);
959   _print_json_string(session, "type", "NetworkCollection");
960   json_start_array(session, "collection");
961
962   error = 0;
963   next = parameter;
964   while (next && *next) {
965     next = _handle_netjson_object(session, next, false, &error);
966   }
967
968   if (error) {
969     _create_error_json(session, "Could not parse sub-command for netjsoninfo", parameter);
970   }
971
972   json_end_array(session);
973   json_end_object(session);
974 }
975
976 /**
977  * Callback for netjsoninfo telnet command
978  * @param con telnet connection
979  * @return active or internal_error
980  */
981 static enum oonf_telnet_result
982 _cb_netjsoninfo(struct oonf_telnet_data *con) {
983   struct json_session session;
984   struct autobuf out;
985   const char *ptr, *next;
986
987   if (abuf_init(&out)) {
988     return TELNET_RESULT_INTERNAL_ERROR;
989   }
990
991   json_init_session(&session, &out);
992
993   next = con->parameter;
994   if (next && *next) {
995     if ((ptr = str_hasnextword(next, JSON_NAME_FILTER))) {
996       _handle_filter(&session, ptr);
997     }
998     else {
999       _handle_collection(&session, next);
1000     }
1001   }
1002
1003   /* copy output into telnet buffer */
1004   abuf_memcpy(con->out, abuf_getptr(&out), abuf_getlen(&out));
1005   abuf_free(&out);
1006   return TELNET_RESULT_ACTIVE;
1007 }
1008
1009 /**
1010  * Helper to print a json string
1011  * @param session json session
1012  * @param key json key
1013  * @param value json string value
1014  */
1015 static void
1016 _print_json_string(struct json_session *session, const char *key, const char *value) {
1017   json_print(session, key, true, value);
1018 }
1019
1020 /**
1021  * Helper to print a json number
1022  * @param session json session
1023  * @param key json key
1024  * @param value number
1025  */
1026 static void
1027 _print_json_number(struct json_session *session, const char *key, uint64_t value) {
1028   char buffer[21];
1029
1030   snprintf(buffer, sizeof(buffer), "%" PRIu64, value);
1031   json_print(session, key, false, buffer);
1032 }
1033
1034 /**
1035  * Helper function to print a json netaddr object
1036  * @param session json session
1037  * @param key json key
1038  * @param addr address
1039  */
1040 static void
1041 _print_json_netaddr(struct json_session *session, const char *key, const struct netaddr *addr) {
1042   struct netaddr_str nbuf;
1043
1044   json_print(session, key, true, netaddr_to_string(&nbuf, addr));
1045 }