2020-03-17 22:16:21

by Stotland, Inga

[permalink] [raw]
Subject: [PATCH BlueZ v2 1/2] tools/mesh-cfgclient: Implement node-reset command

This implements one-pass removal oa a remote node from a mesh network
by issuing a node-reset command from config menu. The following actions
are performed:
- Config Node Reset message is sent to a remote node
- Upon either receiving Config Node Reset Status or response timeout,
node record is removed from configuration client's database and,
by calling DeleteRemoteNode() method on mesh.Management interface

node-delete command from the main menu is removed.
---
tools/mesh-cfgclient.c | 77 ++++++++++++++++--------------------------
tools/mesh/cfgcli.c | 60 ++++++++++++++++++++++++++++----
tools/mesh/cfgcli.h | 4 ++-
tools/mesh/mesh-db.c | 39 +++++++++++++++++++++
tools/mesh/remote.c | 20 +++++++++++
tools/mesh/remote.h | 1 +
6 files changed, 147 insertions(+), 54 deletions(-)

diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
index 4b7bd2200..e4523e5fc 100644
--- a/tools/mesh-cfgclient.c
+++ b/tools/mesh-cfgclient.c
@@ -342,9 +342,38 @@ static bool send_key(void *user_data, uint16_t dst, uint16_t key_idx,
send_key_setup, NULL, req, l_free) != 0;
}

+static void delete_node_setup(struct l_dbus_message *msg, void *user_data)
+{
+ struct generic_request *req = user_data;
+ uint16_t primary;
+ uint8_t ele_cnt;
+
+ primary = (uint16_t) req->arg1;
+ ele_cnt = (uint8_t) req->arg2;
+
+ l_dbus_message_set_arguments(msg, "qy", primary, ele_cnt);
+}
+
+static void delete_node(uint16_t primary, uint8_t ele_cnt)
+{
+ struct generic_request *req;
+
+ if (!local || !local->proxy || !local->mgmt_proxy) {
+ bt_shell_printf("Node is not attached\n");
+ return;
+ }
+
+ req = l_new(struct generic_request, 1);
+ req->arg1 = primary;
+ req->arg2 = ele_cnt;
+
+ l_dbus_proxy_method_call(local->mgmt_proxy, "DeleteRemoteNode",
+ delete_node_setup, NULL, req, l_free);
+}
+
static void client_init(void)
{
- cfgcli = cfgcli_init(send_key, (void *) app.ele.path);
+ cfgcli = cfgcli_init(send_key, delete_node, (void *) app.ele.path);
cfgcli->ops.set_send_func(send_msg, (void *) app.ele.path);
}

@@ -801,50 +830,6 @@ static void free_generic_request(void *data)
l_free(req);
}

