rpcd-mod-luci: handle lease files from all dnsmasq/odhcpd sections
[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 + 8;
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, *o, *o2, *a;
806 int nret;
807
808 if (!iw_backend || !iw_close || !iw_modenames) {
809 iwlib = dlopen("libiwinfo.so", RTLD_LAZY | RTLD_LOCAL);
810
811 if (!iwlib)
812 return false;
813
814 iw_backend = dlsym(iwlib, "iwinfo_backend");
815 iw_close = dlsym(iwlib, "iwinfo_close");
816 iw_modenames = dlsym(iwlib, "IWINFO_OPMODE_NAMES");
817
818 if (!iw_backend || !iw_close || !iw_modenames)
819 return false;
820 }
821
822 iw = iw_backend(devname);
823
824 if (!iw)
825 return false;
826
827 o = blobmsg_open_table(buf, "iwinfo");
828
829 iw_call_num(iw->signal, devname, buf, "signal");
830 iw_call_num(iw->noise, devname, buf, "noise");
831 iw_call_num(iw->channel, devname, buf, "channel");
832 iw_call_str(iw->country, devname, buf, "country");
833 iw_call_str(iw->phyname, devname, buf, "phy");
834 iw_call_num(iw->txpower, devname, buf, "txpower");
835 iw_call_num(iw->txpower_offset, devname, buf, "txpower_offset");
836 iw_call_num(iw->frequency, devname, buf, "frequency");
837 iw_call_num(iw->frequency_offset, devname, buf, "frequency_offset");
838
839 if (!iw->hwmodelist(devname, &nret)) {
840 a = blobmsg_open_array(buf, "hwmodes");
841
842 if (nret & IWINFO_80211_AC)
843 blobmsg_add_string(buf, NULL, "ac");
844
845 if (nret & IWINFO_80211_A)
846 blobmsg_add_string(buf, NULL, "a");
847
848 if (nret & IWINFO_80211_B)
849 blobmsg_add_string(buf, NULL, "b");
850
851 if (nret & IWINFO_80211_G)
852 blobmsg_add_string(buf, NULL, "g");
853
854 if (nret & IWINFO_80211_N)
855 blobmsg_add_string(buf, NULL, "n");
856
857 blobmsg_close_array(buf, a);
858 }
859
860 if (!iw->htmodelist(devname, &nret)) {
861 a = blobmsg_open_array(buf, "htmodes");
862
863 if (nret & IWINFO_HTMODE_HT20)
864 blobmsg_add_string(buf, NULL, "HT20");
865
866 if (nret & IWINFO_HTMODE_HT40)
867 blobmsg_add_string(buf, NULL, "HT40");
868
869 if (nret & IWINFO_HTMODE_VHT20)
870 blobmsg_add_string(buf, NULL, "VHT20");
871
872 if (nret & IWINFO_HTMODE_VHT40)
873 blobmsg_add_string(buf, NULL, "VHT40");
874
875 if (nret & IWINFO_HTMODE_VHT80)
876 blobmsg_add_string(buf, NULL, "VHT80");
877
878 if (nret & IWINFO_HTMODE_VHT80_80)
879 blobmsg_add_string(buf, NULL, "VHT80+80");
880
881 if (nret & IWINFO_HTMODE_VHT160)
882 blobmsg_add_string(buf, NULL, "VHT160");
883
884 blobmsg_close_array(buf, a);
885 }
886
887 if (!iw->hardware_id(devname, (char *)&ids)) {
888 o2 = blobmsg_open_table(buf, "hardware");
889
890 a = blobmsg_open_array(buf, "id");
891 blobmsg_add_u32(buf, NULL, ids.vendor_id);
892 blobmsg_add_u32(buf, NULL, ids.device_id);
893 blobmsg_add_u32(buf, NULL, ids.subsystem_vendor_id);
894 blobmsg_add_u32(buf, NULL, ids.subsystem_device_id);
895 blobmsg_close_array(buf, a);
896
897 iw_call_str(iw->hardware_name, devname, buf, "name");
898
899 blobmsg_close_table(buf, o2);
900 }
901
902 if (!phy_only) {
903 iw_call_num(iw->quality, devname, buf, "quality");
904 iw_call_num(iw->quality_max, devname, buf, "quality_max");
905 iw_call_num(iw->bitrate, devname, buf, "bitrate");
906
907 if (!iw->mode(devname, &nret))
908 blobmsg_add_string(buf, "mode", iw_modenames[nret]);
909
910 iw_call_str(iw->ssid, devname, buf, "ssid");
911 iw_call_str(iw->bssid, devname, buf, "bssid");
912
913 if (!iw->encryption(devname, (char *)&crypto)) {
914 o2 = blobmsg_open_table(buf, "encryption");
915
916 blobmsg_add_u8(buf, "enabled", crypto.enabled);
917
918 if (crypto.enabled) {
919 if (!crypto.wpa_version) {
920 a = blobmsg_open_array(buf, "wep");
921
922 if (crypto.auth_algs & IWINFO_AUTH_OPEN)
923 blobmsg_add_string(buf, NULL, "open");
924
925 if (crypto.auth_algs & IWINFO_AUTH_SHARED)
926 blobmsg_add_string(buf, NULL, "shared");
927
928 blobmsg_close_array(buf, a);
929 }
930 else {
931 a = blobmsg_open_array(buf, "wpa");
932
933 for (nret = 1; nret <= 3; nret++)
934 if (crypto.wpa_version & (1 << (nret - 1)))
935 blobmsg_add_u32(buf, NULL, nret);
936
937 blobmsg_close_array(buf, a);
938
939 a = blobmsg_open_array(buf, "authentication");
940
941 if (crypto.auth_suites & IWINFO_KMGMT_PSK)
942 blobmsg_add_string(buf, NULL, "psk");
943
944 if (crypto.auth_suites & IWINFO_KMGMT_8021x)
945 blobmsg_add_string(buf, NULL, "802.1x");
946
947 if (crypto.auth_suites & IWINFO_KMGMT_SAE)
948 blobmsg_add_string(buf, NULL, "sae");
949
950 if (crypto.auth_suites & IWINFO_KMGMT_OWE)
951 blobmsg_add_string(buf, NULL, "owe");
952
953 if (!crypto.auth_suites ||
954 (crypto.auth_suites & IWINFO_KMGMT_NONE))
955 blobmsg_add_string(buf, NULL, "none");
956
957 blobmsg_close_array(buf, a);
958 }
959
960 a = blobmsg_open_array(buf, "ciphers");
961 nret = crypto.pair_ciphers | crypto.group_ciphers;
962
963 if (nret & IWINFO_CIPHER_WEP40)
964 blobmsg_add_string(buf, NULL, "wep-40");
965
966 if (nret & IWINFO_CIPHER_WEP104)
967 blobmsg_add_string(buf, NULL, "wep-104");
968
969 if (nret & IWINFO_CIPHER_TKIP)
970 blobmsg_add_string(buf, NULL, "tkip");
971
972 if (nret & IWINFO_CIPHER_CCMP)
973 blobmsg_add_string(buf, NULL, "ccmp");
974
975 if (nret & IWINFO_CIPHER_WRAP)
976 blobmsg_add_string(buf, NULL, "wrap");
977
978 if (nret & IWINFO_CIPHER_AESOCB)
979 blobmsg_add_string(buf, NULL, "aes-ocb");
980
981 if (nret & IWINFO_CIPHER_CKIP)
982 blobmsg_add_string(buf, NULL, "ckip");
983
984 if (!nret || (nret & IWINFO_CIPHER_NONE))
985 blobmsg_add_string(buf, NULL, "none");
986
987 blobmsg_close_array(buf, a);
988 }
989
990 blobmsg_close_table(buf, o2);
991 }
992 }
993
994 blobmsg_close_table(buf, o);
995
996 iw_close();
997
998 return true;
999 }
1000
1001 static void rpc_luci_get_wireless_devices_cb(struct ubus_request *req,
1002 int type, struct blob_attr *msg)
1003 {
1004 struct blob_attr *wifi, *cur, *iface, *cur2;
1005 struct reply_context *rctx = req->priv;
1006 const char *name, *first_ifname;
1007 int rem, rem2, rem3, rem4;
1008 void *o, *a, *o2;
1009
1010 blob_for_each_attr(wifi, msg, rem) {
1011 if (blobmsg_type(wifi) != BLOBMSG_TYPE_TABLE ||
1012 blobmsg_name(wifi) == NULL)
1013 continue;
1014
1015 o = blobmsg_open_table(&rctx->blob, blobmsg_name(wifi));
1016
1017 rem2 = blobmsg_data_len(wifi);
1018 first_ifname = NULL;
1019
1020 __blob_for_each_attr(cur, blobmsg_data(wifi), rem2) {
1021 name = blobmsg_name(cur);
1022
1023 if (!name || !strcmp(name, "iwinfo")) {
1024 continue;
1025 }
1026 else if (!strcmp(name, "interfaces")) {
1027 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY)
1028 continue;
1029
1030 a = blobmsg_open_array(&rctx->blob, "interfaces");
1031
1032 rem3 = blobmsg_data_len(cur);
1033
1034 __blob_for_each_attr(iface, blobmsg_data(cur), rem3) {
1035 if (blobmsg_type(iface) != BLOBMSG_TYPE_TABLE)
1036 continue;
1037
1038 o2 = blobmsg_open_table(&rctx->blob, NULL);
1039
1040 rem4 = blobmsg_data_len(iface);
1041 name = NULL;
1042
1043 __blob_for_each_attr(cur2, blobmsg_data(iface), rem4) {
1044 if (!strcmp(blobmsg_name(cur2), "ifname"))
1045 name = blobmsg_get_string(cur2);
1046 else if (!strcmp(blobmsg_name(cur2), "iwinfo"))
1047 continue;
1048
1049 blobmsg_add_blob(&rctx->blob, cur2);
1050 }
1051
1052 if (name)
1053 if (rpc_luci_get_iwinfo(&rctx->blob, name, false))
1054 first_ifname = first_ifname ? first_ifname : name;
1055
1056 blobmsg_close_table(&rctx->blob, o2);
1057 }
1058
1059 blobmsg_close_array(&rctx->blob, a);
1060 }
1061 else {
1062 blobmsg_add_blob(&rctx->blob, cur);
1063 }
1064 }
1065
1066 rpc_luci_get_iwinfo(&rctx->blob,
1067 first_ifname ? first_ifname : blobmsg_name(wifi),
1068 true);
1069
1070 blobmsg_close_table(&rctx->blob, o);
1071 }
1072
1073 finish_request(rctx, UBUS_STATUS_OK);
1074 }
1075
1076 static int
1077 rpc_luci_get_wireless_devices(struct ubus_context *ctx,
1078 struct ubus_object *obj,
1079 struct ubus_request_data *req,
1080 const char *method,
1081 struct blob_attr *msg)
1082 {
1083 struct reply_context *rctx = defer_request(ctx, req);
1084
1085 if (!rctx)
1086 return UBUS_STATUS_UNKNOWN_ERROR;
1087
1088 if (!invoke_ubus(ctx, "network.wireless", "status", NULL,
1089 rpc_luci_get_wireless_devices_cb, rctx))
1090 return finish_request(rctx, UBUS_STATUS_NOT_FOUND);
1091
1092 return UBUS_STATUS_OK;
1093 }
1094
1095 struct host_hint {
1096 struct avl_node avl;
1097 char *hostname;
1098 struct in_addr ip;
1099 struct in6_addr ip6;
1100 };
1101
1102 static int
1103 nl_cb_done(struct nl_msg *msg, void *arg)
1104 {
1105 struct reply_context *rctx = arg;
1106 rctx->pending = 0;
1107 return NL_STOP;
1108 }
1109
1110 static int
1111 nl_cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
1112 {
1113 struct reply_context *rctx = arg;
1114 rctx->pending = 0;
1115 return NL_STOP;
1116 }
1117
1118 static struct host_hint *
1119 rpc_luci_get_host_hint(struct reply_context *rctx, struct ether_addr *ea)
1120 {
1121 struct host_hint *hint;
1122 char *p, *mac;
1123
1124 if (!ea)
1125 return NULL;
1126
1127 mac = ea2str(ea);
1128 hint = avl_find_element(&rctx->avl, mac, hint, avl);
1129
1130 if (!hint) {
1131 hint = calloc_a(sizeof(*hint), &p, strlen(mac) + 1);
1132
1133 if (!hint)
1134 return NULL;
1135
1136 hint->avl.key = strcpy(p, mac);
1137 avl_insert(&rctx->avl, &hint->avl);
1138 }
1139
1140 return hint;
1141 }
1142
1143 static int nl_cb_dump_neigh(struct nl_msg *msg, void *arg)
1144 {
1145 struct reply_context *rctx = arg;
1146 struct ether_addr *mac;
1147 struct in6_addr *dst;
1148 struct nlmsghdr *hdr = nlmsg_hdr(msg);
1149 struct ndmsg *nd = NLMSG_DATA(hdr);
1150 struct nlattr *tb[NDA_MAX+1];
1151 struct host_hint *hint;
1152
1153 rctx->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI);
1154
1155 if (hdr->nlmsg_type != RTM_NEWNEIGH ||
1156 (nd->ndm_family != AF_INET && nd->ndm_family != AF_INET6))
1157 return NL_SKIP;
1158
1159 if (!(nd->ndm_state & (0xFF & ~NUD_NOARP)))
1160 return NL_SKIP;
1161
1162 nlmsg_parse(hdr, sizeof(*nd), tb, NDA_MAX, NULL);
1163
1164 mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL;
1165 dst = tb[NDA_DST] ? RTA_DATA(tb[NDA_DST]) : NULL;
1166
1167 if (!mac || !dst)
1168 return NL_SKIP;
1169
1170 hint = rpc_luci_get_host_hint(rctx, mac);
1171
1172 if (!hint)
1173 return NL_SKIP;
1174
1175 if (nd->ndm_family == AF_INET)
1176 hint->ip = *(struct in_addr *)dst;
1177 else
1178 hint->ip6 = *(struct in6_addr *)dst;
1179
1180 return NL_SKIP;
1181 }
1182
1183 static void
1184 rpc_luci_get_host_hints_nl(struct reply_context *rctx)
1185 {
1186 struct nl_sock *sock = NULL;
1187 struct nl_msg *msg = NULL;
1188 struct nl_cb *cb = NULL;
1189 struct ndmsg ndm = {};
1190
1191 sock = nl_socket_alloc();
1192
1193 if (!sock)
1194 goto out;
1195
1196 if (nl_connect(sock, NETLINK_ROUTE))
1197 goto out;
1198
1199 cb = nl_cb_alloc(NL_CB_DEFAULT);
1200
1201 if (!cb)
1202 goto out;
1203
1204 msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
1205
1206 if (!msg)
1207 goto out;
1208
1209 nlmsg_append(msg, &ndm, sizeof(ndm), 0);
1210
1211 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nl_cb_dump_neigh, rctx);
1212 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_cb_done, rctx);
1213 nl_cb_err(cb, NL_CB_CUSTOM, nl_cb_error, rctx);
1214
1215 avl_init(&rctx->avl, avl_strcmp, false, NULL);
1216
1217 rctx->pending = 1;
1218
1219 nl_send_auto_complete(sock, msg);
1220
1221 while (rctx->pending)
1222 nl_recvmsgs(sock, cb);
1223
1224 out:
1225 if (sock)
1226 nl_socket_free(sock);
1227
1228 if (cb)
1229 nl_cb_put(cb);
1230
1231 if (msg)
1232 nlmsg_free(msg);
1233 }
1234
1235 static void
1236 rpc_luci_get_host_hints_ether(struct reply_context *rctx)
1237 {
1238 struct host_hint *hint;
1239 struct in_addr in;
1240 char buf[512], *p;
1241 FILE *f;
1242
1243 f = fopen("/etc/ethers", "r");
1244
1245 if (!f)
1246 return;
1247
1248 while (fgets(buf, sizeof(buf), f)) {
1249 p = strtok(buf, " \t\n");
1250 hint = rpc_luci_get_host_hint(rctx, p ? ether_aton(p) : NULL);
1251
1252 if (!hint)
1253 continue;
1254
1255 p = strtok(NULL, " \t\n");
1256
1257 if (!p)
1258 continue;
1259
1260 if (inet_pton(AF_INET, p, &in) == 1) {
1261 if (hint->ip.s_addr == 0)
1262 hint->ip = in;
1263 }
1264 else if (*p && !hint->hostname) {
1265 hint->hostname = strdup(p);
1266 }
1267 }
1268
1269 fclose(f);
1270 }
1271
1272 static void
1273 rpc_luci_get_host_hints_uci(struct reply_context *rctx)
1274 {
1275 struct uci_ptr ptr = { .package = "dhcp" };
1276 struct uci_context *uci = NULL;
1277 struct uci_package *pkg = NULL;
1278 struct in6_addr empty = {};
1279 struct lease_entry *lease;
1280 struct host_hint *hint;
1281 struct uci_element *e, *l;
1282 struct uci_section *s;
1283 struct in_addr in;
1284 char *p, *n;
1285
1286 uci = uci_alloc_context();
1287
1288 if (!uci)
1289 goto out;
1290
1291 uci_load(uci, ptr.package, &pkg);
1292
1293 if (!pkg)
1294 goto out;
1295
1296 uci_foreach_element(&pkg->sections, e)
1297 {
1298 s = uci_to_section(e);
1299
1300 if (strcmp(s->type, "host"))
1301 continue;
1302
1303 ptr.section = s->e.name;
1304 ptr.s = NULL;
1305
1306 ptr.option = "ip";
1307 ptr.o = NULL;
1308
1309 if (!uci_lookup_ptr(uci, &ptr, NULL, true) && ptr.o != NULL &&
1310 ptr.o->type != UCI_TYPE_STRING)
1311 n = ptr.o->v.string;
1312 else
1313 n = NULL;
1314
1315 if (!n || inet_pton(AF_INET, n, &in) != 1)
1316 in.s_addr = 0;
1317
1318 ptr.option = "name";
1319 ptr.o = NULL;
1320
1321 if (!uci_lookup_ptr(uci, &ptr, NULL, true) && ptr.o != NULL &&
1322 ptr.o->type == UCI_TYPE_STRING)
1323 n = ptr.o->v.string;
1324 else
1325 n = NULL;
1326
1327 ptr.option = "mac";
1328 ptr.o = NULL;
1329
1330 if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL)
1331 continue;
1332
1333 if (ptr.o->type == UCI_TYPE_STRING) {
1334 for (p = strtok(ptr.o->v.string, " \t");
1335 p != NULL;
1336 p = strtok(NULL, " \t")) {
1337 hint = rpc_luci_get_host_hint(rctx, ether_aton(p));
1338
1339 if (!hint)
1340 continue;
1341
1342 if (hint->ip.s_addr == 0 && in.s_addr != 0)
1343 hint->ip = in;
1344
1345 if (n && !hint->hostname)
1346 hint->hostname = strdup(n);
1347 }
1348 }
1349 else if (ptr.o->type == UCI_TYPE_LIST) {
1350 uci_foreach_element(&ptr.o->v.list, l) {
1351 hint = rpc_luci_get_host_hint(rctx, ether_aton(l->name));
1352
1353 if (!hint)
1354 continue;
1355
1356 if (hint->ip.s_addr == 0 && in.s_addr != 0)
1357 hint->ip = in;
1358
1359 if (n && !hint->hostname)
1360 hint->hostname = strdup(n);
1361 }
1362 }
1363 }
1364
1365 lease_open();
1366
1367 while ((lease = lease_next()) != NULL) {
1368 hint = rpc_luci_get_host_hint(rctx, &lease->mac);
1369
1370 if (!hint)
1371 continue;
1372
1373 if (lease->af == AF_INET && lease->n_addr && hint->ip.s_addr == 0)
1374 hint->ip = lease->addr[0].in;
1375 else if (lease->af == AF_INET6 && lease->n_addr &&
1376 !memcmp(&hint->ip6, &empty, sizeof(empty)))
1377 hint->ip6 = lease->addr[0].in6;
1378
1379 if (lease->hostname && !hint->hostname)
1380 hint->hostname = strdup(lease->hostname);
1381 }
1382
1383 lease_close();
1384
1385 out:
1386 if (uci)
1387 uci_free_context(uci);
1388 }
1389
1390 static void
1391 rpc_luci_get_host_hints_ifaddrs(struct reply_context *rctx)
1392 {
1393 struct ether_addr empty_ea = {};
1394 struct in6_addr empty_in6 = {};
1395 struct ifaddrs *ifaddr, *ifa;
1396 struct sockaddr_ll *sll;
1397 struct avl_tree devices;
1398 struct host_hint *hint;
1399 struct {
1400 struct avl_node avl;
1401 struct ether_addr ea;
1402 struct in6_addr in6;
1403 struct in_addr in;
1404 } *device, *nextdevice;
1405 char *p;
1406
1407 avl_init(&devices, avl_strcmp, false, NULL);
1408
1409 if (getifaddrs(&ifaddr) == -1)
1410 return;
1411
1412 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
1413 if (!ifa->ifa_addr)
1414 continue;
1415
1416 device = avl_find_element(&devices, ifa->ifa_name, device, avl);
1417
1418 if (!device) {
1419 device = calloc_a(sizeof(*device), &p, strlen(ifa->ifa_name) + 1);
1420
1421 if (!device)
1422 continue;
1423
1424 device->avl.key = strcpy(p, ifa->ifa_name);
1425 avl_insert(&devices, &device->avl);
1426 }
1427
1428 switch (ifa->ifa_addr->sa_family) {
1429 case AF_PACKET:
1430 sll = (struct sockaddr_ll *)ifa->ifa_addr;
1431
1432 if (sll->sll_halen == 6)
1433 memcpy(&device->ea, sll->sll_addr, 6);
1434
1435 break;
1436
1437 case AF_INET6:
1438 device->in6 = ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
1439 break;
1440
1441 case AF_INET:
1442 device->in = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
1443 break;
1444 }
1445 }
1446
1447 freeifaddrs(ifaddr);
1448
1449 avl_remove_all_elements(&devices, device, avl, nextdevice) {
1450 if (memcmp(&device->ea, &empty_ea, sizeof(empty_ea)) &&
1451 (memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) ||
1452 device->in.s_addr != 0)) {
1453 hint = rpc_luci_get_host_hint(rctx, &device->ea);
1454
1455 if (hint) {
1456 if (hint->ip.s_addr == 0 && device->in.s_addr != 0)
1457 hint->ip = device->in;
1458
1459 if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6)) == 0 &&
1460 memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) != 0)
1461 hint->ip6 = device->in6;
1462 }
1463 }
1464
1465 free(device);
1466 }
1467 }
1468
1469 static int
1470 rpc_luci_get_host_hints_finish(struct reply_context *rctx);
1471
1472 static void
1473 rpc_luci_get_host_hints_rrdns_cb(struct ubus_request *req, int type,
1474 struct blob_attr *msg)
1475 {
1476 struct reply_context *rctx = req->priv;
1477 struct host_hint *hint;
1478 struct blob_attr *cur;
1479 struct in6_addr in6;
1480 struct in_addr in;
1481 int rem;
1482
1483 if (msg) {
1484 blob_for_each_attr(cur, msg, rem) {
1485 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
1486 continue;
1487
1488 if (inet_pton(AF_INET6, blobmsg_name(cur), &in6) == 1) {
1489 avl_for_each_element(&rctx->avl, hint, avl) {
1490 if (!memcmp(&hint->ip6, &in6, sizeof(in6))) {
1491 if (hint->hostname)
1492 free(hint->hostname);
1493
1494 hint->hostname = strdup(blobmsg_get_string(cur));
1495 break;
1496 }
1497 }
1498 }
1499 else if (inet_pton(AF_INET, blobmsg_name(cur), &in) == 1) {
1500 avl_for_each_element(&rctx->avl, hint, avl) {
1501 if (!memcmp(&hint->ip, &in, sizeof(in))) {
1502 if (hint->hostname)
1503 free(hint->hostname);
1504
1505 hint->hostname = strdup(blobmsg_get_string(cur));
1506 break;
1507 }
1508 }
1509 }
1510 }
1511 }
1512
1513 rpc_luci_get_host_hints_finish(rctx);
1514 }
1515
1516 static void
1517 rpc_luci_get_host_hints_rrdns(struct reply_context *rctx)
1518 {
1519 struct in6_addr empty_in6 = {};
1520 char buf[INET6_ADDRSTRLEN];
1521 struct blob_buf req = {};
1522 struct host_hint *hint;
1523 int n = 0;
1524 void *a;
1525
1526 blob_buf_init(&req, 0);
1527
1528 a = blobmsg_open_array(&req, "addrs");
1529
1530 avl_for_each_element(&rctx->avl, hint, avl) {
1531 if (hint->ip.s_addr != 0) {
1532 inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
1533 blobmsg_add_string(&req, NULL, buf);
1534 n++;
1535 }
1536 else if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6))) {
1537 inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
1538 blobmsg_add_string(&req, NULL, buf);
1539 n++;
1540 }
1541 }
1542
1543 blobmsg_close_array(&req, a);
1544
1545 if (n > 0) {
1546 blobmsg_add_u32(&req, "timeout", 250);
1547 blobmsg_add_u32(&req, "limit", n);
1548
1549 if (!invoke_ubus(rctx->context, "network.rrdns", "lookup", &req,
1550 rpc_luci_get_host_hints_rrdns_cb, rctx))
1551 rpc_luci_get_host_hints_finish(rctx);
1552 }
1553 else {
1554 rpc_luci_get_host_hints_finish(rctx);
1555 }
1556
1557 blob_buf_free(&req);
1558 }
1559
1560 static int
1561 rpc_luci_get_host_hints_finish(struct reply_context *rctx)
1562 {
1563 struct host_hint *hint, *nexthint;
1564 char buf[INET6_ADDRSTRLEN];
1565 struct in6_addr in6 = {};
1566 struct in_addr in = {};
1567 void *o;
1568
1569 avl_remove_all_elements(&rctx->avl, hint, avl, nexthint) {
1570 o = blobmsg_open_table(&rctx->blob, hint->avl.key);
1571
1572 if (memcmp(&hint->ip, &in, sizeof(in))) {
1573 inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
1574 blobmsg_add_string(&rctx->blob, "ipv4", buf);
1575 }
1576
1577 if (memcmp(&hint->ip6, &in6, sizeof(in6))) {
1578 inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
1579 blobmsg_add_string(&rctx->blob, "ipv6", buf);
1580 }
1581
1582 if (hint->hostname)
1583 blobmsg_add_string(&rctx->blob, "name", hint->hostname);
1584
1585 blobmsg_close_table(&rctx->blob, o);
1586
1587 if (hint->hostname)
1588 free(hint->hostname);
1589
1590 free(hint);
1591 }
1592
1593 return finish_request(rctx, UBUS_STATUS_OK);
1594 }
1595
1596 static int
1597 rpc_luci_get_host_hints(struct ubus_context *ctx, struct ubus_object *obj,
1598 struct ubus_request_data *req, const char *method,
1599 struct blob_attr *msg)
1600 {
1601 struct reply_context *rctx = defer_request(ctx, req);
1602
1603 if (!rctx)
1604 return UBUS_STATUS_UNKNOWN_ERROR;
1605
1606 rpc_luci_get_host_hints_nl(rctx);
1607 rpc_luci_get_host_hints_uci(rctx);
1608 rpc_luci_get_host_hints_ether(rctx);
1609 rpc_luci_get_host_hints_ifaddrs(rctx);
1610 rpc_luci_get_host_hints_rrdns(rctx);
1611
1612 return UBUS_STATUS_OK;
1613 }
1614
1615 static int
1616 rpc_luci_get_duid_hints(struct ubus_context *ctx, struct ubus_object *obj,
1617 struct ubus_request_data *req, const char *method,
1618 struct blob_attr *msg)
1619 {
1620 struct { struct avl_node avl; } *e, *next;
1621 char s[INET6_ADDRSTRLEN], *p;
1622 struct ether_addr empty = {};
1623 struct lease_entry *lease;
1624 struct avl_tree avl;
1625 void *o, *a;
1626 int n;
1627
1628 avl_init(&avl, avl_strcmp, false, NULL);
1629 blob_buf_init(&blob, 0);
1630
1631 lease_open();
1632
1633 while ((lease = lease_next()) != NULL) {
1634 if (lease->af != AF_INET6 || lease->duid == NULL)
1635 continue;
1636
1637 e = avl_find_element(&avl, lease->duid, e, avl);
1638
1639 if (e)
1640 continue;
1641
1642 e = calloc_a(sizeof(*e), &p, strlen(lease->duid) + 1);
1643
1644 if (!e)
1645 continue;
1646
1647 o = blobmsg_open_table(&blob, lease->duid);
1648
1649 inet_ntop(AF_INET6, &lease->addr[0].in6, s, sizeof(s));
1650 blobmsg_add_string(&blob, "ip6addr", s);
1651
1652 a = blobmsg_open_array(&blob, "ip6addrs");
1653
1654 for (n = 0; n < lease->n_addr; n++) {
1655 inet_ntop(AF_INET6, &lease->addr[n].in6, s, sizeof(s));
1656 blobmsg_add_string(&blob, NULL, s);
1657 }
1658
1659 blobmsg_close_array(&blob, a);
1660
1661 if (lease->hostname)
1662 blobmsg_add_string(&blob, "hostname", lease->hostname);
1663
1664 if (memcmp(&lease->mac, &empty, sizeof(empty)))
1665 blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
1666
1667 blobmsg_close_table(&blob, o);
1668
1669 e->avl.key = strcpy(p, lease->duid);
1670 avl_insert(&avl, &e->avl);
1671 }
1672
1673 lease_close();
1674
1675 avl_remove_all_elements(&avl, e, avl, next) {
1676 free(e);
1677 }
1678
1679 ubus_send_reply(ctx, req, blob.head);
1680
1681 return UBUS_STATUS_OK;
1682 }
1683
1684 static int
1685 rpc_luci_get_board_json(struct ubus_context *ctx, struct ubus_object *obj,
1686 struct ubus_request_data *req, const char *method,
1687 struct blob_attr *msg)
1688 {
1689 blob_buf_init(&blob, 0);
1690
1691 if (!blobmsg_add_json_from_file(&blob, "/etc/board.json"))
1692 return UBUS_STATUS_UNKNOWN_ERROR;
1693
1694 ubus_send_reply(ctx, req, blob.head);
1695 return UBUS_STATUS_OK;
1696 }
1697
1698 static int
1699 rpc_luci_get_dsl_status(struct ubus_context *ctx, struct ubus_object *obj,
1700 struct ubus_request_data *req, const char *method,
1701 struct blob_attr *msg)
1702 {
1703 char line[128], *p, *s;
1704 FILE *cmd;
1705
1706 cmd = popen("/etc/init.d/dsl_control lucistat", "r");
1707
1708 if (!cmd)
1709 return UBUS_STATUS_NOT_FOUND;
1710
1711 blob_buf_init(&blob, 0);
1712
1713 while (fgets(line, sizeof(line), cmd)) {
1714 if (strncmp(line, "dsl.", 4))
1715 continue;
1716
1717 p = strchr(line, '=');
1718
1719 if (!p)
1720 continue;
1721
1722 s = p + strlen(p) - 1;
1723
1724 while (s >= p && isspace(*s))
1725 *s-- = 0;
1726
1727 *p++ = 0;
1728
1729 if (!strcmp(p, "nil"))
1730 continue;
1731
1732 if (isdigit(*p)) {
1733 blobmsg_add_u32(&blob, line + 4, strtoul(p, NULL, 0));
1734 }
1735 else if (*p == '"') {
1736 s = p + strlen(p) - 1;
1737
1738 if (s >= p && *s == '"')
1739 *s = 0;
1740
1741 blobmsg_add_string(&blob, line + 4, p + 1);
1742 }
1743 }
1744
1745 fclose(cmd);
1746
1747 ubus_send_reply(ctx, req, blob.head);
1748 return UBUS_STATUS_OK;
1749 }
1750
1751
1752 enum {
1753 RPC_L_FAMILY,
1754 __RPC_L_MAX,
1755 };
1756
1757 static const struct blobmsg_policy rpc_get_leases_policy[__RPC_L_MAX] = {
1758 [RPC_L_FAMILY] = { .name = "family", .type = BLOBMSG_TYPE_INT32 }
1759 };
1760
1761 static int
1762 rpc_luci_get_dhcp_leases(struct ubus_context *ctx, struct ubus_object *obj,
1763 struct ubus_request_data *req, const char *method,
1764 struct blob_attr *msg)
1765 {
1766 struct blob_attr *tb[__RPC_L_MAX];
1767 struct ether_addr emptymac = {};
1768 struct lease_entry *lease;
1769 char s[INET6_ADDRSTRLEN];
1770 int af, family = 0;
1771 void *a, *a2, *o;
1772 int n;
1773
1774 blobmsg_parse(rpc_get_leases_policy, __RPC_L_MAX, tb,
1775 blob_data(msg), blob_len(msg));
1776
1777 switch (tb[RPC_L_FAMILY] ? blobmsg_get_u32(tb[RPC_L_FAMILY]) : 0) {
1778 case 0:
1779 family = 0;
1780 break;
1781
1782 case 4:
1783 family = AF_INET;
1784 break;
1785
1786 case 6:
1787 family = AF_INET6;
1788 break;
1789
1790 default:
1791 return UBUS_STATUS_INVALID_ARGUMENT;
1792 }
1793
1794 blob_buf_init(&blob, 0);
1795
1796 for (af = family ? family : AF_INET;
1797 af != 0;
1798 af = (family == 0) ? (af == AF_INET ? AF_INET6 : 0) : 0) {
1799
1800 a = blobmsg_open_array(&blob, (af == AF_INET) ? "dhcp_leases"
1801 : "dhcp6_leases");
1802
1803 lease_open();
1804
1805 while ((lease = lease_next()) != NULL) {
1806 if (lease->af != af)
1807 continue;
1808
1809 o = blobmsg_open_table(&blob, NULL);
1810
1811 if (lease->expire == -1)
1812 blobmsg_add_u8(&blob, "expires", 0);
1813 else
1814 blobmsg_add_u32(&blob, "expires", lease->expire);
1815
1816 if (lease->hostname)
1817 blobmsg_add_string(&blob, "hostname", lease->hostname);
1818
1819 if (memcmp(&lease->mac, &emptymac, sizeof(emptymac)))
1820 blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
1821
1822 if (lease->duid)
1823 blobmsg_add_string(&blob, "duid", lease->duid);
1824
1825 inet_ntop(lease->af, &lease->addr[0].in6, s, sizeof(s));
1826 blobmsg_add_string(&blob, (af == AF_INET) ? "ipaddr" : "ip6addr",
1827 s);
1828
1829 if (af == AF_INET6) {
1830 a2 = blobmsg_open_array(&blob, "ip6addrs");
1831
1832 for (n = 0; n < lease->n_addr; n++) {
1833 inet_ntop(lease->af, &lease->addr[n].in6, s, sizeof(s));
1834 blobmsg_add_string(&blob, NULL, s);
1835 }
1836
1837 blobmsg_close_array(&blob, a2);
1838 }
1839
1840 blobmsg_close_table(&blob, o);
1841 }
1842
1843 lease_close();
1844
1845 blobmsg_close_array(&blob, a);
1846 }
1847
1848 ubus_send_reply(ctx, req, blob.head);
1849
1850 return UBUS_STATUS_OK;
1851 }
1852
1853 static int
1854 rpc_luci_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
1855 {
1856 static const struct ubus_method luci_methods[] = {
1857 UBUS_METHOD_NOARG("getNetworkDevices", rpc_luci_get_network_devices),
1858 UBUS_METHOD_NOARG("getWirelessDevices", rpc_luci_get_wireless_devices),
1859 UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints),
1860 UBUS_METHOD_NOARG("getDUIDHints", rpc_luci_get_duid_hints),
1861 UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json),
1862 UBUS_METHOD_NOARG("getDSLStatus", rpc_luci_get_dsl_status),
1863 UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases, rpc_get_leases_policy)
1864 };
1865
1866 static struct ubus_object_type luci_type =
1867 UBUS_OBJECT_TYPE("rpcd-luci", luci_methods);
1868
1869 static struct ubus_object obj = {
1870 .name = "luci-rpc",
1871 .type = &luci_type,
1872 .methods = luci_methods,
1873 .n_methods = ARRAY_SIZE(luci_methods),
1874 };
1875
1876 return ubus_add_object(ctx, &obj);
1877 }
1878
1879 struct rpc_plugin rpc_plugin = {
1880 .init = rpc_luci_api_init
1881 };