All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ 00/20] Mesh Configuration Database
@ 2021-09-23  3:25 Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 01/20] tools/mesh-cfgclient: Save provisioner info Inga Stotland
                   ` (20 more replies)
  0 siblings, 21 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This patch set (I apologize for its size, but this cannot be helped)
implements support for the newly published Mesh Configuration Database
Profile.

The changes are mostly contained to tools/mesh-cfgclient.c and tools/mesh
subdirectory. The only exception is the introduction of a new D-Bus mesh
API method ExportKeys() on org.bluez.mesh.Management1 interface.

The new functionality allows to export a snapshot of mesh state
from the point of view of mesh provisioner/configuration manager in
a standard format that can be used for to transferring the "ownership"
of the mesh configuration to another provisioner/configuration manager.

The changes break backwards compatibility with for the previous
versions of config-db.json that were generated when using
mesh-cfgclient tool. This can be amended by manually correcting the
field names and property values.

Inga Stotland (20):
  tools/mesh-cfgclient: Save provisioner info
  tools/mesh-cfgclient: Add timestamp to config database
  tools/mesh-cfgclient: Update stored NetKey and AppKey
  tools/mesh-cfgclient: Keep track of updated keys
  tools/mesh: Add new info to stored remote nodes
  tools/mesh-cfgclient: Overwrite config values when adding new ones
  tools/mesh-cfgclient: Store remote node's model bindings
  tools/mesh-cfgclient: Store remote node's model subs
  tools/mesh-cfgclient: Disallow model commands w/o composition
  tools/mesh-cfgclient: Store remote's model publication info
  tools/mesh-cfgclient: Check the result of config save
  tools/mesh-cfgclient: Rename mesh-db APIs for consistency
  tools/mesh-cfgclient: Save remote node feature setting
  tools/mesh-cfgclient: Store remote's heartbeat sub/pub
  tools/mesh-cfgclient: Add group parent address for DB compliance
  doc/mesh-api: Add ExportKeys call
  mesh: Implement ExportKeys() method
  tools/mesh-cfgclient: Store UUIDs in standard format
  tools/mesh-cfgclient: Excluded addresses property
  tools/mesh-cfgclient: Export configuration database

 doc/mesh-api.txt       |   56 ++
 mesh/keyring.c         |  286 ++++++++-
 mesh/keyring.h         |    2 +
 mesh/manager.c         |   35 ++
 tools/mesh-cfgclient.c |  212 ++++++-
 tools/mesh/cfgcli.c    |  284 ++++++---
 tools/mesh/keys.c      |    4 +-
 tools/mesh/mesh-db.c   | 1260 +++++++++++++++++++++++++++++++++++++---
 tools/mesh/mesh-db.h   |   66 ++-
 tools/mesh/model.h     |   13 +-
 tools/mesh/remote.c    |  205 +++++--
 tools/mesh/remote.h    |   11 +-
 12 files changed, 2206 insertions(+), 228 deletions(-)

-- 
2.31.1


^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 01/20] tools/mesh-cfgclient: Save provisioner info
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  4:00   ` Mesh Configuration Database bluez.test.bot
  2021-09-23 16:52   ` bluez.test.bot
  2021-09-23  3:25 ` [PATCH BlueZ 02/20] tools/mesh-cfgclient: Add timestamp to config database Inga Stotland
                   ` (19 subsequent siblings)
  20 siblings, 2 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This adds "provisioners" property to the config database.
The property includes the provisioner's name, UUID of the provisioner
device (corresponds to the local node), allocated unicast, group and
scene ranges.
The current implementation limitations:
- only one provisioner in the mesh network is supported,
- the unicast range is assumed to be contiguous,
- the group range is assumed to be contiguous,
- no support for scenes (empty array).
---
 tools/mesh-cfgclient.c |   5 +-
 tools/mesh/mesh-db.c   | 110 ++++++++++++++++++++++++++++++++++++-----
 tools/mesh/mesh-db.h   |   1 -
 3 files changed, 103 insertions(+), 13 deletions(-)

diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
index 70553c95c..62dcecb2f 100644
--- a/tools/mesh-cfgclient.c
+++ b/tools/mesh-cfgclient.c
@@ -1823,13 +1823,16 @@ static struct l_dbus_message *join_complete(struct l_dbus *dbus,
 		return l_dbus_message_new_error(message, dbus_err_fail, NULL);
 	}
 
-	mesh_db_set_addr_range(low_addr, high_addr);
 	keys_add_net_key(PRIMARY_NET_IDX);
 	mesh_db_net_key_add(PRIMARY_NET_IDX);
 
 	remote_add_node(app.uuid, 0x0001, 1, PRIMARY_NET_IDX);
 	mesh_db_add_node(app.uuid, 0x0001, 1, PRIMARY_NET_IDX);
 
+	mesh_db_add_provisioner("BlueZ mesh-cfgclient", app.uuid,
+					low_addr, high_addr,
+					GROUP_ADDRESS_LOW, GROUP_ADDRESS_HIGH);
+
 	l_idle_oneshot(attach_node, NULL, NULL);
 
 	return l_dbus_message_new_method_return(message);
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 034d7be2b..50fbc18cb 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -245,7 +245,7 @@ static json_object *get_node_by_uuid(json_object *jcfg, uint8_t uuid[16])
 		const char *str;
 
 		jentry = json_object_array_get_idx(jarray, i);
-		if (!json_object_object_get_ex(jentry, "uuid", &jval))
+		if (!json_object_object_get_ex(jentry, "UUID", &jval))
 			return NULL;
 
 		str = json_object_get_string(jval);
@@ -429,7 +429,7 @@ static void load_remotes(json_object *jcfg)
 		if (!jnode)
 			continue;
 
-		if (!json_object_object_get_ex(jnode, "uuid", &jval))
+		if (!json_object_object_get_ex(jnode, "UUID", &jval))
 			continue;
 
 		str = json_object_get_string(jval);
@@ -931,7 +931,7 @@ bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
 	if (!jnode)
 		return false;
 
-	if (!add_u8_16(jnode, "uuid", uuid))
+	if (!add_u8_16(jnode, "UUID", uuid))
 		goto fail;
 
 	jelements = init_elements(num_els);
@@ -1188,14 +1188,32 @@ bool mesh_db_get_token(uint8_t token[8])
 
 bool mesh_db_get_addr_range(uint16_t *low, uint16_t *high)
 {
-	json_object *jlow, *jhigh;
+	json_object *jprov, *jarray, *jobj, *jlow, *jhigh;
 	const char *str;
 
 	if (!cfg || !cfg->jcfg)
 		return false;
 
-	if (!json_object_object_get_ex(cfg->jcfg, "low", &jlow) ||
-			!json_object_object_get_ex(cfg->jcfg, "high", &jhigh))
+	jarray = json_object_object_get(cfg->jcfg, "provisioniers");
+
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	/* Assumption: only one provisioner in the system */
+	jprov = json_object_array_get_idx(jarray, 0);
+	if (!jprov)
+		return false;
+
+	if (!json_object_object_get_ex(jprov, "allocatedUnicastRange", &jarray))
+		return false;
+
+	/* Assumption: only one contiguous range is specified */
+	jobj = json_object_array_get_idx(jarray, 0);
+	if (!jobj)
+		return false;
+
+	if (!json_object_object_get_ex(jobj, "lowAddress", &jlow) ||
+			!json_object_object_get_ex(jobj, "highAddress", &jhigh))
 		return false;
 
 	str = json_object_get_string(jlow);
@@ -1209,18 +1227,82 @@ bool mesh_db_get_addr_range(uint16_t *low, uint16_t *high)
 	return true;
 }
 
-bool mesh_db_set_addr_range(uint16_t low, uint16_t high)
+/*
+ * This is a simplistic implementation onf allocated range, where
+ * the range is one contiguous chunk of the address space.
+ */
+static bool add_range(json_object *jobj, const char *keyword, uint16_t low,
+								uint16_t high)
 {
+	json_object *jarray, *jrange;
+
+	jrange = json_object_new_object();
+
+	if (!write_uint16_hex(jrange, "lowAddress", low))
+		goto fail;
+
+	if (!write_uint16_hex(jrange, "highAddress", high))
+		goto fail;
+
+	jarray = json_object_new_array();
+	if (!jarray)
+		goto fail;
+
+	json_object_array_add(jarray, jrange);
+	json_object_object_add(jobj, keyword, jarray);
+
+	return true;
+
+fail:
+	json_object_put(jrange);
+
+	return false;
+}
+
+bool mesh_db_add_provisioner(const char *name, uint8_t uuid[16],
+				uint16_t unicast_low, uint16_t unicast_high,
+					uint16_t group_low, uint16_t group_high)
+{
+	json_object *jprovs, *jprov, *jscenes;
+
 	if (!cfg || !cfg->jcfg)
 		return false;
 
-	if (!write_uint16_hex(cfg->jcfg, "low", low))
+	if (!json_object_object_get_ex(cfg->jcfg, "provisioners", &jprovs))
 		return false;
 
-	if (!write_uint16_hex(cfg->jcfg, "high", high))
+	if (!jprovs || json_object_get_type(jprovs) != json_type_array)
 		return false;
 
+	jprov = json_object_new_object();
+
+	if (!add_string(jprov, "provisionerName", name))
+		goto fail;
+
+	if (!add_u8_16(jprov, "UUID", uuid))
+		goto fail;
+
+	if (!add_range(jprov, "allocatedUnicastRange", unicast_low,
+								unicast_high))
+		goto fail;
+
+	if (!add_range(jprov, "allocatedGroupRange", group_low, group_high))
+		goto fail;
+
+	/* Scenes are not supported. Just add an empty array */
+	jscenes = json_object_new_array();
+	if (!jscenes)
+		goto fail;
+
+	json_object_object_add(jprov, "allocatedSceneRange", jscenes);
+
+	json_object_array_add(jprovs, jprov);
+
 	return save_config();
+
+fail:
+	json_object_put(jprov);
+	return false;
 }
 
 uint32_t mesh_db_get_iv_index(void)
@@ -1408,10 +1490,10 @@ bool mesh_db_create(const char *fname, const uint8_t token[8],
 
 	l_uuid_v4(uuid);
 
-	if (!add_u8_16(jcfg, "uuid", uuid))
+	if (!add_u8_16(jcfg, "meshUUID", uuid))
 		goto fail;
 
-	if (mesh_name && !add_string(jcfg, "name", mesh_name))
+	if (mesh_name && !add_string(jcfg, "meshName", mesh_name))
 		goto fail;
 
 	jarray = json_object_new_array();
@@ -1420,6 +1502,12 @@ bool mesh_db_create(const char *fname, const uint8_t token[8],
 
 	json_object_object_add(jcfg, "nodes", jarray);
 
+	jarray = json_object_new_array();
+	if (!jarray)
+		goto fail;
+
+	json_object_object_add(jcfg, "provisioners", jarray);
+
 	jarray = json_object_new_array();
 	if (!jarray)
 		goto fail;
diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h
index 22518c618..efd579553 100644
--- a/tools/mesh/mesh-db.h
+++ b/tools/mesh/mesh-db.h
@@ -24,7 +24,6 @@ bool mesh_db_net_key_del(uint16_t idx);
 bool mesh_db_net_key_phase_set(uint16_t net_idx, uint8_t phase);
 bool mesh_db_app_key_add(uint16_t net_idx, uint16_t app_idx);
 bool mesh_db_app_key_del(uint16_t app_idx);
-bool mesh_db_set_addr_range(uint16_t low, uint16_t high);
 bool mesh_db_get_addr_range(uint16_t *low, uint16_t *high);
 
 bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 02/20] tools/mesh-cfgclient: Add timestamp to config database
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 01/20] tools/mesh-cfgclient: Save provisioner info Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 03/20] tools/mesh-cfgclient: Update stored NetKey and AppKey Inga Stotland
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This adds timestamp property to the configuration database.
The UTC timestamp format: YYYY-MM-DDThh:mm:ssZ
---
 tools/mesh/mesh-db.c | 56 +++++++++++++++++++++++++++-----------------
 1 file changed, 35 insertions(+), 21 deletions(-)

diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 50fbc18cb..d42587da8 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -3,7 +3,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2019  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2019-2020  Intel Corporation. All rights reserved.
  *
  *
  */
@@ -20,10 +20,9 @@
 #include <libgen.h>
 #include <stdio.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
-#include <sys/time.h>
-
 #include <ell/ell.h>
 #include <json-c/json.h>
 
@@ -42,13 +41,39 @@ struct mesh_db {
 	json_object *jcfg;
 	char *cfg_fname;
 	uint8_t token[8];
-	struct timeval write_time;
 };
 
 static struct mesh_db *cfg;
 static const char *bak_ext = ".bak";
 static const char *tmp_ext = ".tmp";
 
+static bool add_string(json_object *jobj, const char *desc, const char *str)
+{
+	json_object *jstring = json_object_new_string(str);
+
+	if (!jstring)
+		return false;
+
+	json_object_object_add(jobj, desc, jstring);
+	return true;
+}
+
+static bool set_timestamp(json_object *jobj)
+{
+	time_t time_raw;
+	struct tm *tp;
+	char buf[80];
+
+	time(&time_raw);
+	tp = gmtime(&time_raw);
+
+	strftime(buf, 80, "%FT%TZ", tp);
+
+	json_object_object_del(jobj, "timestamp");
+
+	return add_string(jobj, "timestamp", buf);
+}
+
 static bool save_config_file(const char *fname)
 {
 	FILE *outfile;
@@ -61,6 +86,8 @@ static bool save_config_file(const char *fname)
 		return false;
 	}
 
+	set_timestamp(cfg->jcfg);
+
 	str = json_object_to_json_string_ext(cfg->jcfg,
 						JSON_C_TO_STRING_PRETTY);
 
@@ -97,8 +124,6 @@ static bool save_config(void)
 	l_free(fname_tmp);
 	l_free(fname_bak);
 
-	gettimeofday(&cfg->write_time, NULL);
-
 	return result;
 }
 
@@ -289,17 +314,6 @@ static bool add_u8_16(json_object *jobj, const char *desc,
 	return true;
 }
 
-static bool add_string(json_object *jobj, const char *desc, const char *str)
-{
-	json_object *jstring = json_object_new_string(str);
-
-	if (!jstring)
-		return false;
-
-	json_object_object_add(jobj, desc, jstring);
-	return true;
-}
-
 static bool get_token(json_object *jobj, uint8_t token[8])
 {
 	json_object *jval;
@@ -726,6 +740,9 @@ bool mesh_db_net_key_add(uint16_t net_idx)
 	if (!write_int(jkey, "phase", KEY_REFRESH_PHASE_NONE))
 		goto fail;
 
+	if (!set_timestamp(jkey))
+		goto fail;
+
 	json_object_array_add(jarray, jkey);
 
 	return save_config();
@@ -960,10 +977,7 @@ bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
 
 	json_object_array_add(jnodes, jnode);
 
-	if (!save_config())
-		goto fail;
-
-	return true;
+	return save_config();
 
 fail:
 	json_object_put(jnode);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 03/20] tools/mesh-cfgclient: Update stored NetKey and AppKey
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 01/20] tools/mesh-cfgclient: Save provisioner info Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 02/20] tools/mesh-cfgclient: Add timestamp to config database Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 04/20] tools/mesh-cfgclient: Keep track of updated keys Inga Stotland
                   ` (17 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

NetKeys:
- Record additional properties: "name", "minSecurity", "timestamp"

AppKeys:
- Record additional property: "name"
---
 tools/mesh/mesh-db.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index d42587da8..5b3c4b2f7 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -517,6 +517,7 @@ static void load_remotes(json_object *jcfg)
 static bool add_app_key(json_object *jobj, uint16_t net_idx, uint16_t app_idx)
 {
 	json_object *jkey, *jarray;
+	char buf[12];
 
 	json_object_object_get_ex(jobj, "appKeys", &jarray);
 	if (!jarray || json_object_get_type(jarray) != json_type_array)
@@ -524,6 +525,11 @@ static bool add_app_key(json_object *jobj, uint16_t net_idx, uint16_t app_idx)
 
 	jkey = json_object_new_object();
 
+	snprintf(buf, 12, "AppKey %4.4x", app_idx);
+
+	if (!add_string(jkey, "name", buf))
+		goto fail;
+
 	if (!write_int(jkey, "boundNetKey", (int)net_idx))
 		goto fail;
 
@@ -721,6 +727,7 @@ static bool load_keys(json_object *jobj)
 bool mesh_db_net_key_add(uint16_t net_idx)
 {
 	json_object *jkey, *jarray;
+	char buf[12];
 
 	if (!cfg || !cfg->jcfg)
 		return false;
@@ -734,12 +741,20 @@ bool mesh_db_net_key_add(uint16_t net_idx)
 
 	jkey = json_object_new_object();
 
+	snprintf(buf, 12, "Subnet %4.4x", net_idx);
+
+	if (!add_string(jkey, "name", buf))
+		goto fail;
+
 	if (!write_int(jkey, "index", net_idx))
 		goto fail;
 
 	if (!write_int(jkey, "phase", KEY_REFRESH_PHASE_NONE))
 		goto fail;
 
+	if (!add_string(jkey, "minSecurity", "secure"))
+		goto fail;
+
 	if (!set_timestamp(jkey))
 		goto fail;
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 04/20] tools/mesh-cfgclient: Keep track of updated keys
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (2 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 03/20] tools/mesh-cfgclient: Update stored NetKey and AppKey Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 05/20] tools/mesh: Add new info to stored remote nodes Inga Stotland
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

---
 tools/mesh/cfgcli.c  |  33 ++++----
 tools/mesh/mesh-db.c | 151 ++++++++++++++++++++++++++++++-----
 tools/mesh/mesh-db.h |   2 +
 tools/mesh/remote.c  | 182 +++++++++++++++++++++++++++++++++----------
 tools/mesh/remote.h  |   9 ++-
 5 files changed, 299 insertions(+), 78 deletions(-)

diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index d8eee4edc..485d13b5a 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -447,13 +447,12 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		if (!cmd)
 			break;
 
-		if (cmd->opcode == OP_APPKEY_ADD) {
-			if (remote_add_app_key(src, app_idx))
-				mesh_db_node_app_key_add(src, app_idx);
-		} else if (cmd->opcode == OP_APPKEY_DELETE) {
-			if (remote_del_app_key(src, app_idx))
-				mesh_db_node_app_key_del(src, app_idx);
-		}
+		if (cmd->opcode == OP_APPKEY_ADD)
+			remote_add_app_key(src, app_idx, true);
+		else if (cmd->opcode == OP_APPKEY_DELETE)
+			remote_del_app_key(src, app_idx);
+		else if (cmd->opcode == OP_APPKEY_UPDATE)
+			remote_update_app_key(src, app_idx, true, true);
 
 		break;
 
@@ -492,13 +491,12 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		if (!cmd)
 			break;
 
-		if (cmd->opcode == OP_NETKEY_ADD) {
-			if (remote_add_net_key(src, net_idx))
-				mesh_db_node_net_key_add(src, net_idx);
-		} else if (cmd->opcode == OP_NETKEY_DELETE) {
-			if (remote_del_net_key(src, net_idx))
-				mesh_db_node_net_key_del(src, net_idx);
-		}
+		if (cmd->opcode == OP_NETKEY_ADD)
+			remote_add_net_key(src, net_idx, true);
+		else if (cmd->opcode == OP_NETKEY_DELETE)
+			remote_del_net_key(src, net_idx);
+		else if (cmd->opcode == OP_NETKEY_UPDATE)
+			remote_update_net_key(src, net_idx, true, true);
 
 		break;
 
@@ -534,6 +532,13 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 		bt_shell_printf("\tNetKey %u (0x%3.3x)\n", net_idx, net_idx);
 		bt_shell_printf("\tKR Phase %2.2x\n", data[3]);
+
+		if (data[0] != MESH_STATUS_SUCCESS)
+			return true;
+
+		if (data[3] == KEY_REFRESH_PHASE_NONE)
+			remote_finish_key_refresh(src, net_idx);
+
 		break;
 
 	case OP_MODEL_APP_STATUS:
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 5b3c4b2f7..a57a5d547 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -200,6 +200,38 @@ static bool write_int(json_object *jobj, const char *keyword, int val)
 	return true;
 }
 
+static bool get_bool(json_object *jobj, const char *keyword, bool *value)
+{
+	json_object *jvalue;
+
+	if (!json_object_object_get_ex(jobj, keyword, &jvalue))
+		return false;
+
+	if (json_object_get_type(jvalue) != json_type_boolean) {
+		l_error("Error: %s should contain a boolean value\n",
+								keyword);
+		return false;
+	}
+
+	*value = json_object_get_boolean(jvalue);
+
+	return true;
+}
+
+static bool write_bool(json_object *jobj, const char *keyword, bool val)
+{
+	json_object *jval;
+
+	json_object_object_del(jobj, keyword);
+
+	jval = json_object_new_boolean(val);
+	if (!jval)
+		return false;
+
+	json_object_object_add(jobj, keyword, jval);
+	return true;
+}
+
 static json_object *get_key_object(json_object *jarray, uint16_t idx)
 {
 	int i, sz = json_object_array_length(jarray);
@@ -347,6 +379,20 @@ static uint16_t node_parse_key(json_object *jarray, int i)
 	return (uint16_t)idx;
 }
 
