+static int
+rpc_luci_get_duid_hints(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct { struct avl_node avl; } *e, *next;
+ char s[INET6_ADDRSTRLEN], *p;
+ struct ether_addr empty = {};
+ struct lease_entry *lease;
+ struct avl_tree avl;
+ void *o;
+
+ avl_init(&avl, avl_strcmp, false, NULL);
+ blob_buf_init(&blob, 0);
+
+ lease_open();
+
+ while ((lease = lease_next()) != NULL) {
+ if (lease->af != AF_INET6 || lease->duid == NULL)
+ continue;
+
+ e = avl_find_element(&avl, lease->duid, e, avl);
+
+ if (e)
+ continue;
+
+ e = calloc_a(sizeof(*e), &p, strlen(lease->duid) + 1);
+
+ if (!e)
+ continue;
+
+ o = blobmsg_open_table(&blob, lease->duid);
+
+ inet_ntop(AF_INET6, &lease->addr.in6, s, sizeof(s));
+ blobmsg_add_string(&blob, "ip6addr", s);
+
+ if (lease->hostname)
+ blobmsg_add_string(&blob, "hostname", lease->hostname);
+
+ if (memcmp(&lease->mac, &empty, sizeof(empty)))
+ blobmsg_add_string(&blob, "macaddr", ea2str(&lease->mac));
+
+ blobmsg_close_table(&blob, o);
+
+ e->avl.key = strcpy(p, lease->duid);
+ avl_insert(&avl, &e->avl);
+ }
+
+ lease_close();
+
+ avl_remove_all_elements(&avl, e, avl, next) {
+ free(e);
+ }
+
+ ubus_send_reply(ctx, req, blob.head);
+
+ return UBUS_STATUS_OK;
+}
+