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) || ptr.o == NULL)
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");
438 p = strtok(NULL, " \t\n");
440 if (p && inet_pton(AF_INET6, p, &e.addr.in6))
442 else if (p && inet_pton(AF_INET, p, &e.addr.in))
447 if (!ea && e.af != AF_INET6)
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, "*"))
463 ea = duid2ea(e.duid);
471 fclose(lease_state.dnsmasq_file);
472 lease_state.dnsmasq_file = NULL;
475 if (lease_state.odhcpd_file) {
476 while (fgets(e.buf, sizeof(e.buf), lease_state.odhcpd_file)) {
477 strtok(e.buf, " \t\n"); /* # */
478 strtok(NULL, " \t\n"); /* iface */
480 e.duid = strtok(NULL, " \t\n"); /* duid */
485 p = strtok(NULL, " \t\n"); /* iaid */
488 e.af = strcmp(p, "ipv4") ? AF_INET6 : AF_INET;
492 e.hostname = strtok(NULL, " \t\n"); /* name */
497 p = strtok(NULL, " \t\n"); /* ts */
502 e.expire = strtol(p, NULL, 10);
503 e.expire = (e.expire > 0) ? e.expire - lease_state.now : e.expire;
505 strtok(NULL, " \t\n"); /* id */
506 strtok(NULL, " \t\n"); /* length */
508 p = strtok(NULL, "/ \t\n"); /* ip */
510 if (!p || !inet_pton(e.af, p, &e.addr.in6))
513 ea = duid2ea(e.duid);
518 if (!strcmp(e.hostname, "-"))
521 if (!strcmp(e.duid, "-"))
527 fclose(lease_state.odhcpd_file);
528 lease_state.odhcpd_file = NULL;
536 rpc_luci_parse_network_device_sys(const char *name, struct ifaddrs *ifaddr)
538 char link[64], buf[512], *p;
539 unsigned int ifa_flags = 0;
540 struct sockaddr_ll *sll;
549 const char *stats[] = {
550 "rx_bytes", "tx_bytes", "tx_errors", "rx_errors", "tx_packets",
551 "rx_packets", "multicast", "collisions", "rx_dropped", "tx_dropped"
554 o = blobmsg_open_table(&blob, name);
556 blobmsg_add_string(&blob, "name", name);
558 snprintf(buf, sizeof(buf), "/sys/class/net/%s/brif", name);
563 blobmsg_add_u8(&blob, "bridge", 1);
565 a = blobmsg_open_array(&blob, "ports");
573 if (strcmp(e->d_name, ".") && strcmp(e->d_name, ".."))
574 blobmsg_add_string(&blob, NULL, e->d_name);
577 blobmsg_close_array(&blob, a);
581 p = readstr("/sys/class/net/%s/bridge/bridge_id", name);
582 blobmsg_add_string(&blob, "id", p);
584 p = readstr("/sys/class/net/%s/bridge/stp_state", name);
585 blobmsg_add_u8(&blob, "stp", strcmp(p, "0") ? 1 : 0);
588 snprintf(buf, sizeof(buf), "/sys/class/net/%s/master", name);
589 len = readlink(buf, link, sizeof(link) - 1);
593 blobmsg_add_string(&blob, "master", basename(link));
596 p = readstr("/sys/class/net/%s/phy80211/index", name);
597 blobmsg_add_u8(&blob, "wireless", *p ? 1 : 0);
599 p = readstr("/sys/class/net/%s/operstate", name);
600 blobmsg_add_u8(&blob, "up", !strcmp(p, "up") || !strcmp(p, "unknown"));
602 n = atoi(readstr("/sys/class/net/%s/mtu", name));
604 blobmsg_add_u32(&blob, "mtu", n);
606 n = atoi(readstr("/sys/class/net/%s/tx_queue_len", name));
608 blobmsg_add_u32(&blob, "qlen", n);
610 p = readstr("/sys/class/net/%s/master", name);
612 blobmsg_add_string(&blob, "master", p);
614 for (af = AF_INET; af != 0; af = (af == AF_INET) ? AF_INET6 : 0) {
615 a = blobmsg_open_array(&blob,
616 (af == AF_INET) ? "ipaddrs" : "ip6addrs");
618 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
619 if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != af)
622 if (strcmp(ifa->ifa_name, name))
625 o2 = blobmsg_open_table(&blob, NULL);
627 blobmsg_add_string(&blob, "address",
628 sa2str(ifa->ifa_addr));
630 blobmsg_add_string(&blob, "netmask",
631 sa2str(ifa->ifa_netmask));
633 if (ifa->ifa_dstaddr && (ifa->ifa_flags & IFF_POINTOPOINT))
634 blobmsg_add_string(&blob, "remote",
635 sa2str(ifa->ifa_dstaddr));
636 else if (ifa->ifa_broadaddr && (ifa->ifa_flags & IFF_BROADCAST))
637 blobmsg_add_string(&blob, "broadcast",
638 sa2str(ifa->ifa_broadaddr));
640 blobmsg_close_table(&blob, o2);
642 ifa_flags |= ifa->ifa_flags;
645 blobmsg_close_array(&blob, a);
648 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
649 if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_PACKET)
652 if (strcmp(ifa->ifa_name, name))
655 sll = (struct sockaddr_ll *)ifa->ifa_addr;
657 if (sll->sll_hatype == 1)
658 blobmsg_add_string(&blob, "mac", sa2str(ifa->ifa_addr));
660 blobmsg_add_u32(&blob, "type", sll->sll_hatype);
661 blobmsg_add_u32(&blob, "ifindex", sll->sll_ifindex);
663 ifa_flags |= ifa->ifa_flags;
667 o2 = blobmsg_open_table(&blob, "stats");
669 for (n = 0; n < ARRAY_SIZE(stats); n++) {
670 v = strtoull(readstr("/sys/class/net/%s/statistics/%s",
671 name, stats[n]), NULL, 10);
673 blobmsg_add_u64(&blob, stats[n], v);
676 blobmsg_close_table(&blob, o2);
678 o2 = blobmsg_open_table(&blob, "flags");
679 blobmsg_add_u8(&blob, "up", ifa_flags & IFF_UP);
680 blobmsg_add_u8(&blob, "broadcast", ifa_flags & IFF_BROADCAST);
681 blobmsg_add_u8(&blob, "promisc", ifa_flags & IFF_PROMISC);
682 blobmsg_add_u8(&blob, "loopback", ifa_flags & IFF_LOOPBACK);
683 blobmsg_add_u8(&blob, "noarp", ifa_flags & IFF_NOARP);
684 blobmsg_add_u8(&blob, "multicast", ifa_flags & IFF_MULTICAST);
685 blobmsg_add_u8(&blob, "pointtopoint", ifa_flags & IFF_POINTOPOINT);
686 blobmsg_close_table(&blob, o2);
688 blobmsg_close_table(&blob, o);
692 rpc_luci_get_network_devices(struct ubus_context *ctx,
693 struct ubus_object *obj,
694 struct ubus_request_data *req,
696 struct blob_attr *msg)
698 struct ifaddrs *ifaddr;
702 blob_buf_init(&blob, 0);
704 d = opendir("/sys/class/net");
707 if (getifaddrs(&ifaddr) == 1)
716 if (strcmp(e->d_name, ".") && strcmp(e->d_name, ".."))
717 rpc_luci_parse_network_device_sys(e->d_name, ifaddr);
726 ubus_send_reply(ctx, req, blob.head);
732 iw_call_str(int (*method)(const char *, char *), const char *dev,
733 struct blob_buf *blob, const char *field)
735 char buf[IWINFO_BUFSIZE] = {};
737 if (method(dev, buf) == 0)
738 blobmsg_add_string(blob, field, buf);
742 iw_call_num(int (*method)(const char *, int *), const char *dev,
743 struct blob_buf *blob, const char *field)
747 if (method(dev, &val) == 0)
748 blobmsg_add_u32(blob, field, val);
751 static bool rpc_luci_get_iwinfo(struct blob_buf *buf, const char *devname,
754 struct iwinfo_crypto_entry crypto = {};
755 struct iwinfo_hardware_id ids = {};
756 const struct iwinfo_ops *iw;
757 void *iwlib, *o, *o2, *a;
760 if (!iw_backend || !iw_close || !iw_modenames) {
761 iwlib = dlopen("libiwinfo.so", RTLD_LOCAL);
766 iw_backend = dlsym(iwlib, "iwinfo_backend");
767 iw_close = dlsym(iwlib, "iwinfo_close");
768 iw_modenames = dlsym(iwlib, "IWINFO_OPMODE_NAMES");
770 if (!iw_backend || !iw_close || !iw_modenames)
774 iw = iw_backend(devname);
779 o = blobmsg_open_table(buf, "iwinfo");
781 iw_call_num(iw->signal, devname, buf, "signal");
782 iw_call_num(iw->noise, devname, buf, "noise");
783 iw_call_num(iw->channel, devname, buf, "channel");
784 iw_call_str(iw->country, devname, buf, "country");
785 iw_call_str(iw->phyname, devname, buf, "phy");
786 iw_call_num(iw->txpower, devname, buf, "txpower");
787 iw_call_num(iw->txpower_offset, devname, buf, "txpower_offset");
788 iw_call_num(iw->frequency, devname, buf, "frequency");
789 iw_call_num(iw->frequency_offset, devname, buf, "frequency_offset");
791 if (!iw->hwmodelist(devname, &nret)) {
792 a = blobmsg_open_array(buf, "hwmodes");
794 if (nret & IWINFO_80211_AC)
795 blobmsg_add_string(buf, NULL, "ac");
797 if (nret & IWINFO_80211_A)
798 blobmsg_add_string(buf, NULL, "a");
800 if (nret & IWINFO_80211_B)
801 blobmsg_add_string(buf, NULL, "b");
803 if (nret & IWINFO_80211_G)
804 blobmsg_add_string(buf, NULL, "g");
806 if (nret & IWINFO_80211_N)
807 blobmsg_add_string(buf, NULL, "n");
809 blobmsg_close_array(buf, a);
812 if (!iw->htmodelist(devname, &nret)) {
813 a = blobmsg_open_array(buf, "htmodes");
815 if (nret & IWINFO_HTMODE_HT20)
816 blobmsg_add_string(buf, NULL, "HT20");
818 if (nret & IWINFO_HTMODE_HT40)
819 blobmsg_add_string(buf, NULL, "HT40");
821 if (nret & IWINFO_HTMODE_VHT20)
822 blobmsg_add_string(buf, NULL, "VHT20");
824 if (nret & IWINFO_HTMODE_VHT40)
825 blobmsg_add_string(buf, NULL, "VHT40");
827 if (nret & IWINFO_HTMODE_VHT80)
828 blobmsg_add_string(buf, NULL, "VHT80");
830 if (nret & IWINFO_HTMODE_VHT80_80)
831 blobmsg_add_string(buf, NULL, "VHT80+80");
833 if (nret & IWINFO_HTMODE_VHT160)
834 blobmsg_add_string(buf, NULL, "VHT160");
836 blobmsg_close_array(buf, a);
839 if (!iw->hardware_id(devname, (char *)&ids)) {
840 o2 = blobmsg_open_table(buf, "hardware");
842 a = blobmsg_open_array(buf, "id");
843 blobmsg_add_u32(buf, NULL, ids.vendor_id);
844 blobmsg_add_u32(buf, NULL, ids.device_id);
845 blobmsg_add_u32(buf, NULL, ids.subsystem_vendor_id);
846 blobmsg_add_u32(buf, NULL, ids.subsystem_device_id);
847 blobmsg_close_array(buf, a);
849 iw_call_str(iw->hardware_name, devname, buf, "name");
851 blobmsg_close_table(buf, o2);
855 iw_call_num(iw->quality, devname, buf, "quality");
856 iw_call_num(iw->quality_max, devname, buf, "quality_max");
857 iw_call_num(iw->bitrate, devname, buf, "bitrate");
859 if (!iw->mode(devname, &nret))
860 blobmsg_add_string(buf, "mode", iw_modenames[nret]);
862 iw_call_str(iw->ssid, devname, buf, "ssid");
863 iw_call_str(iw->bssid, devname, buf, "bssid");
865 if (!iw->encryption(devname, (char *)&crypto)) {
866 o2 = blobmsg_open_table(buf, "encryption");
868 blobmsg_add_u8(buf, "enabled", crypto.enabled);
870 if (crypto.enabled) {
871 if (!crypto.wpa_version) {
872 a = blobmsg_open_array(buf, "wep");
874 if (crypto.auth_algs & IWINFO_AUTH_OPEN)
875 blobmsg_add_string(buf, NULL, "open");
877 if (crypto.auth_algs & IWINFO_AUTH_SHARED)
878 blobmsg_add_string(buf, NULL, "shared");
880 blobmsg_close_array(buf, a);
883 a = blobmsg_open_array(buf, "wpa");
885 for (nret = 1; nret <= 3; nret++)
886 if (crypto.wpa_version & (1 << (nret - 1)))
887 blobmsg_add_u32(buf, NULL, nret);
889 blobmsg_close_array(buf, a);
891 a = blobmsg_open_array(buf, "authentication");
893 if (crypto.auth_suites & IWINFO_KMGMT_PSK)
894 blobmsg_add_string(buf, NULL, "psk");
896 if (crypto.auth_suites & IWINFO_KMGMT_8021x)
897 blobmsg_add_string(buf, NULL, "802.1x");
899 if (crypto.auth_suites & IWINFO_KMGMT_SAE)
900 blobmsg_add_string(buf, NULL, "sae");
902 if (crypto.auth_suites & IWINFO_KMGMT_OWE)
903 blobmsg_add_string(buf, NULL, "owe");
905 if (!crypto.auth_suites ||
906 (crypto.auth_suites & IWINFO_KMGMT_NONE))
907 blobmsg_add_string(buf, NULL, "none");
909 blobmsg_close_array(buf, a);
912 a = blobmsg_open_array(buf, "ciphers");
913 nret = crypto.pair_ciphers | crypto.group_ciphers;
915 if (nret & IWINFO_CIPHER_WEP40)
916 blobmsg_add_string(buf, NULL, "wep-40");
918 if (nret & IWINFO_CIPHER_WEP104)
919 blobmsg_add_string(buf, NULL, "wep-104");
921 if (nret & IWINFO_CIPHER_TKIP)
922 blobmsg_add_string(buf, NULL, "tkip");
924 if (nret & IWINFO_CIPHER_CCMP)
925 blobmsg_add_string(buf, NULL, "ccmp");
927 if (nret & IWINFO_CIPHER_WRAP)
928 blobmsg_add_string(buf, NULL, "wrap");
930 if (nret & IWINFO_CIPHER_AESOCB)
931 blobmsg_add_string(buf, NULL, "aes-ocb");
933 if (nret & IWINFO_CIPHER_CKIP)
934 blobmsg_add_string(buf, NULL, "ckip");
936 if (!nret || (nret & IWINFO_CIPHER_NONE))
937 blobmsg_add_string(buf, NULL, "none");
939 blobmsg_close_array(buf, a);
942 blobmsg_close_table(buf, o2);
946 blobmsg_close_table(buf, o);
953 static void rpc_luci_get_wireless_devices_cb(struct ubus_request *req,
954 int type, struct blob_attr *msg)
956 struct blob_attr *wifi, *cur, *iface, *cur2;
957 struct reply_context *rctx = req->priv;
958 const char *name, *first_ifname;
959 int rem, rem2, rem3, rem4;
962 blob_for_each_attr(wifi, msg, rem) {
963 if (blobmsg_type(wifi) != BLOBMSG_TYPE_TABLE ||
964 blobmsg_name(wifi) == NULL)
967 o = blobmsg_open_table(&rctx->blob, blobmsg_name(wifi));
969 rem2 = blobmsg_data_len(wifi);
972 __blob_for_each_attr(cur, blobmsg_data(wifi), rem2) {
973 name = blobmsg_name(cur);
975 if (!name || !strcmp(name, "iwinfo")) {
978 else if (!strcmp(name, "interfaces")) {
979 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY)
982 a = blobmsg_open_array(&rctx->blob, "interfaces");
984 rem3 = blobmsg_data_len(cur);
986 __blob_for_each_attr(iface, blobmsg_data(cur), rem3) {
987 if (blobmsg_type(iface) != BLOBMSG_TYPE_TABLE)
990 o2 = blobmsg_open_table(&rctx->blob, NULL);
992 rem4 = blobmsg_data_len(iface);
995 __blob_for_each_attr(cur2, blobmsg_data(iface), rem4) {
996 if (!strcmp(blobmsg_name(cur2), "ifname"))
997 name = blobmsg_get_string(cur2);
998 else if (!strcmp(blobmsg_name(cur2), "iwinfo"))
1001 blobmsg_add_blob(&rctx->blob, cur2);
1005 if (rpc_luci_get_iwinfo(&rctx->blob, name, false))
1006 first_ifname = first_ifname ? first_ifname : name;
1008 blobmsg_close_table(&rctx->blob, o2);
1011 blobmsg_close_array(&rctx->blob, a);
1014 blobmsg_add_blob(&rctx->blob, cur);
1018 rpc_luci_get_iwinfo(&rctx->blob,
1019 first_ifname ? first_ifname : blobmsg_name(wifi),
1022 blobmsg_close_table(&rctx->blob, o);
1025 finish_request(rctx, UBUS_STATUS_OK);
1029 rpc_luci_get_wireless_devices(struct ubus_context *ctx,
1030 struct ubus_object *obj,
1031 struct ubus_request_data *req,
1033 struct blob_attr *msg)
1035 struct reply_context *rctx = defer_request(ctx, req);
1038 return UBUS_STATUS_UNKNOWN_ERROR;
1040 if (!invoke_ubus(ctx, "network.wireless", "status", NULL,
1041 rpc_luci_get_wireless_devices_cb, rctx))
1042 return finish_request(rctx, UBUS_STATUS_NOT_FOUND);
1044 return UBUS_STATUS_OK;
1048 struct avl_node avl;
1051 struct in6_addr ip6;
1055 nl_cb_done(struct nl_msg *msg, void *arg)
1057 struct reply_context *rctx = arg;
1063 nl_cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
1065 struct reply_context *rctx = arg;
1070 static struct host_hint *
1071 rpc_luci_get_host_hint(struct reply_context *rctx, struct ether_addr *ea)
1073 struct host_hint *hint;
1080 hint = avl_find_element(&rctx->avl, mac, hint, avl);
1083 hint = calloc_a(sizeof(*hint), &p, strlen(mac) + 1);
1088 hint->avl.key = strcpy(p, mac);
1089 avl_insert(&rctx->avl, &hint->avl);
1095 static int nl_cb_dump_neigh(struct nl_msg *msg, void *arg)
1097 struct reply_context *rctx = arg;
1098 struct ether_addr *mac;
1099 struct in6_addr *dst;
1100 struct nlmsghdr *hdr = nlmsg_hdr(msg);
1101 struct ndmsg *nd = NLMSG_DATA(hdr);
1102 struct nlattr *tb[NDA_MAX+1];
1103 struct host_hint *hint;
1105 rctx->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI);
1107 if (hdr->nlmsg_type != RTM_NEWNEIGH ||
1108 (nd->ndm_family != AF_INET && nd->ndm_family != AF_INET6))
1111 if (!(nd->ndm_state & (0xFF & ~NUD_NOARP)))
1114 nlmsg_parse(hdr, sizeof(*nd), tb, NDA_MAX, NULL);
1116 mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL;
1117 dst = tb[NDA_DST] ? RTA_DATA(tb[NDA_DST]) : NULL;
1122 hint = rpc_luci_get_host_hint(rctx, mac);
1127 if (nd->ndm_family == AF_INET)
1128 hint->ip = *(struct in_addr *)dst;
1130 hint->ip6 = *(struct in6_addr *)dst;
1136 rpc_luci_get_host_hints_nl(struct reply_context *rctx)
1138 struct nl_sock *sock = NULL;
1139 struct nl_msg *msg = NULL;
1140 struct nl_cb *cb = NULL;
1141 struct ndmsg ndm = {};
1143 sock = nl_socket_alloc();
1148 if (nl_connect(sock, NETLINK_ROUTE))
1151 cb = nl_cb_alloc(NL_CB_DEFAULT);
1156 msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
1161 nlmsg_append(msg, &ndm, sizeof(ndm), 0);
1163 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nl_cb_dump_neigh, rctx);
1164 nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_cb_done, rctx);
1165 nl_cb_err(cb, NL_CB_CUSTOM, nl_cb_error, rctx);
1167 avl_init(&rctx->avl, avl_strcmp, false, NULL);
1171 nl_send_auto_complete(sock, msg);
1173 while (rctx->pending)
1174 nl_recvmsgs(sock, cb);
1178 nl_socket_free(sock);
1188 rpc_luci_get_host_hints_ether(struct reply_context *rctx)
1190 struct host_hint *hint;
1195 f = fopen("/etc/ethers", "r");
1200 while (fgets(buf, sizeof(buf), f)) {
1201 p = strtok(buf, " \t\n");
1202 hint = rpc_luci_get_host_hint(rctx, p ? ether_aton(p) : NULL);
1207 p = strtok(NULL, " \t\n");
1212 if (inet_pton(AF_INET, p, &in) == 1) {
1213 if (hint->ip.s_addr == 0)
1216 else if (*p && !hint->hostname) {
1217 hint->hostname = strdup(p);
1225 rpc_luci_get_host_hints_uci(struct reply_context *rctx)
1227 struct uci_ptr ptr = { .package = "dhcp" };
1228 struct uci_context *uci = NULL;
1229 struct uci_package *pkg = NULL;
1230 struct in6_addr empty = {};
1231 struct lease_entry *lease;
1232 struct host_hint *hint;
1233 struct uci_element *e, *l;
1234 struct uci_section *s;
1238 uci = uci_alloc_context();
1243 uci_load(uci, ptr.package, &pkg);
1248 uci_foreach_element(&pkg->sections, e)
1250 s = uci_to_section(e);
1252 if (strcmp(s->type, "host"))
1255 ptr.section = s->e.name;
1261 if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL)
1264 if (ptr.o->type != UCI_TYPE_STRING)
1267 if (inet_pton(AF_INET, ptr.o->v.string, &in) != 1)
1270 ptr.option = "name";
1273 if (!uci_lookup_ptr(uci, &ptr, NULL, true) && ptr.o != NULL &&
1274 ptr.o->type == UCI_TYPE_STRING)
1275 n = ptr.o->v.string;
1282 if (uci_lookup_ptr(uci, &ptr, NULL, true) || ptr.o == NULL)
1285 if (ptr.o->type == UCI_TYPE_STRING) {
1286 for (p = strtok(ptr.o->v.string, " \t");
1288 p = strtok(NULL, " \t")) {
1289 hint = rpc_luci_get_host_hint(rctx, ether_aton(p));
1294 if (hint->ip.s_addr == 0)
1297 if (n && !hint->hostname)
1298 hint->hostname = strdup(n);
1301 else if (ptr.o->type == UCI_TYPE_LIST) {
1302 uci_foreach_element(&ptr.o->v.list, l) {
1303 hint = rpc_luci_get_host_hint(rctx, ether_aton(l->name));
1308 if (hint->ip.s_addr == 0)
1311 if (n && !hint->hostname)
1312 hint->hostname = strdup(n);
1319 while ((lease = lease_next()) != NULL) {
1320 hint = rpc_luci_get_host_hint(rctx, &lease->mac);
1325 if (lease->af == AF_INET && hint->ip.s_addr == 0)
1326 hint->ip = lease->addr.in;
1327 else if (lease->af == AF_INET6 &&
1328 !memcmp(&hint->ip6, &empty, sizeof(empty)))
1329 hint->ip6 = lease->addr.in6;
1331 if (lease->hostname && !hint->hostname)
1332 hint->hostname = strdup(lease->hostname);
1339 uci_free_context(uci);
1343 rpc_luci_get_host_hints_ifaddrs(struct reply_context *rctx)
1345 struct ether_addr empty_ea = {};
1346 struct in6_addr empty_in6 = {};
1347 struct ifaddrs *ifaddr, *ifa;
1348 struct sockaddr_ll *sll;
1349 struct avl_tree devices;
1350 struct host_hint *hint;
1352 struct avl_node avl;
1353 struct ether_addr ea;
1354 struct in6_addr in6;
1356 } *device, *nextdevice;
1359 avl_init(&devices, avl_strcmp, false, NULL);
1361 if (getifaddrs(&ifaddr) == -1)
1364 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
1368 device = avl_find_element(&devices, ifa->ifa_name, device, avl);
1371 device = calloc_a(sizeof(*device), &p, strlen(ifa->ifa_name) + 1);
1376 device->avl.key = strcpy(p, ifa->ifa_name);
1377 avl_insert(&devices, &device->avl);
1380 switch (ifa->ifa_addr->sa_family) {
1382 sll = (struct sockaddr_ll *)ifa->ifa_addr;
1384 if (sll->sll_halen == 6)
1385 memcpy(&device->ea, sll->sll_addr, 6);
1390 device->in6 = ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
1394 device->in = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
1399 freeifaddrs(ifaddr);
1401 avl_for_each_element_safe(&devices, device, avl, nextdevice) {
1402 if (memcmp(&device->ea, &empty_ea, sizeof(empty_ea)) &&
1403 (memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) ||
1404 device->in.s_addr != 0)) {
1405 hint = rpc_luci_get_host_hint(rctx, &device->ea);
1408 if (hint->ip.s_addr == 0 && device->in.s_addr != 0)
1409 hint->ip = device->in;
1411 if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6)) == 0 &&
1412 memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) != 0)
1413 hint->ip6 = device->in6;
1417 avl_delete(&devices, &device->avl);
1423 rpc_luci_get_host_hints_finish(struct reply_context *rctx);
1426 rpc_luci_get_host_hints_rrdns_cb(struct ubus_request *req, int type,
1427 struct blob_attr *msg)
1429 struct reply_context *rctx = req->priv;
1430 struct host_hint *hint;
1431 struct blob_attr *cur;
1432 struct in6_addr in6;
1437 blob_for_each_attr(cur, msg, rem) {
1438 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
1441 if (inet_pton(AF_INET6, blobmsg_name(cur), &in6) == 1) {
1442 avl_for_each_element(&rctx->avl, hint, avl) {
1443 if (!memcmp(&hint->ip6, &in6, sizeof(in6))) {
1445 free(hint->hostname);
1447 hint->hostname = strdup(blobmsg_get_string(cur));
1452 else if (inet_pton(AF_INET, blobmsg_name(cur), &in) == 1) {
1453 avl_for_each_element(&rctx->avl, hint, avl) {
1454 if (!memcmp(&hint->ip, &in, sizeof(in))) {
1456 free(hint->hostname);
1458 hint->hostname = strdup(blobmsg_get_string(cur));
1466 rpc_luci_get_host_hints_finish(rctx);
1470 rpc_luci_get_host_hints_rrdns(struct reply_context *rctx)
1472 struct in6_addr empty_in6 = {};
1473 char buf[INET6_ADDRSTRLEN];
1474 struct blob_buf req = {};
1475 struct host_hint *hint;
1479 blob_buf_init(&req, 0);
1481 a = blobmsg_open_array(&req, "addrs");
1483 avl_for_each_element(&rctx->avl, hint, avl) {
1484 if (hint->ip.s_addr != 0) {
1485 inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
1486 blobmsg_add_string(&req, NULL, buf);
1489 else if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6))) {
1490 inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
1491 blobmsg_add_string(&req, NULL, buf);
1496 blobmsg_close_array(&req, a);
1499 blobmsg_add_u32(&req, "timeout", 250);
1500 blobmsg_add_u32(&req, "limit", n);
1502 if (!invoke_ubus(rctx->context, "network.rrdns", "lookup", &req,
1503 rpc_luci_get_host_hints_rrdns_cb, rctx))
1504 rpc_luci_get_host_hints_finish(rctx);
1507 rpc_luci_get_host_hints_finish(rctx);
1510 blob_buf_free(&req);
1514 rpc_luci_get_host_hints_finish(struct reply_context *rctx)
1516 struct host_hint *hint, *nexthint;
1517 char buf[INET6_ADDRSTRLEN];
1518 struct in6_addr in6 = {};
1519 struct in_addr in = {};
1522 avl_for_each_element_safe(&rctx->avl, hint, avl, nexthint) {
1523 o = blobmsg_open_table(&rctx->blob, hint->avl.key);
1525 if (memcmp(&hint->ip, &in, sizeof(in))) {
1526 inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
1527 blobmsg_add_string(&rctx->blob, "ipv4", buf);
1530 if (memcmp(&hint->ip6, &in6, sizeof(in6))) {
1531 inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
1532 blobmsg_add_string(&rctx->blob, "ipv6", buf);
1536 blobmsg_add_string(&rctx->blob, "name", hint->hostname);
1538 blobmsg_close_table(&rctx->blob, o);
1540 avl_delete(&rctx->avl, &hint->avl);
1543 free(hint->hostname);
1548 return finish_request(rctx, UBUS_STATUS_OK);
1552 rpc_luci_get_host_hints(struct ubus_context *ctx, struct ubus_object *obj,
1553 struct ubus_request_data *req, const char *method,
1554 struct blob_attr *msg)
1556 struct reply_context *rctx = defer_request(ctx, req);
1559 return UBUS_STATUS_UNKNOWN_ERROR;
1561 rpc_luci_get_host_hints_nl(rctx);
1562 rpc_luci_get_host_hints_uci(rctx);
1563 rpc_luci_get_host_hints_ether(rctx);
1564 rpc_luci_get_host_hints_ifaddrs(rctx);
1565 rpc_luci_get_host_hints_rrdns(rctx);
1567 return UBUS_STATUS_OK;
1571 rpc_luci_get_board_json(struct ubus_context *ctx, struct ubus_object *obj,
1572 struct ubus_request_data *req, const char *method,
1573 struct blob_attr *msg)
1575 blob_buf_init(&blob, 0);
1577 if (!blobmsg_add_json_from_file(&blob, "/etc/board.json"))
1578 return UBUS_STATUS_UNKNOWN_ERROR;
1580 ubus_send_reply(ctx, req, blob.head);
1581 return UBUS_STATUS_OK;
1585 rpc_luci_get_dsl_status(struct ubus_context *ctx, struct ubus_object *obj,
1586 struct ubus_request_data *req, const char *method,
1587 struct blob_attr *msg)
1589 char line[128], *p, *s;
1592 cmd = popen("/etc/init.d/dsl_control lucistat", "r");
1595 return UBUS_STATUS_NOT_FOUND;
1597 blob_buf_init(&blob, 0);
1599 while (fgets(line, sizeof(line), cmd)) {
1600 if (strncmp(line, "dsl.", 4))
1603 p = strchr(line, '=');
1608 s = p + strlen(p) - 1;
1610 while (s >= p && isspace(*s))
1615 if (!strcmp(p, "nil"))
1619 blobmsg_add_u32(&blob, line + 4, strtoul(p, NULL, 0));
1621 else if (*p == '"') {
1622 s = p + strlen(p) - 1;
1624 if (s >= p && *s == '"')
1627 blobmsg_add_string(&blob, line + 4, p + 1);
1633 ubus_send_reply(ctx, req, blob.head);
1634 return UBUS_STATUS_OK;
1643 static const struct blobmsg_policy rpc_get_leases_policy[__RPC_L_MAX] = {
1644 [RPC_L_FAMILY] = { .name = "family", .type = BLOBMSG_TYPE_INT32 }
1648 rpc_luci_get_dhcp_leases(struct ubus_context *ctx, struct ubus_object *obj,
1649 struct ubus_request_data *req, const char *method,
1650 struct blob_attr *msg)
1652 struct blob_attr *tb[__RPC_L_MAX];
1653 struct ether_addr emptymac = {};
1654 struct lease_entry *lease;
1655 char s[INET6_ADDRSTRLEN];
1656 struct uci_context *uci;
1660 blobmsg_parse(rpc_get_leases_policy, __RPC_L_MAX, tb,
1661 blob_data(msg), blob_len(msg));
1663 switch (tb[RPC_L_FAMILY] ? blobmsg_get_u32(tb[RPC_L_FAMILY]) : 0) {
1677 return UBUS_STATUS_INVALID_ARGUMENT;
1680 uci = uci_alloc_context();
1683 return UBUS_STATUS_UNKNOWN_ERROR;
1685 blob_buf_init(&blob, 0);
1687 for (af = family ? family : AF_INET;
1689 af = (family == 0) ? (af == AF_INET ? AF_INET6 : 0) : 0) {
1691 a = blobmsg_open_array(&blob, (af == AF_INET) ? "dhcp_leases"
1696 while ((lease = lease_next()) != NULL) {
1697 if (lease->af != af)
1700 o = blobmsg_open_table(&blob, NULL);
1702 if (lease->expire == -1)
1703 blobmsg_add_u8(&blob, "expires", 0);
1705 blobmsg_add_u32(&blob, "expires", lease->expire);
1707 if (lease->hostname)
1708 blobmsg_add_string(&blob, "hostname", lease->hostname);
1710 if (memcmp(&lease->mac, &emptymac, sizeof(emptymac)))
1711 blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
1714 blobmsg_add_string(&blob, "duid", lease->duid);
1716 inet_ntop(lease->af, &lease->addr.in6, s, sizeof(s));
1717 blobmsg_add_string(&blob, (af == AF_INET) ? "ipaddr" : "ip6addr",
1720 blobmsg_close_table(&blob, o);
1725 blobmsg_close_array(&blob, a);
1728 uci_free_context(uci);
1729 ubus_send_reply(ctx, req, blob.head);
1731 return UBUS_STATUS_OK;
1735 rpc_luci_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
1737 static const struct ubus_method luci_methods[] = {
1738 UBUS_METHOD_NOARG("getNetworkDevices", rpc_luci_get_network_devices),
1739 UBUS_METHOD_NOARG("getWirelessDevices", rpc_luci_get_wireless_devices),
1740 UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints),
1741 UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json),
1742 UBUS_METHOD_NOARG("getDSLStatus", rpc_luci_get_dsl_status),
1743 UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases, rpc_get_leases_policy)
1746 static struct ubus_object_type luci_type =
1747 UBUS_OBJECT_TYPE("rpcd-luci", luci_methods);
1749 static struct ubus_object obj = {
1752 .methods = luci_methods,
1753 .n_methods = ARRAY_SIZE(luci_methods),
1756 return ubus_add_object(ctx, &obj);
1759 struct rpc_plugin rpc_plugin = {
1760 .init = rpc_luci_api_init