+static bool node_check_key_updated(json_object *jarray, int i, bool *updated)
+{
+	json_object *jkey;
+
+	jkey = json_object_array_get_idx(jarray, i);
+	if (!jkey)
+		return false;
+
+	if (!get_bool(jkey, "updated", updated))
+		return false;
+
+	return true;
+}
+
 static int compare_group_addr(const void *a, const void *b, void *user_data)
 {
 	const struct mesh_group *grp0 = a;
@@ -483,10 +529,17 @@ static void load_remotes(json_object *jcfg)
 		remote_add_node((const uint8_t *)uuid, unicast, ele_cnt,
 								key_idx);
 		for (j = 1; j < key_cnt; j++) {
+			bool updated = false;
+
 			key_idx = node_parse_key(jarray, j);
 
-			if (key_idx != KEY_IDX_INVALID)
-				remote_add_net_key(unicast, key_idx);
+			if (key_idx == KEY_IDX_INVALID)
+				continue;
+
+			remote_add_net_key(unicast, key_idx, false);
+
+			node_check_key_updated(jarray, j, &updated);
+			remote_update_net_key(unicast, key_idx, updated, false);
 		}
 
 		json_object_object_get_ex(jnode, "appKeys", &jarray);
@@ -496,10 +549,17 @@ static void load_remotes(json_object *jcfg)
 		key_cnt = json_object_array_length(jarray);
 
 		for (j = 0; j < key_cnt; j++) {
+			bool updated = false;
+
 			key_idx = node_parse_key(jarray, j);
 
-			if (key_idx != KEY_IDX_INVALID)
-				remote_add_app_key(unicast, key_idx);
+			if (key_idx == KEY_IDX_INVALID)
+				continue;
+
+			remote_add_app_key(unicast, key_idx, false);
+
+			node_check_key_updated(jarray, j, &updated);
+			remote_update_app_key(unicast, key_idx, updated, false);
 		}
 
 		load_composition(jnode, unicast);
@@ -554,28 +614,19 @@ static bool add_node_key(json_object *jobj, const char *desc, uint16_t idx)
 
 	jkey = json_object_new_object();
 
-	if (!write_int(jkey, "index", (int)idx)) {
-		json_object_put(jkey);
-		return false;
-	}
+	if (!write_int(jkey, "index", (int)idx))
+		goto fail;
+
+	if (!write_bool(jkey, "updated", false))
+		goto fail;
 
 	json_object_array_add(jarray, jkey);
 
 	return save_config();
-}
-
-bool mesh_db_node_net_key_add(uint16_t unicast, uint16_t idx)
-{
-	json_object *jnode;
-
-	if (!cfg || !cfg->jcfg)
-		return false;
 
-	jnode = get_node_by_unicast(unicast);
-	if (!jnode)
-		return false;
-
-	return add_node_key(jnode, "netKeys", idx);
+fail:
+	json_object_put(jkey);
+	return false;
 }
 
 bool mesh_db_node_ttl_set(uint16_t unicast, uint8_t ttl)
@@ -628,6 +679,20 @@ static bool delete_key(json_object *jobj, const char *desc, uint16_t idx)
 	return save_config();
 }
 
+bool mesh_db_node_net_key_add(uint16_t unicast, uint16_t idx)
+{
+	json_object *jnode;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jnode = get_node_by_unicast(unicast);
+	if (!jnode)
+		return false;
+
+	return add_node_key(jnode, "netKeys", idx);
+}
+
 bool mesh_db_node_net_key_del(uint16_t unicast, uint16_t net_idx)
 {
 	json_object *jnode;
@@ -642,6 +707,45 @@ bool mesh_db_node_net_key_del(uint16_t unicast, uint16_t net_idx)
 	return delete_key(jnode, "netKeys", net_idx);
 }
 
+static bool key_update(uint16_t unicast, int16_t idx, bool updated,
+							const char *desc)
+{
+	json_object *jnode, *jarray;
+	int i, sz;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jnode = get_node_by_unicast(unicast);
+	if (!jnode)
+		return false;
+
+	if (!json_object_object_get_ex(jnode, desc, &jarray))
+		return false;
+
+	sz = json_object_array_length(jarray);
+
+	for (i = 0; i < sz; ++i) {
+		json_object *jentry;
+		int val;
+
+		jentry = json_object_array_get_idx(jarray, i);
+
+		if (!get_int(jentry, "index", &val))
+			continue;
+
+		if ((val == idx) && write_bool(jentry, "updated", updated))
+			return save_config();
+	}
+
+	return false;
+}
+
+bool mesh_db_node_net_key_update(uint16_t unicast, uint16_t idx, bool updated)
+{
+	return key_update(unicast, idx, updated, "netKeys");
+}
+
 bool mesh_db_node_app_key_add(uint16_t unicast, uint16_t idx)
 {
 	json_object *jnode;
@@ -670,6 +774,11 @@ bool mesh_db_node_app_key_del(uint16_t unicast, uint16_t idx)
 	return delete_key(jnode, "appKeys", idx);
 }
 
+bool mesh_db_node_app_key_update(uint16_t unicast, uint16_t idx, bool updated)
+{
+	return key_update(unicast, idx, updated, "appKeys");
+}
+
 static bool load_keys(json_object *jobj)
 {
 	json_object *jarray, *jentry;
diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h
index efd579553..c1bcb3ae6 100644
--- a/tools/mesh/mesh-db.h
+++ b/tools/mesh/mesh-db.h
@@ -38,8 +38,10 @@ bool mesh_db_node_set_net_transmit(uint16_t unicast, uint8_t cnt,
 							uint16_t interval);
 bool mesh_db_node_net_key_add(uint16_t unicast, uint16_t idx);
 bool mesh_db_node_net_key_del(uint16_t unicast, uint16_t idx);
+bool mesh_db_node_net_key_update(uint16_t unicast, uint16_t idx, bool updated);
 bool mesh_db_node_app_key_add(uint16_t unicast, uint16_t idx);
 bool mesh_db_node_app_key_del(uint16_t unicast, uint16_t idx);
+bool mesh_db_node_app_key_update(uint16_t unicast, uint16_t idx, bool updated);
 bool mesh_db_node_ttl_set(uint16_t unicast, uint8_t ttl);
 bool mesh_db_node_write_mode(uint16_t unicast, const char *keyword, int value);
 bool mesh_db_node_model_binding_add(uint16_t unicast, uint8_t ele, bool vendor,
diff --git a/tools/mesh/remote.c b/tools/mesh/remote.c
index e60a3681d..5f598cb8b 100644
--- a/tools/mesh/remote.c
+++ b/tools/mesh/remote.c
@@ -25,6 +25,11 @@
 
 #define abs_diff(a, b) ((a) > (b) ? (a) - (b) : (b) - (a))
 
+struct remote_key {
+	uint16_t idx;
+	bool updated;
+};
+
 struct remote_node {
 	uint16_t unicast;
 	struct l_queue *net_keys;
@@ -42,20 +47,6 @@ struct rejected_addr {
 static struct l_queue *nodes;
 static struct l_queue *reject_list;
 
-static bool key_present(struct l_queue *keys, uint16_t app_idx)
-{
-	const struct l_queue_entry *l;
-
-	for (l = l_queue_get_entries(keys); l; l = l->next) {
-		uint16_t idx = L_PTR_TO_UINT(l->data);
-
-		if (idx == app_idx)
-			return true;
-	}
-
-	return false;
-}
-
 static int compare_mod_id(const void *a, const void *b, void *user_data)
 {
 	uint32_t id1 = L_PTR_TO_UINT(a);
@@ -102,12 +93,20 @@ static bool match_node_addr(const void *a, const void *b)
 	return false;
 }
 
+static bool match_key(const void *a, const void *b)
+{
+	const struct remote_key *key = a;
+	uint16_t idx = L_PTR_TO_UINT(b);
+
+	return (key->idx == idx);
+}
+
 static bool match_bound_key(const void *a, const void *b)
 {
-	uint16_t app_idx = L_PTR_TO_UINT(a);
+	const struct remote_key *app_key = a;
 	uint16_t net_idx = L_PTR_TO_UINT(b);
 
-	return (net_idx == keys_get_bound_key(app_idx));
+	return (net_idx == keys_get_bound_key(app_key->idx));
 }
 
 uint8_t remote_del_node(uint16_t unicast)
@@ -142,6 +141,7 @@ bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
 					uint8_t ele_cnt, uint16_t net_idx)
 {
 	struct remote_node *rmt;
+	struct remote_key *key;
 
 	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(unicast));
 	if (rmt)
@@ -153,7 +153,10 @@ bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
 	rmt->num_ele = ele_cnt;
 	rmt->net_keys = l_queue_new();
 
-	l_queue_push_tail(rmt->net_keys, L_UINT_TO_PTR(net_idx));
+	key = l_new(struct remote_key, 1);
+	key->idx = net_idx;
+
+	l_queue_push_tail(rmt->net_keys, key);
 
 	rmt->els = l_new(struct l_queue *, ele_cnt);
 
@@ -161,6 +164,7 @@ bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
 		nodes = l_queue_new();
 
 	l_queue_insert(nodes, rmt, compare_unicast, NULL);
+
 	return true;
 }
 
@@ -188,49 +192,84 @@ bool remote_set_model(uint16_t unicast, uint8_t ele_idx, uint32_t mod_id,
 	return true;
 }
 
-bool remote_add_net_key(uint16_t addr, uint16_t net_idx)
+bool remote_add_net_key(uint16_t addr, uint16_t net_idx, bool save)
 {
 	struct remote_node *rmt;
+	struct remote_key *key;
 
 	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
 	if (!rmt)
 		return false;
 
-	if (key_present(rmt->net_keys, net_idx))
-		return false;
+	if (l_queue_find(rmt->net_keys, match_key, L_UINT_TO_PTR(net_idx)))
+		return true;
 
-	l_queue_push_tail(rmt->net_keys, L_UINT_TO_PTR(net_idx));
-	return true;
+	key = l_new(struct remote_key, 1);
+	key->idx = net_idx;
+
+	l_queue_push_tail(rmt->net_keys, key);
+
+	if (save)
+		return mesh_db_node_net_key_add(addr, net_idx);
+	else
+		return true;
 }
 
 bool remote_del_net_key(uint16_t addr, uint16_t net_idx)
 {
 	struct remote_node *rmt;
-	void *data;
+	struct remote_key *key;
 
 	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
 	if (!rmt)
 		return false;
 
-	if (!l_queue_remove(rmt->net_keys, L_UINT_TO_PTR(net_idx)))
+	key = l_queue_remove_if(rmt->net_keys, match_key,
+							L_UINT_TO_PTR(net_idx));
+	if (!key)
 		return false;
 
-	data = l_queue_remove_if(rmt->app_keys, match_bound_key,
+	mesh_db_node_net_key_del(addr, net_idx);
+
+	l_free(key);
+	key = l_queue_remove_if(rmt->app_keys, match_bound_key,
 						L_UINT_TO_PTR(net_idx));
-	while (data) {
-		uint16_t app_idx = (uint16_t) L_PTR_TO_UINT(data);
 
-		mesh_db_node_app_key_del(rmt->unicast, app_idx);
-		data = l_queue_remove_if(rmt->app_keys, match_bound_key,
+	while (key) {
+		mesh_db_node_app_key_del(rmt->unicast, key->idx);
+		l_free(key);
+
+		key = l_queue_remove_if(rmt->app_keys, match_bound_key,
 						L_UINT_TO_PTR(net_idx));
 	}
 
 	return true;
 }
 
-bool remote_add_app_key(uint16_t addr, uint16_t app_idx)
+bool remote_update_net_key(uint16_t addr, uint16_t net_idx, bool update,
+								bool save)
+{
+	struct remote_node *rmt;
+	struct remote_key *key;
+
+	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
+	if (!rmt)
+		return false;
+
+	key = l_queue_find(rmt->net_keys, match_key,
+						L_UINT_TO_PTR(net_idx));
+	key->updated = update;
+
+	if (save)
+		return mesh_db_node_net_key_update(addr, net_idx, update);
+	else
+		return true;
+}
+
+bool remote_add_app_key(uint16_t addr, uint16_t app_idx, bool save)
 {
 	struct remote_node *rmt;
+	struct remote_key *key;
 
 	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
 	if (!rmt)
@@ -239,44 +278,105 @@ bool remote_add_app_key(uint16_t addr, uint16_t app_idx)
 	if (!rmt->app_keys)
 		rmt->app_keys = l_queue_new();
 
-	if (key_present(rmt->app_keys, app_idx))
-		return false;
+	if (l_queue_find(rmt->app_keys, match_key, L_UINT_TO_PTR(app_idx)))
+		return true;
 
-	l_queue_push_tail(rmt->app_keys, L_UINT_TO_PTR(app_idx));
-	return true;
+	key = l_new(struct remote_key, 1);
+	key->idx = app_idx;
+
+	l_queue_push_tail(rmt->app_keys, key);
+
+	if (save)
+		return mesh_db_node_app_key_add(addr, app_idx);
+	else
+		return true;
 }
 
 bool remote_del_app_key(uint16_t addr, uint16_t app_idx)
 {
 	struct remote_node *rmt;
+	struct remote_key *key;
+
+	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
+	if (!rmt)
+		return false;
+
+	key = l_queue_remove_if(rmt->app_keys, match_key,
+						L_UINT_TO_PTR(app_idx));
+	l_free(key);
+
+	return mesh_db_node_app_key_del(addr, app_idx);
+}
+
+bool remote_update_app_key(uint16_t addr, uint16_t app_idx, bool update,
+								bool save)
+{
+	struct remote_node *rmt;
+	struct remote_key *key;
 
 	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
 	if (!rmt)
 		return false;
 
-	return l_queue_remove(rmt->app_keys, L_UINT_TO_PTR(app_idx));
+	key = l_queue_find(rmt->app_keys, match_key,
+						L_UINT_TO_PTR(app_idx));
+	key->updated = update;
+
+	if (save)
+		return mesh_db_node_app_key_update(addr, app_idx, update);
+	else
+		return true;
+}
+
+void remote_finish_key_refresh(uint16_t addr, uint16_t net_idx)
+{
+	struct remote_node *rmt;
+	struct remote_key *key;
+	const struct l_queue_entry *l;
+
+	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
+	if (!rmt)
+		return;
+
+	if (!remote_update_net_key(addr, net_idx, false, true))
+		return;
+
+	l = l_queue_get_entries(rmt->app_keys);
+
+	for (; l; l = l->next) {
+		key = l->data;
+
+		if (net_idx != keys_get_bound_key(key->idx))
+			continue;
+
+		key->updated = false;
+
+		mesh_db_node_app_key_update(addr, key->idx, false);
+	}
+
 }
 
 uint16_t remote_get_subnet_idx(uint16_t addr)
 {
 	struct remote_node *rmt;
-	uint32_t net_idx;
+	struct remote_key *key;
 
 	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
 
 	if (!rmt || l_queue_isempty(rmt->net_keys))
 		return NET_IDX_INVALID;
 
-	net_idx = L_PTR_TO_UINT(l_queue_peek_head(rmt->net_keys));
+	key = l_queue_peek_head(rmt->net_keys);
 
-	return (uint16_t) net_idx;
+	return key->idx;
 }
 
-static void print_key(void *key, void *user_data)
+static void print_key(void *data, void *user_data)
 {
-	uint16_t idx = L_PTR_TO_UINT(key);
+	struct remote_key *key = data;
 
-	bt_shell_printf("%u (0x%3.3x), ", idx, idx);
+	bt_shell_printf("%u (0x%3.3x) %s, ", key->idx, key->idx,
+						key->updated ? ", updated":"");
 }
 
 static void print_model(void *model, void *user_data)
diff --git a/tools/mesh/remote.h b/tools/mesh/remote.h
index 8ecb097ae..74747689a 100644
--- a/tools/mesh/remote.h
+++ b/tools/mesh/remote.h
@@ -16,10 +16,15 @@ bool remote_set_model(uint16_t unicast, uint8_t ele_idx, uint32_t mod_id,
 void remote_add_rejected_address(uint16_t addr, uint32_t iv_index, bool save);
 void remote_clear_rejected_addresses(uint32_t iv_index);
 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_add_net_key(uint16_t addr, uint16_t net_idx, bool save);
 bool remote_del_net_key(uint16_t addr, uint16_t net_idx);
-bool remote_add_app_key(uint16_t addr, uint16_t app_idx);
+bool remote_update_net_key(uint16_t addr, uint16_t net_idx, bool update,
+								bool save);
+bool remote_add_app_key(uint16_t addr, uint16_t app_idx, bool save);
 bool remote_del_app_key(uint16_t addr, uint16_t app_idx);
+bool remote_update_app_key(uint16_t addr, uint16_t app_idx, bool update,
+								bool save);
+void remote_finish_key_refresh(uint16_t addr, uint16_t net_idx);
 uint16_t remote_get_subnet_idx(uint16_t addr);
 void remote_print_node(uint16_t addr);
 void remote_print_all(void);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 05/20] tools/mesh: Add new info to stored remote nodes
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (3 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 04/20] tools/mesh-cfgclient: Keep track of updated keys Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 06/20] tools/mesh-cfgclient: Overwrite config values when adding new ones Inga Stotland
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

When adding a new remote node into configuration database,
initialize additional default properties:
"blacklisted": false
"security": "secure"
"configComplete": false
---
 tools/mesh/mesh-db.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index a57a5d547..bf9344931 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -1075,7 +1075,18 @@ bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
 	if (!add_u8_16(jnode, "UUID", uuid))
 		goto fail;
 
+
+	if (!add_string(jnode, "security", "secure"))
+		goto fail;
+
+	if (!write_bool(jnode, "blacklisted", false))
+		goto fail;
+
+	if (!write_bool(jnode, "configComplete", false))
+		goto fail;
+
 	jelements = init_elements(num_els);
+
 	json_object_object_add(jnode, "elements", jelements);
 
 	jnetkeys = json_object_new_array();
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 06/20] tools/mesh-cfgclient: Overwrite config values when adding new ones
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (4 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 05/20] tools/mesh: Add new info to stored remote nodes Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 07/20] tools/mesh-cfgclient: Store remote node's model bindings Inga Stotland
                   ` (14 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This changes common utilities used in mesh-db.c to replace old values
by default whenever a new value is written.
---
 tools/mesh/mesh-db.c | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index bf9344931..f0e0aeb71 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -54,6 +54,9 @@ static bool add_string(json_object *jobj, const char *desc, const char *str)
 	if (!jstring)
 		return false;
 
+	/* Overwrite old value if present */
+	json_object_object_del(jobj, desc);
+
 	json_object_object_add(jobj, desc, jstring);
 	return true;
 }
@@ -69,8 +72,6 @@ static bool set_timestamp(json_object *jobj)
 
 	strftime(buf, 80, "%FT%TZ", tp);
 
-	json_object_object_del(jobj, "timestamp");
-
 	return add_string(jobj, "timestamp", buf);
 }
 
@@ -190,12 +191,13 @@ static bool write_int(json_object *jobj, const char *keyword, int val)
 {
 	json_object *jval;
 
-	json_object_object_del(jobj, keyword);
-
 	jval = json_object_new_int(val);
 	if (!jval)
 		return false;
 
+	/* Overwrite old value if present */
+	json_object_object_del(jobj, keyword);
+
 	json_object_object_add(jobj, keyword, jval);
 	return true;
 }
@@ -222,12 +224,13 @@ static bool write_bool(json_object *jobj, const char *keyword, bool val)
 {
 	json_object *jval;
 
-	json_object_object_del(jobj, keyword);
-
 	jval = json_object_new_boolean(val);
 	if (!jval)
 		return false;
 
+	/* Overwrite old value if present */
+	json_object_object_del(jobj, keyword);
+
 	json_object_object_add(jobj, keyword, jval);
 	return true;
 }
@@ -262,6 +265,9 @@ static bool write_uint16_hex(json_object *jobj, const char *desc,
 	if (!jstring)
 		return false;
 
+	/* Overwrite old value if present */
+	json_object_object_del(jobj, desc);
+
 	json_object_object_add(jobj, desc, jstring);
 	return true;
 }
@@ -327,6 +333,9 @@ static bool add_u8_8(json_object *jobj, const char *desc,
 	if (!jstring)
 		return false;
 
+	/* Overwrite old value if present */
+	json_object_object_del(jobj, desc);
+
 	json_object_object_add(jobj, desc, jstring);
 	return true;
 }
@@ -342,6 +351,9 @@ static bool add_u8_16(json_object *jobj, const char *desc,
 	if (!jstring)
 		return false;
 
+	/* Overwrite old value if present */
+	json_object_object_del(jobj, desc);
+
 	json_object_object_add(jobj, desc, jstring);
 	return true;
 }
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 07/20] tools/mesh-cfgclient: Store remote node's model bindings
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (5 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 06/20] tools/mesh-cfgclient: Overwrite config values when adding new ones Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 08/20] tools/mesh-cfgclient: Store remote node's model subs Inga Stotland
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

Update remote node's model binding after a successful completion
of "bind" or "unbind" commands.
---
 tools/mesh/cfgcli.c  |  14 +++-
 tools/mesh/mesh-db.c | 166 ++++++++++++++++++++++++++++++++++++++++++-
 tools/mesh/mesh-db.h |   4 +-
 3 files changed, 178 insertions(+), 6 deletions(-)

diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index 485d13b5a..c3241a9b7 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -340,7 +340,7 @@ static void print_pub(uint16_t ele_addr, uint32_t mod_id,
 		bt_shell_printf("\tModel: %8.8x\n", mod_id);
 	else
 		bt_shell_printf("\tModel: %4.4x\n",
-				(uint16_t) (mod_id & 0xffff));
+					(uint16_t) (mod_id & ~VENDOR_ID_MASK));
 
 	bt_shell_printf("\tApp Key Idx: %u (0x%3.3x)\n", pub->app_idx,
 								pub->app_idx);
@@ -552,10 +552,20 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 		bt_shell_printf("Element Addr\t%4.4x\n", addr);
 
-		print_mod_id(data + 5, len == 9, "");
+		mod_id = print_mod_id(data + 5, len == 9, "");
 
 		bt_shell_printf("AppIdx\t\t%u (0x%3.3x)\n ", app_idx, app_idx);
 
+		if (data[0] != MESH_STATUS_SUCCESS || !cmd)
+			break;
+
+		if (cmd->opcode == OP_MODEL_APP_BIND)
+			mesh_db_node_model_bind(src, addr, len == 9, mod_id,
+								app_idx);
+		else
+			mesh_db_node_model_unbind(src, addr, len == 9, mod_id,
+								app_idx);
+
 		break;
 
 	case OP_NODE_IDENTITY_STATUS:
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index f0e0aeb71..da5817acf 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -658,6 +658,162 @@ bool mesh_db_node_ttl_set(uint16_t unicast, uint8_t ttl)
 	return save_config();
 }
 
+static json_object *get_element(uint16_t unicast, uint16_t ele_addr)
+{
+	json_object *jnode, *jarray;
+	int i, ele_cnt;
+
+	jnode = get_node_by_unicast(unicast);
+	if (!jnode)
+		return false;
+
+	if (!json_object_object_get_ex(jnode, "elements", &jarray))
+		return NULL;
+
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return NULL;
+
+	ele_cnt = json_object_array_length(jarray);
+
+	for (i = 0; i < ele_cnt; ++i) {
+		json_object *jentry, *jval;
+		int32_t index;
+
+		jentry = json_object_array_get_idx(jarray, i);
+		if (!json_object_object_get_ex(jentry, "index", &jval))
+			return NULL;
+
+		index = json_object_get_int(jval);
+		if (index > 0xff)
+			return NULL;
+
+		if (ele_addr == unicast + index)
+			return jentry;
+	}
+
+	return NULL;
+}
+
+static json_object *get_model(uint16_t unicast, uint16_t ele_addr,
+						uint32_t mod_id, bool vendor)
+{
+	json_object *jelement, *jarray;
+	int i, sz;
+
+	jelement = get_element(unicast, ele_addr);
+	if (!jelement)
+		return false;
+
+	if (!json_object_object_get_ex(jelement, "models", &jarray))
+		return NULL;
+
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return NULL;
+
+	if (!vendor)
+		mod_id = mod_id & ~VENDOR_ID_MASK;
+
+	sz = json_object_array_length(jarray);
+
+	for (i = 0; i < sz; ++i) {
+		json_object *jentry, *jval;
+		uint32_t id, len;
+		const char *str;
+
+		jentry = json_object_array_get_idx(jarray, i);
+		if (!json_object_object_get_ex(jentry, "modelId",
+								&jval))
+			return NULL;
+
+		str = json_object_get_string(jval);
+		len = strlen(str);
+		if (len != 4 && len != 8)
+			return NULL;
+
+		if ((len == 4 && vendor) || (len == 8 && !vendor))
+			continue;
+
+		if (sscanf(str, "%08x", &id) != 1)
+			return NULL;
+
+		if (id == mod_id)
+			return jentry;
+	}
+
+	return NULL;
+}
+
+static void jarray_int_del(json_object *jarray, int val)
+{
+	int i, sz = json_object_array_length(jarray);
+
+	for (i = 0; i < sz; ++i) {
+		json_object *jentry;
+
+		jentry = json_object_array_get_idx(jarray, i);
+
+		if (val == json_object_get_int(jentry)) {
+			json_object_array_del_idx(jarray, i, 1);
+			return;
+		}
+	}
+}
+
+static bool update_model_int_array(uint16_t unicast, uint16_t ele_addr,
+					bool vendor, uint32_t mod_id,
+					int val, const char *keyword, bool add)
+{
+	json_object *jarray, *jmod, *jvalue;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jmod = get_model(unicast, ele_addr, mod_id, vendor);
+	if (!jmod)
+		return false;
+
+	if (!json_object_object_get_ex(jmod, keyword, &jarray))
+		return false;
+
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	jarray_int_del(jarray, val);
+
+	if (!add)
+		return true;
+
+	jvalue = json_object_new_int(val);
+	if (!jvalue)
+		return false;
+
+	json_object_array_add(jarray, jvalue);
+
+	return save_config();
+}
+
+bool mesh_db_node_model_bind(uint16_t unicast, uint16_t ele_addr, bool vendor,
+					uint32_t mod_id, uint16_t app_idx)
+{
+	char buf[5];
+
+	snprintf(buf, 5, "%4.4x", app_idx);
+
+	return update_model_int_array(unicast, ele_addr, vendor, mod_id,
+						(int) app_idx, "bind", true);
+}
+
+bool mesh_db_node_model_unbind(uint16_t unicast, uint16_t ele_addr, bool vendor,
+					uint32_t mod_id, uint16_t app_idx)
+{
+	char buf[5];
+
+	snprintf(buf, 5, "%4.4x", app_idx);
+
+	return update_model_int_array(unicast, ele_addr, vendor, mod_id,
+						(int) app_idx, "bind", false);
+}
+
 static void jarray_key_del(json_object *jarray, int16_t idx)
 {
 	int i, sz = json_object_array_length(jarray);
@@ -1172,7 +1328,7 @@ bool mesh_db_del_node(uint16_t unicast)
 
 static json_object *init_model(uint16_t mod_id)
 {
-	json_object *jmod;
+	json_object *jmod, *jarray;
 
 	jmod = json_object_new_object();
 
@@ -1181,12 +1337,15 @@ static json_object *init_model(uint16_t mod_id)
 		return NULL;
 	}
 
+	jarray = json_object_new_array();
+	json_object_object_add(jmod, "bind", jarray);
+
 	return jmod;
 }
 
 static json_object *init_vendor_model(uint32_t mod_id)
 {
-	json_object *jmod;
+	json_object *jmod, *jarray;
 
 	jmod = json_object_new_object();
 
@@ -1195,6 +1354,9 @@ static json_object *init_vendor_model(uint32_t mod_id)
 		return NULL;
 	}
 
+	jarray = json_object_new_array();
+	json_object_object_add(jmod, "bind", jarray);
+
 	return jmod;
 }
 
diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h
index c1bcb3ae6..c3ee81457 100644
--- a/tools/mesh/mesh-db.h
+++ b/tools/mesh/mesh-db.h
@@ -44,9 +44,9 @@ bool mesh_db_node_app_key_del(uint16_t unicast, uint16_t idx);
 bool mesh_db_node_app_key_update(uint16_t unicast, uint16_t idx, bool updated);
 bool mesh_db_node_ttl_set(uint16_t unicast, uint8_t ttl);
 bool mesh_db_node_write_mode(uint16_t unicast, const char *keyword, int value);
-bool mesh_db_node_model_binding_add(uint16_t unicast, uint8_t ele, bool vendor,
+bool mesh_db_node_model_bind(uint16_t unicast, uint16_t ele_addr, bool vendor,
 					uint32_t mod_id, uint16_t app_idx);
-bool mesh_db_node_model_binding_del(uint16_t unicast, uint8_t ele, bool vendor,
+bool mesh_db_node_model_unbind(uint16_t unicast, uint16_t ele_addr, bool vendor,
 					uint32_t mod_id, uint16_t app_idx);
 struct l_queue *mesh_db_load_groups(void);
 bool mesh_db_add_group(struct mesh_group *grp);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 08/20] tools/mesh-cfgclient: Store remote node's model subs
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (6 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 07/20] tools/mesh-cfgclient: Store remote node's model bindings Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 09/20] tools/mesh-cfgclient: Disallow model commands w/o composition Inga Stotland
                   ` (12 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

Update remote node's model subscriptions after a successful completion
of "sub-add", "sub-del", "sub-wrt" and "sub-del_all" commands.
---
 tools/mesh/cfgcli.c  |  68 +++++++++++++----
 tools/mesh/mesh-db.c | 178 +++++++++++++++++++++++++++++++++++++++++++
 tools/mesh/mesh-db.h |  17 +++++
 3 files changed, 250 insertions(+), 13 deletions(-)

diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index c3241a9b7..71bf2e706 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -387,14 +387,21 @@ static void print_appkey_list(uint16_t len, uint8_t *data)
 	}
 }
 
+static bool match_group_addr(const void *a, const void *b)
+{
+	const struct mesh_group *grp = a;
+	uint16_t addr = L_PTR_TO_UINT(b);
+
+	return grp->addr == addr;
+}
+
 static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 							uint16_t len)
 {
-	uint32_t opcode;
+	uint32_t opcode, mod_id;
 	const struct cfg_cmd *cmd;
-	uint16_t app_idx, net_idx, addr;
-	uint16_t ele_addr;
-	uint32_t mod_id;
+	uint16_t app_idx, net_idx, addr, ele_addr;
+	struct mesh_group *grp;
 	struct model_pub pub;
 	int n;
 	struct pending_req *req;
@@ -664,10 +671,53 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		addr = get_le16(data + 3);
 		bt_shell_printf("Element Addr\t%4.4x\n", ele_addr);
 
-		print_mod_id(data + 5, len == 9, "");
+		mod_id = print_mod_id(data + 5, len == 9, "");
 
 		bt_shell_printf("Subscr Addr\t%4.4x\n", addr);
 
+		grp = l_queue_find(groups, match_group_addr,
+							L_UINT_TO_PTR(addr));
+
+		if (data[0] != MESH_STATUS_SUCCESS || !cmd)
+			return true;
+
+		switch (cmd->opcode) {
+		default:
+			return true;
+		case OP_CONFIG_MODEL_SUB_ADD:
+			mesh_db_node_model_add_sub(src, ele_addr, len == 9,
+								mod_id, addr);
+			break;
+		case OP_CONFIG_MODEL_SUB_DELETE:
+			mesh_db_node_model_del_sub(src, ele_addr, len == 9,
+								mod_id, addr);
+			break;
+		case OP_CONFIG_MODEL_SUB_OVERWRITE:
+			mesh_db_node_model_overwrt_sub(src, ele_addr, len == 9,
+								mod_id, addr);
+			break;
+		case OP_CONFIG_MODEL_SUB_DELETE_ALL:
+			mesh_db_node_model_del_sub_all(src, ele_addr, len == 9,
+									mod_id);
+			break;
+		case OP_CONFIG_MODEL_SUB_VIRT_ADD:
+			if (grp)
+				mesh_db_node_model_add_sub_virt(src, ele_addr,
+						len == 9, mod_id, grp->label);
+			break;
+		case OP_CONFIG_MODEL_SUB_VIRT_DELETE:
+			if (grp)
+				mesh_db_node_model_del_sub_virt(src, ele_addr,
+						len == 9, mod_id, grp->label);
+			break;
+		case OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE:
+			if (grp)
+				mesh_db_node_model_overwrt_sub_virt(src,
+							ele_addr, len == 9,
+							mod_id, grp->label);
+			break;
+		}
+
 		break;
 
 	/* Per Mesh Profile 4.3.2.27 */
@@ -820,14 +870,6 @@ static uint32_t read_input_parameters(int argc, char *argv[])
 	return i;
 }
 
