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