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