-static bool match_group_addr(const void *a, const void *b)
-{
-	const struct mesh_group *grp = a;
-	uint16_t addr = L_PTR_TO_UINT(b);
-
-	return grp->addr == addr;
-}
-
 static int compare_group_addr(const void *a, const void *b, void *user_data)
 {
 	const struct mesh_group *grp0 = a;
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index da5817acf..1b03e2d90 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -814,6 +814,178 @@ bool mesh_db_node_model_unbind(uint16_t unicast, uint16_t ele_addr, bool vendor,
 						(int) app_idx, "bind", false);
 }
 
+static void jarray_string_del(json_object *jarray, const char *str, size_t len)
+{
+	int i, sz = json_object_array_length(jarray);
+
+	for (i = 0; i < sz; ++i) {
+		json_object *jentry;
+		char *str_entry;
+
+		jentry = json_object_array_get_idx(jarray, i);
+		str_entry = (char *)json_object_get_string(jentry);
+
+		if (str_entry && (strlen(str_entry) == len) &&
+						!strncmp(str, str_entry, len)) {
+			json_object_array_del_idx(jarray, i, 1);
+			return;
+		}
+	}
+}
+
+static bool update_model_string_array(uint16_t unicast, uint16_t ele_addr,
+						bool vendor, uint32_t mod_id,
+						const char *str, uint32_t len,
+						const char *keyword, bool add)
+{
+	json_object *jarray, *jmod, *jstring;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jmod = get_model(unicast, ele_addr, mod_id, vendor);
+	if (!jmod)
+		return false;
+
+	if (!json_object_object_get_ex(jmod, keyword, &jarray))
+		return false;
+
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	jarray_string_del(jarray, str, len);
+
+	if (!add)
+		return true;
+
+	jstring = json_object_new_string(str);
+	if (!jstring)
+		return false;
+
+	json_object_array_add(jarray, jstring);
+
+	return save_config();
+}
+
+bool mesh_db_node_model_add_sub(uint16_t unicast, uint16_t ele, bool vendor,
+						uint32_t mod_id, uint16_t addr)
+{
+	char buf[5];
+
+	snprintf(buf, 5, "%4.4x", addr);
+
+	return update_model_string_array(unicast, ele, vendor, mod_id, buf, 4,
+							"subscribe", true);
+}
+
+bool mesh_db_node_model_del_sub(uint16_t unicast, uint16_t ele, bool vendor,
+						uint32_t mod_id, uint16_t addr)
+{
+	char buf[5];
+
+	snprintf(buf, 5, "%4.4x", addr);
+
+	return update_model_string_array(unicast, ele, vendor, mod_id, buf, 4,
+							"subscribe", false);
+}
+
+bool mesh_db_node_model_add_sub_virt(uint16_t unicast, uint16_t ele,
+						bool vendor, uint32_t mod_id,
+								uint8_t *label)
+{
+	char buf[33];
+
+	hex2str(label, 16, buf, sizeof(buf));
+
+	return update_model_string_array(unicast, ele, vendor, mod_id, buf, 32,
+							"subscribe", true);
+
+}
+
+bool mesh_db_node_model_del_sub_virt(uint16_t unicast, uint16_t ele,
+						bool vendor, uint32_t mod_id,
+								uint8_t *label)
+{
+	char buf[33];
+
+	hex2str(label, 16, buf, sizeof(buf));
+
+	return update_model_string_array(unicast, ele, vendor, mod_id, buf, 32,
+							"subscribe", false);
+}
+
+static json_object *delete_subs(uint16_t unicast, uint16_t ele, bool vendor,
+								uint32_t mod_id)
+{
+	json_object *jarray, *jmod;
+
+	if (!cfg || !cfg->jcfg)
+		return NULL;
+
+	jmod = get_model(unicast, ele, mod_id, vendor);
+	if (!jmod)
+		return NULL;
+
+	json_object_object_del(jmod, "subscribe");
+
+	jarray = json_object_new_array();
+	if (!jarray)
+		return NULL;
+
+	json_object_object_add(jmod, "subscribe", jarray);
+
+	return jarray;
+}
+
+bool mesh_db_node_model_del_sub_all(uint16_t unicast, uint16_t ele, bool vendor,
+								uint32_t mod_id)
+{
+
+	if (!delete_subs(unicast, ele, vendor, mod_id))
+		return false;
+
+	return save_config();
+}
+
+static bool sub_overwrite(uint16_t unicast, uint16_t ele, bool vendor,
+						uint32_t mod_id, char *buf)
+{
+	json_object *jarray, *jstring;
+
+	jarray = delete_subs(unicast, ele, vendor, mod_id);
+	if (!jarray)
+		return false;
+
+	jstring = json_object_new_string(buf);
+	if (!jstring)
+		return false;
+
+	json_object_array_add(jarray, jstring);
+
+	return save_config();
+}
+
+bool mesh_db_node_model_overwrt_sub(uint16_t unicast, uint16_t ele, bool vendor,
+						uint32_t mod_id, uint16_t addr)
+{
+	char buf[5];
+
+	snprintf(buf, 5, "%4.4x", addr);
+
+	return sub_overwrite(unicast, ele, vendor, mod_id, buf);
+}
+
+bool mesh_db_node_model_overwrt_sub_virt(uint16_t unicast, uint16_t ele,
+						bool vendor, uint32_t mod_id,
+								uint8_t *label)
+{
+	char buf[33];
+
+	hex2str(label, 16, buf, sizeof(buf));
+
+	return sub_overwrite(unicast, ele, vendor, mod_id, buf);
+}
+
 static void jarray_key_del(json_object *jarray, int16_t idx)
 {
 	int i, sz = json_object_array_length(jarray);
@@ -1340,6 +1512,9 @@ static json_object *init_model(uint16_t mod_id)
 	jarray = json_object_new_array();
 	json_object_object_add(jmod, "bind", jarray);
 
+	jarray = json_object_new_array();
+	json_object_object_add(jmod, "subscribe", jarray);
+
 	return jmod;
 }
 
@@ -1357,6 +1532,9 @@ static json_object *init_vendor_model(uint32_t mod_id)
 	jarray = json_object_new_array();
 	json_object_object_add(jmod, "bind", jarray);
 
+	jarray = json_object_new_array();
+	json_object_object_add(jmod, "subscribe", jarray);
+
 	return jmod;
 }
 
diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h
index c3ee81457..384376cbd 100644
--- a/tools/mesh/mesh-db.h
+++ b/tools/mesh/mesh-db.h
@@ -48,6 +48,23 @@ bool mesh_db_node_model_bind(uint16_t unicast, uint16_t ele_addr, bool vendor,
 					uint32_t mod_id, uint16_t app_idx);
 bool mesh_db_node_model_unbind(uint16_t unicast, uint16_t ele_addr, bool vendor,
 					uint32_t mod_id, uint16_t app_idx);
