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 for (af
= AF_INET
; af
!= 0; af
= (af
== AF_INET
) ? AF_INET6
: 0) {
663 a
= blobmsg_open_array(&blob
,
664 (af
== AF_INET
) ? "ipaddrs" : "ip6addrs");
666 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
667 if (ifa
->ifa_addr
== NULL
|| ifa
->ifa_addr
->sa_family
!= af
)
670 if (strcmp(ifa
->ifa_name
, name
))
673 o2
= blobmsg_open_table(&blob
, NULL
);
675 blobmsg_add_string(&blob
, "address",
676 sa2str(ifa
->ifa_addr
));
678 blobmsg_add_string(&blob
, "netmask",
679 sa2str(ifa
->ifa_netmask
));
681 if (ifa
->ifa_dstaddr
&& (ifa
->ifa_flags
& IFF_POINTOPOINT
))
682 blobmsg_add_string(&blob
, "remote",
683 sa2str(ifa
->ifa_dstaddr
));
684 else if (ifa
->ifa_broadaddr
&& (ifa
->ifa_flags
& IFF_BROADCAST
))
685 blobmsg_add_string(&blob
, "broadcast",
686 sa2str(ifa
->ifa_broadaddr
));
688 blobmsg_close_table(&blob
, o2
);
690 ifa_flags
|= ifa
->ifa_flags
;
693 blobmsg_close_array(&blob
, a
);
696 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
697 if (ifa
->ifa_addr
== NULL
|| ifa
->ifa_addr
->sa_family
!= AF_PACKET
)
700 if (strcmp(ifa
->ifa_name
, name
))
703 sll
= (struct sockaddr_ll
*)ifa
->ifa_addr
;
705 if (sll
->sll_hatype
== 1)
706 blobmsg_add_string(&blob
, "mac", sa2str(ifa
->ifa_addr
));
708 blobmsg_add_u32(&blob
, "type", sll
->sll_hatype
);
709 blobmsg_add_u32(&blob
, "ifindex", sll
->sll_ifindex
);
711 ifa_flags
|= ifa
->ifa_flags
;
715 o2
= blobmsg_open_table(&blob
, "stats");
717 for (n
= 0; n
< ARRAY_SIZE(stats
); n
++) {
718 v
= strtoull(readstr("/sys/class/net/%s/statistics/%s",
719 name
, stats
[n
]), NULL
, 10);
721 blobmsg_add_u64(&blob
, stats
[n
], v
);
724 blobmsg_close_table(&blob
, o2
);
726 o2
= blobmsg_open_table(&blob
, "flags");
727 blobmsg_add_u8(&blob
, "up", ifa_flags
& IFF_UP
);
728 blobmsg_add_u8(&blob
, "broadcast", ifa_flags
& IFF_BROADCAST
);
729 blobmsg_add_u8(&blob
, "promisc", ifa_flags
& IFF_PROMISC
);
730 blobmsg_add_u8(&blob
, "loopback", ifa_flags
& IFF_LOOPBACK
);
731 blobmsg_add_u8(&blob
, "noarp", ifa_flags
& IFF_NOARP
);
732 blobmsg_add_u8(&blob
, "multicast", ifa_flags
& IFF_MULTICAST
);
733 blobmsg_add_u8(&blob
, "pointtopoint", ifa_flags
& IFF_POINTOPOINT
);
734 blobmsg_close_table(&blob
, o2
);
736 blobmsg_close_table(&blob
, o
);
740 rpc_luci_get_network_devices(struct ubus_context
*ctx
,
741 struct ubus_object
*obj
,
742 struct ubus_request_data
*req
,
744 struct blob_attr
*msg
)
746 struct ifaddrs
*ifaddr
;
750 blob_buf_init(&blob
, 0);
752 d
= opendir("/sys/class/net");
755 if (getifaddrs(&ifaddr
) == 1)
764 if (strcmp(e
->d_name
, ".") && strcmp(e
->d_name
, ".."))
765 rpc_luci_parse_network_device_sys(e
->d_name
, ifaddr
);
774 ubus_send_reply(ctx
, req
, blob
.head
);
780 iw_call_str(int (*method
)(const char *, char *), const char *dev
,
781 struct blob_buf
*blob
, const char *field
)
783 char buf
[IWINFO_BUFSIZE
] = {};
785 if (method(dev
, buf
) == 0)
786 blobmsg_add_string(blob
, field
, buf
);
790 iw_call_num(int (*method
)(const char *, int *), const char *dev
,
791 struct blob_buf
*blob
, const char *field
)
795 if (method(dev
, &val
) == 0)
796 blobmsg_add_u32(blob
, field
, val
);
799 static bool rpc_luci_get_iwinfo(struct blob_buf
*buf
, const char *devname
,
802 struct iwinfo_crypto_entry crypto
= {};
803 struct iwinfo_hardware_id ids
= {};
804 const struct iwinfo_ops
*iw
;
810 if (!iw_backend
|| !iw_close
|| !iw_modenames
) {
811 if (glob("/usr/lib/libiwinfo.so*", 0, NULL
, &paths
) != 0)
814 for (i
= 0; i
< paths
.gl_pathc
&& !iwlib
; i
++)
815 iwlib
= dlopen(paths
.gl_pathv
[i
], RTLD_LAZY
| RTLD_LOCAL
);
822 iw_backend
= dlsym(iwlib
, "iwinfo_backend");
823 iw_close
= dlsym(iwlib
, "iwinfo_close");
824 iw_modenames
= dlsym(iwlib
, "IWINFO_OPMODE_NAMES");
826 if (!iw_backend
|| !iw_close
|| !iw_modenames
)
830 iw
= iw_backend(devname
);
835 o
= blobmsg_open_table(buf
, "iwinfo");
837 iw_call_num(iw
->signal
, devname
, buf
, "signal");
838 iw_call_num(iw
->noise
, devname
, buf
, "noise");
839 iw_call_num(iw
->channel
, devname
, buf
, "channel");
840 iw_call_str(iw
->country
, devname
, buf
, "country");
841 iw_call_str(iw
->phyname
, devname
, buf
, "phy");
842 iw_call_num(iw
->txpower
, devname
, buf
, "txpower");
843 iw_call_num(iw
->txpower_offset
, devname
, buf
, "txpower_offset");
844 iw_call_num(iw
->frequency
, devname
, buf
, "frequency");
845 iw_call_num(iw
->frequency_offset
, devname
, buf
, "frequency_offset");
847 if (!iw
->hwmodelist(devname
, &nret
)) {
848 a
= blobmsg_open_array(buf
, "hwmodes");
850 if (nret
& IWINFO_80211_AC
)
851 blobmsg_add_string(buf
, NULL
, "ac");
853 if (nret
& IWINFO_80211_A
)
854 blobmsg_add_string(buf
, NULL
, "a");
856 if (nret
& IWINFO_80211_B
)
857 blobmsg_add_string(buf
, NULL
, "b");
859 if (nret
& IWINFO_80211_G
)
860 blobmsg_add_string(buf
, NULL
, "g");
862 if (nret
& IWINFO_80211_N
)
863 blobmsg_add_string(buf
, NULL
, "n");
865 blobmsg_close_array(buf
, a
);
868 if (!iw
->htmodelist(devname
, &nret
)) {
869 a
= blobmsg_open_array(buf
, "htmodes");
871 if (nret
& IWINFO_HTMODE_HT20
)
872 blobmsg_add_string(buf
, NULL
, "HT20");
874 if (nret
& IWINFO_HTMODE_HT40
)
875 blobmsg_add_string(buf
, NULL
, "HT40");
877 if (nret
& IWINFO_HTMODE_VHT20
)
878 blobmsg_add_string(buf
, NULL
, "VHT20");
880 if (nret
& IWINFO_HTMODE_VHT40
)
881 blobmsg_add_string(buf
, NULL
, "VHT40");
883 if (nret
& IWINFO_HTMODE_VHT80
)
884 blobmsg_add_string(buf
, NULL
, "VHT80");
886 if (nret
& IWINFO_HTMODE_VHT80_80
)
887 blobmsg_add_string(buf
, NULL
, "VHT80+80");
889 if (nret
& IWINFO_HTMODE_VHT160
)
890 blobmsg_add_string(buf
, NULL
, "VHT160");
892 blobmsg_close_array(buf
, a
);
895 if (!iw
->hardware_id(devname
, (char *)&ids
)) {
896 o2
= blobmsg_open_table(buf
, "hardware");
898 a
= blobmsg_open_array(buf
, "id");
899 blobmsg_add_u32(buf
, NULL
, ids
.vendor_id
);
900 blobmsg_add_u32(buf
, NULL
, ids
.device_id
);
901 blobmsg_add_u32(buf
, NULL
, ids
.subsystem_vendor_id
);
902 blobmsg_add_u32(buf
, NULL
, ids
.subsystem_device_id
);
903 blobmsg_close_array(buf
, a
);
905 iw_call_str(iw
->hardware_name
, devname
, buf
, "name");
907 blobmsg_close_table(buf
, o2
);
911 iw_call_num(iw
->quality
, devname
, buf
, "quality");
912 iw_call_num(iw
->quality_max
, devname
, buf
, "quality_max");
913 iw_call_num(iw
->bitrate
, devname
, buf
, "bitrate");
915 if (!iw
->mode(devname
, &nret
))
916 blobmsg_add_string(buf
, "mode", iw_modenames
[nret
]);
918 iw_call_str(iw
->ssid
, devname
, buf
, "ssid");
919 iw_call_str(iw
->bssid
, devname
, buf
, "bssid");
921 if (!iw
->encryption(devname
, (char *)&crypto
)) {
922 o2
= blobmsg_open_table(buf
, "encryption");
924 blobmsg_add_u8(buf
, "enabled", crypto
.enabled
);
926 if (crypto
.enabled
) {
927 if (!crypto
.wpa_version
) {
928 a
= blobmsg_open_array(buf
, "wep");
930 if (crypto
.auth_algs
& IWINFO_AUTH_OPEN
)
931 blobmsg_add_string(buf
, NULL
, "open");
933 if (crypto
.auth_algs
& IWINFO_AUTH_SHARED
)
934 blobmsg_add_string(buf
, NULL
, "shared");
936 blobmsg_close_array(buf
, a
);
939 a
= blobmsg_open_array(buf
, "wpa");
941 for (nret
= 1; nret
<= 3; nret
++)
942 if (crypto
.wpa_version
& (1 << (nret
- 1)))
943 blobmsg_add_u32(buf
, NULL
, nret
);
945 blobmsg_close_array(buf
, a
);
947 a
= blobmsg_open_array(buf
, "authentication");
949 if (crypto
.auth_suites
& IWINFO_KMGMT_PSK
)
950 blobmsg_add_string(buf
, NULL
, "psk");
952 if (crypto
.auth_suites
& IWINFO_KMGMT_8021x
)
953 blobmsg_add_string(buf
, NULL
, "802.1x");
955 if (crypto
.auth_suites
& IWINFO_KMGMT_SAE
)
956 blobmsg_add_string(buf
, NULL
, "sae");
958 if (crypto
.auth_suites
& IWINFO_KMGMT_OWE
)
959 blobmsg_add_string(buf
, NULL
, "owe");
961 if (!crypto
.auth_suites
||
962 (crypto
.auth_suites
& IWINFO_KMGMT_NONE
))
963 blobmsg_add_string(buf
, NULL
, "none");
965 blobmsg_close_array(buf
, a
);
968 a
= blobmsg_open_array(buf
, "ciphers");
969 nret
= crypto
.pair_ciphers
| crypto
.group_ciphers
;
971 if (nret
& IWINFO_CIPHER_WEP40
)
972 blobmsg_add_string(buf
, NULL
, "wep-40");
974 if (nret
& IWINFO_CIPHER_WEP104
)
975 blobmsg_add_string(buf
, NULL
, "wep-104");
977 if (nret
& IWINFO_CIPHER_TKIP
)
978 blobmsg_add_string(buf
, NULL
, "tkip");
980 if (nret
& IWINFO_CIPHER_CCMP
)
981 blobmsg_add_string(buf
, NULL
, "ccmp");
983 if (nret
& IWINFO_CIPHER_WRAP
)
984 blobmsg_add_string(buf
, NULL
, "wrap");
986 if (nret
& IWINFO_CIPHER_AESOCB
)
987 blobmsg_add_string(buf
, NULL
, "aes-ocb");
989 if (nret
& IWINFO_CIPHER_CKIP
)
990 blobmsg_add_string(buf
, NULL
, "ckip");
992 if (!nret
|| (nret
& IWINFO_CIPHER_NONE
))
993 blobmsg_add_string(buf
, NULL
, "none");
995 blobmsg_close_array(buf
, a
);
998 blobmsg_close_table(buf
, o2
);
1002 blobmsg_close_table(buf
, o
);
1009 static void rpc_luci_get_wireless_devices_cb(struct ubus_request
*req
,
1010 int type
, struct blob_attr
*msg
)
1012 struct blob_attr
*wifi
, *cur
, *iface
, *cur2
;
1013 struct reply_context
*rctx
= req
->priv
;
1014 const char *name
, *first_ifname
;
1015 int rem
, rem2
, rem3
, rem4
;
1018 blob_for_each_attr(wifi
, msg
, rem
) {
1019 if (blobmsg_type(wifi
) != BLOBMSG_TYPE_TABLE
||
1020 blobmsg_name(wifi
) == NULL
)
1023 o
= blobmsg_open_table(&rctx
->blob
, blobmsg_name(wifi
));
1025 rem2
= blobmsg_data_len(wifi
);
1026 first_ifname
= NULL
;
1028 __blob_for_each_attr(cur
, blobmsg_data(wifi
), rem2
) {
1029 name
= blobmsg_name(cur
);
1031 if (!name
|| !strcmp(name
, "iwinfo")) {
1034 else if (!strcmp(name
, "interfaces")) {
1035 if (blobmsg_type(cur
) != BLOBMSG_TYPE_ARRAY
)
1038 a
= blobmsg_open_array(&rctx
->blob
, "interfaces");
1040 rem3
= blobmsg_data_len(cur
);
1042 __blob_for_each_attr(iface
, blobmsg_data(cur
), rem3
) {
1043 if (blobmsg_type(iface
) != BLOBMSG_TYPE_TABLE
)
1046 o2
= blobmsg_open_table(&rctx
->blob
, NULL
);
1048 rem4
= blobmsg_data_len(iface
);
1051 __blob_for_each_attr(cur2
, blobmsg_data(iface
), rem4
) {
1052 if (!strcmp(blobmsg_name(cur2
), "ifname"))
1053 name
= blobmsg_get_string(cur2
);
1054 else if (!strcmp(blobmsg_name(cur2
), "iwinfo"))
1057 blobmsg_add_blob(&rctx
->blob
, cur2
);
1061 if (rpc_luci_get_iwinfo(&rctx
->blob
, name
, false))
1062 first_ifname
= first_ifname
? first_ifname
: name
;
1064 blobmsg_close_table(&rctx
->blob
, o2
);
1067 blobmsg_close_array(&rctx
->blob
, a
);
1070 blobmsg_add_blob(&rctx
->blob
, cur
);
1074 rpc_luci_get_iwinfo(&rctx
->blob
,
1075 first_ifname
? first_ifname
: blobmsg_name(wifi
),
1078 blobmsg_close_table(&rctx
->blob
, o
);
1081 finish_request(rctx
, UBUS_STATUS_OK
);
1085 rpc_luci_get_wireless_devices(struct ubus_context
*ctx
,
1086 struct ubus_object
*obj
,
1087 struct ubus_request_data
*req
,
1089 struct blob_attr
*msg
)
1091 struct reply_context
*rctx
= defer_request(ctx
, req
);
1094 return UBUS_STATUS_UNKNOWN_ERROR
;
1096 if (!invoke_ubus(ctx
, "network.wireless", "status", NULL
,
1097 rpc_luci_get_wireless_devices_cb
, rctx
))
1098 return finish_request(rctx
, UBUS_STATUS_NOT_FOUND
);
1100 return UBUS_STATUS_OK
;
1104 struct avl_node avl
;
1107 struct in6_addr ip6
;
1111 nl_cb_done(struct nl_msg
*msg
, void *arg
)
1113 struct reply_context
*rctx
= arg
;
1119 nl_cb_error(struct sockaddr_nl
*nla
, struct nlmsgerr
*err
, void *arg
)
1121 struct reply_context
*rctx
= arg
;
1126 static struct host_hint
*
1127 rpc_luci_get_host_hint(struct reply_context
*rctx
, struct ether_addr
*ea
)
1129 struct host_hint
*hint
;
1136 hint
= avl_find_element(&rctx
->avl
, mac
, hint
, avl
);
1139 hint
= calloc_a(sizeof(*hint
), &p
, strlen(mac
) + 1);
1144 hint
->avl
.key
= strcpy(p
, mac
);
1145 avl_insert(&rctx
->avl
, &hint
->avl
);
1151 static int nl_cb_dump_neigh(struct nl_msg
*msg
, void *arg
)
1153 struct reply_context
*rctx
= arg
;
1154 struct ether_addr
*mac
;
1155 struct in6_addr
*dst
;
1156 struct nlmsghdr
*hdr
= nlmsg_hdr(msg
);
1157 struct ndmsg
*nd
= NLMSG_DATA(hdr
);
1158 struct nlattr
*tb
[NDA_MAX
+1];
1159 struct host_hint
*hint
;
1161 rctx
->pending
= !!(hdr
->nlmsg_flags
& NLM_F_MULTI
);
1163 if (hdr
->nlmsg_type
!= RTM_NEWNEIGH
||
1164 (nd
->ndm_family
!= AF_INET
&& nd
->ndm_family
!= AF_INET6
))
1167 if (!(nd
->ndm_state
& (0xFF & ~NUD_NOARP
)))
1170 nlmsg_parse(hdr
, sizeof(*nd
), tb
, NDA_MAX
, NULL
);
1172 mac
= tb
[NDA_LLADDR
] ? RTA_DATA(tb
[NDA_LLADDR
]) : NULL
;
1173 dst
= tb
[NDA_DST
] ? RTA_DATA(tb
[NDA_DST
]) : NULL
;
1178 hint
= rpc_luci_get_host_hint(rctx
, mac
);
1183 if (nd
->ndm_family
== AF_INET
)
1184 hint
->ip
= *(struct in_addr
*)dst
;
1186 hint
->ip6
= *(struct in6_addr
*)dst
;
1192 rpc_luci_get_host_hints_nl(struct reply_context
*rctx
)
1194 struct nl_sock
*sock
= NULL
;
1195 struct nl_msg
*msg
= NULL
;
1196 struct nl_cb
*cb
= NULL
;
1197 struct ndmsg ndm
= {};
1199 sock
= nl_socket_alloc();
1204 if (nl_connect(sock
, NETLINK_ROUTE
))
1207 cb
= nl_cb_alloc(NL_CB_DEFAULT
);
1212 msg
= nlmsg_alloc_simple(RTM_GETNEIGH
, NLM_F_REQUEST
| NLM_F_DUMP
);
1217 nlmsg_append(msg
, &ndm
, sizeof(ndm
), 0);
1219 nl_cb_set(cb
, NL_CB_VALID
, NL_CB_CUSTOM
, nl_cb_dump_neigh
, rctx
);
1220 nl_cb_set(cb
, NL_CB_FINISH
, NL_CB_CUSTOM
, nl_cb_done
, rctx
);
1221 nl_cb_err(cb
, NL_CB_CUSTOM
, nl_cb_error
, rctx
);
1223 avl_init(&rctx
->avl
, avl_strcmp
, false, NULL
);
1227 nl_send_auto_complete(sock
, msg
);
1229 while (rctx
->pending
)
1230 nl_recvmsgs(sock
, cb
);
1234 nl_socket_free(sock
);
1244 rpc_luci_get_host_hints_ether(struct reply_context
*rctx
)
1246 struct host_hint
*hint
;
1251 f
= fopen("/etc/ethers", "r");
1256 while (fgets(buf
, sizeof(buf
), f
)) {
1257 p
= strtok(buf
, " \t\n");
1258 hint
= rpc_luci_get_host_hint(rctx
, p
? ether_aton(p
) : NULL
);
1263 p
= strtok(NULL
, " \t\n");
1268 if (inet_pton(AF_INET
, p
, &in
) == 1) {
1269 if (hint
->ip
.s_addr
== 0)
1272 else if (*p
&& !hint
->hostname
) {
1273 hint
->hostname
= strdup(p
);
1281 rpc_luci_get_host_hints_uci(struct reply_context
*rctx
)
1283 struct uci_ptr ptr
= { .package
= "dhcp" };
1284 struct uci_context
*uci
= NULL
;
1285 struct uci_package
*pkg
= NULL
;
1286 struct in6_addr empty
= {};
1287 struct lease_entry
*lease
;
1288 struct host_hint
*hint
;
1289 struct uci_element
*e
, *l
;
1290 struct uci_section
*s
;
1294 uci
= uci_alloc_context();
1299 uci_load(uci
, ptr
.package
, &pkg
);
1304 uci_foreach_element(&pkg
->sections
, e
)
1306 s
= uci_to_section(e
);
1308 if (strcmp(s
->type
, "host"))
1311 ptr
.section
= s
->e
.name
;
1317 if (!uci_lookup_ptr(uci
, &ptr
, NULL
, true) && ptr
.o
!= NULL
&&
1318 ptr
.o
->type
!= UCI_TYPE_STRING
)
1319 n
= ptr
.o
->v
.string
;
1323 if (!n
|| inet_pton(AF_INET
, n
, &in
) != 1)
1326 ptr
.option
= "name";
1329 if (!uci_lookup_ptr(uci
, &ptr
, NULL
, true) && ptr
.o
!= NULL
&&
1330 ptr
.o
->type
== UCI_TYPE_STRING
)
1331 n
= ptr
.o
->v
.string
;
1338 if (uci_lookup_ptr(uci
, &ptr
, NULL
, true) || ptr
.o
== NULL
)
1341 if (ptr
.o
->type
== UCI_TYPE_STRING
) {
1342 for (p
= strtok(ptr
.o
->v
.string
, " \t");
1344 p
= strtok(NULL
, " \t")) {
1345 hint
= rpc_luci_get_host_hint(rctx
, ether_aton(p
));
1350 if (hint
->ip
.s_addr
== 0 && in
.s_addr
!= 0)
1353 if (n
&& !hint
->hostname
)
1354 hint
->hostname
= strdup(n
);
1357 else if (ptr
.o
->type
== UCI_TYPE_LIST
) {
1358 uci_foreach_element(&ptr
.o
->v
.list
, l
) {
1359 hint
= rpc_luci_get_host_hint(rctx
, ether_aton(l
->name
));
1364 if (hint
->ip
.s_addr
== 0 && in
.s_addr
!= 0)
1367 if (n
&& !hint
->hostname
)
1368 hint
->hostname
= strdup(n
);
1375 while ((lease
= lease_next()) != NULL
) {
1376 hint
= rpc_luci_get_host_hint(rctx
, &lease
->mac
);
1381 if (lease
->af
== AF_INET
&& lease
->n_addr
&& hint
->ip
.s_addr
== 0)
1382 hint
->ip
= lease
->addr
[0].in
;
1383 else if (lease
->af
== AF_INET6
&& lease
->n_addr
&&
1384 !memcmp(&hint
->ip6
, &empty
, sizeof(empty
)))
1385 hint
->ip6
= lease
->addr
[0].in6
;
1387 if (lease
->hostname
&& !hint
->hostname
)
1388 hint
->hostname
= strdup(lease
->hostname
);
1395 uci_free_context(uci
);
1399 rpc_luci_get_host_hints_ifaddrs(struct reply_context
*rctx
)
1401 struct ether_addr empty_ea
= {};
1402 struct in6_addr empty_in6
= {};
1403 struct ifaddrs
*ifaddr
, *ifa
;
1404 struct sockaddr_ll
*sll
;
1405 struct avl_tree devices
;
1406 struct host_hint
*hint
;
1408 struct avl_node avl
;
1409 struct ether_addr ea
;
1410 struct in6_addr in6
;
1412 } *device
, *nextdevice
;
1415 avl_init(&devices
, avl_strcmp
, false, NULL
);
1417 if (getifaddrs(&ifaddr
) == -1)
1420 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
1424 device
= avl_find_element(&devices
, ifa
->ifa_name
, device
, avl
);
1427 device
= calloc_a(sizeof(*device
), &p
, strlen(ifa
->ifa_name
) + 1);
1432 device
->avl
.key
= strcpy(p
, ifa
->ifa_name
);
1433 avl_insert(&devices
, &device
->avl
);
1436 switch (ifa
->ifa_addr
->sa_family
) {
1438 sll
= (struct sockaddr_ll
*)ifa
->ifa_addr
;
1440 if (sll
->sll_halen
== 6)
1441 memcpy(&device
->ea
, sll
->sll_addr
, 6);
1446 device
->in6
= ((struct sockaddr_in6
*)ifa
->ifa_addr
)->sin6_addr
;
1450 device
->in
= ((struct sockaddr_in
*)ifa
->ifa_addr
)->sin_addr
;
1455 freeifaddrs(ifaddr
);
1457 avl_remove_all_elements(&devices
, device
, avl
, nextdevice
) {
1458 if (memcmp(&device
->ea
, &empty_ea
, sizeof(empty_ea
)) &&
1459 (memcmp(&device
->in6
, &empty_in6
, sizeof(empty_in6
)) ||
1460 device
->in
.s_addr
!= 0)) {
1461 hint
= rpc_luci_get_host_hint(rctx
, &device
->ea
);
1464 if (hint
->ip
.s_addr
== 0 && device
->in
.s_addr
!= 0)
1465 hint
->ip
= device
->in
;
1467 if (memcmp(&hint
->ip6
, &empty_in6
, sizeof(empty_in6
)) == 0 &&
1468 memcmp(&device
->in6
, &empty_in6
, sizeof(empty_in6
)) != 0)
1469 hint
->ip6
= device
->in6
;
1478 rpc_luci_get_host_hints_finish(struct reply_context
*rctx
);
1481 rpc_luci_get_host_hints_rrdns_cb(struct ubus_request
*req
, int type
,
1482 struct blob_attr
*msg
)
1484 struct reply_context
*rctx
= req
->priv
;
1485 struct host_hint
*hint
;
1486 struct blob_attr
*cur
;
1487 struct in6_addr in6
;
1492 blob_for_each_attr(cur
, msg
, rem
) {
1493 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
)
1496 if (inet_pton(AF_INET6
, blobmsg_name(cur
), &in6
) == 1) {
1497 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1498 if (!memcmp(&hint
->ip6
, &in6
, sizeof(in6
))) {
1500 free(hint
->hostname
);
1502 hint
->hostname
= strdup(blobmsg_get_string(cur
));
1507 else if (inet_pton(AF_INET
, blobmsg_name(cur
), &in
) == 1) {
1508 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1509 if (!memcmp(&hint
->ip
, &in
, sizeof(in
))) {
1511 free(hint
->hostname
);
1513 hint
->hostname
= strdup(blobmsg_get_string(cur
));
1521 rpc_luci_get_host_hints_finish(rctx
);
1525 rpc_luci_get_host_hints_rrdns(struct reply_context
*rctx
)
1527 struct in6_addr empty_in6
= {};
1528 char buf
[INET6_ADDRSTRLEN
];
1529 struct blob_buf req
= {};
1530 struct host_hint
*hint
;
1534 blob_buf_init(&req
, 0);
1536 a
= blobmsg_open_array(&req
, "addrs");
1538 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1539 if (hint
->ip
.s_addr
!= 0) {
1540 inet_ntop(AF_INET
, &hint
->ip
, buf
, sizeof(buf
));
1541 blobmsg_add_string(&req
, NULL
, buf
);
1544 else if (memcmp(&hint
->ip6
, &empty_in6
, sizeof(empty_in6
))) {
1545 inet_ntop(AF_INET6
, &hint
->ip6
, buf
, sizeof(buf
));
1546 blobmsg_add_string(&req
, NULL
, buf
);
1551 blobmsg_close_array(&req
, a
);
1554 blobmsg_add_u32(&req
, "timeout", 250);
1555 blobmsg_add_u32(&req
, "limit", n
);
1557 if (!invoke_ubus(rctx
->context
, "network.rrdns", "lookup", &req
,
1558 rpc_luci_get_host_hints_rrdns_cb
, rctx
))
1559 rpc_luci_get_host_hints_finish(rctx
);
1562 rpc_luci_get_host_hints_finish(rctx
);
1565 blob_buf_free(&req
);
1569 rpc_luci_get_host_hints_finish(struct reply_context
*rctx
)
1571 struct host_hint
*hint
, *nexthint
;
1572 char buf
[INET6_ADDRSTRLEN
];
1573 struct in6_addr in6
= {};
1574 struct in_addr in
= {};
1577 avl_remove_all_elements(&rctx
->avl
, hint
, avl
, nexthint
) {
1578 o
= blobmsg_open_table(&rctx
->blob
, hint
->avl
.key
);
1580 if (memcmp(&hint
->ip
, &in
, sizeof(in
))) {
1581 inet_ntop(AF_INET
, &hint
->ip
, buf
, sizeof(buf
));
1582 blobmsg_add_string(&rctx
->blob
, "ipv4", buf
);
1585 if (memcmp(&hint
->ip6
, &in6
, sizeof(in6
))) {
1586 inet_ntop(AF_INET6
, &hint
->ip6
, buf
, sizeof(buf
));
1587 blobmsg_add_string(&rctx
->blob
, "ipv6", buf
);
1591 blobmsg_add_string(&rctx
->blob
, "name", hint
->hostname
);
1593 blobmsg_close_table(&rctx
->blob
, o
);
1596 free(hint
->hostname
);
1601 return finish_request(rctx
, UBUS_STATUS_OK
);
1605 rpc_luci_get_host_hints(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1606 struct ubus_request_data
*req
, const char *method
,
1607 struct blob_attr
*msg
)
1609 struct reply_context
*rctx
= defer_request(ctx
, req
);
1612 return UBUS_STATUS_UNKNOWN_ERROR
;
1614 rpc_luci_get_host_hints_nl(rctx
);
1615 rpc_luci_get_host_hints_uci(rctx
);
1616 rpc_luci_get_host_hints_ether(rctx
);
1617 rpc_luci_get_host_hints_ifaddrs(rctx
);
1618 rpc_luci_get_host_hints_rrdns(rctx
);
1620 return UBUS_STATUS_OK
;
1624 rpc_luci_get_duid_hints(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1625 struct ubus_request_data
*req
, const char *method
,
1626 struct blob_attr
*msg
)
1628 struct { struct avl_node avl
; } *e
, *next
;
1629 char s
[INET6_ADDRSTRLEN
], *p
;
1630 struct ether_addr empty
= {};
1631 struct lease_entry
*lease
;
1632 struct avl_tree avl
;
1636 avl_init(&avl
, avl_strcmp
, false, NULL
);
1637 blob_buf_init(&blob
, 0);
1641 while ((lease
= lease_next()) != NULL
) {
1642 if (lease
->af
!= AF_INET6
|| lease
->duid
== NULL
)
1645 e
= avl_find_element(&avl
, lease
->duid
, e
, avl
);
1650 e
= calloc_a(sizeof(*e
), &p
, strlen(lease
->duid
) + 1);
1655 o
= blobmsg_open_table(&blob
, lease
->duid
);
1657 inet_ntop(AF_INET6
, &lease
->addr
[0].in6
, s
, sizeof(s
));
1658 blobmsg_add_string(&blob
, "ip6addr", s
);
1660 a
= blobmsg_open_array(&blob
, "ip6addrs");
1662 for (n
= 0; n
< lease
->n_addr
; n
++) {
1663 inet_ntop(AF_INET6
, &lease
->addr
[n
].in6
, s
, sizeof(s
));
1664 blobmsg_add_string(&blob
, NULL
, s
);
1667 blobmsg_close_array(&blob
, a
);
1669 if (lease
->hostname
)
1670 blobmsg_add_string(&blob
, "hostname", lease
->hostname
);
1672 if (memcmp(&lease
->mac
, &empty
, sizeof(empty
)))
1673 blobmsg_add_string(&blob
, "macaddr", ea2str(&lease
->mac
));
1675 blobmsg_close_table(&blob
, o
);
1677 e
->avl
.key
= strcpy(p
, lease
->duid
);
1678 avl_insert(&avl
, &e
->avl
);
1683 avl_remove_all_elements(&avl
, e
, avl
, next
) {
1687 ubus_send_reply(ctx
, req
, blob
.head
);
1689 return UBUS_STATUS_OK
;
1693 rpc_luci_get_board_json(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1694 struct ubus_request_data
*req
, const char *method
,
1695 struct blob_attr
*msg
)
1697 blob_buf_init(&blob
, 0);
1699 if (!blobmsg_add_json_from_file(&blob
, "/etc/board.json"))
1700 return UBUS_STATUS_UNKNOWN_ERROR
;
1702 ubus_send_reply(ctx
, req
, blob
.head
);
1703 return UBUS_STATUS_OK
;
1711 static const struct blobmsg_policy rpc_get_leases_policy
[__RPC_L_MAX
] = {
1712 [RPC_L_FAMILY
] = { .name
= "family", .type
= BLOBMSG_TYPE_INT32
}
1716 rpc_luci_get_dhcp_leases(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1717 struct ubus_request_data
*req
, const char *method
,
1718 struct blob_attr
*msg
)
1720 struct blob_attr
*tb
[__RPC_L_MAX
];
1721 struct ether_addr emptymac
= {};
1722 struct lease_entry
*lease
;
1723 char s
[INET6_ADDRSTRLEN
];
1728 blobmsg_parse(rpc_get_leases_policy
, __RPC_L_MAX
, tb
,
1729 blob_data(msg
), blob_len(msg
));
1731 switch (tb
[RPC_L_FAMILY
] ? blobmsg_get_u32(tb
[RPC_L_FAMILY
]) : 0) {
1745 return UBUS_STATUS_INVALID_ARGUMENT
;
1748 blob_buf_init(&blob
, 0);
1750 for (af
= family
? family
: AF_INET
;
1752 af
= (family
== 0) ? (af
== AF_INET
? AF_INET6
: 0) : 0) {
1754 a
= blobmsg_open_array(&blob
, (af
== AF_INET
) ? "dhcp_leases"
1759 while ((lease
= lease_next()) != NULL
) {
1760 if (lease
->af
!= af
)
1763 o
= blobmsg_open_table(&blob
, NULL
);
1765 if (lease
->expire
== -1)
1766 blobmsg_add_u8(&blob
, "expires", 0);
1768 blobmsg_add_u32(&blob
, "expires", lease
->expire
);
1770 if (lease
->hostname
)
1771 blobmsg_add_string(&blob
, "hostname", lease
->hostname
);
1773 if (memcmp(&lease
->mac
, &emptymac
, sizeof(emptymac
)))
1774 blobmsg_add_string(&blob
, "macaddr", ea2str(&lease
->mac
));
1777 blobmsg_add_string(&blob
, "duid", lease
->duid
);
1779 inet_ntop(lease
->af
, &lease
->addr
[0].in6
, s
, sizeof(s
));
1780 blobmsg_add_string(&blob
, (af
== AF_INET
) ? "ipaddr" : "ip6addr",
1783 if (af
== AF_INET6
) {
1784 a2
= blobmsg_open_array(&blob
, "ip6addrs");
1786 for (n
= 0; n
< lease
->n_addr
; n
++) {
1787 inet_ntop(lease
->af
, &lease
->addr
[n
].in6
, s
, sizeof(s
));
1788 blobmsg_add_string(&blob
, NULL
, s
);
1791 blobmsg_close_array(&blob
, a2
);
1794 blobmsg_close_table(&blob
, o
);
1799 blobmsg_close_array(&blob
, a
);
1802 ubus_send_reply(ctx
, req
, blob
.head
);
1804 return UBUS_STATUS_OK
;
1808 rpc_luci_api_init(const struct rpc_daemon_ops
*o
, struct ubus_context
*ctx
)
1810 static const struct ubus_method luci_methods
[] = {
1811 UBUS_METHOD_NOARG("getNetworkDevices", rpc_luci_get_network_devices
),
1812 UBUS_METHOD_NOARG("getWirelessDevices", rpc_luci_get_wireless_devices
),
1813 UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints
),
1814 UBUS_METHOD_NOARG("getDUIDHints", rpc_luci_get_duid_hints
),
1815 UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json
),
1816 UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases
, rpc_get_leases_policy
)
1819 static struct ubus_object_type luci_type
=
1820 UBUS_OBJECT_TYPE("rpcd-luci", luci_methods
);
1822 static struct ubus_object obj
= {
1825 .methods
= luci_methods
,
1826 .n_methods
= ARRAY_SIZE(luci_methods
),
1829 return ubus_add_object(ctx
, &obj
);
1832 struct rpc_plugin rpc_plugin
= {
1833 .init
= rpc_luci_api_init