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