e0124407705cc8e3c8843967141f33a12ff67436
[project/rpcd.git] / luci2.c
1 /*
2  * luci-rpcd - LuCI UBUS RPC server
3  *
4  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <fcntl.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <dirent.h>
28 #include <arpa/inet.h>
29 #include <signal.h>
30
31 #include "luci2.h"
32
33 static struct blob_buf buf;
34 static struct uci_context *cursor;
35
36 enum {
37         RPC_S_PID,
38         RPC_S_SIGNAL,
39         __RPC_S_MAX,
40 };
41
42 static const struct blobmsg_policy rpc_signal_policy[__RPC_S_MAX] = {
43         [RPC_S_PID]    = { .name = "pid",    .type = BLOBMSG_TYPE_INT32 },
44         [RPC_S_SIGNAL] = { .name = "signal", .type = BLOBMSG_TYPE_INT32 },
45 };
46
47 enum {
48         RPC_I_NAME,
49         RPC_I_ACTION,
50         __RPC_I_MAX,
51 };
52
53 static const struct blobmsg_policy rpc_init_policy[__RPC_I_MAX] = {
54         [RPC_I_NAME]   = { .name = "name",   .type = BLOBMSG_TYPE_STRING },
55         [RPC_I_ACTION] = { .name = "action", .type = BLOBMSG_TYPE_STRING },
56 };
57
58 enum {
59         RPC_K_KEYS,
60         __RPC_K_MAX
61 };
62
63 static const struct blobmsg_policy rpc_sshkey_policy[__RPC_K_MAX] = {
64         [RPC_K_KEYS]   = { .name = "keys",   .type = BLOBMSG_TYPE_ARRAY },
65 };
66
67
68 static int
69 rpc_errno_status(void)
70 {
71         switch (errno)
72         {
73         case EACCES:
74                 return UBUS_STATUS_PERMISSION_DENIED;
75
76         case ENOTDIR:
77                 return UBUS_STATUS_INVALID_ARGUMENT;
78
79         case ENOENT:
80                 return UBUS_STATUS_NOT_FOUND;
81
82         case EINVAL:
83                 return UBUS_STATUS_INVALID_ARGUMENT;
84
85         default:
86                 return UBUS_STATUS_UNKNOWN_ERROR;
87         }
88 }
89
90 static void
91 log_read(FILE *log, int logsize)
92 {
93         int len;
94         char *logbuf;
95
96         if (logsize == 0)
97                 logsize = RPC_LUCI2_DEF_LOGSIZE;
98
99         len = (logsize > RPC_LUCI2_MAX_LOGSIZE) ? RPC_LUCI2_MAX_LOGSIZE : logsize;
100         logbuf = blobmsg_alloc_string_buffer(&buf, "log", len + 1);
101
102         if (!logbuf)
103                 return;
104
105         while (logsize > RPC_LUCI2_MAX_LOGSIZE)
106         {
107                 len = logsize % RPC_LUCI2_MAX_LOGSIZE;
108
109                 if (len == 0)
110                         len = RPC_LUCI2_MAX_LOGSIZE;
111
112                 fread(logbuf, 1, len, log);
113                 logsize -= len;
114         }
115
116         len = fread(logbuf, 1, logsize, log);
117         *(logbuf + len) = 0;
118
119         blobmsg_add_string_buffer(&buf);
120 }
121
122 static int
123 rpc_luci2_system_log(struct ubus_context *ctx, struct ubus_object *obj,
124                      struct ubus_request_data *req, const char *method,
125                      struct blob_attr *msg)
126 {
127         FILE *log;
128         int logsize = 0;
129         const char *logfile = NULL;
130         struct stat st;
131         struct uci_package *p;
132         struct uci_element *e;
133         struct uci_section *s;
134         struct uci_ptr ptr = { .package = "system" };
135
136         uci_load(cursor, ptr.package, &p);
137
138         if (!p)
139                 return UBUS_STATUS_NOT_FOUND;
140
141         uci_foreach_element(&p->sections, e)
142         {
143                 s = uci_to_section(e);
144
145                 if (strcmp(s->type, "system"))
146                         continue;
147
148                 ptr.o = NULL;
149                 ptr.option = "log_type";
150                 ptr.section = e->name;
151                 uci_lookup_ptr(cursor, &ptr, NULL, true);
152                 break;
153         }
154
155         if (ptr.o && ptr.o->type == UCI_TYPE_STRING &&
156             !strcmp(ptr.o->v.string, "file"))
157         {
158                 ptr.o = NULL;
159                 ptr.option = "log_file";
160                 uci_lookup_ptr(cursor, &ptr, NULL, true);
161
162                 if (ptr.o && ptr.o->type == UCI_TYPE_STRING)
163                         logfile = ptr.o->v.string;
164                 else
165                         logfile = "/var/log/messages";
166
167                 if (stat(logfile, &st) || !(log = fopen(logfile, "r")))
168                         goto fail;
169
170                 logsize = st.st_size;
171         }
172         else
173         {
174                 ptr.o = NULL;
175                 ptr.option = "log_size";
176                 uci_lookup_ptr(cursor, &ptr, NULL, true);
177
178                 if (ptr.o && ptr.o->type == UCI_TYPE_STRING)
179                         logsize = atoi(ptr.o->v.string) * 1024;
180
181                 if (!(log = popen("logread", "r")))
182                         goto fail;
183         }
184
185         blob_buf_init(&buf, 0);
186
187         log_read(log, logsize);
188         fclose(log);
189
190         uci_unload(cursor, p);
191         ubus_send_reply(ctx, req, buf.head);
192         return 0;
193
194 fail:
195         uci_unload(cursor, p);
196         return rpc_errno_status();
197 }
198
199 static int
200 rpc_luci2_system_dmesg(struct ubus_context *ctx, struct ubus_object *obj,
201                        struct ubus_request_data *req, const char *method,
202                        struct blob_attr *msg)
203 {
204         FILE *log;
205
206         if (!(log = popen("dmesg", "r")))
207                 return rpc_errno_status();
208
209         blob_buf_init(&buf, 0);
210
211         log_read(log, RPC_LUCI2_MAX_LOGSIZE);
212         fclose(log);
213
214         ubus_send_reply(ctx, req, buf.head);
215         return 0;
216 }
217
218 static int
219 rpc_luci2_process_list(struct ubus_context *ctx, struct ubus_object *obj,
220                        struct ubus_request_data *req, const char *method,
221                        struct blob_attr *msg)
222 {
223         FILE *top;
224         void *c, *d;
225         char line[1024];
226         char *pid, *ppid, *user, *stat, *vsz, *pvsz, *pcpu, *cmd;
227
228         if (!(top = popen("/bin/busybox top -bn1", "r")))
229                 return rpc_errno_status();
230
231         blob_buf_init(&buf, 0);
232         c = blobmsg_open_array(&buf, "processes");
233
234         while (fgets(line, sizeof(line) - 1, top))
235         {
236                 pid  = strtok(line, " ");
237
238                 if (*pid < '0' || *pid > '9')
239                         continue;
240
241                 ppid = strtok(NULL, " ");
242                 user = strtok(NULL, " ");
243                 stat = strtok(NULL, " ");
244
245                 if (!stat)
246                         continue;
247
248                 if (!*(stat + 1))
249                         *(stat + 1) = ' ';
250
251                 if (!*(stat + 2))
252                         *(stat + 2) = ' ';
253
254                 *(stat + 3) = 0;
255
256                 vsz  = strtok(stat + 4, " ");
257                 pvsz = strtok(NULL, " ");
258                 pcpu = strtok(NULL, " ");
259                 cmd  = strtok(NULL, "\n");
260
261                 if (!cmd)
262                         continue;
263
264                 d = blobmsg_open_table(&buf, NULL);
265
266                 blobmsg_add_u32(&buf, "pid", atoi(pid));
267                 blobmsg_add_u32(&buf, "ppid", atoi(ppid));
268                 blobmsg_add_string(&buf, "user", user);
269                 blobmsg_add_string(&buf, "stat", stat);
270                 blobmsg_add_u32(&buf, "vsize", atoi(vsz) * 1024);
271                 blobmsg_add_u32(&buf, "vsize_percent", atoi(pvsz));
272                 blobmsg_add_u32(&buf, "cpu_percent", atoi(pcpu));
273                 blobmsg_add_string(&buf, "command", cmd);
274
275                 blobmsg_close_table(&buf, d);
276         }
277
278         fclose(top);
279         blobmsg_close_array(&buf, c);
280
281         ubus_send_reply(ctx, req, buf.head);
282         return 0;
283 }
284
285 static int
286 rpc_luci2_process_signal(struct ubus_context *ctx, struct ubus_object *obj,
287                          struct ubus_request_data *req, const char *method,
288                          struct blob_attr *msg)
289 {
290         int pid, sig;
291         struct blob_attr *tb[__RPC_S_MAX];
292
293         blobmsg_parse(rpc_signal_policy, __RPC_S_MAX, tb,
294                       blob_data(msg), blob_len(msg));
295
296         if (!tb[RPC_S_SIGNAL] || !tb[RPC_S_PID])
297         {
298                 errno = EINVAL;
299                 return rpc_errno_status();
300         }
301
302         pid = blobmsg_get_u32(tb[RPC_S_PID]);
303         sig = blobmsg_get_u32(tb[RPC_S_SIGNAL]);
304
305         if (kill(pid, sig))
306                 return rpc_errno_status();
307
308         return 0;
309 }
310
311 static int
312 rpc_luci2_init_list(struct ubus_context *ctx, struct ubus_object *obj,
313                     struct ubus_request_data *req, const char *method,
314                     struct blob_attr *msg)
315 {
316         int n;
317         void *c, *t;
318         char *p, path[PATH_MAX];
319         struct stat s;
320         struct dirent *e;
321         FILE *f;
322         DIR *d;
323
324         if (!(d = opendir("/etc/init.d")))
325                 return rpc_errno_status();
326
327         blob_buf_init(&buf, 0);
328         c = blobmsg_open_array(&buf, "initscripts");
329
330         while ((e = readdir(d)) != NULL)
331         {
332                 snprintf(path, sizeof(path) - 1, "/etc/init.d/%s", e->d_name);
333
334                 if (stat(path, &s) || !S_ISREG(s.st_mode) || !(s.st_mode & S_IXUSR))
335                         continue;
336
337                 if ((f = fopen(path, "r")) != NULL)
338                 {
339                         n = -1;
340                         p = fgets(path, sizeof(path) - 1, f);
341
342                         if (!p || !strstr(p, "/etc/rc.common"))
343                                 goto skip;
344
345                         t = blobmsg_open_table(&buf, NULL);
346
347                         blobmsg_add_string(&buf, "name", e->d_name);
348
349                         while (fgets(path, sizeof(path) - 1, f))
350                         {
351                                 p = strtok(path, "= \t");
352
353                                 if (!strcmp(p, "START") && !!(p = strtok(NULL, "= \t\n")))
354                                 {
355                                         n = atoi(p);
356                                         blobmsg_add_u32(&buf, "start", n);
357                                 }
358                                 else if (!strcmp(p, "STOP") && !!(p = strtok(NULL, "= \t\n")))
359                                 {
360                                         blobmsg_add_u32(&buf, "stop", atoi(p));
361                                         break;
362                                 }
363                         }
364
365                         if (n > -1)
366                         {
367                                 snprintf(path, sizeof(path) - 1, "/etc/rc.d/S%02d%s",
368                                          n, e->d_name);
369
370                                 blobmsg_add_u8(&buf, "enabled",
371                                                (!stat(path, &s) && (s.st_mode & S_IXUSR)));
372                         }
373                         else
374                         {
375                                 blobmsg_add_u8(&buf, "enabled", 0);
376                         }
377
378                         blobmsg_close_table(&buf, t);
379
380 skip:
381                         fclose(f);
382                 }
383         }
384
385         closedir(d);
386         blobmsg_close_array(&buf, c);
387
388         ubus_send_reply(ctx, req, buf.head);
389         return 0;
390 }
391
392 static int
393 rpc_luci2_init_action(struct ubus_context *ctx, struct ubus_object *obj,
394                       struct ubus_request_data *req, const char *method,
395                       struct blob_attr *msg)
396 {
397         int fd;
398         pid_t pid;
399         struct stat s;
400         char path[PATH_MAX];
401         const char *action;
402         struct blob_attr *tb[__RPC_I_MAX];
403
404         blobmsg_parse(rpc_init_policy, __RPC_I_MAX, tb,
405                       blob_data(msg), blob_len(msg));
406
407         if (!tb[RPC_I_NAME] || !tb[RPC_I_ACTION])
408                 return UBUS_STATUS_INVALID_ARGUMENT;
409
410         action = blobmsg_data(tb[RPC_I_ACTION]);
411
412         if (strcmp(action, "start") && strcmp(action, "stop") &&
413             strcmp(action, "reload") && strcmp(action, "restart") &&
414             strcmp(action, "enable") && strcmp(action, "disable"))
415                 return UBUS_STATUS_INVALID_ARGUMENT;
416
417         snprintf(path, sizeof(path) - 1, "/etc/init.d/%s",
418                  (char *)blobmsg_data(tb[RPC_I_NAME]));
419
420         if (stat(path, &s))
421                 return rpc_errno_status();
422
423         if (!(s.st_mode & S_IXUSR))
424                 return UBUS_STATUS_PERMISSION_DENIED;
425
426         switch ((pid = fork()))
427         {
428         case -1:
429                 return rpc_errno_status();
430
431         case 0:
432                 uloop_done();
433
434                 if ((fd = open("/dev/null", O_RDWR)) > -1)
435                 {
436                         dup2(fd, 0);
437                         dup2(fd, 1);
438                         dup2(fd, 2);
439
440                         close(fd);
441                 }
442
443                 chdir("/");
444
445                 if (execl(path, path, action, NULL))
446                         return rpc_errno_status();
447
448         default:
449                 return 0;
450         }
451 }
452
453 static int
454 rpc_luci2_sshkeys_get(struct ubus_context *ctx, struct ubus_object *obj,
455                       struct ubus_request_data *req, const char *method,
456                       struct blob_attr *msg)
457 {
458         FILE *f;
459         void *c;
460         char *p, line[4096];
461
462         if (!(f = fopen("/etc/dropbear/authorized_keys", "r")))
463                 return rpc_errno_status();
464
465         blob_buf_init(&buf, 0);
466         c = blobmsg_open_array(&buf, "keys");
467
468         while (fgets(line, sizeof(line) - 1, f))
469         {
470                 for (p = line + strlen(line) - 1; (p > line) && isspace(*p); p--)
471                         *p = 0;
472
473                 for (p = line; isspace(*p); p++)
474                         *p = 0;
475
476                 if (*p)
477                         blobmsg_add_string(&buf, NULL, p);
478         }
479
480         blobmsg_close_array(&buf, c);
481         fclose(f);
482
483         ubus_send_reply(ctx, req, buf.head);
484         return 0;
485 }
486
487 static int
488 rpc_luci2_sshkeys_set(struct ubus_context *ctx, struct ubus_object *obj,
489                       struct ubus_request_data *req, const char *method,
490                       struct blob_attr *msg)
491 {
492         FILE *f;
493         int rem;
494         struct blob_attr *cur, *tb[__RPC_K_MAX];
495
496         blobmsg_parse(rpc_sshkey_policy, __RPC_K_MAX, tb,
497                       blob_data(msg), blob_len(msg));
498
499         if (!tb[RPC_K_KEYS])
500                 return UBUS_STATUS_INVALID_ARGUMENT;
501
502         if (!(f = fopen("/etc/dropbear/authorized_keys", "w")))
503                 return rpc_errno_status();
504
505         blobmsg_for_each_attr(cur, tb[RPC_K_KEYS], rem)
506         {
507                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
508                         continue;
509
510                 fwrite(blobmsg_data(cur), blobmsg_data_len(cur) - 1, 1, f);
511                 fwrite("\n", 1, 1, f);
512         }
513
514         fclose(f);
515         return 0;
516 }
517
518
519 static FILE *
520 dnsmasq_leasefile(void)
521 {
522         FILE *leases = NULL;
523         struct uci_package *p;
524         struct uci_element *e;
525         struct uci_section *s;
526         struct uci_ptr ptr = {
527                 .package = "dhcp",
528                 .section = NULL,
529                 .option  = "leasefile"
530         };
531
532         uci_load(cursor, ptr.package, &p);
533
534         if (!p)
535                 return NULL;
536
537         uci_foreach_element(&p->sections, e)
538         {
539                 s = uci_to_section(e);
540
541                 if (strcmp(s->type, "dnsmasq"))
542                         continue;
543
544                 ptr.section = e->name;
545                 uci_lookup_ptr(cursor, &ptr, NULL, true);
546                 break;
547         }
548
549         if (ptr.o && ptr.o->type == UCI_TYPE_STRING)
550                 leases = fopen(ptr.o->v.string, "r");
551
552         uci_unload(cursor, p);
553
554         return leases;
555 }
556
557 static int
558 rpc_luci2_network_leases(struct ubus_context *ctx, struct ubus_object *obj,
559                          struct ubus_request_data *req, const char *method,
560                          struct blob_attr *msg)
561 {
562         FILE *leases;
563         void *c, *d;
564         char line[128];
565         char *ts, *mac, *addr, *name;
566         time_t now = time(NULL);
567
568         blob_buf_init(&buf, 0);
569         c = blobmsg_open_array(&buf, "leases");
570
571         leases = dnsmasq_leasefile();
572
573         if (!leases)
574                 goto out;
575
576         while (fgets(line, sizeof(line) - 1, leases))
577         {
578                 ts   = strtok(line, " \t");
579                 mac  = strtok(NULL, " \t");
580                 addr = strtok(NULL, " \t");
581                 name = strtok(NULL, " \t");
582
583                 if (!ts || !mac || !addr || !name)
584                         continue;
585
586                 if (strchr(addr, ':'))
587                         continue;
588
589                 d = blobmsg_open_table(&buf, NULL);
590
591                 blobmsg_add_u32(&buf, "expires", atoi(ts) - now);
592                 blobmsg_add_string(&buf, "macaddr", mac);
593                 blobmsg_add_string(&buf, "ipaddr", addr);
594
595                 if (strcmp(name, "*"))
596                         blobmsg_add_string(&buf, "hostname", name);
597
598                 blobmsg_close_table(&buf, d);
599         }
600
601         fclose(leases);
602
603 out:
604         blobmsg_close_array(&buf, c);
605         ubus_send_reply(ctx, req, buf.head);
606
607         return 0;
608 }
609
610 static int
611 rpc_luci2_network_leases6(struct ubus_context *ctx, struct ubus_object *obj,
612                           struct ubus_request_data *req, const char *method,
613                           struct blob_attr *msg)
614 {
615         FILE *leases;
616         void *c, *d;
617         char line[128];
618         char *ts, *mac, *addr, *name, *duid;
619         time_t now = time(NULL);
620
621         blob_buf_init(&buf, 0);
622         c = blobmsg_open_array(&buf, "leases");
623
624         leases = fopen("/tmp/hosts/6relayd", "r");
625
626         if (leases)
627         {
628                 while (fgets(line, sizeof(line) - 1, leases))
629                 {
630                         if (strncmp(line, "# ", 2))
631                                 continue;
632
633                         strtok(line + 2, " \t"); /* iface */
634
635                         duid = strtok(NULL, " \t");
636
637                         strtok(NULL, " \t"); /* iaid */
638
639                         name = strtok(NULL, " \t");
640                         ts   = strtok(NULL, " \t");
641
642                         strtok(NULL, " \t"); /* id */
643                         strtok(NULL, " \t"); /* length */
644
645                         addr = strtok(NULL, " \t\n");
646
647                         if (!addr)
648                                 continue;
649
650                         d = blobmsg_open_table(&buf, NULL);
651
652                         blobmsg_add_u32(&buf, "expires", atoi(ts) - now);
653                         blobmsg_add_string(&buf, "duid", duid);
654                         blobmsg_add_string(&buf, "ip6addr", addr);
655
656                         if (strcmp(name, "-"))
657                                 blobmsg_add_string(&buf, "hostname", name);
658
659                         blobmsg_close_array(&buf, d);
660                 }
661
662                 fclose(leases);
663         }
664         else
665         {
666                 leases = dnsmasq_leasefile();
667
668                 if (!leases)
669                         goto out;
670
671                 while (fgets(line, sizeof(line) - 1, leases))
672                 {
673                         ts   = strtok(line, " \t");
674                         mac  = strtok(NULL, " \t");
675                         addr = strtok(NULL, " \t");
676                         name = strtok(NULL, " \t");
677                         duid = strtok(NULL, " \t\n");
678
679                         if (!ts || !mac || !addr || !duid)
680                                 continue;
681
682                         if (!strchr(addr, ':'))
683                                 continue;
684
685                         d = blobmsg_open_table(&buf, NULL);
686
687                         blobmsg_add_u32(&buf, "expires", atoi(ts) - now);
688                         blobmsg_add_string(&buf, "macaddr", mac);
689                         blobmsg_add_string(&buf, "ip6addr", addr);
690
691                         if (strcmp(name, "*"))
692                                 blobmsg_add_string(&buf, "hostname", name);
693
694                         if (strcmp(duid, "*"))
695                                 blobmsg_add_string(&buf, "duid", name);
696
697                         blobmsg_close_table(&buf, d);
698                 }
699
700                 fclose(leases);
701         }
702
703 out:
704         blobmsg_close_array(&buf, c);
705         ubus_send_reply(ctx, req, buf.head);
706
707         return 0;
708 }
709
710 static int
711 rpc_luci2_network_ct_count(struct ubus_context *ctx, struct ubus_object *obj,
712                            struct ubus_request_data *req, const char *method,
713                            struct blob_attr *msg)
714 {
715         FILE *f;
716         char line[128];
717
718         blob_buf_init(&buf, 0);
719
720         if ((f = fopen("/proc/sys/net/netfilter/nf_conntrack_count", "r")) != NULL)
721         {
722                 if (fgets(line, sizeof(line) - 1, f))
723                         blobmsg_add_u32(&buf, "count", atoi(line));
724
725                 fclose(f);
726         }
727
728         if ((f = fopen("/proc/sys/net/netfilter/nf_conntrack_max", "r")) != NULL)
729         {
730                 if (fgets(line, sizeof(line) - 1, f))
731                         blobmsg_add_u32(&buf, "limit", atoi(line));
732
733                 fclose(f);
734         }
735
736         ubus_send_reply(ctx, req, buf.head);
737
738         return 0;
739 }
740
741 static int
742 rpc_luci2_network_ct_table(struct ubus_context *ctx, struct ubus_object *obj,
743                            struct ubus_request_data *req, const char *method,
744                            struct blob_attr *msg)
745 {
746         FILE *f;
747         int i;
748         void *c, *d;
749         char *p, line[512];
750         bool seen[6];
751
752         blob_buf_init(&buf, 0);
753         c = blobmsg_open_array(&buf, "entries");
754
755         if ((f = fopen("/proc/net/nf_conntrack", "r")) != NULL)
756         {
757                 while (fgets(line, sizeof(line) - 1, f))
758                 {
759                         d = blobmsg_open_table(&buf, NULL);
760                         memset(seen, 0, sizeof(seen));
761
762                         for (i = 0, p = strtok(line, " "); p; i++, p = strtok(NULL, " "))
763                         {
764                                 if (i == 0)
765                                         blobmsg_add_u8(&buf, "ipv6", !strcmp(p, "ipv6"));
766                                 else if (i == 3)
767                                         blobmsg_add_u32(&buf, "protocol", atoi(p));
768                                 else if (i == 4)
769                                         blobmsg_add_u32(&buf, "expires", atoi(p));
770                                 else if (i >= 5)
771                                 {
772                                         if (*p == '[')
773                                                 continue;
774
775                                         if (!seen[0] && !strncmp(p, "src=", 4))
776                                         {
777                                                 blobmsg_add_string(&buf, "src", p + 4);
778                                                 seen[0] = true;
779                                         }
780                                         else if (!seen[1] && !strncmp(p, "dst=", 4))
781                                         {
782                                                 blobmsg_add_string(&buf, "dest", p + 4);
783                                                 seen[1] = true;
784                                         }
785                                         else if (!seen[2] && !strncmp(p, "sport=", 6))
786                                         {
787                                                 blobmsg_add_u32(&buf, "sport", atoi(p + 6));
788                                                 seen[2] = true;
789                                         }
790                                         else if (!seen[3] && !strncmp(p, "dport=", 6))
791                                         {
792                                                 blobmsg_add_u32(&buf, "dport", atoi(p + 6));
793                                                 seen[3] = true;
794                                         }
795                                         else if (!strncmp(p, "packets=", 8))
796                                         {
797                                                 blobmsg_add_u32(&buf,
798                                                                 seen[4] ? "tx_packets" : "rx_packets",
799                                                                 atoi(p + 8));
800                                                 seen[4] = true;
801                                         }
802                                         else if (!strncmp(p, "bytes=", 6))
803                                         {
804                                                 blobmsg_add_u32(&buf,
805                                                                                 seen[5] ? "tx_bytes" : "rx_bytes",
806                                                                 atoi(p + 6));
807                                                 seen[5] = true;
808                                         }
809                                 }
810                         }
811
812                         blobmsg_close_table(&buf, d);
813                 }
814
815                 fclose(f);
816         }
817
818         blobmsg_close_array(&buf, c);
819         ubus_send_reply(ctx, req, buf.head);
820
821         return 0;
822 }
823
824 static int
825 rpc_luci2_network_arp_table(struct ubus_context *ctx, struct ubus_object *obj,
826                             struct ubus_request_data *req, const char *method,
827                             struct blob_attr *msg)
828 {
829         FILE *f;
830         void *c, *d;
831         char *addr, *mac, *dev, line[128];
832
833         blob_buf_init(&buf, 0);
834         c = blobmsg_open_array(&buf, "entries");
835
836         if ((f = fopen("/proc/net/arp", "r")) != NULL)
837         {
838                 /* skip header line */
839                 fgets(line, sizeof(line) - 1, f);
840
841                 while (fgets(line, sizeof(line) - 1, f))
842                 {
843                         addr = strtok(line, " \t");
844
845                         strtok(NULL, " \t"); /* HW type */
846                         strtok(NULL, " \t"); /* Flags */
847
848                         mac = strtok(NULL, " \t");
849
850                         strtok(NULL, " \t"); /* Mask */
851
852                         dev = strtok(NULL, " \t\n");
853
854                         if (!dev)
855                                 continue;
856
857                         d = blobmsg_open_table(&buf, NULL);
858                         blobmsg_add_string(&buf, "ipaddr", addr);
859                         blobmsg_add_string(&buf, "macaddr", mac);
860                         blobmsg_add_string(&buf, "device", dev);
861                         blobmsg_close_table(&buf, d);
862                 }
863
864                 fclose(f);
865         }
866
867         blobmsg_close_array(&buf, c);
868         ubus_send_reply(ctx, req, buf.head);
869
870         return 0;
871 }
872
873 static void
874 put_hexaddr(const char *name, const char *s, const char *m)
875 {
876         int bits;
877         struct in_addr a;
878         char as[sizeof("255.255.255.255/32\0")];
879
880         a.s_addr = strtoul(s, NULL, 16);
881         inet_ntop(AF_INET, &a, as, sizeof(as));
882
883         if (m)
884         {
885                 for (a.s_addr = ntohl(strtoul(m, NULL, 16)), bits = 0;
886                      a.s_addr & 0x80000000;
887                      a.s_addr <<= 1)
888                         bits++;
889
890                 sprintf(as + strlen(as), "/%u", bits);
891         }
892
893         blobmsg_add_string(&buf, name, as);
894 }
895
896 static int
897 rpc_luci2_network_routes(struct ubus_context *ctx, struct ubus_object *obj,
898                          struct ubus_request_data *req, const char *method,
899                          struct blob_attr *msg)
900 {
901         FILE *routes;
902         void *c, *d;
903         char *dst, *dmask, *next, *metric, *device;
904         char line[256];
905         unsigned int n;
906
907         if (!(routes = fopen("/proc/net/route", "r")))
908                 return rpc_errno_status();
909
910         blob_buf_init(&buf, 0);
911         c = blobmsg_open_array(&buf, "routes");
912
913         /* skip header line */
914         fgets(line, sizeof(line) - 1, routes);
915
916         while (fgets(line, sizeof(line) - 1, routes))
917         {
918                 device = strtok(line, "\t ");
919                 dst    = strtok(NULL, "\t ");
920                 next   = strtok(NULL, "\t ");
921
922                 strtok(NULL, "\t "); /* flags */
923                 strtok(NULL, "\t "); /* refcount */
924                 strtok(NULL, "\t "); /* usecount */
925
926                 metric = strtok(NULL, "\t ");
927                 dmask  = strtok(NULL, "\t ");
928
929                 if (!dmask)
930                         continue;
931
932                 d = blobmsg_open_table(&buf, NULL);
933
934                 put_hexaddr("target", dst, dmask);
935                 put_hexaddr("nexthop", next, NULL);
936
937                 n = strtoul(metric, NULL, 10);
938                 blobmsg_add_u32(&buf, "metric", n);
939
940                 blobmsg_add_string(&buf, "device", device);
941
942                 blobmsg_close_table(&buf, d);
943         }
944
945         blobmsg_close_array(&buf, c);
946         fclose(routes);
947
948         ubus_send_reply(ctx, req, buf.head);
949         return 0;
950 }
951
952 static void
953 put_hex6addr(const char *name, const char *s, const char *m)
954 {
955         int i;
956         struct in6_addr a;
957         char as[INET6_ADDRSTRLEN + sizeof("/128")];
958
959 #define hex(x) \
960         (((x) <= '9') ? ((x) - '0') : \
961                 (((x) <= 'F') ? ((x) - 'A' + 10) : \
962                         ((x) - 'a' + 10)))
963
964         for (i = 0; i < 16; i++, s += 2)
965                 a.s6_addr[i] = (16 * hex(*s)) + hex(*(s+1));
966
967         inet_ntop(AF_INET6, &a, as, sizeof(as));
968
969         if (m)
970                 sprintf(as + strlen(as), "/%lu", strtoul(m, NULL, 16));
971
972         blobmsg_add_string(&buf, name, as);
973 }
974
975 static int
976 rpc_luci2_network_routes6(struct ubus_context *ctx, struct ubus_object *obj,
977                           struct ubus_request_data *req, const char *method,
978                           struct blob_attr *msg)
979 {
980         FILE *routes;
981         void *c, *d;
982         char *src, *smask, *dst, *dmask, *next, *metric, *flags, *device;
983         char line[256];
984         unsigned int n;
985
986         if (!(routes = fopen("/proc/net/ipv6_route", "r")))
987                 return rpc_errno_status();
988
989         blob_buf_init(&buf, 0);
990         c = blobmsg_open_array(&buf, "routes");
991
992         while (fgets(line, sizeof(line) - 1, routes))
993         {
994                 dst    = strtok(line, " ");
995                 dmask  = strtok(NULL, " ");
996                 src    = strtok(NULL, " ");
997                 smask  = strtok(NULL, " ");
998                 next   = strtok(NULL, " ");
999                 metric = strtok(NULL, " ");
1000
1001                 strtok(NULL, " "); /* refcount */
1002                 strtok(NULL, " "); /* usecount */
1003
1004                 flags  = strtok(NULL, " ");
1005                 device = strtok(NULL, " \n");
1006
1007                 if (!device)
1008                         continue;
1009
1010                 n = strtoul(flags, NULL, 16);
1011
1012                 if (!(n & 1))
1013                         continue;
1014
1015                 d = blobmsg_open_table(&buf, NULL);
1016
1017                 put_hex6addr("target", dst, dmask);
1018                 put_hex6addr("source", src, smask);
1019                 put_hex6addr("nexthop", next, NULL);
1020
1021                 n = strtoul(metric, NULL, 16);
1022                 blobmsg_add_u32(&buf, "metric", n);
1023
1024                 blobmsg_add_string(&buf, "device", device);
1025
1026                 blobmsg_close_table(&buf, d);
1027         }
1028
1029         blobmsg_close_array(&buf, c);
1030         fclose(routes);
1031
1032         ubus_send_reply(ctx, req, buf.head);
1033         return 0;
1034 }
1035
1036
1037 int rpc_luci2_api_init(struct ubus_context *ctx)
1038 {
1039         int rv = 0;
1040
1041         static const struct ubus_method luci2_system_methods[] = {
1042                 UBUS_METHOD_NOARG("syslog",       rpc_luci2_system_log),
1043                 UBUS_METHOD_NOARG("dmesg",        rpc_luci2_system_dmesg),
1044                 UBUS_METHOD_NOARG("process_list", rpc_luci2_process_list),
1045                 UBUS_METHOD("process_signal",     rpc_luci2_process_signal,
1046                                                   rpc_signal_policy),
1047                 UBUS_METHOD_NOARG("init_list",    rpc_luci2_init_list),
1048                 UBUS_METHOD("init_action",        rpc_luci2_init_action,
1049                                                   rpc_init_policy),
1050                 UBUS_METHOD_NOARG("sshkeys_get",  rpc_luci2_sshkeys_get),
1051                 UBUS_METHOD("sshkeys_set",        rpc_luci2_sshkeys_set,
1052                                                   rpc_sshkey_policy)
1053         };
1054
1055         static struct ubus_object_type luci2_system_type =
1056                 UBUS_OBJECT_TYPE("luci-rpc-luci2-system", luci2_system_methods);
1057
1058         static struct ubus_object system_obj = {
1059                 .name = "luci2.system",
1060                 .type = &luci2_system_type,
1061                 .methods = luci2_system_methods,
1062                 .n_methods = ARRAY_SIZE(luci2_system_methods),
1063         };
1064
1065
1066         static const struct ubus_method luci2_network_methods[] = {
1067                 UBUS_METHOD_NOARG("conntrack_count", rpc_luci2_network_ct_count),
1068                 UBUS_METHOD_NOARG("conntrack_table", rpc_luci2_network_ct_table),
1069                 UBUS_METHOD_NOARG("arp_table",       rpc_luci2_network_arp_table),
1070                 UBUS_METHOD_NOARG("dhcp_leases",     rpc_luci2_network_leases),
1071                 UBUS_METHOD_NOARG("dhcp6_leases",    rpc_luci2_network_leases6),
1072                 UBUS_METHOD_NOARG("routes",          rpc_luci2_network_routes),
1073                 UBUS_METHOD_NOARG("routes6",         rpc_luci2_network_routes6),
1074         };
1075
1076         static struct ubus_object_type luci2_network_type =
1077                 UBUS_OBJECT_TYPE("luci-rpc-luci2-network", luci2_network_methods);
1078
1079         static struct ubus_object network_obj = {
1080                 .name = "luci2.network",
1081                 .type = &luci2_network_type,
1082                 .methods = luci2_network_methods,
1083                 .n_methods = ARRAY_SIZE(luci2_network_methods),
1084         };
1085
1086         cursor = uci_alloc_context();
1087
1088         if (!cursor)
1089                 return UBUS_STATUS_UNKNOWN_ERROR;
1090
1091         rv |= ubus_add_object(ctx, &system_obj);
1092         rv |= ubus_add_object(ctx, &network_obj);
1093
1094         return rv;
1095 }