+bool mesh_db_node_model_add_sub(uint16_t unicast, uint16_t ele, bool vendor,
+						uint32_t mod_id, uint16_t addr);
+bool mesh_db_node_model_del_sub(uint16_t unicast, uint16_t ele, bool vendor,
+						uint32_t mod_id, uint16_t addr);
+bool mesh_db_node_model_overwrt_sub(uint16_t unicast, uint16_t ele, bool vendor,
+						uint32_t mod_id, uint16_t addr);
+bool mesh_db_node_model_add_sub_virt(uint16_t unicast, uint16_t ele,
+						bool vendor, uint32_t mod_id,
+								uint8_t *label);
+bool mesh_db_node_model_del_sub_virt(uint16_t unicast, uint16_t ele,
+						bool vendor, uint32_t mod_id,
+								uint8_t *label);
+bool mesh_db_node_model_overwrt_sub_virt(uint16_t unicast, uint16_t ele,
+						bool vendor, uint32_t mod_id,
+								uint8_t *label);
+bool mesh_db_node_model_del_sub_all(uint16_t unicast, uint16_t ele, bool vendor,
+							uint32_t mod_id);
 struct l_queue *mesh_db_load_groups(void);
 bool mesh_db_add_group(struct mesh_group *grp);
 bool mesh_db_add_rejected_addr(uint16_t unicast, uint32_t iv_index);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 09/20] tools/mesh-cfgclient: Disallow model commands w/o composition
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (7 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 08/20] tools/mesh-cfgclient: Store remote node's model subs Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 10/20] tools/mesh-cfgclient: Store remote's model publication info Inga Stotland
                   ` (11 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

If remote node's composition hasn't been acquired, disallow commands
that change model state (that is, bindings, subscriptions, publications).
Prompt to run "get-composition" command first.
---
 tools/mesh/cfgcli.c  | 26 ++++++++++++++++++++++++++
 tools/mesh/mesh-db.c | 10 ++++++++--
 tools/mesh/remote.c  | 23 +++++++++++++++++++++++
 tools/mesh/remote.h  |  2 ++
 4 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index 71bf2e706..19a42947e 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -434,6 +434,9 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 		if (!mesh_db_node_set_composition(src, data, len))
 			bt_shell_printf("Failed to save node composition!\n");
+		else
+			remote_set_composition(src, true);
+
 		break;
 
 	case OP_APPKEY_STATUS:
@@ -1233,6 +1236,12 @@ static void cmd_bind(uint32_t opcode, int argc, char *argv[])
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 	}
 
+	if (!remote_has_composition(target)) {
+		bt_shell_printf("Node composition is unknown\n");
+		bt_shell_printf("Call \"get-composition\" first\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
 	n = mesh_opcode_set(opcode, msg);
 
 	put_le16(parms[0], msg + n);
@@ -1429,6 +1438,12 @@ static void cmd_pub_set(int argc, char *argv[])
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 	}
 
+	if (!remote_has_composition(target)) {
+		bt_shell_printf("Node composition is unknown\n");
+		bt_shell_printf("Call \"get-composition\" first\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
 	pub_addr = parms[1];
 
 	grp = l_queue_find(groups, match_group_addr, L_UINT_TO_PTR(pub_addr));
@@ -1523,6 +1538,12 @@ static void subscription_cmd(int argc, char *argv[], uint32_t opcode)
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 	}
 
+	if (!remote_has_composition(target)) {
+		bt_shell_printf("Node composition is unknown\n");
+		bt_shell_printf("Call \"get-composition\" first\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
 	sub_addr = parms[1];
 
 	grp = l_queue_find(groups, match_group_addr, L_UINT_TO_PTR(sub_addr));
@@ -1722,6 +1743,11 @@ static void cmd_hb_sub_set(int argc, char *argv[])
 	uint8_t msg[32];
 	uint32_t parm_cnt;
 
+	if (IS_UNASSIGNED(target)) {
+		bt_shell_printf("Destination not set\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
 	n = mesh_opcode_set(OP_CONFIG_HEARTBEAT_SUB_SET, msg);
 
 	parm_cnt = read_input_parameters(argc, argv);
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 1b03e2d90..8445d33f4 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -574,11 +574,17 @@ static void load_remotes(json_object *jcfg)
 			remote_update_app_key(unicast, key_idx, updated, false);
 		}
 
-		load_composition(jnode, unicast);
+		if (!load_composition(jnode, unicast))
+			continue;
 
-		node_count++;
+		/* If "crpl" is present, composition's is available */
+		jval = NULL;
+		if (json_object_object_get_ex(jnode, "crpl", &jval) && jval)
+			remote_set_composition(unicast, true);
 
 		/* TODO: Add the rest of the configuration */
+
+		node_count++;
 	}
 
 	if (node_count != sz)
diff --git a/tools/mesh/remote.c b/tools/mesh/remote.c
index 5f598cb8b..2f8493f8a 100644
--- a/tools/mesh/remote.c
+++ b/tools/mesh/remote.c
@@ -35,6 +35,7 @@ struct remote_node {
 	struct l_queue *net_keys;
 	struct l_queue *app_keys;
 	struct l_queue **els;
+	bool comp;
 	uint8_t uuid[16];
 	uint8_t num_ele;
 };
@@ -192,6 +193,28 @@ bool remote_set_model(uint16_t unicast, uint8_t ele_idx, uint32_t mod_id,
 	return true;
 }
 
+void remote_set_composition(uint16_t addr, bool comp)
+{
+	struct remote_node *rmt;
+
+	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
+	if (!rmt)
+		return;
+
+	rmt->comp = comp;
+}
+
+bool remote_has_composition(uint16_t addr)
+{
+	struct remote_node *rmt;
+
+	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
+	if (!rmt)
+		return false;
+
+	return rmt->comp;
+}
+
 bool remote_add_net_key(uint16_t addr, uint16_t net_idx, bool save)
 {
 	struct remote_node *rmt;
diff --git a/tools/mesh/remote.h b/tools/mesh/remote.h
index 74747689a..2fb0d83ce 100644
--- a/tools/mesh/remote.h
+++ b/tools/mesh/remote.h
@@ -25,6 +25,8 @@ bool remote_del_app_key(uint16_t addr, uint16_t app_idx);
 bool remote_update_app_key(uint16_t addr, uint16_t app_idx, bool update,
 								bool save);
 void remote_finish_key_refresh(uint16_t addr, uint16_t net_idx);
+void remote_set_composition(uint16_t addr, bool comp);
+bool remote_has_composition(uint16_t addr);
 uint16_t remote_get_subnet_idx(uint16_t addr);
 void remote_print_node(uint16_t addr);
 void remote_print_all(void);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 10/20] tools/mesh-cfgclient: Store remote's model publication info
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (8 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 09/20] tools/mesh-cfgclient: Disallow model commands w/o composition Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 11/20] tools/mesh-cfgclient: Check the result of config save Inga Stotland
                   ` (10 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

Update remote node's model publication after a successful completion
of "pub-set" or "pub-get" commands.
---
 tools/mesh/cfgcli.c  | 42 +++++++++++++++++------
 tools/mesh/mesh-db.c | 81 ++++++++++++++++++++++++++++++++++++++++++++
 tools/mesh/mesh-db.h |  4 +++
 tools/mesh/model.h   | 13 ++++---
 4 files changed, 124 insertions(+), 16 deletions(-)

diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index 19a42947e..2766d47ca 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -334,7 +334,7 @@ static void print_pub(uint16_t ele_addr, uint32_t mod_id,
 						struct model_pub *pub)
 {
 	bt_shell_printf("\tElement: %4.4x\n", ele_addr);
-	bt_shell_printf("\tPub Addr: %4.4x\n", pub->u.addr16);
+	bt_shell_printf("\tPub Addr: %4.4x\n", pub->u.addr);
 
 	if (mod_id < VENDOR_ID_MASK)
 		bt_shell_printf("\tModel: %8.8x\n", mod_id);
@@ -634,31 +634,51 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 		mod_id = print_mod_id(data + 10, len == 14, "");
 
-		pub.u.addr16 = get_le16(data + 3);
+		pub.u.addr = get_le16(data + 3);
+
 		pub.app_idx = get_le16(data + 5);
+		pub.cred = ((pub.app_idx & 0x1000) != 0);
+		pub.app_idx &= 0x3ff;
+
 		pub.ttl = data[7];
-		pub.period = data[8];
-		n = (data[8] & 0x3f);
+		pub.prd_steps = (data[8] & 0x3f);
 
 		print_pub(ele_addr, mod_id, &pub);
 
 		switch (data[8] >> 6) {
 		case 0:
-			bt_shell_printf("Period\t\t%d ms\n", n * 100);
+			pub.prd_res = 100;
 			break;
 		case 2:
-			n *= 10;
-			/* fall through */
+			pub.prd_res = 10;
+			break;
 		case 1:
-			bt_shell_printf("Period\t\t%d sec\n", n);
+			pub.prd_res = 10000;
 			break;
 		case 3:
-			bt_shell_printf("Period\t\t%d min\n", n * 10);
+			pub.prd_res = 600000;
 			break;
 		}
 
-		bt_shell_printf("Rexmit count\t%d\n", data[9] & 0x7);
-		bt_shell_printf("Rexmit steps\t%d\n", data[9] >> 3);
+		bt_shell_printf("Period\t\t%d ms\n", pub.period);
+
+		pub.rtx_cnt = data[9] & 0x7;
+		pub.rtx_interval = ((data[9] >> 3) + 1) * 50;
+		bt_shell_printf("Rexmit count\t%d\n", pub.rtx_cnt);
+		bt_shell_printf("Rexmit steps\t%d\n", pub.rtx_interval);
+
+		if (IS_VIRTUAL(pub.u.addr)) {
+			grp = l_queue_find(groups, match_group_addr,
+						L_UINT_TO_PTR(pub.u.addr));
+			if (!grp)
+				return true;
+
+			memcpy(pub.u.label, grp->label, sizeof(pub.u.label));
+
+		}
+
+		mesh_db_node_model_set_pub(src, ele_addr, len == 14, mod_id,
+						&pub, IS_VIRTUAL(pub.u.addr));
 
 		break;
 
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 8445d33f4..f63edd5ae 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -32,6 +32,7 @@
 #include "tools/mesh/keys.h"
 #include "tools/mesh/remote.h"
 #include "tools/mesh/cfgcli.h"
+#include "tools/mesh/model.h"
 #include "tools/mesh/mesh-db.h"
 
 #define KEY_IDX_INVALID NET_IDX_INVALID
@@ -992,6 +993,86 @@ bool mesh_db_node_model_overwrt_sub_virt(uint16_t unicast, uint16_t ele,
 	return sub_overwrite(unicast, ele, vendor, mod_id, buf);
 }
 
+static bool add_transmit_info(json_object *jobj, int cnt, int interval,
+							const char *desc)
+{
+	json_object *jtxmt;
+
+	jtxmt = json_object_new_object();
+
+	if (!write_int(jtxmt, "count", cnt))
+		return false;
+
+	if (!write_int(jtxmt, "interval", interval))
+		return false;
+
+	json_object_object_add(jobj, desc, jtxmt);
+	return true;
+}
+
+bool mesh_db_node_model_set_pub(uint16_t unicast, uint16_t ele_addr,
+					bool vendor, uint32_t mod_id,
+					struct model_pub *pub, bool virt)
+{
+	json_object *jmod, *jpub, *jobj = NULL;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jmod = get_model(unicast, ele_addr, mod_id, vendor);
+	if (!jmod)
+		return false;
+
+	jpub = json_object_new_object();
+
+	if (!virt && !write_uint16_hex(jpub, "address", pub->u.addr))
+		goto fail;
+
+	if (virt) {
+		char buf[33];
+
+		hex2str(pub->u.label, 16, buf, sizeof(buf));
+
+		if (!add_string(jpub, "address", buf))
+			goto fail;
+	}
+
+	if (!write_int(jpub, "index", pub->app_idx))
+		goto fail;
+
+	if (!write_int(jpub, "ttl", pub->ttl))
+		goto fail;
+
+	if (!write_int(jpub, "credentials", pub->cred ? 1 : 0))
+		goto fail;
+
+	if (!add_transmit_info(jpub, pub->rtx_cnt, pub->rtx_interval,
+							"retransmit"))
+		goto fail;
+
+	jobj = json_object_new_object();
+
+	if (!write_int(jobj, "numberOfSteps", pub->prd_steps))
+		goto fail;
+
+	if (!write_int(jobj, "resolution", pub->prd_res))
+		goto fail;
+
+	json_object_object_add(jpub, "period", jobj);
+
+	json_object_object_del(jmod, "publish");
+	json_object_object_add(jmod, "publish", jpub);
+
+	return save_config();
+
+fail:
+	if (jobj)
+		json_object_put(jobj);
+
+	json_object_put(jpub);
+	return false;
+}
+
 static void jarray_key_del(json_object *jarray, int16_t idx)
 {
 	int i, sz = json_object_array_length(jarray);
diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h
index 384376cbd..885dabe90 100644
--- a/tools/mesh/mesh-db.h
+++ b/tools/mesh/mesh-db.h
@@ -11,6 +11,7 @@
 #include "mesh/mesh-config.h"
 
 struct mesh_group;
+struct model_pub;
 
 bool mesh_db_create(const char *fname, const uint8_t token[8],
 							const char *name);
@@ -65,6 +66,9 @@ bool mesh_db_node_model_overwrt_sub_virt(uint16_t unicast, uint16_t ele,
 								uint8_t *label);
 bool mesh_db_node_model_del_sub_all(uint16_t unicast, uint16_t ele, bool vendor,
 							uint32_t mod_id);
+bool mesh_db_node_model_set_pub(uint16_t unicast, uint16_t ele_addr,
+					bool vendor, uint32_t mod_id,
+					struct model_pub *pub, bool virt);
 struct l_queue *mesh_db_load_groups(void);
 bool mesh_db_add_group(struct mesh_group *grp);
 bool mesh_db_add_rejected_addr(uint16_t unicast, uint32_t iv_index);
diff --git a/tools/mesh/model.h b/tools/mesh/model.h
index 7359ea7df..5e20719b2 100644
--- a/tools/mesh/model.h
+++ b/tools/mesh/model.h
@@ -25,14 +25,17 @@ typedef int (*model_bind_func_t)(uint16_t app_idx, int action);
 
 struct model_pub {
 	uint16_t app_idx;
+	uint16_t period;
 	union {
-		uint16_t addr16;
-		uint8_t va_128[16];
+		uint16_t addr;
+		uint8_t label[16];
 	} u;
+	bool cred;
+	uint32_t prd_res;
+	uint16_t rtx_interval;
+	uint8_t prd_steps;
+	uint8_t rtx_cnt;
 	uint8_t ttl;
-	uint8_t credential;
-	uint8_t period;
-	uint8_t retransmit;
 };
 
 typedef int (*model_pub_func_t)(struct model_pub *pub);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 11/20] tools/mesh-cfgclient: Check the result of config save
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (9 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 10/20] tools/mesh-cfgclient: Store remote's model publication info Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 12/20] tools/mesh-cfgclient: Rename mesh-db APIs for consistency Inga Stotland
                   ` (9 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

After successful completion of configuration commands that change
configuration state of network, the updates are expected to be
recorded in configuration file. If for the results are not saved,
print a warning message.
---
 tools/mesh/cfgcli.c | 119 +++++++++++++++++++++++---------------------
 tools/mesh/remote.c |  10 ++--
 tools/mesh/remote.h |   2 +-
 3 files changed, 70 insertions(+), 61 deletions(-)

diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index 2766d47ca..9399228c8 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -405,6 +405,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 	struct model_pub pub;
 	int n;
 	struct pending_req *req;
+	bool saved = false;
 
 	if (mesh_opcode_get(data, len, &opcode, &n)) {
 		len -= n;
@@ -428,20 +429,19 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 	case OP_DEV_COMP_STATUS:
 		if (len < MIN_COMPOSITION_LEN)
-			break;
+			return true;
 
 		print_composition(data, len);
 
-		if (!mesh_db_node_set_composition(src, data, len))
-			bt_shell_printf("Failed to save node composition!\n");
-		else
+		saved = mesh_db_node_set_composition(src, data, len);
+		if (saved)
 			remote_set_composition(src, true);
 
 		break;
 
 	case OP_APPKEY_STATUS:
 		if (len != 4)
-			break;
+			return true;
 
 		bt_shell_printf("Node %4.4x AppKey status %s\n", src,
 						mesh_status_str(data[0]));
@@ -452,23 +452,22 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		bt_shell_printf("AppKey\t%u (0x%3.3x)\n", app_idx, app_idx);
 
 		if (data[0] != MESH_STATUS_SUCCESS)
-			break;
+			return true;
 
 		if (!cmd)
-			break;
+			return true;
 
 		if (cmd->opcode == OP_APPKEY_ADD)
-			remote_add_app_key(src, app_idx, true);
+			saved = remote_add_app_key(src, app_idx, true);
 		else if (cmd->opcode == OP_APPKEY_DELETE)
-			remote_del_app_key(src, app_idx);
+			saved = remote_del_app_key(src, app_idx);
 		else if (cmd->opcode == OP_APPKEY_UPDATE)
-			remote_update_app_key(src, app_idx, true, true);
-
+			saved = remote_update_app_key(src, app_idx, true, true);
 		break;
 
 	case OP_APPKEY_LIST:
 		if (len < 3)
-			break;
+			return true;
 
 		bt_shell_printf("AppKey List (node %4.4x) Status %s\n",
 						src, mesh_status_str(data[0]));
@@ -478,16 +477,16 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		len -= 3;
 
 		if (data[0] != MESH_STATUS_SUCCESS)
-			break;
+			return true;
 
 		data += 3;
 		print_appkey_list(len, data);
 
-		break;
+		return true;
 
 	case OP_NETKEY_STATUS:
 		if (len != 3)
-			break;
+			return true;
 
 		bt_shell_printf("Node %4.4x NetKey status %s\n", src,
 						mesh_status_str(data[0]));
@@ -496,23 +495,23 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		bt_shell_printf("\tNetKey %u (0x%3.3x)\n", net_idx, net_idx);
 
 		if (data[0] != MESH_STATUS_SUCCESS)
-			break;
+			return true;
 
 		if (!cmd)
-			break;
+			return true;
 
 		if (cmd->opcode == OP_NETKEY_ADD)
-			remote_add_net_key(src, net_idx, true);
+			saved = remote_add_net_key(src, net_idx, true);
 		else if (cmd->opcode == OP_NETKEY_DELETE)
-			remote_del_net_key(src, net_idx);
+			saved = remote_del_net_key(src, net_idx);
 		else if (cmd->opcode == OP_NETKEY_UPDATE)
-			remote_update_net_key(src, net_idx, true, true);
+			saved = remote_update_net_key(src, net_idx, true, true);
 
 		break;
 
 	case OP_NETKEY_LIST:
 		if (len < 2)
-			break;
+			return true;
 
 		bt_shell_printf("NetKey List (node %4.4x):\n", src);
 
@@ -530,11 +529,11 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 			bt_shell_printf("\t %u (0x%3.3x)\n", net_idx, net_idx);
 		}
 
-		break;
+		return true;
 
 	case OP_CONFIG_KEY_REFRESH_PHASE_STATUS:
 		if (len != 4)
-			break;
+			return true;
 
 		bt_shell_printf("Node %4.4x Key Refresh Phase status %s\n", src,
 						mesh_status_str(data[0]));
@@ -546,14 +545,16 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		if (data[0] != MESH_STATUS_SUCCESS)
 			return true;
 
-		if (data[3] == KEY_REFRESH_PHASE_NONE)
-			remote_finish_key_refresh(src, net_idx);
+		if (data[3] != KEY_REFRESH_PHASE_NONE)
+			return true;
+
+		saved = remote_finish_key_refresh(src, net_idx);
 
 		break;
 
 	case OP_MODEL_APP_STATUS:
 		if (len != 7 && len != 9)
-			break;
+			return true;
 
 		bt_shell_printf("Node %4.4x: Model App status %s\n", src,
 						mesh_status_str(data[0]));
@@ -567,14 +568,14 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		bt_shell_printf("AppIdx\t\t%u (0x%3.3x)\n ", app_idx, app_idx);
 
 		if (data[0] != MESH_STATUS_SUCCESS || !cmd)
-			break;
+			return true;
 
 		if (cmd->opcode == OP_MODEL_APP_BIND)
-			mesh_db_node_model_bind(src, addr, len == 9, mod_id,
-								app_idx);
+			saved = mesh_db_node_model_bind(src, addr, len == 9,
+							mod_id, app_idx);
 		else
-			mesh_db_node_model_unbind(src, addr, len == 9, mod_id,
-								app_idx);
+			saved = mesh_db_node_model_unbind(src, addr, len == 9,
+							mod_id, app_idx);
 
 		break;
 
@@ -585,7 +586,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		bt_shell_printf("NetIdx %4.4x, NodeIdState 0x%02x, status %s\n",
 				get_le16(data + 1), data[3],
 				mesh_status_str(data[0]));
-		break;
+		return true;
 
 	case OP_CONFIG_BEACON_STATUS:
 		if (len != 1)
@@ -616,7 +617,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 			return true;
 
 		bt_shell_printf("Node %4.4x Default TTL %d\n", src, data[0]);
-		mesh_db_node_ttl_set(src, data[0]);
+		saved = mesh_db_node_ttl_set(src, data[0]);
 
 		break;
 
@@ -670,15 +671,18 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		if (IS_VIRTUAL(pub.u.addr)) {
 			grp = l_queue_find(groups, match_group_addr,
 						L_UINT_TO_PTR(pub.u.addr));
-			if (!grp)
+			if (!grp) {
+				bt_shell_printf("Unknown virtual group\n");
 				return true;
+			}
 
 			memcpy(pub.u.label, grp->label, sizeof(pub.u.label));
 
 		}
 
-		mesh_db_node_model_set_pub(src, ele_addr, len == 14, mod_id,
-						&pub, IS_VIRTUAL(pub.u.addr));
+		saved = mesh_db_node_model_set_pub(src, ele_addr, len == 14,
+							mod_id, &pub,
+							IS_VIRTUAL(pub.u.addr));
 
 		break;
 
@@ -708,34 +712,36 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		default:
 			return true;
 		case OP_CONFIG_MODEL_SUB_ADD:
-			mesh_db_node_model_add_sub(src, ele_addr, len == 9,
-								mod_id, addr);
+			saved = mesh_db_node_model_add_sub(src, ele_addr,
+							len == 9, mod_id, addr);
 			break;
 		case OP_CONFIG_MODEL_SUB_DELETE:
-			mesh_db_node_model_del_sub(src, ele_addr, len == 9,
-								mod_id, addr);
+			saved = mesh_db_node_model_del_sub(src, ele_addr,
+							len == 9, mod_id, addr);
 			break;
 		case OP_CONFIG_MODEL_SUB_OVERWRITE:
-			mesh_db_node_model_overwrt_sub(src, ele_addr, len == 9,
-								mod_id, addr);
+			saved = mesh_db_node_model_overwrt_sub(src, ele_addr,
+							len == 9, mod_id, addr);
 			break;
 		case OP_CONFIG_MODEL_SUB_DELETE_ALL:
-			mesh_db_node_model_del_sub_all(src, ele_addr, len == 9,
-									mod_id);
+			saved = mesh_db_node_model_del_sub_all(src, ele_addr,
+							len == 9, mod_id);
 			break;
 		case OP_CONFIG_MODEL_SUB_VIRT_ADD:
 			if (grp)
-				mesh_db_node_model_add_sub_virt(src, ele_addr,
-						len == 9, mod_id, grp->label);
+				saved = mesh_db_node_model_add_sub_virt(src,
+							ele_addr, len == 9,
+							mod_id, grp->label);
 			break;
 		case OP_CONFIG_MODEL_SUB_VIRT_DELETE:
 			if (grp)
-				mesh_db_node_model_del_sub_virt(src, ele_addr,
-						len == 9, mod_id, grp->label);
+				saved = mesh_db_node_model_del_sub_virt(src,
+							ele_addr, len == 9,
+							mod_id, grp->label);
 			break;
 		case OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE:
 			if (grp)
-				mesh_db_node_model_overwrt_sub_virt(src,
+				saved = mesh_db_node_model_overwrt_sub_virt(src,
 							ele_addr, len == 9,
 							mod_id, grp->label);
 			break;
@@ -749,14 +755,14 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 			return true;
 
 		print_sub_list(src, false, data, len);
-		break;
+		return true;
 
 	case OP_CONFIG_VEND_MODEL_SUB_LIST:
 		if (len < 7)
 			return true;
 
 		print_sub_list(src, true, data, len);
-		break;
+		return true;
 
 	/* Per Mesh Profile 4.3.2.50 */
 	case OP_MODEL_APP_LIST:
@@ -772,8 +778,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		data += 5;
 		len -= 5;
 		print_appkey_list(len, data);
-
-		break;
+		return true;
 
 	case OP_VEND_MODEL_APP_LIST:
 		if (len < 7)
@@ -791,8 +796,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		data += 7;
 		len -= 7;
 		print_appkey_list(len, data);
-
-		break;
+		return true;
 
 	/* Per Mesh Profile 4.3.2.63 */
 	case OP_CONFIG_HEARTBEAT_PUB_STATUS:
@@ -842,7 +846,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		bt_shell_printf("Node %4.4x is reset\n", src);
 		reset_remote_node(src);
 
-		break;
+		return true;
 
 	/* Per Mesh Profile 4.3.2.57 */
 	case OP_CONFIG_FRIEND_STATUS:
@@ -854,6 +858,9 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		break;
 	}
 
+	if (!saved)
+		bt_shell_printf("Warning: Configuration not updated\n");
+
 	return true;
 }
 
diff --git a/tools/mesh/remote.c b/tools/mesh/remote.c
index 2f8493f8a..dd294fe4d 100644
--- a/tools/mesh/remote.c
+++ b/tools/mesh/remote.c
@@ -351,18 +351,19 @@ bool remote_update_app_key(uint16_t addr, uint16_t app_idx, bool update,
 		return true;
 }
 
-void remote_finish_key_refresh(uint16_t addr, uint16_t net_idx)
+bool remote_finish_key_refresh(uint16_t addr, uint16_t net_idx)
 {
 	struct remote_node *rmt;
 	struct remote_key *key;
 	const struct l_queue_entry *l;
+	bool res = true;
 
 	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
 	if (!rmt)
-		return;
+		return false;
 
 	if (!remote_update_net_key(addr, net_idx, false, true))
-		return;
+		return false;
 
 	l = l_queue_get_entries(rmt->app_keys);
 
@@ -374,9 +375,10 @@ void remote_finish_key_refresh(uint16_t addr, uint16_t net_idx)
 
 		key->updated = false;
 
-		mesh_db_node_app_key_update(addr, key->idx, false);
+		res &= mesh_db_node_app_key_update(addr, key->idx, false);
 	}
 
+	return res;
 }
 
 uint16_t remote_get_subnet_idx(uint16_t addr)
diff --git a/tools/mesh/remote.h b/tools/mesh/remote.h
index 2fb0d83ce..66457237e 100644
--- a/tools/mesh/remote.h
+++ b/tools/mesh/remote.h
@@ -24,7 +24,7 @@ bool remote_add_app_key(uint16_t addr, uint16_t app_idx, bool save);
 bool remote_del_app_key(uint16_t addr, uint16_t app_idx);
 bool remote_update_app_key(uint16_t addr, uint16_t app_idx, bool update,
 								bool save);
-void remote_finish_key_refresh(uint16_t addr, uint16_t net_idx);
+bool remote_finish_key_refresh(uint16_t addr, uint16_t net_idx);
 void remote_set_composition(uint16_t addr, bool comp);
 bool remote_has_composition(uint16_t addr);
 uint16_t remote_get_subnet_idx(uint16_t addr);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 12/20] tools/mesh-cfgclient: Rename mesh-db APIs for consistency
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (10 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 11/20] tools/mesh-cfgclient: Check the result of config save Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 13/20] tools/mesh-cfgclient: Save remote node feature setting Inga Stotland
                   ` (8 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

---
 tools/mesh-cfgclient.c | 12 ++++++------
 tools/mesh/cfgcli.c    |  2 +-
 tools/mesh/keys.c      |  4 ++--
 tools/mesh/mesh-db.c   | 24 ++++++++++++------------
 tools/mesh/mesh-db.h   | 26 +++++++++++++-------------
 tools/mesh/remote.c    | 16 ++++++++--------
 6 files changed, 42 insertions(+), 42 deletions(-)

diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
index 62dcecb2f..beeb115dc 100644
--- a/tools/mesh-cfgclient.c
+++ b/tools/mesh-cfgclient.c
@@ -1041,15 +1041,15 @@ static void mgr_key_reply(struct l_dbus_proxy *proxy,
 
 	if (!strcmp("CreateSubnet", method)) {
 		keys_add_net_key(idx);
-		mesh_db_net_key_add(idx);
+		mesh_db_add_net_key(idx);
 	} else if (!strcmp("DeleteSubnet", method)) {
 		keys_del_net_key(idx);
-		mesh_db_net_key_del(idx);
+		mesh_db_del_net_key(idx);
 	} else if (!strcmp("UpdateSubnet", method)) {
 		keys_set_net_key_phase(idx, KEY_REFRESH_PHASE_ONE, true);
 	} else if (!strcmp("DeleteAppKey", method)) {
 		keys_del_app_key(idx);
-		mesh_db_app_key_del(idx);
+		mesh_db_del_app_key(idx);
 	}
 }
 
@@ -1133,13 +1133,13 @@ static void add_key_reply(struct l_dbus_proxy *proxy,
 
 	if (!strcmp(method, "ImportSubnet")) {
 		keys_add_net_key(net_idx);
-		mesh_db_net_key_add(net_idx);
+		mesh_db_add_net_key(net_idx);
 		return;
 	}
 
 	app_idx = (uint16_t) req->arg2;
 	keys_add_app_key(net_idx, app_idx);
-	mesh_db_app_key_add(net_idx, app_idx);
+	mesh_db_add_app_key(net_idx, app_idx);
 }
 
 static void import_appkey_setup(struct l_dbus_message *msg, void *user_data)
@@ -1824,7 +1824,7 @@ static struct l_dbus_message *join_complete(struct l_dbus *dbus,
 	}
 
 	keys_add_net_key(PRIMARY_NET_IDX);
-	mesh_db_net_key_add(PRIMARY_NET_IDX);
+	mesh_db_add_net_key(PRIMARY_NET_IDX);
 
 	remote_add_node(app.uuid, 0x0001, 1, PRIMARY_NET_IDX);
 	mesh_db_add_node(app.uuid, 0x0001, 1, PRIMARY_NET_IDX);
diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index 9399228c8..b30edca19 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -617,7 +617,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 			return true;
 
 		bt_shell_printf("Node %4.4x Default TTL %d\n", src, data[0]);
-		saved = mesh_db_node_ttl_set(src, data[0]);
+		saved = mesh_db_node_set_ttl(src, data[0]);
 
 		break;
 
diff --git a/tools/mesh/keys.c b/tools/mesh/keys.c
index c08348bff..134c1a10e 100644
--- a/tools/mesh/keys.c
+++ b/tools/mesh/keys.c
@@ -54,7 +54,7 @@ static void delete_bound_appkey(void *a)
 {
 	uint32_t idx = L_PTR_TO_UINT(a);
 
-	mesh_db_app_key_del(idx);
+	mesh_db_del_app_key(idx);
 }
 
 void keys_add_net_key(uint16_t net_idx)
@@ -102,7 +102,7 @@ void keys_set_net_key_phase(uint16_t net_idx, uint8_t phase, bool save)
 
 	key->phase = phase;
 
-	if (save && !mesh_db_net_key_phase_set(net_idx, phase))
+	if (save && !mesh_db_set_net_key_phase(net_idx, phase))
 		bt_shell_printf("Failed to save updated KR phase\n");
 }
 
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index f63edd5ae..46efb81ea 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -648,7 +648,7 @@ fail:
 	return false;
 }
 
-bool mesh_db_node_ttl_set(uint16_t unicast, uint8_t ttl)
+bool mesh_db_node_set_ttl(uint16_t unicast, uint8_t ttl)
 {
 	json_object *jnode;
 
@@ -1106,7 +1106,7 @@ static bool delete_key(json_object *jobj, const char *desc, uint16_t idx)
 	return save_config();
 }
 
-bool mesh_db_node_net_key_add(uint16_t unicast, uint16_t idx)
+bool mesh_db_node_add_net_key(uint16_t unicast, uint16_t idx)
 {
 	json_object *jnode;
 
@@ -1120,7 +1120,7 @@ bool mesh_db_node_net_key_add(uint16_t unicast, uint16_t idx)
 	return add_node_key(jnode, "netKeys", idx);
 }
 
-bool mesh_db_node_net_key_del(uint16_t unicast, uint16_t net_idx)
+bool mesh_db_node_del_net_key(uint16_t unicast, uint16_t net_idx)
 {
 	json_object *jnode;
 
@@ -1168,12 +1168,12 @@ static bool key_update(uint16_t unicast, int16_t idx, bool updated,
 	return false;
 }
 
-bool mesh_db_node_net_key_update(uint16_t unicast, uint16_t idx, bool updated)
+bool mesh_db_node_update_net_key(uint16_t unicast, uint16_t idx, bool updated)
 {
 	return key_update(unicast, idx, updated, "netKeys");
 }
 
-bool mesh_db_node_app_key_add(uint16_t unicast, uint16_t idx)
+bool mesh_db_node_add_app_key(uint16_t unicast, uint16_t idx)
 {
 	json_object *jnode;
 
@@ -1187,7 +1187,7 @@ bool mesh_db_node_app_key_add(uint16_t unicast, uint16_t idx)
 	return add_node_key(jnode, "appKeys", idx);
 }
 
-bool mesh_db_node_app_key_del(uint16_t unicast, uint16_t idx)
+bool mesh_db_node_del_app_key(uint16_t unicast, uint16_t idx)
 {
 	json_object *jnode;
 
@@ -1201,7 +1201,7 @@ bool mesh_db_node_app_key_del(uint16_t unicast, uint16_t idx)
 	return delete_key(jnode, "appKeys", idx);
 }
 
-bool mesh_db_node_app_key_update(uint16_t unicast, uint16_t idx, bool updated)
+bool mesh_db_node_update_app_key(uint16_t unicast, uint16_t idx, bool updated)
 {
 	return key_update(unicast, idx, updated, "appKeys");
 }
@@ -1260,7 +1260,7 @@ static bool load_keys(json_object *jobj)
 	return true;
 }
 
-bool mesh_db_net_key_add(uint16_t net_idx)
+bool mesh_db_add_net_key(uint16_t net_idx)
 {
 	json_object *jkey, *jarray;
 	char buf[12];
@@ -1303,7 +1303,7 @@ fail:
 	return false;
 }
 
-bool mesh_db_net_key_del(uint16_t net_idx)
+bool mesh_db_del_net_key(uint16_t net_idx)
 {
 	if (!cfg || !cfg->jcfg)
 		return false;
@@ -1311,7 +1311,7 @@ bool mesh_db_net_key_del(uint16_t net_idx)
 	return delete_key(cfg->jcfg, "netKeys", net_idx);
 }
 
-bool mesh_db_net_key_phase_set(uint16_t net_idx, uint8_t phase)
+bool mesh_db_set_net_key_phase(uint16_t net_idx, uint8_t phase)
 {
 	json_object *jval, *jarray, *jkey;
 
@@ -1335,7 +1335,7 @@ bool mesh_db_net_key_phase_set(uint16_t net_idx, uint8_t phase)
 	return save_config();
 }
 
-bool mesh_db_app_key_add(uint16_t net_idx, uint16_t app_idx)
+bool mesh_db_add_app_key(uint16_t net_idx, uint16_t app_idx)
 {
 	if (!cfg || !cfg->jcfg)
 		return false;
@@ -1346,7 +1346,7 @@ bool mesh_db_app_key_add(uint16_t net_idx, uint16_t app_idx)
 	return save_config();
 }
 
-bool mesh_db_app_key_del(uint16_t app_idx)
+bool mesh_db_del_app_key(uint16_t app_idx)
 {
 	if (!cfg || !cfg->jcfg)
 		return false;
diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h
index 885dabe90..147fbf98c 100644
--- a/tools/mesh/mesh-db.h
+++ b/tools/mesh/mesh-db.h
@@ -20,13 +20,13 @@ bool mesh_db_load(const char *fname);
 bool mesh_db_get_token(uint8_t token[8]);
 bool mesh_db_set_iv_index(uint32_t ivi);
 uint32_t mesh_db_get_iv_index(void);
-bool mesh_db_net_key_add(uint16_t idx);
-bool mesh_db_net_key_del(uint16_t idx);
-bool mesh_db_net_key_phase_set(uint16_t net_idx, uint8_t phase);
-bool mesh_db_app_key_add(uint16_t net_idx, uint16_t app_idx);
-bool mesh_db_app_key_del(uint16_t app_idx);
-bool mesh_db_get_addr_range(uint16_t *low, uint16_t *high);
 
+bool mesh_db_add_net_key(uint16_t idx);
+bool mesh_db_del_net_key(uint16_t idx);
+bool mesh_db_set_net_key_phase(uint16_t net_idx, uint8_t phase);
+bool mesh_db_add_app_key(uint16_t net_idx, uint16_t app_idx);
+bool mesh_db_del_app_key(uint16_t app_idx);
+bool mesh_db_get_addr_range(uint16_t *low, uint16_t *high);
 bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
 							uint16_t net_idx);
 bool mesh_db_del_node(uint16_t unicast);
@@ -37,13 +37,13 @@ bool mesh_db_add_provisioner(const char *name, uint8_t uuid[16],
 				uint16_t group_low, uint16_t group_high);
 bool mesh_db_node_set_net_transmit(uint16_t unicast, uint8_t cnt,
 							uint16_t interval);
-bool mesh_db_node_net_key_add(uint16_t unicast, uint16_t idx);
-bool mesh_db_node_net_key_del(uint16_t unicast, uint16_t idx);
-bool mesh_db_node_net_key_update(uint16_t unicast, uint16_t idx, bool updated);
-bool mesh_db_node_app_key_add(uint16_t unicast, uint16_t idx);
-bool mesh_db_node_app_key_del(uint16_t unicast, uint16_t idx);
-bool mesh_db_node_app_key_update(uint16_t unicast, uint16_t idx, bool updated);
-bool mesh_db_node_ttl_set(uint16_t unicast, uint8_t ttl);
+bool mesh_db_node_add_net_key(uint16_t unicast, uint16_t idx);
+bool mesh_db_node_del_net_key(uint16_t unicast, uint16_t idx);
+bool mesh_db_node_update_net_key(uint16_t unicast, uint16_t idx, bool updated);
+bool mesh_db_node_add_app_key(uint16_t unicast, uint16_t idx);
+bool mesh_db_node_del_app_key(uint16_t unicast, uint16_t idx);
+bool mesh_db_node_update_app_key(uint16_t unicast, uint16_t idx, bool updated);
+bool mesh_db_node_set_ttl(uint16_t unicast, uint8_t ttl);
 bool mesh_db_node_write_mode(uint16_t unicast, const char *keyword, int value);
 bool mesh_db_node_model_bind(uint16_t unicast, uint16_t ele_addr, bool vendor,
 					uint32_t mod_id, uint16_t app_idx);
diff --git a/tools/mesh/remote.c b/tools/mesh/remote.c
index dd294fe4d..054da5300 100644
--- a/tools/mesh/remote.c
+++ b/tools/mesh/remote.c
@@ -233,7 +233,7 @@ bool remote_add_net_key(uint16_t addr, uint16_t net_idx, bool save)
 	l_queue_push_tail(rmt->net_keys, key);
 
 	if (save)
-		return mesh_db_node_net_key_add(addr, net_idx);
+		return mesh_db_node_add_net_key(addr, net_idx);
 	else
 		return true;
 }
