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
);
91 invoke_done_cb(struct ubus_request
*req
, int ret
)
93 struct invoke_context
*ictx
=
94 container_of(req
, struct invoke_context
, request
);
97 ictx
->cb(req
, -1, NULL
);
99 uloop_timeout_cancel(&ictx
->timeout
);
104 invoke_timeout_cb(struct uloop_timeout
*timeout
)
106 struct invoke_context
*ictx
=
107 container_of(timeout
, struct invoke_context
, timeout
);
109 if (ictx
->cb
!= NULL
)
110 ictx
->cb(&ictx
->request
, -1, NULL
);
112 ubus_abort_request(ictx
->context
, &ictx
->request
);
116 static struct reply_context
*
117 defer_request(struct ubus_context
*ctx
, struct ubus_request_data
*req
)
119 struct reply_context
*rctx
;
121 rctx
= calloc(1, sizeof(*rctx
));
127 blob_buf_init(&rctx
->blob
, 0);
128 ubus_defer_request(ctx
, req
, &rctx
->request
);
134 finish_request(struct reply_context
*rctx
, int status
)
136 if (status
== UBUS_STATUS_OK
)
137 ubus_send_reply(rctx
->context
, &rctx
->request
, rctx
->blob
.head
);
139 ubus_complete_deferred_request(rctx
->context
, &rctx
->request
, status
);
140 blob_buf_free(&rctx
->blob
);
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
*),
152 struct invoke_context
*ictx
;
153 struct blob_buf empty
= {};
157 if (ubus_lookup_id(ctx
, object
, &id
))
161 blob_buf_init(&empty
, 0);
165 ictx
= calloc(1, sizeof(*ictx
));
171 rv
= !ubus_invoke_async(ctx
, id
, method
, req
->head
, &ictx
->request
);
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
);
180 ictx
->timeout
.cb
= invoke_timeout_cb
;
181 uloop_timeout_set(&ictx
->timeout
, 2000);
185 cb(&ictx
->request
, -1, NULL
);
197 readstr(const char *fmt
, ...)
199 static char data
[128];
206 vsnprintf(path
, sizeof(path
), fmt
, ap
);
210 f
= fopen(path
, "r");
213 n
= fread(data
, 1, sizeof(data
) - 1, f
);
216 while (n
> 0 && isspace(data
[n
-1]))
226 ea2str(struct ether_addr
*ea
)
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]);
242 sa2str(struct sockaddr
*sa
)
244 static char buf
[INET6_ADDRSTRLEN
];
246 struct sockaddr_in6
*in6
;
247 struct sockaddr_in
*in
;
248 struct sockaddr_ll
*ll
;
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
));
266 static struct ether_addr
*
267 duid2ea(const char *duid
)
269 static struct ether_addr ea
;
270 const char *p
= NULL
;
276 for (len
= 0; duid
[len
]; len
++)
277 if (!isxdigit(duid
[len
]))
281 (((x) <= '9') ? ((x) - '0') : \
282 (((x) <= 'F') ? ((x) - 'A' + 10) : \
287 if (!strncmp(duid
, "00010001", 8))
293 if (!strncmp(duid
, "00030001", 8))
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]);
330 struct ether_addr mac
;
340 add_leasefile(const char *path
, bool is_odhcpd
)
345 fh
= fopen(path
, "r");
350 ptr
= realloc(lease_state
.files
, sizeof(*lease_state
.files
) * (lease_state
.num
+ 1));
358 lease_state
.files
= ptr
;
359 lease_state
.files
[lease_state
.num
].fh
= fh
;
360 lease_state
.files
[lease_state
.num
].odhcpd
= is_odhcpd
;
367 find_leasefiles(struct uci_context
*uci
, bool is_odhcpd
)
369 struct uci_ptr ptr
= { .package
= "dhcp" };
370 struct uci_package
*pkg
= NULL
;
371 struct uci_section
*s
;
372 struct uci_element
*e
;
375 pkg
= uci_lookup_package(uci
, ptr
.package
);
378 uci_load(uci
, ptr
.package
, &pkg
);
384 uci_foreach_element(&pkg
->sections
, e
) {
385 s
= uci_to_section(e
);
387 if (strcmp(s
->type
, is_odhcpd
? "odhcpd" : "dnsmasq"))
392 ptr
.section
= s
->e
.name
;
395 ptr
.option
= "leasefile";
398 if (uci_lookup_ptr(uci
, &ptr
, NULL
, true) || ptr
.o
== NULL
)
401 if (ptr
.o
->type
!= UCI_TYPE_STRING
)
404 if (add_leasefile(ptr
.o
->v
.string
, is_odhcpd
))
414 while (lease_state
.num
> 0)
415 fclose(lease_state
.files
[--lease_state
.num
].fh
);
417 free(lease_state
.files
);
419 lease_state
.files
= NULL
;
427 struct uci_context
*uci
;
431 uci
= uci_alloc_context();
436 lease_state
.now
= time(NULL
);
438 if (!find_leasefiles(uci
, false))
439 add_leasefile("/tmp/dhcp.leases", false);
441 if (!find_leasefiles(uci
, true))
442 add_leasefile("/tmp/hosts/odhcpd", true);
444 uci_free_context(uci
);
447 static struct lease_entry
*
450 static struct lease_entry e
;
451 struct ether_addr
*ea
;
455 memset(&e
, 0, sizeof(e
));
457 while (lease_state
.off
< lease_state
.num
) {
458 while (fgets(e
.buf
, sizeof(e
.buf
), lease_state
.files
[lease_state
.off
].fh
)) {
459 if (lease_state
.files
[lease_state
.off
].odhcpd
) {
460 strtok(e
.buf
, " \t\n"); /* # */
461 strtok(NULL
, " \t\n"); /* iface */
463 e
.duid
= strtok(NULL
, " \t\n"); /* duid */
468 p
= strtok(NULL
, " \t\n"); /* iaid */
471 e
.af
= strcmp(p
, "ipv4") ? AF_INET6
: AF_INET
;
475 e
.hostname
= strtok(NULL
, " \t\n"); /* name */
480 p
= strtok(NULL
, " \t\n"); /* ts */
485 n
= strtol(p
, NULL
, 10);
487 if (n
> lease_state
.now
)
488 e
.expire
= n
- lease_state
.now
;
494 strtok(NULL
, " \t\n"); /* id */
495 strtok(NULL
, " \t\n"); /* length */
497 for (e
.n_addr
= 0, p
= strtok(NULL
, "/ \t\n");
498 e
.n_addr
< ARRAY_SIZE(e
.addr
) && p
!= NULL
;
499 p
= strtok(NULL
, "/ \t\n")) {
500 if (inet_pton(e
.af
, p
, &e
.addr
[e
.n_addr
].in6
))
504 ea
= duid2ea(e
.duid
);
509 if (!strcmp(e
.hostname
, "-"))
512 if (!strcmp(e
.duid
, "-"))
516 p
= strtok(e
.buf
, " \t\n");
521 n
= strtol(p
, NULL
, 10);
523 if (n
> lease_state
.now
)
524 e
.expire
= n
- lease_state
.now
;
530 p
= strtok(NULL
, " \t\n");
537 p
= strtok(NULL
, " \t\n");
539 if (p
&& inet_pton(AF_INET6
, p
, &e
.addr
[0].in6
)) {
543 else if (p
&& inet_pton(AF_INET
, p
, &e
.addr
[0].in
)) {
551 if (!ea
&& e
.af
!= AF_INET6
)
554 e
.hostname
= strtok(NULL
, " \t\n");
555 e
.duid
= strtok(NULL
, " \t\n");
557 if (!e
.hostname
|| !e
.duid
)
560 if (!strcmp(e
.hostname
, "*"))
563 if (!strcmp(e
.duid
, "*"))
567 ea
= duid2ea(e
.duid
);
584 rpc_luci_parse_network_device_sys(const char *name
, struct ifaddrs
*ifaddr
)
586 char link
[64], buf
[512], *p
;
587 unsigned int ifa_flags
= 0;
588 struct sockaddr_ll
*sll
;
597 const char *stats
[] = {
598 "rx_bytes", "tx_bytes", "tx_errors", "rx_errors", "tx_packets",
599 "rx_packets", "multicast", "collisions", "rx_dropped", "tx_dropped"
602 o
= blobmsg_open_table(&blob
, name
);
604 blobmsg_add_string(&blob
, "name", name
);
606 snprintf(buf
, sizeof(buf
), "/sys/class/net/%s/brif", name
);
611 blobmsg_add_u8(&blob
, "bridge", 1);
613 a
= blobmsg_open_array(&blob
, "ports");
621 if (strcmp(e
->d_name
, ".") && strcmp(e
->d_name
, ".."))
622 blobmsg_add_string(&blob
, NULL
, e
->d_name
);
625 blobmsg_close_array(&blob
, a
);
629 p
= readstr("/sys/class/net/%s/bridge/bridge_id", name
);
630 blobmsg_add_string(&blob
, "id", p
);
632 p
= readstr("/sys/class/net/%s/bridge/stp_state", name
);
633 blobmsg_add_u8(&blob
, "stp", strcmp(p
, "0") ? 1 : 0);
636 snprintf(buf
, sizeof(buf
), "/sys/class/net/%s/master", name
);
637 len
= readlink(buf
, link
, sizeof(link
) - 1);
641 blobmsg_add_string(&blob
, "master", basename(link
));
644 p
= readstr("/sys/class/net/%s/phy80211/index", name
);
645 blobmsg_add_u8(&blob
, "wireless", *p
? 1 : 0);
647 p
= readstr("/sys/class/net/%s/operstate", name
);
648 blobmsg_add_u8(&blob
, "up", !strcmp(p
, "up") || !strcmp(p
, "unknown"));
650 n
= atoi(readstr("/sys/class/net/%s/mtu", name
));
652 blobmsg_add_u32(&blob
, "mtu", n
);
654 n
= atoi(readstr("/sys/class/net/%s/tx_queue_len", name
));
656 blobmsg_add_u32(&blob
, "qlen", n
);
658 p
= readstr("/sys/class/net/%s/master", name
);
660 blobmsg_add_string(&blob
, "master", p
);
662 p
= strstr(readstr("/sys/class/net/%s/uevent", name
), "DEVTYPE=");
664 for (n
= 0, p
+= strlen("DEVTYPE=");; n
++) {
665 if (p
[n
] == '\0' || p
[n
] == '\n') {
667 blobmsg_add_string(&blob
, "devtype", p
);
673 blobmsg_add_string(&blob
, "devtype", "ethernet");
676 for (af
= AF_INET
; af
!= 0; af
= (af
== AF_INET
) ? AF_INET6
: 0) {
677 a
= blobmsg_open_array(&blob
,
678 (af
== AF_INET
) ? "ipaddrs" : "ip6addrs");
680 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
681 if (ifa
->ifa_addr
== NULL
|| ifa
->ifa_addr
->sa_family
!= af
)
684 if (strcmp(ifa
->ifa_name
, name
))
687 o2
= blobmsg_open_table(&blob
, NULL
);
689 blobmsg_add_string(&blob
, "address",
690 sa2str(ifa
->ifa_addr
));
692 blobmsg_add_string(&blob
, "netmask",
693 sa2str(ifa
->ifa_netmask
));
695 if (ifa
->ifa_dstaddr
&& (ifa
->ifa_flags
& IFF_POINTOPOINT
))
696 blobmsg_add_string(&blob
, "remote",
697 sa2str(ifa
->ifa_dstaddr
));
698 else if (ifa
->ifa_broadaddr
&& (ifa
->ifa_flags
& IFF_BROADCAST
))
699 blobmsg_add_string(&blob
, "broadcast",
700 sa2str(ifa
->ifa_broadaddr
));
702 blobmsg_close_table(&blob
, o2
);
704 ifa_flags
|= ifa
->ifa_flags
;
707 blobmsg_close_array(&blob
, a
);
710 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
711 if (ifa
->ifa_addr
== NULL
|| ifa
->ifa_addr
->sa_family
!= AF_PACKET
)
714 if (strcmp(ifa
->ifa_name
, name
))
717 sll
= (struct sockaddr_ll
*)ifa
->ifa_addr
;
719 if (sll
->sll_hatype
== 1)
720 blobmsg_add_string(&blob
, "mac", sa2str(ifa
->ifa_addr
));
722 blobmsg_add_u32(&blob
, "type", sll
->sll_hatype
);
723 blobmsg_add_u32(&blob
, "ifindex", sll
->sll_ifindex
);
725 ifa_flags
|= ifa
->ifa_flags
;
727 n
= atoi(readstr("/sys/class/net/%s/iflink", name
));
729 if (n
!= sll
->sll_ifindex
) {
730 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
731 if (ifa
->ifa_addr
== NULL
|| ifa
->ifa_addr
->sa_family
!= AF_PACKET
)
734 sll
= (struct sockaddr_ll
*)ifa
->ifa_addr
;
736 if (sll
->sll_ifindex
!= n
)
739 blobmsg_add_string(&blob
, "parent", ifa
->ifa_name
);
747 o2
= blobmsg_open_table(&blob
, "stats");
749 for (n
= 0; n
< ARRAY_SIZE(stats
); n
++) {
750 v
= strtoull(readstr("/sys/class/net/%s/statistics/%s",
751 name
, stats
[n
]), NULL
, 10);
753 blobmsg_add_u64(&blob
, stats
[n
], v
);
756 blobmsg_close_table(&blob
, o2
);
758 o2
= blobmsg_open_table(&blob
, "flags");
759 blobmsg_add_u8(&blob
, "up", ifa_flags
& IFF_UP
);
760 blobmsg_add_u8(&blob
, "broadcast", ifa_flags
& IFF_BROADCAST
);
761 blobmsg_add_u8(&blob
, "promisc", ifa_flags
& IFF_PROMISC
);
762 blobmsg_add_u8(&blob
, "loopback", ifa_flags
& IFF_LOOPBACK
);
763 blobmsg_add_u8(&blob
, "noarp", ifa_flags
& IFF_NOARP
);
764 blobmsg_add_u8(&blob
, "multicast", ifa_flags
& IFF_MULTICAST
);
765 blobmsg_add_u8(&blob
, "pointtopoint", ifa_flags
& IFF_POINTOPOINT
);
766 blobmsg_close_table(&blob
, o2
);
768 blobmsg_close_table(&blob
, o
);
772 rpc_luci_get_network_devices(struct ubus_context
*ctx
,
773 struct ubus_object
*obj
,
774 struct ubus_request_data
*req
,
776 struct blob_attr
*msg
)
778 struct ifaddrs
*ifaddr
;
782 blob_buf_init(&blob
, 0);
784 d
= opendir("/sys/class/net");
787 if (getifaddrs(&ifaddr
) == 1)
796 if (strcmp(e
->d_name
, ".") && strcmp(e
->d_name
, ".."))
797 rpc_luci_parse_network_device_sys(e
->d_name
, ifaddr
);
806 ubus_send_reply(ctx
, req
, blob
.head
);
812 iw_call_str(int (*method
)(const char *, char *), const char *dev
,
813 struct blob_buf
*blob
, const char *field
)
815 char buf
[IWINFO_BUFSIZE
] = {};
817 if (method(dev
, buf
) == 0)
818 blobmsg_add_string(blob
, field
, buf
);
822 iw_call_num(int (*method
)(const char *, int *), const char *dev
,
823 struct blob_buf
*blob
, const char *field
)
827 if (method(dev
, &val
) == 0)
828 blobmsg_add_u32(blob
, field
, val
);
831 static bool rpc_luci_get_iwinfo(struct blob_buf
*buf
, const char *devname
,
834 struct iwinfo_crypto_entry crypto
= {};
835 struct iwinfo_hardware_id ids
= {};
836 const struct iwinfo_ops
*iw
;
842 if (!iw_backend
|| !iw_close
|| !iw_modenames
) {
843 if (glob("/usr/lib/libiwinfo.so*", 0, NULL
, &paths
) != 0)
846 for (i
= 0; i
< paths
.gl_pathc
&& !iwlib
; i
++)
847 iwlib
= dlopen(paths
.gl_pathv
[i
], RTLD_LAZY
| RTLD_LOCAL
);
854 iw_backend
= dlsym(iwlib
, "iwinfo_backend");
855 iw_close
= dlsym(iwlib
, "iwinfo_close");
856 iw_modenames
= dlsym(iwlib
, "IWINFO_OPMODE_NAMES");
858 if (!iw_backend
|| !iw_close
|| !iw_modenames
)
862 iw
= iw_backend(devname
);
867 o
= blobmsg_open_table(buf
, "iwinfo");
869 iw_call_num(iw
->signal
, devname
, buf
, "signal");
870 iw_call_num(iw
->noise
, devname
, buf
, "noise");
871 iw_call_num(iw
->channel
, devname
, buf
, "channel");
872 iw_call_str(iw
->country
, devname
, buf
, "country");
873 iw_call_str(iw
->phyname
, devname
, buf
, "phy");
874 iw_call_num(iw
->txpower
, devname
, buf
, "txpower");
875 iw_call_num(iw
->txpower_offset
, devname
, buf
, "txpower_offset");
876 iw_call_num(iw
->frequency
, devname
, buf
, "frequency");
877 iw_call_num(iw
->frequency_offset
, devname
, buf
, "frequency_offset");
879 if (!iw
->hwmodelist(devname
, &nret
)) {
880 a
= blobmsg_open_array(buf
, "hwmodes");
882 if (nret
& IWINFO_80211_AC
)
883 blobmsg_add_string(buf
, NULL
, "ac");
885 if (nret
& IWINFO_80211_A
)
886 blobmsg_add_string(buf
, NULL
, "a");
888 if (nret
& IWINFO_80211_B
)
889 blobmsg_add_string(buf
, NULL
, "b");
891 if (nret
& IWINFO_80211_G
)
892 blobmsg_add_string(buf
, NULL
, "g");
894 if (nret
& IWINFO_80211_N
)
895 blobmsg_add_string(buf
, NULL
, "n");
897 blobmsg_close_array(buf
, a
);
900 if (!iw
->htmodelist(devname
, &nret
)) {
901 a
= blobmsg_open_array(buf
, "htmodes");
903 if (nret
& IWINFO_HTMODE_HT20
)
904 blobmsg_add_string(buf
, NULL
, "HT20");
906 if (nret
& IWINFO_HTMODE_HT40
)
907 blobmsg_add_string(buf
, NULL
, "HT40");
909 if (nret
& IWINFO_HTMODE_VHT20
)
910 blobmsg_add_string(buf
, NULL
, "VHT20");
912 if (nret
& IWINFO_HTMODE_VHT40
)
913 blobmsg_add_string(buf
, NULL
, "VHT40");
915 if (nret
& IWINFO_HTMODE_VHT80
)
916 blobmsg_add_string(buf
, NULL
, "VHT80");
918 if (nret
& IWINFO_HTMODE_VHT80_80
)
919 blobmsg_add_string(buf
, NULL
, "VHT80+80");
921 if (nret
& IWINFO_HTMODE_VHT160
)
922 blobmsg_add_string(buf
, NULL
, "VHT160");
924 blobmsg_close_array(buf
, a
);
927 if (!iw
->hardware_id(devname
, (char *)&ids
)) {
928 o2
= blobmsg_open_table(buf
, "hardware");
930 a
= blobmsg_open_array(buf
, "id");
931 blobmsg_add_u32(buf
, NULL
, ids
.vendor_id
);
932 blobmsg_add_u32(buf
, NULL
, ids
.device_id
);
933 blobmsg_add_u32(buf
, NULL
, ids
.subsystem_vendor_id
);
934 blobmsg_add_u32(buf
, NULL
, ids
.subsystem_device_id
);
935 blobmsg_close_array(buf
, a
);
937 iw_call_str(iw
->hardware_name
, devname
, buf
, "name");
939 blobmsg_close_table(buf
, o2
);
943 iw_call_num(iw
->quality
, devname
, buf
, "quality");
944 iw_call_num(iw
->quality_max
, devname
, buf
, "quality_max");
945 iw_call_num(iw
->bitrate
, devname
, buf
, "bitrate");
947 if (!iw
->mode(devname
, &nret
))
948 blobmsg_add_string(buf
, "mode", iw_modenames
[nret
]);
950 iw_call_str(iw
->ssid
, devname
, buf
, "ssid");
951 iw_call_str(iw
->bssid
, devname
, buf
, "bssid");
953 if (!iw
->encryption(devname
, (char *)&crypto
)) {
954 o2
= blobmsg_open_table(buf
, "encryption");
956 blobmsg_add_u8(buf
, "enabled", crypto
.enabled
);
958 if (crypto
.enabled
) {
959 if (!crypto
.wpa_version
) {
960 a
= blobmsg_open_array(buf
, "wep");
962 if (crypto
.auth_algs
& IWINFO_AUTH_OPEN
)
963 blobmsg_add_string(buf
, NULL
, "open");
965 if (crypto
.auth_algs
& IWINFO_AUTH_SHARED
)
966 blobmsg_add_string(buf
, NULL
, "shared");
968 blobmsg_close_array(buf
, a
);
971 a
= blobmsg_open_array(buf
, "wpa");
973 for (nret
= 1; nret
<= 3; nret
++)
974 if (crypto
.wpa_version
& (1 << (nret
- 1)))
975 blobmsg_add_u32(buf
, NULL
, nret
);
977 blobmsg_close_array(buf
, a
);
979 a
= blobmsg_open_array(buf
, "authentication");
981 if (crypto
.auth_suites
& IWINFO_KMGMT_PSK
)
982 blobmsg_add_string(buf
, NULL
, "psk");
984 if (crypto
.auth_suites
& IWINFO_KMGMT_8021x
)
985 blobmsg_add_string(buf
, NULL
, "802.1x");
987 if (crypto
.auth_suites
& IWINFO_KMGMT_SAE
)
988 blobmsg_add_string(buf
, NULL
, "sae");
990 if (crypto
.auth_suites
& IWINFO_KMGMT_OWE
)
991 blobmsg_add_string(buf
, NULL
, "owe");
993 if (!crypto
.auth_suites
||
994 (crypto
.auth_suites
& IWINFO_KMGMT_NONE
))
995 blobmsg_add_string(buf
, NULL
, "none");
997 blobmsg_close_array(buf
, a
);
1000 a
= blobmsg_open_array(buf
, "ciphers");
1001 nret
= crypto
.pair_ciphers
| crypto
.group_ciphers
;
1003 if (nret
& IWINFO_CIPHER_WEP40
)
1004 blobmsg_add_string(buf
, NULL
, "wep-40");
1006 if (nret
& IWINFO_CIPHER_WEP104
)
1007 blobmsg_add_string(buf
, NULL
, "wep-104");
1009 if (nret
& IWINFO_CIPHER_TKIP
)
1010 blobmsg_add_string(buf
, NULL
, "tkip");
1012 if (nret
& IWINFO_CIPHER_CCMP
)
1013 blobmsg_add_string(buf
, NULL
, "ccmp");
1015 if (nret
& IWINFO_CIPHER_WRAP
)
1016 blobmsg_add_string(buf
, NULL
, "wrap");
1018 if (nret
& IWINFO_CIPHER_AESOCB
)
1019 blobmsg_add_string(buf
, NULL
, "aes-ocb");
1021 if (nret
& IWINFO_CIPHER_CKIP
)
1022 blobmsg_add_string(buf
, NULL
, "ckip");
1024 if (!nret
|| (nret
& IWINFO_CIPHER_NONE
))
1025 blobmsg_add_string(buf
, NULL
, "none");
1027 blobmsg_close_array(buf
, a
);
1030 blobmsg_close_table(buf
, o2
);
1034 blobmsg_close_table(buf
, o
);
1041 static void rpc_luci_get_wireless_devices_cb(struct ubus_request
*req
,
1042 int type
, struct blob_attr
*msg
)
1044 struct blob_attr
*wifi
, *cur
, *iface
, *cur2
;
1045 struct reply_context
*rctx
= req
->priv
;
1046 const char *name
, *first_ifname
;
1047 int rem
, rem2
, rem3
, rem4
;
1050 blob_for_each_attr(wifi
, msg
, rem
) {
1051 if (blobmsg_type(wifi
) != BLOBMSG_TYPE_TABLE
||
1052 blobmsg_name(wifi
) == NULL
)
1055 o
= blobmsg_open_table(&rctx
->blob
, blobmsg_name(wifi
));
1057 rem2
= blobmsg_data_len(wifi
);
1058 first_ifname
= NULL
;
1060 __blob_for_each_attr(cur
, blobmsg_data(wifi
), rem2
) {
1061 name
= blobmsg_name(cur
);
1063 if (!name
|| !strcmp(name
, "iwinfo")) {
1066 else if (!strcmp(name
, "interfaces")) {
1067 if (blobmsg_type(cur
) != BLOBMSG_TYPE_ARRAY
)
1070 a
= blobmsg_open_array(&rctx
->blob
, "interfaces");
1072 rem3
= blobmsg_data_len(cur
);
1074 __blob_for_each_attr(iface
, blobmsg_data(cur
), rem3
) {
1075 if (blobmsg_type(iface
) != BLOBMSG_TYPE_TABLE
)
1078 o2
= blobmsg_open_table(&rctx
->blob
, NULL
);
1080 rem4
= blobmsg_data_len(iface
);
1083 __blob_for_each_attr(cur2
, blobmsg_data(iface
), rem4
) {
1084 if (!strcmp(blobmsg_name(cur2
), "ifname"))
1085 name
= blobmsg_get_string(cur2
);
1086 else if (!strcmp(blobmsg_name(cur2
), "iwinfo"))
1089 blobmsg_add_blob(&rctx
->blob
, cur2
);
1093 if (rpc_luci_get_iwinfo(&rctx
->blob
, name
, false))
1094 first_ifname
= first_ifname
? first_ifname
: name
;
1096 blobmsg_close_table(&rctx
->blob
, o2
);
1099 blobmsg_close_array(&rctx
->blob
, a
);
1102 blobmsg_add_blob(&rctx
->blob
, cur
);
1106 rpc_luci_get_iwinfo(&rctx
->blob
,
1107 first_ifname
? first_ifname
: blobmsg_name(wifi
),
1110 blobmsg_close_table(&rctx
->blob
, o
);
1113 finish_request(rctx
, UBUS_STATUS_OK
);
1117 rpc_luci_get_wireless_devices(struct ubus_context
*ctx
,
1118 struct ubus_object
*obj
,
1119 struct ubus_request_data
*req
,
1121 struct blob_attr
*msg
)
1123 struct reply_context
*rctx
= defer_request(ctx
, req
);
1126 return UBUS_STATUS_UNKNOWN_ERROR
;
1128 if (!invoke_ubus(ctx
, "network.wireless", "status", NULL
,
1129 rpc_luci_get_wireless_devices_cb
, rctx
))
1130 return finish_request(rctx
, UBUS_STATUS_NOT_FOUND
);
1132 return UBUS_STATUS_OK
;
1136 struct avl_node avl
;
1138 struct avl_tree ipaddrs
;
1139 struct avl_tree ip6addrs
;
1142 /* used to ignore priority with avl_find_element */
1143 #define HOST_HINT_PRIO_IGNORE -1
1145 /* higher (larger) priority addresses are listed first */
1146 #define HOST_HINT_PRIO_NL 10 /* neighbor table */
1147 #define HOST_HINT_PRIO_ETHER 50 /* /etc/ethers */
1148 #define HOST_HINT_PRIO_LEASEFILE 100 /* dhcp leasefile */
1149 #define HOST_HINT_PRIO_RRDNS 100 /* rrdns */
1150 #define HOST_HINT_PRIO_IFADDRS 200 /* getifaddrs() */
1151 #define HOST_HINT_PRIO_STATIC_LEASE 200 /* uci static leases */
1153 struct host_hint_addr
{
1154 struct avl_node avl
;
1159 struct in6_addr in6
;
1164 host_hint_addr_avl_cmp(const void *k1
, const void *k2
, void *ptr
)
1166 struct host_hint_addr
*a1
= (struct host_hint_addr
*)k1
;
1167 struct host_hint_addr
*a2
= (struct host_hint_addr
*)k2
;
1169 if (a1
->prio
!= a2
->prio
&&
1170 a1
->prio
!= HOST_HINT_PRIO_IGNORE
&&
1171 a2
->prio
!= HOST_HINT_PRIO_IGNORE
)
1172 return a1
->prio
< a2
->prio
? 1 : -1;
1174 if (a1
->af
!= a2
->af
)
1175 return a1
->af
< a2
->af
? -1 : 1;
1177 return memcmp(&a1
->addr
, &a2
->addr
, sizeof(a1
->addr
));
1181 nl_cb_done(struct nl_msg
*msg
, void *arg
)
1183 struct reply_context
*rctx
= arg
;
1189 nl_cb_error(struct sockaddr_nl
*nla
, struct nlmsgerr
*err
, void *arg
)
1191 struct reply_context
*rctx
= arg
;
1196 static struct host_hint
*
1197 rpc_luci_get_host_hint(struct reply_context
*rctx
, struct ether_addr
*ea
)
1199 struct host_hint
*hint
;
1206 hint
= avl_find_element(&rctx
->avl
, mac
, hint
, avl
);
1209 hint
= calloc_a(sizeof(*hint
), &p
, strlen(mac
) + 1);
1214 hint
->avl
.key
= strcpy(p
, mac
);
1215 avl_init(&hint
->ipaddrs
, host_hint_addr_avl_cmp
, false, NULL
);
1216 avl_init(&hint
->ip6addrs
, host_hint_addr_avl_cmp
, false, NULL
);
1217 avl_insert(&rctx
->avl
, &hint
->avl
);
1224 rpc_luci_add_host_hint_addr(struct host_hint
*hint
, int af
, int prio
, void *addr
)
1226 struct host_hint_addr e
, *a
;
1227 struct avl_tree
*addrs
= af
== AF_INET
? &hint
->ipaddrs
: &hint
->ip6addrs
;
1232 memset(&e
, 0, sizeof(e
));
1234 /* ignore prio when comparing against existing addresses */
1235 e
.prio
= HOST_HINT_PRIO_IGNORE
;
1238 memcpy(&e
.addr
.in
, (struct in_addr
*)addr
, sizeof(e
.addr
.in
));
1240 memcpy(&e
.addr
.in6
, (struct in6_addr
*)addr
, sizeof(e
.addr
.in6
));
1242 a
= avl_find_element(addrs
, &e
, a
, avl
);
1245 /* update prio of existing address if higher */
1246 if (prio
<= a
->prio
)
1249 avl_delete(addrs
, &a
->avl
);
1251 avl_insert(addrs
, &a
->avl
);
1255 a
= calloc(1, sizeof(*a
));
1260 memcpy(a
, &e
, sizeof(*a
));
1263 avl_insert(addrs
, &a
->avl
);
1267 rpc_luci_add_host_hint_ipaddr(struct host_hint
*hint
, int prio
, struct in_addr
*addr
)
1269 return rpc_luci_add_host_hint_addr(hint
, AF_INET
, prio
, (void *)addr
);
1273 rpc_luci_add_host_hint_ip6addr(struct host_hint
*hint
, int prio
, struct in6_addr
*addr
)
1275 return rpc_luci_add_host_hint_addr(hint
, AF_INET6
, prio
, (void *)addr
);
1278 static int nl_cb_dump_neigh(struct nl_msg
*msg
, void *arg
)
1280 struct reply_context
*rctx
= arg
;
1281 struct ether_addr
*mac
;
1282 struct in6_addr
*dst
;
1283 struct nlmsghdr
*hdr
= nlmsg_hdr(msg
);
1284 struct ndmsg
*nd
= NLMSG_DATA(hdr
);
1285 struct nlattr
*tb
[NDA_MAX
+1];
1286 struct host_hint
*hint
;
1288 rctx
->pending
= !!(hdr
->nlmsg_flags
& NLM_F_MULTI
);
1290 if (hdr
->nlmsg_type
!= RTM_NEWNEIGH
||
1291 (nd
->ndm_family
!= AF_INET
&& nd
->ndm_family
!= AF_INET6
))
1294 if (!(nd
->ndm_state
& (0xFF & ~NUD_NOARP
)))
1297 nlmsg_parse(hdr
, sizeof(*nd
), tb
, NDA_MAX
, NULL
);
1299 mac
= tb
[NDA_LLADDR
] ? RTA_DATA(tb
[NDA_LLADDR
]) : NULL
;
1300 dst
= tb
[NDA_DST
] ? RTA_DATA(tb
[NDA_DST
]) : NULL
;
1305 hint
= rpc_luci_get_host_hint(rctx
, mac
);
1310 if (nd
->ndm_family
== AF_INET
)
1311 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_NL
, (struct in_addr
*)dst
);
1313 rpc_luci_add_host_hint_ip6addr(hint
, HOST_HINT_PRIO_NL
, (struct in6_addr
*)dst
);
1319 rpc_luci_get_host_hints_nl(struct reply_context
*rctx
)
1321 struct nl_sock
*sock
= NULL
;
1322 struct nl_msg
*msg
= NULL
;
1323 struct nl_cb
*cb
= NULL
;
1324 struct ndmsg ndm
= {};
1326 sock
= nl_socket_alloc();
1331 if (nl_connect(sock
, NETLINK_ROUTE
))
1334 cb
= nl_cb_alloc(NL_CB_DEFAULT
);
1339 msg
= nlmsg_alloc_simple(RTM_GETNEIGH
, NLM_F_REQUEST
| NLM_F_DUMP
);
1344 nlmsg_append(msg
, &ndm
, sizeof(ndm
), 0);
1346 nl_cb_set(cb
, NL_CB_VALID
, NL_CB_CUSTOM
, nl_cb_dump_neigh
, rctx
);
1347 nl_cb_set(cb
, NL_CB_FINISH
, NL_CB_CUSTOM
, nl_cb_done
, rctx
);
1348 nl_cb_err(cb
, NL_CB_CUSTOM
, nl_cb_error
, rctx
);
1350 avl_init(&rctx
->avl
, avl_strcmp
, false, NULL
);
1354 nl_send_auto_complete(sock
, msg
);
1356 while (rctx
->pending
)
1357 nl_recvmsgs(sock
, cb
);
1361 nl_socket_free(sock
);
1371 rpc_luci_get_host_hints_ether(struct reply_context
*rctx
)
1373 struct host_hint
*hint
;
1378 f
= fopen("/etc/ethers", "r");
1383 while (fgets(buf
, sizeof(buf
), f
)) {
1384 p
= strtok(buf
, " \t\n");
1385 hint
= rpc_luci_get_host_hint(rctx
, p
? ether_aton(p
) : NULL
);
1390 p
= strtok(NULL
, " \t\n");
1395 if (inet_pton(AF_INET
, p
, &in
) == 1) {
1396 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_ETHER
, &in
);
1398 else if (*p
&& !hint
->hostname
) {
1399 hint
->hostname
= strdup(p
);
1407 rpc_luci_get_host_hints_uci(struct reply_context
*rctx
)
1409 struct uci_ptr ptr
= { .package
= "dhcp" };
1410 struct uci_context
*uci
= NULL
;
1411 struct uci_package
*pkg
= NULL
;
1412 struct lease_entry
*lease
;
1413 struct host_hint
*hint
;
1414 struct uci_element
*e
, *l
;
1415 struct uci_section
*s
;
1420 uci
= uci_alloc_context();
1425 uci_load(uci
, ptr
.package
, &pkg
);
1430 uci_foreach_element(&pkg
->sections
, e
)
1432 s
= uci_to_section(e
);
1434 if (strcmp(s
->type
, "host"))
1437 ptr
.section
= s
->e
.name
;
1443 if (!uci_lookup_ptr(uci
, &ptr
, NULL
, true) && ptr
.o
!= NULL
&&
1444 ptr
.o
->type
!= UCI_TYPE_STRING
)
1445 n
= ptr
.o
->v
.string
;
1449 if (!n
|| inet_pton(AF_INET
, n
, &in
) != 1)
1452 ptr
.option
= "name";
1455 if (!uci_lookup_ptr(uci
, &ptr
, NULL
, true) && ptr
.o
!= NULL
&&
1456 ptr
.o
->type
== UCI_TYPE_STRING
)
1457 n
= ptr
.o
->v
.string
;
1464 if (uci_lookup_ptr(uci
, &ptr
, NULL
, true) || ptr
.o
== NULL
)
1467 if (ptr
.o
->type
== UCI_TYPE_STRING
) {
1468 for (p
= strtok(ptr
.o
->v
.string
, " \t");
1470 p
= strtok(NULL
, " \t")) {
1471 hint
= rpc_luci_get_host_hint(rctx
, ether_aton(p
));
1477 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_STATIC_LEASE
, &in
);
1479 if (n
&& !hint
->hostname
)
1480 hint
->hostname
= strdup(n
);
1483 else if (ptr
.o
->type
== UCI_TYPE_LIST
) {
1484 uci_foreach_element(&ptr
.o
->v
.list
, l
) {
1485 hint
= rpc_luci_get_host_hint(rctx
, ether_aton(l
->name
));
1491 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_STATIC_LEASE
, &in
);
1493 if (n
&& !hint
->hostname
)
1494 hint
->hostname
= strdup(n
);
1501 while ((lease
= lease_next()) != NULL
) {
1502 hint
= rpc_luci_get_host_hint(rctx
, &lease
->mac
);
1507 for (i
= 0; i
< lease
->n_addr
; i
++) {
1508 if (lease
->af
== AF_INET
)
1509 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_LEASEFILE
, &lease
->addr
[i
].in
);
1510 else if (lease
->af
== AF_INET6
)
1511 rpc_luci_add_host_hint_ip6addr(hint
, HOST_HINT_PRIO_LEASEFILE
, &lease
->addr
[i
].in6
);
1514 if (lease
->hostname
&& !hint
->hostname
)
1515 hint
->hostname
= strdup(lease
->hostname
);
1522 uci_free_context(uci
);
1526 rpc_luci_get_host_hints_ifaddrs(struct reply_context
*rctx
)
1528 struct ether_addr empty_ea
= {};
1529 struct in6_addr empty_in6
= {};
1530 struct ifaddrs
*ifaddr
, *ifa
;
1531 struct sockaddr_ll
*sll
;
1532 struct avl_tree devices
;
1533 struct host_hint
*hint
;
1535 struct avl_node avl
;
1536 struct ether_addr ea
;
1537 struct in6_addr in6
;
1539 } *device
, *nextdevice
;
1542 avl_init(&devices
, avl_strcmp
, false, NULL
);
1544 if (getifaddrs(&ifaddr
) == -1)
1547 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
1551 device
= avl_find_element(&devices
, ifa
->ifa_name
, device
, avl
);
1554 device
= calloc_a(sizeof(*device
), &p
, strlen(ifa
->ifa_name
) + 1);
1559 device
->avl
.key
= strcpy(p
, ifa
->ifa_name
);
1560 avl_insert(&devices
, &device
->avl
);
1563 switch (ifa
->ifa_addr
->sa_family
) {
1565 sll
= (struct sockaddr_ll
*)ifa
->ifa_addr
;
1567 if (sll
->sll_halen
== 6)
1568 memcpy(&device
->ea
, sll
->sll_addr
, 6);
1573 device
->in6
= ((struct sockaddr_in6
*)ifa
->ifa_addr
)->sin6_addr
;
1577 device
->in
= ((struct sockaddr_in
*)ifa
->ifa_addr
)->sin_addr
;
1582 freeifaddrs(ifaddr
);
1584 avl_remove_all_elements(&devices
, device
, avl
, nextdevice
) {
1585 if (memcmp(&device
->ea
, &empty_ea
, sizeof(empty_ea
)) &&
1586 (memcmp(&device
->in6
, &empty_in6
, sizeof(empty_in6
)) ||
1587 device
->in
.s_addr
!= 0)) {
1588 hint
= rpc_luci_get_host_hint(rctx
, &device
->ea
);
1591 if (device
->in
.s_addr
!= 0)
1592 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_IFADDRS
, &device
->in
);
1594 if (memcmp(&device
->in6
, &empty_in6
, sizeof(empty_in6
)) != 0)
1595 rpc_luci_add_host_hint_ip6addr(hint
, HOST_HINT_PRIO_IFADDRS
, &device
->in6
);
1604 rpc_luci_get_host_hints_finish(struct reply_context
*rctx
);
1607 rpc_luci_get_host_hints_rrdns_cb(struct ubus_request
*req
, int type
,
1608 struct blob_attr
*msg
)
1610 struct reply_context
*rctx
= req
->priv
;
1611 struct host_hint
*hint
;
1612 struct blob_attr
*cur
;
1613 struct in6_addr in6
;
1618 blob_for_each_attr(cur
, msg
, rem
) {
1619 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
)
1622 if (inet_pton(AF_INET6
, blobmsg_name(cur
), &in6
) == 1) {
1623 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1624 rpc_luci_add_host_hint_ip6addr(hint
, HOST_HINT_PRIO_RRDNS
, &in6
);
1625 if (!avl_is_empty(&hint
->ip6addrs
)) {
1627 free(hint
->hostname
);
1629 hint
->hostname
= strdup(blobmsg_get_string(cur
));
1634 else if (inet_pton(AF_INET
, blobmsg_name(cur
), &in
) == 1) {
1635 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1636 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_RRDNS
, &in
);
1637 if (!avl_is_empty(&hint
->ipaddrs
)) {
1639 free(hint
->hostname
);
1641 hint
->hostname
= strdup(blobmsg_get_string(cur
));
1649 rpc_luci_get_host_hints_finish(rctx
);
1653 rpc_luci_get_host_hints_rrdns(struct reply_context
*rctx
)
1655 struct in6_addr empty_in6
= {};
1656 char buf
[INET6_ADDRSTRLEN
];
1657 struct blob_buf req
= {};
1658 struct host_hint
*hint
;
1659 struct host_hint_addr
*addr
;
1663 blob_buf_init(&req
, 0);
1665 a
= blobmsg_open_array(&req
, "addrs");
1667 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1668 avl_for_each_element(&hint
->ipaddrs
, addr
, avl
) {
1669 if (addr
->addr
.in
.s_addr
!= 0) {
1670 inet_ntop(AF_INET
, &addr
->addr
.in
, buf
, sizeof(buf
));
1671 blobmsg_add_string(&req
, NULL
, buf
);
1675 avl_for_each_element(&hint
->ip6addrs
, addr
, avl
) {
1676 if (memcmp(&addr
->addr
.in6
, &empty_in6
, sizeof(empty_in6
))) {
1677 inet_ntop(AF_INET6
, &addr
->addr
.in6
, buf
, sizeof(buf
));
1678 blobmsg_add_string(&req
, NULL
, buf
);
1684 blobmsg_close_array(&req
, a
);
1687 blobmsg_add_u32(&req
, "timeout", 250);
1688 blobmsg_add_u32(&req
, "limit", n
);
1690 if (!invoke_ubus(rctx
->context
, "network.rrdns", "lookup", &req
,
1691 rpc_luci_get_host_hints_rrdns_cb
, rctx
))
1692 rpc_luci_get_host_hints_finish(rctx
);
1695 rpc_luci_get_host_hints_finish(rctx
);
1698 blob_buf_free(&req
);
1702 rpc_luci_get_host_hints_finish(struct reply_context
*rctx
)
1704 struct host_hint
*hint
, *nexthint
;
1705 struct host_hint_addr
*addr
, *nextaddr
;
1706 char buf
[INET6_ADDRSTRLEN
];
1707 struct in6_addr in6
= {};
1710 avl_remove_all_elements(&rctx
->avl
, hint
, avl
, nexthint
) {
1711 o
= blobmsg_open_table(&rctx
->blob
, hint
->avl
.key
);
1713 a
= blobmsg_open_array(&rctx
->blob
, "ipaddrs");
1715 avl_remove_all_elements(&hint
->ipaddrs
, addr
, avl
, nextaddr
) {
1716 if (addr
->addr
.in
.s_addr
!= 0) {
1717 inet_ntop(AF_INET
, &addr
->addr
.in
, buf
, sizeof(buf
));
1718 blobmsg_add_string(&rctx
->blob
, NULL
, buf
);
1724 blobmsg_close_array(&rctx
->blob
, a
);
1726 a
= blobmsg_open_array(&rctx
->blob
, "ip6addrs");
1728 avl_remove_all_elements(&hint
->ip6addrs
, addr
, avl
, nextaddr
) {
1729 if (memcmp(&addr
->addr
.in6
, &in6
, sizeof(in6
))) {
1730 inet_ntop(AF_INET6
, &addr
->addr
.in6
, buf
, sizeof(buf
));
1731 blobmsg_add_string(&rctx
->blob
, NULL
, buf
);
1737 blobmsg_close_array(&rctx
->blob
, a
);
1740 blobmsg_add_string(&rctx
->blob
, "name", hint
->hostname
);
1742 blobmsg_close_table(&rctx
->blob
, o
);
1745 free(hint
->hostname
);
1750 return finish_request(rctx
, UBUS_STATUS_OK
);
1754 rpc_luci_get_host_hints(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1755 struct ubus_request_data
*req
, const char *method
,
1756 struct blob_attr
*msg
)
1758 struct reply_context
*rctx
= defer_request(ctx
, req
);
1761 return UBUS_STATUS_UNKNOWN_ERROR
;
1763 rpc_luci_get_host_hints_nl(rctx
);
1764 rpc_luci_get_host_hints_uci(rctx
);
1765 rpc_luci_get_host_hints_ether(rctx
);
1766 rpc_luci_get_host_hints_ifaddrs(rctx
);
1767 rpc_luci_get_host_hints_rrdns(rctx
);
1769 return UBUS_STATUS_OK
;
1773 rpc_luci_get_duid_hints(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1774 struct ubus_request_data
*req
, const char *method
,
1775 struct blob_attr
*msg
)
1777 struct { struct avl_node avl
; } *e
, *next
;
1778 char s
[INET6_ADDRSTRLEN
], *p
;
1779 struct ether_addr empty
= {};
1780 struct lease_entry
*lease
;
1781 struct avl_tree avl
;
1785 avl_init(&avl
, avl_strcmp
, false, NULL
);
1786 blob_buf_init(&blob
, 0);
1790 while ((lease
= lease_next()) != NULL
) {
1791 if (lease
->af
!= AF_INET6
|| lease
->duid
== NULL
)
1794 e
= avl_find_element(&avl
, lease
->duid
, e
, avl
);
1799 e
= calloc_a(sizeof(*e
), &p
, strlen(lease
->duid
) + 1);
1804 o
= blobmsg_open_table(&blob
, lease
->duid
);
1806 inet_ntop(AF_INET6
, &lease
->addr
[0].in6
, s
, sizeof(s
));
1807 blobmsg_add_string(&blob
, "ip6addr", s
);
1809 a
= blobmsg_open_array(&blob
, "ip6addrs");
1811 for (n
= 0; n
< lease
->n_addr
; n
++) {
1812 inet_ntop(AF_INET6
, &lease
->addr
[n
].in6
, s
, sizeof(s
));
1813 blobmsg_add_string(&blob
, NULL
, s
);
1816 blobmsg_close_array(&blob
, a
);
1818 if (lease
->hostname
)
1819 blobmsg_add_string(&blob
, "hostname", lease
->hostname
);
1821 if (memcmp(&lease
->mac
, &empty
, sizeof(empty
)))
1822 blobmsg_add_string(&blob
, "macaddr", ea2str(&lease
->mac
));
1824 blobmsg_close_table(&blob
, o
);
1826 e
->avl
.key
= strcpy(p
, lease
->duid
);
1827 avl_insert(&avl
, &e
->avl
);
1832 avl_remove_all_elements(&avl
, e
, avl
, next
) {
1836 ubus_send_reply(ctx
, req
, blob
.head
);
1838 return UBUS_STATUS_OK
;
1842 rpc_luci_get_board_json(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1843 struct ubus_request_data
*req
, const char *method
,
1844 struct blob_attr
*msg
)
1846 blob_buf_init(&blob
, 0);
1848 if (!blobmsg_add_json_from_file(&blob
, "/etc/board.json"))
1849 return UBUS_STATUS_UNKNOWN_ERROR
;
1851 ubus_send_reply(ctx
, req
, blob
.head
);
1852 return UBUS_STATUS_OK
;
1860 static const struct blobmsg_policy rpc_get_leases_policy
[__RPC_L_MAX
] = {
1861 [RPC_L_FAMILY
] = { .name
= "family", .type
= BLOBMSG_TYPE_INT32
}
1865 rpc_luci_get_dhcp_leases(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1866 struct ubus_request_data
*req
, const char *method
,
1867 struct blob_attr
*msg
)
1869 struct blob_attr
*tb
[__RPC_L_MAX
];
1870 struct ether_addr emptymac
= {};
1871 struct lease_entry
*lease
;
1872 char s
[INET6_ADDRSTRLEN
];
1877 blobmsg_parse(rpc_get_leases_policy
, __RPC_L_MAX
, tb
,
1878 blob_data(msg
), blob_len(msg
));
1880 switch (tb
[RPC_L_FAMILY
] ? blobmsg_get_u32(tb
[RPC_L_FAMILY
]) : 0) {
1894 return UBUS_STATUS_INVALID_ARGUMENT
;
1897 blob_buf_init(&blob
, 0);
1899 for (af
= family
? family
: AF_INET
;
1901 af
= (family
== 0) ? (af
== AF_INET
? AF_INET6
: 0) : 0) {
1903 a
= blobmsg_open_array(&blob
, (af
== AF_INET
) ? "dhcp_leases"
1908 while ((lease
= lease_next()) != NULL
) {
1909 if (lease
->af
!= af
)
1912 o
= blobmsg_open_table(&blob
, NULL
);
1914 if (lease
->expire
== -1)
1915 blobmsg_add_u8(&blob
, "expires", 0);
1917 blobmsg_add_u32(&blob
, "expires", lease
->expire
);
1919 if (lease
->hostname
)
1920 blobmsg_add_string(&blob
, "hostname", lease
->hostname
);
1922 if (memcmp(&lease
->mac
, &emptymac
, sizeof(emptymac
)))
1923 blobmsg_add_string(&blob
, "macaddr", ea2str(&lease
->mac
));
1926 blobmsg_add_string(&blob
, "duid", lease
->duid
);
1928 inet_ntop(lease
->af
, &lease
->addr
[0].in6
, s
, sizeof(s
));
1929 blobmsg_add_string(&blob
, (af
== AF_INET
) ? "ipaddr" : "ip6addr",
1932 if (af
== AF_INET6
) {
1933 a2
= blobmsg_open_array(&blob
, "ip6addrs");
1935 for (n
= 0; n
< lease
->n_addr
; n
++) {
1936 inet_ntop(lease
->af
, &lease
->addr
[n
].in6
, s
, sizeof(s
));
1937 blobmsg_add_string(&blob
, NULL
, s
);
1940 blobmsg_close_array(&blob
, a2
);
1943 blobmsg_close_table(&blob
, o
);
1948 blobmsg_close_array(&blob
, a
);
1951 ubus_send_reply(ctx
, req
, blob
.head
);
1953 return UBUS_STATUS_OK
;
1957 rpc_luci_api_init(const struct rpc_daemon_ops
*o
, struct ubus_context
*ctx
)
1959 static const struct ubus_method luci_methods
[] = {
1960 UBUS_METHOD_NOARG("getNetworkDevices", rpc_luci_get_network_devices
),
1961 UBUS_METHOD_NOARG("getWirelessDevices", rpc_luci_get_wireless_devices
),
1962 UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints
),
1963 UBUS_METHOD_NOARG("getDUIDHints", rpc_luci_get_duid_hints
),
1964 UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json
),
1965 UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases
, rpc_get_leases_policy
)
1968 static struct ubus_object_type luci_type
=
1969 UBUS_OBJECT_TYPE("rpcd-luci", luci_methods
);
1971 static struct ubus_object obj
= {
1974 .methods
= luci_methods
,
1975 .n_methods
= ARRAY_SIZE(luci_methods
),
1978 return ubus_add_object(ctx
, &obj
);
1981 struct rpc_plugin rpc_plugin
= {
1982 .init
= rpc_luci_api_init