2 * The olsr.org Optimized Link-State Routing daemon(olsrd)
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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.
34 * Visit http://www.olsr.org for more information.
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.
43 * Dynamic linked library for the olsr.org olsr daemon
46 #include <arpa/inet.h>
53 #include "scheduler.h"
54 #include "../../info/http_headers.h"
55 #include "jsoninfo_printers.h"
56 #include "olsrd_jsoninfo_helpers.h"
57 #include "olsrd_jsoninfo.h"
60 #define close(x) closesocket(x)
63 /* defines to make txtinfo and jsoninfo look alike */
64 #define PLUGIN_NAME "JSONINFO"
65 #define info_accept_ip jsoninfo_accept_ip
66 #define info_listen_ip jsoninfo_listen_ip
67 #define info_ipv6_only jsoninfo_ipv6_only
68 #ifdef JSONINFO_ALLOW_LOCALHOST
69 #define INFO_ALLOW_LOCALHOST JSONINFO_ALLOW_LOCALHOST
72 static int ipc_socket;
74 /* IPC initialization function */
75 static int plugin_ipc_init(void);
77 static void send_info(unsigned int /*send_what*/, int /*socket*/);
79 static void ipc_action(int, void *, unsigned int);
81 #define TXT_IPC_BUFSIZE 256
83 /* these provide all of the runtime status info */
84 #define SIW_NEIGHBORS 0x0001
85 #define SIW_LINKS 0x0002
86 #define SIW_ROUTES 0x0004
87 #define SIW_HNA 0x0008
88 #define SIW_MID 0x0010
89 #define SIW_TOPOLOGY 0x0020
90 #define SIW_GATEWAYS 0x0040
91 #define SIW_INTERFACES 0x0080
92 #define SIW_2HOP 0x0100
93 #define SIW_SGW 0x0200
94 #define SIW_RUNTIME_ALL (SIW_NEIGHBORS | SIW_LINKS | SIW_ROUTES | SIW_HNA | SIW_MID | SIW_TOPOLOGY | SIW_GATEWAYS | SIW_INTERFACES | SIW_2HOP | SIW_SGW)
96 /* these only change at olsrd startup */
97 #define SIW_VERSION 0x0400
98 #define SIW_CONFIG 0x0800
99 #define SIW_PLUGINS 0x1000
100 #define SIW_STARTUP_ALL (SIW_VERSION | SIW_CONFIG | SIW_PLUGINS)
102 /* this is everything in normal format */
103 #define SIW_ALL (SIW_RUNTIME_ALL | SIW_STARTUP_ALL)
105 /* this data is not normal format but olsrd.conf format */
106 #define SIW_OLSRD_CONF 0x2000
108 #define MAX_CLIENTS 3
110 static char *outbuffer[MAX_CLIENTS];
111 static size_t outbuffer_size[MAX_CLIENTS];
112 static size_t outbuffer_written[MAX_CLIENTS];
113 static int outbuffer_socket[MAX_CLIENTS];
114 static int outbuffer_count = 0;
116 static struct timer_entry *writetimer_entry;
118 static void determine_action(unsigned int *send_what, char *requ) {
119 if (strstr(requ, "/olsrd.conf"))
120 *send_what |= SIW_OLSRD_CONF;
121 else if (strstr(requ, "/all"))
122 *send_what = SIW_ALL;
124 // these are the two overarching categories
125 if (strstr(requ, "/runtime"))
126 *send_what |= SIW_RUNTIME_ALL;
127 if (strstr(requ, "/startup"))
128 *send_what |= SIW_STARTUP_ALL;
130 // these are the individual sections
131 if (strstr(requ, "/neighbors"))
132 *send_what |= SIW_NEIGHBORS;
133 if (strstr(requ, "/links"))
134 *send_what |= SIW_LINKS;
135 if (strstr(requ, "/routes"))
136 *send_what |= SIW_ROUTES;
137 if (strstr(requ, "/hna"))
138 *send_what |= SIW_HNA;
139 if (strstr(requ, "/mid"))
140 *send_what |= SIW_MID;
141 if (strstr(requ, "/topology"))
142 *send_what |= SIW_TOPOLOGY;
143 if (strstr(requ, "/gateways"))
144 *send_what |= SIW_GATEWAYS;
145 if (strstr(requ, "/interfaces"))
146 *send_what |= SIW_INTERFACES;
147 if (strstr(requ, "/2hop"))
148 *send_what |= SIW_2HOP;
149 if (strstr(requ, "/sgw"))
150 *send_what |= SIW_SGW;
153 if (strstr(requ, "/version"))
154 *send_what |= SIW_VERSION;
155 if (strstr(requ, "/config"))
156 *send_what |= SIW_CONFIG;
157 if (strstr(requ, "/plugins"))
158 *send_what |= SIW_PLUGINS;
160 /* To print out neighbours only on the Freifunk Status
161 * page the normal output is somewhat lengthy. The
162 * header parsing is sufficient for standard wget.
164 if (strstr(requ, "/neighbours"))
165 *send_what = SIW_NEIGHBORS | SIW_LINKS;
170 *Do initialization here
172 *This function is called by the my_init
173 *function in uolsrd_plugin.c
175 int olsrd_plugin_init(void) {
176 /* Initial IPC value */
179 plugin_init(PLUGIN_NAME);
186 * destructor - called at unload
188 void olsr_plugin_exit(void) {
189 if (ipc_socket != -1)
193 static int plugin_ipc_init(void) {
194 union olsr_sockaddr sst;
198 /* Init ipc socket */
199 if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
201 olsr_printf(1, "("PLUGIN_NAME") socket()=%s\n", strerror(errno));
205 if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
207 olsr_printf(1, "("PLUGIN_NAME") setsockopt()=%s\n", strerror(errno));
211 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
212 if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
213 perror("SO_REUSEADDR failed");
216 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
217 #if defined linux && defined IPV6_V6ONLY
218 if (info_ipv6_only && olsr_cnf->ip_version == AF_INET6) {
219 if (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0) {
220 perror("IPV6_V6ONLY failed");
224 #endif /* defined linux && defined IPV6_V6ONLY */
225 /* Bind the socket */
227 /* complete the socket structure */
228 memset(&sst, 0, sizeof(sst));
229 if (olsr_cnf->ip_version == AF_INET) {
230 sst.in4.sin_family = AF_INET;
231 addrlen = sizeof(struct sockaddr_in);
233 sst.in4.sin_len = addrlen;
234 #endif /* SIN6_LEN */
235 sst.in4.sin_addr.s_addr = info_listen_ip.v4.s_addr;
236 sst.in4.sin_port = htons(ipc_port);
238 sst.in6.sin6_family = AF_INET6;
239 addrlen = sizeof(struct sockaddr_in6);
241 sst.in6.sin6_len = addrlen;
242 #endif /* SIN6_LEN */
243 sst.in6.sin6_addr = info_listen_ip.v6;
244 sst.in6.sin6_port = htons(ipc_port);
247 /* bind the socket to the port number */
248 if (bind(ipc_socket, &sst.in, addrlen) == -1) {
250 olsr_printf(1, "("PLUGIN_NAME") bind()=%s\n", strerror(errno));
255 /* show that we are willing to listen */
256 if (listen(ipc_socket, 1) == -1) {
258 olsr_printf(1, "("PLUGIN_NAME") listen()=%s\n", strerror(errno));
263 /* Register with olsrd */
264 add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
267 olsr_printf(2, "("PLUGIN_NAME") listening on port %d\n", ipc_port);
273 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
274 union olsr_sockaddr pin;
276 char addr[INET6_ADDRSTRLEN];
279 unsigned int send_what = 0;
282 socklen_t addrlen = sizeof(pin);
284 if (outbuffer_count >= MAX_CLIENTS) {
288 if ((ipc_connection = accept(fd, &pin.in, &addrlen)) == -1) {
290 olsr_printf(1, "("PLUGIN_NAME") accept()=%s\n", strerror(errno));
295 tv.tv_sec = tv.tv_usec = 0;
296 if (olsr_cnf->ip_version == AF_INET) {
297 if (inet_ntop(olsr_cnf->ip_version, &pin.in4.sin_addr, addr, INET6_ADDRSTRLEN) == NULL)
299 if (!ip4equal(&pin.in4.sin_addr, &info_accept_ip.v4) && info_accept_ip.v4.s_addr != INADDR_ANY) {
300 #ifdef INFO_ALLOW_LOCALHOST
301 if (ntohl(pin.in4.sin_addr.s_addr) != INADDR_LOOPBACK) {
302 #endif /* INFO_ALLOW_LOCALHOST */
303 olsr_printf(1, "("PLUGIN_NAME") From host(%s) not allowed!\n", addr);
304 close(ipc_connection);
306 #ifdef INFO_ALLOW_LOCALHOST
308 #endif /* INFO_ALLOW_LOCALHOST */
311 if (inet_ntop(olsr_cnf->ip_version, &pin.in6.sin6_addr, addr, INET6_ADDRSTRLEN) == NULL)
313 /* Use in6addr_any (::) in olsr.conf to allow anybody. */
314 if (!ip6equal(&in6addr_any, &info_accept_ip.v6) && !ip6equal(&pin.in6.sin6_addr, &info_accept_ip.v6)) {
315 olsr_printf(1, "("PLUGIN_NAME") From host(%s) not allowed!\n", addr);
316 close(ipc_connection);
322 olsr_printf(2, "("PLUGIN_NAME") Connect from %s\n", addr);
325 /* purge read buffer to prevent blocking on linux */
327 FD_SET((unsigned int )ipc_connection, &rfds); /* Win32 needs the cast here */
328 if (0 <= select(ipc_connection + 1, &rfds, NULL, NULL, &tv)) {
330 ssize_t s = recv(ipc_connection, (void *) &requ, sizeof(requ) - 1, 0); /* Win32 needs the cast here */
332 if (s == sizeof(requ) - 1) {
333 /* input was much too long, just skip the rest */
336 while (recv(ipc_connection, (void *) &dummy, sizeof(dummy), 0) == sizeof(dummy))
342 determine_action(&send_what, requ);
349 send_info(send_what, ipc_connection);
352 static void info_write_data(void *foo __attribute__ ((unused))) {
354 int result, i, j, max;
359 for (i = 0; i < outbuffer_count; i++) {
360 /* And we cast here since we get a warning on Win32 */
361 FD_SET((unsigned int )(outbuffer_socket[i]), &set);
363 if (outbuffer_socket[i] > max) {
364 max = outbuffer_socket[i];
371 result = select(max + 1, NULL, &set, NULL, &tv);
376 for (i = 0; i < outbuffer_count; i++) {
377 if (FD_ISSET(outbuffer_socket[i], &set)) {
378 result = send(outbuffer_socket[i], outbuffer[i] + outbuffer_written[i], outbuffer_size[i] - outbuffer_written[i], 0);
380 outbuffer_written[i] += result;
383 if (result <= 0 || outbuffer_written[i] == outbuffer_size[i]) {
384 /* close this socket and cleanup*/
385 close(outbuffer_socket[i]);
388 for (j = i + 1; j < outbuffer_count; j++) {
389 outbuffer[j - 1] = outbuffer[j];
390 outbuffer_size[j - 1] = outbuffer_size[j];
391 outbuffer_socket[j - 1] = outbuffer_socket[j];
392 outbuffer_written[j - 1] = outbuffer_written[j];
398 if (!outbuffer_count) {
399 olsr_stop_timer(writetimer_entry);
403 static void send_info(unsigned int send_what, int the_socket) {
406 const char *content_type = (send_what & SIW_ALL) ? "application/json; charset=utf-8" : "text/plain; charset=utf-8";
407 int contentLengthPlaceholderStart = 0;
408 int headerLength = 0;
410 /* global variables for tracking when to put a comma in for JSON */
411 abuf_json_reset_entry_number_and_depth();
413 abuf_init(&abuf, 2 * 4096);
416 build_http_header(PLUGIN_NAME, HTTP_200, content_type, &abuf, &contentLengthPlaceholderStart);
417 headerLength = abuf.len;
420 // only add if normal format
421 if (send_what & SIW_ALL) {
422 abuf_json_mark_output(true, &abuf);
424 abuf_json_int(&abuf, "systemTime", time(NULL));
425 abuf_json_int(&abuf, "timeSinceStartup", now_times);
427 abuf_json_string(&abuf, "uuid", uuid);
429 if (send_what & SIW_LINKS)
430 ipc_print_links(&abuf);
431 if (send_what & SIW_NEIGHBORS)
432 ipc_print_neighbors(&abuf, false);
433 if (send_what & SIW_TOPOLOGY)
434 ipc_print_topology(&abuf);
435 if (send_what & SIW_HNA)
436 ipc_print_hna(&abuf);
437 if (send_what & SIW_SGW)
438 ipc_print_sgw(&abuf);
439 if (send_what & SIW_MID)
440 ipc_print_mid(&abuf);
441 if (send_what & SIW_ROUTES)
442 ipc_print_routes(&abuf);
443 if (send_what & SIW_GATEWAYS)
444 ipc_print_gateways(&abuf);
445 if (send_what & SIW_CONFIG)
446 ipc_print_config(&abuf);
447 if (send_what & SIW_INTERFACES)
448 ipc_print_interfaces(&abuf);
449 if (send_what & SIW_2HOP)
450 ipc_print_neighbors(&abuf, true);
451 if (send_what & SIW_VERSION)
452 ipc_print_version(&abuf);
453 if (send_what & SIW_PLUGINS)
454 ipc_print_plugins(&abuf);
456 abuf_json_mark_output(false, &abuf);
457 abuf_puts(&abuf, "\n");
458 } else if (send_what & SIW_OLSRD_CONF) {
459 /* this outputs the olsrd.conf text directly, not normal format */
460 ipc_print_olsrd_conf(&abuf);
464 http_header_adjust_content_length(&abuf, contentLengthPlaceholderStart, abuf.len - headerLength);
467 /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
468 outbuffer[outbuffer_count] = abuf.buf;
469 outbuffer_size[outbuffer_count] = abuf.len;
470 outbuffer_written[outbuffer_count] = 0;
471 outbuffer_socket[outbuffer_count] = the_socket;
478 if (outbuffer_count == 1) {
479 writetimer_entry = olsr_start_timer(100, 0, OLSR_TIMER_PERIODIC, &info_write_data, NULL, 0);
490 * indent-tabs-mode: nil