rpcd-mod-luci: expose devtype and iflink properties in getNetworkDevices
[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 in_addr ip;
1139 struct in6_addr ip6;
1140 };
1141
1142 static int
1143 nl_cb_done(struct nl_msg *msg, void *arg)
1144 {
1145 struct reply_context *rctx = arg;
1146 rctx->pending = 0;
1147 return NL_STOP;
1148 }
1149
1150 static int
1151 nl_cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
1152 {
1153 struct reply_context *rctx = arg;
1154 rctx->pending = 0;
1155 return NL_STOP;
1156 }
1157
1158 static struct host_hint *
1159 rpc_luci_get_host_hint(struct reply_context *rctx, struct ether_addr *ea)
1160 {
1161 struct host_hint *hint;
1162 char *p, *mac;
1163
1164 if (!ea)
1165 return NULL;
1166
1167 mac = ea2str(ea);
1168 hint = avl_find_element(&rctx->avl, mac, hint, avl);
1169
1170 if (!hint) {
1171 hint = calloc_a(sizeof(*hint), &p, strlen(mac) + 1);
1172
1173 if (!hint)
1174 return NULL;
1175
1176 hint->avl.key = strcpy(p, mac);
1177 avl_insert(&rctx->avl, &hint->avl);
1178 }
1179
1180 return hint;
1181 }
1182
1183 static int nl_cb_dump_neigh(struct nl_msg *msg, void *arg)
1184 {
1185 struct reply_context *rctx = arg;
1186 struct ether_addr *mac;
1187 struct in6_addr *dst;
1188 struct nlmsghdr *hdr = nlmsg_hdr(msg);
1189 struct ndmsg *nd = NLMSG_DATA(hdr);
1190 struct nlattr *tb[NDA_MAX+1];
1191 struct host_hint *hint;
1192
1193 rctx->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI);
1194
1195 if (hdr->nlmsg_type != RTM_NEWNEIGH ||
1196 (nd->ndm_family != AF_INET && nd->ndm_family != AF_INET6))
1197 return NL_SKIP;
1198
1199 if (!(nd->ndm_state & (0xFF & ~NUD_NOARP)))
1200 return NL_SKIP;
1201
1202 nlmsg_parse(hdr, sizeof(*nd), tb, NDA_MAX, NULL);
1203
1204 mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL;
1205 dst = tb[NDA_DST] ? RTA_DATA(tb[NDA_DST]) : NULL;
1206
1207 if (!mac || !dst)
1208 return NL_SKIP;
1209
1210 hint = rpc_luci_get_host_hint(rctx, mac);
1211
1212 if (!hint)
1213 return NL_SKIP;
1214
1215 if (nd->ndm_family == AF_INET)
1216 hint->ip = *(struct in_addr *)dst;
1217 else
1218 hint->ip6 = *(struct in6_addr *)dst;
1219
1220 return NL_SKIP;
1221 }
1222
1223 static void
1224 rpc_luci_get_host_hints_nl(struct reply_context *rctx)
1225 {
1226 struct nl_sock *sock = NULL;
1227 struct nl_msg *msg = NULL;
1228 struct nl_cb *cb = NULL;
1229 struct ndmsg ndm = {};
1230
1231 sock = nl_socket_alloc();
1232
1233 if (!sock)
1234 goto out;
1235
1236 if (nl_connect(sock, NETLINK_ROUTE))
1237 goto out;
1238
1239 cb = nl_cb_alloc(NL_CB_DEFAULT);
1240
1241 if (!cb)
1242 goto out;
1243
1244 msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
1245
1246 if (!msg)
1247 goto out;
1248
1249 nlmsg_append(msg, &ndm, sizeof(ndm), 0);
1250
1251 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nl_cb_dump_neigh, rctx);
1252 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_cb_done, rctx);
1253 nl_cb_err(cb, NL_CB_CUSTOM, nl_cb_error, rctx);
1254
1255 avl_init(&rctx->avl, avl_strcmp, false, NULL);
1256
1257 rctx->pending = 1;
1258
1259 nl_send_auto_complete(sock, msg);
1260
1261 while (rctx->pending)
1262 nl_recvmsgs(sock, cb);
1263
1264 out:
1265 if (sock)
1266 nl_socket_free(sock);
1267
1268 if (cb)
1269 nl_cb_put(cb);
1270
1271 if (msg)
1272 nlmsg_free(msg);
1273 }
1274
1275 static void
1276 rpc_luci_get_host_hints_ether(struct reply_context *rctx)
1277 {
1278 struct host_hint *hint;
1279 struct in_addr in;
1280 char buf[512], *p;
1281 FILE *f;
1282
1283 f = fopen("/etc/ethers", "r");
1284
1285 if (!f)
1286 return;
1287
1288 while (fgets(buf, sizeof(buf), f)) {
1289 p = strtok(buf, " \t\n");
1290 hint = rpc_luci_get_host_hint(rctx, p ? ether_aton(p) : NULL);
1291
1292 if (!hint)
1293 continue;
1294
1295 p = strtok(NULL, " \t\n");
1296
1297 if (!p)
1298 continue;
1299
1300 if (inet_pton(AF_INET, p, &in) == 1) {
1301 if (hint->ip.s_addr == 0)
1302 hint->ip = in;
1303 }
1304 else if (*p && !hint->hostname) {
1305 hint->hostname = strdup(p);
1306 }
1307 }
1308
1309 fclose(f);
1310 }
1311
1312 static void
1313 rpc_luci_get_host_hints_uci(struct reply_context *rctx)
1314 {
1315 struct uci_ptr ptr = { .package = "dhcp" };
1316 struct uci_context *uci = NULL;
1317 struct uci_package *pkg = NULL;
1318 struct in6_addr empty = {};
1319 struct lease_entry *lease;
1320 struct host_hint *hint;
1321 struct uci_element *e, *l;
1322 struct uci_section *s;
1323 struct in_addr in;
1324 char *p, *n;
1325
1326 uci = uci_alloc_context();
1327
1328 if (!uci)
1329 goto out;
1330
1331 uci_load(uci, ptr.package, &pkg);
1332
1333 if (!pkg)
1334 goto out;
1335
1336 uci_foreach_element(&pkg->sections, e)
1337 {
1338 s = uci_to_section(e);
1339
1340 if (strcmp(s->type, "host"))
1341 continue;
1342
1343 ptr.section = s->e.name;
1344 ptr.s = NULL;
1345
1346 ptr.option = "ip";
1347 ptr.o = NULL;
1348
1349 if (!uci_lookup_ptr(uci, &ptr, NULL, true) && ptr.o != NULL &&
1350 ptr.o->type != UCI_TYPE_STRING)
1351 n = ptr.o->v.string;
1352 else
1353 n = NULL;
1354
1355 if (!n || inet_pton(AF_INET, n, &in) != 1)
1356 in.s_addr = 0;
1357
1358 ptr.option = "name";
1359 ptr.o = NULL;
1360
1361 if (!uci_lookup_ptr(uci, &ptr, NULL, true) && ptr.o != NULL &&
1362 ptr.o->type == UCI_TYPE_STRING)
1363 n = ptr.o->v.string;
1364 else
1365 n = NULL;
1366
1367 ptr.option = "mac";
1368 ptr.o = NULL;
1369
1370 if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL)
1371 continue;
1372
1373 if (ptr.o->type == UCI_TYPE_STRING) {
1374 for (p = strtok(ptr.o->v.string, " \t");
1375 p != NULL;
1376 p = strtok(NULL, " \t")) {
1377 hint = rpc_luci_get_host_hint(rctx, ether_aton(p));
1378
1379 if (!hint)
1380 continue;
1381
1382 if (hint->ip.s_addr == 0 && in.s_addr != 0)
1383 hint->ip = in;
1384
1385 if (n && !hint->hostname)
1386 hint->hostname = strdup(n);
1387 }
1388 }
1389 else if (ptr.o->type == UCI_TYPE_LIST) {
1390 uci_foreach_element(&ptr.o->v.list, l) {
1391 hint = rpc_luci_get_host_hint(rctx, ether_aton(l->name));
1392
1393 if (!hint)
1394 continue;
1395
1396 if (hint->ip.s_addr == 0 && in.s_addr != 0)
1397 hint->ip = in;
1398
1399 if (n && !hint->hostname)
1400 hint->hostname = strdup(n);
1401 }
1402 }
1403 }
1404
1405 lease_open();
1406
1407 while ((lease = lease_next()) != NULL) {
1408 hint = rpc_luci_get_host_hint(rctx, &lease->mac);
1409
1410 if (!hint)
1411 continue;
1412
1413 if (lease->af == AF_INET && lease->n_addr && hint->ip.s_addr == 0)
1414 hint->ip = lease->addr[0].in;
1415 else if (lease->af == AF_INET6 && lease->n_addr &&
1416 !memcmp(&hint->ip6, &empty, sizeof(empty)))
1417 hint->ip6 = lease->addr[0].in6;
1418
1419 if (lease->hostname && !hint->hostname)
1420 hint->hostname = strdup(lease->hostname);
1421 }
1422
1423 lease_close();
1424
1425 out:
1426 if (uci)
1427 uci_free_context(uci);
1428 }
1429
1430 static void
1431 rpc_luci_get_host_hints_ifaddrs(struct reply_context *rctx)
1432 {
1433 struct ether_addr empty_ea = {};
1434 struct in6_addr empty_in6 = {};
1435 struct ifaddrs *ifaddr, *ifa;
1436 struct sockaddr_ll *sll;
1437 struct avl_tree devices;
1438 struct host_hint *hint;
1439 struct {
1440 struct avl_node avl;
1441 struct ether_addr ea;
1442 struct in6_addr in6;
1443 struct in_addr in;
1444 } *device, *nextdevice;
1445 char *p;
1446
1447 avl_init(&devices, avl_strcmp, false, NULL);
1448
1449 if (getifaddrs(&ifaddr) == -1)
1450 return;
1451
1452 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
1453 if (!ifa->ifa_addr)
1454 continue;
1455
1456 device = avl_find_element(&devices, ifa->ifa_name, device, avl);
1457
1458 if (!device) {
1459 device = calloc_a(sizeof(*device), &p, strlen(ifa->ifa_name) + 1);
1460
1461 if (!device)
1462 continue;
1463
1464 device->avl.key = strcpy(p, ifa->ifa_name);
1465 avl_insert(&devices, &device->avl);
1466 }
1467
1468 switch (ifa->ifa_addr->sa_family) {
1469 case AF_PACKET:
1470 sll = (struct sockaddr_ll *)ifa->ifa_addr;
1471
1472 if (sll->sll_halen == 6)
1473 memcpy(&device->ea, sll->sll_addr, 6);
1474
1475 break;
1476
1477 case AF_INET6:
1478 device->in6 = ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
1479 break;
1480
1481 case AF_INET:
1482 device->in = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
1483 break;
1484 }
1485 }
1486
1487 freeifaddrs(ifaddr);
1488
1489 avl_remove_all_elements(&devices, device, avl, nextdevice) {
1490 if (memcmp(&device->ea, &empty_ea, sizeof(empty_ea)) &&
1491 (memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) ||
1492 device->in.s_addr != 0)) {
1493 hint = rpc_luci_get_host_hint(rctx, &device->ea);
1494
1495 if (hint) {
1496 if (hint->ip.s_addr == 0 && device->in.s_addr != 0)
1497 hint->ip = device->in;
1498
1499 if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6)) == 0 &&
1500 memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) != 0)
1501 hint->ip6 = device->in6;
1502 }
1503 }
1504
1505 free(device);
1506 }
1507 }
1508
1509 static int
1510 rpc_luci_get_host_hints_finish(struct reply_context *rctx);
1511
1512 static void
1513 rpc_luci_get_host_hints_rrdns_cb(struct ubus_request *req, int type,
1514 struct blob_attr *msg)
1515 {
1516 struct reply_context *rctx = req->priv;
1517 struct host_hint *hint;
1518 struct blob_attr *cur;
1519 struct in6_addr in6;
1520 struct in_addr in;
1521 int rem;
1522
1523 if (msg) {
1524 blob_for_each_attr(cur, msg, rem) {
1525 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
1526 continue;
1527
1528 if (inet_pton(AF_INET6, blobmsg_name(cur), &in6) == 1) {
1529 avl_for_each_element(&rctx->avl, hint, avl) {
1530 if (!memcmp(&hint->ip6, &in6, sizeof(in6))) {
1531 if (hint->hostname)
1532 free(hint->hostname);
1533
1534 hint->hostname = strdup(blobmsg_get_string(cur));
1535 break;
1536 }
1537 }
1538 }
1539 else if (inet_pton(AF_INET, blobmsg_name(cur), &in) == 1) {
1540 avl_for_each_element(&rctx->avl, hint, avl) {
1541 if (!memcmp(&hint->ip, &in, sizeof(in))) {
1542 if (hint->hostname)
1543 free(hint->hostname);
1544
1545 hint->hostname = strdup(blobmsg_get_string(cur));
1546 break;
1547 }
1548 }
1549 }
1550 }
1551 }
1552
1553 rpc_luci_get_host_hints_finish(rctx);
1554 }
1555
1556 static void
1557 rpc_luci_get_host_hints_rrdns(struct reply_context *rctx)
1558 {
1559 struct in6_addr empty_in6 = {};
1560 char buf[INET6_ADDRSTRLEN];
1561 struct blob_buf req = {};
1562 struct host_hint *hint;
1563 int n = 0;
1564 void *a;
1565
1566 blob_buf_init(&req, 0);
1567
1568 a = blobmsg_open_array(&req, "addrs");
1569
1570 avl_for_each_element(&rctx->avl, hint, avl) {
1571 if (hint->ip.s_addr != 0) {
1572 inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
1573 blobmsg_add_string(&req, NULL, buf);
1574 n++;
1575 }
1576 else if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6))) {
1577 inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
1578 blobmsg_add_string(&req, NULL, buf);
1579 n++;
1580 }
1581 }
1582
1583 blobmsg_close_array(&req, a);
1584
1585 if (n > 0) {
1586 blobmsg_add_u32(&req, "timeout", 250);
1587 blobmsg_add_u32(&req, "limit", n);
1588
1589 if (!invoke_ubus(rctx->context, "network.rrdns", "lookup", &req,
1590 rpc_luci_get_host_hints_rrdns_cb, rctx))
1591 rpc_luci_get_host_hints_finish(rctx);
1592 }
1593 else {
1594 rpc_luci_get_host_hints_finish(rctx);
1595 }
1596
1597 blob_buf_free(&req);
1598 }
1599
1600 static int
1601 rpc_luci_get_host_hints_finish(struct reply_context *rctx)
1602 {
1603 struct host_hint *hint, *nexthint;
1604 char buf[INET6_ADDRSTRLEN];
1605 struct in6_addr in6 = {};
1606 struct in_addr in = {};
1607 void *o;
1608
1609 avl_remove_all_elements(&rctx->avl, hint, avl, nexthint) {
1610 o = blobmsg_open_table(&rctx->blob, hint->avl.key);
1611
1612 if (memcmp(&hint->ip, &in, sizeof(in))) {
1613 inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
1614 blobmsg_add_string(&rctx->blob, "ipv4", buf);
1615 }
1616
1617 if (memcmp(&hint->ip6, &in6, sizeof(in6))) {
1618 inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
1619 blobmsg_add_string(&rctx->blob, "ipv6", buf);
1620 }
1621
1622 if (hint->hostname)
1623 blobmsg_add_string(&rctx->blob, "name", hint->hostname);
1624
1625 blobmsg_close_table(&rctx->blob, o);
1626
1627 if (hint->hostname)
1628 free(hint->hostname);
1629
1630 free(hint);
1631 }
1632
1633 return finish_request(rctx, UBUS_STATUS_OK);
1634 }
1635
1636 static int
1637 rpc_luci_get_host_hints(struct ubus_context *ctx, struct ubus_object *obj,
1638 struct ubus_request_data *req, const char *method,
1639 struct blob_attr *msg)
1640 {
1641 struct reply_context *rctx = defer_request(ctx, req);
1642
1643 if (!rctx)
1644 return UBUS_STATUS_UNKNOWN_ERROR;
1645
1646 rpc_luci_get_host_hints_nl(rctx);
1647 rpc_luci_get_host_hints_uci(rctx);
1648 rpc_luci_get_host_hints_ether(rctx);
1649 rpc_luci_get_host_hints_ifaddrs(rctx);
1650 rpc_luci_get_host_hints_rrdns(rctx);
1651
1652 return UBUS_STATUS_OK;
1653 }
1654
1655 static int
1656 rpc_luci_get_duid_hints(struct ubus_context *ctx, struct ubus_object *obj,
1657 struct ubus_request_data *req, const char *method,
1658 struct blob_attr *msg)
1659 {
1660 struct { struct avl_node avl; } *e, *next;
1661 char s[INET6_ADDRSTRLEN], *p;
1662 struct ether_addr empty = {};
1663 struct lease_entry *lease;
1664 struct avl_tree avl;
1665 void *o, *a;
1666 int n;
1667
1668 avl_init(&avl, avl_strcmp, false, NULL);
1669 blob_buf_init(&blob, 0);
1670
1671 lease_open();
1672
1673 while ((lease = lease_next()) != NULL) {
1674 if (lease->af != AF_INET6 || lease->duid == NULL)
1675 continue;
1676
1677 e = avl_find_element(&avl, lease->duid, e, avl);
1678
1679 if (e)
1680 continue;
1681
1682 e = calloc_a(sizeof(*e), &p, strlen(lease->duid) + 1);
1683
1684 if (!e)
1685 continue;
1686
1687 o = blobmsg_open_table(&blob, lease->duid);
1688
1689 inet_ntop(AF_INET6, &lease->addr[0].in6, s, sizeof(s));
1690 blobmsg_add_string(&blob, "ip6addr", s);
1691
1692 a = blobmsg_open_array(&blob, "ip6addrs");
1693
1694 for (n = 0; n < lease->n_addr; n++) {
1695 inet_ntop(AF_INET6, &lease->addr[n].in6, s, sizeof(s));
1696 blobmsg_add_string(&blob, NULL, s);
1697 }
1698
1699 blobmsg_close_array(&blob, a);
1700
1701 if (lease->hostname)
1702 blobmsg_add_string(&blob, "hostname", lease->hostname);
1703
1704 if (memcmp(&lease->mac, &empty, sizeof(empty)))
1705 blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
1706
1707 blobmsg_close_table(&blob, o);
1708
1709 e->avl.key = strcpy(p, lease->duid);
1710 avl_insert(&avl, &e->avl);
1711 }
1712
1713 lease_close();
1714
1715 avl_remove_all_elements(&avl, e, avl, next) {
1716 free(e);
1717 }
1718
1719 ubus_send_reply(ctx, req, blob.head);
1720
1721 return UBUS_STATUS_OK;
1722 }
1723
1724 static int
1725 rpc_luci_get_board_json(struct ubus_context *ctx, struct ubus_object *obj,
1726 struct ubus_request_data *req, const char *method,
1727 struct blob_attr *msg)
1728 {
1729 blob_buf_init(&blob, 0);
1730
1731 if (!blobmsg_add_json_from_file(&blob, "/etc/board.json"))
1732 return UBUS_STATUS_UNKNOWN_ERROR;
1733
1734 ubus_send_reply(ctx, req, blob.head);
1735 return UBUS_STATUS_OK;
1736 }
1737
1738 enum {
1739 RPC_L_FAMILY,
1740 __RPC_L_MAX,
1741 };
1742
1743 static const struct blobmsg_policy rpc_get_leases_policy[__RPC_L_MAX] = {
1744 [RPC_L_FAMILY] = { .name = "family", .type = BLOBMSG_TYPE_INT32 }
1745 };
1746
1747 static int
1748 rpc_luci_get_dhcp_leases(struct ubus_context *ctx, struct ubus_object *obj,
1749 struct ubus_request_data *req, const char *method,
1750 struct blob_attr *msg)
1751 {
1752 struct blob_attr *tb[__RPC_L_MAX];
1753 struct ether_addr emptymac = {};
1754 struct lease_entry *lease;
1755 char s[INET6_ADDRSTRLEN];
1756 int af, family = 0;
1757 void *a, *a2, *o;
1758 int n;
1759
1760 blobmsg_parse(rpc_get_leases_policy, __RPC_L_MAX, tb,
1761 blob_data(msg), blob_len(msg));
1762
1763 switch (tb[RPC_L_FAMILY] ? blobmsg_get_u32(tb[RPC_L_FAMILY]) : 0) {
1764 case 0:
1765 family = 0;
1766 break;
1767
1768 case 4:
1769 family = AF_INET;
1770 break;
1771
1772 case 6:
1773 family = AF_INET6;
1774 break;
1775
1776 default:
1777 return UBUS_STATUS_INVALID_ARGUMENT;
1778 }
1779
1780 blob_buf_init(&blob, 0);
1781
1782 for (af = family ? family : AF_INET;
1783 af != 0;
1784 af = (family == 0) ? (af == AF_INET ? AF_INET6 : 0) : 0) {
1785
1786 a = blobmsg_open_array(&blob, (af == AF_INET) ? "dhcp_leases"
1787 : "dhcp6_leases");
1788
1789 lease_open();
1790
1791 while ((lease = lease_next()) != NULL) {
1792 if (lease->af != af)
1793 continue;
1794
1795 o = blobmsg_open_table(&blob, NULL);
1796
1797 if (lease->expire == -1)
1798 blobmsg_add_u8(&blob, "expires", 0);
1799 else
1800 blobmsg_add_u32(&blob, "expires", lease->expire);
1801
1802 if (lease->hostname)
1803 blobmsg_add_string(&blob, "hostname", lease->hostname);
1804
1805 if (memcmp(&lease->mac, &emptymac, sizeof(emptymac)))
1806 blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
1807
1808 if (lease->duid)
1809 blobmsg_add_string(&blob, "duid", lease->duid);
1810
1811 inet_ntop(lease->af, &lease->addr[0].in6, s, sizeof(s));
1812 blobmsg_add_string(&blob, (af == AF_INET) ? "ipaddr" : "ip6addr",
1813 s);
1814
1815 if (af == AF_INET6) {
1816 a2 = blobmsg_open_array(&blob, "ip6addrs");
1817
1818 for (n = 0; n < lease->n_addr; n++) {
1819 inet_ntop(lease->af, &lease->addr[n].in6, s, sizeof(s));
1820 blobmsg_add_string(&blob, NULL, s);
1821 }
1822
1823 blobmsg_close_array(&blob, a2);
1824 }
1825
1826 blobmsg_close_table(&blob, o);
1827 }
1828
1829 lease_close();
1830
1831 blobmsg_close_array(&blob, a);
1832 }
1833
1834 ubus_send_reply(ctx, req, blob.head);
1835
1836 return UBUS_STATUS_OK;
1837 }
1838
1839 static int
1840 rpc_luci_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
1841 {
1842 static const struct ubus_method luci_methods[] = {
1843 UBUS_METHOD_NOARG("getNetworkDevices", rpc_luci_get_network_devices),
1844 UBUS_METHOD_NOARG("getWirelessDevices", rpc_luci_get_wireless_devices),
1845 UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints),
1846 UBUS_METHOD_NOARG("getDUIDHints", rpc_luci_get_duid_hints),
1847 UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json),
1848 UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases, rpc_get_leases_policy)
1849 };
1850
1851 static struct ubus_object_type luci_type =
1852 UBUS_OBJECT_TYPE("rpcd-luci", luci_methods);
1853
1854 static struct ubus_object obj = {
1855 .name = "luci-rpc",
1856 .type = &luci_type,
1857 .methods = luci_methods,
1858 .n_methods = ARRAY_SIZE(luci_methods),
1859 };
1860
1861 return ubus_add_object(ctx, &obj);
1862 }
1863
1864 struct rpc_plugin rpc_plugin = {
1865 .init = rpc_luci_api_init
1866 };