@@ -252,14 +252,14 @@ bool remote_del_net_key(uint16_t addr, uint16_t net_idx)
 	if (!key)
 		return false;
 
-	mesh_db_node_net_key_del(addr, net_idx);
+	mesh_db_node_del_net_key(addr, net_idx);
 
 	l_free(key);
 	key = l_queue_remove_if(rmt->app_keys, match_bound_key,
 						L_UINT_TO_PTR(net_idx));
 
 	while (key) {
-		mesh_db_node_app_key_del(rmt->unicast, key->idx);
+		mesh_db_node_del_app_key(rmt->unicast, key->idx);
 		l_free(key);
 
 		key = l_queue_remove_if(rmt->app_keys, match_bound_key,
@@ -284,7 +284,7 @@ bool remote_update_net_key(uint16_t addr, uint16_t net_idx, bool update,
 	key->updated = update;
 
 	if (save)
-		return mesh_db_node_net_key_update(addr, net_idx, update);
+		return mesh_db_node_update_net_key(addr, net_idx, update);
 	else
 		return true;
 }
@@ -310,7 +310,7 @@ bool remote_add_app_key(uint16_t addr, uint16_t app_idx, bool save)
 	l_queue_push_tail(rmt->app_keys, key);
 
 	if (save)
-		return mesh_db_node_app_key_add(addr, app_idx);
+		return mesh_db_node_add_app_key(addr, app_idx);
 	else
 		return true;
 }
@@ -328,7 +328,7 @@ bool remote_del_app_key(uint16_t addr, uint16_t app_idx)
 						L_UINT_TO_PTR(app_idx));
 	l_free(key);
 
