030dabb82371a2e76171dd07f0343e8c6488602d
[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 p = strstr(readstr("/sys/class/net/%s/uevent", name), "DEVTYPE=");
663 if (p) {
664 for (n = 0, p += strlen("DEVTYPE=");; n++) {
665 if (p[n] == '\0' || p[n] == '\n') {
666 p[n] = 0;
667 blobmsg_add_string(&blob, "devtype", p);
668 break;
669 }
670 }
671 }
672 else {
673 blobmsg_add_string(&blob, "devtype", "ethernet");
674 }
675
676 for (af = AF_INET; af != 0; af = (af == AF_INET) ? AF_INET6 : 0) {
677 a = blobmsg_open_array(&blob,
678 (af == AF_INET) ? "ipaddrs" : "ip6addrs");
679
680 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
681 if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != af)
682 continue;
683
684 if (strcmp(ifa->ifa_name, name))
685 continue;
686
687 o2 = blobmsg_open_table(&blob, NULL);
688
689 blobmsg_add_string(&blob, "address",
690 sa2str(ifa->ifa_addr));
691
692 blobmsg_add_string(&blob, "netmask",
693 sa2str(ifa->ifa_netmask));
694
695 if (ifa->ifa_dstaddr && (ifa->ifa_flags & IFF_POINTOPOINT))
696 blobmsg_add_string(&blob, "remote",
697 sa2str(ifa->ifa_dstaddr));
698 else if (ifa->ifa_broadaddr && (ifa->ifa_flags & IFF_BROADCAST))
699 blobmsg_add_string(&blob, "broadcast",
700 sa2str(ifa->ifa_broadaddr));
701
702 blobmsg_close_table(&blob, o2);
703
704 ifa_flags |= ifa->ifa_flags;
705 }
706
707 blobmsg_close_array(&blob, a);
708 }
709
710 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
711 if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_PACKET)
712 continue;
713
714 if (strcmp(ifa->ifa_name, name))
715 continue;
716
717 sll = (struct sockaddr_ll *)ifa->ifa_addr;
718
719 if (sll->sll_hatype == 1)
720 blobmsg_add_string(&blob, "mac", sa2str(ifa->ifa_addr));
721
722 blobmsg_add_u32(&blob, "type", sll->sll_hatype);
723 blobmsg_add_u32(&blob, "ifindex", sll->sll_ifindex);
724
725 ifa_flags |= ifa->ifa_flags;
726
727 n = atoi(readstr("/sys/class/net/%s/iflink", name));
728
729 if (n != sll->sll_ifindex) {
730 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
731 if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_PACKET)
732 continue;
733
734 sll = (struct sockaddr_ll *)ifa->ifa_addr;
735
736 if (sll->sll_ifindex != n)
737 continue;
738
739 blobmsg_add_string(&blob, "parent", ifa->ifa_name);
740 break;
741 }
742 }
743
744 break;
745 }
746
747 o2 = blobmsg_open_table(&blob, "stats");
748
749 for (n = 0; n < ARRAY_SIZE(stats); n++) {
750 v = strtoull(readstr("/sys/class/net/%s/statistics/%s",
751 name, stats[n]), NULL, 10);
752
753 blobmsg_add_u64(&blob, stats[n], v);
754 }
755
756 blobmsg_close_table(&blob, o2);
757
758 o2 = blobmsg_open_table(&blob, "flags");
759 blobmsg_add_u8(&blob, "up", ifa_flags & IFF_UP);
760 blobmsg_add_u8(&blob, "broadcast", ifa_flags & IFF_BROADCAST);
761 blobmsg_add_u8(&blob, "promisc", ifa_flags & IFF_PROMISC);
762 blobmsg_add_u8(&blob, "loopback", ifa_flags & IFF_LOOPBACK);
763 blobmsg_add_u8(&blob, "noarp", ifa_flags & IFF_NOARP);
764 blobmsg_add_u8(&blob, "multicast", ifa_flags & IFF_MULTICAST);
765 blobmsg_add_u8(&blob, "pointtopoint", ifa_flags & IFF_POINTOPOINT);
766 blobmsg_close_table(&blob, o2);
767
768 blobmsg_close_table(&blob, o);
769 }
770
771 static int
772 rpc_luci_get_network_devices(struct ubus_context *ctx,
773 struct ubus_object *obj,
774 struct ubus_request_data *req,
775 const char *method,
776 struct blob_attr *msg)
777 {
778 struct ifaddrs *ifaddr;
779 struct dirent *e;
780 DIR *d;
781
782 blob_buf_init(&blob, 0);
783
784 d = opendir("/sys/class/net");
785
786 if (d != NULL) {
787 if (getifaddrs(&ifaddr) == 1)
788 ifaddr = NULL;
789
790 while (true) {
791 e = readdir(d);
792
793 if (e == NULL)
794 break;
795
796 if (strcmp(e->d_name, ".") && strcmp(e->d_name, ".."))
797 rpc_luci_parse_network_device_sys(e->d_name, ifaddr);
798 }
799
800 if (ifaddr != NULL)
801 freeifaddrs(ifaddr);
802
803 closedir(d);
804 }
805
806 ubus_send_reply(ctx, req, blob.head);
807 return 0;
808 }
809
810
811 static void
812 iw_call_str(int (*method)(const char *, char *), const char *dev,
813 struct blob_buf *blob, const char *field)
814 {
815 char buf[IWINFO_BUFSIZE] = {};
816
817 if (method(dev, buf) == 0)
818 blobmsg_add_string(blob, field, buf);
819 }
820
821 static void
822 iw_call_num(int (*method)(const char *, int *), const char *dev,
823 struct blob_buf *blob, const char *field)
824 {
825 int val = 0;
826
827 if (method(dev, &val) == 0)
828 blobmsg_add_u32(blob, field, val);
829 }
830
831 static bool rpc_luci_get_iwinfo(struct blob_buf *buf, const char *devname,
832 bool phy_only)
833 {
834 struct iwinfo_crypto_entry crypto = {};
835 struct iwinfo_hardware_id ids = {};
836 const struct iwinfo_ops *iw;
837 void *iwlib = NULL;
838 void *o, *o2, *a;
839 glob_t paths;
840 int nret, i;
841
842 if (!iw_backend || !iw_close || !iw_modenames) {
843 if (glob("/usr/lib/libiwinfo.so*", 0, NULL, &paths) != 0)
844 return false;
845
846 for (i = 0; i < paths.gl_pathc && !iwlib; i++)
847 iwlib = dlopen(paths.gl_pathv[i], RTLD_LAZY | RTLD_LOCAL);
848
849 globfree(&paths);
850
851 if (!iwlib)
852 return false;
853
854 iw_backend = dlsym(iwlib, "iwinfo_backend");
855 iw_close = dlsym(iwlib, "iwinfo_close");
856 iw_modenames = dlsym(iwlib, "IWINFO_OPMODE_NAMES");
857
858 if (!iw_backend || !iw_close || !iw_modenames)
859 return false;
860 }
861
862 iw = iw_backend(devname);
863
864 if (!iw)
865 return false;
866
867 o = blobmsg_open_table(buf, "iwinfo");
868
869 iw_call_num(iw->signal, devname, buf, "signal");
870 iw_call_num(iw->noise, devname, buf, "noise");
871 iw_call_num(iw->channel, devname, buf, "channel");
872 iw_call_str(iw->country, devname, buf, "country");
873 iw_call_str(iw->phyname, devname, buf, "phy");
874 iw_call_num(iw->txpower, devname, buf, "txpower");
875 iw_call_num(iw->txpower_offset, devname, buf, "txpower_offset");
876 iw_call_num(iw->frequency, devname, buf, "frequency");
877 iw_call_num(iw->frequency_offset, devname, buf, "frequency_offset");
878
879 if (!iw->hwmodelist(devname, &nret)) {
880 a = blobmsg_open_array(buf, "hwmodes");
881
882 if (nret & IWINFO_80211_AC)
883 blobmsg_add_string(buf, NULL, "ac");
884
885 if (nret & IWINFO_80211_A)
886 blobmsg_add_string(buf, NULL, "a");
887
888 if (nret & IWINFO_80211_B)
889 blobmsg_add_string(buf, NULL, "b");
890
891 if (nret & IWINFO_80211_G)
892 blobmsg_add_string(buf, NULL, "g");
893
894 if (nret & IWINFO_80211_N)
895 blobmsg_add_string(buf, NULL, "n");
896
897 blobmsg_close_array(buf, a);
898 }
899
900 if (!iw->htmodelist(devname, &nret)) {
901 a = blobmsg_open_array(buf, "htmodes");
902
903 if (nret & IWINFO_HTMODE_HT20)
904 blobmsg_add_string(buf, NULL, "HT20");
905
906 if (nret & IWINFO_HTMODE_HT40)
907 blobmsg_add_string(buf, NULL, "HT40");
908
909 if (nret & IWINFO_HTMODE_VHT20)
910 blobmsg_add_string(buf, NULL, "VHT20");
911
912 if (nret & IWINFO_HTMODE_VHT40)
913 blobmsg_add_string(buf, NULL, "VHT40");
914
915 if (nret & IWINFO_HTMODE_VHT80)
916 blobmsg_add_string(buf, NULL, "VHT80");
917
918 if (nret & IWINFO_HTMODE_VHT80_80)
919 blobmsg_add_string(buf, NULL, "VHT80+80");
920
921 if (nret & IWINFO_HTMODE_VHT160)
922 blobmsg_add_string(buf, NULL, "VHT160");
923
924 blobmsg_close_array(buf, a);
925 }
926
927 if (!iw->hardware_id(devname, (char *)&ids)) {
928 o2 = blobmsg_open_table(buf, "hardware");
929
930 a = blobmsg_open_array(buf, "id");
931 blobmsg_add_u32(buf, NULL, ids.vendor_id);
932 blobmsg_add_u32(buf, NULL, ids.device_id);
933 blobmsg_add_u32(buf, NULL, ids.subsystem_vendor_id);
934 blobmsg_add_u32(buf, NULL, ids.subsystem_device_id);
935 blobmsg_close_array(buf, a);
936
937 iw_call_str(iw->hardware_name, devname, buf, "name");
938
939 blobmsg_close_table(buf, o2);
940 }
941
942 if (!phy_only) {
943 iw_call_num(iw->quality, devname, buf, "quality");
944 iw_call_num(iw->quality_max, devname, buf, "quality_max");
945 iw_call_num(iw->bitrate, devname, buf, "bitrate");
946
947 if (!iw->mode(devname, &nret))
948 blobmsg_add_string(buf, "mode", iw_modenames[nret]);
949
950 iw_call_str(iw->ssid, devname, buf, "ssid");
951 iw_call_str(iw->bssid, devname, buf, "bssid");
952
953 if (!iw->encryption(devname, (char *)&crypto)) {
954 o2 = blobmsg_open_table(buf, "encryption");
955
956 blobmsg_add_u8(buf, "enabled", crypto.enabled);
957
958 if (crypto.enabled) {
959 if (!crypto.wpa_version) {
960 a = blobmsg_open_array(buf, "wep");
961
962 if (crypto.auth_algs & IWINFO_AUTH_OPEN)
963 blobmsg_add_string(buf, NULL, "open");
964
965 if (crypto.auth_algs & IWINFO_AUTH_SHARED)
966 blobmsg_add_string(buf, NULL, "shared");
967
968 blobmsg_close_array(buf, a);
969 }
970 else {
971 a = blobmsg_open_array(buf, "wpa");
972
973 for (nret = 1; nret <= 3; nret++)
974 if (crypto.wpa_version & (1 << (nret - 1)))
975 blobmsg_add_u32(buf, NULL, nret);
976
977 blobmsg_close_array(buf, a);
978
979 a = blobmsg_open_array(buf, "authentication");
980
981 if (crypto.auth_suites & IWINFO_KMGMT_PSK)
982 blobmsg_add_string(buf, NULL, "psk");
983
984 if (crypto.auth_suites & IWINFO_KMGMT_8021x)
985 blobmsg_add_string(buf, NULL, "802.1x");
986
987 if (crypto.auth_suites & IWINFO_KMGMT_SAE)
988 blobmsg_add_string(buf, NULL, "sae");
989
990 if (crypto.auth_suites & IWINFO_KMGMT_OWE)
991 blobmsg_add_string(buf, NULL, "owe");
992
993 if (!crypto.auth_suites ||
994 (crypto.auth_suites & IWINFO_KMGMT_NONE))
995 blobmsg_add_string(buf, NULL, "none");
996
997 blobmsg_close_array(buf, a);
998 }
999
1000 a = blobmsg_open_array(buf, "ciphers");
1001 nret = crypto.pair_ciphers | crypto.group_ciphers;
1002
1003 if (nret & IWINFO_CIPHER_WEP40)
1004 blobmsg_add_string(buf, NULL, "wep-40");
1005
1006 if (nret & IWINFO_CIPHER_WEP104)
1007 blobmsg_add_string(buf, NULL, "wep-104");
1008
1009 if (nret & IWINFO_CIPHER_TKIP)
1010 blobmsg_add_string(buf, NULL, "tkip");
1011
1012 if (nret & IWINFO_CIPHER_CCMP)
1013 blobmsg_add_string(buf, NULL, "ccmp");
1014
1015 if (nret & IWINFO_CIPHER_WRAP)
1016 blobmsg_add_string(buf, NULL, "wrap");
1017
1018 if (nret & IWINFO_CIPHER_AESOCB)
1019 blobmsg_add_string(buf, NULL, "aes-ocb");
1020
1021 if (nret & IWINFO_CIPHER_CKIP)
1022 blobmsg_add_string(buf, NULL, "ckip");
1023
1024 if (!nret || (nret & IWINFO_CIPHER_NONE))
1025 blobmsg_add_string(buf, NULL, "none");
1026
1027 blobmsg_close_array(buf, a);
1028 }
1029
1030 blobmsg_close_table(buf, o2);
1031 }
1032 }
1033
1034 blobmsg_close_table(buf, o);
1035
1036 iw_close();
1037
1038 return true;
1039 }
1040
1041 static void rpc_luci_get_wireless_devices_cb(struct ubus_request *req,
1042 int type, struct blob_attr *msg)
1043 {
1044 struct blob_attr *wifi, *cur, *iface, *cur2;
1045 struct reply_context *rctx = req->priv;
1046 const char *name, *first_ifname;
1047 int rem, rem2, rem3, rem4;
1048 void *o, *a, *o2;
1049
1050 blob_for_each_attr(wifi, msg, rem) {
1051 if (blobmsg_type(wifi) != BLOBMSG_TYPE_TABLE ||
1052 blobmsg_name(wifi) == NULL)
1053 continue;
1054
1055 o = blobmsg_open_table(&rctx->blob, blobmsg_name(wifi));
1056
1057 rem2 = blobmsg_data_len(wifi);
1058 first_ifname = NULL;
1059
1060 __blob_for_each_attr(cur, blobmsg_data(wifi), rem2) {
1061 name = blobmsg_name(cur);
1062
1063 if (!name || !strcmp(name, "iwinfo")) {
1064 continue;
1065 }
1066 else if (!strcmp(name, "interfaces")) {
1067 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY)
1068 continue;
1069
1070 a = blobmsg_open_array(&rctx->blob, "interfaces");
1071
1072 rem3 = blobmsg_data_len(cur);
1073
1074 __blob_for_each_attr(iface, blobmsg_data(cur), rem3) {
1075 if (blobmsg_type(iface) != BLOBMSG_TYPE_TABLE)
1076 continue;
1077
1078 o2 = blobmsg_open_table(&rctx->blob, NULL);
1079
1080 rem4 = blobmsg_data_len(iface);
1081 name = NULL;
1082
1083 __blob_for_each_attr(cur2, blobmsg_data(iface), rem4) {
1084 if (!strcmp(blobmsg_name(cur2), "ifname"))
1085 name = blobmsg_get_string(cur2);
1086 else if (!strcmp(blobmsg_name(cur2), "iwinfo"))
1087 continue;
1088
1089 blobmsg_add_blob(&rctx->blob, cur2);
1090 }
1091
1092 if (name)
1093 if (rpc_luci_get_iwinfo(&rctx->blob, name, false))
1094 first_ifname = first_ifname ? first_ifname : name;
1095
1096 blobmsg_close_table(&rctx->blob, o2);
1097 }
1098
1099 blobmsg_close_array(&rctx->blob, a);
1100 }
1101 else {
1102 blobmsg_add_blob(&rctx->blob, cur);
1103 }
1104 }
1105
1106 rpc_luci_get_iwinfo(&rctx->blob,
1107 first_ifname ? first_ifname : blobmsg_name(wifi),
1108 true);
1109
1110 blobmsg_close_table(&rctx->blob, o);
1111 }
1112
1113 finish_request(rctx, UBUS_STATUS_OK);
1114 }
1115
1116 static int
1117 rpc_luci_get_wireless_devices(struct ubus_context *ctx,
1118 struct ubus_object *obj,
1119 struct ubus_request_data *req,
1120 const char *method,
1121 struct blob_attr *msg)
1122 {
1123 struct reply_context *rctx = defer_request(ctx, req);
1124
1125 if (!rctx)
1126 return UBUS_STATUS_UNKNOWN_ERROR;
1127
1128 if (!invoke_ubus(ctx, "network.wireless", "status", NULL,
1129 rpc_luci_get_wireless_devices_cb, rctx))
1130 return finish_request(rctx, UBUS_STATUS_NOT_FOUND);
1131
1132 return UBUS_STATUS_OK;
1133 }
1134
1135 struct host_hint {
1136 struct avl_node avl;
1137 char *hostname;
1138 struct avl_tree ipaddrs;
1139 struct avl_tree ip6addrs;
1140 };
1141
1142 /* used to ignore priority with avl_find_element */
1143 #define HOST_HINT_PRIO_IGNORE -1
1144
1145 /* higher (larger) priority addresses are listed first */
1146 #define HOST_HINT_PRIO_NL 10 /* neighbor table */
1147 #define HOST_HINT_PRIO_ETHER 50 /* /etc/ethers */
1148 #define HOST_HINT_PRIO_LEASEFILE 100 /* dhcp leasefile */
1149 #define HOST_HINT_PRIO_RRDNS 100 /* rrdns */
1150 #define HOST_HINT_PRIO_IFADDRS 200 /* getifaddrs() */
1151 #define HOST_HINT_PRIO_STATIC_LEASE 200 /* uci static leases */
1152
1153 struct host_hint_addr {
1154 struct avl_node avl;
1155 int af;
1156 int prio;
1157 union {
1158 struct in_addr in;
1159 struct in6_addr in6;
1160 } addr;
1161 };
1162
1163 static int
1164 host_hint_addr_avl_cmp(const void *k1, const void *k2, void *ptr)
1165 {
1166 struct host_hint_addr *a1 = (struct host_hint_addr *)k1;
1167 struct host_hint_addr *a2 = (struct host_hint_addr *)k2;
1168
1169 if (a1->prio != a2->prio &&
1170 a1->prio != HOST_HINT_PRIO_IGNORE &&
1171 a2->prio != HOST_HINT_PRIO_IGNORE)
1172 return a1->prio < a2->prio ? 1 : -1;
1173
1174 if (a1->af != a2->af)
1175 return a1->af < a2->af ? -1 : 1;
1176
1177 return memcmp(&a1->addr, &a2->addr, sizeof(a1->addr));
1178 }
1179
1180 static int
1181 nl_cb_done(struct nl_msg *msg, void *arg)
1182 {
1183 struct reply_context *rctx = arg;
1184 rctx->pending = 0;
1185 return NL_STOP;
1186 }
1187
1188 static int
1189 nl_cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
1190 {
1191 struct reply_context *rctx = arg;
1192 rctx->pending = 0;
1193 return NL_STOP;
1194 }
1195
1196 static struct host_hint *
1197 rpc_luci_get_host_hint(struct reply_context *rctx, struct ether_addr *ea)
1198 {
1199 struct host_hint *hint;
1200 char *p, *mac;
1201
1202 if (!ea)
1203 return NULL;
1204
1205 mac = ea2str(ea);
1206 hint = avl_find_element(&rctx->avl, mac, hint, avl);
1207
1208 if (!hint) {
1209 hint = calloc_a(sizeof(*hint), &p, strlen(mac) + 1);
1210
1211 if (!hint)
1212 return NULL;
1213
1214 hint->avl.key = strcpy(p, mac);
1215 avl_init(&hint->ipaddrs, host_hint_addr_avl_cmp, false, NULL);
1216 avl_init(&hint->ip6addrs, host_hint_addr_avl_cmp, false, NULL);
1217 avl_insert(&rctx->avl, &hint->avl);
1218 }
1219
1220 return hint;
1221 }
1222
1223 static void
1224 rpc_luci_add_host_hint_addr(struct host_hint *hint, int af, int prio, void *addr)
1225 {
1226 struct host_hint_addr e, *a;
1227 struct avl_tree *addrs = af == AF_INET ? &hint->ipaddrs : &hint->ip6addrs;
1228
1229 if (!addr)
1230 return;
1231
1232 memset(&e, 0, sizeof(e));
1233 e.af = af;
1234 /* ignore prio when comparing against existing addresses */
1235 e.prio = HOST_HINT_PRIO_IGNORE;
1236
1237 if (af == AF_INET)
1238 memcpy(&e.addr.in, (struct in_addr *)addr, sizeof(e.addr.in));
1239 else
1240 memcpy(&e.addr.in6, (struct in6_addr *)addr, sizeof(e.addr.in6));
1241
1242 a = avl_find_element(addrs, &e, a, avl);
1243
1244 if (a) {
1245 /* update prio of existing address if higher */
1246 if (prio <= a->prio)
1247 return;
1248
1249 avl_delete(addrs, &a->avl);
1250 a->prio = prio;
1251 avl_insert(addrs, &a->avl);
1252 return;
1253 }
1254
1255 a = calloc(1, sizeof(*a));
1256
1257 if (!a)
1258 return;
1259
1260 memcpy(a, &e, sizeof(*a));
1261 a->prio = prio;
1262 a->avl.key = a;
1263 avl_insert(addrs, &a->avl);
1264 }
1265
1266 static void
1267 rpc_luci_add_host_hint_ipaddr(struct host_hint *hint, int prio, struct in_addr *addr)
1268 {
1269 return rpc_luci_add_host_hint_addr(hint, AF_INET, prio, (void *)addr);
1270 }
1271
1272 static void
1273 rpc_luci_add_host_hint_ip6addr(struct host_hint *hint, int prio, struct in6_addr *addr)
1274 {
1275 return rpc_luci_add_host_hint_addr(hint, AF_INET6, prio, (void *)addr);
1276 }
1277
1278 static int nl_cb_dump_neigh(struct nl_msg *msg, void *arg)
1279 {
1280 struct reply_context *rctx = arg;
1281 struct ether_addr *mac;
1282 struct in6_addr *dst;
1283 struct nlmsghdr *hdr = nlmsg_hdr(msg);
1284 struct ndmsg *nd = NLMSG_DATA(hdr);
1285 struct nlattr *tb[NDA_MAX+1];
1286 struct host_hint *hint;
1287
1288 rctx->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI);
1289
1290 if (hdr->nlmsg_type != RTM_NEWNEIGH ||
1291 (nd->ndm_family != AF_INET && nd->ndm_family != AF_INET6))
1292 return NL_SKIP;
1293
1294 if (!(nd->ndm_state & (0xFF & ~NUD_NOARP)))
1295 return NL_SKIP;
1296
1297 nlmsg_parse(hdr, sizeof(*nd), tb, NDA_MAX, NULL);
1298
1299 mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL;
1300 dst = tb[NDA_DST] ? RTA_DATA(tb[NDA_DST]) : NULL;
1301
1302 if (!mac || !dst)
1303 return NL_SKIP;
1304
1305 hint = rpc_luci_get_host_hint(rctx, mac);
1306
1307 if (!hint)
1308 return NL_SKIP;
1309
1310 if (nd->ndm_family == AF_INET)
1311 rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_NL, (struct in_addr *)dst);
1312 else
1313 rpc_luci_add_host_hint_ip6addr(hint, HOST_HINT_PRIO_NL, (struct in6_addr *)dst);
1314
1315 return NL_SKIP;
1316 }
1317
1318 static void
1319 rpc_luci_get_host_hints_nl(struct reply_context *rctx)
1320 {
1321 struct nl_sock *sock = NULL;
1322 struct nl_msg *msg = NULL;
1323 struct nl_cb *cb = NULL;
1324 struct ndmsg ndm = {};
1325
1326 sock = nl_socket_alloc();
1327
1328 if (!sock)
1329 goto out;
1330
1331 if (nl_connect(sock, NETLINK_ROUTE))
1332 goto out;
1333
1334 cb = nl_cb_alloc(NL_CB_DEFAULT);
1335
1336 if (!cb)
1337 goto out;
1338
1339 msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
1340
1341 if (!msg)
1342 goto out;
1343
1344 nlmsg_append(msg, &ndm, sizeof(ndm), 0);
1345
1346 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nl_cb_dump_neigh, rctx);
1347 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_cb_done, rctx);
1348 nl_cb_err(cb, NL_CB_CUSTOM, nl_cb_error, rctx);
1349
1350 avl_init(&rctx->avl, avl_strcmp, false, NULL);
1351
1352 rctx->pending = 1;
1353
1354 nl_send_auto_complete(sock, msg);
1355
1356 while (rctx->pending)
1357 nl_recvmsgs(sock, cb);
1358
1359 out:
1360 if (sock)
1361 nl_socket_free(sock);
1362
1363 if (cb)
1364 nl_cb_put(cb);
1365
1366 if (msg)
1367 nlmsg_free(msg);
1368 }
1369
1370 static void
1371 rpc_luci_get_host_hints_ether(struct reply_context *rctx)
1372 {
1373 struct host_hint *hint;
1374 struct in_addr in;
1375 char buf[512], *p;
1376 FILE *f;
1377
1378 f = fopen("/etc/ethers", "r");
1379
1380 if (!f)
1381 return;
1382
1383 while (fgets(buf, sizeof(buf), f)) {
1384 p = strtok(buf, " \t\n");
1385 hint = rpc_luci_get_host_hint(rctx, p ? ether_aton(p) : NULL);
1386
1387 if (!hint)
1388 continue;
1389
1390 p = strtok(NULL, " \t\n");
1391
1392 if (!p)
1393 continue;
1394
1395 if (inet_pton(AF_INET, p, &in) == 1) {
1396 rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_ETHER, &in);
1397 }
1398 else if (*p && !hint->hostname) {
1399 hint->hostname = strdup(p);
1400 }
1401 }
1402
1403 fclose(f);
1404 }
1405
1406 static void
1407 rpc_luci_get_host_hints_uci(struct reply_context *rctx)
1408 {
1409 struct uci_ptr ptr = { .package = "dhcp" };
1410 struct uci_context *uci = NULL;
1411 struct uci_package *pkg = NULL;
1412 struct lease_entry *lease;
1413 struct host_hint *hint;
1414 struct uci_element *e, *l;
1415 struct uci_section *s;
1416 struct in_addr in;
1417 char *p, *n;
1418 int i;
1419
1420 uci = uci_alloc_context();
1421
1422 if (!uci)
1423 goto out;
1424
1425 uci_load(uci, ptr.package, &pkg);
1426
1427 if (!pkg)
1428 goto out;
1429
1430 uci_foreach_element(&pkg->sections, e)
1431 {
1432 s = uci_to_section(e);
1433
1434 if (strcmp(s->type, "host"))
1435 continue;
1436
1437 ptr.section = s->e.name;
1438 ptr.s = NULL;
1439
1440 ptr.option = "ip";
1441 ptr.o = NULL;
1442
1443 if (!uci_lookup_ptr(uci, &ptr, NULL, true) && ptr.o != NULL &&
1444 ptr.o->type != UCI_TYPE_STRING)
1445 n = ptr.o->v.string;
1446 else
1447 n = NULL;
1448
1449 if (!n || inet_pton(AF_INET, n, &in) != 1)
1450 in.s_addr = 0;
1451
1452 ptr.option = "name";
1453 ptr.o = NULL;
1454
1455 if (!uci_lookup_ptr(uci, &ptr, NULL, true) && ptr.o != NULL &&
1456 ptr.o->type == UCI_TYPE_STRING)
1457 n = ptr.o->v.string;
1458 else
1459 n = NULL;
1460
1461 ptr.option = "mac";
1462 ptr.o = NULL;
1463
1464 if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL)
1465 continue;
1466
1467 if (ptr.o->type == UCI_TYPE_STRING) {
1468 for (p = strtok(ptr.o->v.string, " \t");
1469 p != NULL;
1470 p = strtok(NULL, " \t")) {
1471 hint = rpc_luci_get_host_hint(rctx, ether_aton(p));
1472
1473 if (!hint)
1474 continue;
1475
1476 if (in.s_addr != 0)
1477 rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_STATIC_LEASE, &in);
1478
1479 if (n && !hint->hostname)
1480 hint->hostname = strdup(n);
1481 }
1482 }
1483 else if (ptr.o->type == UCI_TYPE_LIST) {
1484 uci_foreach_element(&ptr.o->v.list, l) {
1485 hint = rpc_luci_get_host_hint(rctx, ether_aton(l->name));
1486
1487 if (!hint)
1488 continue;
1489
1490 if (in.s_addr != 0)
1491 rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_STATIC_LEASE, &in);
1492
1493 if (n && !hint->hostname)
1494 hint->hostname = strdup(n);
1495 }
1496 }
1497 }
1498
1499 lease_open();
1500
1501 while ((lease = lease_next()) != NULL) {
1502 hint = rpc_luci_get_host_hint(rctx, &lease->mac);
1503
1504 if (!hint)
1505 continue;
1506
1507 for (i = 0; i < lease->n_addr; i++) {
1508 if (lease->af == AF_INET)
1509 rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_LEASEFILE, &lease->addr[i].in);
1510 else if (lease->af == AF_INET6)
1511 rpc_luci_add_host_hint_ip6addr(hint, HOST_HINT_PRIO_LEASEFILE, &lease->addr[i].in6);
1512 }
1513
1514 if (lease->hostname && !hint->hostname)
1515 hint->hostname = strdup(lease->hostname);
1516 }
1517
1518 lease_close();
1519
1520 out:
1521 if (uci)
1522 uci_free_context(uci);
1523 }
1524
1525 static void
1526 rpc_luci_get_host_hints_ifaddrs(struct reply_context *rctx)
1527 {
1528 struct ether_addr empty_ea = {};
1529 struct in6_addr empty_in6 = {};
1530 struct ifaddrs *ifaddr, *ifa;
1531 struct sockaddr_ll *sll;
1532 struct avl_tree devices;
1533 struct host_hint *hint;
1534 struct {
1535 struct avl_node avl;
1536 struct ether_addr ea;
1537 struct in6_addr in6;
1538 struct in_addr in;
1539 } *device, *nextdevice;
1540 char *p;
1541
1542 avl_init(&devices, avl_strcmp, false, NULL);
1543
1544 if (getifaddrs(&ifaddr) == -1)
1545 return;
1546
1547 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
1548 if (!ifa->ifa_addr)
1549 continue;
1550
1551 device = avl_find_element(&devices, ifa->ifa_name, device, avl);
1552
1553 if (!device) {
1554 device = calloc_a(sizeof(*device), &p, strlen(ifa->ifa_name) + 1);
1555
1556 if (!device)
1557 continue;
1558
1559 device->avl.key = strcpy(p, ifa->ifa_name);
1560 avl_insert(&devices, &device->avl);
1561 }
1562
1563 switch (ifa->ifa_addr->sa_family) {
1564 case AF_PACKET:
1565 sll = (struct sockaddr_ll *)ifa->ifa_addr;
1566
1567 if (sll->sll_halen == 6)
1568 memcpy(&device->ea, sll->sll_addr, 6);
1569
1570 break;
1571
1572 case AF_INET6:
1573 device->in6 = ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
1574 break;
1575
1576 case AF_INET:
1577 device->in = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
1578 break;
1579 }
1580 }
1581
1582 freeifaddrs(ifaddr);
1583
1584 avl_remove_all_elements(&devices, device, avl, nextdevice) {
1585 if (memcmp(&device->ea, &empty_ea, sizeof(empty_ea)) &&
1586 (memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) ||
1587 device->in.s_addr != 0)) {
1588 hint = rpc_luci_get_host_hint(rctx, &device->ea);
1589
1590 if (hint) {
1591 if (device->in.s_addr != 0)
1592 rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_IFADDRS, &device->in);
1593
1594 if (memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) != 0)
1595 rpc_luci_add_host_hint_ip6addr(hint, HOST_HINT_PRIO_IFADDRS, &device->in6);
1596 }
1597 }
1598
1599 free(device);
1600 }
1601 }
1602
1603 static int
1604 rpc_luci_get_host_hints_finish(struct reply_context *rctx);
1605
1606 static void
1607 rpc_luci_get_host_hints_rrdns_cb(struct ubus_request *req, int type,
1608 struct blob_attr *msg)
1609 {
1610 struct reply_context *rctx = req->priv;
1611 struct host_hint *hint;
1612 struct blob_attr *cur;
1613 struct in6_addr in6;
1614 struct in_addr in;
1615 int rem;
1616
1617 if (msg) {
1618 blob_for_each_attr(cur, msg, rem) {
1619 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
1620 continue;
1621
1622 if (inet_pton(AF_INET6, blobmsg_name(cur), &in6) == 1) {
1623 avl_for_each_element(&rctx->avl, hint, avl) {
1624 rpc_luci_add_host_hint_ip6addr(hint, HOST_HINT_PRIO_RRDNS, &in6);
1625 if (!avl_is_empty(&hint->ip6addrs)) {
1626 if (hint->hostname)
1627 free(hint->hostname);
1628
1629 hint->hostname = strdup(blobmsg_get_string(cur));
1630 break;
1631 }
1632 }
1633 }
1634 else if (inet_pton(AF_INET, blobmsg_name(cur), &in) == 1) {
1635 avl_for_each_element(&rctx->avl, hint, avl) {
1636 rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_RRDNS, &in);
1637 if (!avl_is_empty(&hint->ipaddrs)) {
1638 if (hint->hostname)
1639 free(hint->hostname);
1640
1641 hint->hostname = strdup(blobmsg_get_string(cur));
1642 break;
1643 }
1644 }
1645 }
1646 }
1647 }
1648
1649 rpc_luci_get_host_hints_finish(rctx);
1650 }
1651
1652 static void
1653 rpc_luci_get_host_hints_rrdns(struct reply_context *rctx)
1654 {
1655 struct in6_addr empty_in6 = {};
1656 char buf[INET6_ADDRSTRLEN];
1657 struct blob_buf req = {};
1658 struct host_hint *hint;
1659 struct host_hint_addr *addr;
1660 int n = 0;
1661 void *a;
1662
1663 blob_buf_init(&req, 0);
1664
1665 a = blobmsg_open_array(&req, "addrs");
1666
1667 avl_for_each_element(&rctx->avl, hint, avl) {
1668 avl_for_each_element(&hint->ipaddrs, addr, avl) {
1669 if (addr->addr.in.s_addr != 0) {
1670 inet_ntop(AF_INET, &addr->addr.in, buf, sizeof(buf));
1671 blobmsg_add_string(&req, NULL, buf);
1672 n++;
1673 }
1674 }
1675 avl_for_each_element(&hint->ip6addrs, addr, avl) {
1676 if (memcmp(&addr->addr.in6, &empty_in6, sizeof(empty_in6))) {
1677 inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf));
1678 blobmsg_add_string(&req, NULL, buf);
1679 n++;
1680 }
1681 }
1682 }
1683
1684 blobmsg_close_array(&req, a);
1685
1686 if (n > 0) {
1687 blobmsg_add_u32(&req, "timeout", 250);
1688 blobmsg_add_u32(&req, "limit", n);
1689
1690 if (!invoke_ubus(rctx->context, "network.rrdns", "lookup", &req,
1691 rpc_luci_get_host_hints_rrdns_cb, rctx))
1692 rpc_luci_get_host_hints_finish(rctx);
1693 }
1694 else {
1695 rpc_luci_get_host_hints_finish(rctx);
1696 }
1697
1698 blob_buf_free(&req);
1699 }
1700
1701 static int
1702 rpc_luci_get_host_hints_finish(struct reply_context *rctx)
1703 {
1704 struct host_hint *hint, *nexthint;
1705 struct host_hint_addr *addr, *nextaddr;
1706 char buf[INET6_ADDRSTRLEN];
1707 struct in6_addr in6 = {};
1708 void *o, *a;
1709
1710 avl_remove_all_elements(&rctx->avl, hint, avl, nexthint) {
1711 o = blobmsg_open_table(&rctx->blob, hint->avl.key);
1712
1713 a = blobmsg_open_array(&rctx->blob, "ipaddrs");
1714
1715 avl_remove_all_elements(&hint->ipaddrs, addr, avl, nextaddr) {
1716 if (addr->addr.in.s_addr != 0) {
1717 inet_ntop(AF_INET, &addr->addr.in, buf, sizeof(buf));
1718 blobmsg_add_string(&rctx->blob, NULL, buf);
1719 }
1720
1721 free(addr);
1722 }
1723
1724 blobmsg_close_array(&rctx->blob, a);
1725
1726 a = blobmsg_open_array(&rctx->blob, "ip6addrs");
1727
1728 avl_remove_all_elements(&hint->ip6addrs, addr, avl, nextaddr) {
1729 if (memcmp(&addr->addr.in6, &in6, sizeof(in6))) {
1730 inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf));
1731 blobmsg_add_string(&rctx->blob, NULL, buf);
1732 }
1733
1734 free(addr);
1735 }
1736
1737 blobmsg_close_array(&rctx->blob, a);
1738
1739 if (hint->hostname)
1740 blobmsg_add_string(&rctx->blob, "name", hint->hostname);
1741
1742 blobmsg_close_table(&rctx->blob, o);
1743
1744 if (hint->hostname)
1745 free(hint->hostname);
1746
1747 free(hint);
1748 }
1749
1750 return finish_request(rctx, UBUS_STATUS_OK);
1751 }
1752
1753 static int
1754 rpc_luci_get_host_hints(struct ubus_context *ctx, struct ubus_object *obj,
1755 struct ubus_request_data *req, const char *method,
1756 struct blob_attr *msg)
1757 {
1758 struct reply_context *rctx = defer_request(ctx, req);
1759
1760 if (!rctx)
1761 return UBUS_STATUS_UNKNOWN_ERROR;
1762
1763 rpc_luci_get_host_hints_nl(rctx);
1764 rpc_luci_get_host_hints_uci(rctx);
1765 rpc_luci_get_host_hints_ether(rctx);
1766 rpc_luci_get_host_hints_ifaddrs(rctx);
1767 rpc_luci_get_host_hints_rrdns(rctx);
1768
1769 return UBUS_STATUS_OK;
1770 }
1771
1772 static int
1773 rpc_luci_get_duid_hints(struct ubus_context *ctx, struct ubus_object *obj,
1774 struct ubus_request_data *req, const char *method,
1775 struct blob_attr *msg)
1776 {
1777 struct { struct avl_node avl; } *e, *next;
1778 char s[INET6_ADDRSTRLEN], *p;
1779 struct ether_addr empty = {};
1780 struct lease_entry *lease;
1781 struct avl_tree avl;
1782 void *o, *a;
1783 int n;
1784
1785 avl_init(&avl, avl_strcmp, false, NULL);
1786 blob_buf_init(&blob, 0);
1787
1788 lease_open();
1789
1790 while ((lease = lease_next()) != NULL) {
1791 if (lease->af != AF_INET6 || lease->duid == NULL)
1792 continue;
1793
1794 e = avl_find_element(&avl, lease->duid, e, avl);
1795
1796 if (e)
1797 continue;
1798
1799 e = calloc_a(sizeof(*e), &p, strlen(lease->duid) + 1);
1800
1801 if (!e)
1802 continue;
1803
1804 o = blobmsg_open_table(&blob, lease->duid);
1805
1806 inet_ntop(AF_INET6, &lease->addr[0].in6, s, sizeof(s));
1807 blobmsg_add_string(&blob, "ip6addr", s);
1808
1809 a = blobmsg_open_array(&blob, "ip6addrs");
1810
1811 for (n = 0; n < lease->n_addr; n++) {
1812 inet_ntop(AF_INET6, &lease->addr[n].in6, s, sizeof(s));
1813 blobmsg_add_string(&blob, NULL, s);
1814 }
1815
1816 blobmsg_close_array(&blob, a);
1817
1818 if (lease->hostname)
1819 blobmsg_add_string(&blob, "hostname", lease->hostname);
1820
1821 if (memcmp(&lease->mac, &empty, sizeof(empty)))
1822 blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
1823
1824 blobmsg_close_table(&blob, o);
1825
1826 e->avl.key = strcpy(p, lease->duid);
1827 avl_insert(&avl, &e->avl);
1828 }
1829
1830 lease_close();
1831
1832 avl_remove_all_elements(&avl, e, avl, next) {
1833 free(e);
1834 }
1835
1836 ubus_send_reply(ctx, req, blob.head);
1837
1838 return UBUS_STATUS_OK;
1839 }
1840
1841 static int
1842 rpc_luci_get_board_json(struct ubus_context *ctx, struct ubus_object *obj,
1843 struct ubus_request_data *req, const char *method,
1844 struct blob_attr *msg)
1845 {
1846 blob_buf_init(&blob, 0);
1847
1848 if (!blobmsg_add_json_from_file(&blob, "/etc/board.json"))
1849 return UBUS_STATUS_UNKNOWN_ERROR;
1850
1851 ubus_send_reply(ctx, req, blob.head);
1852 return UBUS_STATUS_OK;
1853 }
1854
1855 enum {
1856 RPC_L_FAMILY,
1857 __RPC_L_MAX,
1858 };
1859
1860 static const struct blobmsg_policy rpc_get_leases_policy[__RPC_L_MAX] = {
1861 [RPC_L_FAMILY] = { .name = "family", .type = BLOBMSG_TYPE_INT32 }
1862 };
1863
1864 static int
1865 rpc_luci_get_dhcp_leases(struct ubus_context *ctx, struct ubus_object *obj,
1866 struct ubus_request_data *req, const char *method,
1867 struct blob_attr *msg)
1868 {
1869 struct blob_attr *tb[__RPC_L_MAX];
1870 struct ether_addr emptymac = {};
1871 struct lease_entry *lease;
1872 char s[INET6_ADDRSTRLEN];
1873 int af, family = 0;
1874 void *a, *a2, *o;
1875 int n;
1876
1877 blobmsg_parse(rpc_get_leases_policy, __RPC_L_MAX, tb,
1878 blob_data(msg), blob_len(msg));
1879
1880 switch (tb[RPC_L_FAMILY] ? blobmsg_get_u32(tb[RPC_L_FAMILY]) : 0) {
1881 case 0:
1882 family = 0;
1883 break;
1884
1885 case 4:
1886 family = AF_INET;
1887 break;
1888
1889 case 6:
1890 family = AF_INET6;
1891 break;
1892
1893 default:
1894 return UBUS_STATUS_INVALID_ARGUMENT;
1895 }
1896
1897 blob_buf_init(&blob, 0);
1898
1899 for (af = family ? family : AF_INET;
1900 af != 0;
1901 af = (family == 0) ? (af == AF_INET ? AF_INET6 : 0) : 0) {
1902
1903 a = blobmsg_open_array(&blob, (af == AF_INET) ? "dhcp_leases"
1904 : "dhcp6_leases");
1905
1906 lease_open();
1907
1908 while ((lease = lease_next()) != NULL) {
1909 if (lease->af != af)
1910 continue;
1911
1912 o = blobmsg_open_table(&blob, NULL);
1913
1914 if (lease->expire == -1)
1915 blobmsg_add_u8(&blob, "expires", 0);
1916 else
1917 blobmsg_add_u32(&blob, "expires", lease->expire);
1918
1919 if (lease->hostname)
1920 blobmsg_add_string(&blob, "hostname", lease->hostname);
1921
1922 if (memcmp(&lease->mac, &emptymac, sizeof(emptymac)))
1923 blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
1924
1925 if (lease->duid)
1926 blobmsg_add_string(&blob, "duid", lease->duid);
1927
1928 inet_ntop(lease->af, &lease->addr[0].in6, s, sizeof(s));
1929 blobmsg_add_string(&blob, (af == AF_INET) ? "ipaddr" : "ip6addr",
1930 s);
1931
1932 if (af == AF_INET6) {
1933 a2 = blobmsg_open_array(&blob, "ip6addrs");
1934
1935 for (n = 0; n < lease->n_addr; n++) {
1936 inet_ntop(lease->af, &lease->addr[n].in6, s, sizeof(s));
1937 blobmsg_add_string(&blob, NULL, s);
1938 }
1939
1940 blobmsg_close_array(&blob, a2);
1941 }
1942
1943 blobmsg_close_table(&blob, o);
1944 }
1945
1946 lease_close();
1947
1948 blobmsg_close_array(&blob, a);
1949 }
1950
1951 ubus_send_reply(ctx, req, blob.head);
1952
1953 return UBUS_STATUS_OK;
1954 }
1955
1956 static int
1957 rpc_luci_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
1958 {
1959 static const struct ubus_method luci_methods[] = {
1960 UBUS_METHOD_NOARG("getNetworkDevices", rpc_luci_get_network_devices),
1961 UBUS_METHOD_NOARG("getWirelessDevices", rpc_luci_get_wireless_devices),
1962 UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints),
1963 UBUS_METHOD_NOARG("getDUIDHints", rpc_luci_get_duid_hints),
1964 UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json),
1965 UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases, rpc_get_leases_policy)
1966 };
1967
1968 static struct ubus_object_type luci_type =
1969 UBUS_OBJECT_TYPE("rpcd-luci", luci_methods);
1970
1971 static struct ubus_object obj = {
1972 .name = "luci-rpc",
1973 .type = &luci_type,
1974 .methods = luci_methods,
1975 .n_methods = ARRAY_SIZE(luci_methods),
1976 };
1977
1978 return ubus_add_object(ctx, &obj);
1979 }
1980
1981 struct rpc_plugin rpc_plugin = {
1982 .init = rpc_luci_api_init
1983 };