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/in.h>
35 #include <netinet/ether.h>
36 #include <linux/rtnetlink.h>
37 #include <linux/if_packet.h>
39 #include <netlink/msg.h>
40 #include <netlink/attr.h>
41 #include <netlink/socket.h>
44 #include <libubox/avl.h>
45 #include <libubox/avl-cmp.h>
46 #include <libubox/usock.h>
47 #include <libubox/uloop.h>
53 #include <rpcd/plugin.h>
55 #ifndef IN6_IS_ADDR_ULA
56 #define IN6_IS_ADDR_ULA(a) (((a)->s6_addr[0] & 0xfe) == 0xfc)
60 static struct blob_buf blob
;
62 struct reply_context
{
63 struct ubus_context
*context
;
64 struct ubus_request_data request
;
65 struct uloop_timeout timeout
;
71 struct invoke_context
{
72 struct ubus_request request
;
73 struct uloop_timeout timeout
;
74 struct ubus_context
*context
;
75 void (*cb
)(struct ubus_request
*, int, struct blob_attr
*);
79 static const char **iw_modenames
;
80 static struct iwinfo_ops
*(*iw_backend
)(const char *);
81 static void (*iw_close
)(void);
84 invoke_data_cb(struct ubus_request
*req
, int type
, struct blob_attr
*msg
)
86 struct invoke_context
*ictx
=
87 container_of(req
, struct invoke_context
, request
);
90 ictx
->cb(req
, type
, msg
);
96 invoke_done_cb(struct ubus_request
*req
, int ret
)
98 struct invoke_context
*ictx
=
99 container_of(req
, struct invoke_context
, request
);
101 if (ictx
->cb
!= NULL
)
102 ictx
->cb(req
, -1, NULL
);
104 uloop_timeout_cancel(&ictx
->timeout
);
109 invoke_timeout_cb(struct uloop_timeout
*timeout
)
111 struct invoke_context
*ictx
=
112 container_of(timeout
, struct invoke_context
, timeout
);
114 if (ictx
->cb
!= NULL
)
115 ictx
->cb(&ictx
->request
, -1, NULL
);
117 ubus_abort_request(ictx
->context
, &ictx
->request
);
121 static struct reply_context
*
122 defer_request(struct ubus_context
*ctx
, struct ubus_request_data
*req
)
124 struct reply_context
*rctx
;
126 rctx
= calloc(1, sizeof(*rctx
));
132 blob_buf_init(&rctx
->blob
, 0);
133 ubus_defer_request(ctx
, req
, &rctx
->request
);
139 finish_request(struct reply_context
*rctx
, int status
)
141 if (status
== UBUS_STATUS_OK
)
142 ubus_send_reply(rctx
->context
, &rctx
->request
, rctx
->blob
.head
);
144 ubus_complete_deferred_request(rctx
->context
, &rctx
->request
, status
);
145 blob_buf_free(&rctx
->blob
);
152 invoke_ubus(struct ubus_context
*ctx
, const char *object
, const char *method
,
153 struct blob_buf
*req
,
154 void (*cb
)(struct ubus_request
*, int, struct blob_attr
*),
157 struct invoke_context
*ictx
;
158 struct blob_buf empty
= {};
162 if (ubus_lookup_id(ctx
, object
, &id
))
166 blob_buf_init(&empty
, 0);
170 ictx
= calloc(1, sizeof(*ictx
));
176 rv
= !ubus_invoke_async(ctx
, id
, method
, req
->head
, &ictx
->request
);
180 ictx
->request
.priv
= priv
;
181 ictx
->request
.data_cb
= invoke_data_cb
;
182 ictx
->request
.complete_cb
= invoke_done_cb
;
183 ubus_complete_request_async(ctx
, &ictx
->request
);
185 ictx
->timeout
.cb
= invoke_timeout_cb
;
186 uloop_timeout_set(&ictx
->timeout
, 2000);
190 cb(&ictx
->request
, -1, NULL
);
202 readstr(const char *fmt
, ...)
204 static char data
[128];
211 vsnprintf(path
, sizeof(path
), fmt
, ap
);
215 f
= fopen(path
, "r");
218 n
= fread(data
, 1, sizeof(data
) - 1, f
);
221 while (n
> 0 && isspace(data
[n
-1]))
231 ea_empty(struct ether_addr
*ea
)
233 struct ether_addr empty
= { 0 };
235 return !memcmp(ea
, &empty
, sizeof(empty
));
239 ea2str(struct ether_addr
*ea
)
246 snprintf(mac
, sizeof(mac
), "%02X:%02X:%02X:%02X:%02X:%02X",
247 ea
->ether_addr_octet
[0], ea
->ether_addr_octet
[1],
248 ea
->ether_addr_octet
[2], ea
->ether_addr_octet
[3],
249 ea
->ether_addr_octet
[4], ea
->ether_addr_octet
[5]);
255 sa2str(struct sockaddr
*sa
)
257 static char buf
[INET6_ADDRSTRLEN
];
259 struct sockaddr_in6
*in6
;
260 struct sockaddr_in
*in
;
261 struct sockaddr_ll
*ll
;
267 if (s
.sa
->sa_family
== AF_INET
)
268 inet_ntop(sa
->sa_family
, &s
.in
->sin_addr
, buf
, sizeof(buf
));
269 else if (s
.sa
->sa_family
== AF_INET6
)
270 inet_ntop(sa
->sa_family
, &s
.in6
->sin6_addr
, buf
, sizeof(buf
));
271 else if (s
.sa
->sa_family
== AF_PACKET
)
272 strcpy(buf
, ea2str((struct ether_addr
*)s
.ll
->sll_addr
));
279 static struct ether_addr
*
280 duid2ea(const char *duid
)
282 static struct ether_addr ea
;
283 const char *p
= NULL
;
289 for (len
= 0; duid
[len
]; len
++)
290 if (!isxdigit(duid
[len
]))
294 (((x) <= '9') ? ((x) - '0') : \
295 (((x) <= 'F') ? ((x) - 'A' + 10) : \
300 if (!strncmp(duid
, "00010001", 8))
306 if (!strncmp(duid
, "00030001", 8))
319 ea
.ether_addr_octet
[0] = hex(p
[0]) * 16 + hex(p
[1]);
320 ea
.ether_addr_octet
[1] = hex(p
[2]) * 16 + hex(p
[3]);
321 ea
.ether_addr_octet
[2] = hex(p
[4]) * 16 + hex(p
[5]);
322 ea
.ether_addr_octet
[3] = hex(p
[6]) * 16 + hex(p
[7]);
323 ea
.ether_addr_octet
[4] = hex(p
[8]) * 16 + hex(p
[9]);
324 ea
.ether_addr_octet
[5] = hex(p
[10]) * 16 + hex(p
[11]);
343 struct ether_addr mac
;
354 add_leasefile(const char *path
, bool is_odhcpd
)
359 fh
= fopen(path
, "r");
364 ptr
= realloc(lease_state
.files
, sizeof(*lease_state
.files
) * (lease_state
.num
+ 1));
372 lease_state
.files
= ptr
;
373 lease_state
.files
[lease_state
.num
].fh
= fh
;
374 lease_state
.files
[lease_state
.num
].odhcpd
= is_odhcpd
;
381 find_leasefiles(struct uci_context
*uci
, bool is_odhcpd
)
383 struct uci_ptr ptr
= { .package
= "dhcp" };
384 struct uci_package
*pkg
= NULL
;
385 struct uci_section
*s
;
386 struct uci_element
*e
;
389 pkg
= uci_lookup_package(uci
, ptr
.package
);
392 uci_load(uci
, ptr
.package
, &pkg
);
398 uci_foreach_element(&pkg
->sections
, e
) {
399 s
= uci_to_section(e
);
401 if (strcmp(s
->type
, is_odhcpd
? "odhcpd" : "dnsmasq"))
406 ptr
.section
= s
->e
.name
;
409 ptr
.option
= "leasefile";
412 if (uci_lookup_ptr(uci
, &ptr
, NULL
, true) || ptr
.o
== NULL
)
415 if (ptr
.o
->type
!= UCI_TYPE_STRING
)
418 if (add_leasefile(ptr
.o
->v
.string
, is_odhcpd
))
428 while (lease_state
.num
> 0)
429 fclose(lease_state
.files
[--lease_state
.num
].fh
);
431 free(lease_state
.files
);
433 lease_state
.files
= NULL
;
441 struct uci_context
*uci
;
445 uci
= uci_alloc_context();
450 lease_state
.now
= time(NULL
);
452 if (!find_leasefiles(uci
, false))
453 add_leasefile("/tmp/dhcp.leases", false);
455 if (!find_leasefiles(uci
, true))
456 add_leasefile("/tmp/hosts/odhcpd", true);
458 uci_free_context(uci
);
461 static struct lease_entry
*
464 static struct lease_entry e
;
465 struct ether_addr
*ea
;
469 while (lease_state
.off
< lease_state
.num
) {
470 memset(&e
, 0, sizeof(e
));
472 while (fgets(e
.buf
, sizeof(e
.buf
), lease_state
.files
[lease_state
.off
].fh
)) {
473 if (lease_state
.files
[lease_state
.off
].odhcpd
) {
474 strtok(e
.buf
, " \t\n"); /* # */
475 strtok(NULL
, " \t\n"); /* iface */
477 e
.duid
= strtok(NULL
, " \t\n"); /* duid */
482 p
= strtok(NULL
, " \t\n"); /* iaid */
487 if (!strcmp(p
, "ipv4")) {
496 e
.hostname
= strtok(NULL
, " \t\n"); /* name */
501 p
= strtok(NULL
, " \t\n"); /* ts */
506 n
= strtol(p
, NULL
, 10);
508 if (n
> lease_state
.now
)
509 e
.expire
= n
- lease_state
.now
;
515 strtok(NULL
, " \t\n"); /* id */
517 p
= strtok(NULL
, " \t\n"); /* length */
522 n
= atoi(p
); /* length */
527 for (e
.n_addr
= 0, p
= strtok(NULL
, "/ \t\n");
528 e
.n_addr
< ARRAY_SIZE(e
.addr
) && p
!= NULL
;
529 p
= strtok(NULL
, "/ \t\n")) {
530 if (inet_pton(e
.af
, p
, &e
.addr
[e
.n_addr
].in6
))
534 ea
= duid2ea(e
.duid
);
539 if (!strcmp(e
.hostname
, "-"))
542 if (!strcmp(e
.duid
, "-"))
546 p
= strtok(e
.buf
, " \t\n");
551 n
= strtol(p
, NULL
, 10);
553 if (n
> lease_state
.now
)
554 e
.expire
= n
- lease_state
.now
;
560 p
= strtok(NULL
, " \t\n");
567 p
= strtok(NULL
, " \t\n");
569 if (p
&& inet_pton(AF_INET6
, p
, &e
.addr
[0].in6
)) {
574 else if (p
&& inet_pton(AF_INET
, p
, &e
.addr
[0].in
)) {
583 if (!ea
&& e
.af
!= AF_INET6
)
586 e
.hostname
= strtok(NULL
, " \t\n");
587 e
.duid
= strtok(NULL
, " \t\n");
589 if (!e
.hostname
|| !e
.duid
)
592 if (!strcmp(e
.hostname
, "*"))
595 if (!strcmp(e
.duid
, "*"))
599 ea
= duid2ea(e
.duid
);
616 rpc_luci_parse_network_device_sys(const char *name
, struct ifaddrs
*ifaddr
)
618 char link
[64], buf
[512], *p
;
619 unsigned int ifa_flags
= 0;
620 struct sockaddr_ll
*sll
;
629 const char *stats
[] = {
630 "rx_bytes", "tx_bytes", "tx_errors", "rx_errors", "tx_packets",
631 "rx_packets", "multicast", "collisions", "rx_dropped", "tx_dropped"
634 o
= blobmsg_open_table(&blob
, name
);
636 blobmsg_add_string(&blob
, "name", name
);
638 snprintf(buf
, sizeof(buf
), "/sys/class/net/%s/brif", name
);
643 blobmsg_add_u8(&blob
, "bridge", 1);
645 a
= blobmsg_open_array(&blob
, "ports");
653 if (strcmp(e
->d_name
, ".") && strcmp(e
->d_name
, ".."))
654 blobmsg_add_string(&blob
, NULL
, e
->d_name
);
657 blobmsg_close_array(&blob
, a
);
661 p
= readstr("/sys/class/net/%s/bridge/bridge_id", name
);
662 blobmsg_add_string(&blob
, "id", p
);
664 p
= readstr("/sys/class/net/%s/bridge/stp_state", name
);
665 blobmsg_add_u8(&blob
, "stp", strcmp(p
, "0") ? 1 : 0);
668 snprintf(buf
, sizeof(buf
), "/sys/class/net/%s/master", name
);
669 len
= readlink(buf
, link
, sizeof(link
) - 1);
673 blobmsg_add_string(&blob
, "master", basename(link
));
676 p
= readstr("/sys/class/net/%s/phy80211/index", name
);
677 blobmsg_add_u8(&blob
, "wireless", *p
? 1 : 0);
679 p
= readstr("/sys/class/net/%s/operstate", name
);
680 blobmsg_add_u8(&blob
, "up", !strcmp(p
, "up") || !strcmp(p
, "unknown"));
682 n
= atoi(readstr("/sys/class/net/%s/mtu", name
));
684 blobmsg_add_u32(&blob
, "mtu", n
);
686 n
= atoi(readstr("/sys/class/net/%s/tx_queue_len", name
));
688 blobmsg_add_u32(&blob
, "qlen", n
);
690 p
= readstr("/sys/class/net/%s/master", name
);
692 blobmsg_add_string(&blob
, "master", p
);
694 p
= strstr(readstr("/sys/class/net/%s/uevent", name
), "DEVTYPE=");
696 for (n
= 0, p
+= strlen("DEVTYPE=");; n
++) {
697 if (p
[n
] == '\0' || p
[n
] == '\n') {
699 blobmsg_add_string(&blob
, "devtype", p
);
705 blobmsg_add_string(&blob
, "devtype", "ethernet");
708 for (af
= AF_INET
; af
!= 0; af
= (af
== AF_INET
) ? AF_INET6
: 0) {
709 a
= blobmsg_open_array(&blob
,
710 (af
== AF_INET
) ? "ipaddrs" : "ip6addrs");
712 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
713 if (ifa
->ifa_addr
== NULL
|| ifa
->ifa_addr
->sa_family
!= af
)
716 if (strcmp(ifa
->ifa_name
, name
))
719 o2
= blobmsg_open_table(&blob
, NULL
);
721 blobmsg_add_string(&blob
, "address",
722 sa2str(ifa
->ifa_addr
));
724 blobmsg_add_string(&blob
, "netmask",
725 sa2str(ifa
->ifa_netmask
));
727 if (ifa
->ifa_dstaddr
&& (ifa
->ifa_flags
& IFF_POINTOPOINT
))
728 blobmsg_add_string(&blob
, "remote",
729 sa2str(ifa
->ifa_dstaddr
));
730 else if (ifa
->ifa_broadaddr
&& (ifa
->ifa_flags
& IFF_BROADCAST
))
731 blobmsg_add_string(&blob
, "broadcast",
732 sa2str(ifa
->ifa_broadaddr
));
734 blobmsg_close_table(&blob
, o2
);
736 ifa_flags
|= ifa
->ifa_flags
;
739 blobmsg_close_array(&blob
, a
);
742 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
743 if (ifa
->ifa_addr
== NULL
|| ifa
->ifa_addr
->sa_family
!= AF_PACKET
)
746 if (strcmp(ifa
->ifa_name
, name
))
749 sll
= (struct sockaddr_ll
*)ifa
->ifa_addr
;
751 if (sll
->sll_hatype
== 1)
752 blobmsg_add_string(&blob
, "mac", sa2str(ifa
->ifa_addr
));
754 blobmsg_add_u32(&blob
, "type", sll
->sll_hatype
);
755 blobmsg_add_u32(&blob
, "ifindex", sll
->sll_ifindex
);
757 ifa_flags
|= ifa
->ifa_flags
;
759 n
= atoi(readstr("/sys/class/net/%s/iflink", name
));
761 if (n
!= sll
->sll_ifindex
) {
762 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
763 if (ifa
->ifa_addr
== NULL
|| ifa
->ifa_addr
->sa_family
!= AF_PACKET
)
766 sll
= (struct sockaddr_ll
*)ifa
->ifa_addr
;
768 if (sll
->sll_ifindex
!= n
)
771 blobmsg_add_string(&blob
, "parent", ifa
->ifa_name
);
779 o2
= blobmsg_open_table(&blob
, "stats");
781 for (n
= 0; n
< ARRAY_SIZE(stats
); n
++) {
782 v
= strtoull(readstr("/sys/class/net/%s/statistics/%s",
783 name
, stats
[n
]), NULL
, 10);
785 blobmsg_add_u64(&blob
, stats
[n
], v
);
788 blobmsg_close_table(&blob
, o2
);
790 o2
= blobmsg_open_table(&blob
, "flags");
791 blobmsg_add_u8(&blob
, "up", ifa_flags
& IFF_UP
);
792 blobmsg_add_u8(&blob
, "broadcast", ifa_flags
& IFF_BROADCAST
);
793 blobmsg_add_u8(&blob
, "promisc", ifa_flags
& IFF_PROMISC
);
794 blobmsg_add_u8(&blob
, "loopback", ifa_flags
& IFF_LOOPBACK
);
795 blobmsg_add_u8(&blob
, "noarp", ifa_flags
& IFF_NOARP
);
796 blobmsg_add_u8(&blob
, "multicast", ifa_flags
& IFF_MULTICAST
);
797 blobmsg_add_u8(&blob
, "pointtopoint", ifa_flags
& IFF_POINTOPOINT
);
798 blobmsg_close_table(&blob
, o2
);
800 o2
= blobmsg_open_table(&blob
, "link");
802 p
= readstr("/sys/class/net/%s/speed", name
);
804 blobmsg_add_u32(&blob
, "speed", atoi(p
));
806 p
= readstr("/sys/class/net/%s/duplex", name
);
808 blobmsg_add_string(&blob
, "duplex", p
);
810 n
= atoi(readstr("/sys/class/net/%s/carrier", name
));
811 blobmsg_add_u8(&blob
, "carrier", n
== 1);
813 n
= atoi(readstr("/sys/class/net/%s/carrier_changes", name
));
814 blobmsg_add_u32(&blob
, "changes", n
);
816 n
= atoi(readstr("/sys/class/net/%s/carrier_up_count", name
));
817 blobmsg_add_u32(&blob
, "up_count", n
);
819 n
= atoi(readstr("/sys/class/net/%s/carrier_down_count", name
));
820 blobmsg_add_u32(&blob
, "down_count", n
);
822 blobmsg_close_table(&blob
, o2
);
824 blobmsg_close_table(&blob
, o
);
828 rpc_luci_get_network_devices(struct ubus_context
*ctx
,
829 struct ubus_object
*obj
,
830 struct ubus_request_data
*req
,
832 struct blob_attr
*msg
)
834 struct ifaddrs
*ifaddr
;
838 blob_buf_init(&blob
, 0);
840 d
= opendir("/sys/class/net");
843 if (getifaddrs(&ifaddr
) == 1)
852 if (e
->d_type
!= DT_DIR
&& e
->d_type
!= DT_REG
)
853 rpc_luci_parse_network_device_sys(e
->d_name
, ifaddr
);
862 ubus_send_reply(ctx
, req
, blob
.head
);
868 iw_call_str(int (*method
)(const char *, char *), const char *dev
,
869 struct blob_buf
*blob
, const char *field
)
871 char buf
[IWINFO_BUFSIZE
] = {};
873 if (method(dev
, buf
) == 0)
874 blobmsg_add_string(blob
, field
, buf
);
878 iw_call_num(int (*method
)(const char *, int *), const char *dev
,
879 struct blob_buf
*blob
, const char *field
)
883 if (method(dev
, &val
) == 0)
884 blobmsg_add_u32(blob
, field
, val
);
887 static bool rpc_luci_get_iwinfo(struct blob_buf
*buf
, const char *devname
,
890 struct iwinfo_crypto_entry crypto
= {};
891 struct iwinfo_hardware_id ids
= {};
892 const struct iwinfo_ops
*iw
;
898 if (!iw_backend
|| !iw_close
|| !iw_modenames
) {
899 if (glob("/usr/lib/libiwinfo.so*", 0, NULL
, &paths
) != 0)
902 for (i
= 0; i
< paths
.gl_pathc
&& !iwlib
; i
++)
903 iwlib
= dlopen(paths
.gl_pathv
[i
], RTLD_LAZY
| RTLD_LOCAL
);
910 iw_backend
= dlsym(iwlib
, "iwinfo_backend");
911 iw_close
= dlsym(iwlib
, "iwinfo_close");
912 iw_modenames
= dlsym(iwlib
, "IWINFO_OPMODE_NAMES");
914 if (!iw_backend
|| !iw_close
|| !iw_modenames
)
918 iw
= iw_backend(devname
);
923 o
= blobmsg_open_table(buf
, "iwinfo");
925 iw_call_num(iw
->signal
, devname
, buf
, "signal");
926 iw_call_num(iw
->noise
, devname
, buf
, "noise");
927 iw_call_num(iw
->channel
, devname
, buf
, "channel");
928 iw_call_str(iw
->country
, devname
, buf
, "country");
929 iw_call_str(iw
->phyname
, devname
, buf
, "phy");
930 iw_call_num(iw
->txpower
, devname
, buf
, "txpower");
931 iw_call_num(iw
->txpower_offset
, devname
, buf
, "txpower_offset");
932 iw_call_num(iw
->frequency
, devname
, buf
, "frequency");
933 iw_call_num(iw
->frequency_offset
, devname
, buf
, "frequency_offset");
935 if (!iw
->hwmodelist(devname
, &nret
)) {
936 a
= blobmsg_open_array(buf
, "hwmodes");
938 if (nret
& IWINFO_80211_AX
)
939 blobmsg_add_string(buf
, NULL
, "ax");
941 if (nret
& IWINFO_80211_AC
)
942 blobmsg_add_string(buf
, NULL
, "ac");
944 if (nret
& IWINFO_80211_A
)
945 blobmsg_add_string(buf
, NULL
, "a");
947 if (nret
& IWINFO_80211_B
)
948 blobmsg_add_string(buf
, NULL
, "b");
950 if (nret
& IWINFO_80211_G
)
951 blobmsg_add_string(buf
, NULL
, "g");
953 if (nret
& IWINFO_80211_N
)
954 blobmsg_add_string(buf
, NULL
, "n");
956 blobmsg_close_array(buf
, a
);
959 if (!iw
->htmodelist(devname
, &nret
)) {
960 a
= blobmsg_open_array(buf
, "htmodes");
962 if (nret
& IWINFO_HTMODE_HT20
)
963 blobmsg_add_string(buf
, NULL
, "HT20");
965 if (nret
& IWINFO_HTMODE_HT40
)
966 blobmsg_add_string(buf
, NULL
, "HT40");
968 if (nret
& IWINFO_HTMODE_VHT20
)
969 blobmsg_add_string(buf
, NULL
, "VHT20");
971 if (nret
& IWINFO_HTMODE_VHT40
)
972 blobmsg_add_string(buf
, NULL
, "VHT40");
974 if (nret
& IWINFO_HTMODE_VHT80
)
975 blobmsg_add_string(buf
, NULL
, "VHT80");
977 if (nret
& IWINFO_HTMODE_VHT80_80
)
978 blobmsg_add_string(buf
, NULL
, "VHT80+80");
980 if (nret
& IWINFO_HTMODE_VHT160
)
981 blobmsg_add_string(buf
, NULL
, "VHT160");
983 if (nret
& IWINFO_HTMODE_HE20
)
984 blobmsg_add_string(buf
, NULL
, "HE20");
986 if (nret
& IWINFO_HTMODE_HE40
)
987 blobmsg_add_string(buf
, NULL
, "HE40");
989 if (nret
& IWINFO_HTMODE_HE80
)
990 blobmsg_add_string(buf
, NULL
, "HE80");
992 if (nret
& IWINFO_HTMODE_HE160
)
993 blobmsg_add_string(buf
, NULL
, "HE160");
995 blobmsg_close_array(buf
, a
);
998 if (!iw
->hardware_id(devname
, (char *)&ids
)) {
999 o2
= blobmsg_open_table(buf
, "hardware");
1001 a
= blobmsg_open_array(buf
, "id");
1002 blobmsg_add_u32(buf
, NULL
, ids
.vendor_id
);
1003 blobmsg_add_u32(buf
, NULL
, ids
.device_id
);
1004 blobmsg_add_u32(buf
, NULL
, ids
.subsystem_vendor_id
);
1005 blobmsg_add_u32(buf
, NULL
, ids
.subsystem_device_id
);
1006 blobmsg_close_array(buf
, a
);
1008 iw_call_str(iw
->hardware_name
, devname
, buf
, "name");
1010 blobmsg_close_table(buf
, o2
);
1014 iw_call_num(iw
->quality
, devname
, buf
, "quality");
1015 iw_call_num(iw
->quality_max
, devname
, buf
, "quality_max");
1016 iw_call_num(iw
->bitrate
, devname
, buf
, "bitrate");
1018 if (!iw
->mode(devname
, &nret
))
1019 blobmsg_add_string(buf
, "mode", iw_modenames
[nret
]);
1021 iw_call_str(iw
->ssid
, devname
, buf
, "ssid");
1022 iw_call_str(iw
->bssid
, devname
, buf
, "bssid");
1024 if (!iw
->encryption(devname
, (char *)&crypto
)) {
1025 o2
= blobmsg_open_table(buf
, "encryption");
1027 blobmsg_add_u8(buf
, "enabled", crypto
.enabled
);
1029 if (crypto
.enabled
) {
1030 if (!crypto
.wpa_version
) {
1031 a
= blobmsg_open_array(buf
, "wep");
1033 if (crypto
.auth_algs
& IWINFO_AUTH_OPEN
)
1034 blobmsg_add_string(buf
, NULL
, "open");
1036 if (crypto
.auth_algs
& IWINFO_AUTH_SHARED
)
1037 blobmsg_add_string(buf
, NULL
, "shared");
1039 blobmsg_close_array(buf
, a
);
1042 a
= blobmsg_open_array(buf
, "wpa");
1044 for (nret
= 1; nret
<= 3; nret
++)
1045 if (crypto
.wpa_version
& (1 << (nret
- 1)))
1046 blobmsg_add_u32(buf
, NULL
, nret
);
1048 blobmsg_close_array(buf
, a
);
1050 a
= blobmsg_open_array(buf
, "authentication");
1052 if (crypto
.auth_suites
& IWINFO_KMGMT_PSK
)
1053 blobmsg_add_string(buf
, NULL
, "psk");
1055 if (crypto
.auth_suites
& IWINFO_KMGMT_8021x
)
1056 blobmsg_add_string(buf
, NULL
, "802.1x");
1058 if (crypto
.auth_suites
& IWINFO_KMGMT_SAE
)
1059 blobmsg_add_string(buf
, NULL
, "sae");
1061 if (crypto
.auth_suites
& IWINFO_KMGMT_OWE
)
1062 blobmsg_add_string(buf
, NULL
, "owe");
1064 if (!crypto
.auth_suites
||
1065 (crypto
.auth_suites
& IWINFO_KMGMT_NONE
))
1066 blobmsg_add_string(buf
, NULL
, "none");
1068 blobmsg_close_array(buf
, a
);
1071 a
= blobmsg_open_array(buf
, "ciphers");
1072 nret
= crypto
.pair_ciphers
| crypto
.group_ciphers
;
1074 if (nret
& IWINFO_CIPHER_WEP40
)
1075 blobmsg_add_string(buf
, NULL
, "wep-40");
1077 if (nret
& IWINFO_CIPHER_WEP104
)
1078 blobmsg_add_string(buf
, NULL
, "wep-104");
1080 if (nret
& IWINFO_CIPHER_TKIP
)
1081 blobmsg_add_string(buf
, NULL
, "tkip");
1083 if (nret
& IWINFO_CIPHER_CCMP
)
1084 blobmsg_add_string(buf
, NULL
, "ccmp");
1086 if (nret
& IWINFO_CIPHER_CCMP256
)
1087 blobmsg_add_string(buf
, NULL
, "ccmp-256");
1089 if (nret
& IWINFO_CIPHER_GCMP
)
1090 blobmsg_add_string(buf
, NULL
, "gcmp");
1092 if (nret
& IWINFO_CIPHER_GCMP256
)
1093 blobmsg_add_string(buf
, NULL
, "gcmp-256");
1095 if (nret
& IWINFO_CIPHER_WRAP
)
1096 blobmsg_add_string(buf
, NULL
, "wrap");
1098 if (nret
& IWINFO_CIPHER_AESOCB
)
1099 blobmsg_add_string(buf
, NULL
, "aes-ocb");
1101 if (nret
& IWINFO_CIPHER_CKIP
)
1102 blobmsg_add_string(buf
, NULL
, "ckip");
1104 if (!nret
|| (nret
& IWINFO_CIPHER_NONE
))
1105 blobmsg_add_string(buf
, NULL
, "none");
1107 blobmsg_close_array(buf
, a
);
1110 blobmsg_close_table(buf
, o2
);
1114 blobmsg_close_table(buf
, o
);
1121 static void rpc_luci_get_wireless_devices_cb(struct ubus_request
*req
,
1122 int type
, struct blob_attr
*msg
)
1124 struct blob_attr
*wifi
, *cur
, *iface
, *cur2
;
1125 struct reply_context
*rctx
= req
->priv
;
1126 const char *name
, *first_ifname
;
1127 int rem
, rem2
, rem3
, rem4
;
1130 blob_for_each_attr(wifi
, msg
, rem
) {
1131 if (blobmsg_type(wifi
) != BLOBMSG_TYPE_TABLE
||
1132 blobmsg_name(wifi
) == NULL
)
1135 o
= blobmsg_open_table(&rctx
->blob
, blobmsg_name(wifi
));
1137 rem2
= blobmsg_data_len(wifi
);
1138 first_ifname
= NULL
;
1140 __blob_for_each_attr(cur
, blobmsg_data(wifi
), rem2
) {
1141 name
= blobmsg_name(cur
);
1143 if (!name
|| !strcmp(name
, "iwinfo")) {
1146 else if (!strcmp(name
, "interfaces")) {
1147 if (blobmsg_type(cur
) != BLOBMSG_TYPE_ARRAY
)
1150 a
= blobmsg_open_array(&rctx
->blob
, "interfaces");
1152 rem3
= blobmsg_data_len(cur
);
1154 __blob_for_each_attr(iface
, blobmsg_data(cur
), rem3
) {
1155 if (blobmsg_type(iface
) != BLOBMSG_TYPE_TABLE
)
1158 o2
= blobmsg_open_table(&rctx
->blob
, NULL
);
1160 rem4
= blobmsg_data_len(iface
);
1163 __blob_for_each_attr(cur2
, blobmsg_data(iface
), rem4
) {
1164 if (!strcmp(blobmsg_name(cur2
), "ifname"))
1165 name
= blobmsg_get_string(cur2
);
1166 else if (!strcmp(blobmsg_name(cur2
), "iwinfo"))
1169 blobmsg_add_blob(&rctx
->blob
, cur2
);
1173 if (rpc_luci_get_iwinfo(&rctx
->blob
, name
, false))
1174 first_ifname
= first_ifname
? first_ifname
: name
;
1176 blobmsg_close_table(&rctx
->blob
, o2
);
1179 blobmsg_close_array(&rctx
->blob
, a
);
1182 blobmsg_add_blob(&rctx
->blob
, cur
);
1186 rpc_luci_get_iwinfo(&rctx
->blob
,
1187 first_ifname
? first_ifname
: blobmsg_name(wifi
),
1190 blobmsg_close_table(&rctx
->blob
, o
);
1193 finish_request(rctx
, UBUS_STATUS_OK
);
1197 rpc_luci_get_wireless_devices(struct ubus_context
*ctx
,
1198 struct ubus_object
*obj
,
1199 struct ubus_request_data
*req
,
1201 struct blob_attr
*msg
)
1203 struct reply_context
*rctx
= defer_request(ctx
, req
);
1206 return UBUS_STATUS_UNKNOWN_ERROR
;
1208 if (!invoke_ubus(ctx
, "network.wireless", "status", NULL
,
1209 rpc_luci_get_wireless_devices_cb
, rctx
))
1210 return finish_request(rctx
, UBUS_STATUS_NOT_FOUND
);
1212 return UBUS_STATUS_OK
;
1216 struct avl_node avl
;
1218 struct avl_tree ipaddrs
;
1219 struct avl_tree ip6addrs
;
1222 /* used to ignore priority with avl_find_element */
1223 #define HOST_HINT_PRIO_IGNORE -1
1225 /* higher (larger) priority addresses are listed first */
1226 #define HOST_HINT_PRIO_NL 10 /* neighbor table */
1227 #define HOST_HINT_PRIO_ETHER 50 /* /etc/ethers */
1228 #define HOST_HINT_PRIO_LEASEFILE 100 /* dhcp leasefile */
1229 #define HOST_HINT_PRIO_IFADDRS 200 /* getifaddrs() */
1230 #define HOST_HINT_PRIO_STATIC_LEASE 250 /* uci static leases */
1232 struct host_hint_addr
{
1233 struct avl_node avl
;
1238 struct in6_addr in6
;
1243 host_hint_addr_avl_cmp(const void *k1
, const void *k2
, void *ptr
)
1245 struct host_hint_addr
*a1
= (struct host_hint_addr
*)k1
;
1246 struct host_hint_addr
*a2
= (struct host_hint_addr
*)k2
;
1248 if (a1
->prio
!= a2
->prio
&&
1249 a1
->prio
!= HOST_HINT_PRIO_IGNORE
&&
1250 a2
->prio
!= HOST_HINT_PRIO_IGNORE
)
1251 return a1
->prio
< a2
->prio
? 1 : -1;
1253 if (a1
->af
!= a2
->af
)
1254 return a1
->af
< a2
->af
? -1 : 1;
1256 return memcmp(&a1
->addr
, &a2
->addr
, sizeof(a1
->addr
));
1260 nl_cb_done(struct nl_msg
*msg
, void *arg
)
1262 struct reply_context
*rctx
= arg
;
1268 nl_cb_error(struct sockaddr_nl
*nla
, struct nlmsgerr
*err
, void *arg
)
1270 struct reply_context
*rctx
= arg
;
1275 static struct host_hint
*
1276 rpc_luci_get_host_hint(struct reply_context
*rctx
, struct ether_addr
*ea
)
1278 struct host_hint
*hint
;
1285 hint
= avl_find_element(&rctx
->avl
, mac
, hint
, avl
);
1288 hint
= calloc_a(sizeof(*hint
), &p
, strlen(mac
) + 1);
1293 hint
->avl
.key
= strcpy(p
, mac
);
1294 avl_init(&hint
->ipaddrs
, host_hint_addr_avl_cmp
, false, NULL
);
1295 avl_init(&hint
->ip6addrs
, host_hint_addr_avl_cmp
, false, NULL
);
1296 avl_insert(&rctx
->avl
, &hint
->avl
);
1303 rpc_luci_add_host_hint_addr(struct host_hint
*hint
, int af
, int prio
, void *addr
)
1305 struct host_hint_addr e
, *a
;
1306 struct avl_tree
*addrs
= af
== AF_INET
? &hint
->ipaddrs
: &hint
->ip6addrs
;
1311 memset(&e
, 0, sizeof(e
));
1313 /* ignore prio when comparing against existing addresses */
1314 e
.prio
= HOST_HINT_PRIO_IGNORE
;
1317 memcpy(&e
.addr
.in
, (struct in_addr
*)addr
, sizeof(e
.addr
.in
));
1319 memcpy(&e
.addr
.in6
, (struct in6_addr
*)addr
, sizeof(e
.addr
.in6
));
1321 a
= avl_find_element(addrs
, &e
, a
, avl
);
1324 /* update prio of existing address if higher */
1325 if (prio
<= a
->prio
)
1328 avl_delete(addrs
, &a
->avl
);
1330 avl_insert(addrs
, &a
->avl
);
1334 a
= calloc(1, sizeof(*a
));
1339 memcpy(a
, &e
, sizeof(*a
));
1342 avl_insert(addrs
, &a
->avl
);
1346 rpc_luci_add_host_hint_ipaddr(struct host_hint
*hint
, int prio
, struct in_addr
*addr
)
1348 return rpc_luci_add_host_hint_addr(hint
, AF_INET
, prio
, (void *)addr
);
1352 rpc_luci_add_host_hint_ip6addr(struct host_hint
*hint
, int prio
, struct in6_addr
*addr
)
1354 return rpc_luci_add_host_hint_addr(hint
, AF_INET6
, prio
, (void *)addr
);
1357 static int nl_cb_dump_neigh(struct nl_msg
*msg
, void *arg
)
1359 struct reply_context
*rctx
= arg
;
1360 struct ether_addr
*mac
;
1361 struct in6_addr
*dst
;
1362 struct nlmsghdr
*hdr
= nlmsg_hdr(msg
);
1363 struct ndmsg
*nd
= NLMSG_DATA(hdr
);
1364 struct nlattr
*tb
[NDA_MAX
+1];
1365 struct host_hint
*hint
;
1367 rctx
->pending
= !!(hdr
->nlmsg_flags
& NLM_F_MULTI
);
1369 if (hdr
->nlmsg_type
!= RTM_NEWNEIGH
||
1370 (nd
->ndm_family
!= AF_INET
&& nd
->ndm_family
!= AF_INET6
))
1373 if (!(nd
->ndm_state
& (0xFF & ~NUD_NOARP
)))
1376 nlmsg_parse(hdr
, sizeof(*nd
), tb
, NDA_MAX
, NULL
);
1378 mac
= tb
[NDA_LLADDR
] ? RTA_DATA(tb
[NDA_LLADDR
]) : NULL
;
1379 dst
= tb
[NDA_DST
] ? RTA_DATA(tb
[NDA_DST
]) : NULL
;
1384 hint
= rpc_luci_get_host_hint(rctx
, mac
);
1389 if (nd
->ndm_family
== AF_INET
)
1390 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_NL
, (struct in_addr
*)dst
);
1392 rpc_luci_add_host_hint_ip6addr(hint
, HOST_HINT_PRIO_NL
, (struct in6_addr
*)dst
);
1398 rpc_luci_get_host_hints_nl(struct reply_context
*rctx
)
1400 struct nl_sock
*sock
= NULL
;
1401 struct nl_msg
*msg
= NULL
;
1402 struct nl_cb
*cb
= NULL
;
1403 struct ndmsg ndm
= {};
1405 sock
= nl_socket_alloc();
1410 if (nl_connect(sock
, NETLINK_ROUTE
))
1413 cb
= nl_cb_alloc(NL_CB_DEFAULT
);
1418 msg
= nlmsg_alloc_simple(RTM_GETNEIGH
, NLM_F_REQUEST
| NLM_F_DUMP
);
1423 nlmsg_append(msg
, &ndm
, sizeof(ndm
), 0);
1425 nl_cb_set(cb
, NL_CB_VALID
, NL_CB_CUSTOM
, nl_cb_dump_neigh
, rctx
);
1426 nl_cb_set(cb
, NL_CB_FINISH
, NL_CB_CUSTOM
, nl_cb_done
, rctx
);
1427 nl_cb_err(cb
, NL_CB_CUSTOM
, nl_cb_error
, rctx
);
1429 avl_init(&rctx
->avl
, avl_strcmp
, false, NULL
);
1433 nl_send_auto_complete(sock
, msg
);
1435 while (rctx
->pending
)
1436 nl_recvmsgs(sock
, cb
);
1440 nl_socket_free(sock
);
1450 rpc_luci_get_host_hints_ether(struct reply_context
*rctx
)
1452 struct host_hint
*hint
;
1457 f
= fopen("/etc/ethers", "r");
1462 while (fgets(buf
, sizeof(buf
), f
)) {
1463 p
= strtok(buf
, " \t\n");
1464 hint
= rpc_luci_get_host_hint(rctx
, p
? ether_aton(p
) : NULL
);
1469 p
= strtok(NULL
, " \t\n");
1474 if (inet_pton(AF_INET
, p
, &in
) == 1) {
1475 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_ETHER
, &in
);
1477 else if (*p
&& !hint
->hostname
) {
1478 hint
->hostname
= strdup(p
);
1486 rpc_luci_get_host_hints_uci(struct reply_context
*rctx
)
1488 struct uci_ptr ptr
= { .package
= "dhcp" };
1489 struct uci_context
*uci
= NULL
;
1490 struct uci_package
*pkg
= NULL
;
1491 struct lease_entry
*lease
;
1492 struct host_hint
*hint
;
1493 struct uci_element
*e
, *l
;
1494 struct uci_section
*s
;
1499 uci
= uci_alloc_context();
1504 uci_load(uci
, ptr
.package
, &pkg
);
1509 uci_foreach_element(&pkg
->sections
, e
)
1511 s
= uci_to_section(e
);
1513 if (strcmp(s
->type
, "host"))
1516 ptr
.section
= s
->e
.name
;
1522 if (!uci_lookup_ptr(uci
, &ptr
, NULL
, true) && ptr
.o
!= NULL
&&
1523 ptr
.o
->type
!= UCI_TYPE_STRING
)
1524 n
= ptr
.o
->v
.string
;
1528 if (!n
|| inet_pton(AF_INET
, n
, &in
) != 1)
1531 ptr
.option
= "name";
1534 if (!uci_lookup_ptr(uci
, &ptr
, NULL
, true) && ptr
.o
!= NULL
&&
1535 ptr
.o
->type
== UCI_TYPE_STRING
)
1536 n
= ptr
.o
->v
.string
;
1543 if (uci_lookup_ptr(uci
, &ptr
, NULL
, true) || ptr
.o
== NULL
)
1546 if (ptr
.o
->type
== UCI_TYPE_STRING
) {
1547 for (p
= strtok(ptr
.o
->v
.string
, " \t");
1549 p
= strtok(NULL
, " \t")) {
1550 hint
= rpc_luci_get_host_hint(rctx
, ether_aton(p
));
1556 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_STATIC_LEASE
, &in
);
1558 if (n
&& !hint
->hostname
)
1559 hint
->hostname
= strdup(n
);
1562 else if (ptr
.o
->type
== UCI_TYPE_LIST
) {
1563 uci_foreach_element(&ptr
.o
->v
.list
, l
) {
1564 hint
= rpc_luci_get_host_hint(rctx
, ether_aton(l
->name
));
1570 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_STATIC_LEASE
, &in
);
1572 if (n
&& !hint
->hostname
)
1573 hint
->hostname
= strdup(n
);
1580 while ((lease
= lease_next()) != NULL
) {
1581 if (ea_empty(&lease
->mac
))
1584 hint
= rpc_luci_get_host_hint(rctx
, &lease
->mac
);
1589 for (i
= 0; i
< lease
->n_addr
; i
++) {
1590 if (lease
->af
== AF_INET
)
1591 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_LEASEFILE
, &lease
->addr
[i
].in
);
1592 else if (lease
->af
== AF_INET6
)
1593 rpc_luci_add_host_hint_ip6addr(hint
, HOST_HINT_PRIO_LEASEFILE
, &lease
->addr
[i
].in6
);
1596 if (lease
->hostname
&& !hint
->hostname
)
1597 hint
->hostname
= strdup(lease
->hostname
);
1604 uci_free_context(uci
);
1608 rpc_luci_get_host_hints_ifaddrs(struct reply_context
*rctx
)
1610 struct ifaddrs
*ifaddr
, *ifa
;
1611 struct sockaddr_ll
*sll
;
1612 struct avl_tree devices
;
1613 struct host_hint
*hint
;
1615 struct avl_node avl
;
1616 struct ether_addr ea
;
1617 struct in6_addr in6
;
1619 } *device
, *nextdevice
;
1622 avl_init(&devices
, avl_strcmp
, false, NULL
);
1624 if (getifaddrs(&ifaddr
) == -1)
1627 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
1631 device
= avl_find_element(&devices
, ifa
->ifa_name
, device
, avl
);
1634 device
= calloc_a(sizeof(*device
), &p
, strlen(ifa
->ifa_name
) + 1);
1639 device
->avl
.key
= strcpy(p
, ifa
->ifa_name
);
1640 avl_insert(&devices
, &device
->avl
);
1643 switch (ifa
->ifa_addr
->sa_family
) {
1645 sll
= (struct sockaddr_ll
*)ifa
->ifa_addr
;
1647 if (sll
->sll_halen
== 6)
1648 memcpy(&device
->ea
, sll
->sll_addr
, 6);
1653 device
->in6
= ((struct sockaddr_in6
*)ifa
->ifa_addr
)->sin6_addr
;
1657 device
->in
= ((struct sockaddr_in
*)ifa
->ifa_addr
)->sin_addr
;
1662 freeifaddrs(ifaddr
);
1664 avl_remove_all_elements(&devices
, device
, avl
, nextdevice
) {
1665 if (!ea_empty(&device
->ea
) &&
1666 (!IN6_IS_ADDR_UNSPECIFIED(&device
->in6
) ||
1667 device
->in
.s_addr
!= 0)) {
1668 hint
= rpc_luci_get_host_hint(rctx
, &device
->ea
);
1671 if (device
->in
.s_addr
!= 0)
1672 rpc_luci_add_host_hint_ipaddr(hint
, HOST_HINT_PRIO_IFADDRS
, &device
->in
);
1674 if (!IN6_IS_ADDR_UNSPECIFIED(&device
->in6
))
1675 rpc_luci_add_host_hint_ip6addr(hint
, HOST_HINT_PRIO_IFADDRS
, &device
->in6
);
1684 rpc_luci_get_host_hints_finish(struct reply_context
*rctx
);
1687 rpc_luci_get_host_hints_rrdns_cb(struct ubus_request
*req
, int type
,
1688 struct blob_attr
*msg
)
1690 struct reply_context
*rctx
= req
->priv
;
1691 struct host_hint_addr
*addr
;
1692 struct host_hint
*hint
;
1693 struct blob_attr
*cur
;
1694 struct in6_addr in6
;
1699 blob_for_each_attr(cur
, msg
, rem
) {
1700 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
)
1703 if (inet_pton(AF_INET6
, blobmsg_name(cur
), &in6
) == 1) {
1704 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1705 avl_for_each_element(&hint
->ip6addrs
, addr
, avl
) {
1706 if (!memcmp(&addr
->addr
.in6
, &in6
, sizeof(in6
))) {
1707 if (!hint
->hostname
)
1708 hint
->hostname
= strdup(blobmsg_get_string(cur
));
1715 else if (inet_pton(AF_INET
, blobmsg_name(cur
), &in
) == 1) {
1716 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1717 avl_for_each_element(&hint
->ipaddrs
, addr
, avl
) {
1718 if (addr
->addr
.in
.s_addr
== in
.s_addr
) {
1719 free(hint
->hostname
);
1720 hint
->hostname
= strdup(blobmsg_get_string(cur
));
1729 rpc_luci_get_host_hints_finish(rctx
);
1733 rpc_luci_get_host_hints_rrdns(struct reply_context
*rctx
)
1735 char buf
[INET6_ADDRSTRLEN
];
1736 struct blob_buf req
= {};
1737 struct host_hint
*hint
;
1738 struct host_hint_addr
*addr
;
1742 blob_buf_init(&req
, 0);
1744 a
= blobmsg_open_array(&req
, "addrs");
1746 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1747 avl_for_each_element(&hint
->ipaddrs
, addr
, avl
) {
1748 if (addr
->addr
.in
.s_addr
!= 0) {
1749 inet_ntop(AF_INET
, &addr
->addr
.in
, buf
, sizeof(buf
));
1750 blobmsg_add_string(&req
, NULL
, buf
);
1754 avl_for_each_element(&hint
->ip6addrs
, addr
, avl
) {
1755 if (!IN6_IS_ADDR_UNSPECIFIED(&addr
->addr
.in6
) &&
1756 !IN6_IS_ADDR_LINKLOCAL(&addr
->addr
.in6
) &&
1757 !IN6_IS_ADDR_ULA(&addr
->addr
.in6
)) {
1758 inet_ntop(AF_INET6
, &addr
->addr
.in6
, buf
, sizeof(buf
));
1759 blobmsg_add_string(&req
, NULL
, buf
);
1765 blobmsg_close_array(&req
, a
);
1768 blobmsg_add_u32(&req
, "timeout", 250);
1769 blobmsg_add_u32(&req
, "limit", n
);
1771 if (!invoke_ubus(rctx
->context
, "network.rrdns", "lookup", &req
,
1772 rpc_luci_get_host_hints_rrdns_cb
, rctx
))
1773 rpc_luci_get_host_hints_finish(rctx
);
1776 rpc_luci_get_host_hints_finish(rctx
);
1779 blob_buf_free(&req
);
1783 rpc_luci_get_host_hints_finish(struct reply_context
*rctx
)
1785 struct host_hint
*hint
, *nexthint
;
1786 struct host_hint_addr
*addr
, *nextaddr
;
1787 char buf
[INET6_ADDRSTRLEN
];
1788 struct in6_addr in6
= {};
1791 avl_remove_all_elements(&rctx
->avl
, hint
, avl
, nexthint
) {
1792 o
= blobmsg_open_table(&rctx
->blob
, hint
->avl
.key
);
1794 a
= blobmsg_open_array(&rctx
->blob
, "ipaddrs");
1796 avl_remove_all_elements(&hint
->ipaddrs
, addr
, avl
, nextaddr
) {
1797 if (addr
->addr
.in
.s_addr
!= 0) {
1798 inet_ntop(AF_INET
, &addr
->addr
.in
, buf
, sizeof(buf
));
1799 blobmsg_add_string(&rctx
->blob
, NULL
, buf
);
1805 blobmsg_close_array(&rctx
->blob
, a
);
1807 a
= blobmsg_open_array(&rctx
->blob
, "ip6addrs");
1809 avl_remove_all_elements(&hint
->ip6addrs
, addr
, avl
, nextaddr
) {
1810 if (memcmp(&addr
->addr
.in6
, &in6
, sizeof(in6
))) {
1811 inet_ntop(AF_INET6
, &addr
->addr
.in6
, buf
, sizeof(buf
));
1812 blobmsg_add_string(&rctx
->blob
, NULL
, buf
);
1818 blobmsg_close_array(&rctx
->blob
, a
);
1821 blobmsg_add_string(&rctx
->blob
, "name", hint
->hostname
);
1823 blobmsg_close_table(&rctx
->blob
, o
);
1826 free(hint
->hostname
);
1831 return finish_request(rctx
, UBUS_STATUS_OK
);
1835 rpc_luci_get_host_hints(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1836 struct ubus_request_data
*req
, const char *method
,
1837 struct blob_attr
*msg
)
1839 struct reply_context
*rctx
= defer_request(ctx
, req
);
1842 return UBUS_STATUS_UNKNOWN_ERROR
;
1844 rpc_luci_get_host_hints_nl(rctx
);
1845 rpc_luci_get_host_hints_uci(rctx
);
1846 rpc_luci_get_host_hints_ether(rctx
);
1847 rpc_luci_get_host_hints_ifaddrs(rctx
);
1848 rpc_luci_get_host_hints_rrdns(rctx
);
1850 return UBUS_STATUS_OK
;
1854 rpc_luci_get_duid_hints(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1855 struct ubus_request_data
*req
, const char *method
,
1856 struct blob_attr
*msg
)
1858 struct { struct avl_node avl
; } *e
, *next
;
1859 char s
[INET6_ADDRSTRLEN
], *p
;
1860 struct lease_entry
*lease
;
1861 struct avl_tree avl
;
1865 avl_init(&avl
, avl_strcmp
, false, NULL
);
1866 blob_buf_init(&blob
, 0);
1870 while ((lease
= lease_next()) != NULL
) {
1871 if (lease
->af
!= AF_INET6
|| lease
->duid
== NULL
)
1874 e
= avl_find_element(&avl
, lease
->duid
, e
, avl
);
1879 e
= calloc_a(sizeof(*e
), &p
, strlen(lease
->duid
) + 1);
1884 o
= blobmsg_open_table(&blob
, lease
->duid
);
1886 inet_ntop(AF_INET6
, &lease
->addr
[0].in6
, s
, sizeof(s
));
1887 blobmsg_add_string(&blob
, "ip6addr", s
);
1889 a
= blobmsg_open_array(&blob
, "ip6addrs");
1891 for (n
= 0; n
< lease
->n_addr
; n
++) {
1892 inet_ntop(AF_INET6
, &lease
->addr
[n
].in6
, s
, sizeof(s
));
1893 blobmsg_add_string(&blob
, NULL
, s
);
1896 blobmsg_close_array(&blob
, a
);
1898 if (lease
->hostname
)
1899 blobmsg_add_string(&blob
, "hostname", lease
->hostname
);
1901 if (!ea_empty(&lease
->mac
))
1902 blobmsg_add_string(&blob
, "macaddr", ea2str(&lease
->mac
));
1904 blobmsg_close_table(&blob
, o
);
1906 e
->avl
.key
= strcpy(p
, lease
->duid
);
1907 avl_insert(&avl
, &e
->avl
);
1912 avl_remove_all_elements(&avl
, e
, avl
, next
) {
1916 ubus_send_reply(ctx
, req
, blob
.head
);
1918 return UBUS_STATUS_OK
;
1922 rpc_luci_get_board_json(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1923 struct ubus_request_data
*req
, const char *method
,
1924 struct blob_attr
*msg
)
1926 blob_buf_init(&blob
, 0);
1928 if (!blobmsg_add_json_from_file(&blob
, "/etc/board.json"))
1929 return UBUS_STATUS_UNKNOWN_ERROR
;
1931 ubus_send_reply(ctx
, req
, blob
.head
);
1932 return UBUS_STATUS_OK
;
1940 static const struct blobmsg_policy rpc_get_leases_policy
[__RPC_L_MAX
] = {
1941 [RPC_L_FAMILY
] = { .name
= "family", .type
= BLOBMSG_TYPE_INT32
}
1945 rpc_luci_get_dhcp_leases(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1946 struct ubus_request_data
*req
, const char *method
,
1947 struct blob_attr
*msg
)
1949 char s
[INET6_ADDRSTRLEN
+ strlen("/128")];
1950 struct blob_attr
*tb
[__RPC_L_MAX
];
1951 struct lease_entry
*lease
;
1957 blobmsg_parse(rpc_get_leases_policy
, __RPC_L_MAX
, tb
,
1958 blob_data(msg
), blob_len(msg
));
1960 switch (tb
[RPC_L_FAMILY
] ? blobmsg_get_u32(tb
[RPC_L_FAMILY
]) : 0) {
1974 return UBUS_STATUS_INVALID_ARGUMENT
;
1977 blob_buf_init(&blob
, 0);
1979 for (af
= family
? family
: AF_INET
;
1981 af
= (family
== 0) ? (af
== AF_INET
? AF_INET6
: 0) : 0) {
1983 a
= blobmsg_open_array(&blob
, (af
== AF_INET
) ? "dhcp_leases"
1988 while ((lease
= lease_next()) != NULL
) {
1989 if (lease
->af
!= af
)
1992 o
= blobmsg_open_table(&blob
, NULL
);
1994 if (lease
->expire
== -1)
1995 blobmsg_add_u8(&blob
, "expires", 0);
1997 blobmsg_add_u32(&blob
, "expires", lease
->expire
);
1999 if (lease
->hostname
)
2000 blobmsg_add_string(&blob
, "hostname", lease
->hostname
);
2002 if (!ea_empty(&lease
->mac
))
2003 blobmsg_add_string(&blob
, "macaddr", ea2str(&lease
->mac
));
2006 blobmsg_add_string(&blob
, "duid", lease
->duid
);
2008 inet_ntop(lease
->af
, &lease
->addr
[0].in6
, s
, sizeof(s
));
2009 blobmsg_add_string(&blob
, (af
== AF_INET
) ? "ipaddr" : "ip6addr", s
);
2011 if (af
== AF_INET6
) {
2012 a2
= blobmsg_open_array(&blob
, "ip6addrs");
2014 for (n
= 0; n
< lease
->n_addr
; n
++) {
2015 inet_ntop(lease
->af
, &lease
->addr
[n
].in6
, s
, sizeof(s
));
2018 snprintf(s
+ l
, sizeof(s
) - l
, "/%hhu", lease
->mask
);
2020 blobmsg_add_string(&blob
, NULL
, s
);
2023 blobmsg_close_array(&blob
, a2
);
2026 blobmsg_close_table(&blob
, o
);
2031 blobmsg_close_array(&blob
, a
);
2034 ubus_send_reply(ctx
, req
, blob
.head
);
2036 return UBUS_STATUS_OK
;
2040 rpc_luci_api_init(const struct rpc_daemon_ops
*o
, struct ubus_context
*ctx
)
2042 static const struct ubus_method luci_methods
[] = {
2043 UBUS_METHOD_NOARG("getNetworkDevices", rpc_luci_get_network_devices
),
2044 UBUS_METHOD_NOARG("getWirelessDevices", rpc_luci_get_wireless_devices
),
2045 UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints
),
2046 UBUS_METHOD_NOARG("getDUIDHints", rpc_luci_get_duid_hints
),
2047 UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json
),
2048 UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases
, rpc_get_leases_policy
)
2051 static struct ubus_object_type luci_type
=
2052 UBUS_OBJECT_TYPE("rpcd-luci", luci_methods
);
2054 static struct ubus_object obj
= {
2057 .methods
= luci_methods
,
2058 .n_methods
= ARRAY_SIZE(luci_methods
),
2061 return ubus_add_object(ctx
, &obj
);
2064 struct rpc_plugin rpc_plugin
= {
2065 .init
= rpc_luci_api_init