-	return mesh_db_node_app_key_del(addr, app_idx);
+	return mesh_db_node_del_app_key(addr, app_idx);
 }
 
 bool remote_update_app_key(uint16_t addr, uint16_t app_idx, bool update,
@@ -346,7 +346,7 @@ bool remote_update_app_key(uint16_t addr, uint16_t app_idx, bool update,
 	key->updated = update;
 
 	if (save)
-		return mesh_db_node_app_key_update(addr, app_idx, update);
+		return mesh_db_node_update_app_key(addr, app_idx, update);
 	else
 		return true;
 }
@@ -375,7 +375,7 @@ bool remote_finish_key_refresh(uint16_t addr, uint16_t net_idx)
 
 		key->updated = false;
 
-		res &= mesh_db_node_app_key_update(addr, key->idx, false);
+		res &= mesh_db_node_update_app_key(addr, key->idx, false);
 	}
 
 	return res;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 13/20] tools/mesh-cfgclient: Save remote node feature setting
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (11 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 12/20] tools/mesh-cfgclient: Rename mesh-db APIs for consistency Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 14/20] tools/mesh-cfgclient: Store remote's heartbeat sub/pub Inga Stotland
                   ` (7 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

Save the information for remote node's relay, friend, proxy and
secure network beacon settings.
---
 tools/mesh/cfgcli.c  |  14 +++-
 tools/mesh/mesh-db.c | 160 +++++++++++++++++++++++++++++++++++--------
 tools/mesh/mesh-db.h |   5 ++
 3 files changed, 151 insertions(+), 28 deletions(-)

diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index b30edca19..b5f39df18 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -594,6 +594,8 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 		bt_shell_printf("Node %4.4x: Config Beacon Status 0x%02x\n",
 				src, data[0]);
+
+		saved = mesh_db_node_set_beacon(src, data[0] != 0);
 		break;
 
 	case OP_CONFIG_RELAY_STATUS:
@@ -602,6 +604,10 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 		bt_shell_printf("Node %4.4x: Relay 0x%02x, cnt %d, steps %d\n",
 				src, data[0], data[1] & 0x7, data[1] >> 3);
+
+		saved = mesh_db_node_set_relay(src, data[0], data[1] & 7,
+						((data[1] >> 3) + 1) * 10);
+
 		break;
 
 	case OP_CONFIG_PROXY_STATUS:
@@ -610,6 +616,8 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 		bt_shell_printf("Node %4.4x Proxy state 0x%02x\n",
 				src, data[0]);
+
+		saved = mesh_db_node_set_proxy(src, data[0]);
 		break;
 
 	case OP_CONFIG_DEFAULT_TTL_STATUS:
@@ -618,7 +626,6 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 		bt_shell_printf("Node %4.4x Default TTL %d\n", src, data[0]);
 		saved = mesh_db_node_set_ttl(src, data[0]);
-
 		break;
 
 	case OP_CONFIG_MODEL_PUB_STATUS:
@@ -838,6 +845,9 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 		bt_shell_printf("Node %4.4x: Net transmit cnt %d, steps %d\n",
 				src, data[0] & 7, data[0] >> 3);
+
+		saved = mesh_db_node_set_net_transmit(src, data[0] & 7,
+						((data[0] >> 3) + 1) * 10);
 		break;
 
 	/* Per Mesh Profile 4.3.2.54 */
@@ -855,6 +865,8 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 		bt_shell_printf("Node %4.4x Friend state 0x%02x\n",
 				src, data[0]);
+
+		saved = mesh_db_node_set_friend(src, data[0]);
 		break;
 	}
 
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 46efb81ea..3290c5b8e 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -137,12 +137,12 @@ static void release_config(void)
 	cfg = NULL;
 }
 
-static json_object *get_node_by_unicast(uint16_t unicast)
+static json_object *get_node_by_unicast(json_object *jcfg, uint16_t unicast)
 {
 	json_object *jarray;
 	int i, sz;
 
-	if (!json_object_object_get_ex(cfg->jcfg, "nodes", &jarray))
+	if (!json_object_object_get_ex(jcfg, "nodes", &jarray))
 		return NULL;
 
 	if (!jarray || json_object_get_type(jarray) != json_type_array)
@@ -655,7 +655,7 @@ bool mesh_db_node_set_ttl(uint16_t unicast, uint8_t ttl)
 	if (!cfg || !cfg->jcfg)
 		return false;
 
-	jnode = get_node_by_unicast(unicast);
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
 	if (!jnode)
 		return false;
 
@@ -665,12 +665,135 @@ bool mesh_db_node_set_ttl(uint16_t unicast, uint8_t ttl)
 	return save_config();
 }
 
+static bool add_transmit_info(json_object *jobj, int cnt, int interval,
+							const char *desc)
+{
+	json_object *jtxmt;
+
+	json_object_object_del(jobj, desc);
+	jtxmt = json_object_new_object();
+
+	if (!write_int(jtxmt, "count", cnt))
+		goto fail;
+
+	if (!write_int(jtxmt, "interval", interval))
+		goto fail;
+
+	json_object_object_add(jobj, desc, jtxmt);
+	return true;
+
+fail:
+	json_object_put(jtxmt);
+	return false;
+}
+
+bool mesh_db_node_set_net_transmit(uint16_t unicast, uint8_t cnt,
+							uint16_t interval)
+{
+	json_object *jnode;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
+	if (!jnode)
+		return false;
+
+	if (!add_transmit_info(jnode, cnt, interval, "networkTransmit"))
+		return false;
+
+	return save_config();
+}
+
+static bool set_feature(json_object *jnode, const char *desc, uint8_t feature)
+{
+	json_object *jobj;
+
+	if (feature > MESH_MODE_UNSUPPORTED)
+		return false;
+
+	jobj = json_object_object_get(jnode, "features");
+	if (!jobj) {
+		jobj = json_object_new_object();
+		json_object_object_add(jnode, "features", jobj);
+	}
+
+	if (!write_int(jobj, desc, feature))
+		return false;
+
+	return save_config();
+}
+
+bool mesh_db_node_set_relay(uint16_t unicast, uint8_t relay, uint8_t cnt,
+							uint16_t interval)
+{
+	json_object *jnode;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
+	if (!jnode)
+		return false;
+
+	if (relay < MESH_MODE_UNSUPPORTED &&
+		!add_transmit_info(jnode, cnt, interval, "relayRetransmit"))
+		return false;
+
+	return set_feature(jnode, "relay", relay);
+}
+
+bool mesh_db_node_set_proxy(uint16_t unicast, uint8_t proxy)
+{
+	json_object *jnode;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
+	if (!jnode)
+		return false;
+
+	return set_feature(jnode, "proxy", proxy);
+}
+
+bool mesh_db_node_set_friend(uint16_t unicast, uint8_t friend)
+{
+	json_object *jnode;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
+	if (!jnode)
+		return false;
+
+	return set_feature(jnode, "friend", friend);
+}
+
+bool mesh_db_node_set_beacon(uint16_t unicast, bool enabled)
+{
+	json_object *jnode;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
+	if (!jnode)
+		return false;
+
+	if (!write_bool(jnode, "secureNetworkBeacon", enabled))
+		return false;
+
+	return save_config();
+}
+
 static json_object *get_element(uint16_t unicast, uint16_t ele_addr)
 {
 	json_object *jnode, *jarray;
 	int i, ele_cnt;
 
-	jnode = get_node_by_unicast(unicast);
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
 	if (!jnode)
 		return false;
 
@@ -993,23 +1116,6 @@ bool mesh_db_node_model_overwrt_sub_virt(uint16_t unicast, uint16_t ele,
 	return sub_overwrite(unicast, ele, vendor, mod_id, buf);
 }
 
-static bool add_transmit_info(json_object *jobj, int cnt, int interval,
-							const char *desc)
-{
-	json_object *jtxmt;
-
-	jtxmt = json_object_new_object();
-
-	if (!write_int(jtxmt, "count", cnt))
-		return false;
-
-	if (!write_int(jtxmt, "interval", interval))
-		return false;
-
-	json_object_object_add(jobj, desc, jtxmt);
-	return true;
-}
-
 bool mesh_db_node_model_set_pub(uint16_t unicast, uint16_t ele_addr,
 					bool vendor, uint32_t mod_id,
 					struct model_pub *pub, bool virt)
@@ -1113,7 +1219,7 @@ bool mesh_db_node_add_net_key(uint16_t unicast, uint16_t idx)
 	if (!cfg || !cfg->jcfg)
 		return false;
 
-	jnode = get_node_by_unicast(unicast);
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
 	if (!jnode)
 		return false;
 
@@ -1127,7 +1233,7 @@ bool mesh_db_node_del_net_key(uint16_t unicast, uint16_t net_idx)
 	if (!cfg || !cfg->jcfg)
 		return false;
 
-	jnode = get_node_by_unicast(unicast);
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
 	if (!jnode)
 		return false;
 
@@ -1143,7 +1249,7 @@ static bool key_update(uint16_t unicast, int16_t idx, bool updated,
 	if (!cfg || !cfg->jcfg)
 		return false;
 
-	jnode = get_node_by_unicast(unicast);
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
 	if (!jnode)
 		return false;
 
@@ -1180,7 +1286,7 @@ bool mesh_db_node_add_app_key(uint16_t unicast, uint16_t idx)
 	if (!cfg || !cfg->jcfg)
 		return false;
 
-	jnode = get_node_by_unicast(unicast);
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
 	if (!jnode)
 		return false;
 
@@ -1194,7 +1300,7 @@ bool mesh_db_node_del_app_key(uint16_t unicast, uint16_t idx)
 	if (!cfg || !cfg->jcfg)
 		return false;
 
-	jnode = get_node_by_unicast(unicast);
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
 	if (!jnode)
 		return false;
 
@@ -1635,7 +1741,7 @@ bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data, uint16_t len)
 	if (!cfg || !cfg->jcfg)
 		return false;
 
-	jnode = get_node_by_unicast(unicast);
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
 	if (!jnode)
 		return false;
 
diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h
index 147fbf98c..b3b0bce79 100644
--- a/tools/mesh/mesh-db.h
+++ b/tools/mesh/mesh-db.h
@@ -37,6 +37,11 @@ bool mesh_db_add_provisioner(const char *name, uint8_t uuid[16],
 				uint16_t group_low, uint16_t group_high);
 bool mesh_db_node_set_net_transmit(uint16_t unicast, uint8_t cnt,
 							uint16_t interval);
+bool mesh_db_node_set_relay(uint16_t unicast, uint8_t relay, uint8_t cnt,
+							uint16_t interval);
+bool mesh_db_node_set_proxy(uint16_t unicast, uint8_t proxy);
+bool mesh_db_node_set_friend(uint16_t unicast, uint8_t friend);
+bool mesh_db_node_set_beacon(uint16_t unicast, bool enabled);
 bool mesh_db_node_add_net_key(uint16_t unicast, uint16_t idx);
 bool mesh_db_node_del_net_key(uint16_t unicast, uint16_t idx);
 bool mesh_db_node_update_net_key(uint16_t unicast, uint16_t idx, bool updated);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 14/20] tools/mesh-cfgclient: Store remote's heartbeat sub/pub
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (12 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 13/20] tools/mesh-cfgclient: Save remote node feature setting Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 15/20] tools/mesh-cfgclient: Add group parent address for DB compliance Inga Stotland
                   ` (6 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

Store remote node's heartbeat publication or subscription info upon
receiving CONFIG_HEARTBEAT_PUB_STATUS or CONFIG_HEARTBEAT_SUB_STATUS
messages when the messages' status code is set to Success.
---
 tools/mesh/cfgcli.c  |  30 +++++++++---
 tools/mesh/mesh-db.c | 113 +++++++++++++++++++++++++++++++++++++++++--
 tools/mesh/mesh-db.h |   4 ++
 3 files changed, 135 insertions(+), 12 deletions(-)

diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index b5f39df18..2bf1a1503 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -400,7 +400,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 {
 	uint32_t opcode, mod_id;
 	const struct cfg_cmd *cmd;
-	uint16_t app_idx, net_idx, addr, ele_addr;
+	uint16_t app_idx, net_idx, addr, ele_addr, features;
 	struct mesh_group *grp;
 	struct model_pub pub;
 	int n;
@@ -813,13 +813,21 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		bt_shell_printf("\nNode %4.4x Heartbeat publish status %s\n",
 				src, mesh_status_str(data[0]));
 
-		bt_shell_printf("Destination\t%4.4x\n", get_le16(data + 1));
-		bt_shell_printf("Count\t\t%2.2x\n", data[3]);
-		bt_shell_printf("Period\t\t%2.2x\n", data[4]);
+		if (data[0] != MESH_STATUS_SUCCESS)
+			return true;
+
+		addr = get_le16(data + 1);
+		bt_shell_printf("Destination\t%4.4x\n", addr);
+		bt_shell_printf("CountLog\t\t%2.2x\n", data[3]);
+		bt_shell_printf("PeriodLog\t\t%2.2x\n", data[4]);
 		bt_shell_printf("TTL\t\t%2.2x\n", data[5]);
-		bt_shell_printf("Features\t%4.4x\n", get_le16(data + 6));
+		features = get_le16(data + 6);
+		bt_shell_printf("Features\t%4.4x\n", features);
 		net_idx = get_le16(data + 8);
 		bt_shell_printf("Net_Idx\t%u (0x%3.3x)\n", net_idx, net_idx);
+
+		saved = mesh_db_node_set_hb_pub(src, addr, net_idx, data[4],
+							data[5], features);
 		break;
 
 	/* Per Mesh Profile 4.3.2.66 */
@@ -830,12 +838,20 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		bt_shell_printf("\nNode %4.4x Heartbeat subscribe status %s\n",
 				src, mesh_status_str(data[0]));
 
-		bt_shell_printf("Source\t\t%4.4x\n", get_le16(data + 1));
-		bt_shell_printf("Destination\t%4.4x\n", get_le16(data + 3));
+		if (data[0] != MESH_STATUS_SUCCESS)
+			return true;
+
+		ele_addr = get_le16(data + 1);
+		bt_shell_printf("Source\t\t%4.4x\n", ele_addr);
+		addr = get_le16(data + 3);
+		bt_shell_printf("Destination\t%4.4x\n", addr);
 		bt_shell_printf("Period\t\t%2.2x\n", data[5]);
 		bt_shell_printf("Count\t\t%2.2x\n", data[6]);
 		bt_shell_printf("Min Hops\t%2.2x\n", data[7]);
 		bt_shell_printf("Max Hops\t%2.2x\n", data[8]);
+
+		saved = mesh_db_node_set_hb_sub(src, ele_addr, addr);
+
 		break;
 
 	/* Per Mesh Profile 4.3.2.71 */
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 3290c5b8e..42ba831d4 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -963,12 +963,24 @@ static void jarray_string_del(json_object *jarray, const char *str, size_t len)
 	}
 }
 
+static bool add_array_string(json_object *jarray, const char *str)
+{
+	json_object *jstring;
+
+	jstring = json_object_new_string(str);
+	if (!jstring)
+		return false;
+
+	json_object_array_add(jarray, jstring);
+	return true;
+}
+
 static bool update_model_string_array(uint16_t unicast, uint16_t ele_addr,
 						bool vendor, uint32_t mod_id,
 						const char *str, uint32_t len,
 						const char *keyword, bool add)
 {
-	json_object *jarray, *jmod, *jstring;
+	json_object *jarray, *jmod;
 
 	if (!cfg || !cfg->jcfg)
 		return false;
@@ -988,12 +1000,9 @@ static bool update_model_string_array(uint16_t unicast, uint16_t ele_addr,
 	if (!add)
 		return true;
 
-	jstring = json_object_new_string(str);
-	if (!jstring)
+	if (!add_array_string(jarray, str))
 		return false;
 
-	json_object_array_add(jarray, jstring);
-
 	return save_config();
 }
 
@@ -1179,6 +1188,100 @@ fail:
 	return false;
 }
 
