rpcd-mod-luci: handle DHCPv6 leases in dnsmasq lease file
[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                         p = strtok(NULL, " \t\n");
439
440                         if (p && inet_pton(AF_INET6, p, &e.addr.in6))
441                                 e.af = AF_INET6;
442                         else if (p && inet_pton(AF_INET, p, &e.addr.in))
443                                 e.af = AF_INET;
444                         else
445                                 continue;
446
447                         if (!ea && e.af != AF_INET6)
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                         if (!ea && e.duid)
463                                 ea = duid2ea(e.duid);
464
465                         if (ea)
466                                 e.mac = *ea;
467
468                         return &e;
469                 }
470
471                 fclose(lease_state.dnsmasq_file);
472                 lease_state.dnsmasq_file = NULL;
473         }
474
475         if (lease_state.odhcpd_file) {
476                 while (fgets(e.buf, sizeof(e.buf), lease_state.odhcpd_file)) {
477                         strtok(e.buf, " \t\n"); /* # */
478                         strtok(NULL, " \t\n"); /* iface */
479
480                         e.duid = strtok(NULL, " \t\n"); /* duid */
481
482                         if (!e.duid)
483                                 continue;
484
485                         p = strtok(NULL, " \t\n"); /* iaid */
486
487                         if (p)
488                                 e.af = strcmp(p, "ipv4") ? AF_INET6 : AF_INET;
489                         else
490                                 continue;
491
492                         e.hostname = strtok(NULL, " \t\n"); /* name */
493
494                         if (!e.hostname)
495                                 continue;
496
497                         p = strtok(NULL, " \t\n"); /* ts */
498
499                         if (!p)
500                                 continue;
501
502                         e.expire = strtol(p, NULL, 10);
503                         e.expire = (e.expire > 0) ? e.expire - lease_state.now : e.expire;
504
505                         strtok(NULL, " \t\n"); /* id */
506                         strtok(NULL, " \t\n"); /* length */
507
508                         p = strtok(NULL, "/ \t\n"); /* ip */
509
510                         if (!p || !inet_pton(e.af, p, &e.addr.in6))
511                                 continue;
512
513                         ea = duid2ea(e.duid);
514
515                         if (ea)
516                                 e.mac = *ea;
517
518                         if (!strcmp(e.hostname, "-"))
519                                 e.hostname = NULL;
520
521                         if (!strcmp(e.duid, "-"))
522                                 e.duid = NULL;
523
524                         return &e;
525                 }
526
527                 fclose(lease_state.odhcpd_file);
528                 lease_state.odhcpd_file = NULL;
529         }
530
531         return NULL;
532 }
533
534
535 static void
536 rpc_luci_parse_network_device_sys(const char *name, struct ifaddrs *ifaddr)
537 {
538         char link[64], buf[512], *p;
539         unsigned int ifa_flags = 0;
540         struct sockaddr_ll *sll;
541         struct ifaddrs *ifa;
542         struct dirent *e;
543         void *o, *o2, *a;
544         ssize_t len;
545         uint64_t v;
546         int n, af;
547         DIR *d;
548
549         const char *stats[] = {
550                 "rx_bytes", "tx_bytes", "tx_errors", "rx_errors", "tx_packets",
551                 "rx_packets", "multicast", "collisions", "rx_dropped", "tx_dropped"
552         };
553
554         o = blobmsg_open_table(&blob, name);
555
556         blobmsg_add_string(&blob, "name", name);
557
558         snprintf(buf, sizeof(buf), "/sys/class/net/%s/brif", name);
559
560         d = opendir(buf);
561
562         if (d) {
563                 blobmsg_add_u8(&blob, "bridge", 1);
564
565                 a = blobmsg_open_array(&blob, "ports");
566
567                 while (true) {
568                         e = readdir(d);
569
570                         if (e == NULL)
571                                 break;
572
573                         if (strcmp(e->d_name, ".") && strcmp(e->d_name, ".."))
574                                 blobmsg_add_string(&blob, NULL, e->d_name);
575                 }
576
577                 blobmsg_close_array(&blob, a);
578
579                 closedir(d);
580
581                 p = readstr("/sys/class/net/%s/bridge/bridge_id", name);
582                 blobmsg_add_string(&blob, "id", p);
583
584                 p = readstr("/sys/class/net/%s/bridge/stp_state", name);
585                 blobmsg_add_u8(&blob, "stp", strcmp(p, "0") ? 1 : 0);
586         }
587
588         snprintf(buf, sizeof(buf), "/sys/class/net/%s/master", name);
589         len = readlink(buf, link, sizeof(link) - 1);
590
591         if (len > 0) {
592                 link[len] = 0;
593                 blobmsg_add_string(&blob, "master", basename(link));
594         }
595
596         p = readstr("/sys/class/net/%s/phy80211/index", name);
597         blobmsg_add_u8(&blob, "wireless", *p ? 1 : 0);
598
599         p = readstr("/sys/class/net/%s/operstate", name);
600         blobmsg_add_u8(&blob, "up", !strcmp(p, "up") || !strcmp(p, "unknown"));
601
602         n = atoi(readstr("/sys/class/net/%s/mtu", name));
603         if (n > 0)
604                 blobmsg_add_u32(&blob, "mtu", n);
605
606         n = atoi(readstr("/sys/class/net/%s/tx_queue_len", name));
607         if (n > 0)
608                 blobmsg_add_u32(&blob, "qlen", n);
609
610         p = readstr("/sys/class/net/%s/master", name);
611         if (*p)
612                 blobmsg_add_string(&blob, "master", p);
613
614         for (af = AF_INET; af != 0; af = (af == AF_INET) ? AF_INET6 : 0) {
615                 a = blobmsg_open_array(&blob,
616                                        (af == AF_INET) ? "ipaddrs" : "ip6addrs");
617
618                 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
619                         if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != af)
620                                 continue;
621
622                         if (strcmp(ifa->ifa_name, name))
623                                 continue;
624
625                         o2 = blobmsg_open_table(&blob, NULL);
626
627                         blobmsg_add_string(&blob, "address",
628                                            sa2str(ifa->ifa_addr));
629
630                         blobmsg_add_string(&blob, "netmask",
631                                            sa2str(ifa->ifa_netmask));
632
633                         if (ifa->ifa_dstaddr && (ifa->ifa_flags & IFF_POINTOPOINT))
634                                 blobmsg_add_string(&blob, "remote",
635                                                    sa2str(ifa->ifa_dstaddr));
636                         else if (ifa->ifa_broadaddr && (ifa->ifa_flags & IFF_BROADCAST))
637                                 blobmsg_add_string(&blob, "broadcast",
638                                                    sa2str(ifa->ifa_broadaddr));
639
640                         blobmsg_close_table(&blob, o2);
641
642                         ifa_flags |= ifa->ifa_flags;
643                 }
644
645                 blobmsg_close_array(&blob, a);
646         }
647
648         for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
649                 if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_PACKET)
650                         continue;
651
652                 if (strcmp(ifa->ifa_name, name))
653                         continue;
654
655                 sll = (struct sockaddr_ll *)ifa->ifa_addr;
656
657                 if (sll->sll_hatype == 1)
658                         blobmsg_add_string(&blob, "mac", sa2str(ifa->ifa_addr));
659
660                 blobmsg_add_u32(&blob, "type", sll->sll_hatype);
661                 blobmsg_add_u32(&blob, "ifindex", sll->sll_ifindex);
662
663                 ifa_flags |= ifa->ifa_flags;
664                 break;
665         }
666
667         o2 = blobmsg_open_table(&blob, "stats");
668
669         for (n = 0; n < ARRAY_SIZE(stats); n++) {
670         v = strtoull(readstr("/sys/class/net/%s/statistics/%s",
671                                       name, stats[n]), NULL, 10);
672
673                 blobmsg_add_u64(&blob, stats[n], v);
674         }
675
676         blobmsg_close_table(&blob, o2);
677
678         o2 = blobmsg_open_table(&blob, "flags");
679         blobmsg_add_u8(&blob, "up", ifa_flags & IFF_UP);
680         blobmsg_add_u8(&blob, "broadcast", ifa_flags & IFF_BROADCAST);
681         blobmsg_add_u8(&blob, "promisc", ifa_flags & IFF_PROMISC);
682         blobmsg_add_u8(&blob, "loopback", ifa_flags & IFF_LOOPBACK);
683         blobmsg_add_u8(&blob, "noarp", ifa_flags & IFF_NOARP);
684         blobmsg_add_u8(&blob, "multicast", ifa_flags & IFF_MULTICAST);
685         blobmsg_add_u8(&blob, "pointtopoint", ifa_flags & IFF_POINTOPOINT);
686         blobmsg_close_table(&blob, o2);
687
688         blobmsg_close_table(&blob, o);
689 }
690
691 static int
692 rpc_luci_get_network_devices(struct ubus_context *ctx,
693                              struct ubus_object *obj,
694                              struct ubus_request_data *req,
695                              const char *method,
696                              struct blob_attr *msg)
697 {
698         struct ifaddrs *ifaddr;
699         struct dirent *e;
700         DIR *d;
701
702         blob_buf_init(&blob, 0);
703
704         d = opendir("/sys/class/net");
705
706         if (d != NULL) {
707                 if (getifaddrs(&ifaddr) == 1)
708                         ifaddr = NULL;
709
710                 while (true) {
711                         e = readdir(d);
712
713                         if (e == NULL)
714                                 break;
715
716                         if (strcmp(e->d_name, ".") && strcmp(e->d_name, ".."))
717                                 rpc_luci_parse_network_device_sys(e->d_name, ifaddr);
718                 }
719
720                 if (ifaddr != NULL)
721                         freeifaddrs(ifaddr);
722
723                 closedir(d);
724         }
725
726         ubus_send_reply(ctx, req, blob.head);
727         return 0;
728 }
729
730
731 static void
732 iw_call_str(int (*method)(const char *, char *), const char *dev,
733             struct blob_buf *blob, const char *field)
734 {
735         char buf[IWINFO_BUFSIZE] = {};
736
737         if (method(dev, buf) == 0)
738                 blobmsg_add_string(blob, field, buf);
739 }
740
741 static void
742 iw_call_num(int (*method)(const char *, int *), const char *dev,
743             struct blob_buf *blob, const char *field)
744 {
745         int val = 0;
746
747         if (method(dev, &val) == 0)
748                 blobmsg_add_u32(blob, field, val);
749 }
750
751 static bool rpc_luci_get_iwinfo(struct blob_buf *buf, const char *devname,
752                                 bool phy_only)
753 {
754         struct iwinfo_crypto_entry crypto = {};
755         struct iwinfo_hardware_id ids = {};
756         const struct iwinfo_ops *iw;
757         void *iwlib, *o, *o2, *a;
758         int nret;
759
760         if (!iw_backend || !iw_close || !iw_modenames) {
761                 iwlib = dlopen("libiwinfo.so", RTLD_LOCAL);
762
763                 if (!iwlib)
764                         return false;
765
766                 iw_backend = dlsym(iwlib, "iwinfo_backend");
767                 iw_close = dlsym(iwlib, "iwinfo_close");
768                 iw_modenames = dlsym(iwlib, "IWINFO_OPMODE_NAMES");
769
770                 if (!iw_backend || !iw_close || !iw_modenames)
771                         return false;
772         }
773
774         iw = iw_backend(devname);
775
776         if (!iw)
777                 return false;
778
779         o = blobmsg_open_table(buf, "iwinfo");
780
781         iw_call_num(iw->signal, devname, buf, "signal");
782         iw_call_num(iw->noise, devname, buf, "noise");
783         iw_call_num(iw->channel, devname, buf, "channel");
784         iw_call_str(iw->country, devname, buf, "country");
785         iw_call_str(iw->phyname, devname, buf, "phy");
786         iw_call_num(iw->txpower, devname, buf, "txpower");
787         iw_call_num(iw->txpower_offset, devname, buf, "txpower_offset");
788         iw_call_num(iw->frequency, devname, buf, "frequency");
789         iw_call_num(iw->frequency_offset, devname, buf, "frequency_offset");
790
791         if (!iw->hwmodelist(devname, &nret)) {
792                 a = blobmsg_open_array(buf, "hwmodes");
793
794                 if (nret & IWINFO_80211_AC)
795                         blobmsg_add_string(buf, NULL, "ac");
796
797                 if (nret & IWINFO_80211_A)
798                         blobmsg_add_string(buf, NULL, "a");
799
800                 if (nret & IWINFO_80211_B)
801                         blobmsg_add_string(buf, NULL, "b");
802
803                 if (nret & IWINFO_80211_G)
804                         blobmsg_add_string(buf, NULL, "g");
805
806                 if (nret & IWINFO_80211_N)
807                         blobmsg_add_string(buf, NULL, "n");
808
809                 blobmsg_close_array(buf, a);
810         }
811
812         if (!iw->htmodelist(devname, &nret)) {
813                 a = blobmsg_open_array(buf, "htmodes");
814
815                 if (nret & IWINFO_HTMODE_HT20)
816                         blobmsg_add_string(buf, NULL, "HT20");
817
818                 if (nret & IWINFO_HTMODE_HT40)
819                         blobmsg_add_string(buf, NULL, "HT40");
820
821                 if (nret & IWINFO_HTMODE_VHT20)
822                         blobmsg_add_string(buf, NULL, "VHT20");
823
824                 if (nret & IWINFO_HTMODE_VHT40)
825                         blobmsg_add_string(buf, NULL, "VHT40");
826
827                 if (nret & IWINFO_HTMODE_VHT80)
828                         blobmsg_add_string(buf, NULL, "VHT80");
829
830                 if (nret & IWINFO_HTMODE_VHT80_80)
831                         blobmsg_add_string(buf, NULL, "VHT80+80");
832
833                 if (nret & IWINFO_HTMODE_VHT160)
834                         blobmsg_add_string(buf, NULL, "VHT160");
835
836                 blobmsg_close_array(buf, a);
837         }
838
839         if (!iw->hardware_id(devname, (char *)&ids)) {
840                 o2 = blobmsg_open_table(buf, "hardware");
841
842                 a = blobmsg_open_array(buf, "id");
843                 blobmsg_add_u32(buf, NULL, ids.vendor_id);
844                 blobmsg_add_u32(buf, NULL, ids.device_id);
845                 blobmsg_add_u32(buf, NULL, ids.subsystem_vendor_id);
846                 blobmsg_add_u32(buf, NULL, ids.subsystem_device_id);
847                 blobmsg_close_array(buf, a);
848
849                 iw_call_str(iw->hardware_name, devname, buf, "name");
850
851                 blobmsg_close_table(buf, o2);
852         }
853
854         if (!phy_only) {
855                 iw_call_num(iw->quality, devname, buf, "quality");
856                 iw_call_num(iw->quality_max, devname, buf, "quality_max");
857                 iw_call_num(iw->bitrate, devname, buf, "bitrate");
858
859                 if (!iw->mode(devname, &nret))
860                         blobmsg_add_string(buf, "mode", iw_modenames[nret]);
861
862                 iw_call_str(iw->ssid, devname, buf, "ssid");
863                 iw_call_str(iw->bssid, devname, buf, "bssid");
864
865                 if (!iw->encryption(devname, (char *)&crypto)) {
866                         o2 = blobmsg_open_table(buf, "encryption");
867
868                         blobmsg_add_u8(buf, "enabled", crypto.enabled);
869
870                         if (crypto.enabled) {
871                                 if (!crypto.wpa_version) {
872                                         a = blobmsg_open_array(buf, "wep");
873
874                                         if (crypto.auth_algs & IWINFO_AUTH_OPEN)
875                                             blobmsg_add_string(buf, NULL, "open");
876
877                                         if (crypto.auth_algs & IWINFO_AUTH_SHARED)
878                                             blobmsg_add_string(buf, NULL, "shared");
879
880                                         blobmsg_close_array(buf, a);
881                                 }
882                                 else {
883                                         a = blobmsg_open_array(buf, "wpa");
884
885                                         for (nret = 1; nret <= 3; nret++)
886                                                 if (crypto.wpa_version & (1 << (nret - 1)))
887                                                         blobmsg_add_u32(buf, NULL, nret);
888
889                                         blobmsg_close_array(buf, a);
890
891                                         a = blobmsg_open_array(buf, "authentication");
892
893                                         if (crypto.auth_suites & IWINFO_KMGMT_PSK)
894                                                 blobmsg_add_string(buf, NULL, "psk");
895
896                                         if (crypto.auth_suites & IWINFO_KMGMT_8021x)
897                                                 blobmsg_add_string(buf, NULL, "802.1x");
898
899                                         if (crypto.auth_suites & IWINFO_KMGMT_SAE)
900                                                 blobmsg_add_string(buf, NULL, "sae");
901
902                                         if (crypto.auth_suites & IWINFO_KMGMT_OWE)
903                                                 blobmsg_add_string(buf, NULL, "owe");
904
905                                         if (!crypto.auth_suites ||
906                                             (crypto.auth_suites & IWINFO_KMGMT_NONE))
907                                                 blobmsg_add_string(buf, NULL, "none");
908
909                                         blobmsg_close_array(buf, a);
910                                 }
911
912                                 a = blobmsg_open_array(buf, "ciphers");
913                                 nret = crypto.pair_ciphers | crypto.group_ciphers;
914
915                                 if (nret & IWINFO_CIPHER_WEP40)
916                                         blobmsg_add_string(buf, NULL, "wep-40");
917
918                                 if (nret & IWINFO_CIPHER_WEP104)
919                                         blobmsg_add_string(buf, NULL, "wep-104");
920
921                                 if (nret & IWINFO_CIPHER_TKIP)
922                                         blobmsg_add_string(buf, NULL, "tkip");
923
924                                 if (nret & IWINFO_CIPHER_CCMP)
925                                         blobmsg_add_string(buf, NULL, "ccmp");
926
927                                 if (nret & IWINFO_CIPHER_WRAP)
928                                         blobmsg_add_string(buf, NULL, "wrap");
929
930                                 if (nret & IWINFO_CIPHER_AESOCB)
931                                         blobmsg_add_string(buf, NULL, "aes-ocb");
932
933                                 if (nret & IWINFO_CIPHER_CKIP)
934                                         blobmsg_add_string(buf, NULL, "ckip");
935
936                                 if (!nret || (nret & IWINFO_CIPHER_NONE))
937                                         blobmsg_add_string(buf, NULL, "none");
938
939                 blobmsg_close_array(buf, a);
940                         }
941
942                         blobmsg_close_table(buf, o2);
943                 }
944         }
945
946         blobmsg_close_table(buf, o);
947
948         iw_close();
949
950         return true;
951 }
952
953 static void rpc_luci_get_wireless_devices_cb(struct ubus_request *req,
954                                              int type, struct blob_attr *msg)
955 {
956         struct blob_attr *wifi, *cur, *iface, *cur2;
957         struct reply_context *rctx = req->priv;
958         const char *name, *first_ifname;
959         int rem, rem2, rem3, rem4;
960         void *o, *a, *o2;
961
962         blob_for_each_attr(wifi, msg, rem) {
963                 if (blobmsg_type(wifi) != BLOBMSG_TYPE_TABLE ||
964                     blobmsg_name(wifi) == NULL)
965                         continue;
966
967                 o = blobmsg_open_table(&rctx->blob, blobmsg_name(wifi));
968
969                 rem2 = blobmsg_data_len(wifi);
970                 first_ifname = NULL;
971
972                 __blob_for_each_attr(cur, blobmsg_data(wifi), rem2) {
973                         name = blobmsg_name(cur);
974
975                         if (!name || !strcmp(name, "iwinfo")) {
976                                 continue;
977                         }
978                         else if (!strcmp(name, "interfaces")) {
979                                 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY)
980                                         continue;
981
982                                 a = blobmsg_open_array(&rctx->blob, "interfaces");
983
984                                 rem3 = blobmsg_data_len(cur);
985
986                                 __blob_for_each_attr(iface, blobmsg_data(cur), rem3) {
987                                         if (blobmsg_type(iface) != BLOBMSG_TYPE_TABLE)
988                                                 continue;
989
990                                         o2 = blobmsg_open_table(&rctx->blob, NULL);
991
992                                         rem4 = blobmsg_data_len(iface);
993                                         name = NULL;
994
995                                         __blob_for_each_attr(cur2, blobmsg_data(iface), rem4) {
996                                                 if (!strcmp(blobmsg_name(cur2), "ifname"))
997                                                         name = blobmsg_get_string(cur2);
998                                                 else if (!strcmp(blobmsg_name(cur2), "iwinfo"))
999                                                         continue;
1000
1001                                                 blobmsg_add_blob(&rctx->blob, cur2);
1002                                         }
1003
1004                                         if (name)
1005                                                 if (rpc_luci_get_iwinfo(&rctx->blob, name, false))
1006                                                         first_ifname = first_ifname ? first_ifname : name;
1007
1008                                         blobmsg_close_table(&rctx->blob, o2);
1009                                 }
1010
1011                                 blobmsg_close_array(&rctx->blob, a);
1012                         }
1013                         else {
1014                                 blobmsg_add_blob(&rctx->blob, cur);
1015                         }
1016                 }
1017
1018                 rpc_luci_get_iwinfo(&rctx->blob,
1019                                     first_ifname ? first_ifname : blobmsg_name(wifi),
1020                                     true);
1021
1022                 blobmsg_close_table(&rctx->blob, o);
1023         }
1024
1025         finish_request(rctx, UBUS_STATUS_OK);
1026 }
1027
1028 static int
1029 rpc_luci_get_wireless_devices(struct ubus_context *ctx,
1030                               struct ubus_object *obj,
1031                               struct ubus_request_data *req,
1032                               const char *method,
1033                               struct blob_attr *msg)
1034 {
1035         struct reply_context *rctx = defer_request(ctx, req);
1036
1037         if (!rctx)
1038                 return UBUS_STATUS_UNKNOWN_ERROR;
1039
1040         if (!invoke_ubus(ctx, "network.wireless", "status", NULL,
1041                          rpc_luci_get_wireless_devices_cb, rctx))
1042                 return finish_request(rctx, UBUS_STATUS_NOT_FOUND);
1043
1044         return UBUS_STATUS_OK;
1045 }
1046
1047 struct host_hint {
1048         struct avl_node avl;
1049         char *hostname;
1050         struct in_addr ip;
1051         struct in6_addr ip6;
1052 };
1053
1054 static int
1055 nl_cb_done(struct nl_msg *msg, void *arg)
1056 {
1057         struct reply_context *rctx = arg;
1058         rctx->pending = 0;
1059         return NL_STOP;
1060 }
1061
1062 static int
1063 nl_cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
1064 {
1065         struct reply_context *rctx = arg;
1066         rctx->pending = 0;
1067         return NL_STOP;
1068 }
1069
1070 static struct host_hint *
1071 rpc_luci_get_host_hint(struct reply_context *rctx, struct ether_addr *ea)
1072 {
1073         struct host_hint *hint;
1074         char *p, *mac;
1075
1076         if (!ea)
1077                 return NULL;
1078
1079         mac = ea2str(ea);
1080         hint = avl_find_element(&rctx->avl, mac, hint, avl);
1081
1082         if (!hint) {
1083                 hint = calloc_a(sizeof(*hint), &p, strlen(mac) + 1);
1084
1085                 if (!hint)
1086                         return NULL;
1087
1088                 hint->avl.key = strcpy(p, mac);
1089                 avl_insert(&rctx->avl, &hint->avl);
1090         }
1091
1092         return hint;
1093 }
1094
1095 static int nl_cb_dump_neigh(struct nl_msg *msg, void *arg)
1096 {
1097         struct reply_context *rctx = arg;
1098         struct ether_addr *mac;
1099         struct in6_addr *dst;
1100         struct nlmsghdr *hdr = nlmsg_hdr(msg);
1101         struct ndmsg *nd = NLMSG_DATA(hdr);
1102         struct nlattr *tb[NDA_MAX+1];
1103         struct host_hint *hint;
1104
1105         rctx->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI);
1106
1107         if (hdr->nlmsg_type != RTM_NEWNEIGH ||
1108             (nd->ndm_family != AF_INET && nd->ndm_family != AF_INET6))
1109                 return NL_SKIP;
1110
1111         if (!(nd->ndm_state & (0xFF & ~NUD_NOARP)))
1112                 return NL_SKIP;
1113
1114         nlmsg_parse(hdr, sizeof(*nd), tb, NDA_MAX, NULL);
1115
1116         mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL;
1117         dst = tb[NDA_DST]    ? RTA_DATA(tb[NDA_DST])    : NULL;
1118
1119         if (!mac || !dst)
1120                 return NL_SKIP;
1121
1122         hint = rpc_luci_get_host_hint(rctx, mac);
1123
1124         if (!hint)
1125                 return NL_SKIP;
1126
1127         if (nd->ndm_family == AF_INET)
1128                 hint->ip = *(struct in_addr *)dst;
1129         else
1130                 hint->ip6 = *(struct in6_addr *)dst;
1131
1132         return NL_SKIP;
1133 }
1134
1135 static void
1136 rpc_luci_get_host_hints_nl(struct reply_context *rctx)
1137 {
1138         struct nl_sock *sock = NULL;
1139         struct nl_msg *msg = NULL;
1140         struct nl_cb *cb = NULL;
1141         struct ndmsg ndm = {};
1142
1143         sock = nl_socket_alloc();
1144
1145         if (!sock)
1146                 goto out;
1147
1148         if (nl_connect(sock, NETLINK_ROUTE))
1149                 goto out;
1150
1151         cb = nl_cb_alloc(NL_CB_DEFAULT);
1152
1153         if (!cb)
1154                 goto out;
1155
1156         msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
1157
1158         if (!msg)
1159                 goto out;
1160
1161         nlmsg_append(msg, &ndm, sizeof(ndm), 0);
1162
1163         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nl_cb_dump_neigh, rctx);
1164         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_cb_done, rctx);
1165         nl_cb_err(cb, NL_CB_CUSTOM, nl_cb_error, rctx);
1166
1167         avl_init(&rctx->avl, avl_strcmp, false, NULL);
1168
1169         rctx->pending = 1;
1170
1171         nl_send_auto_complete(sock, msg);
1172
1173         while (rctx->pending)
1174                 nl_recvmsgs(sock, cb);
1175
1176 out:
1177         if (sock)
1178                 nl_socket_free(sock);
1179
1180         if (cb)
1181                 nl_cb_put(cb);
1182
1183         if (msg)
1184                 nlmsg_free(msg);
1185 }
1186
1187 static void
1188 rpc_luci_get_host_hints_ether(struct reply_context *rctx)
1189 {
1190         struct host_hint *hint;
1191         struct in_addr in;
1192         char buf[512], *p;
1193         FILE *f;
1194
1195         f = fopen("/etc/ethers", "r");
1196
1197         if (!f)
1198                 return;
1199
1200         while (fgets(buf, sizeof(buf), f)) {
1201                 p = strtok(buf, " \t\n");
1202                 hint = rpc_luci_get_host_hint(rctx, p ? ether_aton(p) : NULL);
1203
1204                 if (!hint)
1205                         continue;
1206
1207                 p = strtok(NULL, " \t\n");
1208
1209                 if (!p)
1210                         continue;
1211
1212                 if (inet_pton(AF_INET, p, &in) == 1) {
1213                         if (hint->ip.s_addr == 0)
1214                                 hint->ip = in;
1215                 }
1216                 else if (*p && !hint->hostname) {
1217                         hint->hostname = strdup(p);
1218                 }
1219         }
1220
1221         fclose(f);
1222 }
1223
1224 static void
1225 rpc_luci_get_host_hints_uci(struct reply_context *rctx)
1226 {
1227         struct uci_ptr ptr = { .package = "dhcp" };
1228         struct uci_context *uci = NULL;
1229         struct uci_package *pkg = NULL;
1230         struct in6_addr empty = {};
1231         struct lease_entry *lease;
1232         struct host_hint *hint;
1233         struct uci_element *e, *l;
1234         struct uci_section *s;
1235         struct in_addr in;
1236         char *p, *n;
1237
1238         uci = uci_alloc_context();
1239
1240         if (!uci)
1241                 goto out;
1242
1243         uci_load(uci, ptr.package, &pkg);
1244
1245         if (!pkg)
1246                 goto out;
1247
1248         uci_foreach_element(&pkg->sections, e)
1249         {
1250                 s = uci_to_section(e);
1251
1252                 if (strcmp(s->type, "host"))
1253                         continue;
1254
1255                 ptr.section = s->e.name;
1256                 ptr.s = NULL;
1257
1258                 ptr.option = "ip";
1259                 ptr.o = NULL;
1260
1261                 if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL)
1262                         continue;
1263
1264                 if (ptr.o->type != UCI_TYPE_STRING)
1265                         continue;
1266
1267                 if (inet_pton(AF_INET, ptr.o->v.string, &in) != 1)
1268                         continue;
1269
1270                 ptr.option = "name";
1271                 ptr.o = NULL;
1272
1273                 if (!uci_lookup_ptr(uci, &ptr, NULL, true) && ptr.o != NULL &&
1274                     ptr.o->type == UCI_TYPE_STRING)
1275                     n = ptr.o->v.string;
1276                 else
1277                         n = NULL;
1278
1279                 ptr.option = "mac";
1280                 ptr.o = NULL;
1281
1282                 if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL)
1283                         continue;
1284
1285                 if (ptr.o->type == UCI_TYPE_STRING) {
1286                         for (p = strtok(ptr.o->v.string, " \t");
1287                              p != NULL;
1288                              p = strtok(NULL, " \t")) {
1289                                 hint = rpc_luci_get_host_hint(rctx, ether_aton(p));
1290
1291                                 if (!hint)
1292                                         continue;
1293
1294                                 if (hint->ip.s_addr == 0)
1295                                         hint->ip = in;
1296
1297                                 if (n && !hint->hostname)
1298                                         hint->hostname = strdup(n);
1299                         }
1300                 }
1301                 else if (ptr.o->type == UCI_TYPE_LIST) {
1302                         uci_foreach_element(&ptr.o->v.list, l) {
1303                                 hint = rpc_luci_get_host_hint(rctx, ether_aton(l->name));
1304
1305                                 if (!hint)
1306                                         continue;
1307
1308                                 if (hint->ip.s_addr == 0)
1309                                         hint->ip = in;
1310
1311                                 if (n && !hint->hostname)
1312                                         hint->hostname = strdup(n);
1313                         }
1314                 }
1315         }
1316
1317         lease_open();
1318
1319         while ((lease = lease_next()) != NULL) {
1320                 hint = rpc_luci_get_host_hint(rctx, &lease->mac);
1321
1322                 if (!hint)
1323                         continue;
1324
1325                 if (lease->af == AF_INET && hint->ip.s_addr == 0)
1326                         hint->ip = lease->addr.in;
1327                 else if (lease->af == AF_INET6 &&
1328                          !memcmp(&hint->ip6, &empty, sizeof(empty)))
1329                         hint->ip6 = lease->addr.in6;
1330
1331                 if (lease->hostname && !hint->hostname)
1332                         hint->hostname = strdup(lease->hostname);
1333         }
1334
1335         lease_close();
1336
1337 out:
1338         if (uci)
1339                 uci_free_context(uci);
1340 }
1341
1342 static void
1343 rpc_luci_get_host_hints_ifaddrs(struct reply_context *rctx)
1344 {
1345         struct ether_addr empty_ea = {};
1346         struct in6_addr empty_in6 = {};
1347         struct ifaddrs *ifaddr, *ifa;
1348         struct sockaddr_ll *sll;
1349         struct avl_tree devices;
1350         struct host_hint *hint;
1351         struct {
1352                 struct avl_node avl;
1353                 struct ether_addr ea;
1354                 struct in6_addr in6;
1355                 struct in_addr in;
1356         } *device, *nextdevice;
1357         char *p;
1358
1359         avl_init(&devices, avl_strcmp, false, NULL);
1360
1361         if (getifaddrs(&ifaddr) == -1)
1362                 return;
1363
1364         for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
1365                 if (!ifa->ifa_addr)
1366                         continue;
1367
1368                 device = avl_find_element(&devices, ifa->ifa_name, device, avl);
1369
1370                 if (!device) {
1371                         device = calloc_a(sizeof(*device), &p, strlen(ifa->ifa_name) + 1);
1372
1373                         if (!device)
1374                                 continue;
1375
1376                         device->avl.key = strcpy(p, ifa->ifa_name);
1377                         avl_insert(&devices, &device->avl);
1378                 }
1379
1380                 switch (ifa->ifa_addr->sa_family) {
1381                 case AF_PACKET:
1382                         sll = (struct sockaddr_ll *)ifa->ifa_addr;
1383
1384                         if (sll->sll_halen == 6)
1385                                 memcpy(&device->ea, sll->sll_addr, 6);
1386
1387                         break;
1388
1389                 case AF_INET6:
1390                         device->in6 = ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
1391                         break;
1392
1393                 case AF_INET:
1394                         device->in = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
1395                         break;
1396                 }
1397         }
1398
1399         freeifaddrs(ifaddr);
1400
1401         avl_for_each_element_safe(&devices, device, avl, nextdevice) {
1402                 if (memcmp(&device->ea, &empty_ea, sizeof(empty_ea)) &&
1403                     (memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) ||
1404                      device->in.s_addr != 0)) {
1405                         hint = rpc_luci_get_host_hint(rctx, &device->ea);
1406
1407                         if (hint) {
1408                                 if (hint->ip.s_addr == 0 && device->in.s_addr != 0)
1409                                         hint->ip = device->in;
1410
1411                                 if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6)) == 0 &&
1412                                     memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) != 0)
1413                                         hint->ip6 = device->in6;
1414                         }
1415                 }
1416
1417                 avl_delete(&devices, &device->avl);
1418                 free(device);
1419         }
1420 }
1421
1422 static int
1423 rpc_luci_get_host_hints_finish(struct reply_context *rctx);
1424
1425 static void
1426 rpc_luci_get_host_hints_rrdns_cb(struct ubus_request *req, int type,
1427                                  struct blob_attr *msg)
1428 {
1429         struct reply_context *rctx = req->priv;
1430         struct host_hint *hint;
1431         struct blob_attr *cur;
1432         struct in6_addr in6;
1433         struct in_addr in;
1434         int rem;
1435
1436         if (msg) {
1437                 blob_for_each_attr(cur, msg, rem) {
1438                         if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
1439                                 continue;
1440
1441                         if (inet_pton(AF_INET6, blobmsg_name(cur), &in6) == 1) {
1442                                 avl_for_each_element(&rctx->avl, hint, avl) {
1443                                         if (!memcmp(&hint->ip6, &in6, sizeof(in6))) {
1444                                                 if (hint->hostname)
1445                                                         free(hint->hostname);
1446
1447                                                 hint->hostname = strdup(blobmsg_get_string(cur));
1448                                                 break;
1449                                         }
1450                                 }
1451                         }
1452                         else if (inet_pton(AF_INET, blobmsg_name(cur), &in) == 1) {
1453                                 avl_for_each_element(&rctx->avl, hint, avl) {
1454                                         if (!memcmp(&hint->ip, &in, sizeof(in))) {
1455                                                 if (hint->hostname)
1456                                                         free(hint->hostname);
1457
1458                                                 hint->hostname = strdup(blobmsg_get_string(cur));
1459                                                 break;
1460                                         }
1461                                 }
1462                         }
1463                 }
1464         }
1465
1466         rpc_luci_get_host_hints_finish(rctx);
1467 }
1468
1469 static void
1470 rpc_luci_get_host_hints_rrdns(struct reply_context *rctx)
1471 {
1472         struct in6_addr empty_in6 = {};
1473         char buf[INET6_ADDRSTRLEN];
1474         struct blob_buf req = {};
1475         struct host_hint *hint;
1476         int n = 0;
1477         void *a;
1478
1479         blob_buf_init(&req, 0);
1480
1481         a = blobmsg_open_array(&req, "addrs");
1482
1483         avl_for_each_element(&rctx->avl, hint, avl) {
1484                 if (hint->ip.s_addr != 0) {
1485                         inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
1486                         blobmsg_add_string(&req, NULL, buf);
1487                         n++;
1488                 }
1489                 else if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6))) {
1490                         inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
1491                         blobmsg_add_string(&req, NULL, buf);
1492                         n++;
1493                 }
1494         }
1495
1496         blobmsg_close_array(&req, a);
1497
1498         if (n > 0) {
1499                 blobmsg_add_u32(&req, "timeout", 250);
1500                 blobmsg_add_u32(&req, "limit", n);
1501
1502                 if (!invoke_ubus(rctx->context, "network.rrdns", "lookup", &req,
1503                                  rpc_luci_get_host_hints_rrdns_cb, rctx))
1504                         rpc_luci_get_host_hints_finish(rctx);
1505         }
1506         else {
1507                 rpc_luci_get_host_hints_finish(rctx);
1508         }
1509
1510         blob_buf_free(&req);
1511 }
1512
1513 static int
1514 rpc_luci_get_host_hints_finish(struct reply_context *rctx)
1515 {
1516         struct host_hint *hint, *nexthint;
1517         char buf[INET6_ADDRSTRLEN];
1518         struct in6_addr in6 = {};
1519         struct in_addr in = {};
1520         void *o;
1521
1522         avl_for_each_element_safe(&rctx->avl, hint, avl, nexthint) {
1523                 o = blobmsg_open_table(&rctx->blob, hint->avl.key);
1524
1525                 if (memcmp(&hint->ip, &in, sizeof(in))) {
1526                         inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
1527                         blobmsg_add_string(&rctx->blob, "ipv4", buf);
1528                 }
1529
1530                 if (memcmp(&hint->ip6, &in6, sizeof(in6))) {
1531                         inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
1532                         blobmsg_add_string(&rctx->blob, "ipv6", buf);
1533                 }
1534
1535                 if (hint->hostname)
1536                         blobmsg_add_string(&rctx->blob, "name", hint->hostname);
1537
1538                 blobmsg_close_table(&rctx->blob, o);
1539
1540                 avl_delete(&rctx->avl, &hint->avl);
1541
1542                 if (hint->hostname)
1543                         free(hint->hostname);
1544
1545                 free(hint);
1546         }
1547
1548         return finish_request(rctx, UBUS_STATUS_OK);
1549 }
1550
1551 static int
1552 rpc_luci_get_host_hints(struct ubus_context *ctx, struct ubus_object *obj,
1553                         struct ubus_request_data *req, const char *method,
1554                         struct blob_attr *msg)
1555 {
1556         struct reply_context *rctx = defer_request(ctx, req);
1557
1558         if (!rctx)
1559                 return UBUS_STATUS_UNKNOWN_ERROR;
1560
1561         rpc_luci_get_host_hints_nl(rctx);
1562         rpc_luci_get_host_hints_uci(rctx);
1563         rpc_luci_get_host_hints_ether(rctx);
1564         rpc_luci_get_host_hints_ifaddrs(rctx);
1565         rpc_luci_get_host_hints_rrdns(rctx);
1566
1567         return UBUS_STATUS_OK;
1568 }
1569
1570 static int
1571 rpc_luci_get_board_json(struct ubus_context *ctx, struct ubus_object *obj,
1572                         struct ubus_request_data *req, const char *method,
1573                         struct blob_attr *msg)
1574 {
1575         blob_buf_init(&blob, 0);
1576
1577         if (!blobmsg_add_json_from_file(&blob, "/etc/board.json"))
1578                 return UBUS_STATUS_UNKNOWN_ERROR;
1579
1580         ubus_send_reply(ctx, req, blob.head);
1581         return UBUS_STATUS_OK;
1582 }
1583
1584 static int
1585 rpc_luci_get_dsl_status(struct ubus_context *ctx, struct ubus_object *obj,
1586                         struct ubus_request_data *req, const char *method,
1587                         struct blob_attr *msg)
1588 {
1589         char line[128], *p, *s;
1590         FILE *cmd;
1591
1592         cmd = popen("/etc/init.d/dsl_control lucistat", "r");
1593
1594         if (!cmd)
1595                 return UBUS_STATUS_NOT_FOUND;
1596
1597         blob_buf_init(&blob, 0);
1598
1599         while (fgets(line, sizeof(line), cmd)) {
1600                 if (strncmp(line, "dsl.", 4))
1601                         continue;
1602
1603                 p = strchr(line, '=');
1604
1605                 if (!p)
1606                         continue;
1607
1608                 s = p + strlen(p) - 1;
1609
1610                 while (s >= p && isspace(*s))
1611                         *s-- = 0;
1612
1613                 *p++ = 0;
1614
1615                 if (!strcmp(p, "nil"))
1616                         continue;
1617
1618                 if (isdigit(*p)) {
1619                         blobmsg_add_u32(&blob, line + 4, strtoul(p, NULL, 0));
1620                 }
1621                 else if (*p == '"') {
1622                         s = p + strlen(p) - 1;
1623
1624                         if (s >= p && *s == '"')
1625                                 *s = 0;
1626
1627                         blobmsg_add_string(&blob, line + 4, p + 1);
1628                 }
1629         }
1630
1631         fclose(cmd);
1632
1633         ubus_send_reply(ctx, req, blob.head);
1634         return UBUS_STATUS_OK;
1635 }
1636
1637
1638 enum {
1639         RPC_L_FAMILY,
1640         __RPC_L_MAX,
1641 };
1642
1643 static const struct blobmsg_policy rpc_get_leases_policy[__RPC_L_MAX] = {
1644         [RPC_L_FAMILY] = { .name = "family", .type = BLOBMSG_TYPE_INT32 }
1645 };
1646
1647 static int
1648 rpc_luci_get_dhcp_leases(struct ubus_context *ctx, struct ubus_object *obj,
1649                          struct ubus_request_data *req, const char *method,
1650                          struct blob_attr *msg)
1651 {
1652         struct blob_attr *tb[__RPC_L_MAX];
1653         struct ether_addr emptymac = {};
1654         struct lease_entry *lease;
1655         char s[INET6_ADDRSTRLEN];
1656         struct uci_context *uci;
1657         int af, family = 0;
1658         void *a, *o;
1659
1660         blobmsg_parse(rpc_get_leases_policy, __RPC_L_MAX, tb,
1661                       blob_data(msg), blob_len(msg));
1662
1663         switch (tb[RPC_L_FAMILY] ? blobmsg_get_u32(tb[RPC_L_FAMILY]) : 0) {
1664         case 0:
1665                 family = 0;
1666                 break;
1667
1668         case 4:
1669                 family = AF_INET;
1670                 break;
1671
1672         case 6:
1673                 family = AF_INET6;
1674                 break;
1675
1676         default:
1677                 return UBUS_STATUS_INVALID_ARGUMENT;
1678         }
1679
1680         uci = uci_alloc_context();
1681
1682         if (!uci)
1683                 return UBUS_STATUS_UNKNOWN_ERROR;
1684
1685         blob_buf_init(&blob, 0);
1686
1687         for (af = family ? family : AF_INET;
1688              af != 0;
1689              af = (family == 0) ? (af == AF_INET ? AF_INET6 : 0) : 0) {
1690
1691                 a = blobmsg_open_array(&blob, (af == AF_INET) ? "dhcp_leases"
1692                                                               : "dhcp6_leases");
1693
1694                 lease_open();
1695
1696                 while ((lease = lease_next()) != NULL) {
1697                         if (lease->af != af)
1698                                 continue;
1699
1700                         o = blobmsg_open_table(&blob, NULL);
1701
1702                         if (lease->expire == -1)
1703                                 blobmsg_add_u8(&blob, "expires", 0);
1704                         else
1705                                 blobmsg_add_u32(&blob, "expires", lease->expire);
1706
1707                         if (lease->hostname)
1708                                 blobmsg_add_string(&blob, "hostname", lease->hostname);
1709
1710                         if (memcmp(&lease->mac, &emptymac, sizeof(emptymac)))
1711                                 blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
1712
1713                         if (lease->duid)
1714                                 blobmsg_add_string(&blob, "duid", lease->duid);
1715
1716                         inet_ntop(lease->af, &lease->addr.in6, s, sizeof(s));
1717                         blobmsg_add_string(&blob, (af == AF_INET) ? "ipaddr" : "ip6addr",
1718                                            s);
1719
1720                         blobmsg_close_table(&blob, o);
1721                 }
1722
1723                 lease_close();
1724
1725                 blobmsg_close_array(&blob, a);
1726         }
1727
1728         uci_free_context(uci);
1729         ubus_send_reply(ctx, req, blob.head);
1730
1731         return UBUS_STATUS_OK;
1732 }
1733
1734 static int
1735 rpc_luci_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
1736 {
1737         static const struct ubus_method luci_methods[] = {
1738                 UBUS_METHOD_NOARG("getNetworkDevices", rpc_luci_get_network_devices),
1739                 UBUS_METHOD_NOARG("getWirelessDevices", rpc_luci_get_wireless_devices),
1740                 UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints),
1741                 UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json),
1742                 UBUS_METHOD_NOARG("getDSLStatus", rpc_luci_get_dsl_status),
1743                 UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases, rpc_get_leases_policy)
1744         };
1745
1746         static struct ubus_object_type luci_type =
1747                 UBUS_OBJECT_TYPE("rpcd-luci", luci_methods);
1748
1749         static struct ubus_object obj = {
1750                 .name = "luci-rpc",
1751                 .type = &luci_type,
1752                 .methods = luci_methods,
1753                 .n_methods = ARRAY_SIZE(luci_methods),
1754         };
1755
1756         return ubus_add_object(ctx, &obj);
1757 }
1758
1759 struct rpc_plugin rpc_plugin = {
1760         .init = rpc_luci_api_init
1761 };