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 o2
= blobmsg_open_table(&blob
, "link");
770 p
= readstr("/sys/class/net/%s/speed", name
);
772 blobmsg_add_u32(&blob
, "speed", atoi(p
));
774 p
= readstr("/sys/class/net/%s/duplex", name
);
776 blobmsg_add_string(&blob
, "duplex", p
);
778 n
= atoi(readstr("/sys/class/net/%s/carrier", name
));
779 blobmsg_add_u8(&blob
, "carrier", n
== 1);
781 n
= atoi(readstr("/sys/class/net/%s/carrier_changes", name
));
782 blobmsg_add_u32(&blob
, "changes", n
);
784 n
= atoi(readstr("/sys/class/net/%s/carrier_up_count", name
));
785 blobmsg_add_u32(&blob
, "up_count", n
);
787 n
= atoi(readstr("/sys/class/net/%s/carrier_down_count", name
));
788 blobmsg_add_u32(&blob
, "down_count", n
);
790 blobmsg_close_table(&blob
, o2
);
792 blobmsg_close_table(&blob
, o
);
796 rpc_luci_get_network_devices(struct ubus_context
*ctx
,
797 struct ubus_object
*obj
,
798 struct ubus_request_data
*req
,
800 struct blob_attr
*msg
)
802 struct ifaddrs
*ifaddr
;
806 blob_buf_init(&blob
, 0);
808 d
= opendir("/sys/class/net");
811 if (getifaddrs(&ifaddr
) == 1)
820 if (strcmp(e
->d_name
, ".") && strcmp(e
->d_name
, ".."))
821 rpc_luci_parse_network_device_sys(e
->d_name
, ifaddr
);
830 ubus_send_reply(ctx
, req
, blob
.head
);
836 iw_call_str(int (*method
)(const char *, char *), const char *dev
,
837 struct blob_buf
*blob
, const char *field
)
839 char buf
[IWINFO_BUFSIZE
] = {};
841 if (method(dev
, buf
) == 0)
842 blobmsg_add_string(blob
, field
, buf
);
846 iw_call_num(int (*method
)(const char *, int *), const char *dev
,
847 struct blob_buf
*blob
, const char *field
)
851 if (method(dev
, &val
) == 0)
852 blobmsg_add_u32(blob
, field
, val
);
855 static bool rpc_luci_get_iwinfo(struct blob_buf
*buf
, const char *devname
,
858 struct iwinfo_crypto_entry crypto
= {};
859 struct iwinfo_hardware_id ids
= {};
860 const struct iwinfo_ops
*iw
;
866 if (!iw_backend
|| !iw_close
|| !iw_modenames
) {
867 if (glob("/usr/lib/libiwinfo.so*", 0, NULL
, &paths
) != 0)
870 for (i
= 0; i
< paths
.gl_pathc
&& !iwlib
; i
++)
871 iwlib
= dlopen(paths
.gl_pathv
[i
], RTLD_LAZY
| RTLD_LOCAL
);
878 iw_backend
= dlsym(iwlib
, "iwinfo_backend");
879 iw_close
= dlsym(iwlib
, "iwinfo_close");
880 iw_modenames
= dlsym(iwlib
, "IWINFO_OPMODE_NAMES");
882 if (!iw_backend
|| !iw_close
|| !iw_modenames
)
886 iw
= iw_backend(devname
);
891 o
= blobmsg_open_table(buf
, "iwinfo");
893 iw_call_num(iw
->signal
, devname
, buf
, "signal");
894 iw_call_num(iw
->noise
, devname
, buf
, "noise");
895 iw_call_num(iw
->channel
, devname
, buf
, "channel");
896 iw_call_str(iw
->country
, devname
, buf
, "country");
897 iw_call_str(iw
->phyname
, devname
, buf
, "phy");
898 iw_call_num(iw
->txpower
, devname
, buf
, "txpower");
899 iw_call_num(iw
->txpower_offset
, devname
, buf
, "txpower_offset");
900 iw_call_num(iw
->frequency
, devname
, buf
, "frequency");
901 iw_call_num(iw
->frequency_offset
, devname
, buf
, "frequency_offset");
903 if (!iw
->hwmodelist(devname
, &nret
)) {
904 a
= blobmsg_open_array(buf
, "hwmodes");
906 if (nret
& IWINFO_80211_AX
)
907 blobmsg_add_string(buf
, NULL
, "ax");
909 if (nret
& IWINFO_80211_AC
)
910 blobmsg_add_string(buf
, NULL
, "ac");
912 if (nret
& IWINFO_80211_A
)
913 blobmsg_add_string(buf
, NULL
, "a");
915 if (nret
& IWINFO_80211_B
)
916 blobmsg_add_string(buf
, NULL
, "b");
918 if (nret
& IWINFO_80211_G
)
919 blobmsg_add_string(buf
, NULL
, "g");
921 if (nret
& IWINFO_80211_N
)
922 blobmsg_add_string(buf
, NULL
, "n");
924 blobmsg_close_array(buf
, a
);
927 if (!iw
->htmodelist(devname
, &nret
)) {
928 a
= blobmsg_open_array(buf
, "htmodes");
930 if (nret
& IWINFO_HTMODE_HT20
)
931 blobmsg_add_string(buf
, NULL
, "HT20");
933 if (nret
& IWINFO_HTMODE_HT40
)
934 blobmsg_add_string(buf
, NULL
, "HT40");
936 if (nret
& IWINFO_HTMODE_VHT20
)
937 blobmsg_add_string(buf
, NULL
, "VHT20");
939 if (nret
& IWINFO_HTMODE_VHT40
)
940 blobmsg_add_string(buf
, NULL
, "VHT40");
942 if (nret
& IWINFO_HTMODE_VHT80
)
943 blobmsg_add_string(buf
, NULL
, "VHT80");
945 if (nret
& IWINFO_HTMODE_VHT80_80
)
946 blobmsg_add_string(buf
, NULL
, "VHT80+80");
948 if (nret
& IWINFO_HTMODE_VHT160
)
949 blobmsg_add_string(buf
, NULL
, "VHT160");
951 if (nret
& IWINFO_HTMODE_HE20
)
952 blobmsg_add_string(buf
, NULL
, "HE20");
954 if (nret
& IWINFO_HTMODE_HE40
)
955 blobmsg_add_string(buf
, NULL
, "HE40");
957 if (nret
& IWINFO_HTMODE_HE80
)
958 blobmsg_add_string(buf
, NULL
, "HE80");
960 if (nret
& IWINFO_HTMODE_HE160
)
961 blobmsg_add_string(buf
, NULL
, "HE160");
963 blobmsg_close_array(buf
, a
);
966 if (!iw
->hardware_id(devname
, (char *)&ids
)) {
967 o2
= blobmsg_open_table(buf
, "hardware");
969 a
= blobmsg_open_array(buf
, "id");
970 blobmsg_add_u32(buf
, NULL
, ids
.vendor_id
);
971 blobmsg_add_u32(buf
, NULL
, ids
.device_id
);
972 blobmsg_add_u32(buf
, NULL
, ids
.subsystem_vendor_id
);
973 blobmsg_add_u32(buf
, NULL
, ids
.subsystem_device_id
);
974 blobmsg_close_array(buf
, a
);
976 iw_call_str(iw
->hardware_name
, devname
, buf
, "name");
978 blobmsg_close_table(buf
, o2
);
982 iw_call_num(iw
->quality
, devname
, buf
, "quality");
983 iw_call_num(iw
->quality_max
, devname
, buf
, "quality_max");
984 iw_call_num(iw
->bitrate
, devname
, buf
, "bitrate");
986 if (!iw
->mode(devname
, &nret
))
987 blobmsg_add_string(buf
, "mode", iw_modenames
[nret
]);
989 iw_call_str(iw
->ssid
, devname
, buf
, "ssid");
990 iw_call_str(iw
->bssid
, devname
, buf
, "bssid");
992 if (!iw
->encryption(devname
, (char *)&crypto
)) {
993 o2
= blobmsg_open_table(buf
, "encryption");
995 blobmsg_add_u8(buf
, "enabled", crypto
.enabled
);
997 if (crypto
.enabled
) {
998 if (!crypto
.wpa_version
) {
999 a
= blobmsg_open_array(buf
, "wep");
1001 if (crypto
.auth_algs
& IWINFO_AUTH_OPEN
)
1002 blobmsg_add_string(buf
, NULL
, "open");
1004 if (crypto
.auth_algs
& IWINFO_AUTH_SHARED
)
1005 blobmsg_add_string(buf
, NULL
, "shared");
1007 blobmsg_close_array(buf
, a
);
1010 a
= blobmsg_open_array(buf
, "wpa");
1012 for (nret
= 1; nret
<= 3; nret
++)
1013 if (crypto
.wpa_version
& (1 << (nret
- 1)))
1014 blobmsg_add_u32(buf
, NULL
, nret
);
1016 blobmsg_close_array(buf
, a
);
1018 a
= blobmsg_open_array(buf
, "authentication");
1020 if (crypto
.auth_suites
& IWINFO_KMGMT_PSK
)
1021 blobmsg_add_string(buf
, NULL
, "psk");
1023 if (crypto
.auth_suites
& IWINFO_KMGMT_8021x
)
1024 blobmsg_add_string(buf
, NULL
, "802.1x");
1026 if (crypto
.auth_suites
& IWINFO_KMGMT_SAE
)
1027 blobmsg_add_string(buf
, NULL
, "sae");
1029 if (crypto
.auth_suites
& IWINFO_KMGMT_OWE
)
1030 blobmsg_add_string(buf
, NULL
, "owe");
1032 if (!crypto
.auth_suites
||
1033 (crypto
.auth_suites
& IWINFO_KMGMT_NONE
))
1034 blobmsg_add_string(buf
, NULL
, "none");
1036 blobmsg_close_array(buf
, a
);
1039 a
= blobmsg_open_array(buf
, "ciphers");
1040 nret
= crypto
.pair_ciphers
| crypto
.group_ciphers
;
1042 if (nret
& IWINFO_CIPHER_WEP40
)
1043 blobmsg_add_string(buf
, NULL
, "wep-40");
1045 if (nret
& IWINFO_CIPHER_WEP104
)
1046 blobmsg_add_string(buf
, NULL
, "wep-104");
1048 if (nret
& IWINFO_CIPHER_TKIP
)
1049 blobmsg_add_string(buf
, NULL
, "tkip");
1051 if (nret
& IWINFO_CIPHER_CCMP
)
1052 blobmsg_add_string(buf
, NULL
, "ccmp");
1054 if (nret
& IWINFO_CIPHER_WRAP
)
1055 blobmsg_add_string(buf
, NULL
, "wrap");
1057 if (nret
& IWINFO_CIPHER_AESOCB
)
1058 blobmsg_add_string(buf
, NULL
, "aes-ocb");
1060 if (nret
& IWINFO_CIPHER_CKIP
)
1061 blobmsg_add_string(buf
, NULL
, "ckip");
1063 if (!nret
|| (nret
& IWINFO_CIPHER_NONE
))
1064 blobmsg_add_string(buf
, NULL
, "none");
1066 blobmsg_close_array(buf
, a
);
1069 blobmsg_close_table(buf
, o2
);
1073 blobmsg_close_table(buf
, o
);
1080 static void rpc_luci_get_wireless_devices_cb(struct ubus_request
*req
,
1081 int type
, struct blob_attr
*msg
)
1083 struct blob_attr
*wifi
, *cur
, *iface
, *cur2
;
1084 struct reply_context
*rctx
= req
->priv
;
1085 const char *name
, *first_ifname
;
1086 int rem
, rem2
, rem3
, rem4
;
1089 blob_for_each_attr(wifi
, msg
, rem
) {
1090 if (blobmsg_type(wifi
) != BLOBMSG_TYPE_TABLE
||
1091 blobmsg_name(wifi
) == NULL
)
1094 o
= blobmsg_open_table(&rctx
->blob
, blobmsg_name(wifi
));
1096 rem2
= blobmsg_data_len(wifi
);
1097 first_ifname
= NULL
;
1099 __blob_for_each_attr(cur
, blobmsg_data(wifi
), rem2
) {
1100 name
= blobmsg_name(cur
);
1102 if (!name
|| !strcmp(name
, "iwinfo")) {
1105 else if (!strcmp(name
, "interfaces")) {
1106 if (blobmsg_type(cur
) != BLOBMSG_TYPE_ARRAY
)
1109 a
= blobmsg_open_array(&rctx
->blob
, "interfaces");
1111 rem3
= blobmsg_data_len(cur
);
1113 __blob_for_each_attr(iface
, blobmsg_data(cur
), rem3
) {
1114 if (blobmsg_type(iface
) != BLOBMSG_TYPE_TABLE
)
1117 o2
= blobmsg_open_table(&rctx
->blob
, NULL
);
1119 rem4
= blobmsg_data_len(iface
);
1122 __blob_for_each_attr(cur2
, blobmsg_data(iface
), rem4
) {
1123 if (!strcmp(blobmsg_name(cur2
), "ifname"))
1124 name
= blobmsg_get_string(cur2
);
1125 else if (!strcmp(blobmsg_name(cur2
), "iwinfo"))
1128 blobmsg_add_blob(&rctx
->blob
, cur2
);
1132 if (rpc_luci_get_iwinfo(&rctx
->blob
, name
, false))
1133 first_ifname
= first_ifname
? first_ifname
: name
;
1135 blobmsg_close_table(&rctx
->blob
, o2
);
1138 blobmsg_close_array(&rctx
->blob
, a
);
1141 blobmsg_add_blob(&rctx
->blob
, cur
);
1145 rpc_luci_get_iwinfo(&rctx
->blob
,
1146 first_ifname
? first_ifname
: blobmsg_name(wifi
),
1149 blobmsg_close_table(&rctx
->blob
, o
);
1152 finish_request(rctx
, UBUS_STATUS_OK
);
1156 rpc_luci_get_wireless_devices(struct ubus_context
*ctx
,
1157 struct ubus_object
*obj
,
1158 struct ubus_request_data
*req
,
1160 struct blob_attr
*msg
)
1162 struct reply_context
*rctx
= defer_request(ctx
, req
);
1165 return UBUS_STATUS_UNKNOWN_ERROR
;
1167 if (!invoke_ubus(ctx
, "network.wireless", "status", NULL
,
1168 rpc_luci_get_wireless_devices_cb
, rctx
))
1169 return finish_request(rctx
, UBUS_STATUS_NOT_FOUND
);
1171 return UBUS_STATUS_OK
;
1175 struct avl_node avl
;
1177 struct avl_tree ipaddrs
;
1178 struct avl_tree ip6addrs
;
1181 /* used to ignore priority with avl_find_element */
1182 #define HOST_HINT_PRIO_IGNORE -1
1184 /* higher (larger) priority addresses are listed first */
1185 #define HOST_HINT_PRIO_NL 10 /* neighbor table */
1186 #define HOST_HINT_PRIO_ETHER 50 /* /etc/ethers */
1187 #define HOST_HINT_PRIO_LEASEFILE 100 /* dhcp leasefile */
1188 #define HOST_HINT_PRIO_RRDNS 100 /* rrdns */
1189 #define HOST_HINT_PRIO_IFADDRS 200 /* getifaddrs() */
1190 #define HOST_HINT_PRIO_STATIC_LEASE 200 /* uci static leases */
1192 struct host_hint_addr
{
1193 struct avl_node avl
;
1198 struct in6_addr in6
;
1203 host_hint_addr_avl_cmp(const void *k1
, const void *k2
, void *ptr
)
1205 struct host_hint_addr
*a1
= (struct host_hint_addr
*)k1
;
1206 struct host_hint_addr
*a2
= (struct host_hint_addr
*)k2
;
1208 if (a1
->prio
!= a2
->prio
&&
1209 a1
->prio
!= HOST_HINT_PRIO_IGNORE
&&
1210 a2
->prio
!= HOST_HINT_PRIO_IGNORE
)
1211 return a1
->prio
< a2
->prio
? 1 : -1;
1213 if (a1
->af
!= a2
->af
)
1214 return a1
->af
< a2
->af
? -1 : 1;
1216 return memcmp(&a1
->addr
, &a2
->addr
, sizeof(a1
->addr
));
1220 nl_cb_done(struct nl_msg
*msg
, void *arg
)
1222 struct reply_context
*rctx
= arg
;
1228 nl_cb_error(struct sockaddr_nl
*nla
, struct nlmsgerr
*err
, void *arg
)
1230 struct reply_context
*rctx
= arg
;
1235 static struct host_hint
*
1236 rpc_luci_get_host_hint(struct reply_context
*rctx
, struct ether_addr
*ea
)
1238 struct host_hint
*hint
;
1245 hint
= avl_find_element(&rctx
->avl
, mac
, hint
, avl
);
1248 hint
= calloc_a(sizeof(*hint
), &p
, strlen(mac
) + 1);
1253 hint
->avl
.key
= strcpy(p
, mac
);
1254 avl_init(&hint
->ipaddrs
, host_hint_addr_avl_cmp
, false, NULL
);
1255 avl_init(&hint
->ip6addrs
, host_hint_addr_avl_cmp
, false, NULL
);
1256 avl_insert(&rctx
->avl
, &hint
->avl
);
1263 rpc_luci_add_host_hint_addr(struct host_hint
*hint
, int af
, int prio
, void *addr
)
1265 struct host_hint_addr e
, *a
;
1266 struct avl_tree
*addrs
= af
== AF_INET
? &hint
->ipaddrs
: &hint
->ip6addrs
;
1271 memset(&e
, 0, sizeof(e
));
1273 /* ignore prio when comparing against existing addresses */
1274 e
.prio
= HOST_HINT_PRIO_IGNORE
;
1277 memcpy(&e
.addr
.in
, (struct in_addr
*)addr
, sizeof(e
.addr
.in
));
1279 memcpy(&e
.addr
.in6
, (struct in6_addr
*)addr
, sizeof(e
.addr
.in6
));
1281 a
= avl_find_element(addrs
, &e
, a
, avl
);
1284 /* update prio of existing address if higher */
1285 if (prio
<= a
->prio
)
1288 avl_delete(addrs
, &a
->avl
);
1290 avl_insert(addrs
, &a
->avl
);
1294 a
= calloc(1, sizeof(*a
));
1299 memcpy(a
, &e
, sizeof(*a
));
1302 avl_insert(addrs
, &a
->avl
);
1306 rpc_luci_add_host_hint_ipaddr(struct host_hint
*hint
, int prio
, struct in_addr
*addr
)
1308 return rpc_luci_add_host_hint_addr(hint
, AF_INET
, prio
, (void *)addr
);
1312 rpc_luci_add_host_hint_ip6addr(struct host_hint
*hint
, int prio
, struct in6_addr
*addr
)
1314 return rpc_luci_add_host_hint_addr(hint
, AF_INET6
, prio
, (void *)addr
);
1317 static int nl_cb_dump_neigh(struct nl_msg
*msg
, void *arg
)
1319 struct reply_context
*rctx
= arg
;
1320 struct ether_addr
*mac
;
1321 struct in6_addr
*dst
;
1322 struct nlmsghdr
*hdr
= nlmsg_hdr(msg
);
1323 struct ndmsg
*nd
= NLMSG_DATA(hdr
);
1324 struct nlattr
*tb
[NDA_MAX
+1];
1325 struct host_hint
*hint
;
1327 rctx
->pending
= !!(hdr
->nlmsg_flags
& NLM_F_MULTI
);
1329 if (hdr
->nlmsg_type
!= RTM_NEWNEIGH
||
1330 (nd
->ndm_family
!= AF_INET
&& nd
->ndm_family
!= AF_INET6
))
1333 if (!(nd
->ndm_state
& (0xFF & ~NUD_NOARP
)))
1336 nlmsg_parse(hdr
, sizeof(*nd
), tb
, NDA_MAX
, NULL
);
1338 mac
= tb
[NDA_LLADDR
] ? RTA_DATA(tb
[NDA_LLADDR
]) : NULL
;
1339 dst
= tb
[NDA_DST
] ? RTA_DATA(tb
[NDA_DST
]) : NULL
;
1344 hint
= rpc_luci_get_host_hint(rctx
, mac
);
1349 if (nd
->ndm_family
== AF_INET
)
1350 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_NL
, (struct in_addr
*)dst
);
1352 rpc_luci_add_host_hint_ip6addr(hint
, HOST_HINT_PRIO_NL
, (struct in6_addr
*)dst
);
1358 rpc_luci_get_host_hints_nl(struct reply_context
*rctx
)
1360 struct nl_sock
*sock
= NULL
;
1361 struct nl_msg
*msg
= NULL
;
1362 struct nl_cb
*cb
= NULL
;
1363 struct ndmsg ndm
= {};
1365 sock
= nl_socket_alloc();
1370 if (nl_connect(sock
, NETLINK_ROUTE
))
1373 cb
= nl_cb_alloc(NL_CB_DEFAULT
);
1378 msg
= nlmsg_alloc_simple(RTM_GETNEIGH
, NLM_F_REQUEST
| NLM_F_DUMP
);
1383 nlmsg_append(msg
, &ndm
, sizeof(ndm
), 0);
1385 nl_cb_set(cb
, NL_CB_VALID
, NL_CB_CUSTOM
, nl_cb_dump_neigh
, rctx
);
1386 nl_cb_set(cb
, NL_CB_FINISH
, NL_CB_CUSTOM
, nl_cb_done
, rctx
);
1387 nl_cb_err(cb
, NL_CB_CUSTOM
, nl_cb_error
, rctx
);
1389 avl_init(&rctx
->avl
, avl_strcmp
, false, NULL
);
1393 nl_send_auto_complete(sock
, msg
);
1395 while (rctx
->pending
)
1396 nl_recvmsgs(sock
, cb
);
1400 nl_socket_free(sock
);
1410 rpc_luci_get_host_hints_ether(struct reply_context
*rctx
)
1412 struct host_hint
*hint
;
1417 f
= fopen("/etc/ethers", "r");
1422 while (fgets(buf
, sizeof(buf
), f
)) {
1423 p
= strtok(buf
, " \t\n");
1424 hint
= rpc_luci_get_host_hint(rctx
, p
? ether_aton(p
) : NULL
);
1429 p
= strtok(NULL
, " \t\n");
1434 if (inet_pton(AF_INET
, p
, &in
) == 1) {
1435 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_ETHER
, &in
);
1437 else if (*p
&& !hint
->hostname
) {
1438 hint
->hostname
= strdup(p
);
1446 rpc_luci_get_host_hints_uci(struct reply_context
*rctx
)
1448 struct uci_ptr ptr
= { .package
= "dhcp" };
1449 struct uci_context
*uci
= NULL
;
1450 struct uci_package
*pkg
= NULL
;
1451 struct lease_entry
*lease
;
1452 struct host_hint
*hint
;
1453 struct uci_element
*e
, *l
;
1454 struct uci_section
*s
;
1459 uci
= uci_alloc_context();
1464 uci_load(uci
, ptr
.package
, &pkg
);
1469 uci_foreach_element(&pkg
->sections
, e
)
1471 s
= uci_to_section(e
);
1473 if (strcmp(s
->type
, "host"))
1476 ptr
.section
= s
->e
.name
;
1482 if (!uci_lookup_ptr(uci
, &ptr
, NULL
, true) && ptr
.o
!= NULL
&&
1483 ptr
.o
->type
!= UCI_TYPE_STRING
)
1484 n
= ptr
.o
->v
.string
;
1488 if (!n
|| inet_pton(AF_INET
, n
, &in
) != 1)
1491 ptr
.option
= "name";
1494 if (!uci_lookup_ptr(uci
, &ptr
, NULL
, true) && ptr
.o
!= NULL
&&
1495 ptr
.o
->type
== UCI_TYPE_STRING
)
1496 n
= ptr
.o
->v
.string
;
1503 if (uci_lookup_ptr(uci
, &ptr
, NULL
, true) || ptr
.o
== NULL
)
1506 if (ptr
.o
->type
== UCI_TYPE_STRING
) {
1507 for (p
= strtok(ptr
.o
->v
.string
, " \t");
1509 p
= strtok(NULL
, " \t")) {
1510 hint
= rpc_luci_get_host_hint(rctx
, ether_aton(p
));
1516 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_STATIC_LEASE
, &in
);
1518 if (n
&& !hint
->hostname
)
1519 hint
->hostname
= strdup(n
);
1522 else if (ptr
.o
->type
== UCI_TYPE_LIST
) {
1523 uci_foreach_element(&ptr
.o
->v
.list
, l
) {
1524 hint
= rpc_luci_get_host_hint(rctx
, ether_aton(l
->name
));
1530 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_STATIC_LEASE
, &in
);
1532 if (n
&& !hint
->hostname
)
1533 hint
->hostname
= strdup(n
);
1540 while ((lease
= lease_next()) != NULL
) {
1541 hint
= rpc_luci_get_host_hint(rctx
, &lease
->mac
);
1546 for (i
= 0; i
< lease
->n_addr
; i
++) {
1547 if (lease
->af
== AF_INET
)
1548 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_LEASEFILE
, &lease
->addr
[i
].in
);
1549 else if (lease
->af
== AF_INET6
)
1550 rpc_luci_add_host_hint_ip6addr(hint
, HOST_HINT_PRIO_LEASEFILE
, &lease
->addr
[i
].in6
);
1553 if (lease
->hostname
&& !hint
->hostname
)
1554 hint
->hostname
= strdup(lease
->hostname
);
1561 uci_free_context(uci
);
1565 rpc_luci_get_host_hints_ifaddrs(struct reply_context
*rctx
)
1567 struct ether_addr empty_ea
= {};
1568 struct in6_addr empty_in6
= {};
1569 struct ifaddrs
*ifaddr
, *ifa
;
1570 struct sockaddr_ll
*sll
;
1571 struct avl_tree devices
;
1572 struct host_hint
*hint
;
1574 struct avl_node avl
;
1575 struct ether_addr ea
;
1576 struct in6_addr in6
;
1578 } *device
, *nextdevice
;
1581 avl_init(&devices
, avl_strcmp
, false, NULL
);
1583 if (getifaddrs(&ifaddr
) == -1)
1586 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
1590 device
= avl_find_element(&devices
, ifa
->ifa_name
, device
, avl
);
1593 device
= calloc_a(sizeof(*device
), &p
, strlen(ifa
->ifa_name
) + 1);
1598 device
->avl
.key
= strcpy(p
, ifa
->ifa_name
);
1599 avl_insert(&devices
, &device
->avl
);
1602 switch (ifa
->ifa_addr
->sa_family
) {
1604 sll
= (struct sockaddr_ll
*)ifa
->ifa_addr
;
1606 if (sll
->sll_halen
== 6)
1607 memcpy(&device
->ea
, sll
->sll_addr
, 6);
1612 device
->in6
= ((struct sockaddr_in6
*)ifa
->ifa_addr
)->sin6_addr
;
1616 device
->in
= ((struct sockaddr_in
*)ifa
->ifa_addr
)->sin_addr
;
1621 freeifaddrs(ifaddr
);
1623 avl_remove_all_elements(&devices
, device
, avl
, nextdevice
) {
1624 if (memcmp(&device
->ea
, &empty_ea
, sizeof(empty_ea
)) &&
1625 (memcmp(&device
->in6
, &empty_in6
, sizeof(empty_in6
)) ||
1626 device
->in
.s_addr
!= 0)) {
1627 hint
= rpc_luci_get_host_hint(rctx
, &device
->ea
);
1630 if (device
->in
.s_addr
!= 0)
1631 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_IFADDRS
, &device
->in
);
1633 if (memcmp(&device
->in6
, &empty_in6
, sizeof(empty_in6
)) != 0)
1634 rpc_luci_add_host_hint_ip6addr(hint
, HOST_HINT_PRIO_IFADDRS
, &device
->in6
);
1643 rpc_luci_get_host_hints_finish(struct reply_context
*rctx
);
1646 rpc_luci_get_host_hints_rrdns_cb(struct ubus_request
*req
, int type
,
1647 struct blob_attr
*msg
)
1649 struct reply_context
*rctx
= req
->priv
;
1650 struct host_hint
*hint
;
1651 struct blob_attr
*cur
;
1652 struct in6_addr in6
;
1657 blob_for_each_attr(cur
, msg
, rem
) {
1658 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
)
1661 if (inet_pton(AF_INET6
, blobmsg_name(cur
), &in6
) == 1) {
1662 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1663 rpc_luci_add_host_hint_ip6addr(hint
, HOST_HINT_PRIO_RRDNS
, &in6
);
1664 if (!avl_is_empty(&hint
->ip6addrs
)) {
1666 free(hint
->hostname
);
1668 hint
->hostname
= strdup(blobmsg_get_string(cur
));
1673 else if (inet_pton(AF_INET
, blobmsg_name(cur
), &in
) == 1) {
1674 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1675 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_RRDNS
, &in
);
1676 if (!avl_is_empty(&hint
->ipaddrs
)) {
1678 free(hint
->hostname
);
1680 hint
->hostname
= strdup(blobmsg_get_string(cur
));
1688 rpc_luci_get_host_hints_finish(rctx
);
1692 rpc_luci_get_host_hints_rrdns(struct reply_context
*rctx
)
1694 struct in6_addr empty_in6
= {};
1695 char buf
[INET6_ADDRSTRLEN
];
1696 struct blob_buf req
= {};
1697 struct host_hint
*hint
;
1698 struct host_hint_addr
*addr
;
1702 blob_buf_init(&req
, 0);
1704 a
= blobmsg_open_array(&req
, "addrs");
1706 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1707 avl_for_each_element(&hint
->ipaddrs
, addr
, avl
) {
1708 if (addr
->addr
.in
.s_addr
!= 0) {
1709 inet_ntop(AF_INET
, &addr
->addr
.in
, buf
, sizeof(buf
));
1710 blobmsg_add_string(&req
, NULL
, buf
);
1714 avl_for_each_element(&hint
->ip6addrs
, addr
, avl
) {
1715 if (memcmp(&addr
->addr
.in6
, &empty_in6
, sizeof(empty_in6
))) {
1716 inet_ntop(AF_INET6
, &addr
->addr
.in6
, buf
, sizeof(buf
));
1717 blobmsg_add_string(&req
, NULL
, buf
);
1723 blobmsg_close_array(&req
, a
);
1726 blobmsg_add_u32(&req
, "timeout", 250);
1727 blobmsg_add_u32(&req
, "limit", n
);
1729 if (!invoke_ubus(rctx
->context
, "network.rrdns", "lookup", &req
,
1730 rpc_luci_get_host_hints_rrdns_cb
, rctx
))
1731 rpc_luci_get_host_hints_finish(rctx
);
1734 rpc_luci_get_host_hints_finish(rctx
);
1737 blob_buf_free(&req
);
1741 rpc_luci_get_host_hints_finish(struct reply_context
*rctx
)
1743 struct host_hint
*hint
, *nexthint
;
1744 struct host_hint_addr
*addr
, *nextaddr
;
1745 char buf
[INET6_ADDRSTRLEN
];
1746 struct in6_addr in6
= {};
1749 avl_remove_all_elements(&rctx
->avl
, hint
, avl
, nexthint
) {
1750 o
= blobmsg_open_table(&rctx
->blob
, hint
->avl
.key
);
1752 a
= blobmsg_open_array(&rctx
->blob
, "ipaddrs");
1754 avl_remove_all_elements(&hint
->ipaddrs
, addr
, avl
, nextaddr
) {
1755 if (addr
->addr
.in
.s_addr
!= 0) {
1756 inet_ntop(AF_INET
, &addr
->addr
.in
, buf
, sizeof(buf
));
1757 blobmsg_add_string(&rctx
->blob
, NULL
, buf
);
1763 blobmsg_close_array(&rctx
->blob
, a
);
1765 a
= blobmsg_open_array(&rctx
->blob
, "ip6addrs");
1767 avl_remove_all_elements(&hint
->ip6addrs
, addr
, avl
, nextaddr
) {
1768 if (memcmp(&addr
->addr
.in6
, &in6
, sizeof(in6
))) {
1769 inet_ntop(AF_INET6
, &addr
->addr
.in6
, buf
, sizeof(buf
));
1770 blobmsg_add_string(&rctx
->blob
, NULL
, buf
);
1776 blobmsg_close_array(&rctx
->blob
, a
);
1779 blobmsg_add_string(&rctx
->blob
, "name", hint
->hostname
);
1781 blobmsg_close_table(&rctx
->blob
, o
);
1784 free(hint
->hostname
);
1789 return finish_request(rctx
, UBUS_STATUS_OK
);
1793 rpc_luci_get_host_hints(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1794 struct ubus_request_data
*req
, const char *method
,
1795 struct blob_attr
*msg
)
1797 struct reply_context
*rctx
= defer_request(ctx
, req
);
1800 return UBUS_STATUS_UNKNOWN_ERROR
;
1802 rpc_luci_get_host_hints_nl(rctx
);
1803 rpc_luci_get_host_hints_uci(rctx
);
1804 rpc_luci_get_host_hints_ether(rctx
);
1805 rpc_luci_get_host_hints_ifaddrs(rctx
);
1806 rpc_luci_get_host_hints_rrdns(rctx
);
1808 return UBUS_STATUS_OK
;
1812 rpc_luci_get_duid_hints(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1813 struct ubus_request_data
*req
, const char *method
,
1814 struct blob_attr
*msg
)
1816 struct { struct avl_node avl
; } *e
, *next
;
1817 char s
[INET6_ADDRSTRLEN
], *p
;
1818 struct ether_addr empty
= {};
1819 struct lease_entry
*lease
;
1820 struct avl_tree avl
;
1824 avl_init(&avl
, avl_strcmp
, false, NULL
);
1825 blob_buf_init(&blob
, 0);
1829 while ((lease
= lease_next()) != NULL
) {
1830 if (lease
->af
!= AF_INET6
|| lease
->duid
== NULL
)
1833 e
= avl_find_element(&avl
, lease
->duid
, e
, avl
);
1838 e
= calloc_a(sizeof(*e
), &p
, strlen(lease
->duid
) + 1);
1843 o
= blobmsg_open_table(&blob
, lease
->duid
);
1845 inet_ntop(AF_INET6
, &lease
->addr
[0].in6
, s
, sizeof(s
));
1846 blobmsg_add_string(&blob
, "ip6addr", s
);
1848 a
= blobmsg_open_array(&blob
, "ip6addrs");
1850 for (n
= 0; n
< lease
->n_addr
; n
++) {
1851 inet_ntop(AF_INET6
, &lease
->addr
[n
].in6
, s
, sizeof(s
));
1852 blobmsg_add_string(&blob
, NULL
, s
);
1855 blobmsg_close_array(&blob
, a
);
1857 if (lease
->hostname
)
1858 blobmsg_add_string(&blob
, "hostname", lease
->hostname
);
1860 if (memcmp(&lease
->mac
, &empty
, sizeof(empty
)))
1861 blobmsg_add_string(&blob
, "macaddr", ea2str(&lease
->mac
));
1863 blobmsg_close_table(&blob
, o
);
1865 e
->avl
.key
= strcpy(p
, lease
->duid
);
1866 avl_insert(&avl
, &e
->avl
);
1871 avl_remove_all_elements(&avl
, e
, avl
, next
) {
1875 ubus_send_reply(ctx
, req
, blob
.head
);
1877 return UBUS_STATUS_OK
;
1881 rpc_luci_get_board_json(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1882 struct ubus_request_data
*req
, const char *method
,
1883 struct blob_attr
*msg
)
1885 blob_buf_init(&blob
, 0);
1887 if (!blobmsg_add_json_from_file(&blob
, "/etc/board.json"))
1888 return UBUS_STATUS_UNKNOWN_ERROR
;
1890 ubus_send_reply(ctx
, req
, blob
.head
);
1891 return UBUS_STATUS_OK
;
1899 static const struct blobmsg_policy rpc_get_leases_policy
[__RPC_L_MAX
] = {
1900 [RPC_L_FAMILY
] = { .name
= "family", .type
= BLOBMSG_TYPE_INT32
}
1904 rpc_luci_get_dhcp_leases(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1905 struct ubus_request_data
*req
, const char *method
,
1906 struct blob_attr
*msg
)
1908 struct blob_attr
*tb
[__RPC_L_MAX
];
1909 struct ether_addr emptymac
= {};
1910 struct lease_entry
*lease
;
1911 char s
[INET6_ADDRSTRLEN
];
1916 blobmsg_parse(rpc_get_leases_policy
, __RPC_L_MAX
, tb
,
1917 blob_data(msg
), blob_len(msg
));
1919 switch (tb
[RPC_L_FAMILY
] ? blobmsg_get_u32(tb
[RPC_L_FAMILY
]) : 0) {
1933 return UBUS_STATUS_INVALID_ARGUMENT
;
1936 blob_buf_init(&blob
, 0);
1938 for (af
= family
? family
: AF_INET
;
1940 af
= (family
== 0) ? (af
== AF_INET
? AF_INET6
: 0) : 0) {
1942 a
= blobmsg_open_array(&blob
, (af
== AF_INET
) ? "dhcp_leases"
1947 while ((lease
= lease_next()) != NULL
) {
1948 if (lease
->af
!= af
)
1951 o
= blobmsg_open_table(&blob
, NULL
);
1953 if (lease
->expire
== -1)
1954 blobmsg_add_u8(&blob
, "expires", 0);
1956 blobmsg_add_u32(&blob
, "expires", lease
->expire
);
1958 if (lease
->hostname
)
1959 blobmsg_add_string(&blob
, "hostname", lease
->hostname
);
1961 if (memcmp(&lease
->mac
, &emptymac
, sizeof(emptymac
)))
1962 blobmsg_add_string(&blob
, "macaddr", ea2str(&lease
->mac
));
1965 blobmsg_add_string(&blob
, "duid", lease
->duid
);
1967 inet_ntop(lease
->af
, &lease
->addr
[0].in6
, s
, sizeof(s
));
1968 blobmsg_add_string(&blob
, (af
== AF_INET
) ? "ipaddr" : "ip6addr",
1971 if (af
== AF_INET6
) {
1972 a2
= blobmsg_open_array(&blob
, "ip6addrs");
1974 for (n
= 0; n
< lease
->n_addr
; n
++) {
1975 inet_ntop(lease
->af
, &lease
->addr
[n
].in6
, s
, sizeof(s
));
1976 blobmsg_add_string(&blob
, NULL
, s
);
1979 blobmsg_close_array(&blob
, a2
);
1982 blobmsg_close_table(&blob
, o
);
1987 blobmsg_close_array(&blob
, a
);
1990 ubus_send_reply(ctx
, req
, blob
.head
);
1992 return UBUS_STATUS_OK
;
1996 rpc_luci_api_init(const struct rpc_daemon_ops
*o
, struct ubus_context
*ctx
)
1998 static const struct ubus_method luci_methods
[] = {
1999 UBUS_METHOD_NOARG("getNetworkDevices", rpc_luci_get_network_devices
),
2000 UBUS_METHOD_NOARG("getWirelessDevices", rpc_luci_get_wireless_devices
),
2001 UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints
),
2002 UBUS_METHOD_NOARG("getDUIDHints", rpc_luci_get_duid_hints
),
2003 UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json
),
2004 UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases
, rpc_get_leases_policy
)
2007 static struct ubus_object_type luci_type
=
2008 UBUS_OBJECT_TYPE("rpcd-luci", luci_methods
);
2010 static struct ubus_object obj
= {
2013 .methods
= luci_methods
,
2014 .n_methods
= ARRAY_SIZE(luci_methods
),
2017 return ubus_add_object(ctx
, &obj
);
2020 struct rpc_plugin rpc_plugin
= {
2021 .init
= rpc_luci_api_init