+bool mesh_db_node_set_hb_pub(uint16_t unicast, uint16_t dst, uint16_t net_idx,
+						uint8_t period_log, uint8_t ttl,
+							uint16_t features)
+{
+	json_object *jnode, *jpub, *jarray = NULL;
+	uint32_t period;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	if (period_log > 0x12 || ttl > 0x7F)
+		return  false;
+
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
+	if (!jnode)
+		return false;
+
+	jpub = json_object_new_object();
+
+	if (!write_uint16_hex(jpub, "address", dst))
+		goto fail;
+
+	period = period_log ? 1 << (period_log - 1) : 0;
+
+	if (!write_int(jpub, "period", period))
+		goto fail;
+
+	if (!write_int(jpub, "ttl", ttl))
+		goto fail;
+
+	if (!write_int(jpub, "index", net_idx))
+		goto fail;
+
+	jarray = json_object_new_array();
+
+	if (features & FEATURE_PROXY)
+		if (!add_array_string(jarray, "proxy"))
+			goto fail;
+
+	if (features & FEATURE_RELAY)
+		if (!add_array_string(jarray, "relay"))
+			goto fail;
+
+	if (features & FEATURE_FRIEND)
+		if (!add_array_string(jarray, "friend"))
+			goto fail;
+
+	if (features & FEATURE_LPN)
+		if (!add_array_string(jarray, "lowPower"))
+			goto fail;
+
+	json_object_object_add(jpub, "features", jarray);
+	json_object_object_del(jnode, "heartbeatPub");
+	json_object_object_add(jnode, "heartbeatPub", jpub);
+
+	return save_config();
+
+fail:
+	if (jarray)
+		json_object_put(jarray);
+
+	json_object_put(jpub);
+	return false;
+}
+
+bool mesh_db_node_set_hb_sub(uint16_t unicast, uint16_t src, uint16_t dst)
+{
+	json_object *jnode, *jsub;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jnode = get_node_by_unicast(cfg->jcfg, unicast);
+	if (!jnode)
+		return false;
+
+	jsub = json_object_new_object();
+
+	if (!write_uint16_hex(jsub, "source", src))
+		goto fail;
+
+	if (!write_uint16_hex(jsub, "destination", dst))
+		goto fail;
+
+	json_object_object_del(jnode, "heartbeatSub");
+	json_object_object_add(jnode, "heartbeatSub", jsub);
+
+	return save_config();
+
+fail:
+	json_object_put(jsub);
+	return false;
+}
+
 static void jarray_key_del(json_object *jarray, int16_t idx)
 {
 	int i, sz = json_object_array_length(jarray);
diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h
index b3b0bce79..16c46c046 100644
--- a/tools/mesh/mesh-db.h
+++ b/tools/mesh/mesh-db.h
@@ -74,6 +74,10 @@ bool mesh_db_node_model_del_sub_all(uint16_t unicast, uint16_t ele, bool vendor,
 bool mesh_db_node_model_set_pub(uint16_t unicast, uint16_t ele_addr,
 					bool vendor, uint32_t mod_id,
 					struct model_pub *pub, bool virt);
+bool mesh_db_node_set_hb_pub(uint16_t unicast, uint16_t dst, uint16_t net_idx,
+						uint8_t period_log, uint8_t ttl,
+							uint16_t features);
+bool mesh_db_node_set_hb_sub(uint16_t unicast, uint16_t src, uint16_t dst);
 struct l_queue *mesh_db_load_groups(void);
 bool mesh_db_add_group(struct mesh_group *grp);
 bool mesh_db_add_rejected_addr(uint16_t unicast, uint32_t iv_index);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 15/20] tools/mesh-cfgclient: Add group parent address for DB compliance
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (13 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 14/20] tools/mesh-cfgclient: Store remote's heartbeat sub/pub Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:25 ` [PATCH BlueZ 16/20] doc/mesh-api: Add ExportKeys call Inga Stotland
                   ` (5 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

---
 tools/mesh/mesh-db.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 42ba831d4..0dc811801 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -1590,6 +1590,10 @@ bool mesh_db_add_group(struct mesh_group *grp)
 			goto fail;
 	}
 
+	/* Initialize parent group to unassigned address for now*/
+	if (!write_uint16_hex(jgroup, "parentAddress", UNASSIGNED_ADDRESS))
+		goto fail;
+
 	json_object_array_add(jgroups, jgroup);
 
 	return save_config();
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 16/20] doc/mesh-api: Add ExportKeys call
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (14 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 15/20] tools/mesh-cfgclient: Add group parent address for DB compliance Inga Stotland
@ 2021-09-23  3:25 ` Inga Stotland
  2021-09-23  3:26 ` [PATCH BlueZ 17/20] mesh: Implement ExportKeys() method Inga Stotland
                   ` (4 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

Add description for a new method:
ExportKeys() on org.bluez.mesh.Management1 interface.

This method is used by the authorized application to export information
about network keys, application keys and device keys present in the local
key database.
---
 doc/mesh-api.txt | 56 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/doc/mesh-api.txt b/doc/mesh-api.txt
index ce651c801..85de6705e 100644
--- a/doc/mesh-api.txt
+++ b/doc/mesh-api.txt
@@ -728,6 +728,62 @@ Methods:
 		PossibleErrors:
 			org.bluez.mesh.Error.InvalidArguments
 
+	dict ExportKeys(void)
+
+		This method is used by the application to export information
+		about network keys, application keys and device keys present
+		in the local key database.
+
+		dict
+			A dictionary that contains information for the keys
+			stored in the keyring with the following keys defined:
+
+			NetKeys:
+				array{struct} net_keys:
+
+				uint16 index
+					Subnet index
+
+				array{byte}[16] key
+
+				dict:
+					A dictionary that contains optional
+					key info with the following keys
+					defined:
+
+					uint8 Phase
+						Key Refresh phase of the subnet
+
+					array{byte}[16] OldKey
+
+					array{struct} AppKeys:
+
+						uint16 index
+
+							Application key index
+
+						array{byte}[16] key
+
+						dict:
+							A dictionary of optional
+							key info with the
+							following keys defined:
+
+							array{byte}[16] OldKey
+
+			DevKeys:
+				array{struct} dev_keys:
+
+				Device Key information for known remote
+				nodes in the configured Mesh network
+
+				uint16 unicast
+
+					Unicast address of the node's primary
+					element
+
+				array{byte}[16] key
+
 Mesh Application Hierarchy
 ==========================
 Service		unique name
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 17/20] mesh: Implement ExportKeys() method
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (15 preceding siblings ...)
  2021-09-23  3:25 ` [PATCH BlueZ 16/20] doc/mesh-api: Add ExportKeys call Inga Stotland
@ 2021-09-23  3:26 ` Inga Stotland
  2021-09-23  3:26 ` [PATCH BlueZ 18/20] tools/mesh-cfgclient: Store UUIDs in standard format Inga Stotland
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

Add implementation for new method ExportKeys() on
org.bluez.mesh.Management1 interface.
This method is used by the authorized application to export information
about network keys, application keys and device keys present in the
local key database.
---
 mesh/keyring.c | 286 ++++++++++++++++++++++++++++++++++++++++++++++++-
 mesh/keyring.h |   2 +
 mesh/manager.c |  35 ++++++
 3 files changed, 322 insertions(+), 1 deletion(-)

diff --git a/mesh/keyring.c b/mesh/keyring.c
index f27fe4291..4b901643c 100644
--- a/mesh/keyring.c
+++ b/mesh/keyring.c
@@ -26,6 +26,7 @@
 
 #include "mesh/mesh-defs.h"
 
+#include "mesh/dbus.h"
 #include "mesh/node.h"
 #include "mesh/keyring.h"
 
@@ -276,7 +277,6 @@ bool keyring_get_remote_dev_key(struct mesh_node *node, uint16_t unicast,
 
 	snprintf(key_file, PATH_MAX, "%s%s/%4.4x", node_path, dev_key_dir,
 								unicast);
-
 	fd = open(key_file, O_RDONLY);
 	if (fd >= 0) {
 		if (read(fd, dev_key, 16) == 16)
@@ -349,3 +349,287 @@ bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast,
 
 	return true;
 }
+
+static DIR *open_key_dir(const char *node_path, const char *key_dir_name)
+{
+	char dir_path[PATH_MAX];
+	DIR *key_dir;
+
+	if (strlen(node_path) + strlen(key_dir_name) + 1 >= PATH_MAX)
+		return NULL;
+
+	snprintf(dir_path, PATH_MAX, "%s%s", node_path, key_dir_name);
+
+	key_dir = opendir(dir_path);
+	if (!key_dir) {
+		l_error("Failed to open keyring storage directory: %s",
+								dir_path);
+		return NULL;
+	}
+
+	return key_dir;
+}
+
+static int open_key_dir_entry(int dir_fd, struct dirent *entry,
+							uint8_t fname_len)
+{
+	if (entry->d_type != DT_REG)
+		return -1;
+
+	/* Check the file name length */
+	if (strlen(entry->d_name) != fname_len)
+		return -1;
+
+	return openat(dir_fd, entry->d_name, O_RDONLY);
+}
+
+static void append_old_key(struct l_dbus_message_builder *builder,
+							const uint8_t key[16])
+{
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', "OldKey");
+	l_dbus_message_builder_enter_variant(builder, "ay");
+	dbus_append_byte_array(builder, key, 16);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+}
+
+static void build_app_keys_reply(const char *node_path,
+					struct l_dbus_message_builder *builder,
+					uint16_t net_idx, uint8_t phase)
+{
+	DIR *key_dir;
+	int key_dir_fd;
+	struct dirent *entry;
+
+	key_dir = open_key_dir(node_path, app_key_dir);
+	if (!key_dir)
+		return;
+
+	key_dir_fd = dirfd(key_dir);
+
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', "AppKeys");
+	l_dbus_message_builder_enter_variant(builder, "a(qaya{sv})");
+	l_dbus_message_builder_enter_array(builder, "(qaya{sv})");
+
+	while ((entry = readdir(key_dir)) != NULL) {
+		struct keyring_app_key key;
+		int fd = open_key_dir_entry(key_dir_fd, entry, 3);
+
+		if (fd < 0)
+			continue;
+
+		if (read(fd, &key, sizeof(key)) != sizeof(key) ||
+						key.net_idx != net_idx) {
+			close(fd);
+			continue;
+		}
+
+		close(fd);
+
+		l_dbus_message_builder_enter_struct(builder, "qaya{sv}");
+
+		l_dbus_message_builder_append_basic(builder, 'q', &key.app_idx);
+		dbus_append_byte_array(builder, key.new_key, 16);
+
+		l_dbus_message_builder_enter_array(builder, "{sv}");
+
+		if (phase != KEY_REFRESH_PHASE_NONE)
+			append_old_key(builder, key.old_key);
+
+		l_dbus_message_builder_leave_array(builder);
+		l_dbus_message_builder_leave_struct(builder);
+	}
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+
+	closedir(key_dir);
+}
+
+static bool build_net_keys_reply(const char *node_path,
+					struct l_dbus_message_builder *builder)
+{
+	DIR *key_dir;
+	int key_dir_fd;
+	struct dirent *entry;
+	bool result = false;
+
+	key_dir = open_key_dir(node_path, net_key_dir);
+	if (!key_dir)
+		return false;
+
+	key_dir_fd = dirfd(key_dir);
+
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', "NetKeys");
+	l_dbus_message_builder_enter_variant(builder, "a(qaya{sv})");
+	l_dbus_message_builder_enter_array(builder, "(qaya{sv})");
+
+	while ((entry = readdir(key_dir)) != NULL) {
+		struct keyring_net_key key;
+		int fd = open_key_dir_entry(key_dir_fd, entry, 3);
+
+		if (fd < 0)
+			continue;
+
+		if (read(fd, &key, sizeof(key)) != sizeof(key)) {
+			close(fd);
+			goto done;
+		}
+
+		close(fd);
+
+		/*
+		 * If network key is stuck in phase 3, keyring
+		 * write failed and this key info is unreliable.
+		 */
+		if (key.phase == KEY_REFRESH_PHASE_THREE)
+			continue;
+
+		l_dbus_message_builder_enter_struct(builder, "qaya{sv}");
+
+		l_dbus_message_builder_append_basic(builder, 'q', &key.net_idx);
+		dbus_append_byte_array(builder, key.new_key, 16);
+
+		l_dbus_message_builder_enter_array(builder, "{sv}");
+
+		if (key.phase != KEY_REFRESH_PHASE_NONE) {
+			dbus_append_dict_entry_basic(builder, "Phase", "y",
+								&key.phase);
+			append_old_key(builder, key.old_key);
+		}
+
+		build_app_keys_reply(node_path, builder, key.net_idx,
+								key.phase);
+
+		l_dbus_message_builder_leave_array(builder);
+		l_dbus_message_builder_leave_struct(builder);
+	}
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+
+	result = true;
+done:
+	closedir(key_dir);
+
+	return result;
+
+}
+
+struct dev_key_entry {
+	uint16_t unicast;
+	uint8_t value[16];
+};
+
+static bool match_key_value(const void *a, const void *b)
+{
+	const struct dev_key_entry *key = a;
+	const uint8_t *value = b;
+
+	return (memcmp(key->value, value, 16) == 0);
+}
+
+static void build_dev_key_entry(void *a, void *b)
+{
+	struct dev_key_entry *key = a;
+	struct l_dbus_message_builder *builder = b;
+
+	l_dbus_message_builder_enter_struct(builder, "qay");
+	l_dbus_message_builder_append_basic(builder, 'q', &key->unicast);
+	dbus_append_byte_array(builder, key->value, 16);
+	l_dbus_message_builder_leave_struct(builder);
+}
+
+static bool build_dev_keys_reply(const char *node_path,
+					struct l_dbus_message_builder *builder)
+{
+	DIR *key_dir;
+	int key_dir_fd;
+	struct dirent *entry;
+	struct l_queue *keys;
+	bool result = false;
+
+	key_dir = open_key_dir(node_path, dev_key_dir);
+	/*
+	 * There is always at least one device key present for a local node.
+	 * Therefore, return false, if the directory does not exist.
+	 */
+	if (!key_dir)
+		return false;
+
+	key_dir_fd = dirfd(key_dir);
+
+	keys = l_queue_new();
+
+	while ((entry = readdir(key_dir)) != NULL) {
+		uint8_t buf[16];
+		uint16_t unicast;
+		struct dev_key_entry *key;
+		int fd = open_key_dir_entry(key_dir_fd, entry, 4);
+
+		if (fd < 0)
+			continue;
+
+		if (read(fd, buf, 16) != 16) {
+			close(fd);
+			goto done;
+		}
+
+		close(fd);
+
+		if (sscanf(entry->d_name, "%04hx", &unicast) != 1)
+			goto done;
+
+		key = l_queue_find(keys, match_key_value, buf);
+
+		if (key) {
+			if (key->unicast > unicast)
+				key->unicast = unicast;
+			continue;
+		}
+
+		key = l_new(struct dev_key_entry, 1);
+		key->unicast = unicast;
+		memcpy(key->value, buf, 16);
+		l_queue_push_tail(keys, key);
+	}
+
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', "DevKeys");
+	l_dbus_message_builder_enter_variant(builder, "a(qay)");
+	l_dbus_message_builder_enter_array(builder, "(qay)");
+
+	l_queue_foreach(keys, build_dev_key_entry, builder);
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+
+	result = true;
+done:
+	l_queue_destroy(keys, l_free);
+	closedir(key_dir);
+
+	return result;
+}
+
+bool keyring_build_export_keys_reply(struct mesh_node *node,
+					struct l_dbus_message_builder *builder)
+{
+	const char *node_path;
+
+	if (!node)
+		return false;
+
+	node_path = node_get_storage_dir(node);
+
+	if (!build_net_keys_reply(node_path, builder))
+		return false;
+
+	return build_dev_keys_reply(node_path, builder);
+}
diff --git a/mesh/keyring.h b/mesh/keyring.h
index c2d38e9ff..ecf62cbc1 100644
--- a/mesh/keyring.h
+++ b/mesh/keyring.h
@@ -39,3 +39,5 @@ bool keyring_put_remote_dev_key(struct mesh_node *node, uint16_t unicast,
 					uint8_t count, uint8_t dev_key[16]);
 bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast,
 								uint8_t count);
+bool keyring_build_export_keys_reply(struct mesh_node *node,
+					struct l_dbus_message_builder *builder);
diff --git a/mesh/manager.c b/mesh/manager.c
index d70993e3b..e66b1a45b 100644
--- a/mesh/manager.c
+++ b/mesh/manager.c
@@ -776,6 +776,38 @@ static struct l_dbus_message *set_key_phase_call(struct l_dbus *dbus,
 	return l_dbus_message_new_method_return(msg);
 }
 
+static struct l_dbus_message *export_keys_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	const char *sender = l_dbus_message_get_sender(msg);
+	struct l_dbus_message_builder *builder;
+	struct l_dbus_message *reply;
+	struct mesh_node *node = user_data;
+
+	l_debug("Export Keys");
+
+	if (strcmp(sender, node_get_owner(node)))
+		return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+
+	reply = l_dbus_message_new_method_return(msg);
+	builder = l_dbus_message_builder_new(reply);
+
+	l_dbus_message_builder_enter_array(builder, "{sv}");
+
+	if (!keyring_build_export_keys_reply(node, builder)) {
+		l_dbus_message_builder_destroy(builder);
+		l_dbus_message_unref(reply);
+		return dbus_error(msg, MESH_ERROR_FAILED, NULL);
+	}
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+
+	return reply;
+}
+
 static void setup_management_interface(struct l_dbus_interface *iface)
 {
 	l_dbus_interface_method(iface, "AddNode", 0, add_node_call, "",
@@ -807,6 +839,9 @@ static void setup_management_interface(struct l_dbus_interface *iface)
 							"app_index", "app_key");
 	l_dbus_interface_method(iface, "SetKeyPhase", 0, set_key_phase_call, "",
 						"qy", "net_index", "phase");
+	l_dbus_interface_method(iface, "ExportKeys", 0, export_keys_call,
+							"a(qaya{sv})a(qay)", "",
+							"net_keys", "dev_keys");
 }
 
 bool manager_dbus_init(struct l_dbus *bus)
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 18/20] tools/mesh-cfgclient: Store UUIDs in standard format
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (16 preceding siblings ...)
  2021-09-23  3:26 ` [PATCH BlueZ 17/20] mesh: Implement ExportKeys() method Inga Stotland
@ 2021-09-23  3:26 ` Inga Stotland
  2021-09-23  3:26 ` [PATCH BlueZ 19/20] tools/mesh-cfgclient: Excluded addresses property Inga Stotland
                   ` (2 subsequent siblings)
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

Use standard xxxx-xx-xx-xx-xxxxxxxx format for string
representation of mesh and node UUIDs in stored configuration.
---
 tools/mesh/mesh-db.c | 29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 0dc811801..b45212fc9 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -293,10 +293,11 @@ static bool write_uint32_hex(json_object *jobj, const char *desc, uint32_t val)
 static json_object *get_node_by_uuid(json_object *jcfg, uint8_t uuid[16])
 {
 	json_object *jarray = NULL;
-	char buf[33];
+	char buf[37];
 	int i, sz;
 
-	hex2str(uuid, 16, buf, sizeof(buf));
+	if (!l_uuid_to_string(uuid, buf, sizeof(buf)))
+		return NULL;
 
 	json_object_object_get_ex(jcfg, "nodes", &jarray);
 	if (!jarray || json_object_get_type(jarray) != json_type_array)
@@ -313,7 +314,7 @@ static json_object *get_node_by_uuid(json_object *jcfg, uint8_t uuid[16])
 			return NULL;
 
 		str = json_object_get_string(jval);
-		if (strlen(str) != 32)
+		if (strlen(str) != 36)
 			continue;
 
 		if (!strcmp(buf, str))
@@ -506,10 +507,11 @@ static void load_remotes(json_object *jcfg)
 			continue;
 
 		str = json_object_get_string(jval);
-		if (strlen(str) != 32)
+		if (strlen(str) != 36)
 			continue;
 
-		str2hex(str, 32, uuid, 16);
+		if (!l_uuid_from_string(str, uuid))
+			continue;
 
 		if (!json_object_object_get_ex(jnode, "unicastAddress", &jval))
 			continue;
@@ -1698,6 +1700,7 @@ bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
 {
 	json_object *jnode;
 	json_object *jelements, *jnodes, *jnetkeys, *jappkeys;
+	char buf[37];
 
 	if (!cfg || !cfg->jcfg)
 		return false;
@@ -1712,9 +1715,11 @@ bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
 	if (!jnode)
 		return false;
 
-	if (!add_u8_16(jnode, "UUID", uuid))
+	if (!l_uuid_to_string(uuid, buf, sizeof(buf)))
 		goto fail;
 
+	if (!add_string(jnode, "UUID", buf))
+		goto fail;
 
 	if (!add_string(jnode, "security", "secure"))
 		goto fail;
@@ -2065,6 +2070,7 @@ bool mesh_db_add_provisioner(const char *name, uint8_t uuid[16],
 					uint16_t group_low, uint16_t group_high)
 {
 	json_object *jprovs, *jprov, *jscenes;
+	char buf[37];
 
 	if (!cfg || !cfg->jcfg)
 		return false;
@@ -2080,7 +2086,10 @@ bool mesh_db_add_provisioner(const char *name, uint8_t uuid[16],
 	if (!add_string(jprov, "provisionerName", name))
 		goto fail;
 
-	if (!add_u8_16(jprov, "UUID", uuid))
+	if (!l_uuid_to_string(uuid, buf, sizeof(buf)))
+		goto fail;
+
+	if (!add_string(jprov, "UUID", buf))
 		goto fail;
 
 	if (!add_range(jprov, "allocatedUnicastRange", unicast_low,
@@ -2270,6 +2279,7 @@ bool mesh_db_create(const char *fname, const uint8_t token[8],
 {
 	json_object *jcfg, *jarray;
 	uint8_t uuid[16];
+	char buf[37];
 
 	if (cfg)
 		return false;
@@ -2291,7 +2301,10 @@ bool mesh_db_create(const char *fname, const uint8_t token[8],
 
 	l_uuid_v4(uuid);
 
-	if (!add_u8_16(jcfg, "meshUUID", uuid))
+	if (!l_uuid_to_string(uuid, buf, sizeof(buf)))
+		goto fail;
+
+	if (!add_string(jcfg, "meshUUID", buf))
 		goto fail;
 
 	if (mesh_name && !add_string(jcfg, "meshName", mesh_name))
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 19/20] tools/mesh-cfgclient: Excluded addresses property
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (17 preceding siblings ...)
  2021-09-23  3:26 ` [PATCH BlueZ 18/20] tools/mesh-cfgclient: Store UUIDs in standard format Inga Stotland
@ 2021-09-23  3:26 ` Inga Stotland
  2021-09-23  3:26 ` [PATCH BlueZ 20/20] tools/mesh-cfgclient: Export configuration database Inga Stotland
  2021-09-27 20:27 ` [PATCH BlueZ 00/20] Mesh Configuration Database Gix, Brian
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

The correct name for a property that contains a list of addresses
not allowed in circulation is "networkExclusions"
The "excluded" property in "nodes" array entry is used to indicated that
this node has been removed from a mesh network and it's address should not
be re-used.
---
 tools/mesh/mesh-db.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index b45212fc9..262a274c7 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -1724,7 +1724,7 @@ bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
 	if (!add_string(jnode, "security", "secure"))
 		goto fail;
 
-	if (!write_bool(jnode, "blacklisted", false))
+	if (!write_bool(jnode, "excluded", false))
 		goto fail;
 
 	if (!write_bool(jnode, "configComplete", false))
@@ -2165,7 +2165,7 @@ static bool load_rejected_addresses(json_object *jobj)
 	json_object *jarray;
 	int i, cnt;
 
-	json_object_object_get_ex(jobj, "blacklistedAddresses", &jarray);
+	json_object_object_get_ex(jobj, "networkExclusions", &jarray);
 	if (!jarray || json_object_get_type(jarray) != json_type_array)
 		return true;
 
@@ -2212,11 +2212,10 @@ bool mesh_db_add_rejected_addr(uint16_t unicast, uint32_t iv_index)
 	if (!cfg || !cfg->jcfg)
 		return false;
 
-	json_object_object_get_ex(cfg->jcfg, "blacklistedAddresses", &jarray);
+	json_object_object_get_ex(cfg->jcfg, "networkExclusions", &jarray);
 	if (!jarray) {
 		jarray = json_object_new_array();
-		json_object_object_add(cfg->jcfg, "blacklistedAddresses",
-									jarray);
+		json_object_object_add(cfg->jcfg, "networkExclusions", jarray);
 	}
 
 	idx = get_rejected_by_iv_index(jarray, iv_index);
@@ -2261,7 +2260,7 @@ bool mesh_db_clear_rejected(uint32_t iv_index)
 	if (!cfg || !cfg->jcfg)
 		return false;
 
-	json_object_object_get_ex(cfg->jcfg, "blacklistedAddresses", &jarray);
+	json_object_object_get_ex(cfg->jcfg, "networkExclusions", &jarray);
 	if (!jarray || json_object_get_type(jarray) != json_type_array)
 		return false;
 
@@ -2338,7 +2337,7 @@ bool mesh_db_create(const char *fname, const uint8_t token[8],
 	if (!jarray)
 		goto fail;
 
-	json_object_object_add(jcfg, "blacklistedAddresses", jarray);
+	json_object_object_add(jcfg, "networkExclusions", jarray);
 
 	write_int(jcfg, "ivIndex", 0);
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH BlueZ 20/20] tools/mesh-cfgclient: Export configuration database
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (18 preceding siblings ...)
  2021-09-23  3:26 ` [PATCH BlueZ 19/20] tools/mesh-cfgclient: Excluded addresses property Inga Stotland
@ 2021-09-23  3:26 ` Inga Stotland
  2021-09-27 20:27 ` [PATCH BlueZ 00/20] Mesh Configuration Database Gix, Brian
  20 siblings, 0 replies; 25+ messages in thread
From: Inga Stotland @ 2021-09-23  3:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This adds main menu command "export-db".
When the command is invoked, JSON configuration object is
cloned and trimmed of extraneous properties.
Information about netkeys, appkeys and device keys are obtained
from bluetooth-meshd by calling ExportKeys() method.
The obtained key values are recorded in the export JSON object.
---
 tools/mesh-cfgclient.c | 195 +++++++++++++++++++++++++++++++++++++++++
 tools/mesh/mesh-db.c   | 191 ++++++++++++++++++++++++++++++++++++++++
 tools/mesh/mesh-db.h   |   7 ++
 3 files changed, 393 insertions(+)

diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
index beeb115dc..237afbb5f 100644
--- a/tools/mesh-cfgclient.c
+++ b/tools/mesh-cfgclient.c
@@ -17,6 +17,7 @@
 #include <ctype.h>
 #include <dbus/dbus.h>
 #include <errno.h>
+#include <libgen.h>
 #include <stdio.h>
 #include <time.h>
 
@@ -51,6 +52,7 @@
 #define MAX_CRPL_SIZE		0x7fff
 
 #define DEFAULT_CFG_FILE	"config_db.json"
+#define DEFAULT_EXPORT_FILE	"export_db.json"
 
 struct meshcfg_el {
 	const char *path;
@@ -835,6 +837,197 @@ static void cmd_scan_unprov(int argc, char *argv[])
 
 }
 
+static uint8_t *parse_key(struct l_dbus_message_iter *iter, uint16_t id,
+							const char *name)
+{
+	uint8_t *val;
+	uint32_t len;
+
+	if (!l_dbus_message_iter_get_fixed_array(iter, &val, &len)
+								|| len != 16) {
+		bt_shell_printf("Failed to parse %s %4.4x\n", name, id);
+		return NULL;
+	}
+
+	return val;
+}
+
+static bool parse_app_keys(struct l_dbus_message_iter *iter, uint16_t net_idx,
+								void *user_data)
+{
+	struct l_dbus_message_iter app_keys, app_key, opts;
+	uint16_t app_idx;
+
+	if (!l_dbus_message_iter_get_variant(iter, "a(qaya{sv})", &app_keys))
+		return false;
+
+	while (l_dbus_message_iter_next_entry(&app_keys, &app_idx, &app_key,
+								&opts)) {
+		struct l_dbus_message_iter var;
+		uint8_t *val, *old_val = NULL;
+		const char *key;
+
+		val = parse_key(&app_key, app_idx, "AppKey");
+		if (!val)
+			return false;
+
+		while (l_dbus_message_iter_next_entry(&opts, &key, &var)) {
+			if (!strcmp(key, "OldKey")) {
+				if (!l_dbus_message_iter_get_variant(&var, "ay",
+								&app_key))
+					return false;
+
+				old_val = parse_key(&app_key, app_idx,
+								"old NetKey");
+
+				if (!old_val)
+					return false;
+			}
+		}
+
+		mesh_db_set_app_key(user_data, net_idx, app_idx, val, old_val);
+	}
+
+	return true;
+}
+
+static bool parse_net_keys(struct l_dbus_message_iter *iter, void *user_data)
+{
+	struct l_dbus_message_iter net_keys, net_key, opts;
+	uint16_t idx;
+
+	if (!l_dbus_message_iter_get_variant(iter, "a(qaya{sv})", &net_keys))
+		return false;
+
+	while (l_dbus_message_iter_next_entry(&net_keys, &idx, &net_key,
+								&opts)) {
+		struct l_dbus_message_iter var;
+		uint8_t *val, *old_val = NULL;
+		uint8_t phase = KEY_REFRESH_PHASE_NONE;
+		const char *key;
+
+		val = parse_key(&net_key, idx, "NetKey");
+		if (!val)
+			return false;
+
+		while (l_dbus_message_iter_next_entry(&opts, &key, &var)) {
+			if (!strcmp(key, "AppKeys")) {
+				if (!parse_app_keys(&var, idx, user_data))
+					return false;
+			} else if (!strcmp(key, "Phase")) {
+				if (!l_dbus_message_iter_get_variant(&var, "y",
+									&phase))
+					return false;
+			} else if (!strcmp(key, "OldKey")) {
+				if (!l_dbus_message_iter_get_variant(&var, "ay",
+								&net_key))
+					return false;
+
+				old_val = parse_key(&net_key, idx,
+								"old NetKey");
+
+				if (!old_val)
+					return false;
+			}
+		}
+
+		mesh_db_set_net_key(user_data, idx, val, old_val, phase);
+	}
+
+	return true;
+}
+
+static bool parse_dev_keys(struct l_dbus_message_iter *iter, void *user_data)
+{
+	struct l_dbus_message_iter keys, dev_key;
+	uint16_t unicast;
+
+	if (!l_dbus_message_iter_get_variant(iter, "a(qay)", &keys))
+		return false;
+
+	while (l_dbus_message_iter_next_entry(&keys, &unicast, &dev_key)) {
+		uint8_t *data;
+
+		data = parse_key(&dev_key, unicast, "Device Key");
+		if (!data)
+			return false;
+
+		mesh_db_set_device_key(user_data, unicast, data);
+	}
+
+	return true;
+}
+
+static void export_keys_reply(struct l_dbus_proxy *proxy,
+				struct l_dbus_message *msg, void *user_data)
+{
+	struct l_dbus_message_iter iter, var;
+	char *cfg_dir = NULL, *fname = NULL;
+	const char *key;
+	bool is_error = true;
+
+	if (l_dbus_message_is_error(msg)) {
+		const char *name;
+
+		l_dbus_message_get_error(msg, &name, NULL);
+		bt_shell_printf("Failed to export keys: %s", name);
+		goto done;
+
+	}
+
+	if (!l_dbus_message_get_arguments(msg, "a{sv}", &iter)) {
+		bt_shell_printf("Malformed ExportKeys reply");
+		goto done;
+	}
+
+	while (l_dbus_message_iter_next_entry(&iter, &key, &var)) {
+		if (!strcmp(key, "NetKeys")) {
+			if (!parse_net_keys(&var, user_data))
+				goto done;
+		} else if (!strcmp(key, "DevKeys")) {
+			if (!parse_dev_keys(&var, user_data))
+				goto done;
+		}
+	}
+
+	is_error = false;
+
+	cfg_dir = l_strdup(cfg_fname);
+	cfg_dir = dirname(cfg_dir);
+
+	fname = l_strdup_printf("%s/%s", cfg_dir, DEFAULT_EXPORT_FILE);
+
+done:
+	if (mesh_db_finish_export(is_error, user_data, fname)) {
+		if (!is_error)
+			bt_shell_printf("Config DB is exported to %s\n", fname);
+	}
+
+	l_free(cfg_dir);
+	l_free(fname);
+}
+
+static void cmd_export_db(int argc, char *argv[])
+{
+	void *cfg_export;
+
+	if (!local || !local->proxy || !local->mgmt_proxy) {
+		bt_shell_printf("Node is not attached\n");
+		return;
+	}
+
+	/* Generate a properly formatted DB from the local config */
+	cfg_export = mesh_db_prepare_export();
+	if (!cfg_export) {
+		bt_shell_printf("Failed to prepare config db\n");
+		return;
+	}
+
+	/* Export the keys from the daemon */
+	l_dbus_proxy_method_call(local->mgmt_proxy, "ExportKeys", NULL,
+					export_keys_reply, cfg_export, NULL);
+}
+
 static void cmd_list_unprov(int argc, char *argv[])
 {
 	bt_shell_printf(COLOR_YELLOW "Unprovisioned devices:\n" COLOR_OFF);
@@ -1395,6 +1588,8 @@ static const struct bt_shell_menu main_menu = {
 			"List remote mesh nodes"},
 	{ "keys", NULL, cmd_keys,
 			"List available keys"},
+	{ "export-db", NULL, cmd_export_db,
+			"Export mesh configuration database"},
 	{ } },
 };
 
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 262a274c7..12055c1a9 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -48,6 +48,13 @@ static struct mesh_db *cfg;
 static const char *bak_ext = ".bak";
 static const char *tmp_ext = ".tmp";
 
