rpcd-mod-luci: fix crash on not existing uci options
[oweals/luci.git] / libs / rpcd-mod-luci / src / luci.c
1 /*
2  * luci - LuCI core functions plugin for rpcd
3  *
4  *   Copyright (C) 2019 Jo-Philipp Wich <jo@mein.io>
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 #define _GNU_SOURCE
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27 #include <ctype.h>
28 #include <dlfcn.h>
29 #include <time.h>
30 #include <ifaddrs.h>
31 #include <net/if.h>
32 #include <arpa/inet.h>
33 #include <sys/types.h>
34 #include <netinet/ether.h>
35 #include <linux/rtnetlink.h>
36 #include <linux/if_packet.h>
37
38 #include <netlink/msg.h>
39 #include <netlink/attr.h>
40 #include <netlink/socket.h>
41
42 #include <libubus.h>
43 #include <libubox/avl.h>
44 #include <libubox/avl-cmp.h>
45 #include <libubox/usock.h>
46 #include <libubox/uloop.h>
47
48 #include <uci.h>
49
50 #include <iwinfo.h>
51
52 #include <rpcd/plugin.h>
53
54
55 static struct blob_buf blob;
56
57 struct reply_context {
58         struct ubus_context *context;
59         struct ubus_request_data request;
60         struct uloop_timeout timeout;
61         struct blob_buf blob;
62         struct avl_tree avl;
63         int pending;
64 };
65
66 struct invoke_context {
67         struct ubus_request request;
68         struct uloop_timeout timeout;
69         struct ubus_context *context;
70         void (*cb)(struct ubus_request *, int, struct blob_attr *);
71         void *priv;
72 };
73
74 static const char **iw_modenames;
75 static struct iwinfo_ops *(*iw_backend)(const char *);
76 static void (*iw_close)(void);
77
78 static void
79 invoke_data_cb(struct ubus_request *req, int type, struct blob_attr *msg)
80 {
81         struct invoke_context *ictx =
82                 container_of(req, struct invoke_context, request);
83
84         if (ictx->cb != NULL)
85                 ictx->cb(req, type, msg);
86 }
87
88 static void
89 invoke_done_cb(struct ubus_request *req, int ret)
90 {
91         struct invoke_context *ictx =
92                 container_of(req, struct invoke_context, request);
93
94         uloop_timeout_cancel(&ictx->timeout);
95         free(ictx);
96 }
97
98 static void
99 invoke_timeout_cb(struct uloop_timeout *timeout)
100 {
101         struct invoke_context *ictx =
102                 container_of(timeout, struct invoke_context, timeout);
103
104         if (ictx->cb != NULL)
105                 ictx->cb(&ictx->request, -1, NULL);
106
107         ubus_abort_request(ictx->context, &ictx->request);
108         free(ictx);
109 }
110
111 static struct reply_context *
112 defer_request(struct ubus_context *ctx, struct ubus_request_data *req)
113 {
114         struct reply_context *rctx;
115
116         rctx = calloc(1, sizeof(*rctx));
117
118         if (!rctx)
119                 return NULL;
120
121         rctx->context = ctx;
122         blob_buf_init(&rctx->blob, 0);
123         ubus_defer_request(ctx, req, &rctx->request);
124
125         return rctx;
126 }
127
128 static int
129 finish_request(struct reply_context *rctx, int status)
130 {
131         if (status == UBUS_STATUS_OK)
132                 ubus_send_reply(rctx->context, &rctx->request, rctx->blob.head);
133
134         ubus_complete_deferred_request(rctx->context, &rctx->request, status);
135         blob_buf_free(&rctx->blob);
136         free(rctx);
137
138         return status;
139 }
140
141 static bool
142 invoke_ubus(struct ubus_context *ctx, const char *object, const char *method,
143             struct blob_buf *req,
144             void (*cb)(struct ubus_request *, int, struct blob_attr *),
145             void *priv)
146 {
147         struct invoke_context *ictx;
148         struct blob_buf empty = {};
149         uint32_t id;
150         bool rv;
151
152         if (ubus_lookup_id(ctx, object, &id))
153                 return false;
154
155         if (req == NULL) {
156                 blob_buf_init(&empty, 0);
157                 req = &empty;
158         }
159
160         ictx = calloc(1, sizeof(*ictx));
161
162         if (ictx == NULL)
163                 return false;
164
165         ictx->context = ctx;
166         rv = !ubus_invoke_async(ctx, id, method, req->head, &ictx->request);
167
168         if (rv) {
169                 ictx->cb = cb;
170                 ictx->request.priv = priv;
171                 ictx->request.data_cb = invoke_data_cb;
172                 ictx->request.complete_cb = invoke_done_cb;
173                 ubus_complete_request_async(ctx, &ictx->request);
174
175                 ictx->timeout.cb = invoke_timeout_cb;
176                 uloop_timeout_set(&ictx->timeout, 2000);
177         }
178         else {
179                 if (cb != NULL)
180                         cb(&ictx->request, -1, NULL);
181
182                 free(ictx);
183         }
184
185         if (req == &empty)
186                 blob_buf_free(req);
187
188         return rv;
189 }
190
191 static char *
192 readstr(const char *fmt, ...)
193 {
194         static char data[128];
195         char path[128];
196         va_list ap;
197         size_t n;
198         FILE *f;
199
200         va_start(ap, fmt);
201         vsnprintf(path, sizeof(path), fmt, ap);
202         va_end(ap);
203
204         data[0] = 0;
205         f = fopen(path, "r");
206
207         if (f != NULL) {
208                 n = fread(data, 1, sizeof(data) - 1, f);
209                 data[n] = 0;
210
211                 while (n > 0 && isspace(data[n-1]))
212                         data[--n] = 0;
213
214                 fclose(f);
215         }
216
217         return data;
218 }
219
220 static char *
221 ea2str(struct ether_addr *ea)
222 {
223         static char mac[18];
224
225         if (!ea)
226                 return NULL;
227
228         snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X",
229                  ea->ether_addr_octet[0], ea->ether_addr_octet[1],
230                  ea->ether_addr_octet[2], ea->ether_addr_octet[3],
231                  ea->ether_addr_octet[4], ea->ether_addr_octet[5]);
232
233         return mac;
234 }
235
236 static char *
237 sa2str(struct sockaddr *sa)
238 {
239         static char buf[INET6_ADDRSTRLEN];
240         union {
241                 struct sockaddr_in6 *in6;
242                 struct sockaddr_in *in;
243                 struct sockaddr_ll *ll;
244                 struct sockaddr *sa;
245         } s;
246
247         s.sa = sa;
248
249         if (s.sa->sa_family == AF_INET)
250                 inet_ntop(sa->sa_family, &s.in->sin_addr, buf, sizeof(buf));
251         else if (s.sa->sa_family == AF_INET6)
252                 inet_ntop(sa->sa_family, &s.in6->sin6_addr, buf, sizeof(buf));
253         else if (s.sa->sa_family == AF_PACKET)
254                 strcpy(buf, ea2str((struct ether_addr *)s.ll->sll_addr));
255         else
256                 buf[0] = 0;
257
258         return buf;
259 }
260
261 static struct ether_addr *
262 duid2ea(const char *duid)
263 {
264         static struct ether_addr ea;
265         const char *p = NULL;
266         size_t len;
267
268         if (!duid)
269                 return NULL;
270
271         for (len = 0; duid[len]; len++)
272                 if (!isxdigit(duid[len]))
273                         return NULL;
274
275 #define hex(x) \
276         (((x) <= '9') ? ((x) - '0') : \
277                 (((x) <= 'F') ? ((x) - 'A' + 10) : \
278                         ((x) - 'a' + 10)))
279
280         switch (len) {
281         case 28:
282                 if (!strncmp(duid, "00010001", 8))
283                         p = duid + 8;
284
285                 break;
286
287         case 20:
288                 if (!strncmp(duid, "00030001", 8))
289                         p = duid + 8;
290
291                 break;
292
293         case 12:
294                 p = duid;
295                 break;
296         }
297
298         if (!p)
299                 return NULL;
300
301         ea.ether_addr_octet[0] = hex(p[0]) * 16 + hex(p[1]);
302         ea.ether_addr_octet[1] = hex(p[2]) * 16 + hex(p[3]);
303         ea.ether_addr_octet[2] = hex(p[4]) * 16 + hex(p[5]);
304         ea.ether_addr_octet[3] = hex(p[6]) * 16 + hex(p[7]);
305         ea.ether_addr_octet[4] = hex(p[8]) * 16 + hex(p[9]);
306         ea.ether_addr_octet[5] = hex(p[10]) * 16 + hex(p[11]);
307
308         return &ea;
309 }
310
311
312 static struct {
313         FILE *dnsmasq_file;
314         FILE *odhcpd_file;
315         time_t now;
316 } lease_state = { };
317
318 struct lease_entry {
319         int af;
320         char buf[512];
321         int32_t expire;
322         struct ether_addr mac;
323         union {
324                 struct in_addr in;
325                 struct in6_addr in6;
326         } addr;
327         char *hostname;
328         char *duid;
329 };
330
331 static char *
332 find_leasefile(struct uci_context *uci, const char *section)
333 {
334         struct uci_ptr ptr = { .package = "dhcp" };
335         struct uci_package *pkg = NULL;
336         struct uci_section *s;
337         struct uci_element *e;
338
339         pkg = uci_lookup_package(uci, ptr.package);
340
341         if (!pkg) {
342                 uci_load(uci, ptr.package, &pkg);
343
344                 if (!pkg)
345                         return NULL;
346         }
347
348         uci_foreach_element(&pkg->sections, e) {
349                 s = uci_to_section(e);
350
351                 if (strcmp(s->type, section))
352                         continue;
353
354                 ptr.flags = 0;
355
356                 ptr.section = s->e.name;
357                 ptr.s = NULL;
358
359                 ptr.option = "leasefile";
360                 ptr.o = NULL;
361
362                 if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL)
363                         continue;
364
365                 if (ptr.o->type != UCI_TYPE_STRING)
366                         continue;
367
368                 return ptr.o->v.string;
369         }
370
371         return NULL;
372 }
373
374 static void
375 lease_close(void)
376 {
377         if (lease_state.dnsmasq_file) {
378                 fclose(lease_state.dnsmasq_file);
379                 lease_state.dnsmasq_file = NULL;
380         }
381
382         if (lease_state.odhcpd_file) {
383                 fclose(lease_state.odhcpd_file);
384                 lease_state.odhcpd_file = NULL;
385         }
386 }
387
388 static void
389 lease_open(void)
390 {
391         struct uci_context *uci;
392         char *p;
393
394         lease_close();
395
396         uci = uci_alloc_context();
397
398         if (!uci)
399                 return;
400
401         lease_state.now = time(NULL);
402
403         p = find_leasefile(uci, "dnsmasq");
404         lease_state.dnsmasq_file = fopen(p ? p : "/tmp/dhcp.leases", "r");
405
406         p = find_leasefile(uci, "odhcpd");
407         lease_state.odhcpd_file = fopen(p ? p : "/tmp/hosts/odhcpd", "r");
408
409         uci_free_context(uci);
410 }
411
412 static struct lease_entry *
413 lease_next(void)
414 {
415         static struct lease_entry e;
416         struct ether_addr *ea;
417         char *p;
418
419         memset(&e, 0, sizeof(e));
420
421         if (lease_state.dnsmasq_file) {
422                 while (fgets(e.buf, sizeof(e.buf), lease_state.dnsmasq_file)) {
423                         p = strtok(e.buf, " \t\n");
424
425                         if (!p)
426                                 continue;
427
428                         e.expire = strtol(p, NULL, 10);
429                         e.expire = (e.expire >= 0) ? e.expire - lease_state.now : 0;
430
431                         p = strtok(NULL, " \t\n");
432
433                         if (!p)
434                                 continue;
435
436                         ea = ether_aton(p);
437
438                         if (!ea)
439                                 continue;
440
441                         p = strtok(NULL, " \t\n");
442
443                         if (p && inet_pton(AF_INET6, p, &e.addr.in6))
444                                 e.af = AF_INET6;
445                         else if (p && inet_pton(AF_INET, p, &e.addr.in))
446                                 e.af = AF_INET;
447                         else
448                                 continue;
449
450                         e.hostname = strtok(NULL, " \t\n");
451                         e.duid     = strtok(NULL, " \t\n");
452
453                         if (!e.hostname || !e.duid)
454                                 continue;
455
456                         if (!strcmp(e.hostname, "*"))
457                                 e.hostname = NULL;
458
459                         if (!strcmp(e.duid, "*"))
460                                 e.duid = NULL;
461
462                         e.mac = *ea;
463
464                         return &e;
465                 }
466
467                 fclose(lease_state.dnsmasq_file);
468                 lease_state.dnsmasq_file = NULL;
469         }
470
471         if (lease_state.odhcpd_file) {
472                 while (fgets(e.buf, sizeof(e.buf), lease_state.odhcpd_file)) {
473                         strtok(e.buf, " \t\n"); /* # */
474                         strtok(NULL, " \t\n"); /* iface */
475
476                         e.duid = strtok(NULL, " \t\n"); /* duid */
477
478                         if (!e.duid)
479                                 continue;
480
481                         p = strtok(NULL, " \t\n"); /* iaid */
482
483                         if (p)
484                                 e.af = strcmp(p, "ipv4") ? AF_INET6 : AF_INET;
485                         else
486                                 continue;
487
488                         e.hostname = strtok(NULL, " \t\n"); /* name */
489
490                         if (!e.hostname)
491                                 continue;
492
493                         p = strtok(NULL, " \t\n"); /* ts */
494
495                         if (!p)
496                                 continue;
497
498                         e.expire = strtol(p, NULL, 10);
499                         e.expire = (e.expire > 0) ? e.expire - lease_state.now : e.expire;
500
501                         strtok(NULL, " \t\n"); /* id */
502                         strtok(NULL, " \t\n"); /* length */
503
504                         p = strtok(NULL, "/ \t\n"); /* ip */
505
506                         if (!p || !inet_pton(e.af, p, &e.addr.in6))
507                                 continue;
508
509                         ea = duid2ea(e.duid);
510
511                         if (ea)
512                                 e.mac = *ea;
513
514                         if (!strcmp(e.hostname, "-"))
515                                 e.hostname = NULL;
516
517                         if (!strcmp(e.duid, "-"))
518                                 e.duid = NULL;
519
520                         return &e;
521                 }
522
523                 fclose(lease_state.odhcpd_file);
524                 lease_state.odhcpd_file = NULL;
525         }
526
527         return NULL;
528 }
529
530
531 static void
532 rpc_luci_parse_network_device_sys(const char *name, struct ifaddrs *ifaddr)
533 {
534         char link[64], buf[512], *p;
535         unsigned int ifa_flags = 0;
536         struct sockaddr_ll *sll;
537         struct ifaddrs *ifa;
538         struct dirent *e;
539         void *o, *o2, *a;
540         ssize_t len;
541         uint64_t v;
542         int n, af;
543         DIR *d;
544
545         const char *stats[] = {
546                 "rx_bytes", "tx_bytes", "tx_errors", "rx_errors", "tx_packets",
547                 "rx_packets", "multicast", "collisions", "rx_dropped", "tx_dropped"
548         };
549
550         o = blobmsg_open_table(&blob, name);
551
552         blobmsg_add_string(&blob, "name", name);
553
554         snprintf(buf, sizeof(buf), "/sys/class/net/%s/brif", name);
555
556         d = opendir(buf);
557
558         if (d) {
559                 blobmsg_add_u8(&blob, "bridge", 1);
560
561                 a = blobmsg_open_array(&blob, "ports");
562
563                 while (true) {
564                         e = readdir(d);
565
566                         if (e == NULL)
567                                 break;
568
569                         if (strcmp(e->d_name, ".") && strcmp(e->d_name, ".."))
570                                 blobmsg_add_string(&blob, NULL, e->d_name);
571                 }
572
573                 blobmsg_close_array(&blob, a);
574
575                 closedir(d);
576
577                 p = readstr("/sys/class/net/%s/bridge/bridge_id", name);
578                 blobmsg_add_string(&blob, "id", p);
579
580                 p = readstr("/sys/class/net/%s/bridge/stp_state", name);
581                 blobmsg_add_u8(&blob, "stp", strcmp(p, "0") ? 1 : 0);
582         }
583
584         snprintf(buf, sizeof(buf), "/sys/class/net/%s/master", name);
585         len = readlink(buf, link, sizeof(link) - 1);
586
587         if (len > 0) {
588                 link[len] = 0;
589                 blobmsg_add_string(&blob, "master", basename(link));
590         }
591
592         p = readstr("/sys/class/net/%s/phy80211/index", name);
593         blobmsg_add_u8(&blob, "wireless", *p ? 1 : 0);
594
595         p = readstr("/sys/class/net/%s/operstate", name);
596         blobmsg_add_u8(&blob, "up", !strcmp(p, "up") || !strcmp(p, "unknown"));
597
598         n = atoi(readstr("/sys/class/net/%s/mtu", name));
599         if (n > 0)
600                 blobmsg_add_u32(&blob, "mtu", n);
601
602         n = atoi(readstr("/sys/class/net/%s/tx_queue_len", name));
603         if (n > 0)
604                 blobmsg_add_u32(&blob, "qlen", n);
605
606         p = readstr("/sys/class/net/%s/master", name);
607         if (*p)
608                 blobmsg_add_string(&blob, "master", p);
609
610         for (af = AF_INET; af != 0; af = (af == AF_INET) ? AF_INET6 : 0) {
611                 a = blobmsg_open_array(&blob,
612                                        (af == AF_INET) ? "ipaddrs" : "ip6addrs");
613
614                 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
615                         if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != af)
616                                 continue;
617
618                         if (strcmp(ifa->ifa_name, name))
619                                 continue;
620
621                         o2 = blobmsg_open_table(&blob, NULL);
622
623                         blobmsg_add_string(&blob, "address",
624                                            sa2str(ifa->ifa_addr));
625
626                         blobmsg_add_string(&blob, "netmask",
627                                            sa2str(ifa->ifa_netmask));
628
629                         if (ifa->ifa_dstaddr && (ifa->ifa_flags & IFF_POINTOPOINT))
630                                 blobmsg_add_string(&blob, "remote",
631                                                    sa2str(ifa->ifa_dstaddr));
632                         else if (ifa->ifa_broadaddr && (ifa->ifa_flags & IFF_BROADCAST))
633                                 blobmsg_add_string(&blob, "broadcast",
634                                                    sa2str(ifa->ifa_broadaddr));
635
636                         blobmsg_close_table(&blob, o2);
637
638                         ifa_flags |= ifa->ifa_flags;
639                 }
640
641                 blobmsg_close_array(&blob, a);
642         }
643
644         for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
645                 if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_PACKET)
646                         continue;
647
648                 if (strcmp(ifa->ifa_name, name))
649                         continue;
650
651                 sll = (struct sockaddr_ll *)ifa->ifa_addr;
652
653                 if (sll->sll_hatype == 1)
654                         blobmsg_add_string(&blob, "mac", sa2str(ifa->ifa_addr));
655
656                 blobmsg_add_u32(&blob, "type", sll->sll_hatype);
657                 blobmsg_add_u32(&blob, "ifindex", sll->sll_ifindex);
658
659                 ifa_flags |= ifa->ifa_flags;
660                 break;
661         }
662
663         o2 = blobmsg_open_table(&blob, "stats");
664
665         for (n = 0; n < ARRAY_SIZE(stats); n++) {
666         v = strtoull(readstr("/sys/class/net/%s/statistics/%s",
667                                       name, stats[n]), NULL, 10);
668
669                 blobmsg_add_u64(&blob, stats[n], v);
670         }
671
672         blobmsg_close_table(&blob, o2);
673
674         o2 = blobmsg_open_table(&blob, "flags");
675         blobmsg_add_u8(&blob, "up", ifa_flags & IFF_UP);
676         blobmsg_add_u8(&blob, "broadcast", ifa_flags & IFF_BROADCAST);
677         blobmsg_add_u8(&blob, "promisc", ifa_flags & IFF_PROMISC);
678         blobmsg_add_u8(&blob, "loopback", ifa_flags & IFF_LOOPBACK);
679         blobmsg_add_u8(&blob, "noarp", ifa_flags & IFF_NOARP);
680         blobmsg_add_u8(&blob, "multicast", ifa_flags & IFF_MULTICAST);
681         blobmsg_add_u8(&blob, "pointtopoint", ifa_flags & IFF_POINTOPOINT);
682         blobmsg_close_table(&blob, o2);
683
684         blobmsg_close_table(&blob, o);
685 }
686
687 static int
688 rpc_luci_get_network_devices(struct ubus_context *ctx,
689                              struct ubus_object *obj,
690                              struct ubus_request_data *req,
691                              const char *method,
692                              struct blob_attr *msg)
693 {
694         struct ifaddrs *ifaddr;
695         struct dirent *e;
696         DIR *d;
697
698         blob_buf_init(&blob, 0);
699
700         d = opendir("/sys/class/net");
701
702         if (d != NULL) {
703                 if (getifaddrs(&ifaddr) == 1)
704                         ifaddr = NULL;
705
706                 while (true) {
707                         e = readdir(d);
708
709                         if (e == NULL)
710                                 break;
711
712                         if (strcmp(e->d_name, ".") && strcmp(e->d_name, ".."))
713                                 rpc_luci_parse_network_device_sys(e->d_name, ifaddr);
714                 }
715
716                 if (ifaddr != NULL)
717                         freeifaddrs(ifaddr);
718
719                 closedir(d);
720         }
721
722         ubus_send_reply(ctx, req, blob.head);
723         return 0;
724 }
725
726
727 static void
728 iw_call_str(int (*method)(const char *, char *), const char *dev,
729             struct blob_buf *blob, const char *field)
730 {
731         char buf[IWINFO_BUFSIZE] = {};
732
733         if (method(dev, buf) == 0)
734                 blobmsg_add_string(blob, field, buf);
735 }
736
737 static void
738 iw_call_num(int (*method)(const char *, int *), const char *dev,
739             struct blob_buf *blob, const char *field)
740 {
741         int val = 0;
742
743         if (method(dev, &val) == 0)
744                 blobmsg_add_u32(blob, field, val);
745 }
746
747 static bool rpc_luci_get_iwinfo(struct blob_buf *buf, const char *devname,
748                                 bool phy_only)
749 {
750         struct iwinfo_crypto_entry crypto = {};
751         struct iwinfo_hardware_id ids = {};
752         const struct iwinfo_ops *iw;
753         void *iwlib, *o, *o2, *a;
754         int nret;
755
756         if (!iw_backend || !iw_close || !iw_modenames) {
757                 iwlib = dlopen("libiwinfo.so", RTLD_LOCAL);
758
759                 if (!iwlib)
760                         return false;
761
762                 iw_backend = dlsym(iwlib, "iwinfo_backend");
763                 iw_close = dlsym(iwlib, "iwinfo_close");
764                 iw_modenames = dlsym(iwlib, "IWINFO_OPMODE_NAMES");
765
766                 if (!iw_backend || !iw_close || !iw_modenames)
767                         return false;
768         }
769
770         iw = iw_backend(devname);
771
772         if (!iw)
773                 return false;
774
775         o = blobmsg_open_table(buf, "iwinfo");
776
777         iw_call_num(iw->signal, devname, buf, "signal");
778         iw_call_num(iw->noise, devname, buf, "noise");
779         iw_call_num(iw->channel, devname, buf, "channel");
780         iw_call_str(iw->country, devname, buf, "country");
781         iw_call_str(iw->phyname, devname, buf, "phy");
782         iw_call_num(iw->txpower, devname, buf, "txpower");
783         iw_call_num(iw->txpower_offset, devname, buf, "txpower_offset");
784         iw_call_num(iw->frequency, devname, buf, "frequency");
785         iw_call_num(iw->frequency_offset, devname, buf, "frequency_offset");
786
787         if (!iw->hwmodelist(devname, &nret)) {
788                 a = blobmsg_open_array(buf, "hwmodes");
789
790                 if (nret & IWINFO_80211_AC)
791                         blobmsg_add_string(buf, NULL, "ac");
792
793                 if (nret & IWINFO_80211_A)
794                         blobmsg_add_string(buf, NULL, "a");
795
796                 if (nret & IWINFO_80211_B)
797                         blobmsg_add_string(buf, NULL, "b");
798
799                 if (nret & IWINFO_80211_G)
800                         blobmsg_add_string(buf, NULL, "g");
801
802                 if (nret & IWINFO_80211_N)
803                         blobmsg_add_string(buf, NULL, "n");
804
805                 blobmsg_close_array(buf, a);
806         }
807
808         if (!iw->htmodelist(devname, &nret)) {
809                 a = blobmsg_open_array(buf, "htmodes");
810
811                 if (nret & IWINFO_HTMODE_HT20)
812                         blobmsg_add_string(buf, NULL, "HT20");
813
814                 if (nret & IWINFO_HTMODE_HT40)
815                         blobmsg_add_string(buf, NULL, "HT40");
816
817                 if (nret & IWINFO_HTMODE_VHT20)
818                         blobmsg_add_string(buf, NULL, "VHT20");
819
820                 if (nret & IWINFO_HTMODE_VHT40)
821                         blobmsg_add_string(buf, NULL, "VHT40");
822
823                 if (nret & IWINFO_HTMODE_VHT80)
824                         blobmsg_add_string(buf, NULL, "VHT80");
825
826                 if (nret & IWINFO_HTMODE_VHT80_80)
827                         blobmsg_add_string(buf, NULL, "VHT80+80");
828
829                 if (nret & IWINFO_HTMODE_VHT160)
830                         blobmsg_add_string(buf, NULL, "VHT160");
831
832                 blobmsg_close_array(buf, a);
833         }
834
835         if (!iw->hardware_id(devname, (char *)&ids)) {
836                 o2 = blobmsg_open_table(buf, "hardware");
837
838                 a = blobmsg_open_array(buf, "id");
839                 blobmsg_add_u32(buf, NULL, ids.vendor_id);
840                 blobmsg_add_u32(buf, NULL, ids.device_id);
841                 blobmsg_add_u32(buf, NULL, ids.subsystem_vendor_id);
842                 blobmsg_add_u32(buf, NULL, ids.subsystem_device_id);
843                 blobmsg_close_array(buf, a);
844
845                 iw_call_str(iw->hardware_name, devname, buf, "name");
846
847                 blobmsg_close_table(buf, o2);
848         }
849
850         if (!phy_only) {
851                 iw_call_num(iw->quality, devname, buf, "quality");
852                 iw_call_num(iw->quality_max, devname, buf, "quality_max");
853                 iw_call_num(iw->bitrate, devname, buf, "bitrate");
854
855                 if (!iw->mode(devname, &nret))
856                         blobmsg_add_string(buf, "mode", iw_modenames[nret]);
857
858                 iw_call_str(iw->ssid, devname, buf, "ssid");
859                 iw_call_str(iw->bssid, devname, buf, "bssid");
860
861                 if (!iw->encryption(devname, (char *)&crypto)) {
862                         o2 = blobmsg_open_table(buf, "encryption");
863
864                         blobmsg_add_u8(buf, "enabled", crypto.enabled);
865
866                         if (crypto.enabled) {
867                                 if (!crypto.wpa_version) {
868                                         a = blobmsg_open_array(buf, "wep");
869
870                                         if (crypto.auth_algs & IWINFO_AUTH_OPEN)
871                                             blobmsg_add_string(buf, NULL, "open");
872
873                                         if (crypto.auth_algs & IWINFO_AUTH_SHARED)
874                                             blobmsg_add_string(buf, NULL, "shared");
875
876                                         blobmsg_close_array(buf, a);
877                                 }
878                                 else {
879                                         a = blobmsg_open_array(buf, "wpa");
880
881                                         for (nret = 1; nret <= 3; nret++)
882                                                 if (crypto.wpa_version & (1 << (nret - 1)))
883                                                         blobmsg_add_u32(buf, NULL, nret);
884
885                                         blobmsg_close_array(buf, a);
886
887                                         a = blobmsg_open_array(buf, "authentication");
888
889                                         if (crypto.auth_suites & IWINFO_KMGMT_PSK)
890                                                 blobmsg_add_string(buf, NULL, "psk");
891
892                                         if (crypto.auth_suites & IWINFO_KMGMT_8021x)
893                                                 blobmsg_add_string(buf, NULL, "802.1x");
894
895                                         if (crypto.auth_suites & IWINFO_KMGMT_SAE)
896                                                 blobmsg_add_string(buf, NULL, "sae");
897
898                                         if (crypto.auth_suites & IWINFO_KMGMT_OWE)
899                                                 blobmsg_add_string(buf, NULL, "owe");
900
901                                         if (!crypto.auth_suites ||
902                                             (crypto.auth_suites & IWINFO_KMGMT_NONE))
903                                                 blobmsg_add_string(buf, NULL, "none");
904
905                                         blobmsg_close_array(buf, a);
906                                 }
907
908                                 a = blobmsg_open_array(buf, "ciphers");
909                                 nret = crypto.pair_ciphers | crypto.group_ciphers;
910
911                                 if (nret & IWINFO_CIPHER_WEP40)
912                                         blobmsg_add_string(buf, NULL, "wep-40");
913
914                                 if (nret & IWINFO_CIPHER_WEP104)
915                                         blobmsg_add_string(buf, NULL, "wep-104");
916
917                                 if (nret & IWINFO_CIPHER_TKIP)
918                                         blobmsg_add_string(buf, NULL, "tkip");
919
920                                 if (nret & IWINFO_CIPHER_CCMP)
921                                         blobmsg_add_string(buf, NULL, "ccmp");
922
923                                 if (nret & IWINFO_CIPHER_WRAP)
924                                         blobmsg_add_string(buf, NULL, "wrap");
925
926                                 if (nret & IWINFO_CIPHER_AESOCB)
927                                         blobmsg_add_string(buf, NULL, "aes-ocb");
928
929                                 if (nret & IWINFO_CIPHER_CKIP)
930                                         blobmsg_add_string(buf, NULL, "ckip");
931
932                                 if (!nret || (nret & IWINFO_CIPHER_NONE))
933                                         blobmsg_add_string(buf, NULL, "none");
934
935                 blobmsg_close_array(buf, a);
936                         }
937
938                         blobmsg_close_table(buf, o2);
939                 }
940         }
941
942         blobmsg_close_table(buf, o);
943
944         iw_close();
945
946         return true;
947 }
948
949 static void rpc_luci_get_wireless_devices_cb(struct ubus_request *req,
950                                              int type, struct blob_attr *msg)
951 {
952         struct blob_attr *wifi, *cur, *iface, *cur2;
953         struct reply_context *rctx = req->priv;
954         const char *name, *first_ifname;
955         int rem, rem2, rem3, rem4;
956         void *o, *a, *o2;
957
958         blob_for_each_attr(wifi, msg, rem) {
959                 if (blobmsg_type(wifi) != BLOBMSG_TYPE_TABLE ||
960                     blobmsg_name(wifi) == NULL)
961                         continue;
962
963                 o = blobmsg_open_table(&rctx->blob, blobmsg_name(wifi));
964
965                 rem2 = blobmsg_data_len(wifi);
966                 first_ifname = NULL;
967
968                 __blob_for_each_attr(cur, blobmsg_data(wifi), rem2) {
969                         name = blobmsg_name(cur);
970
971                         if (!name || !strcmp(name, "iwinfo")) {
972                                 continue;
973                         }
974                         else if (!strcmp(name, "interfaces")) {
975                                 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY)
976                                         continue;
977
978                                 a = blobmsg_open_array(&rctx->blob, "interfaces");
979
980                                 rem3 = blobmsg_data_len(cur);
981
982                                 __blob_for_each_attr(iface, blobmsg_data(cur), rem3) {
983                                         if (blobmsg_type(iface) != BLOBMSG_TYPE_TABLE)
984                                                 continue;
985
986                                         o2 = blobmsg_open_table(&rctx->blob, NULL);
987
988                                         rem4 = blobmsg_data_len(iface);
989                                         name = NULL;
990
991                                         __blob_for_each_attr(cur2, blobmsg_data(iface), rem4) {
992                                                 if (!strcmp(blobmsg_name(cur2), "ifname"))
993                                                         name = blobmsg_get_string(cur2);
994                                                 else if (!strcmp(blobmsg_name(cur2), "iwinfo"))
995                                                         continue;
996
997                                                 blobmsg_add_blob(&rctx->blob, cur2);
998                                         }
999
1000                                         if (name)
1001                                                 if (rpc_luci_get_iwinfo(&rctx->blob, name, false))
1002                                                         first_ifname = first_ifname ? first_ifname : name;
1003
1004                                         blobmsg_close_table(&rctx->blob, o2);
1005                                 }
1006
1007                                 blobmsg_close_array(&rctx->blob, a);
1008                         }
1009                         else {
1010                                 blobmsg_add_blob(&rctx->blob, cur);
1011                         }
1012                 }
1013
1014                 rpc_luci_get_iwinfo(&rctx->blob,
1015                                     first_ifname ? first_ifname : blobmsg_name(wifi),
1016                                     true);
1017
1018                 blobmsg_close_table(&rctx->blob, o);
1019         }
1020
1021         finish_request(rctx, UBUS_STATUS_OK);
1022 }
1023
1024 static int
1025 rpc_luci_get_wireless_devices(struct ubus_context *ctx,
1026                               struct ubus_object *obj,
1027                               struct ubus_request_data *req,
1028                               const char *method,
1029                               struct blob_attr *msg)
1030 {
1031         struct reply_context *rctx = defer_request(ctx, req);
1032
1033         if (!rctx)
1034                 return UBUS_STATUS_UNKNOWN_ERROR;
1035
1036         if (!invoke_ubus(ctx, "network.wireless", "status", NULL,
1037                          rpc_luci_get_wireless_devices_cb, rctx))
1038                 return finish_request(rctx, UBUS_STATUS_NOT_FOUND);
1039
1040         return UBUS_STATUS_OK;
1041 }
1042
1043 struct host_hint {
1044         struct avl_node avl;
1045         char *hostname;
1046         struct in_addr ip;
1047         struct in6_addr ip6;
1048 };
1049
1050 static int
1051 nl_cb_done(struct nl_msg *msg, void *arg)
1052 {
1053         struct reply_context *rctx = arg;
1054         rctx->pending = 0;
1055         return NL_STOP;
1056 }
1057
1058 static int
1059 nl_cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
1060 {
1061         struct reply_context *rctx = arg;
1062         rctx->pending = 0;
1063         return NL_STOP;
1064 }
1065
1066 static struct host_hint *
1067 rpc_luci_get_host_hint(struct reply_context *rctx, struct ether_addr *ea)
1068 {
1069         struct host_hint *hint;
1070         char *p, *mac;
1071
1072         if (!ea)
1073                 return NULL;
1074
1075         mac = ea2str(ea);
1076         hint = avl_find_element(&rctx->avl, mac, hint, avl);
1077
1078         if (!hint) {
1079                 hint = calloc_a(sizeof(*hint), &p, strlen(mac) + 1);
1080
1081                 if (!hint)
1082                         return NULL;
1083
1084                 hint->avl.key = strcpy(p, mac);
1085                 avl_insert(&rctx->avl, &hint->avl);
1086         }
1087
1088         return hint;
1089 }
1090
1091 static int nl_cb_dump_neigh(struct nl_msg *msg, void *arg)
1092 {
1093         struct reply_context *rctx = arg;
1094         struct ether_addr *mac;
1095         struct in6_addr *dst;
1096         struct nlmsghdr *hdr = nlmsg_hdr(msg);
1097         struct ndmsg *nd = NLMSG_DATA(hdr);
1098         struct nlattr *tb[NDA_MAX+1];
1099         struct host_hint *hint;
1100
1101         rctx->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI);
1102
1103         if (hdr->nlmsg_type != RTM_NEWNEIGH ||
1104             (nd->ndm_family != AF_INET && nd->ndm_family != AF_INET6))
1105                 return NL_SKIP;
1106
1107         if (!(nd->ndm_state & (0xFF & ~NUD_NOARP)))
1108                 return NL_SKIP;
1109
1110         nlmsg_parse(hdr, sizeof(*nd), tb, NDA_MAX, NULL);
1111
1112         mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL;
1113         dst = tb[NDA_DST]    ? RTA_DATA(tb[NDA_DST])    : NULL;
1114
1115         if (!mac || !dst)
1116                 return NL_SKIP;
1117
1118         hint = rpc_luci_get_host_hint(rctx, mac);
1119
1120         if (!hint)
1121                 return NL_SKIP;
1122
1123         if (nd->ndm_family == AF_INET)
1124                 hint->ip = *(struct in_addr *)dst;
1125         else
1126                 hint->ip6 = *(struct in6_addr *)dst;
1127
1128         return NL_SKIP;
1129 }
1130
1131 static void
1132 rpc_luci_get_host_hints_nl(struct reply_context *rctx)
1133 {
1134         struct nl_sock *sock = NULL;
1135         struct nl_msg *msg = NULL;
1136         struct nl_cb *cb = NULL;
1137         struct ndmsg ndm = {};
1138
1139         sock = nl_socket_alloc();
1140
1141         if (!sock)
1142                 goto out;
1143
1144         if (nl_connect(sock, NETLINK_ROUTE))
1145                 goto out;
1146
1147         cb = nl_cb_alloc(NL_CB_DEFAULT);
1148
1149         if (!cb)
1150                 goto out;
1151
1152         msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
1153
1154         if (!msg)
1155                 goto out;
1156
1157         nlmsg_append(msg, &ndm, sizeof(ndm), 0);
1158
1159         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nl_cb_dump_neigh, rctx);
1160         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_cb_done, rctx);
1161         nl_cb_err(cb, NL_CB_CUSTOM, nl_cb_error, rctx);
1162
1163         avl_init(&rctx->avl, avl_strcmp, false, NULL);
1164
1165         rctx->pending = 1;
1166
1167         nl_send_auto_complete(sock, msg);
1168
1169         while (rctx->pending)
1170                 nl_recvmsgs(sock, cb);
1171
1172 out:
1173         if (sock)
1174                 nl_socket_free(sock);
1175
1176         if (cb)
1177                 nl_cb_put(cb);
1178
1179         if (msg)
1180                 nlmsg_free(msg);
1181 }
1182
1183 static void
1184 rpc_luci_get_host_hints_ether(struct reply_context *rctx)
1185 {
1186         struct host_hint *hint;
1187         struct in_addr in;
1188         char buf[512], *p;
1189         FILE *f;
1190
1191         f = fopen("/etc/ethers", "r");
1192
1193         if (!f)
1194                 return;
1195
1196         while (fgets(buf, sizeof(buf), f)) {
1197                 p = strtok(buf, " \t\n");
1198                 hint = rpc_luci_get_host_hint(rctx, p ? ether_aton(p) : NULL);
1199
1200                 if (!hint)
1201                         continue;
1202
1203                 p = strtok(NULL, " \t\n");
1204
1205                 if (!p)
1206                         continue;
1207
1208                 if (inet_pton(AF_INET, p, &in) == 1) {
1209                         if (hint->ip.s_addr == 0)
1210                                 hint->ip = in;
1211                 }
1212                 else if (*p && !hint->hostname) {
1213                         hint->hostname = strdup(p);
1214                 }
1215         }
1216
1217         fclose(f);
1218 }
1219
1220 static void
1221 rpc_luci_get_host_hints_uci(struct reply_context *rctx)
1222 {
1223         struct uci_ptr ptr = { .package = "dhcp" };
1224         struct uci_context *uci = NULL;
1225         struct uci_package *pkg = NULL;
1226         struct in6_addr empty = {};
1227         struct lease_entry *lease;
1228         struct host_hint *hint;
1229         struct uci_element *e, *l;
1230         struct uci_section *s;
1231         struct in_addr in;
1232         char *p, *n;
1233
1234         uci = uci_alloc_context();
1235
1236         if (!uci)
1237                 goto out;
1238
1239         uci_load(uci, ptr.package, &pkg);
1240
1241         if (!pkg)
1242                 goto out;
1243
1244         uci_foreach_element(&pkg->sections, e)
1245         {
1246                 s = uci_to_section(e);
1247
1248                 if (strcmp(s->type, "host"))
1249                         continue;
1250
1251                 ptr.section = s->e.name;
1252                 ptr.s = NULL;
1253
1254                 ptr.option = "ip";
1255                 ptr.o = NULL;
1256
1257                 if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL)
1258                         continue;
1259
1260                 if (ptr.o->type != UCI_TYPE_STRING)
1261                         continue;
1262
1263                 if (inet_pton(AF_INET, ptr.o->v.string, &in) != 1)
1264                         continue;
1265
1266                 ptr.option = "name";
1267                 ptr.o = NULL;
1268
1269                 if (!uci_lookup_ptr(uci, &ptr, NULL, true) && ptr.o != NULL &&
1270                     ptr.o->type == UCI_TYPE_STRING)
1271                     n = ptr.o->v.string;
1272                 else
1273                         n = NULL;
1274
1275                 ptr.option = "mac";
1276                 ptr.o = NULL;
1277
1278                 if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL)
1279                         continue;
1280
1281                 if (ptr.o->type == UCI_TYPE_STRING) {
1282                         for (p = strtok(ptr.o->v.string, " \t");
1283                              p != NULL;
1284                              p = strtok(NULL, " \t")) {
1285                                 hint = rpc_luci_get_host_hint(rctx, ether_aton(p));
1286
1287                                 if (!hint)
1288                                         continue;
1289
1290                                 if (hint->ip.s_addr == 0)
1291                                         hint->ip = in;
1292
1293                                 if (n && !hint->hostname)
1294                                         hint->hostname = strdup(n);
1295                         }
1296                 }
1297                 else if (ptr.o->type == UCI_TYPE_LIST) {
1298                         uci_foreach_element(&ptr.o->v.list, l) {
1299                                 hint = rpc_luci_get_host_hint(rctx, ether_aton(l->name));
1300
1301                                 if (!hint)
1302                                         continue;
1303
1304                                 if (hint->ip.s_addr == 0)
1305                                         hint->ip = in;
1306
1307                                 if (n && !hint->hostname)
1308                                         hint->hostname = strdup(n);
1309                         }
1310                 }
1311         }
1312
1313         lease_open();
1314
1315         while ((lease = lease_next()) != NULL) {
1316                 hint = rpc_luci_get_host_hint(rctx, &lease->mac);
1317
1318                 if (!hint)
1319                         continue;
1320
1321                 if (lease->af == AF_INET && hint->ip.s_addr == 0)
1322                         hint->ip = lease->addr.in;
1323                 else if (lease->af == AF_INET6 &&
1324                          !memcmp(&hint->ip6, &empty, sizeof(empty)))
1325                         hint->ip6 = lease->addr.in6;
1326
1327                 if (lease->hostname && !hint->hostname)
1328                         hint->hostname = strdup(lease->hostname);
1329         }
1330
1331         lease_close();
1332
1333 out:
1334         if (uci)
1335                 uci_free_context(uci);
1336 }
1337
1338 static void
1339 rpc_luci_get_host_hints_ifaddrs(struct reply_context *rctx)
1340 {
1341         struct ether_addr empty_ea = {};
1342         struct in6_addr empty_in6 = {};
1343         struct ifaddrs *ifaddr, *ifa;
1344         struct sockaddr_ll *sll;
1345         struct avl_tree devices;
1346         struct host_hint *hint;
1347         struct {
1348                 struct avl_node avl;
1349                 struct ether_addr ea;
1350                 struct in6_addr in6;
1351                 struct in_addr in;
1352         } *device, *nextdevice;
1353         char *p;
1354
1355         avl_init(&devices, avl_strcmp, false, NULL);
1356
1357         if (getifaddrs(&ifaddr) == -1)
1358                 return;
1359
1360         for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
1361                 if (!ifa->ifa_addr)
1362                         continue;
1363
1364                 device = avl_find_element(&devices, ifa->ifa_name, device, avl);
1365
1366                 if (!device) {
1367                         device = calloc_a(sizeof(*device), &p, strlen(ifa->ifa_name) + 1);
1368
1369                         if (!device)
1370                                 continue;
1371
1372                         device->avl.key = strcpy(p, ifa->ifa_name);
1373                         avl_insert(&devices, &device->avl);
1374                 }
1375
1376                 switch (ifa->ifa_addr->sa_family) {
1377                 case AF_PACKET:
1378                         sll = (struct sockaddr_ll *)ifa->ifa_addr;
1379
1380                         if (sll->sll_halen == 6)
1381                                 memcpy(&device->ea, sll->sll_addr, 6);
1382
1383                         break;
1384
1385                 case AF_INET6:
1386                         device->in6 = ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
1387                         break;
1388
1389                 case AF_INET:
1390                         device->in = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
1391                         break;
1392                 }
1393         }
1394
1395         freeifaddrs(ifaddr);
1396
1397         avl_for_each_element_safe(&devices, device, avl, nextdevice) {
1398                 if (memcmp(&device->ea, &empty_ea, sizeof(empty_ea)) &&
1399                     (memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) ||
1400                      device->in.s_addr != 0)) {
1401                         hint = rpc_luci_get_host_hint(rctx, &device->ea);
1402
1403                         if (hint) {
1404                                 if (hint->ip.s_addr == 0 && device->in.s_addr != 0)
1405                                         hint->ip = device->in;
1406
1407                                 if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6)) == 0 &&
1408                                     memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) != 0)
1409                                         hint->ip6 = device->in6;
1410                         }
1411                 }
1412
1413                 avl_delete(&devices, &device->avl);
1414                 free(device);
1415         }
1416 }
1417
1418 static int
1419 rpc_luci_get_host_hints_finish(struct reply_context *rctx);
1420
1421 static void
1422 rpc_luci_get_host_hints_rrdns_cb(struct ubus_request *req, int type,
1423                                  struct blob_attr *msg)
1424 {
1425         struct reply_context *rctx = req->priv;
1426         struct host_hint *hint;
1427         struct blob_attr *cur;
1428         struct in6_addr in6;
1429         struct in_addr in;
1430         int rem;
1431
1432         if (msg) {
1433                 blob_for_each_attr(cur, msg, rem) {
1434                         if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
1435                                 continue;
1436
1437                         if (inet_pton(AF_INET6, blobmsg_name(cur), &in6) == 1) {
1438                                 avl_for_each_element(&rctx->avl, hint, avl) {
1439                                         if (!memcmp(&hint->ip6, &in6, sizeof(in6))) {
1440                                                 if (hint->hostname)
1441                                                         free(hint->hostname);
1442
1443                                                 hint->hostname = strdup(blobmsg_get_string(cur));
1444                                                 break;
1445                                         }
1446                                 }
1447                         }
1448                         else if (inet_pton(AF_INET, blobmsg_name(cur), &in) == 1) {
1449                                 avl_for_each_element(&rctx->avl, hint, avl) {
1450                                         if (!memcmp(&hint->ip, &in, sizeof(in))) {
1451                                                 if (hint->hostname)
1452                                                         free(hint->hostname);
1453
1454                                                 hint->hostname = strdup(blobmsg_get_string(cur));
1455                                                 break;
1456                                         }
1457                                 }
1458                         }
1459                 }
1460         }
1461
1462         rpc_luci_get_host_hints_finish(rctx);
1463 }
1464
1465 static void
1466 rpc_luci_get_host_hints_rrdns(struct reply_context *rctx)
1467 {
1468         struct in6_addr empty_in6 = {};
1469         char buf[INET6_ADDRSTRLEN];
1470         struct blob_buf req = {};
1471         struct host_hint *hint;
1472         int n = 0;
1473         void *a;
1474
1475         blob_buf_init(&req, 0);
1476
1477         a = blobmsg_open_array(&req, "addrs");
1478
1479         avl_for_each_element(&rctx->avl, hint, avl) {
1480                 if (hint->ip.s_addr != 0) {
1481                         inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
1482                         blobmsg_add_string(&req, NULL, buf);
1483                         n++;
1484                 }
1485                 else if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6))) {
1486                         inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
1487                         blobmsg_add_string(&req, NULL, buf);
1488                         n++;
1489                 }
1490         }
1491
1492         blobmsg_close_array(&req, a);
1493
1494         if (n > 0) {
1495                 blobmsg_add_u32(&req, "timeout", 250);
1496                 blobmsg_add_u32(&req, "limit", n);
1497
1498                 if (!invoke_ubus(rctx->context, "network.rrdns", "lookup", &req,
1499                                  rpc_luci_get_host_hints_rrdns_cb, rctx))
1500                         rpc_luci_get_host_hints_finish(rctx);
1501         }
1502         else {
1503                 rpc_luci_get_host_hints_finish(rctx);
1504         }
1505
1506         blob_buf_free(&req);
1507 }
1508
1509 static int
1510 rpc_luci_get_host_hints_finish(struct reply_context *rctx)
1511 {
1512         struct host_hint *hint, *nexthint;
1513         char buf[INET6_ADDRSTRLEN];
1514         struct in6_addr in6 = {};
1515         struct in_addr in = {};
1516         void *o;
1517
1518         avl_for_each_element_safe(&rctx->avl, hint, avl, nexthint) {
1519                 o = blobmsg_open_table(&rctx->blob, hint->avl.key);
1520
1521                 if (memcmp(&hint->ip, &in, sizeof(in))) {
1522                         inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
1523                         blobmsg_add_string(&rctx->blob, "ipv4", buf);
1524                 }
1525
1526                 if (memcmp(&hint->ip6, &in6, sizeof(in6))) {
1527                         inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
1528                         blobmsg_add_string(&rctx->blob, "ipv6", buf);
1529                 }
1530
1531                 if (hint->hostname)
1532                         blobmsg_add_string(&rctx->blob, "name", hint->hostname);
1533
1534                 blobmsg_close_table(&rctx->blob, o);
1535
1536                 avl_delete(&rctx->avl, &hint->avl);
1537
1538                 if (hint->hostname)
1539                         free(hint->hostname);
1540
1541                 free(hint);
1542         }
1543
1544         return finish_request(rctx, UBUS_STATUS_OK);
1545 }
1546
1547 static int
1548 rpc_luci_get_host_hints(struct ubus_context *ctx, struct ubus_object *obj,
1549                         struct ubus_request_data *req, const char *method,
1550                         struct blob_attr *msg)
1551 {
1552         struct reply_context *rctx = defer_request(ctx, req);
1553
1554         if (!rctx)
1555                 return UBUS_STATUS_UNKNOWN_ERROR;
1556
1557         rpc_luci_get_host_hints_nl(rctx);
1558         rpc_luci_get_host_hints_uci(rctx);
1559         rpc_luci_get_host_hints_ether(rctx);
1560         rpc_luci_get_host_hints_ifaddrs(rctx);
1561         rpc_luci_get_host_hints_rrdns(rctx);
1562
1563         return UBUS_STATUS_OK;
1564 }
1565
1566 static int
1567 rpc_luci_get_board_json(struct ubus_context *ctx, struct ubus_object *obj,
1568                         struct ubus_request_data *req, const char *method,
1569                         struct blob_attr *msg)
1570 {
1571         blob_buf_init(&blob, 0);
1572
1573         if (!blobmsg_add_json_from_file(&blob, "/etc/board.json"))
1574                 return UBUS_STATUS_UNKNOWN_ERROR;
1575
1576         ubus_send_reply(ctx, req, blob.head);
1577         return UBUS_STATUS_OK;
1578 }
1579
1580 static int
1581 rpc_luci_get_dsl_status(struct ubus_context *ctx, struct ubus_object *obj,
1582                         struct ubus_request_data *req, const char *method,
1583                         struct blob_attr *msg)
1584 {
1585         char line[128], *p, *s;
1586         FILE *cmd;
1587
1588         cmd = popen("/etc/init.d/dsl_control lucistat", "r");
1589
1590         if (!cmd)
1591                 return UBUS_STATUS_NOT_FOUND;
1592
1593         blob_buf_init(&blob, 0);
1594
1595         while (fgets(line, sizeof(line), cmd)) {
1596                 if (strncmp(line, "dsl.", 4))
1597                         continue;
1598
1599                 p = strchr(line, '=');
1600
1601                 if (!p)
1602                         continue;
1603
1604                 s = p + strlen(p) - 1;
1605
1606                 while (s >= p && isspace(*s))
1607                         *s-- = 0;
1608
1609                 *p++ = 0;
1610
1611                 if (!strcmp(p, "nil"))
1612                         continue;
1613
1614                 if (isdigit(*p)) {
1615                         blobmsg_add_u32(&blob, line + 4, strtoul(p, NULL, 0));
1616                 }
1617                 else if (*p == '"') {
1618                         s = p + strlen(p) - 1;
1619
1620                         if (s >= p && *s == '"')
1621                                 *s = 0;
1622
1623                         blobmsg_add_string(&blob, line + 4, p + 1);
1624                 }
1625         }
1626
1627         fclose(cmd);
1628
1629         ubus_send_reply(ctx, req, blob.head);
1630         return UBUS_STATUS_OK;
1631 }
1632
1633
1634 enum {
1635         RPC_L_FAMILY,
1636         __RPC_L_MAX,
1637 };
1638
1639 static const struct blobmsg_policy rpc_get_leases_policy[__RPC_L_MAX] = {
1640         [RPC_L_FAMILY] = { .name = "family", .type = BLOBMSG_TYPE_INT32 }
1641 };
1642
1643 static int
1644 rpc_luci_get_dhcp_leases(struct ubus_context *ctx, struct ubus_object *obj,
1645                          struct ubus_request_data *req, const char *method,
1646                          struct blob_attr *msg)
1647 {
1648         struct blob_attr *tb[__RPC_L_MAX];
1649         struct ether_addr emptymac = {};
1650         struct lease_entry *lease;
1651         char s[INET6_ADDRSTRLEN];
1652         struct uci_context *uci;
1653         int af, family = 0;
1654         void *a, *o;
1655
1656         blobmsg_parse(rpc_get_leases_policy, __RPC_L_MAX, tb,
1657                       blob_data(msg), blob_len(msg));
1658
1659         switch (tb[RPC_L_FAMILY] ? blobmsg_get_u32(tb[RPC_L_FAMILY]) : 0) {
1660         case 0:
1661                 family = 0;
1662                 break;
1663
1664         case 4:
1665                 family = AF_INET;
1666                 break;
1667
1668         case 6:
1669                 family = AF_INET6;
1670                 break;
1671
1672         default:
1673                 return UBUS_STATUS_INVALID_ARGUMENT;
1674         }
1675
1676         uci = uci_alloc_context();
1677
1678         if (!uci)
1679                 return UBUS_STATUS_UNKNOWN_ERROR;
1680
1681         blob_buf_init(&blob, 0);
1682
1683         for (af = family ? family : AF_INET;
1684              af != 0;
1685              af = (family == 0) ? (af == AF_INET ? AF_INET6 : 0) : 0) {
1686
1687                 a = blobmsg_open_array(&blob, (af == AF_INET) ? "dhcp_leases"
1688                                                               : "dhcp6_leases");
1689
1690                 lease_open();
1691
1692                 while ((lease = lease_next()) != NULL) {
1693                         if (lease->af != af)
1694                                 continue;
1695
1696                         o = blobmsg_open_table(&blob, NULL);
1697
1698                         if (lease->expire == -1)
1699                                 blobmsg_add_u8(&blob, "expires", 0);
1700                         else
1701                                 blobmsg_add_u32(&blob, "expires", lease->expire);
1702
1703                         if (lease->hostname)
1704                                 blobmsg_add_string(&blob, "hostname", lease->hostname);
1705
1706                         if (memcmp(&lease->mac, &emptymac, sizeof(emptymac)))
1707                                 blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
1708
1709                         if (lease->duid)
1710                                 blobmsg_add_string(&blob, "duid", lease->duid);
1711
1712                         inet_ntop(lease->af, &lease->addr.in6, s, sizeof(s));
1713                         blobmsg_add_string(&blob, (af == AF_INET) ? "ipaddr" : "ip6addr",
1714                                            s);
1715
1716                         blobmsg_close_table(&blob, o);
1717                 }
1718
1719                 lease_close();
1720
1721                 blobmsg_close_array(&blob, a);
1722         }
1723
1724         uci_free_context(uci);
1725         ubus_send_reply(ctx, req, blob.head);
1726
1727         return UBUS_STATUS_OK;
1728 }
1729
1730 static int
1731 rpc_luci_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
1732 {
1733         static const struct ubus_method luci_methods[] = {
1734                 UBUS_METHOD_NOARG("getNetworkDevices", rpc_luci_get_network_devices),
1735                 UBUS_METHOD_NOARG("getWirelessDevices", rpc_luci_get_wireless_devices),
1736                 UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints),
1737                 UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json),
1738                 UBUS_METHOD_NOARG("getDSLStatus", rpc_luci_get_dsl_status),
1739                 UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases, rpc_get_leases_policy)
1740         };
1741
1742         static struct ubus_object_type luci_type =
1743                 UBUS_OBJECT_TYPE("rpcd-luci", luci_methods);
1744
1745         static struct ubus_object obj = {
1746                 .name = "luci-rpc",
1747                 .type = &luci_type,
1748                 .methods = luci_methods,
1749                 .n_methods = ARRAY_SIZE(luci_methods),
1750         };
1751
1752         return ubus_add_object(ctx, &obj);
1753 }
1754
1755 struct rpc_plugin rpc_plugin = {
1756         .init = rpc_luci_api_init
1757 };