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