-static void delete_node_setup(struct l_dbus_message *msg, void *user_data)
-{
- struct generic_request *req = user_data;
- uint16_t primary;
- uint8_t ele_cnt;
-
- primary = (uint16_t) req->arg1;
- ele_cnt = (uint8_t) req->arg2;
-
- l_dbus_message_set_arguments(msg, "qy", primary, ele_cnt);
-}
-
-static void cmd_delete_node(int argc, char *argv[])
-{
- struct generic_request *req;
-
- if (!local || !local->proxy || !local->mgmt_proxy) {
- bt_shell_printf("Node is not attached\n");
- return;
- }
-
- if (argc < 3) {
- bt_shell_printf("Unicast and element count are required\n");
- return;
- }
-
- req = l_new(struct generic_request, 1);
-
- if (sscanf(argv[1], "%04x", &req->arg1) != 1)
- goto fail;
-
- if (sscanf(argv[2], "%u", &req->arg2) != 1)
- goto fail;
-
- l_dbus_proxy_method_call(local->mgmt_proxy, "DeleteRemoteNode",
- delete_node_setup, NULL, req, l_free);
-
- /* TODO:: Delete node from configuration */
- return;
-
-fail:
- l_free(req);
-}
-
static void import_node_reply(struct l_dbus_proxy *proxy,
struct l_dbus_message *msg, void *user_data)
{
@@ -1359,8 +1344,6 @@ static const struct bt_shell_menu main_menu = {
{ "node-import", "<uuid> <net_idx> <primary> <ele_count> <dev_key>",
cmd_import_node,
"Import an externally provisioned remote node"},
- { "node-delete", "<primary> <ele_count>", cmd_delete_node,
- "Delete a remote node"},
{ "list-nodes", NULL, cmd_list_nodes,
"List remote mesh nodes"},
{ "keys", NULL, cmd_keys,
diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index 33e77d878..0c82d9f82 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -65,6 +65,7 @@ static struct l_queue *groups;

static void *send_data;
static model_send_msg_func_t send_msg;
+static delete_remote_func_t mgr_del_remote;

static void *key_data;
static key_send_func_t send_key_msg;
@@ -191,6 +192,15 @@ static const char *opcode_str(uint32_t opcode)
return cmd->desc;
}

+static void reset_remote_node(uint16_t addr)
+{
+ uint8_t ele_cnt = remote_del_node(addr);
+
+ bt_shell_printf("Remote removed (primary %4.4x)\n", addr);
+ if (ele_cnt && mgr_del_remote)
+ mgr_del_remote(addr, ele_cnt);
+}
+
static void free_request(void *a)
{
struct pending_req *req = a;
@@ -222,6 +232,10 @@ static void wait_rsp_timeout(struct l_timeout *timeout, void *user_data)
bt_shell_printf("No response for \"%s\" from %4.4x\n",
req->cmd->desc, req->addr);

+ /* Node reset case: delete the remote even if there is no response */
+ if (req->cmd->opcode == OP_NODE_RESET)
+ reset_remote_node(req->addr);
+
l_queue_remove(requests, req);
free_request(req);
}
@@ -713,11 +727,9 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,

/* Per Mesh Profile 4.3.2.54 */
case OP_NODE_RESET_STATUS:
- if (len != 1)
- return true;

- bt_shell_printf("Node %4.4x reset status %s\n",
- src, mesh_status_str(data[0]));
+ bt_shell_printf("Node %4.4x is reset\n", src);
+ reset_remote_node(src);

break;

@@ -1656,7 +1668,41 @@ static void cmd_friend_get(int argc, char *argv[])

static void cmd_node_reset(int argc, char *argv[])
{
- cmd_default(OP_NODE_RESET);
+ uint16_t n, i;
+ uint8_t msg[8];
+ struct pending_req *req;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ /* Cannot remet self */
+ if (target == 0x0001) {
+ bt_shell_printf("Resetting self not allowed\n");
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ n = mesh_opcode_set(OP_NODE_RESET, msg);
+
+ req = l_new(struct pending_req, 1);
+ req->addr = target;
+ req->cmd = get_cmd(OP_NODE_RESET);
+
+ /*
+ * As a courtesy to the remote node, send the reset command
+ * several times. Treat this as a single request with a longer
+ * response timeout.
+ */
+ req->timer = l_timeout_create(rsp_timeout * 2,
+ wait_rsp_timeout, req, NULL);
+
+ l_queue_push_tail(requests, req);
+
+ for (i = 0; i < 5; i++)
+ send_msg(send_data, target, APP_IDX_DEV_REMOTE, msg, n);
+
+ return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}

static void cmd_netkey_get(int argc, char *argv[])
@@ -1831,13 +1877,15 @@ static struct model_info cli_info = {
.vendor_id = VENDOR_ID_INVALID
};

-struct model_info *cfgcli_init(key_send_func_t key_send, void *user_data)
+struct model_info *cfgcli_init(key_send_func_t key_send,
+ delete_remote_func_t del_node, void *user_data)
{
if (!key_send)
return NULL;

send_key_msg = key_send;
key_data = user_data;
+ mgr_del_remote = del_node;
requests = l_queue_new();
groups = mesh_db_load_groups();
bt_shell_add_submenu(&cfg_menu);
diff --git a/tools/mesh/cfgcli.h b/tools/mesh/cfgcli.h
index 9b283d9d5..89a67f5de 100644
--- a/tools/mesh/cfgcli.h
+++ b/tools/mesh/cfgcli.h
@@ -25,6 +25,8 @@ struct mesh_group {

typedef bool (*key_send_func_t) (void *user_data, uint16_t dst,
uint16_t idx, bool is_appkey, bool update);
+typedef void (*delete_remote_func_t) (uint16_t primary, uint8_t ele_cnt);

-struct model_info *cfgcli_init(key_send_func_t key_func, void *user_data);
+struct model_info *cfgcli_init(key_send_func_t key_func,
+ delete_remote_func_t del_node, void *user_data);
void cfgcli_cleanup(void);
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 4a26a667e..e938ee733 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -845,6 +845,45 @@ fail:
return false;
}

+bool mesh_db_del_node(uint16_t unicast)
+{
+ json_object *jarray;
+ int i, sz;
+
+ if (!json_object_object_get_ex(cfg->jcfg, "nodes", &jarray))
+ return false;
+
+ if (!jarray || json_object_get_type(jarray) != json_type_array)
+ return false;
+
+ sz = json_object_array_length(jarray);
+
+ for (i = 0; i < sz; ++i) {
+ json_object *jentry, *jval;
+ uint16_t addr;
+ const char *str;
+
+ jentry = json_object_array_get_idx(jarray, i);
+ if (!json_object_object_get_ex(jentry, "unicastAddress",
+ &jval))
+ continue;
+
+ str = json_object_get_string(jval);
+ if (sscanf(str, "%04hx", &addr) != 1)
+ continue;
+
+ if (addr == unicast)
+ break;
+ }
+
+ if (i == sz)
+ return true;
+
+ json_object_array_del_idx(jarray, i, 1);
+
+ return mesh_config_save((struct mesh_config *) cfg, true, NULL, NULL);
+}
+
bool mesh_db_get_token(uint8_t token[8])
{
if (!cfg || !cfg->jcfg)
diff --git a/tools/mesh/remote.c b/tools/mesh/remote.c
index 533d59b28..b9bc6b5c0 100644
--- a/tools/mesh/remote.c
+++ b/tools/mesh/remote.c
@@ -89,6 +89,26 @@ static bool match_bound_key(const void *a, const void *b)
return (net_idx == keys_get_bound_key(app_idx));
}

+uint8_t remote_del_node(uint16_t unicast)
+{
+ struct remote_node *rmt;
+ uint8_t num_ele;
+
+ rmt = l_queue_remove_if(nodes, match_node_addr, L_UINT_TO_PTR(unicast));
+ if (!rmt)
+ return 0;
+
+ num_ele = rmt->num_ele;
+
+ l_queue_destroy(rmt->net_keys, NULL);
+ l_queue_destroy(rmt->app_keys, NULL);
+ l_free(rmt);
+
+ mesh_db_del_node(unicast);
+
+ return num_ele;
+}
+
bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
uint8_t ele_cnt, uint16_t net_idx)
{
diff --git a/tools/mesh/remote.h b/tools/mesh/remote.h
index f2a6f48dd..63382ed90 100644
--- a/tools/mesh/remote.h
+++ b/tools/mesh/remote.h
@@ -19,6 +19,7 @@

bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
uint8_t ele_cnt, uint16_t net_idx);
+uint8_t remote_del_node(uint16_t unicast);
uint16_t remote_get_next_unicast(uint16_t low, uint16_t high, uint8_t ele_cnt);
bool remote_add_net_key(uint16_t addr, uint16_t net_idx);
bool remote_del_net_key(uint16_t addr, uint16_t net_idx);
--
2.21.1


2020-03-18 16:40:15

by Gix, Brian

[permalink] [raw]
Subject: Re: [PATCH BlueZ v2 1/2] tools/mesh-cfgclient: Implement node-reset command

Patchset Applied

On Tue, 2020-03-17 at 15:15 -0700, Inga Stotland wrote:
> This implements one-pass removal oa a remote node from a mesh network
> by issuing a node-reset command from config menu. The following actions
> are performed:
> - Config Node Reset message is sent to a remote node
> - Upon either receiving Config Node Reset Status or response timeout,
> node record is removed from configuration client's database and,
> by calling DeleteRemoteNode() method on mesh.Management interface
>
> node-delete command from the main menu is removed.
> ---
> tools/mesh-cfgclient.c | 77 ++++++++++++++++--------------------------
> tools/mesh/cfgcli.c | 60 ++++++++++++++++++++++++++++----
> tools/mesh/cfgcli.h | 4 ++-
> tools/mesh/mesh-db.c | 39 +++++++++++++++++++++
> tools/mesh/remote.c | 20 +++++++++++
> tools/mesh/remote.h | 1 +
> 6 files changed, 147 insertions(+), 54 deletions(-)
>
> diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
> index 4b7bd2200..e4523e5fc 100644
> --- a/tools/mesh-cfgclient.c
> +++ b/tools/mesh-cfgclient.c
> @@ -342,9 +342,38 @@ static bool send_key(void *user_data, uint16_t dst, uint16_t key_idx,
> send_key_setup, NULL, req, l_free) != 0;
> }
>
> +static void delete_node_setup(struct l_dbus_message *msg, void *user_data)
> +{
> + struct generic_request *req = user_data;
> + uint16_t primary;
> + uint8_t ele_cnt;
> +
> + primary = (uint16_t) req->arg1;
> + ele_cnt = (uint8_t) req->arg2;
> +
> + l_dbus_message_set_arguments(msg, "qy", primary, ele_cnt);
> +}
> +
> +static void delete_node(uint16_t primary, uint8_t ele_cnt)
> +{
> + struct generic_request *req;
> +
> + if (!local || !local->proxy || !local->mgmt_proxy) {
> + bt_shell_printf("Node is not attached\n");
> + return;
> + }
> +
> + req = l_new(struct generic_request, 1);
> + req->arg1 = primary;
> + req->arg2 = ele_cnt;
> +
> + l_dbus_proxy_method_call(local->mgmt_proxy, "DeleteRemoteNode",
> + delete_node_setup, NULL, req, l_free);
> +}
> +
> static void client_init(void)
> {
> - cfgcli = cfgcli_init(send_key, (void *) app.ele.path);
> + cfgcli = cfgcli_init(send_key, delete_node, (void *) app.ele.path);
> cfgcli->ops.set_send_func(send_msg, (void *) app.ele.path);
> }
>
> @@ -801,50 +830,6 @@ static void free_generic_request(void *data)
> l_free(req);
> }
>
> -static void delete_node_setup(struct l_dbus_message *msg, void *user_data)
> -{
> - struct generic_request *req = user_data;
> - uint16_t primary;
> - uint8_t ele_cnt;
> -
> - primary = (uint16_t) req->arg1;
> - ele_cnt = (uint8_t) req->arg2;
> -
> - l_dbus_message_set_arguments(msg, "qy", primary, ele_cnt);
> -}
> -
> -static void cmd_delete_node(int argc, char *argv[])
> -{
> - struct generic_request *req;
> -
> - if (!local || !local->proxy || !local->mgmt_proxy) {
> - bt_shell_printf("Node is not attached\n");
> - return;
> - }
> -
> - if (argc < 3) {
> - bt_shell_printf("Unicast and element count are required\n");
> - return;
> - }
> -
> - req = l_new(struct generic_request, 1);
> -
> - if (sscanf(argv[1], "%04x", &req->arg1) != 1)
> - goto fail;
> -
> - if (sscanf(argv[2], "%u", &req->arg2) != 1)
> - goto fail;
> -
> - l_dbus_proxy_method_call(local->mgmt_proxy, "DeleteRemoteNode",
> - delete_node_setup, NULL, req, l_free);
> -
> - /* TODO:: Delete node from configuration */
> - return;
> -
> -fail:
> - l_free(req);
> -}
> -
> static void import_node_reply(struct l_dbus_proxy *proxy,
> struct l_dbus_message *msg, void *user_data)
> {
> @@ -1359,8 +1344,6 @@ static const struct bt_shell_menu main_menu = {
> { "node-import", "<uuid> <net_idx> <primary> <ele_count> <dev_key>",
> cmd_import_node,
> "Import an externally provisioned remote node"},
> - { "node-delete", "<primary> <ele_count>", cmd_delete_node,
> - "Delete a remote node"},
> { "list-nodes", NULL, cmd_list_nodes,
> "List remote mesh nodes"},
> { "keys", NULL, cmd_keys,
> diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
> index 33e77d878..0c82d9f82 100644
> --- a/tools/mesh/cfgcli.c
> +++ b/tools/mesh/cfgcli.c
> @@ -65,6 +65,7 @@ static struct l_queue *groups;
>
> static void *send_data;
> static model_send_msg_func_t send_msg;
> +static delete_remote_func_t mgr_del_remote;
>
> static void *key_data;
> static key_send_func_t send_key_msg;
> @@ -191,6 +192,15 @@ static const char *opcode_str(uint32_t opcode)
> return cmd->desc;
> }
>
> +static void reset_remote_node(uint16_t addr)
> +{
> + uint8_t ele_cnt = remote_del_node(addr);
> +
> + bt_shell_printf("Remote removed (primary %4.4x)\n", addr);
> + if (ele_cnt && mgr_del_remote)
> + mgr_del_remote(addr, ele_cnt);
> +}
> +
> static void free_request(void *a)
> {
> struct pending_req *req = a;
> @@ -222,6 +232,10 @@ static void wait_rsp_timeout(struct l_timeout *timeout, void *user_data)
> bt_shell_printf("No response for \"%s\" from %4.4x\n",
> req->cmd->desc, req->addr);
>
> + /* Node reset case: delete the remote even if there is no response */
> + if (req->cmd->opcode == OP_NODE_RESET)
> + reset_remote_node(req->addr);
> +
> l_queue_remove(requests, req);
> free_request(req);
> }
> @@ -713,11 +727,9 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
>
> /* Per Mesh Profile 4.3.2.54 */
> case OP_NODE_RESET_STATUS:
> - if (len != 1)
> - return true;
>
> - bt_shell_printf("Node %4.4x reset status %s\n",
> - src, mesh_status_str(data[0]));
> + bt_shell_printf("Node %4.4x is reset\n", src);
> + reset_remote_node(src);
>
> break;
>
> @@ -1656,7 +1668,41 @@ static void cmd_friend_get(int argc, char *argv[])
>
> static void cmd_node_reset(int argc, char *argv[])
> {
> - cmd_default(OP_NODE_RESET);
> + uint16_t n, i;
> + uint8_t msg[8];
> + struct pending_req *req;
> +
> + if (IS_UNASSIGNED(target)) {
> + bt_shell_printf("Destination not set\n");
> + return bt_shell_noninteractive_quit(EXIT_FAILURE);
> + }
> +
> + /* Cannot remet self */
> + if (target == 0x0001) {
> + bt_shell_printf("Resetting self not allowed\n");
> + return bt_shell_noninteractive_quit(EXIT_FAILURE);
> + }
> +
> + n = mesh_opcode_set(OP_NODE_RESET, msg);
> +
> + req = l_new(struct pending_req, 1);
> + req->addr = target;
> + req->cmd = get_cmd(OP_NODE_RESET);
> +
> + /*
> + * As a courtesy to the remote node, send the reset command
> + * several times. Treat this as a single request with a longer
> + * response timeout.
> + */
> + req->timer = l_timeout_create(rsp_timeout * 2,
> + wait_rsp_timeout, req, NULL);
> +
> + l_queue_push_tail(requests, req);
> +
> + for (i = 0; i < 5; i++)
> + send_msg(send_data, target, APP_IDX_DEV_REMOTE, msg, n);
> +
> + return bt_shell_noninteractive_quit(EXIT_SUCCESS);
> }
>
> static void cmd_netkey_get(int argc, char *argv[])
> @@ -1831,13 +1877,15 @@ static struct model_info cli_info = {
> .vendor_id = VENDOR_ID_INVALID
> };
>
> -struct model_info *cfgcli_init(key_send_func_t key_send, void *user_data)
> +struct model_info *cfgcli_init(key_send_func_t key_send,
> + delete_remote_func_t del_node, void *user_data)
> {
> if (!key_send)
> return NULL;
>
> send_key_msg = key_send;
> key_data = user_data;
> + mgr_del_remote = del_node;
> requests = l_queue_new();
> groups = mesh_db_load_groups();
> bt_shell_add_submenu(&cfg_menu);
> diff --git a/tools/mesh/cfgcli.h b/tools/mesh/cfgcli.h
> index 9b283d9d5..89a67f5de 100644
> --- a/tools/mesh/cfgcli.h
> +++ b/tools/mesh/cfgcli.h
> @@ -25,6 +25,8 @@ struct mesh_group {
>
> typedef bool (*key_send_func_t) (void *user_data, uint16_t dst,
> uint16_t idx, bool is_appkey, bool update);
> +typedef void (*delete_remote_func_t) (uint16_t primary, uint8_t ele_cnt);
>
> -struct model_info *cfgcli_init(key_send_func_t key_func, void *user_data);
> +struct model_info *cfgcli_init(key_send_func_t key_func,
> + delete_remote_func_t del_node, void *user_data);
> void cfgcli_cleanup(void);
> diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
> index 4a26a667e..e938ee733 100644
> --- a/tools/mesh/mesh-db.c
> +++ b/tools/mesh/mesh-db.c
> @@ -845,6 +845,45 @@ fail:
> return false;
> }
>
> +bool mesh_db_del_node(uint16_t unicast)
> +{
> + json_object *jarray;
> + int i, sz;
> +
> + if (!json_object_object_get_ex(cfg->jcfg, "nodes", &jarray))
> + return false;
> +
> + if (!jarray || json_object_get_type(jarray) != json_type_array)
> + return false;
> +
> + sz = json_object_array_length(jarray);
> +
> + for (i = 0; i < sz; ++i) {
> + json_object *jentry, *jval;
> + uint16_t addr;
> + const char *str;
> +
> + jentry = json_object_array_get_idx(jarray, i);
> + if (!json_object_object_get_ex(jentry, "unicastAddress",
> + &jval))
> + continue;
> +
> + str = json_object_get_string(jval);
> + if (sscanf(str, "%04hx", &addr) != 1)
> + continue;
> +
> + if (addr == unicast)
> + break;
> + }
> +
> + if (i == sz)
> + return true;
> +
> + json_object_array_del_idx(jarray, i, 1);
> +
> + return mesh_config_save((struct mesh_config *) cfg, true, NULL, NULL);
> +}
> +
> bool mesh_db_get_token(uint8_t token[8])
> {
> if (!cfg || !cfg->jcfg)
> diff --git a/tools/mesh/remote.c b/tools/mesh/remote.c
> index 533d59b28..b9bc6b5c0 100644
> --- a/tools/mesh/remote.c
> +++ b/tools/mesh/remote.c
> @@ -89,6 +89,26 @@ static bool match_bound_key(const void *a, const void *b)
> return (net_idx == keys_get_bound_key(app_idx));
> }
>
> +uint8_t remote_del_node(uint16_t unicast)
> +{
> + struct remote_node *rmt;
> + uint8_t num_ele;
> +
> + rmt = l_queue_remove_if(nodes, match_node_addr, L_UINT_TO_PTR(unicast));
> + if (!rmt)
> + return 0;
> +
> + num_ele = rmt->num_ele;
> +
> + l_queue_destroy(rmt->net_keys, NULL);
> + l_queue_destroy(rmt->app_keys, NULL);
> + l_free(rmt);
> +
> + mesh_db_del_node(unicast);
> +
> + return num_ele;
> +}
> +
> bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
> uint8_t ele_cnt, uint16_t net_idx)
> {
> diff --git a/tools/mesh/remote.h b/tools/mesh/remote.h
> index f2a6f48dd..63382ed90 100644
> --- a/tools/mesh/remote.h
> +++ b/tools/mesh/remote.h
> @@ -19,6 +19,7 @@
>
> bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
> uint8_t ele_cnt, uint16_t net_idx);
> +uint8_t remote_del_node(uint16_t unicast);
> uint16_t remote_get_next_unicast(uint16_t low, uint16_t high, uint8_t ele_cnt);
> bool remote_add_net_key(uint16_t addr, uint16_t net_idx);
> bool remote_del_net_key(uint16_t addr, uint16_t net_idx);