2 * luci - LuCI core functions plugin for rpcd
4 * Copyright (C) 2019 Jo-Philipp Wich <jo@mein.io>
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.
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.
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>
38 #include <netlink/msg.h>
39 #include <netlink/attr.h>
40 #include <netlink/socket.h>
43 #include <libubox/avl.h>
44 #include <libubox/avl-cmp.h>
45 #include <libubox/usock.h>
46 #include <libubox/uloop.h>
52 #include <rpcd/plugin.h>
55 static struct blob_buf blob;
57 struct reply_context {
58 struct ubus_context *context;
59 struct ubus_request_data request;
60 struct uloop_timeout timeout;
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 *);
74 static const char **iw_modenames;
75 static struct iwinfo_ops *(*iw_backend)(const char *);
76 static void (*iw_close)(void);
79 invoke_data_cb(struct ubus_request *req, int type, struct blob_attr *msg)
81 struct invoke_context *ictx =
82 container_of(req, struct invoke_context, request);
85 ictx->cb(req, type, msg);
89 invoke_done_cb(struct ubus_request *req, int ret)
91 struct invoke_context *ictx =
92 container_of(req, struct invoke_context, request);
94 uloop_timeout_cancel(&ictx->timeout);
99 invoke_timeout_cb(struct uloop_timeout *timeout)
101 struct invoke_context *ictx =
102 container_of(timeout, struct invoke_context, timeout);
104 if (ictx->cb != NULL)
105 ictx->cb(&ictx->request, -1, NULL);
107 ubus_abort_request(ictx->context, &ictx->request);
111 static struct reply_context *
112 defer_request(struct ubus_context *ctx, struct ubus_request_data *req)
114 struct reply_context *rctx;
116 rctx = calloc(1, sizeof(*rctx));
122 blob_buf_init(&rctx->blob, 0);
123 ubus_defer_request(ctx, req, &rctx->request);
129 finish_request(struct reply_context *rctx, int status)
131 if (status == UBUS_STATUS_OK)
132 ubus_send_reply(rctx->context, &rctx->request, rctx->blob.head);
134 ubus_complete_deferred_request(rctx->context, &rctx->request, status);
135 blob_buf_free(&rctx->blob);
142 invoke_ubus(struct ubus_context *ctx, const char *object, const char *method,
143 struct blob_buf *req,
144 void (*cb)(struct ubus_request *, int, struct blob_attr *),
147 struct invoke_context *ictx;
148 struct blob_buf empty = {};
152 if (ubus_lookup_id(ctx, object, &id))
156 blob_buf_init(&empty, 0);
160 ictx = calloc(1, sizeof(*ictx));
166 rv = !ubus_invoke_async(ctx, id, method, req->head, &ictx->request);
170 ictx->request.priv = priv;
171 ictx->request.data_cb = invoke_data_cb;
172 ictx->request.complete_cb = invoke_done_cb;
173 ubus_complete_request_async(ctx, &ictx->request);
175 ictx->timeout.cb = invoke_timeout_cb;
176 uloop_timeout_set(&ictx->timeout, 2000);
180 cb(&ictx->request, -1, NULL);
192 readstr(const char *fmt, ...)
194 static char data[128];
201 vsnprintf(path, sizeof(path), fmt, ap);
205 f = fopen(path, "r");
208 n = fread(data, 1, sizeof(data) - 1, f);
211 while (n > 0 && isspace(data[n-1]))
221 ea2str(struct ether_addr *ea)
228 snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X",
229 ea->ether_addr_octet[0], ea->ether_addr_octet[1],
230 ea->ether_addr_octet[2], ea->ether_addr_octet[3],
231 ea->ether_addr_octet[4], ea->ether_addr_octet[5]);
237 sa2str(struct sockaddr *sa)
239 static char buf[INET6_ADDRSTRLEN];
241 struct sockaddr_in6 *in6;
242 struct sockaddr_in *in;
243 struct sockaddr_ll *ll;
249 if (s.sa->sa_family == AF_INET)
250 inet_ntop(sa->sa_family, &s.in->sin_addr, buf, sizeof(buf));
251 else if (s.sa->sa_family == AF_INET6)
252 inet_ntop(sa->sa_family, &s.in6->sin6_addr, buf, sizeof(buf));
253 else if (s.sa->sa_family == AF_PACKET)
254 strcpy(buf, ea2str((struct ether_addr *)s.ll->sll_addr));
261 static struct ether_addr *
262 duid2ea(const char *duid)
264 static struct ether_addr ea;
265 const char *p = NULL;
271 for (len = 0; duid[len]; len++)
272 if (!isxdigit(duid[len]))
276 (((x) <= '9') ? ((x) - '0') : \
277 (((x) <= 'F') ? ((x) - 'A' + 10) : \
282 if (!strncmp(duid, "00010001", 8))
288 if (!strncmp(duid, "00030001", 8))
301 ea.ether_addr_octet[0] = hex(p[0]) * 16 + hex(p[1]);
302 ea.ether_addr_octet[1] = hex(p[2]) * 16 + hex(p[3]);
303 ea.ether_addr_octet[2] = hex(p[4]) * 16 + hex(p[5]);
304 ea.ether_addr_octet[3] = hex(p[6]) * 16 + hex(p[7]);
305 ea.ether_addr_octet[4] = hex(p[8]) * 16 + hex(p[9]);
306 ea.ether_addr_octet[5] = hex(p[10]) * 16 + hex(p[11]);
322 struct ether_addr mac;
332 find_leasefile(struct uci_context *uci, const char *section)
334 struct uci_ptr ptr = { .package = "dhcp" };
335 struct uci_package *pkg = NULL;
336 struct uci_section *s;
337 struct uci_element *e;
339 pkg = uci_lookup_package(uci, ptr.package);
342 uci_load(uci, ptr.package, &pkg);
348 uci_foreach_element(&pkg->sections, e) {
349 s = uci_to_section(e);
351 if (strcmp(s->type, section))
356 ptr.section = s->e.name;
359 ptr.option = "leasefile";
362 if (uci_lookup_ptr(uci, &ptr, NULL, true))
365 if (ptr.o->type != UCI_TYPE_STRING)
368 return ptr.o->v.string;
377 if (lease_state.dnsmasq_file) {
378 fclose(lease_state.dnsmasq_file);
379 lease_state.dnsmasq_file = NULL;
382 if (lease_state.odhcpd_file) {
383 fclose(lease_state.odhcpd_file);
384 lease_state.odhcpd_file = NULL;
391 struct uci_context *uci;
396 uci = uci_alloc_context();
401 lease_state.now = time(NULL);
403 p = find_leasefile(uci, "dnsmasq");
404 lease_state.dnsmasq_file = fopen(p ? p : "/tmp/dhcp.leases", "r");
406 p = find_leasefile(uci, "odhcpd");
407 lease_state.odhcpd_file = fopen(p ? p : "/tmp/hosts/odhcpd", "r");
409 uci_free_context(uci);
412 static struct lease_entry *
415 static struct lease_entry e;
416 struct ether_addr *ea;
419 memset(&e, 0, sizeof(e));
421 if (lease_state.dnsmasq_file) {
422 while (fgets(e.buf, sizeof(e.buf), lease_state.dnsmasq_file)) {
423 p = strtok(e.buf, " \t\n");
428 e.expire = strtol(p, NULL, 10);
429 e.expire = (e.expire >= 0) ? e.expire - lease_state.now : 0;
431 p = strtok(NULL, " \t\n");
441 p = strtok(NULL, " \t\n");
443 if (p && inet_pton(AF_INET6, p, &e.addr.in6))
445 else if (p && inet_pton(AF_INET, p, &e.addr.in))
450 e.hostname = strtok(NULL, " \t\n");
451 e.duid = strtok(NULL, " \t\n");
453 if (!e.hostname || !e.duid)
456 if (!strcmp(e.hostname, "*"))
459 if (!strcmp(e.duid, "*"))
467 fclose(lease_state.dnsmasq_file);
468 lease_state.dnsmasq_file = NULL;
471 if (lease_state.odhcpd_file) {
472 while (fgets(e.buf, sizeof(e.buf), lease_state.odhcpd_file)) {
473 strtok(e.buf, " \t\n"); /* # */
474 strtok(NULL, " \t\n"); /* iface */
476 e.duid = strtok(NULL, " \t\n"); /* duid */
481 p = strtok(NULL, " \t\n"); /* iaid */
484 e.af = strcmp(p, "ipv4") ? AF_INET6 : AF_INET;
488 e.hostname = strtok(NULL, " \t\n"); /* name */
493 p = strtok(NULL, " \t\n"); /* ts */
498 e.expire = strtol(p, NULL, 10);
499 e.expire = (e.expire > 0) ? e.expire - lease_state.now : e.expire;
501 strtok(NULL, " \t\n"); /* id */
502 strtok(NULL, " \t\n"); /* length */
504 p = strtok(NULL, "/ \t\n"); /* ip */
506 if (!p || !inet_pton(e.af, p, &e.addr.in6))
509 ea = duid2ea(e.duid);
514 if (!strcmp(e.hostname, "-"))
517 if (!strcmp(e.duid, "-"))
523 fclose(lease_state.odhcpd_file);
524 lease_state.odhcpd_file = NULL;
532 rpc_luci_parse_network_device_sys(const char *name, struct ifaddrs *ifaddr)
534 char link[64], buf[512], *p;
535 unsigned int ifa_flags = 0;
536 struct sockaddr_ll *sll;
545 const char *stats[] = {
546 "rx_bytes", "tx_bytes", "tx_errors", "rx_errors", "tx_packets",
547 "rx_packets", "multicast", "collisions", "rx_dropped", "tx_dropped"
550 o = blobmsg_open_table(&blob, name);
552 blobmsg_add_string(&blob, "name", name);
554 snprintf(buf, sizeof(buf), "/sys/class/net/%s/brif", name);
559 blobmsg_add_u8(&blob, "bridge", 1);
561 a = blobmsg_open_array(&blob, "ports");
569 if (strcmp(e->d_name, ".") && strcmp(e->d_name, ".."))
570 blobmsg_add_string(&blob, NULL, e->d_name);
573 blobmsg_close_array(&blob, a);
577 p = readstr("/sys/class/net/%s/bridge/bridge_id", name);
578 blobmsg_add_string(&blob, "id", p);
580 p = readstr("/sys/class/net/%s/bridge/stp_state", name);
581 blobmsg_add_u8(&blob, "stp", strcmp(p, "0") ? 1 : 0);
584 snprintf(buf, sizeof(buf), "/sys/class/net/%s/master", name);
585 len = readlink(buf, link, sizeof(link) - 1);
589 blobmsg_add_string(&blob, "master", basename(link));
592 p = readstr("/sys/class/net/%s/phy80211/index", name);
593 blobmsg_add_u8(&blob, "wireless", *p ? 1 : 0);
595 p = readstr("/sys/class/net/%s/operstate", name);
596 blobmsg_add_u8(&blob, "up", !strcmp(p, "up") || !strcmp(p, "unknown"));
598 n = atoi(readstr("/sys/class/net/%s/mtu", name));
600 blobmsg_add_u32(&blob, "mtu", n);
602 n = atoi(readstr("/sys/class/net/%s/tx_queue_len", name));
604 blobmsg_add_u32(&blob, "qlen", n);
606 p = readstr("/sys/class/net/%s/master", name);
608 blobmsg_add_string(&blob, "master", p);
610 for (af = AF_INET; af != 0; af = (af == AF_INET) ? AF_INET6 : 0) {
611 a = blobmsg_open_array(&blob,
612 (af == AF_INET) ? "ipaddrs" : "ip6addrs");
614 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
615 if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != af)
618 if (strcmp(ifa->ifa_name, name))
621 o2 = blobmsg_open_table(&blob, NULL);
623 blobmsg_add_string(&blob, "address",
624 sa2str(ifa->ifa_addr));
626 blobmsg_add_string(&blob, "netmask",
627 sa2str(ifa->ifa_netmask));
629 if (ifa->ifa_dstaddr && (ifa->ifa_flags & IFF_POINTOPOINT))
630 blobmsg_add_string(&blob, "remote",
631 sa2str(ifa->ifa_dstaddr));
632 else if (ifa->ifa_broadaddr && (ifa->ifa_flags & IFF_BROADCAST))
633 blobmsg_add_string(&blob, "broadcast",
634 sa2str(ifa->ifa_broadaddr));
636 blobmsg_close_table(&blob, o2);
638 ifa_flags |= ifa->ifa_flags;
641 blobmsg_close_array(&blob, a);
644 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
645 if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_PACKET)
648 if (strcmp(ifa->ifa_name, name))
651 sll = (struct sockaddr_ll *)ifa->ifa_addr;
653 if (sll->sll_hatype == 1)
654 blobmsg_add_string(&blob, "mac", sa2str(ifa->ifa_addr));
656 blobmsg_add_u32(&blob, "type", sll->sll_hatype);
657 blobmsg_add_u32(&blob, "ifindex", sll->sll_ifindex);
659 ifa_flags |= ifa->ifa_flags;
663 o2 = blobmsg_open_table(&blob, "stats");
665 for (n = 0; n < ARRAY_SIZE(stats); n++) {
666 v = strtoull(readstr("/sys/class/net/%s/statistics/%s",
667 name, stats[n]), NULL, 10);
669 blobmsg_add_u64(&blob, stats[n], v);
672 blobmsg_close_table(&blob, o2);
674 o2 = blobmsg_open_table(&blob, "flags");
675 blobmsg_add_u8(&blob, "up", ifa_flags & IFF_UP);
676 blobmsg_add_u8(&blob, "broadcast", ifa_flags & IFF_BROADCAST);
677 blobmsg_add_u8(&blob, "promisc", ifa_flags & IFF_PROMISC);
678 blobmsg_add_u8(&blob, "loopback", ifa_flags & IFF_LOOPBACK);
679 blobmsg_add_u8(&blob, "noarp", ifa_flags & IFF_NOARP);
680 blobmsg_add_u8(&blob, "multicast", ifa_flags & IFF_MULTICAST);
681 blobmsg_add_u8(&blob, "pointtopoint", ifa_flags & IFF_POINTOPOINT);
682 blobmsg_close_table(&blob, o2);
684 blobmsg_close_table(&blob, o);
688 rpc_luci_get_network_devices(struct ubus_context *ctx,
689 struct ubus_object *obj,
690 struct ubus_request_data *req,
692 struct blob_attr *msg)
694 struct ifaddrs *ifaddr;
698 blob_buf_init(&blob, 0);
700 d = opendir("/sys/class/net");
703 if (getifaddrs(&ifaddr) == 1)
712 if (strcmp(e->d_name, ".") && strcmp(e->d_name, ".."))
713 rpc_luci_parse_network_device_sys(e->d_name, ifaddr);
722 ubus_send_reply(ctx, req, blob.head);
728 iw_call_str(int (*method)(const char *, char *), const char *dev,
729 struct blob_buf *blob, const char *field)
731 char buf[IWINFO_BUFSIZE] = {};
733 if (method(dev, buf) == 0)
734 blobmsg_add_string(blob, field, buf);
738 iw_call_num(int (*method)(const char *, int *), const char *dev,
739 struct blob_buf *blob, const char *field)
743 if (method(dev, &val) == 0)
744 blobmsg_add_u32(blob, field, val);
747 static bool rpc_luci_get_iwinfo(struct blob_buf *buf, const char *devname,
750 struct iwinfo_crypto_entry crypto = {};
751 struct iwinfo_hardware_id ids = {};
752 const struct iwinfo_ops *iw;
753 void *iwlib, *o, *o2, *a;
756 if (!iw_backend || !iw_close || !iw_modenames) {
757 iwlib = dlopen("libiwinfo.so", RTLD_LOCAL);
762 iw_backend = dlsym(iwlib, "iwinfo_backend");
763 iw_close = dlsym(iwlib, "iwinfo_close");
764 iw_modenames = dlsym(iwlib, "IWINFO_OPMODE_NAMES");
766 if (!iw_backend || !iw_close || !iw_modenames)
770 iw = iw_backend(devname);
775 o = blobmsg_open_table(buf, "iwinfo");
777 iw_call_num(iw->signal, devname, buf, "signal");
778 iw_call_num(iw->noise, devname, buf, "noise");
779 iw_call_num(iw->channel, devname, buf, "channel");
780 iw_call_str(iw->country, devname, buf, "country");
781 iw_call_str(iw->phyname, devname, buf, "phy");
782 iw_call_num(iw->txpower, devname, buf, "txpower");
783 iw_call_num(iw->txpower_offset, devname, buf, "txpower_offset");
784 iw_call_num(iw->frequency, devname, buf, "frequency");
785 iw_call_num(iw->frequency_offset, devname, buf, "frequency_offset");
787 if (!iw->hwmodelist(devname, &nret)) {
788 a = blobmsg_open_array(buf, "hwmodes");
790 if (nret & IWINFO_80211_AC)
791 blobmsg_add_string(buf, NULL, "ac");
793 if (nret & IWINFO_80211_A)
794 blobmsg_add_string(buf, NULL, "a");
796 if (nret & IWINFO_80211_B)
797 blobmsg_add_string(buf, NULL, "b");
799 if (nret & IWINFO_80211_G)
800 blobmsg_add_string(buf, NULL, "g");
802 if (nret & IWINFO_80211_N)
803 blobmsg_add_string(buf, NULL, "n");
805 blobmsg_close_array(buf, a);
808 if (!iw->htmodelist(devname, &nret)) {
809 a = blobmsg_open_array(buf, "htmodes");
811 if (nret & IWINFO_HTMODE_HT20)
812 blobmsg_add_string(buf, NULL, "HT20");
814 if (nret & IWINFO_HTMODE_HT40)
815 blobmsg_add_string(buf, NULL, "HT40");
817 if (nret & IWINFO_HTMODE_VHT20)
818 blobmsg_add_string(buf, NULL, "VHT20");
820 if (nret & IWINFO_HTMODE_VHT40)
821 blobmsg_add_string(buf, NULL, "VHT40");
823 if (nret & IWINFO_HTMODE_VHT80)
824 blobmsg_add_string(buf, NULL, "VHT80");
826 if (nret & IWINFO_HTMODE_VHT80_80)
827 blobmsg_add_string(buf, NULL, "VHT80+80");
829 if (nret & IWINFO_HTMODE_VHT160)
830 blobmsg_add_string(buf, NULL, "VHT160");
832 blobmsg_close_array(buf, a);
835 if (!iw->hardware_id(devname, (char *)&ids)) {
836 o2 = blobmsg_open_table(buf, "hardware");
838 a = blobmsg_open_array(buf, "id");
839 blobmsg_add_u32(buf, NULL, ids.vendor_id);
840 blobmsg_add_u32(buf, NULL, ids.device_id);
841 blobmsg_add_u32(buf, NULL, ids.subsystem_vendor_id);
842 blobmsg_add_u32(buf, NULL, ids.subsystem_device_id);
843 blobmsg_close_array(buf, a);
845 iw_call_str(iw->hardware_name, devname, buf, "name");
847 blobmsg_close_table(buf, o2);
851 iw_call_num(iw->quality, devname, buf, "quality");
852 iw_call_num(iw->quality_max, devname, buf, "quality_max");
853 iw_call_num(iw->bitrate, devname, buf, "bitrate");
855 if (!iw->mode(devname, &nret))
856 blobmsg_add_string(buf, "mode", iw_modenames[nret]);
858 iw_call_str(iw->ssid, devname, buf, "ssid");
859 iw_call_str(iw->bssid, devname, buf, "bssid");
861 if (!iw->encryption(devname, (char *)&crypto)) {
862 o2 = blobmsg_open_table(buf, "encryption");
864 blobmsg_add_u8(buf, "enabled", crypto.enabled);
866 if (crypto.enabled) {
867 if (!crypto.wpa_version) {
868 a = blobmsg_open_array(buf, "wep");
870 if (crypto.auth_algs & IWINFO_AUTH_OPEN)
871 blobmsg_add_string(buf, NULL, "open");
873 if (crypto.auth_algs & IWINFO_AUTH_SHARED)
874 blobmsg_add_string(buf, NULL, "shared");
876 blobmsg_close_array(buf, a);
879 a = blobmsg_open_array(buf, "wpa");
881 for (nret = 1; nret <= 3; nret++)
882 if (crypto.wpa_version & (1 << (nret - 1)))
883 blobmsg_add_u32(buf, NULL, nret);
885 blobmsg_close_array(buf, a);
887 a = blobmsg_open_array(buf, "authentication");
889 if (crypto.auth_suites & IWINFO_KMGMT_PSK)
890 blobmsg_add_string(buf, NULL, "psk");
892 if (crypto.auth_suites & IWINFO_KMGMT_8021x)
893 blobmsg_add_string(buf, NULL, "802.1x");
895 if (crypto.auth_suites & IWINFO_KMGMT_SAE)
896 blobmsg_add_string(buf, NULL, "sae");
898 if (crypto.auth_suites & IWINFO_KMGMT_OWE)
899 blobmsg_add_string(buf, NULL, "owe");
901 if (!crypto.auth_suites ||
902 (crypto.auth_suites & IWINFO_KMGMT_NONE))
903 blobmsg_add_string(buf, NULL, "none");
905 blobmsg_close_array(buf, a);
908 a = blobmsg_open_array(buf, "ciphers");
909 nret = crypto.pair_ciphers | crypto.group_ciphers;
911 if (nret & IWINFO_CIPHER_WEP40)
912 blobmsg_add_string(buf, NULL, "wep-40");
914 if (nret & IWINFO_CIPHER_WEP104)
915 blobmsg_add_string(buf, NULL, "wep-104");
917 if (nret & IWINFO_CIPHER_TKIP)
918 blobmsg_add_string(buf, NULL, "tkip");
920 if (nret & IWINFO_CIPHER_CCMP)
921 blobmsg_add_string(buf, NULL, "ccmp");
923 if (nret & IWINFO_CIPHER_WRAP)
924 blobmsg_add_string(buf, NULL, "wrap");
926 if (nret & IWINFO_CIPHER_AESOCB)
927 blobmsg_add_string(buf, NULL, "aes-ocb");
929 if (nret & IWINFO_CIPHER_CKIP)
930 blobmsg_add_string(buf, NULL, "ckip");
932 if (!nret || (nret & IWINFO_CIPHER_NONE))
933 blobmsg_add_string(buf, NULL, "none");
935 blobmsg_close_array(buf, a);
938 blobmsg_close_table(buf, o2);
942 blobmsg_close_table(buf, o);
949 static void rpc_luci_get_wireless_devices_cb(struct ubus_request *req,
950 int type, struct blob_attr *msg)
952 struct blob_attr *wifi, *cur, *iface, *cur2;
953 struct reply_context *rctx = req->priv;
954 const char *name, *first_ifname;
955 int rem, rem2, rem3, rem4;
958 blob_for_each_attr(wifi, msg, rem) {
959 if (blobmsg_type(wifi) != BLOBMSG_TYPE_TABLE ||
960 blobmsg_name(wifi) == NULL)
963 o = blobmsg_open_table(&rctx->blob, blobmsg_name(wifi));
965 rem2 = blobmsg_data_len(wifi);
968 __blob_for_each_attr(cur, blobmsg_data(wifi), rem2) {
969 name = blobmsg_name(cur);
971 if (!name || !strcmp(name, "iwinfo")) {
974 else if (!strcmp(name, "interfaces")) {
975 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY)
978 a = blobmsg_open_array(&rctx->blob, "interfaces");
980 rem3 = blobmsg_data_len(cur);
982 __blob_for_each_attr(iface, blobmsg_data(cur), rem3) {
983 if (blobmsg_type(iface) != BLOBMSG_TYPE_TABLE)
986 o2 = blobmsg_open_table(&rctx->blob, NULL);
988 rem4 = blobmsg_data_len(iface);
991 __blob_for_each_attr(cur2, blobmsg_data(iface), rem4) {
992 if (!strcmp(blobmsg_name(cur2), "ifname"))
993 name = blobmsg_get_string(cur2);
994 else if (!strcmp(blobmsg_name(cur2), "iwinfo"))
997 blobmsg_add_blob(&rctx->blob, cur2);
1001 if (rpc_luci_get_iwinfo(&rctx->blob, name, false))
1002 first_ifname = first_ifname ? first_ifname : name;
1004 blobmsg_close_table(&rctx->blob, o2);
1007 blobmsg_close_array(&rctx->blob, a);
1010 blobmsg_add_blob(&rctx->blob, cur);
1014 rpc_luci_get_iwinfo(&rctx->blob,
1015 first_ifname ? first_ifname : blobmsg_name(wifi),
1018 blobmsg_close_table(&rctx->blob, o);
1021 finish_request(rctx, UBUS_STATUS_OK);
1025 rpc_luci_get_wireless_devices(struct ubus_context *ctx,
1026 struct ubus_object *obj,
1027 struct ubus_request_data *req,
1029 struct blob_attr *msg)
1031 struct reply_context *rctx = defer_request(ctx, req);
1034 return UBUS_STATUS_UNKNOWN_ERROR;
1036 if (!invoke_ubus(ctx, "network.wireless", "status", NULL,
1037 rpc_luci_get_wireless_devices_cb, rctx))
1038 return finish_request(rctx, UBUS_STATUS_NOT_FOUND);
1040 return UBUS_STATUS_OK;
1044 struct avl_node avl;
1047 struct in6_addr ip6;
1051 nl_cb_done(struct nl_msg *msg, void *arg)
1053 struct reply_context *rctx = arg;
1059 nl_cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
1061 struct reply_context *rctx = arg;
1066 static struct host_hint *
1067 rpc_luci_get_host_hint(struct reply_context *rctx, struct ether_addr *ea)
1069 struct host_hint *hint;
1076 hint = avl_find_element(&rctx->avl, mac, hint, avl);
1079 hint = calloc_a(sizeof(*hint), &p, strlen(mac) + 1);
1084 hint->avl.key = strcpy(p, mac);
1085 avl_insert(&rctx->avl, &hint->avl);
1091 static int nl_cb_dump_neigh(struct nl_msg *msg, void *arg)
1093 struct reply_context *rctx = arg;
1094 struct ether_addr *mac;
1095 struct in6_addr *dst;
1096 struct nlmsghdr *hdr = nlmsg_hdr(msg);
1097 struct ndmsg *nd = NLMSG_DATA(hdr);
1098 struct nlattr *tb[NDA_MAX+1];
1099 struct host_hint *hint;
1101 rctx->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI);
1103 if (hdr->nlmsg_type != RTM_NEWNEIGH ||
1104 (nd->ndm_family != AF_INET && nd->ndm_family != AF_INET6))
1107 if (!(nd->ndm_state & (0xFF & ~NUD_NOARP)))
1110 nlmsg_parse(hdr, sizeof(*nd), tb, NDA_MAX, NULL);
1112 mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL;
1113 dst = tb[NDA_DST] ? RTA_DATA(tb[NDA_DST]) : NULL;
1118 hint = rpc_luci_get_host_hint(rctx, mac);
1123 if (nd->ndm_family == AF_INET)
1124 hint->ip = *(struct in_addr *)dst;
1126 hint->ip6 = *(struct in6_addr *)dst;
1132 rpc_luci_get_host_hints_nl(struct reply_context *rctx)
1134 struct nl_sock *sock = NULL;
1135 struct nl_msg *msg = NULL;
1136 struct nl_cb *cb = NULL;
1137 struct ndmsg ndm = {};
1139 sock = nl_socket_alloc();
1144 if (nl_connect(sock, NETLINK_ROUTE))
1147 cb = nl_cb_alloc(NL_CB_DEFAULT);
1152 msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
1157 nlmsg_append(msg, &ndm, sizeof(ndm), 0);
1159 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nl_cb_dump_neigh, rctx);
1160 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_cb_done, rctx);
1161 nl_cb_err(cb, NL_CB_CUSTOM, nl_cb_error, rctx);
1163 avl_init(&rctx->avl, avl_strcmp, false, NULL);
1167 nl_send_auto_complete(sock, msg);
1169 while (rctx->pending)
1170 nl_recvmsgs(sock, cb);
1174 nl_socket_free(sock);
1184 rpc_luci_get_host_hints_ether(struct reply_context *rctx)
1186 struct host_hint *hint;
1191 f = fopen("/etc/ethers", "r");
1196 while (fgets(buf, sizeof(buf), f)) {
1197 p = strtok(buf, " \t\n");
1198 hint = rpc_luci_get_host_hint(rctx, p ? ether_aton(p) : NULL);
1203 p = strtok(NULL, " \t\n");
1208 if (inet_pton(AF_INET, p, &in) == 1) {
1209 if (hint->ip.s_addr == 0)
1212 else if (*p && !hint->hostname) {
1213 hint->hostname = strdup(p);
1221 rpc_luci_get_host_hints_uci(struct reply_context *rctx)
1223 struct uci_ptr ptr = { .package = "dhcp" };
1224 struct uci_context *uci = NULL;
1225 struct uci_package *pkg = NULL;
1226 struct in6_addr empty = {};
1227 struct lease_entry *lease;
1228 struct host_hint *hint;
1229 struct uci_element *e, *l;
1230 struct uci_section *s;
1234 uci = uci_alloc_context();
1239 uci_load(uci, ptr.package, &pkg);
1244 uci_foreach_element(&pkg->sections, e)
1246 s = uci_to_section(e);
1248 if (strcmp(s->type, "host"))
1251 ptr.section = s->e.name;
1257 if (uci_lookup_ptr(uci, &ptr, NULL, true))
1260 if (ptr.o->type != UCI_TYPE_STRING)
1263 if (inet_pton(AF_INET, ptr.o->v.string, &in) != 1)
1266 ptr.option = "name";
1269 if (!uci_lookup_ptr(uci, &ptr, NULL, true) &&
1270 ptr.o->type == UCI_TYPE_STRING)
1271 n = ptr.o->v.string;
1278 if (uci_lookup_ptr(uci, &ptr, NULL, true))
1281 if (ptr.o->type == UCI_TYPE_STRING) {
1282 for (p = strtok(ptr.o->v.string, " \t");
1284 p = strtok(NULL, " \t")) {
1285 hint = rpc_luci_get_host_hint(rctx, ether_aton(p));
1290 if (hint->ip.s_addr == 0)
1293 if (n && !hint->hostname)
1294 hint->hostname = strdup(n);
1297 else if (ptr.o->type == UCI_TYPE_LIST) {
1298 uci_foreach_element(&ptr.o->v.list, l) {
1299 hint = rpc_luci_get_host_hint(rctx, ether_aton(l->name));
1304 if (hint->ip.s_addr == 0)
1307 if (n && !hint->hostname)
1308 hint->hostname = strdup(n);
1315 while ((lease = lease_next()) != NULL) {
1316 hint = rpc_luci_get_host_hint(rctx, &lease->mac);
1321 if (lease->af == AF_INET && hint->ip.s_addr == 0)
1322 hint->ip = lease->addr.in;
1323 else if (lease->af == AF_INET6 &&
1324 !memcmp(&hint->ip6, &empty, sizeof(empty)))
1325 hint->ip6 = lease->addr.in6;
1327 if (lease->hostname && !hint->hostname)
1328 hint->hostname = strdup(lease->hostname);
1335 uci_free_context(uci);
1339 rpc_luci_get_host_hints_ifaddrs(struct reply_context *rctx)
1341 struct ether_addr empty_ea = {};
1342 struct in6_addr empty_in6 = {};
1343 struct ifaddrs *ifaddr, *ifa;
1344 struct sockaddr_ll *sll;
1345 struct avl_tree devices;
1346 struct host_hint *hint;
1348 struct avl_node avl;
1349 struct ether_addr ea;
1350 struct in6_addr in6;
1352 } *device, *nextdevice;
1355 avl_init(&devices, avl_strcmp, false, NULL);
1357 if (getifaddrs(&ifaddr) == -1)
1360 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
1364 device = avl_find_element(&devices, ifa->ifa_name, device, avl);
1367 device = calloc_a(sizeof(*device), &p, strlen(ifa->ifa_name) + 1);
1372 device->avl.key = strcpy(p, ifa->ifa_name);
1373 avl_insert(&devices, &device->avl);
1376 switch (ifa->ifa_addr->sa_family) {
1378 sll = (struct sockaddr_ll *)ifa->ifa_addr;
1380 if (sll->sll_halen == 6)
1381 memcpy(&device->ea, sll->sll_addr, 6);
1386 device->in6 = ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
1390 device->in = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
1395 freeifaddrs(ifaddr);
1397 avl_for_each_element_safe(&devices, device, avl, nextdevice) {
1398 if (memcmp(&device->ea, &empty_ea, sizeof(empty_ea)) &&
1399 (memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) ||
1400 device->in.s_addr != 0)) {
1401 hint = rpc_luci_get_host_hint(rctx, &device->ea);
1404 if (hint->ip.s_addr == 0 && device->in.s_addr != 0)
1405 hint->ip = device->in;
1407 if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6)) == 0 &&
1408 memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) != 0)
1409 hint->ip6 = device->in6;
1413 avl_delete(&devices, &device->avl);
1419 rpc_luci_get_host_hints_finish(struct reply_context *rctx);
1422 rpc_luci_get_host_hints_rrdns_cb(struct ubus_request *req, int type,
1423 struct blob_attr *msg)
1425 struct reply_context *rctx = req->priv;
1426 struct host_hint *hint;
1427 struct blob_attr *cur;
1428 struct in6_addr in6;
1433 blob_for_each_attr(cur, msg, rem) {
1434 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
1437 if (inet_pton(AF_INET6, blobmsg_name(cur), &in6) == 1) {
1438 avl_for_each_element(&rctx->avl, hint, avl) {
1439 if (!memcmp(&hint->ip6, &in6, sizeof(in6))) {
1441 free(hint->hostname);
1443 hint->hostname = strdup(blobmsg_get_string(cur));
1448 else if (inet_pton(AF_INET, blobmsg_name(cur), &in) == 1) {
1449 avl_for_each_element(&rctx->avl, hint, avl) {
1450 if (!memcmp(&hint->ip, &in, sizeof(in))) {
1452 free(hint->hostname);
1454 hint->hostname = strdup(blobmsg_get_string(cur));
1462 rpc_luci_get_host_hints_finish(rctx);
1466 rpc_luci_get_host_hints_rrdns(struct reply_context *rctx)
1468 struct in6_addr empty_in6 = {};
1469 char buf[INET6_ADDRSTRLEN];
1470 struct blob_buf req = {};
1471 struct host_hint *hint;
1475 blob_buf_init(&req, 0);
1477 a = blobmsg_open_array(&req, "addrs");
1479 avl_for_each_element(&rctx->avl, hint, avl) {
1480 if (hint->ip.s_addr != 0) {
1481 inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
1482 blobmsg_add_string(&req, NULL, buf);
1485 else if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6))) {
1486 inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
1487 blobmsg_add_string(&req, NULL, buf);
1492 blobmsg_close_array(&req, a);
1495 blobmsg_add_u32(&req, "timeout", 250);
1496 blobmsg_add_u32(&req, "limit", n);
1498 if (!invoke_ubus(rctx->context, "network.rrdns", "lookup", &req,
1499 rpc_luci_get_host_hints_rrdns_cb, rctx))
1500 rpc_luci_get_host_hints_finish(rctx);
1503 rpc_luci_get_host_hints_finish(rctx);
1506 blob_buf_free(&req);
1510 rpc_luci_get_host_hints_finish(struct reply_context *rctx)
1512 struct host_hint *hint, *nexthint;
1513 char buf[INET6_ADDRSTRLEN];
1514 struct in6_addr in6 = {};
1515 struct in_addr in = {};
1518 avl_for_each_element_safe(&rctx->avl, hint, avl, nexthint) {
1519 o = blobmsg_open_table(&rctx->blob, hint->avl.key);
1521 if (memcmp(&hint->ip, &in, sizeof(in))) {
1522 inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
1523 blobmsg_add_string(&rctx->blob, "ipv4", buf);
1526 if (memcmp(&hint->ip6, &in6, sizeof(in6))) {
1527 inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
1528 blobmsg_add_string(&rctx->blob, "ipv6", buf);
1532 blobmsg_add_string(&rctx->blob, "name", hint->hostname);
1534 blobmsg_close_table(&rctx->blob, o);
1536 avl_delete(&rctx->avl, &hint->avl);
1539 free(hint->hostname);
1544 return finish_request(rctx, UBUS_STATUS_OK);
1548 rpc_luci_get_host_hints(struct ubus_context *ctx, struct ubus_object *obj,
1549 struct ubus_request_data *req, const char *method,
1550 struct blob_attr *msg)
1552 struct reply_context *rctx = defer_request(ctx, req);
1555 return UBUS_STATUS_UNKNOWN_ERROR;
1557 rpc_luci_get_host_hints_nl(rctx);
1558 rpc_luci_get_host_hints_uci(rctx);
1559 rpc_luci_get_host_hints_ether(rctx);
1560 rpc_luci_get_host_hints_ifaddrs(rctx);
1561 rpc_luci_get_host_hints_rrdns(rctx);
1563 return UBUS_STATUS_OK;
1567 rpc_luci_get_board_json(struct ubus_context *ctx, struct ubus_object *obj,
1568 struct ubus_request_data *req, const char *method,
1569 struct blob_attr *msg)
1571 blob_buf_init(&blob, 0);
1573 if (!blobmsg_add_json_from_file(&blob, "/etc/board.json"))
1574 return UBUS_STATUS_UNKNOWN_ERROR;
1576 ubus_send_reply(ctx, req, blob.head);
1577 return UBUS_STATUS_OK;
1581 rpc_luci_get_dsl_status(struct ubus_context *ctx, struct ubus_object *obj,
1582 struct ubus_request_data *req, const char *method,
1583 struct blob_attr *msg)
1585 char line[128], *p, *s;
1588 cmd = popen("/etc/init.d/dsl_control lucistat", "r");
1591 return UBUS_STATUS_NOT_FOUND;
1593 blob_buf_init(&blob, 0);
1595 while (fgets(line, sizeof(line), cmd)) {
1596 if (strncmp(line, "dsl.", 4))
1599 p = strchr(line, '=');
1604 s = p + strlen(p) - 1;
1606 while (s >= p && isspace(*s))
1611 if (!strcmp(p, "nil"))
1615 blobmsg_add_u32(&blob, line + 4, strtoul(p, NULL, 0));
1617 else if (*p == '"') {
1618 s = p + strlen(p) - 1;
1620 if (s >= p && *s == '"')
1623 blobmsg_add_string(&blob, line + 4, p + 1);
1629 ubus_send_reply(ctx, req, blob.head);
1630 return UBUS_STATUS_OK;
1639 static const struct blobmsg_policy rpc_get_leases_policy[__RPC_L_MAX] = {
1640 [RPC_L_FAMILY] = { .name = "family", .type = BLOBMSG_TYPE_INT32 }
1644 rpc_luci_get_dhcp_leases(struct ubus_context *ctx, struct ubus_object *obj,
1645 struct ubus_request_data *req, const char *method,
1646 struct blob_attr *msg)
1648 struct blob_attr *tb[__RPC_L_MAX];
1649 struct ether_addr emptymac = {};
1650 struct lease_entry *lease;
1651 char s[INET6_ADDRSTRLEN];
1652 struct uci_context *uci;
1656 blobmsg_parse(rpc_get_leases_policy, __RPC_L_MAX, tb,
1657 blob_data(msg), blob_len(msg));
1659 switch (tb[RPC_L_FAMILY] ? blobmsg_get_u32(tb[RPC_L_FAMILY]) : 0) {
1673 return UBUS_STATUS_INVALID_ARGUMENT;
1676 uci = uci_alloc_context();
1679 return UBUS_STATUS_UNKNOWN_ERROR;
1681 blob_buf_init(&blob, 0);
1683 for (af = family ? family : AF_INET;
1685 af = (family == 0) ? (af == AF_INET ? AF_INET6 : 0) : 0) {
1687 a = blobmsg_open_array(&blob, (af == AF_INET) ? "dhcp_leases"
1692 while ((lease = lease_next()) != NULL) {
1693 if (lease->af != af)
1696 o = blobmsg_open_table(&blob, NULL);
1698 if (lease->expire == -1)
1699 blobmsg_add_u8(&blob, "expires", 0);
1701 blobmsg_add_u32(&blob, "expires", lease->expire);
1703 if (lease->hostname)
1704 blobmsg_add_string(&blob, "hostname", lease->hostname);
1706 if (memcmp(&lease->mac, &emptymac, sizeof(emptymac)))
1707 blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
1710 blobmsg_add_string(&blob, "duid", lease->duid);
1712 inet_ntop(lease->af, &lease->addr.in6, s, sizeof(s));
1713 blobmsg_add_string(&blob, (af == AF_INET) ? "ipaddr" : "ip6addr",
1716 blobmsg_close_table(&blob, o);
1721 blobmsg_close_array(&blob, a);
1724 uci_free_context(uci);
1725 ubus_send_reply(ctx, req, blob.head);
1727 return UBUS_STATUS_OK;
1731 rpc_luci_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
1733 static const struct ubus_method luci_methods[] = {
1734 UBUS_METHOD_NOARG("getNetworkDevices", rpc_luci_get_network_devices),
1735 UBUS_METHOD_NOARG("getWirelessDevices", rpc_luci_get_wireless_devices),
1736 UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints),
1737 UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json),
1738 UBUS_METHOD_NOARG("getDSLStatus", rpc_luci_get_dsl_status),
1739 UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases, rpc_get_leases_policy)
1742 static struct ubus_object_type luci_type =
1743 UBUS_OBJECT_TYPE("rpcd-luci", luci_methods);
1745 static struct ubus_object obj = {
1748 .methods = luci_methods,
1749 .n_methods = ARRAY_SIZE(luci_methods),
1752 return ubus_add_object(ctx, &obj);
1755 struct rpc_plugin rpc_plugin = {
1756 .init = rpc_luci_api_init