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
;
805 void *iwlib
, *o
, *o2
, *a
;
808 if (!iw_backend
|| !iw_close
|| !iw_modenames
) {
809 iwlib
= dlopen("libiwinfo.so", RTLD_LAZY
| RTLD_LOCAL
);
814 iw_backend
= dlsym(iwlib
, "iwinfo_backend");
815 iw_close
= dlsym(iwlib
, "iwinfo_close");
816 iw_modenames
= dlsym(iwlib
, "IWINFO_OPMODE_NAMES");
818 if (!iw_backend
|| !iw_close
|| !iw_modenames
)
822 iw
= iw_backend(devname
);
827 o
= blobmsg_open_table(buf
, "iwinfo");
829 iw_call_num(iw
->signal
, devname
, buf
, "signal");
830 iw_call_num(iw
->noise
, devname
, buf
, "noise");
831 iw_call_num(iw
->channel
, devname
, buf
, "channel");
832 iw_call_str(iw
->country
, devname
, buf
, "country");
833 iw_call_str(iw
->phyname
, devname
, buf
, "phy");
834 iw_call_num(iw
->txpower
, devname
, buf
, "txpower");
835 iw_call_num(iw
->txpower_offset
, devname
, buf
, "txpower_offset");
836 iw_call_num(iw
->frequency
, devname
, buf
, "frequency");
837 iw_call_num(iw
->frequency_offset
, devname
, buf
, "frequency_offset");
839 if (!iw
->hwmodelist(devname
, &nret
)) {
840 a
= blobmsg_open_array(buf
, "hwmodes");
842 if (nret
& IWINFO_80211_AC
)
843 blobmsg_add_string(buf
, NULL
, "ac");
845 if (nret
& IWINFO_80211_A
)
846 blobmsg_add_string(buf
, NULL
, "a");
848 if (nret
& IWINFO_80211_B
)
849 blobmsg_add_string(buf
, NULL
, "b");
851 if (nret
& IWINFO_80211_G
)
852 blobmsg_add_string(buf
, NULL
, "g");
854 if (nret
& IWINFO_80211_N
)
855 blobmsg_add_string(buf
, NULL
, "n");
857 blobmsg_close_array(buf
, a
);
860 if (!iw
->htmodelist(devname
, &nret
)) {
861 a
= blobmsg_open_array(buf
, "htmodes");
863 if (nret
& IWINFO_HTMODE_HT20
)
864 blobmsg_add_string(buf
, NULL
, "HT20");
866 if (nret
& IWINFO_HTMODE_HT40
)
867 blobmsg_add_string(buf
, NULL
, "HT40");
869 if (nret
& IWINFO_HTMODE_VHT20
)
870 blobmsg_add_string(buf
, NULL
, "VHT20");
872 if (nret
& IWINFO_HTMODE_VHT40
)
873 blobmsg_add_string(buf
, NULL
, "VHT40");
875 if (nret
& IWINFO_HTMODE_VHT80
)
876 blobmsg_add_string(buf
, NULL
, "VHT80");
878 if (nret
& IWINFO_HTMODE_VHT80_80
)
879 blobmsg_add_string(buf
, NULL
, "VHT80+80");
881 if (nret
& IWINFO_HTMODE_VHT160
)
882 blobmsg_add_string(buf
, NULL
, "VHT160");
884 blobmsg_close_array(buf
, a
);
887 if (!iw
->hardware_id(devname
, (char *)&ids
)) {
888 o2
= blobmsg_open_table(buf
, "hardware");
890 a
= blobmsg_open_array(buf
, "id");
891 blobmsg_add_u32(buf
, NULL
, ids
.vendor_id
);
892 blobmsg_add_u32(buf
, NULL
, ids
.device_id
);
893 blobmsg_add_u32(buf
, NULL
, ids
.subsystem_vendor_id
);
894 blobmsg_add_u32(buf
, NULL
, ids
.subsystem_device_id
);
895 blobmsg_close_array(buf
, a
);
897 iw_call_str(iw
->hardware_name
, devname
, buf
, "name");
899 blobmsg_close_table(buf
, o2
);
903 iw_call_num(iw
->quality
, devname
, buf
, "quality");
904 iw_call_num(iw
->quality_max
, devname
, buf
, "quality_max");
905 iw_call_num(iw
->bitrate
, devname
, buf
, "bitrate");
907 if (!iw
->mode(devname
, &nret
))
908 blobmsg_add_string(buf
, "mode", iw_modenames
[nret
]);
910 iw_call_str(iw
->ssid
, devname
, buf
, "ssid");
911 iw_call_str(iw
->bssid
, devname
, buf
, "bssid");
913 if (!iw
->encryption(devname
, (char *)&crypto
)) {
914 o2
= blobmsg_open_table(buf
, "encryption");
916 blobmsg_add_u8(buf
, "enabled", crypto
.enabled
);
918 if (crypto
.enabled
) {
919 if (!crypto
.wpa_version
) {
920 a
= blobmsg_open_array(buf
, "wep");
922 if (crypto
.auth_algs
& IWINFO_AUTH_OPEN
)
923 blobmsg_add_string(buf
, NULL
, "open");
925 if (crypto
.auth_algs
& IWINFO_AUTH_SHARED
)
926 blobmsg_add_string(buf
, NULL
, "shared");
928 blobmsg_close_array(buf
, a
);
931 a
= blobmsg_open_array(buf
, "wpa");
933 for (nret
= 1; nret
<= 3; nret
++)
934 if (crypto
.wpa_version
& (1 << (nret
- 1)))
935 blobmsg_add_u32(buf
, NULL
, nret
);
937 blobmsg_close_array(buf
, a
);
939 a
= blobmsg_open_array(buf
, "authentication");
941 if (crypto
.auth_suites
& IWINFO_KMGMT_PSK
)
942 blobmsg_add_string(buf
, NULL
, "psk");
944 if (crypto
.auth_suites
& IWINFO_KMGMT_8021x
)
945 blobmsg_add_string(buf
, NULL
, "802.1x");
947 if (crypto
.auth_suites
& IWINFO_KMGMT_SAE
)
948 blobmsg_add_string(buf
, NULL
, "sae");
950 if (crypto
.auth_suites
& IWINFO_KMGMT_OWE
)
951 blobmsg_add_string(buf
, NULL
, "owe");
953 if (!crypto
.auth_suites
||
954 (crypto
.auth_suites
& IWINFO_KMGMT_NONE
))
955 blobmsg_add_string(buf
, NULL
, "none");
957 blobmsg_close_array(buf
, a
);
960 a
= blobmsg_open_array(buf
, "ciphers");
961 nret
= crypto
.pair_ciphers
| crypto
.group_ciphers
;
963 if (nret
& IWINFO_CIPHER_WEP40
)
964 blobmsg_add_string(buf
, NULL
, "wep-40");
966 if (nret
& IWINFO_CIPHER_WEP104
)
967 blobmsg_add_string(buf
, NULL
, "wep-104");
969 if (nret
& IWINFO_CIPHER_TKIP
)
970 blobmsg_add_string(buf
, NULL
, "tkip");
972 if (nret
& IWINFO_CIPHER_CCMP
)
973 blobmsg_add_string(buf
, NULL
, "ccmp");
975 if (nret
& IWINFO_CIPHER_WRAP
)
976 blobmsg_add_string(buf
, NULL
, "wrap");
978 if (nret
& IWINFO_CIPHER_AESOCB
)
979 blobmsg_add_string(buf
, NULL
, "aes-ocb");
981 if (nret
& IWINFO_CIPHER_CKIP
)
982 blobmsg_add_string(buf
, NULL
, "ckip");
984 if (!nret
|| (nret
& IWINFO_CIPHER_NONE
))
985 blobmsg_add_string(buf
, NULL
, "none");
987 blobmsg_close_array(buf
, a
);
990 blobmsg_close_table(buf
, o2
);
994 blobmsg_close_table(buf
, o
);
1001 static void rpc_luci_get_wireless_devices_cb(struct ubus_request
*req
,
1002 int type
, struct blob_attr
*msg
)
1004 struct blob_attr
*wifi
, *cur
, *iface
, *cur2
;
1005 struct reply_context
*rctx
= req
->priv
;
1006 const char *name
, *first_ifname
;
1007 int rem
, rem2
, rem3
, rem4
;
1010 blob_for_each_attr(wifi
, msg
, rem
) {
1011 if (blobmsg_type(wifi
) != BLOBMSG_TYPE_TABLE
||
1012 blobmsg_name(wifi
) == NULL
)
1015 o
= blobmsg_open_table(&rctx
->blob
, blobmsg_name(wifi
));
1017 rem2
= blobmsg_data_len(wifi
);
1018 first_ifname
= NULL
;
1020 __blob_for_each_attr(cur
, blobmsg_data(wifi
), rem2
) {
1021 name
= blobmsg_name(cur
);
1023 if (!name
|| !strcmp(name
, "iwinfo")) {
1026 else if (!strcmp(name
, "interfaces")) {
1027 if (blobmsg_type(cur
) != BLOBMSG_TYPE_ARRAY
)
1030 a
= blobmsg_open_array(&rctx
->blob
, "interfaces");
1032 rem3
= blobmsg_data_len(cur
);
1034 __blob_for_each_attr(iface
, blobmsg_data(cur
), rem3
) {
1035 if (blobmsg_type(iface
) != BLOBMSG_TYPE_TABLE
)
1038 o2
= blobmsg_open_table(&rctx
->blob
, NULL
);
1040 rem4
= blobmsg_data_len(iface
);
1043 __blob_for_each_attr(cur2
, blobmsg_data(iface
), rem4
) {
1044 if (!strcmp(blobmsg_name(cur2
), "ifname"))
1045 name
= blobmsg_get_string(cur2
);
1046 else if (!strcmp(blobmsg_name(cur2
), "iwinfo"))
1049 blobmsg_add_blob(&rctx
->blob
, cur2
);
1053 if (rpc_luci_get_iwinfo(&rctx
->blob
, name
, false))
1054 first_ifname
= first_ifname
? first_ifname
: name
;
1056 blobmsg_close_table(&rctx
->blob
, o2
);
1059 blobmsg_close_array(&rctx
->blob
, a
);
1062 blobmsg_add_blob(&rctx
->blob
, cur
);
1066 rpc_luci_get_iwinfo(&rctx
->blob
,
1067 first_ifname
? first_ifname
: blobmsg_name(wifi
),
1070 blobmsg_close_table(&rctx
->blob
, o
);
1073 finish_request(rctx
, UBUS_STATUS_OK
);
1077 rpc_luci_get_wireless_devices(struct ubus_context
*ctx
,
1078 struct ubus_object
*obj
,
1079 struct ubus_request_data
*req
,
1081 struct blob_attr
*msg
)
1083 struct reply_context
*rctx
= defer_request(ctx
, req
);
1086 return UBUS_STATUS_UNKNOWN_ERROR
;
1088 if (!invoke_ubus(ctx
, "network.wireless", "status", NULL
,
1089 rpc_luci_get_wireless_devices_cb
, rctx
))
1090 return finish_request(rctx
, UBUS_STATUS_NOT_FOUND
);
1092 return UBUS_STATUS_OK
;
1096 struct avl_node avl
;
1099 struct in6_addr ip6
;
1103 nl_cb_done(struct nl_msg
*msg
, void *arg
)
1105 struct reply_context
*rctx
= arg
;
1111 nl_cb_error(struct sockaddr_nl
*nla
, struct nlmsgerr
*err
, void *arg
)
1113 struct reply_context
*rctx
= arg
;
1118 static struct host_hint
*
1119 rpc_luci_get_host_hint(struct reply_context
*rctx
, struct ether_addr
*ea
)
1121 struct host_hint
*hint
;
1128 hint
= avl_find_element(&rctx
->avl
, mac
, hint
, avl
);
1131 hint
= calloc_a(sizeof(*hint
), &p
, strlen(mac
) + 1);
1136 hint
->avl
.key
= strcpy(p
, mac
);
1137 avl_insert(&rctx
->avl
, &hint
->avl
);
1143 static int nl_cb_dump_neigh(struct nl_msg
*msg
, void *arg
)
1145 struct reply_context
*rctx
= arg
;
1146 struct ether_addr
*mac
;
1147 struct in6_addr
*dst
;
1148 struct nlmsghdr
*hdr
= nlmsg_hdr(msg
);
1149 struct ndmsg
*nd
= NLMSG_DATA(hdr
);
1150 struct nlattr
*tb
[NDA_MAX
+1];
1151 struct host_hint
*hint
;
1153 rctx
->pending
= !!(hdr
->nlmsg_flags
& NLM_F_MULTI
);
1155 if (hdr
->nlmsg_type
!= RTM_NEWNEIGH
||
1156 (nd
->ndm_family
!= AF_INET
&& nd
->ndm_family
!= AF_INET6
))
1159 if (!(nd
->ndm_state
& (0xFF & ~NUD_NOARP
)))
1162 nlmsg_parse(hdr
, sizeof(*nd
), tb
, NDA_MAX
, NULL
);
1164 mac
= tb
[NDA_LLADDR
] ? RTA_DATA(tb
[NDA_LLADDR
]) : NULL
;
1165 dst
= tb
[NDA_DST
] ? RTA_DATA(tb
[NDA_DST
]) : NULL
;
1170 hint
= rpc_luci_get_host_hint(rctx
, mac
);
1175 if (nd
->ndm_family
== AF_INET
)
1176 hint
->ip
= *(struct in_addr
*)dst
;
1178 hint
->ip6
= *(struct in6_addr
*)dst
;
1184 rpc_luci_get_host_hints_nl(struct reply_context
*rctx
)
1186 struct nl_sock
*sock
= NULL
;
1187 struct nl_msg
*msg
= NULL
;
1188 struct nl_cb
*cb
= NULL
;
1189 struct ndmsg ndm
= {};
1191 sock
= nl_socket_alloc();
1196 if (nl_connect(sock
, NETLINK_ROUTE
))
1199 cb
= nl_cb_alloc(NL_CB_DEFAULT
);
1204 msg
= nlmsg_alloc_simple(RTM_GETNEIGH
, NLM_F_REQUEST
| NLM_F_DUMP
);
1209 nlmsg_append(msg
, &ndm
, sizeof(ndm
), 0);
1211 nl_cb_set(cb
, NL_CB_VALID
, NL_CB_CUSTOM
, nl_cb_dump_neigh
, rctx
);
1212 nl_cb_set(cb
, NL_CB_FINISH
, NL_CB_CUSTOM
, nl_cb_done
, rctx
);
1213 nl_cb_err(cb
, NL_CB_CUSTOM
, nl_cb_error
, rctx
);
1215 avl_init(&rctx
->avl
, avl_strcmp
, false, NULL
);
1219 nl_send_auto_complete(sock
, msg
);
1221 while (rctx
->pending
)
1222 nl_recvmsgs(sock
, cb
);
1226 nl_socket_free(sock
);
1236 rpc_luci_get_host_hints_ether(struct reply_context
*rctx
)
1238 struct host_hint
*hint
;
1243 f
= fopen("/etc/ethers", "r");
1248 while (fgets(buf
, sizeof(buf
), f
)) {
1249 p
= strtok(buf
, " \t\n");
1250 hint
= rpc_luci_get_host_hint(rctx
, p
? ether_aton(p
) : NULL
);
1255 p
= strtok(NULL
, " \t\n");
1260 if (inet_pton(AF_INET
, p
, &in
) == 1) {
1261 if (hint
->ip
.s_addr
== 0)
1264 else if (*p
&& !hint
->hostname
) {
1265 hint
->hostname
= strdup(p
);
1273 rpc_luci_get_host_hints_uci(struct reply_context
*rctx
)
1275 struct uci_ptr ptr
= { .package
= "dhcp" };
1276 struct uci_context
*uci
= NULL
;
1277 struct uci_package
*pkg
= NULL
;
1278 struct in6_addr empty
= {};
1279 struct lease_entry
*lease
;
1280 struct host_hint
*hint
;
1281 struct uci_element
*e
, *l
;
1282 struct uci_section
*s
;
1286 uci
= uci_alloc_context();
1291 uci_load(uci
, ptr
.package
, &pkg
);
1296 uci_foreach_element(&pkg
->sections
, e
)
1298 s
= uci_to_section(e
);
1300 if (strcmp(s
->type
, "host"))
1303 ptr
.section
= s
->e
.name
;
1309 if (!uci_lookup_ptr(uci
, &ptr
, NULL
, true) && ptr
.o
!= NULL
&&
1310 ptr
.o
->type
!= UCI_TYPE_STRING
)
1311 n
= ptr
.o
->v
.string
;
1315 if (!n
|| inet_pton(AF_INET
, n
, &in
) != 1)
1318 ptr
.option
= "name";
1321 if (!uci_lookup_ptr(uci
, &ptr
, NULL
, true) && ptr
.o
!= NULL
&&
1322 ptr
.o
->type
== UCI_TYPE_STRING
)
1323 n
= ptr
.o
->v
.string
;
1330 if (uci_lookup_ptr(uci
, &ptr
, NULL
, true) || ptr
.o
== NULL
)
1333 if (ptr
.o
->type
== UCI_TYPE_STRING
) {
1334 for (p
= strtok(ptr
.o
->v
.string
, " \t");
1336 p
= strtok(NULL
, " \t")) {
1337 hint
= rpc_luci_get_host_hint(rctx
, ether_aton(p
));
1342 if (hint
->ip
.s_addr
== 0 && in
.s_addr
!= 0)
1345 if (n
&& !hint
->hostname
)
1346 hint
->hostname
= strdup(n
);
1349 else if (ptr
.o
->type
== UCI_TYPE_LIST
) {
1350 uci_foreach_element(&ptr
.o
->v
.list
, l
) {
1351 hint
= rpc_luci_get_host_hint(rctx
, ether_aton(l
->name
));
1356 if (hint
->ip
.s_addr
== 0 && in
.s_addr
!= 0)
1359 if (n
&& !hint
->hostname
)
1360 hint
->hostname
= strdup(n
);
1367 while ((lease
= lease_next()) != NULL
) {
1368 hint
= rpc_luci_get_host_hint(rctx
, &lease
->mac
);
1373 if (lease
->af
== AF_INET
&& lease
->n_addr
&& hint
->ip
.s_addr
== 0)
1374 hint
->ip
= lease
->addr
[0].in
;
1375 else if (lease
->af
== AF_INET6
&& lease
->n_addr
&&
1376 !memcmp(&hint
->ip6
, &empty
, sizeof(empty
)))
1377 hint
->ip6
= lease
->addr
[0].in6
;
1379 if (lease
->hostname
&& !hint
->hostname
)
1380 hint
->hostname
= strdup(lease
->hostname
);
1387 uci_free_context(uci
);
1391 rpc_luci_get_host_hints_ifaddrs(struct reply_context
*rctx
)
1393 struct ether_addr empty_ea
= {};
1394 struct in6_addr empty_in6
= {};
1395 struct ifaddrs
*ifaddr
, *ifa
;
1396 struct sockaddr_ll
*sll
;
1397 struct avl_tree devices
;
1398 struct host_hint
*hint
;
1400 struct avl_node avl
;
1401 struct ether_addr ea
;
1402 struct in6_addr in6
;
1404 } *device
, *nextdevice
;
1407 avl_init(&devices
, avl_strcmp
, false, NULL
);
1409 if (getifaddrs(&ifaddr
) == -1)
1412 for (ifa
= ifaddr
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
1416 device
= avl_find_element(&devices
, ifa
->ifa_name
, device
, avl
);
1419 device
= calloc_a(sizeof(*device
), &p
, strlen(ifa
->ifa_name
) + 1);
1424 device
->avl
.key
= strcpy(p
, ifa
->ifa_name
);
1425 avl_insert(&devices
, &device
->avl
);
1428 switch (ifa
->ifa_addr
->sa_family
) {
1430 sll
= (struct sockaddr_ll
*)ifa
->ifa_addr
;
1432 if (sll
->sll_halen
== 6)
1433 memcpy(&device
->ea
, sll
->sll_addr
, 6);
1438 device
->in6
= ((struct sockaddr_in6
*)ifa
->ifa_addr
)->sin6_addr
;
1442 device
->in
= ((struct sockaddr_in
*)ifa
->ifa_addr
)->sin_addr
;
1447 freeifaddrs(ifaddr
);
1449 avl_remove_all_elements(&devices
, device
, avl
, nextdevice
) {
1450 if (memcmp(&device
->ea
, &empty_ea
, sizeof(empty_ea
)) &&
1451 (memcmp(&device
->in6
, &empty_in6
, sizeof(empty_in6
)) ||
1452 device
->in
.s_addr
!= 0)) {
1453 hint
= rpc_luci_get_host_hint(rctx
, &device
->ea
);
1456 if (hint
->ip
.s_addr
== 0 && device
->in
.s_addr
!= 0)
1457 hint
->ip
= device
->in
;
1459 if (memcmp(&hint
->ip6
, &empty_in6
, sizeof(empty_in6
)) == 0 &&
1460 memcmp(&device
->in6
, &empty_in6
, sizeof(empty_in6
)) != 0)
1461 hint
->ip6
= device
->in6
;
1470 rpc_luci_get_host_hints_finish(struct reply_context
*rctx
);
1473 rpc_luci_get_host_hints_rrdns_cb(struct ubus_request
*req
, int type
,
1474 struct blob_attr
*msg
)
1476 struct reply_context
*rctx
= req
->priv
;
1477 struct host_hint
*hint
;
1478 struct blob_attr
*cur
;
1479 struct in6_addr in6
;
1484 blob_for_each_attr(cur
, msg
, rem
) {
1485 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
)
1488 if (inet_pton(AF_INET6
, blobmsg_name(cur
), &in6
) == 1) {
1489 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1490 if (!memcmp(&hint
->ip6
, &in6
, sizeof(in6
))) {
1492 free(hint
->hostname
);
1494 hint
->hostname
= strdup(blobmsg_get_string(cur
));
1499 else if (inet_pton(AF_INET
, blobmsg_name(cur
), &in
) == 1) {
1500 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1501 if (!memcmp(&hint
->ip
, &in
, sizeof(in
))) {
1503 free(hint
->hostname
);
1505 hint
->hostname
= strdup(blobmsg_get_string(cur
));
1513 rpc_luci_get_host_hints_finish(rctx
);
1517 rpc_luci_get_host_hints_rrdns(struct reply_context
*rctx
)
1519 struct in6_addr empty_in6
= {};
1520 char buf
[INET6_ADDRSTRLEN
];
1521 struct blob_buf req
= {};
1522 struct host_hint
*hint
;
1526 blob_buf_init(&req
, 0);
1528 a
= blobmsg_open_array(&req
, "addrs");
1530 avl_for_each_element(&rctx
->avl
, hint
, avl
) {
1531 if (hint
->ip
.s_addr
!= 0) {
1532 inet_ntop(AF_INET
, &hint
->ip
, buf
, sizeof(buf
));
1533 blobmsg_add_string(&req
, NULL
, buf
);
1536 else if (memcmp(&hint
->ip6
, &empty_in6
, sizeof(empty_in6
))) {
1537 inet_ntop(AF_INET6
, &hint
->ip6
, buf
, sizeof(buf
));
1538 blobmsg_add_string(&req
, NULL
, buf
);
1543 blobmsg_close_array(&req
, a
);
1546 blobmsg_add_u32(&req
, "timeout", 250);
1547 blobmsg_add_u32(&req
, "limit", n
);
1549 if (!invoke_ubus(rctx
->context
, "network.rrdns", "lookup", &req
,
1550 rpc_luci_get_host_hints_rrdns_cb
, rctx
))
1551 rpc_luci_get_host_hints_finish(rctx
);
1554 rpc_luci_get_host_hints_finish(rctx
);
1557 blob_buf_free(&req
);
1561 rpc_luci_get_host_hints_finish(struct reply_context
*rctx
)
1563 struct host_hint
*hint
, *nexthint
;
1564 char buf
[INET6_ADDRSTRLEN
];
1565 struct in6_addr in6
= {};
1566 struct in_addr in
= {};
1569 avl_remove_all_elements(&rctx
->avl
, hint
, avl
, nexthint
) {
1570 o
= blobmsg_open_table(&rctx
->blob
, hint
->avl
.key
);
1572 if (memcmp(&hint
->ip
, &in
, sizeof(in
))) {
1573 inet_ntop(AF_INET
, &hint
->ip
, buf
, sizeof(buf
));
1574 blobmsg_add_string(&rctx
->blob
, "ipv4", buf
);
1577 if (memcmp(&hint
->ip6
, &in6
, sizeof(in6
))) {
1578 inet_ntop(AF_INET6
, &hint
->ip6
, buf
, sizeof(buf
));
1579 blobmsg_add_string(&rctx
->blob
, "ipv6", buf
);
1583 blobmsg_add_string(&rctx
->blob
, "name", hint
->hostname
);
1585 blobmsg_close_table(&rctx
->blob
, o
);
1588 free(hint
->hostname
);
1593 return finish_request(rctx
, UBUS_STATUS_OK
);
1597 rpc_luci_get_host_hints(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1598 struct ubus_request_data
*req
, const char *method
,
1599 struct blob_attr
*msg
)
1601 struct reply_context
*rctx
= defer_request(ctx
, req
);
1604 return UBUS_STATUS_UNKNOWN_ERROR
;
1606 rpc_luci_get_host_hints_nl(rctx
);
1607 rpc_luci_get_host_hints_uci(rctx
);
1608 rpc_luci_get_host_hints_ether(rctx
);
1609 rpc_luci_get_host_hints_ifaddrs(rctx
);
1610 rpc_luci_get_host_hints_rrdns(rctx
);
1612 return UBUS_STATUS_OK
;
1616 rpc_luci_get_duid_hints(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1617 struct ubus_request_data
*req
, const char *method
,
1618 struct blob_attr
*msg
)
1620 struct { struct avl_node avl
; } *e
, *next
;
1621 char s
[INET6_ADDRSTRLEN
], *p
;
1622 struct ether_addr empty
= {};
1623 struct lease_entry
*lease
;
1624 struct avl_tree avl
;
1628 avl_init(&avl
, avl_strcmp
, false, NULL
);
1629 blob_buf_init(&blob
, 0);
1633 while ((lease
= lease_next()) != NULL
) {
1634 if (lease
->af
!= AF_INET6
|| lease
->duid
== NULL
)
1637 e
= avl_find_element(&avl
, lease
->duid
, e
, avl
);
1642 e
= calloc_a(sizeof(*e
), &p
, strlen(lease
->duid
) + 1);
1647 o
= blobmsg_open_table(&blob
, lease
->duid
);
1649 inet_ntop(AF_INET6
, &lease
->addr
[0].in6
, s
, sizeof(s
));
1650 blobmsg_add_string(&blob
, "ip6addr", s
);
1652 a
= blobmsg_open_array(&blob
, "ip6addrs");
1654 for (n
= 0; n
< lease
->n_addr
; n
++) {
1655 inet_ntop(AF_INET6
, &lease
->addr
[n
].in6
, s
, sizeof(s
));
1656 blobmsg_add_string(&blob
, NULL
, s
);
1659 blobmsg_close_array(&blob
, a
);
1661 if (lease
->hostname
)
1662 blobmsg_add_string(&blob
, "hostname", lease
->hostname
);
1664 if (memcmp(&lease
->mac
, &empty
, sizeof(empty
)))
1665 blobmsg_add_string(&blob
, "macaddr", ea2str(&lease
->mac
));
1667 blobmsg_close_table(&blob
, o
);
1669 e
->avl
.key
= strcpy(p
, lease
->duid
);
1670 avl_insert(&avl
, &e
->avl
);
1675 avl_remove_all_elements(&avl
, e
, avl
, next
) {
1679 ubus_send_reply(ctx
, req
, blob
.head
);
1681 return UBUS_STATUS_OK
;
1685 rpc_luci_get_board_json(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1686 struct ubus_request_data
*req
, const char *method
,
1687 struct blob_attr
*msg
)
1689 blob_buf_init(&blob
, 0);
1691 if (!blobmsg_add_json_from_file(&blob
, "/etc/board.json"))
1692 return UBUS_STATUS_UNKNOWN_ERROR
;
1694 ubus_send_reply(ctx
, req
, blob
.head
);
1695 return UBUS_STATUS_OK
;
1699 rpc_luci_get_dsl_status(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1700 struct ubus_request_data
*req
, const char *method
,
1701 struct blob_attr
*msg
)
1703 char line
[128], *p
, *s
;
1706 cmd
= popen("/etc/init.d/dsl_control lucistat", "r");
1709 return UBUS_STATUS_NOT_FOUND
;
1711 blob_buf_init(&blob
, 0);
1713 while (fgets(line
, sizeof(line
), cmd
)) {
1714 if (strncmp(line
, "dsl.", 4))
1717 p
= strchr(line
, '=');
1722 s
= p
+ strlen(p
) - 1;
1724 while (s
>= p
&& isspace(*s
))
1729 if (!strcmp(p
, "nil"))
1733 blobmsg_add_u32(&blob
, line
+ 4, strtoul(p
, NULL
, 0));
1735 else if (*p
== '"') {
1736 s
= p
+ strlen(p
) - 1;
1738 if (s
>= p
&& *s
== '"')
1741 blobmsg_add_string(&blob
, line
+ 4, p
+ 1);
1747 ubus_send_reply(ctx
, req
, blob
.head
);
1748 return UBUS_STATUS_OK
;
1757 static const struct blobmsg_policy rpc_get_leases_policy
[__RPC_L_MAX
] = {
1758 [RPC_L_FAMILY
] = { .name
= "family", .type
= BLOBMSG_TYPE_INT32
}
1762 rpc_luci_get_dhcp_leases(struct ubus_context
*ctx
, struct ubus_object
*obj
,
1763 struct ubus_request_data
*req
, const char *method
,
1764 struct blob_attr
*msg
)
1766 struct blob_attr
*tb
[__RPC_L_MAX
];
1767 struct ether_addr emptymac
= {};
1768 struct lease_entry
*lease
;
1769 char s
[INET6_ADDRSTRLEN
];
1774 blobmsg_parse(rpc_get_leases_policy
, __RPC_L_MAX
, tb
,
1775 blob_data(msg
), blob_len(msg
));
1777 switch (tb
[RPC_L_FAMILY
] ? blobmsg_get_u32(tb
[RPC_L_FAMILY
]) : 0) {
1791 return UBUS_STATUS_INVALID_ARGUMENT
;
1794 blob_buf_init(&blob
, 0);
1796 for (af
= family
? family
: AF_INET
;
1798 af
= (family
== 0) ? (af
== AF_INET
? AF_INET6
: 0) : 0) {
1800 a
= blobmsg_open_array(&blob
, (af
== AF_INET
) ? "dhcp_leases"
1805 while ((lease
= lease_next()) != NULL
) {
1806 if (lease
->af
!= af
)
1809 o
= blobmsg_open_table(&blob
, NULL
);
1811 if (lease
->expire
== -1)
1812 blobmsg_add_u8(&blob
, "expires", 0);
1814 blobmsg_add_u32(&blob
, "expires", lease
->expire
);
1816 if (lease
->hostname
)
1817 blobmsg_add_string(&blob
, "hostname", lease
->hostname
);
1819 if (memcmp(&lease
->mac
, &emptymac
, sizeof(emptymac
)))
1820 blobmsg_add_string(&blob
, "macaddr", ea2str(&lease
->mac
));
1823 blobmsg_add_string(&blob
, "duid", lease
->duid
);
1825 inet_ntop(lease
->af
, &lease
->addr
[0].in6
, s
, sizeof(s
));
1826 blobmsg_add_string(&blob
, (af
== AF_INET
) ? "ipaddr" : "ip6addr",
1829 if (af
== AF_INET6
) {
1830 a2
= blobmsg_open_array(&blob
, "ip6addrs");
1832 for (n
= 0; n
< lease
->n_addr
; n
++) {
1833 inet_ntop(lease
->af
, &lease
->addr
[n
].in6
, s
, sizeof(s
));
1834 blobmsg_add_string(&blob
, NULL
, s
);
1837 blobmsg_close_array(&blob
, a2
);
1840 blobmsg_close_table(&blob
, o
);
1845 blobmsg_close_array(&blob
, a
);
1848 ubus_send_reply(ctx
, req
, blob
.head
);
1850 return UBUS_STATUS_OK
;
1854 rpc_luci_api_init(const struct rpc_daemon_ops
*o
, struct ubus_context
*ctx
)
1856 static const struct ubus_method luci_methods
[] = {
1857 UBUS_METHOD_NOARG("getNetworkDevices", rpc_luci_get_network_devices
),
1858 UBUS_METHOD_NOARG("getWirelessDevices", rpc_luci_get_wireless_devices
),
1859 UBUS_METHOD_NOARG("getHostHints", rpc_luci_get_host_hints
),
1860 UBUS_METHOD_NOARG("getDUIDHints", rpc_luci_get_duid_hints
),
1861 UBUS_METHOD_NOARG("getBoardJSON", rpc_luci_get_board_json
),
1862 UBUS_METHOD_NOARG("getDSLStatus", rpc_luci_get_dsl_status
),
1863 UBUS_METHOD("getDHCPLeases", rpc_luci_get_dhcp_leases
, rpc_get_leases_policy
)
1866 static struct ubus_object_type luci_type
=
1867 UBUS_OBJECT_TYPE("rpcd-luci", luci_methods
);
1869 static struct ubus_object obj
= {
1872 .methods
= luci_methods
,
1873 .n_methods
= ARRAY_SIZE(luci_methods
),
1876 return ubus_add_object(ctx
, &obj
);
1879 struct rpc_plugin rpc_plugin
= {
1880 .init
= rpc_luci_api_init