netjson: fix NetworkGraph output
[olsrd.git] / lib / netjson / src / olsrd_netjson.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon (olsrd)
3  *
4  * (c) by the OLSR project
5  *
6  * See our Git repository to find out who worked on this file
7  * and thus is a copyright holder on it.
8  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * * Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  * * Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in
19  *   the documentation and/or other materials provided with the
20  *   distribution.
21  * * Neither the name of olsr.org, olsrd nor the names of its
22  *   contributors may be used to endorse or promote products derived
23  *   from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *
38  * Visit http://www.olsr.org for more information.
39  *
40  * If you find this software useful feel free to make a donation
41  * to the project. For more information see the website or contact
42  * the copyright holders.
43  *
44  */
45
46 #include "olsrd_netjson.h"
47
48 #include <unistd.h>
49
50 #include "olsrd_netjson_helpers.h"
51 #include "info/info_types.h"
52 #include "info/http_headers.h"
53 #include "info/json_helpers.h"
54 #include "common/avl.h"
55 #include "olsr.h"
56 #include "builddata.h"
57 #include "routing_table.h"
58 #include "mid_set.h"
59 #include "link_set.h"
60
61 #define NETJSON_PROTOCOL "olsrv1"
62
63 static struct json_session json_session;
64
65 unsigned long long get_supported_commands_mask(void) {
66   return SIW_NETJSON & ~(SIW_NETJSON_DEVICE_CONFIGURATION | SIW_NETJSON_DEVICE_MONITORING);
67 }
68
69 bool isCommand(const char *str, unsigned long long siw) {
70   const char * cmd = NULL;
71   switch (siw) {
72     case SIW_NETJSON_NETWORK_ROUTES:
73       cmd = "/NetworkRoutes";
74       break;
75
76     case SIW_NETJSON_NETWORK_GRAPH:
77       cmd = "/NetworkGraph";
78       break;
79
80     case SIW_NETJSON_NETWORK_COLLECTION:
81       cmd = "/NetworkCollection";
82       break;
83
84     default:
85       return false;
86   }
87
88   return !strcmp(str, cmd);
89 }
90
91 const char * determine_mime_type(unsigned int send_what __attribute__((unused))) {
92   return "application/json; charset=utf-8";
93 }
94
95 void output_start(struct autobuf *abuf) {
96   /* global variables for tracking when to put a comma in for JSON */
97   abuf_json_reset_entry_number_and_depth(&json_session);
98   abuf_json_mark_output(&json_session, true, abuf);
99 }
100
101 void output_end(struct autobuf *abuf) {
102   abuf_json_mark_output(&json_session, false, abuf);
103   abuf_puts(abuf, "\n");
104   abuf_json_reset_entry_number_and_depth(&json_session);
105 }
106
107 void output_error(struct autobuf *abuf, unsigned int status, const char * req __attribute__((unused)), bool http_headers) {
108   if (http_headers || (status == INFO_HTTP_OK)) {
109     return;
110   }
111
112   /* !http_headers && !INFO_HTTP_OK */
113
114   output_start(abuf);
115
116   if (status == INFO_HTTP_NOCONTENT) {
117     /* do nothing */
118   } else {
119     abuf_json_string(&json_session, abuf, "error", httpStatusToReply(status));
120   }
121
122   output_end(abuf);
123 }
124
125 void ipc_print_network_routes(struct autobuf *abuf) {
126   struct rt_entry *rt;
127
128   /* mandatory */
129   abuf_json_string(&json_session, abuf, "type", "NetworkRoutes");
130   abuf_json_string(&json_session, abuf, "protocol", NETJSON_PROTOCOL);
131   abuf_json_string(&json_session, abuf, "version", release_version);
132   abuf_json_string(&json_session, abuf, "metric", olsr_cnf->lq_algorithm);
133
134   /* optional */
135   abuf_json_string(&json_session, abuf, "revision", olsrd_version);
136   // topology_id
137   abuf_json_ip_address(&json_session, abuf, "router_id", &olsr_cnf->main_addr);
138
139   /* Walk the route table */
140   abuf_json_mark_object(&json_session, true, true, abuf, "routes");
141   OLSR_FOR_ALL_RT_ENTRIES(rt) {
142     if (rt->rt_best) {
143       struct lqtextbuffer lqbuf;
144
145       abuf_json_mark_array_entry(&json_session, true, abuf);
146       /* mandatory */
147       abuf_json_prefix(&json_session, abuf, "destination", &rt->rt_dst);
148       abuf_json_ip_address(&json_session, abuf, "next", &rt->rt_best->rtp_nexthop.gateway);
149       abuf_json_string(&json_session, abuf, "device", if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index));
150       abuf_json_float(&json_session, abuf, "cost", get_linkcost_scaled(rt->rt_best->rtp_metric.cost, true));
151
152       /* optional */
153       abuf_json_string(&json_session, abuf, "cost_text", get_linkcost_text(rt->rt_best->rtp_metric.cost, true, &lqbuf));
154       // source
155
156       abuf_json_mark_array_entry(&json_session, false, abuf);
157     }
158   } OLSR_FOR_ALL_RT_ENTRIES_END(rt);
159   abuf_json_mark_object(&json_session, false, true, abuf, NULL);
160 }
161
162 void ipc_print_network_graph(struct autobuf *abuf) {
163   struct avl_tree nodes;
164   struct mid_entry mid_self;
165   struct node_entry * node_self;
166   struct tc_entry * tc;
167   struct link_entry * link_entry;
168   struct neighbor_entry * neighbor;
169   int idx;
170
171   avl_init(&nodes, (olsr_cnf->ip_version == AF_INET) ? avl_comp_ipv4 : avl_comp_ipv6);
172
173   /* mandatory */
174   abuf_json_string(&json_session, abuf, "type", "NetworkGraph");
175   abuf_json_string(&json_session, abuf, "protocol", NETJSON_PROTOCOL);
176   abuf_json_string(&json_session, abuf, "version", release_version);
177   abuf_json_string(&json_session, abuf, "metric", olsr_cnf->lq_algorithm);
178
179   /* optional */
180   abuf_json_string(&json_session, abuf, "revision", olsrd_version);
181   // topology_id
182   abuf_json_ip_address(&json_session, abuf, "router_id", &olsr_cnf->main_addr);
183   // label
184
185   /*
186    * Collect all nodes
187    */
188
189   /* MID Self */
190   node_self = netjson_constructMidSelf(&mid_self);
191   netjson_midIntoNodesTree(&nodes, &mid_self);
192
193   /* MID */
194   for (idx = 0; idx < HASHSIZE; idx++) {
195     struct mid_entry * entry = mid_set[idx].next;
196     while (entry != &mid_set[idx]) {
197       netjson_midIntoNodesTree(&nodes, entry);
198       entry = entry->next;
199     }
200   }
201
202   /* TC */
203   OLSR_FOR_ALL_TC_ENTRIES(tc) {
204     struct tc_edge_entry *tc_edge;
205     netjson_tcIntoNodesTree(&nodes, tc);
206     OLSR_FOR_ALL_TC_EDGE_ENTRIES(tc, tc_edge) {
207      netjson_tcEdgeIntoNodesTree(&nodes, tc_edge);
208     } OLSR_FOR_ALL_TC_EDGE_ENTRIES_END(tc, tc_edge);
209   } OLSR_FOR_ALL_TC_ENTRIES_END(tc);
210
211   /* LINKS */
212   OLSR_FOR_ALL_LINK_ENTRIES(link_entry) {
213     netjson_linkIntoNodesTree(&nodes, link_entry, &link_entry->local_iface_addr);
214     netjson_linkIntoNodesTree(&nodes, link_entry, &link_entry->neighbor_iface_addr);
215   } OLSR_FOR_ALL_LINK_ENTRIES_END(link_entry);
216
217   /* NEIGHBORS */
218   OLSR_FOR_ALL_NBR_ENTRIES(neighbor) {
219     netjson_neighborIntoNodesTree(&nodes, neighbor);
220   } OLSR_FOR_ALL_NBR_ENTRIES_END(neighbor);
221
222   /*
223    * Output Nodes
224    */
225
226   abuf_json_mark_object(&json_session, true, true, abuf, "nodes");
227   while (nodes.count > 0) {
228     struct avl_node *node = avl_walk_first(&nodes);
229     struct node_entry *node_entry = avlnode2node(node);
230
231     if (!node_entry->isAlias) {
232       abuf_json_mark_array_entry(&json_session, true, abuf);
233
234       /* mandatory */
235       abuf_json_ip_address(&json_session, abuf, "id", node->key);
236
237       /* optional */
238       // label
239       if (node_entry->mid) {
240         struct mid_address * alias = node_entry->mid->aliases;
241         if (alias) {
242           abuf_json_mark_object(&json_session, true, true, abuf, "local_addresses");
243           while (alias) {
244             abuf_json_ip_address(&json_session, abuf, NULL, &alias->alias);
245             alias = alias->next_alias;
246           }
247           abuf_json_mark_object(&json_session, false, true, abuf, NULL);
248         }
249       } else if (node_entry->tc) {
250         /* no local_addresses */
251       } else if (node_entry->tc_edge) {
252         /* no local_addresses */
253       } else if (node_entry->link) {
254         /* no local_addresses */
255       } else if (node_entry->neighbor) {
256         /* no local_addresses */
257       }
258       // properties
259
260       abuf_json_mark_array_entry(&json_session, false, abuf);
261     }
262
263     if (node_entry->mid && (node_entry == node_self)) {
264       netjson_cleanup_mid_self(node_self);
265     }
266
267     avl_delete(&nodes, node);
268     free(node);
269   }
270   abuf_json_mark_object(&json_session, false, true, abuf, NULL);
271
272   /*
273    * Links
274    */
275
276   abuf_json_mark_object(&json_session, true, true, abuf, "links");
277
278   OLSR_FOR_ALL_LINK_ENTRIES(link_entry) {
279     struct lqtextbuffer lqbuf;
280     int link_status = lookup_link_status(link_entry);
281
282     if ((link_status != ASYM_LINK) && (link_status != SYM_LINK)) {
283       continue;
284     }
285
286     /*
287      * link from node to neighbor
288      */
289     if (link_status == SYM_LINK) {
290       abuf_json_mark_array_entry(&json_session, true, abuf);
291
292       /* mandatory */
293       abuf_json_ip_address(&json_session, abuf, "source", &link_entry->local_iface_addr);
294       abuf_json_ip_address(&json_session, abuf, "target", &link_entry->neighbor_iface_addr);
295       abuf_json_float(&json_session, abuf, "cost", get_linkcost_scaled(link_entry->linkcost, false));
296
297       /* optional */
298       abuf_json_string(&json_session, abuf, "cost_text", get_linkcost_text(link_entry->linkcost, false, &lqbuf));
299       // properties
300
301       abuf_json_mark_array_entry(&json_session, false, abuf);
302     }
303
304     /*
305      * link from neighbor to node
306      */
307     abuf_json_mark_array_entry(&json_session, true, abuf);
308
309     /* mandatory */
310     abuf_json_ip_address(&json_session, abuf, "source", &link_entry->neighbor_iface_addr);
311     abuf_json_ip_address(&json_session, abuf, "target", &link_entry->local_iface_addr);
312     abuf_json_float(&json_session, abuf, "cost", get_linkcost_scaled(link_entry->linkcost, false));
313
314     /* optional */
315     abuf_json_string(&json_session, abuf, "cost_text", get_linkcost_text(link_entry->linkcost, false, &lqbuf));
316     // properties
317
318     abuf_json_mark_array_entry(&json_session, false, abuf);
319   } OLSR_FOR_ALL_LINK_ENTRIES_END(my_link);
320
321   OLSR_FOR_ALL_TC_ENTRIES(tc) {
322     struct tc_edge_entry *tc_edge;
323     OLSR_FOR_ALL_TC_EDGE_ENTRIES(tc, tc_edge) {
324       struct lqtextbuffer lqbuf;
325
326       if (ipequal(&olsr_cnf->main_addr, &tc->addr)) {
327         continue;
328       }
329
330       abuf_json_mark_array_entry(&json_session, true, abuf);
331
332       /* mandatory */
333       abuf_json_ip_address(&json_session, abuf, "source", &tc->addr);
334       abuf_json_ip_address(&json_session, abuf, "target", &tc_edge->T_dest_addr);
335       abuf_json_float(&json_session, abuf, "cost", get_linkcost_scaled(tc_edge->cost, false));
336
337       /* optional */
338       abuf_json_string(&json_session, abuf, "cost_text", get_linkcost_text(tc_edge->cost, false, &lqbuf));
339       // properties
340
341       abuf_json_mark_array_entry(&json_session, false, abuf);
342     } OLSR_FOR_ALL_TC_EDGE_ENTRIES_END(tc, tc_edge);
343   } OLSR_FOR_ALL_TC_ENTRIES_END(tc);
344
345   abuf_json_mark_object(&json_session, false, true, abuf, NULL);
346 }
347
348 void ipc_print_network_collection(struct autobuf *abuf) {
349   /* mandatory */
350   abuf_json_string(&json_session, abuf, "type", "NetworkCollection");
351
352   abuf_json_mark_object(&json_session, true, true, abuf, "collection");
353
354   abuf_json_mark_array_entry(&json_session, true, abuf);
355   ipc_print_network_routes(abuf);
356   abuf_json_mark_array_entry(&json_session, false, abuf);
357
358   abuf_json_mark_array_entry(&json_session, true, abuf);
359   ipc_print_network_graph(abuf);
360   abuf_json_mark_array_entry(&json_session, false, abuf);
361
362   abuf_json_mark_object(&json_session, false, true, abuf, NULL);
363 }