+static const char *js_schema = "http://json-schema.org/draft-04/schema#";
+static const char *schema_id = "http://www.bluetooth.com/specifications/"
+				"assigned-numbers/mesh-profile/"
+				"cdb-schema.json#";
+const char *schema_version = "1.0.0";
+
+
 static bool add_string(json_object *jobj, const char *desc, const char *str)
 {
 	json_object *jstring = json_object_new_string(str);
@@ -2412,3 +2419,187 @@ fail:
 
 	return false;
 }
+
+bool mesh_db_set_device_key(void *expt_cfg, uint16_t unicast, uint8_t key[16])
+{
+	json_object *jnode;
+
+	if (!expt_cfg)
+		return false;
+
+	jnode = get_node_by_unicast(expt_cfg, unicast);
+	if (!jnode)
+		return false;
+
+	return add_u8_16(jnode, "deviceKey", key);
+}
+
+bool mesh_db_set_net_key(void *expt_cfg, uint16_t idx, uint8_t key[16],
+					uint8_t *old_key, uint8_t phase)
+{
+	json_object *jarray, *jkey;
+
+	if (!expt_cfg)
+		return false;
+
+	json_object_object_get_ex(expt_cfg, "netKeys", &jarray);
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	jkey = get_key_object(jarray, idx);
+	if (!jkey)
+		return false;
+
+	if (!write_int(jkey, "phase", phase))
+		return false;
+
+	if (!add_u8_16(jkey, "key", key))
+		return false;
+
+	if (old_key && !(!add_u8_16(jkey, "oldKey", old_key)))
+		return false;
+
+	return true;
+}
+
+
+bool mesh_db_set_app_key(void *expt_cfg, uint16_t net_idx, uint16_t app_idx,
+					uint8_t key[16], uint8_t *old_key)
+{
+	json_object *jarray, *jkey;
+
+	if (!expt_cfg)
+		return false;
+
+	json_object_object_get_ex(expt_cfg, "appKeys", &jarray);
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	jkey = get_key_object(jarray, app_idx);
+	if (!jkey)
+		return false;
+
+	if (!add_u8_16(jkey, "key", key))
+		return false;
+
+	if (old_key && !(!add_u8_16(jkey, "oldKey", old_key)))
+		return false;
+
+	return true;
+}
+
+void *mesh_db_prepare_export(void)
+{
+	json_object *export = NULL, *jarray;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	if (json_object_deep_copy(cfg->jcfg, &export, NULL) != 0)
+		return NULL;
+
+	/* Delete token */
+	json_object_object_del(export, "token");
+
+	/* Delete IV index */
+	json_object_object_del(export, "ivIndex");
+
+	/* Scenes are not supported. Just add an empty array */
+	jarray = json_object_new_array();
+	json_object_object_add(export, "scenes", jarray);
+
+	write_bool(export, "partial", false);
+
+	return export;
+}
+
+bool mesh_db_finish_export(bool is_error, void *expt_cfg, const char *fname)
+{
+	FILE *outfile = NULL;
+	const char *str, *hdr;
+	json_object *jhdr = NULL;
+	bool result = false;
+	char *pos;
+
+	uint32_t sz;
+
+	if (!expt_cfg)
+		return false;
+
+	if (is_error) {
+		json_object_put(expt_cfg);
+		return true;
+	}
+
+	if (!fname)
+		goto done;
+
+	outfile = fopen(fname, "w");
+	if (!outfile) {
+		l_error("Failed to save configuration to %s", fname);
+		goto done;
+	}
+
+	jhdr = json_object_new_object();
+	if (!add_string(jhdr, "$schema", js_schema))
+		goto done;
+
+	if (!add_string(jhdr, "id", schema_id))
+		goto done;
+
+	if (!add_string(jhdr, "version", schema_version))
+		goto done;
+
+	hdr = json_object_to_json_string_ext(jhdr, JSON_C_TO_STRING_PRETTY |
+						JSON_C_TO_STRING_NOSLASHESCAPE);
+
+	str = json_object_to_json_string_ext(expt_cfg, JSON_C_TO_STRING_PRETTY |
+						JSON_C_TO_STRING_NOSLASHESCAPE);
+
+	if (!hdr || !str)
+		goto done;
+
+	/*
+	 * Write two strings to the output while stripping closing "}" from the
+	 * header string and opening "{" from the config object.
+	 */
+
+	pos = strrchr(hdr, '}');
+	if (!pos)
+		goto done;
+
+	*pos = '\0';
+
+	pos = strrchr(hdr, '"');
+	if (!pos)
+		goto done;
+
+	pos[1] = ',';
+
+	if (fwrite(hdr, sizeof(char), strlen(hdr), outfile) < strlen(hdr))
+		goto done;
+
+	pos = strchr(str, '{');
+	if (!pos || pos[1] == '\0')
+		goto done;
+
+	pos++;
+
+	sz = strlen(pos);
+
+	if (fwrite(pos, sizeof(char), sz, outfile) < sz)
+		goto done;
+
+	result = true;
+
+done:
+	if (outfile)
+		fclose(outfile);
+
+	json_object_put(expt_cfg);
+
+	if (jhdr)
+		json_object_put(jhdr);
+
+	return result;
+}
diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h
index 16c46c046..4b6b2adb3 100644
--- a/tools/mesh/mesh-db.h
+++ b/tools/mesh/mesh-db.h
@@ -82,3 +82,10 @@ struct l_queue *mesh_db_load_groups(void);
 bool mesh_db_add_group(struct mesh_group *grp);
 bool mesh_db_add_rejected_addr(uint16_t unicast, uint32_t iv_index);
 bool mesh_db_clear_rejected(uint32_t iv_index);
+bool mesh_db_set_device_key(void *expt_cfg, uint16_t unicast, uint8_t key[16]);
+bool mesh_db_set_net_key(void *expt_cfg, uint16_t idx, uint8_t key[16],
+					uint8_t *old_key, uint8_t phase);
+bool mesh_db_set_app_key(void *expt_cfg, uint16_t net_idx, uint16_t app_idx,
+					uint8_t key[16], uint8_t *old_key);
+void *mesh_db_prepare_export(void);
+bool mesh_db_finish_export(bool is_error, void *expt_cfg, const char *fname);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* RE: Mesh Configuration Database
  2021-09-23  3:25 ` [PATCH BlueZ 01/20] tools/mesh-cfgclient: Save provisioner info Inga Stotland
@ 2021-09-23  4:00   ` bluez.test.bot
  2021-09-23  4:14     ` Tedd Ho-Jeong An
  2021-09-23 16:52   ` bluez.test.bot
  1 sibling, 1 reply; 25+ messages in thread
From: bluez.test.bot @ 2021-09-23  4:00 UTC (permalink / raw)
  To: linux-bluetooth, inga.stotland

[-- Attachment #1: Type: text/plain, Size: 2229 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=551419

---Test result---

Test Summary:
CheckPatch                    PASS      30.23 seconds
GitLint                       FAIL      18.49 seconds
Prep - Setup ELL              PASS      49.39 seconds
Build - Prep                  PASS      0.51 seconds
Build - Configure             PASS      9.05 seconds
Build - Make                  PASS      216.88 seconds
Make Check                    PASS      10.03 seconds
Make Distcheck                PASS      252.94 seconds
Build w/ext ELL - Configure   PASS      9.10 seconds
Build w/ext ELL - Make        PASS      205.82 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint with rule in .gitlint
Output:
[BlueZ,01/20] tools/mesh-cfgclient: Save provisioner info
1: T3 Title has trailing punctuation (.): "This adds "provisioners" property to the config database."

[BlueZ,02/20] tools/mesh-cfgclient: Add timestamp to config database
1: T3 Title has trailing punctuation (.): "This adds timestamp property to the configuration database."

[BlueZ,03/20] tools/mesh-cfgclient: Update stored NetKey and AppKey
1: T3 Title has trailing punctuation (:): "NetKeys:"

[BlueZ,04/20] tools/mesh-cfgclient: Keep track of updated keys
1: T8 Title is too short (3<5): "---"

[BlueZ,05/20] tools/mesh: Add new info to stored remote nodes
1: T3 Title has trailing punctuation (,): "When adding a new remote node into configuration database,"

[BlueZ,12/20] tools/mesh-cfgclient: Rename mesh-db APIs for consistency
1: T8 Title is too short (3<5): "---"

[BlueZ,15/20] tools/mesh-cfgclient: Add group parent address for DB compliance
1: T8 Title is too short (3<5): "---"

[BlueZ,16/20] doc/mesh-api: Add ExportKeys call
1: T3 Title has trailing punctuation (:): "Add description for a new method:"

[BlueZ,20/20] tools/mesh-cfgclient: Export configuration database
1: T3 Title has trailing punctuation (.): "This adds main menu command "export-db"."




---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Mesh Configuration Database
  2021-09-23  4:00   ` Mesh Configuration Database bluez.test.bot
@ 2021-09-23  4:14     ` Tedd Ho-Jeong An
  0 siblings, 0 replies; 25+ messages in thread
From: Tedd Ho-Jeong An @ 2021-09-23  4:14 UTC (permalink / raw)
  To: linux-bluetooth, inga.stotland

Hi Inga,

Please discard the Gitlint result here.
I will take a look CI tomorrow.

On Wed, 2021-09-22 at 21:00 -0700, bluez.test.bot@gmail.com wrote:
> This is automated email and please do not reply to this email!
> 
> Dear submitter,
> 
> Thank you for submitting the patches to the linux bluetooth mailing list.
> This is a CI test results with your patch series:
> PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=551419
> 
> ---Test result---
> 
> Test Summary:
> CheckPatch                    PASS      30.23 seconds
> GitLint                       FAIL      18.49 seconds
> Prep - Setup ELL              PASS      49.39 seconds
> Build - Prep                  PASS      0.51 seconds
> Build - Configure             PASS      9.05 seconds
> Build - Make                  PASS      216.88 seconds
> Make Check                    PASS      10.03 seconds
> Make Distcheck                PASS      252.94 seconds
> Build w/ext ELL - Configure   PASS      9.10 seconds
> Build w/ext ELL - Make        PASS      205.82 seconds
> 
> Details
> ##############################
> Test: GitLint - FAIL
> Desc: Run gitlint with rule in .gitlint
> Output:
> [BlueZ,01/20] tools/mesh-cfgclient: Save provisioner info
> 1: T3 Title has trailing punctuation (.): "This adds "provisioners" property to the config
> database."
> 
> [BlueZ,02/20] tools/mesh-cfgclient: Add timestamp to config database
> 1: T3 Title has trailing punctuation (.): "This adds timestamp property to the configuration
> database."
> 
> [BlueZ,03/20] tools/mesh-cfgclient: Update stored NetKey and AppKey
> 1: T3 Title has trailing punctuation (:): "NetKeys:"
> 
> [BlueZ,04/20] tools/mesh-cfgclient: Keep track of updated keys
> 1: T8 Title is too short (3<5): "---"
> 
> [BlueZ,05/20] tools/mesh: Add new info to stored remote nodes
> 1: T3 Title has trailing punctuation (,): "When adding a new remote node into configuration
> database,"
> 
> [BlueZ,12/20] tools/mesh-cfgclient: Rename mesh-db APIs for consistency
> 1: T8 Title is too short (3<5): "---"
> 
> [BlueZ,15/20] tools/mesh-cfgclient: Add group parent address for DB compliance
> 1: T8 Title is too short (3<5): "---"
> 
> [BlueZ,16/20] doc/mesh-api: Add ExportKeys call
> 1: T3 Title has trailing punctuation (:): "Add description for a new method:"
> 
> [BlueZ,20/20] tools/mesh-cfgclient: Export configuration database
> 1: T3 Title has trailing punctuation (.): "This adds main menu command "export-db"."
> 
> 
> 
> 
> ---
> Regards,
> Linux Bluetooth
> 


^ permalink raw reply	[flat|nested] 25+ messages in thread

* RE: Mesh Configuration Database
  2021-09-23  3:25 ` [PATCH BlueZ 01/20] tools/mesh-cfgclient: Save provisioner info Inga Stotland
  2021-09-23  4:00   ` Mesh Configuration Database bluez.test.bot
@ 2021-09-23 16:52   ` bluez.test.bot
  1 sibling, 0 replies; 25+ messages in thread
From: bluez.test.bot @ 2021-09-23 16:52 UTC (permalink / raw)
  To: linux-bluetooth, inga.stotland

[-- Attachment #1: Type: text/plain, Size: 886 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=551419

---Test result---

Test Summary:
CheckPatch                    PASS      31.26 seconds
GitLint                       PASS      18.98 seconds
Prep - Setup ELL              PASS      51.32 seconds
Build - Prep                  PASS      0.56 seconds
Build - Configure             PASS      9.78 seconds
Build - Make                  PASS      232.28 seconds
Make Check                    PASS      9.78 seconds
Make Distcheck                PASS      276.41 seconds
Build w/ext ELL - Configure   PASS      9.97 seconds
Build w/ext ELL - Make        PASS      223.01 seconds



---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH BlueZ 00/20] Mesh Configuration Database
  2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
                   ` (19 preceding siblings ...)
  2021-09-23  3:26 ` [PATCH BlueZ 20/20] tools/mesh-cfgclient: Export configuration database Inga Stotland
@ 2021-09-27 20:27 ` Gix, Brian
  20 siblings, 0 replies; 25+ messages in thread
From: Gix, Brian @ 2021-09-27 20:27 UTC (permalink / raw)
  To: linux-bluetooth, Stotland, Inga

With very minor tweeks (to avoid introducing, then removing obsolete naming) Patchset has been applied.

On Wed, 2021-09-22 at 20:25 -0700, Inga Stotland wrote:
> This patch set (I apologize for its size, but this cannot be helped)
> implements support for the newly published Mesh Configuration Database
> Profile.
> 
> The changes are mostly contained to tools/mesh-cfgclient.c and tools/mesh
> subdirectory. The only exception is the introduction of a new D-Bus mesh
> API method ExportKeys() on org.bluez.mesh.Management1 interface.
> 
> The new functionality allows to export a snapshot of mesh state
> from the point of view of mesh provisioner/configuration manager in
> a standard format that can be used for to transferring the "ownership"
> of the mesh configuration to another provisioner/configuration manager.
> 
> The changes break backwards compatibility with for the previous
> versions of config-db.json that were generated when using
> mesh-cfgclient tool. This can be amended by manually correcting the
> field names and property values.
> 
> Inga Stotland (20):
>   tools/mesh-cfgclient: Save provisioner info
>   tools/mesh-cfgclient: Add timestamp to config database
>   tools/mesh-cfgclient: Update stored NetKey and AppKey
>   tools/mesh-cfgclient: Keep track of updated keys
>   tools/mesh: Add new info to stored remote nodes
>   tools/mesh-cfgclient: Overwrite config values when adding new ones
>   tools/mesh-cfgclient: Store remote node's model bindings
>   tools/mesh-cfgclient: Store remote node's model subs
>   tools/mesh-cfgclient: Disallow model commands w/o composition
>   tools/mesh-cfgclient: Store remote's model publication info
>   tools/mesh-cfgclient: Check the result of config save
>   tools/mesh-cfgclient: Rename mesh-db APIs for consistency
>   tools/mesh-cfgclient: Save remote node feature setting
>   tools/mesh-cfgclient: Store remote's heartbeat sub/pub
>   tools/mesh-cfgclient: Add group parent address for DB compliance
>   doc/mesh-api: Add ExportKeys call
>   mesh: Implement ExportKeys() method
>   tools/mesh-cfgclient: Store UUIDs in standard format
>   tools/mesh-cfgclient: Excluded addresses property
>   tools/mesh-cfgclient: Export configuration database
> 
>  doc/mesh-api.txt       |   56 ++
>  mesh/keyring.c         |  286 ++++++++-
>  mesh/keyring.h         |    2 +
>  mesh/manager.c         |   35 ++
>  tools/mesh-cfgclient.c |  212 ++++++-
>  tools/mesh/cfgcli.c    |  284 ++++++---
>  tools/mesh/keys.c      |    4 +-
>  tools/mesh/mesh-db.c   | 1260 +++++++++++++++++++++++++++++++++++++---
>  tools/mesh/mesh-db.h   |   66 ++-
>  tools/mesh/model.h     |   13 +-
>  tools/mesh/remote.c    |  205 +++++--
>  tools/mesh/remote.h    |   11 +-
>  12 files changed, 2206 insertions(+), 228 deletions(-)
> 


^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2021-09-27 20:27 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-23  3:25 [PATCH BlueZ 00/20] Mesh Configuration Database Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 01/20] tools/mesh-cfgclient: Save provisioner info Inga Stotland
2021-09-23  4:00   ` Mesh Configuration Database bluez.test.bot
2021-09-23  4:14     ` Tedd Ho-Jeong An
2021-09-23 16:52   ` bluez.test.bot
2021-09-23  3:25 ` [PATCH BlueZ 02/20] tools/mesh-cfgclient: Add timestamp to config database Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 03/20] tools/mesh-cfgclient: Update stored NetKey and AppKey Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 04/20] tools/mesh-cfgclient: Keep track of updated keys Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 05/20] tools/mesh: Add new info to stored remote nodes Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 06/20] tools/mesh-cfgclient: Overwrite config values when adding new ones Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 07/20] tools/mesh-cfgclient: Store remote node's model bindings Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 08/20] tools/mesh-cfgclient: Store remote node's model subs Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 09/20] tools/mesh-cfgclient: Disallow model commands w/o composition Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 10/20] tools/mesh-cfgclient: Store remote's model publication info Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 11/20] tools/mesh-cfgclient: Check the result of config save Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 12/20] tools/mesh-cfgclient: Rename mesh-db APIs for consistency Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 13/20] tools/mesh-cfgclient: Save remote node feature setting Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 14/20] tools/mesh-cfgclient: Store remote's heartbeat sub/pub Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 15/20] tools/mesh-cfgclient: Add group parent address for DB compliance Inga Stotland
2021-09-23  3:25 ` [PATCH BlueZ 16/20] doc/mesh-api: Add ExportKeys call Inga Stotland
2021-09-23  3:26 ` [PATCH BlueZ 17/20] mesh: Implement ExportKeys() method Inga Stotland
2021-09-23  3:26 ` [PATCH BlueZ 18/20] tools/mesh-cfgclient: Store UUIDs in standard format Inga Stotland
2021-09-23  3:26 ` [PATCH BlueZ 19/20] tools/mesh-cfgclient: Excluded addresses property Inga Stotland
2021-09-23  3:26 ` [PATCH BlueZ 20/20] tools/mesh-cfgclient: Export configuration database Inga Stotland
2021-09-27 20:27 ` [PATCH BlueZ 00/20] Mesh Configuration Database Gix, Brian

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.