linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH BlueZ 00/10] Mesh provisioning and configuration tool
@ 2019-10-29 20:40 Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 01/10] tools/mesh: Remove extra dependencies from agent.c Inga Stotland
                   ` (9 more replies)
  0 siblings, 10 replies; 11+ messages in thread
From: Inga Stotland @ 2019-10-29 20:40 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This patch set adds a mesh-cfgclient tool that allows to create
mesh networks operating over advertising (PB-ADV) provisioning
bearer. The toolimplements support for:
 - creating of the inital "pcrovisioner" node with the initial network key
 - adding/removing network and application keys to the network
 - provisioning remote devices into mesh network
 - configuring of the provisioned nodes (Config Model Client operations)

Network configuration is saved in JSON format file.
Disclaimer: the storage is work in progress and is not fully implemented.

Also, some functionality overlaps with meshctl tool (GATT based mesh
network provisioning and configuration tool). For the time being, these
two tools are separate and coexist side by side. Eventually, additional
work needs to be done to eleiminate overlapping areas and potentially
merge the tools. Currently, meshctl supplemenal files are moved to
tools/mesh-gatt


Inga Stotland (10):
  tools/mesh: Remove extra dependencies from agent.c
  tools/mesh: Move meshctl specific files to mesh-gatt
  tools: Add mesh-cfgclient tool
  tools/mesh: move remote node processing to a separate file
  tools/mesh-cfgclient: Commands for Management iface methods
  tools/mesh-cfgclient: Add config menu key commands
  tools/mesh-cfgclient: Add timeout for expected response
  tools/mesh-cfgclient: Command line option for config
  tools/mesh: add initial support for config storage
  tools/mesh-cfgclient: Add README file

 .gitignore                                |    1 +
 Makefile.tools                            |   46 +-
 tools/mesh-cfgclient.c                    | 1861 +++++++++++++++++++++
 tools/mesh-gatt/README                    |   43 +
 tools/{mesh => mesh-gatt}/config-client.c |   14 +-
 tools/{mesh => mesh-gatt}/config-server.c |   14 +-
 tools/{mesh => mesh-gatt}/crypto.c        |    5 +-
 tools/{mesh => mesh-gatt}/crypto.h        |    0
 tools/{mesh => mesh-gatt}/gatt.c          |   11 +-
 tools/{mesh => mesh-gatt}/gatt.h          |    0
 tools/mesh-gatt/keys.h                    |   39 +
 tools/{mesh => mesh-gatt}/local_node.json |    0
 tools/{mesh => mesh-gatt}/mesh-net.h      |    0
 tools/{mesh => mesh-gatt}/net.c           |   16 +-
 tools/{mesh => mesh-gatt}/net.h           |    0
 tools/{mesh => mesh-gatt}/node.c          |   16 +-
 tools/{mesh => mesh-gatt}/node.h          |    0
 tools/{mesh => mesh-gatt}/onoff-model.c   |   15 +-
 tools/{mesh => mesh-gatt}/onoff-model.h   |    0
 tools/{mesh => mesh-gatt}/prov-db.c       |   14 +-
 tools/{mesh => mesh-gatt}/prov-db.h       |    0
 tools/{mesh => mesh-gatt}/prov.c          |   16 +-
 tools/{mesh => mesh-gatt}/prov.h          |    0
 tools/{mesh => mesh-gatt}/prov_db.json    |    0
 tools/mesh-gatt/util.c                    |  220 +++
 tools/mesh-gatt/util.h                    |   41 +
 tools/mesh/README                         |   43 +-
 tools/mesh/agent.c                        |   32 +-
 tools/mesh/cfgcli.c                       | 1378 +++++++++++++++
 tools/mesh/cfgcli.h                       |   25 +
 tools/mesh/config-model.h                 |    3 +-
 tools/mesh/keys.c                         |  166 ++
 tools/mesh/keys.h                         |   27 +-
 tools/mesh/mesh-db.c                      |  742 ++++++++
 tools/mesh/mesh-db.h                      |   54 +
 tools/mesh/model.h                        |   63 +
 tools/mesh/remote.c                       |  229 +++
 tools/mesh/remote.h                       |   27 +
 tools/mesh/util.c                         |  107 +-
 tools/mesh/util.h                         |   14 +-
 tools/meshctl.c                           |   22 +-
 41 files changed, 5073 insertions(+), 231 deletions(-)
 create mode 100644 tools/mesh-cfgclient.c
 create mode 100644 tools/mesh-gatt/README
 rename tools/{mesh => mesh-gatt}/config-client.c (99%)
 rename tools/{mesh => mesh-gatt}/config-server.c (94%)
 rename tools/{mesh => mesh-gatt}/crypto.c (99%)
 rename tools/{mesh => mesh-gatt}/crypto.h (100%)
 rename tools/{mesh => mesh-gatt}/gatt.c (98%)
 rename tools/{mesh => mesh-gatt}/gatt.h (100%)
 create mode 100644 tools/mesh-gatt/keys.h
 rename tools/{mesh => mesh-gatt}/local_node.json (100%)
 rename tools/{mesh => mesh-gatt}/mesh-net.h (100%)
 rename tools/{mesh => mesh-gatt}/net.c (99%)
 rename tools/{mesh => mesh-gatt}/net.h (100%)
 rename tools/{mesh => mesh-gatt}/node.c (98%)
 rename tools/{mesh => mesh-gatt}/node.h (100%)
 rename tools/{mesh => mesh-gatt}/onoff-model.c (96%)
 rename tools/{mesh => mesh-gatt}/onoff-model.h (100%)
 rename tools/{mesh => mesh-gatt}/prov-db.c (99%)
 rename tools/{mesh => mesh-gatt}/prov-db.h (100%)
 rename tools/{mesh => mesh-gatt}/prov.c (98%)
 rename tools/{mesh => mesh-gatt}/prov.h (100%)
 rename tools/{mesh => mesh-gatt}/prov_db.json (100%)
 create mode 100644 tools/mesh-gatt/util.c
 create mode 100644 tools/mesh-gatt/util.h
 create mode 100644 tools/mesh/cfgcli.c
 create mode 100644 tools/mesh/cfgcli.h
 create mode 100644 tools/mesh/keys.c
 create mode 100644 tools/mesh/mesh-db.c
 create mode 100644 tools/mesh/mesh-db.h
 create mode 100644 tools/mesh/model.h
 create mode 100644 tools/mesh/remote.c
 create mode 100644 tools/mesh/remote.h

-- 
2.21.0


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

* [PATCH BlueZ 01/10] tools/mesh: Remove extra dependencies from agent.c
  2019-10-29 20:40 [PATCH BlueZ 00/10] Mesh provisioning and configuration tool Inga Stotland
@ 2019-10-29 20:40 ` Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 02/10] tools/mesh: Move meshctl specific files to mesh-gatt Inga Stotland
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Inga Stotland @ 2019-10-29 20:40 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This removes dependency on glib and mesh/util.c.
Also, fixes few style glitches.
---
 tools/mesh/agent.c | 32 +++++++++++++++++++++++---------
 1 file changed, 23 insertions(+), 9 deletions(-)

diff --git a/tools/mesh/agent.c b/tools/mesh/agent.c
index 08e498541..0ec76f3b7 100644
--- a/tools/mesh/agent.c
+++ b/tools/mesh/agent.c
@@ -30,12 +30,9 @@
 #include <stdbool.h>
 #include <inttypes.h>
 
-#include <glib.h>
-
 #include <lib/bluetooth.h>
 
 #include "src/shared/shell.h"
-#include "tools/mesh/util.h"
 #include "tools/mesh/agent.h"
 
 struct input_request {
@@ -63,6 +60,22 @@ static void reset_input_request(void)
 	pending_request.user_data = NULL;
 }
 
+static bool str2hex(const char *str, uint16_t in_len, uint8_t *out,
+							uint16_t out_len)
+{
+	uint16_t i;
+
+	if (in_len < out_len * 2)
+		return false;
+
+	for (i = 0; i < out_len; i++) {
+		if (sscanf(&str[i * 2], "%02hhx", &out[i]) != 1)
+			return false;
+	}
+
+	return true;
+}
+
 static void response_hexadecimal(const char *input, void *user_data)
 {
 	uint8_t buf[MAX_HEXADECIMAL_OOB_LEN];
@@ -111,8 +124,8 @@ static bool request_hexadecimal(uint16_t len)
 		return false;
 
 	bt_shell_printf("Request hexadecimal key (hex %d octets)\n", len);
-	bt_shell_prompt_input("mesh", "Enter key (hex number):", response_hexadecimal,
-								NULL);
+	bt_shell_prompt_input("mesh", "Enter key (hex number):",
+						response_hexadecimal, NULL);
 
 	return true;
 }
@@ -130,7 +143,8 @@ static uint32_t power_ten(uint8_t power)
 static bool request_decimal(uint16_t len)
 {
 	bt_shell_printf("Request decimal key (0 - %d)\n", power_ten(len) - 1);
-	bt_shell_prompt_input("mesh", "Enter Numeric key:", response_decimal, NULL);
+	bt_shell_prompt_input("mesh-agent", "Enter Numeric key:",
+							response_decimal, NULL);
 
 	return true;
 }
@@ -141,8 +155,8 @@ static bool request_ascii(uint16_t len)
 		return false;
 
 	bt_shell_printf("Request ASCII key (max characters %d)\n", len);
-	bt_shell_prompt_input("mesh", "Enter key (ascii string):", response_ascii,
-									NULL);
+	bt_shell_prompt_input("mesh", "Enter key (ascii string):",
+							response_ascii, NULL);
 
 	return true;
 }
@@ -153,7 +167,7 @@ bool agent_input_request(oob_type_t type, uint16_t max_len, agent_input_cb cb,
 	bool result;
 
 	if (pending_request.type != NONE)
-		return FALSE;
+		return false;
 
 	switch (type) {
 	case HEXADECIMAL:
-- 
2.21.0


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

* [PATCH BlueZ 02/10] tools/mesh: Move meshctl specific files to mesh-gatt
  2019-10-29 20:40 [PATCH BlueZ 00/10] Mesh provisioning and configuration tool Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 01/10] tools/mesh: Remove extra dependencies from agent.c Inga Stotland
@ 2019-10-29 20:40 ` Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 03/10] tools: Add mesh-cfgclient tool Inga Stotland
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Inga Stotland @ 2019-10-29 20:40 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

---
 Makefile.tools                            | 30 +++++++++++++----------
 tools/{mesh => mesh-gatt}/README          |  0
 tools/{mesh => mesh-gatt}/config-client.c | 14 ++++++-----
 tools/{mesh => mesh-gatt}/config-server.c | 14 +++++------
 tools/{mesh => mesh-gatt}/crypto.c        |  5 ++--
 tools/{mesh => mesh-gatt}/crypto.h        |  0
 tools/{mesh => mesh-gatt}/gatt.c          | 11 +++++----
 tools/{mesh => mesh-gatt}/gatt.h          |  0
 tools/{mesh => mesh-gatt}/keys.h          |  0
 tools/{mesh => mesh-gatt}/local_node.json |  0
 tools/{mesh => mesh-gatt}/mesh-net.h      |  0
 tools/{mesh => mesh-gatt}/net.c           | 16 ++++++------
 tools/{mesh => mesh-gatt}/net.h           |  0
 tools/{mesh => mesh-gatt}/node.c          | 16 ++++++------
 tools/{mesh => mesh-gatt}/node.h          |  0
 tools/{mesh => mesh-gatt}/onoff-model.c   | 15 ++++++------
 tools/{mesh => mesh-gatt}/onoff-model.h   |  0
 tools/{mesh => mesh-gatt}/prov-db.c       | 14 +++++------
 tools/{mesh => mesh-gatt}/prov-db.h       |  0
 tools/{mesh => mesh-gatt}/prov.c          | 16 ++++++------
 tools/{mesh => mesh-gatt}/prov.h          |  0
 tools/{mesh => mesh-gatt}/prov_db.json    |  0
 tools/{mesh => mesh-gatt}/util.c          |  7 +++---
 tools/{mesh => mesh-gatt}/util.h          |  0
 tools/meshctl.c                           | 22 +++++++++--------
 25 files changed, 98 insertions(+), 82 deletions(-)
 rename tools/{mesh => mesh-gatt}/README (100%)
 rename tools/{mesh => mesh-gatt}/config-client.c (99%)
 rename tools/{mesh => mesh-gatt}/config-server.c (94%)
 rename tools/{mesh => mesh-gatt}/crypto.c (99%)
 rename tools/{mesh => mesh-gatt}/crypto.h (100%)
 rename tools/{mesh => mesh-gatt}/gatt.c (98%)
 rename tools/{mesh => mesh-gatt}/gatt.h (100%)
 rename tools/{mesh => mesh-gatt}/keys.h (100%)
 rename tools/{mesh => mesh-gatt}/local_node.json (100%)
 rename tools/{mesh => mesh-gatt}/mesh-net.h (100%)
 rename tools/{mesh => mesh-gatt}/net.c (99%)
 rename tools/{mesh => mesh-gatt}/net.h (100%)
 rename tools/{mesh => mesh-gatt}/node.c (98%)
 rename tools/{mesh => mesh-gatt}/node.h (100%)
 rename tools/{mesh => mesh-gatt}/onoff-model.c (96%)
 rename tools/{mesh => mesh-gatt}/onoff-model.h (100%)
 rename tools/{mesh => mesh-gatt}/prov-db.c (99%)
 rename tools/{mesh => mesh-gatt}/prov-db.h (100%)
 rename tools/{mesh => mesh-gatt}/prov.c (98%)
 rename tools/{mesh => mesh-gatt}/prov.h (100%)
 rename tools/{mesh => mesh-gatt}/prov_db.json (100%)
 rename tools/{mesh => mesh-gatt}/util.c (98%)
 rename tools/{mesh => mesh-gatt}/util.h (100%)

diff --git a/Makefile.tools b/Makefile.tools
index 7ce05b7ef..feb2c8550 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -304,25 +304,29 @@ if MESH
 bin_PROGRAMS += tools/meshctl
 
 tools_meshctl_SOURCES = tools/meshctl.c \
-				tools/mesh/mesh-net.h \
-				tools/mesh/node.h tools/mesh/node.c \
-				tools/mesh/gatt.h tools/mesh/gatt.c \
-				tools/mesh/crypto.h tools/mesh/crypto.c \
-				tools/mesh/keys.h \
-				tools/mesh/net.h tools/mesh/net.c \
-				tools/mesh/prov.h tools/mesh/prov.c \
-				tools/mesh/util.h tools/mesh/util.c \
 				tools/mesh/agent.h tools/mesh/agent.c \
-				tools/mesh/prov-db.h tools/mesh/prov-db.c \
-				tools/mesh/config-model.h tools/mesh/config-client.c \
-				tools/mesh/config-server.c \
-				tools/mesh/onoff-model.h tools/mesh/onoff-model.c
+				tools/mesh/config-model.h\
+				tools/mesh-gatt/mesh-net.h \
+				tools/mesh-gatt/node.h tools/mesh-gatt/node.c \
+				tools/mesh-gatt/gatt.h tools/mesh-gatt/gatt.c \
+				tools/mesh-gatt/crypto.h\
+				tools/mesh-gatt/crypto.c \
+				tools/mesh-gatt/keys.h \
+				tools/mesh-gatt/net.h tools/mesh-gatt/net.c \
+				tools/mesh-gatt/prov.h tools/mesh-gatt/prov.c \
+				tools/mesh-gatt/util.h tools/mesh-gatt/util.c \
+				tools/mesh-gatt/prov-db.h \
+				tools/mesh-gatt/prov-db.c \
+				tools/mesh-gatt/config-client.c \
+				tools/mesh-gatt/config-server.c \
+				tools/mesh-gatt/onoff-model.h \
+				tools/mesh-gatt/onoff-model.c
 tools_meshctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \
 				lib/libbluetooth-internal.la \
 				$(GLIB_LIBS) $(DBUS_LIBS) -ljson-c -lreadline
 endif
 
-EXTRA_DIST += tools/mesh/local_node.json tools/mesh/prov_db.json
+EXTRA_DIST += tools/mesh-gatt/local_node.json tools/mesh-gatt/prov_db.json
 
 if DEPRECATED
 bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
diff --git a/tools/mesh/README b/tools/mesh-gatt/README
similarity index 100%
rename from tools/mesh/README
rename to tools/mesh-gatt/README
diff --git a/tools/mesh/config-client.c b/tools/mesh-gatt/config-client.c
similarity index 99%
rename from tools/mesh/config-client.c
rename to tools/mesh-gatt/config-client.c
index df2643622..5e6374f2b 100644
--- a/tools/mesh/config-client.c
+++ b/tools/mesh-gatt/config-client.c
@@ -39,14 +39,16 @@
 
 #include "src/shared/shell.h"
 #include "src/shared/util.h"
-#include "tools/mesh/mesh-net.h"
-#include "tools/mesh/keys.h"
-#include "tools/mesh/net.h"
-#include "tools/mesh/node.h"
-#include "tools/mesh/prov-db.h"
-#include "tools/mesh/util.h"
+
 #include "tools/mesh/config-model.h"
 
+#include "tools/mesh-gatt/mesh-net.h"
+#include "tools/mesh-gatt/keys.h"
+#include "tools/mesh-gatt/net.h"
+#include "tools/mesh-gatt/node.h"
+#include "tools/mesh-gatt/prov-db.h"
+#include "tools/mesh-gatt/util.h"
+
 #define MIN_COMPOSITION_LEN 16
 
 static uint32_t print_mod_id(uint8_t *data, bool vid)
diff --git a/tools/mesh/config-server.c b/tools/mesh-gatt/config-server.c
similarity index 94%
rename from tools/mesh/config-server.c
rename to tools/mesh-gatt/config-server.c
index 55035bacf..8fc6edcc0 100644
--- a/tools/mesh/config-server.c
+++ b/tools/mesh-gatt/config-server.c
@@ -36,16 +36,16 @@
 #include <wordexp.h>
 
 #include <glib.h>
+#include "tools/mesh/config-model.h"
 
 #include "src/shared/util.h"
 #include "src/shared/shell.h"
-#include "tools/mesh/mesh-net.h"
-#include "tools/mesh/keys.h"
-#include "tools/mesh/net.h"
-#include "tools/mesh/node.h"
-#include "tools/mesh/prov-db.h"
-#include "tools/mesh/util.h"
-#include "tools/mesh/config-model.h"
+#include "tools/mesh-gatt/mesh-net.h"
+#include "tools/mesh-gatt/keys.h"
+#include "tools/mesh-gatt/net.h"
+#include "tools/mesh-gatt/node.h"
+#include "tools/mesh-gatt/prov-db.h"
+#include "tools/mesh-gatt/util.h"
 
 static bool server_msg_recvd(uint16_t src, uint8_t *data,
 				uint16_t len, void *user_data)
diff --git a/tools/mesh/crypto.c b/tools/mesh-gatt/crypto.c
similarity index 99%
rename from tools/mesh/crypto.c
rename to tools/mesh-gatt/crypto.c
index 9935612cf..493b502ca 100644
--- a/tools/mesh/crypto.c
+++ b/tools/mesh-gatt/crypto.c
@@ -43,8 +43,9 @@
 #endif
 
 #include "src/shared/util.h"
-#include "tools/mesh/mesh-net.h"
-#include "tools/mesh/crypto.h"
+
+#include "tools/mesh-gatt/mesh-net.h"
+#include "tools/mesh-gatt/crypto.h"
 
 static int alg_new(int fd, const void *keyval, socklen_t keylen,
 		size_t mic_size)
diff --git a/tools/mesh/crypto.h b/tools/mesh-gatt/crypto.h
similarity index 100%
rename from tools/mesh/crypto.h
rename to tools/mesh-gatt/crypto.h
diff --git a/tools/mesh/gatt.c b/tools/mesh-gatt/gatt.c
similarity index 98%
rename from tools/mesh/gatt.c
rename to tools/mesh-gatt/gatt.c
index 0a942d4eb..bff594733 100644
--- a/tools/mesh/gatt.c
+++ b/tools/mesh-gatt/gatt.c
@@ -40,11 +40,12 @@
 #include "gdbus/gdbus.h"
 #include "lib/bluetooth.h"
 #include "lib/uuid.h"
-#include "tools/mesh/node.h"
-#include "tools/mesh/util.h"
-#include "tools/mesh/gatt.h"
-#include "tools/mesh/prov.h"
-#include "tools/mesh/net.h"
+
+#include "tools/mesh-gatt/node.h"
+#include "tools/mesh-gatt/util.h"
+#include "tools/mesh-gatt/gatt.h"
+#include "tools/mesh-gatt/prov.h"
+#include "tools/mesh-gatt/net.h"
 
 #define MESH_PROV_DATA_OUT_UUID_STR	"00002adc-0000-1000-8000-00805f9b34fb"
 #define MESH_PROXY_DATA_OUT_UUID_STR	"00002ade-0000-1000-8000-00805f9b34fb"
diff --git a/tools/mesh/gatt.h b/tools/mesh-gatt/gatt.h
similarity index 100%
rename from tools/mesh/gatt.h
rename to tools/mesh-gatt/gatt.h
diff --git a/tools/mesh/keys.h b/tools/mesh-gatt/keys.h
similarity index 100%
rename from tools/mesh/keys.h
rename to tools/mesh-gatt/keys.h
diff --git a/tools/mesh/local_node.json b/tools/mesh-gatt/local_node.json
similarity index 100%
rename from tools/mesh/local_node.json
rename to tools/mesh-gatt/local_node.json
diff --git a/tools/mesh/mesh-net.h b/tools/mesh-gatt/mesh-net.h
similarity index 100%
rename from tools/mesh/mesh-net.h
rename to tools/mesh-gatt/mesh-net.h
diff --git a/tools/mesh/net.c b/tools/mesh-gatt/net.c
similarity index 99%
rename from tools/mesh/net.c
rename to tools/mesh-gatt/net.c
index f1ef23ab8..e8171c677 100644
--- a/tools/mesh/net.c
+++ b/tools/mesh-gatt/net.c
@@ -35,14 +35,14 @@
 #include "src/shared/util.h"
 #include "src/shared/shell.h"
 
-#include "tools/mesh/crypto.h"
-#include "tools/mesh/gatt.h"
-#include "tools/mesh/mesh-net.h"
-#include "tools/mesh/util.h"
-#include "tools/mesh/keys.h"
-#include "tools/mesh/node.h"
-#include "tools/mesh/prov-db.h"
-#include "tools/mesh/net.h"
+#include "tools/mesh-gatt/crypto.h"
+#include "tools/mesh-gatt/gatt.h"
+#include "tools/mesh-gatt/mesh-net.h"
+#include "tools/mesh-gatt/util.h"
+#include "tools/mesh-gatt/keys.h"
+#include "tools/mesh-gatt/node.h"
+#include "tools/mesh-gatt/prov-db.h"
+#include "tools/mesh-gatt/net.h"
 
 struct address_range
 {
diff --git a/tools/mesh/net.h b/tools/mesh-gatt/net.h
similarity index 100%
rename from tools/mesh/net.h
rename to tools/mesh-gatt/net.h
diff --git a/tools/mesh/node.c b/tools/mesh-gatt/node.c
similarity index 98%
rename from tools/mesh/node.c
rename to tools/mesh-gatt/node.c
index d23315ce4..6afa6d6ab 100644
--- a/tools/mesh/node.c
+++ b/tools/mesh-gatt/node.c
@@ -39,14 +39,16 @@
 #include "src/shared/util.h"
 #include "src/shared/shell.h"
 #include "gdbus/gdbus.h"
-#include "tools/mesh/mesh-net.h"
+
 #include "tools/mesh/config-model.h"
-#include "tools/mesh/node.h"
-#include "tools/mesh/keys.h"
-#include "tools/mesh/gatt.h"
-#include "tools/mesh/net.h"
-#include "tools/mesh/prov-db.h"
-#include "tools/mesh/util.h"
+
+#include "tools/mesh-gatt/mesh-net.h"
+#include "tools/mesh-gatt/node.h"
+#include "tools/mesh-gatt/keys.h"
+#include "tools/mesh-gatt/gatt.h"
+#include "tools/mesh-gatt/net.h"
+#include "tools/mesh-gatt/prov-db.h"
+#include "tools/mesh-gatt/util.h"
 
 struct mesh_model {
 	struct mesh_model_ops cbs;
diff --git a/tools/mesh/node.h b/tools/mesh-gatt/node.h
similarity index 100%
rename from tools/mesh/node.h
rename to tools/mesh-gatt/node.h
diff --git a/tools/mesh/onoff-model.c b/tools/mesh-gatt/onoff-model.c
similarity index 96%
rename from tools/mesh/onoff-model.c
rename to tools/mesh-gatt/onoff-model.c
index b52afe2c8..92c9a3105 100644
--- a/tools/mesh/onoff-model.c
+++ b/tools/mesh-gatt/onoff-model.c
@@ -40,13 +40,14 @@
 
 #include "src/shared/shell.h"
 #include "src/shared/util.h"
-#include "tools/mesh/mesh-net.h"
-#include "tools/mesh/keys.h"
-#include "tools/mesh/net.h"
-#include "tools/mesh/node.h"
-#include "tools/mesh/prov-db.h"
-#include "tools/mesh/util.h"
-#include "tools/mesh/onoff-model.h"
+
+#include "tools/mesh-gatt/mesh-net.h"
+#include "tools/mesh-gatt/keys.h"
+#include "tools/mesh-gatt/net.h"
+#include "tools/mesh-gatt/node.h"
+#include "tools/mesh-gatt/prov-db.h"
+#include "tools/mesh-gatt/util.h"
+#include "tools/mesh-gatt/onoff-model.h"
 
 static uint8_t trans_id;
 static uint16_t onoff_app_idx = APP_IDX_INVALID;
diff --git a/tools/mesh/onoff-model.h b/tools/mesh-gatt/onoff-model.h
similarity index 100%
rename from tools/mesh/onoff-model.h
rename to tools/mesh-gatt/onoff-model.h
diff --git a/tools/mesh/prov-db.c b/tools/mesh-gatt/prov-db.c
similarity index 99%
rename from tools/mesh/prov-db.c
rename to tools/mesh-gatt/prov-db.c
index 05b2547fd..44096e1e1 100644
--- a/tools/mesh/prov-db.c
+++ b/tools/mesh-gatt/prov-db.c
@@ -42,13 +42,13 @@
 #include "src/shared/util.h"
 #include "src/shared/shell.h"
 
-#include "tools/mesh/mesh-net.h"
-#include "tools/mesh/crypto.h"
-#include "tools/mesh/keys.h"
-#include "tools/mesh/net.h"
-#include "tools/mesh/node.h"
-#include "tools/mesh/util.h"
-#include "tools/mesh/prov-db.h"
+#include "tools/mesh-gatt/mesh-net.h"
+#include "tools/mesh-gatt/crypto.h"
+#include "tools/mesh-gatt/keys.h"
+#include "tools/mesh-gatt/net.h"
+#include "tools/mesh-gatt/node.h"
+#include "tools/mesh-gatt/util.h"
+#include "tools/mesh-gatt/prov-db.h"
 
 #define CHECK_KEY_IDX_RANGE(x) (((x) >= 0) && ((x) <= 4095))
 
diff --git a/tools/mesh/prov-db.h b/tools/mesh-gatt/prov-db.h
similarity index 100%
rename from tools/mesh/prov-db.h
rename to tools/mesh-gatt/prov-db.h
diff --git a/tools/mesh/prov.c b/tools/mesh-gatt/prov.c
similarity index 98%
rename from tools/mesh/prov.c
rename to tools/mesh-gatt/prov.c
index acbc5a1c8..0f9d85d01 100644
--- a/tools/mesh/prov.c
+++ b/tools/mesh-gatt/prov.c
@@ -41,14 +41,16 @@
 #include "src/shared/shell.h"
 
 #include "gdbus/gdbus.h"
-#include "tools/mesh/node.h"
-#include "tools/mesh/gatt.h"
-#include "tools/mesh/crypto.h"
-#include "tools/mesh/mesh-net.h"
-#include "tools/mesh/util.h"
+
 #include "tools/mesh/agent.h"
-#include "tools/mesh/prov.h"
-#include "tools/mesh/net.h"
+
+#include "tools/mesh-gatt/node.h"
+#include "tools/mesh-gatt/gatt.h"
+#include "tools/mesh-gatt/crypto.h"
+#include "tools/mesh-gatt/mesh-net.h"
+#include "tools/mesh-gatt/util.h"
+#include "tools/mesh-gatt/prov.h"
+#include "tools/mesh-gatt/net.h"
 
 /* Provisioning Security Levels */
 #define MESH_PROV_SEC_HIGH	2
diff --git a/tools/mesh/prov.h b/tools/mesh-gatt/prov.h
similarity index 100%
rename from tools/mesh/prov.h
rename to tools/mesh-gatt/prov.h
diff --git a/tools/mesh/prov_db.json b/tools/mesh-gatt/prov_db.json
similarity index 100%
rename from tools/mesh/prov_db.json
rename to tools/mesh-gatt/prov_db.json
diff --git a/tools/mesh/util.c b/tools/mesh-gatt/util.c
similarity index 98%
rename from tools/mesh/util.c
rename to tools/mesh-gatt/util.c
index 47abc0b24..564665e5d 100644
--- a/tools/mesh/util.c
+++ b/tools/mesh-gatt/util.c
@@ -33,9 +33,10 @@
 
 #include "src/shared/shell.h"
 #include "src/shared/util.h"
-#include "tools/mesh/mesh-net.h"
-#include "tools/mesh/node.h"
-#include "tools/mesh/util.h"
+
+#include "tools/mesh-gatt/mesh-net.h"
+#include "tools/mesh-gatt/node.h"
+#include "tools/mesh-gatt/util.h"
 
 void set_menu_prompt(const char *name, const char *id)
 {
diff --git a/tools/mesh/util.h b/tools/mesh-gatt/util.h
similarity index 100%
rename from tools/mesh/util.h
rename to tools/mesh-gatt/util.h
diff --git a/tools/meshctl.c b/tools/meshctl.c
index 6b6f10882..57998fded 100644
--- a/tools/meshctl.c
+++ b/tools/meshctl.c
@@ -47,18 +47,20 @@
 #include "src/shared/shell.h"
 #include "src/shared/util.h"
 #include "gdbus/gdbus.h"
-#include "mesh/mesh-net.h"
-#include "mesh/gatt.h"
-#include "mesh/crypto.h"
-#include "mesh/node.h"
-#include "mesh/net.h"
-#include "mesh/keys.h"
-#include "mesh/prov.h"
-#include "mesh/util.h"
+
 #include "mesh/agent.h"
-#include "mesh/prov-db.h"
 #include "mesh/config-model.h"
-#include "mesh/onoff-model.h"
+
+#include "mesh-gatt/mesh-net.h"
+#include "mesh-gatt/gatt.h"
+#include "mesh-gatt/crypto.h"
+#include "mesh-gatt/node.h"
+#include "mesh-gatt/net.h"
+#include "mesh-gatt/keys.h"
+#include "mesh-gatt/prov.h"
+#include "mesh-gatt/util.h"
+#include "mesh-gatt/prov-db.h"
+#include "mesh-gatt/onoff-model.h"
 
 /* String display constants */
 #define COLORED_NEW	COLOR_GREEN "NEW" COLOR_OFF
-- 
2.21.0


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

* [PATCH BlueZ 03/10] tools: Add mesh-cfgclient tool
  2019-10-29 20:40 [PATCH BlueZ 00/10] Mesh provisioning and configuration tool Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 01/10] tools/mesh: Remove extra dependencies from agent.c Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 02/10] tools/mesh: Move meshctl specific files to mesh-gatt Inga Stotland
@ 2019-10-29 20:40 ` Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 04/10] tools/mesh: move remote node processing to a separate file Inga Stotland
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Inga Stotland @ 2019-10-29 20:40 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This adds a tool that allows to create  mesh networks
operating over advertising (PB-ADV) provisioning bearer.
The main commands are:
create - Create new mesh network with one initial node
discover-unprovisioned - Look for devices to provision
list-unprovisioned - List unprovisioned devices
provision - Initiate provisioning
list-nodes - List remote mesh nodes
---
 .gitignore                |    1 +
 Makefile.tools            |   11 +
 tools/mesh-cfgclient.c    | 1238 +++++++++++++++++++++++++++++++++++++
 tools/mesh/cfgcli.c       | 1104 +++++++++++++++++++++++++++++++++
 tools/mesh/cfgcli.h       |   24 +
 tools/mesh/config-model.h |    3 +-
 tools/mesh/keys.c         |  166 +++++
 tools/mesh/keys.h         |   26 +
 tools/mesh/model.h        |   63 ++
 tools/mesh/util.c         |  140 +++++
 tools/mesh/util.h         |   29 +
 11 files changed, 2804 insertions(+), 1 deletion(-)
 create mode 100644 tools/mesh-cfgclient.c
 create mode 100644 tools/mesh/cfgcli.c
 create mode 100644 tools/mesh/cfgcli.h
 create mode 100644 tools/mesh/keys.c
 create mode 100644 tools/mesh/keys.h
 create mode 100644 tools/mesh/model.h
 create mode 100644 tools/mesh/util.c
 create mode 100644 tools/mesh/util.h

diff --git a/.gitignore b/.gitignore
index 52c0d6994..9f389359f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -129,6 +129,7 @@ emulator/b1ee
 emulator/hfp
 client/bluetoothctl
 tools/meshctl
+tools/mesh-cfgclient
 mesh/bluetooth-meshd
 
 src/bluetoothd.8
diff --git a/Makefile.tools b/Makefile.tools
index feb2c8550..12687e495 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -324,6 +324,17 @@ tools_meshctl_SOURCES = tools/meshctl.c \
 tools_meshctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \
 				lib/libbluetooth-internal.la \
 				$(GLIB_LIBS) $(DBUS_LIBS) -ljson-c -lreadline
+
+bin_PROGRAMS +=  tools/mesh-cfgclient
+
+tools_mesh_cfgclient_SOURCES = tools/mesh-cfgclient.c \
+				tools/mesh/model.h tools/mesh/config-model.h \
+				tools/mesh/cfgcli.h tools/mesh/cfgcli.c \
+				tools/mesh/keys.h tools/mesh/keys.c \
+				tools/mesh/util.h tools/mesh/util.c \
+				tools/mesh/agent.h tools/mesh/agent.c
+tools_mesh_cfgclient_LDADD = lib/libbluetooth-internal.la src/libshared-ell.la \
+						$(ell_ldadd) -lreadline
 endif
 
 EXTRA_DIST += tools/mesh-gatt/local_node.json tools/mesh-gatt/prov_db.json
diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
new file mode 100644
index 000000000..f225d021f
--- /dev/null
+++ b/tools/mesh-cfgclient.c
@@ -0,0 +1,1238 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2019  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <ctype.h>
+#include <dbus/dbus.h>
+#include <stdio.h>
+#include <time.h>
+#include <ell/ell.h>
+
+#include "src/shared/shell.h"
+#include "src/shared/util.h"
+
+#include "mesh/mesh.h"
+#include "mesh/mesh-defs.h"
+
+#include "tools/mesh/agent.h"
+#include "tools/mesh/cfgcli.h"
+#include "tools/mesh/keys.h"
+#include "tools/mesh/model.h"
+
+#define PROMPT_ON	COLOR_BLUE "[mesh-cfgclient]" COLOR_OFF "# "
+#define PROMPT_OFF	"Waiting to connect to bluetooth-meshd..."
+
+#define CFG_SRV_MODEL	0x0000
+#define CFG_CLI_MODEL	0x0001
+
+#define UNPROV_SCAN_MAX_SECS	300
+
+#define DEFAULT_START_ADDRESS	0x00aa
+#define DEFAULT_NET_INDEX	0x0000
+
+struct meshcfg_el {
+	const char *path;
+	uint8_t index;
+	uint16_t mods[2];
+};
+
+struct meshcfg_app {
+	const char *path;
+	const char *agent_path;
+	struct meshcfg_el ele;
+	uint16_t cid;
+	uint16_t pid;
+	uint16_t vid;
+	uint16_t crpl;
+	uint8_t uuid[16];
+};
+
+struct meshcfg_node {
+	const char *path;
+	struct l_dbus_proxy *proxy;
+	struct l_dbus_proxy *mgmt_proxy;
+	union {
+		uint64_t u64;
+		uint8_t u8[8];
+	} token;
+};
+
+struct unprov_device {
+	time_t last_seen;
+	int16_t rssi;
+	uint8_t uuid[16];
+};
+
+struct remote_node {
+	uint16_t unicast;
+	uint16_t net_idx;
+	uint8_t uuid[16];
+	uint8_t num_ele;
+};
+
+static struct l_dbus *dbus;
+
+static struct l_queue *node_proxies;
+static struct l_dbus_proxy *net_proxy;
+static struct meshcfg_node *local;
+static struct model_info *cfgcli;
+
+static struct l_queue *devices;
+static struct l_queue *nodes;
+
+static bool prov_in_progress;
+static const char *caps[2] = {"out-numeric", "in-numeric"};
+
+static struct meshcfg_app app = {
+	.path = "/mesh/cfgclient",
+	.agent_path = "/mesh/cfgclient/agent",
+	.cid = 0x05f1,
+	.pid = 0x0002,
+	.vid = 0x0001,
+	.crpl = 10,
+	.ele = {
+		.path = "/mesh/cfgclient/ele0",
+		.index = 0,
+		.mods = {CFG_SRV_MODEL, CFG_CLI_MODEL}
+	}
+};
+
+static const struct option options[] = {
+	{ "address",	optional_argument, 0, 'a' },
+	{ "net-index",	optional_argument, 0, 'n' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *address_opt;
+static const char *net_idx_opt;
+
+static uint16_t prov_address;
+static uint16_t prov_net_idx;
+
+static const char **optargs[] = {
+	&address_opt,
+	&net_idx_opt,
+};
+
+static const char *help[] = {
+	"Starting unicast address for remote nodes",
+	"Net index for provisioning subnet"
+};
+
+static const struct bt_shell_opt opt = {
+	.options = options,
+	.optno = sizeof(options) / sizeof(struct option),
+	.optstr = "a:n:",
+	.optarg = optargs,
+	.help = help,
+};
+
+static const char *dbus_err_args = "org.freedesktop.DBus.Error.InvalidArgs";
+static const char *dbus_err_fail = "org.freedesktop.DBus.Error.Failed";
+static const char *dbus_err_support = "org.freedesktop.DBus.Error.NotSupported";
+
+static bool parse_argument_on_off(int argc, char *argv[], bool *value)
+{
+	if (!strcmp(argv[1], "on") || !strcmp(argv[1], "yes")) {
+		*value = TRUE;
+		return TRUE;
+	}
+
+	if (!strcmp(argv[1], "off") || !strcmp(argv[1], "no")) {
+		*value = FALSE;
+		return TRUE;
+	}
+
+	bt_shell_printf("Invalid argument %s\n", argv[1]);
+	return FALSE;
+}
+
+static bool match_device_uuid(const void *a, const void *b)
+{
+	const struct unprov_device *dev = a;
+	const uint8_t *uuid = b;
+
+	return (memcmp(dev->uuid, uuid, 16) == 0);
+}
+
+static void print_device(void *a, void *b)
+{
+	const struct unprov_device *dev = a;
+	struct tm *tm = localtime(&dev->last_seen);
+	char buf[80];
+	char *str;
+
+	assert(strftime(buf, sizeof(buf), "%c", tm));
+
+	str = l_util_hexstring_upper(dev->uuid, sizeof(dev->uuid));
+	bt_shell_printf("UUID: %s, RSSI %d, Seen: %s\n",
+			str, dev->rssi, buf);
+
+	l_free(str);
+}
+
+static bool match_node_addr(const void *a, const void *b)
+{
+	const struct remote_node *rmt = a;
+	uint16_t addr = L_PTR_TO_UINT(b);
+
+	if (addr >= rmt->unicast &&
+				addr <= (rmt->unicast + rmt->num_ele - 1))
+		return true;
+
+	return false;
+}
+
+static uint16_t get_primary_subnet_idx(uint16_t addr)
+{
+	struct remote_node *rmt;
+
+	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
+
+	if (!rmt) {
+		bt_shell_printf("Net key not found: trying %4.4x\n",
+			PRIMARY_NET_IDX);
+		return prov_net_idx;
+		/*
+		 * TODO: When the remote node recovery from storage is
+		 * implemented, return NET_IDX_INVALID" here.
+		 */
+	}
+
+	return rmt->net_idx;
+}
+
+struct send_data {
+	const char *ele_path;
+	bool rmt;
+	bool is_dev_key;
+	uint16_t dst;
+	uint16_t idx;
+	uint8_t *data;
+	uint16_t len;
+};
+
+struct key_data {
+	const char *ele_path;
+	uint16_t dst;
+	uint16_t idx;
+	uint16_t net_idx;
+	bool update;
+};
+
+static void send_msg_setup(struct l_dbus_message *msg, void *user_data)
+{
+	struct send_data *req = user_data;
+	struct l_dbus_message_builder *builder;
+
+	builder = l_dbus_message_builder_new(msg);
+
+	l_dbus_message_builder_append_basic(builder, 'o', req->ele_path);
+	l_dbus_message_builder_append_basic(builder, 'q', &req->dst);
+	if (req->is_dev_key)
+		l_dbus_message_builder_append_basic(builder, 'b', &req->rmt);
+	l_dbus_message_builder_append_basic(builder, 'q', &req->idx);
+	l_dbus_message_builder_enter_array(builder, "y");
+
+	while (req->len) {
+		l_dbus_message_builder_append_basic(builder, 'y', req->data);
+		req->data++;
+		req->len--;
+	}
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+}
+
+static bool send_msg(void *user_data, uint16_t dst, uint16_t idx,
+						uint8_t *data, uint16_t len)
+{
+	struct send_data *req;
+	uint16_t net_idx_tx = idx;
+	bool is_dev_key;
+	const char *method_name;
+
+	is_dev_key = (idx == APP_IDX_DEV_REMOTE || idx == APP_IDX_DEV_LOCAL);
+	method_name = is_dev_key ? "DevKeySend" : "Send";
+
+	if (is_dev_key) {
+		net_idx_tx = get_primary_subnet_idx(dst);
+		if (net_idx_tx == NET_IDX_INVALID)
+			return false;
+	}
+
+	req = l_new(struct send_data, 1);
+	req->ele_path = user_data;
+	req->dst = dst;
+	req->idx = net_idx_tx;
+	req->data = data;
+	req->len = len;
+	req->rmt = (idx == APP_IDX_DEV_REMOTE);
+	req->is_dev_key = is_dev_key;
+
+	return l_dbus_proxy_method_call(local->proxy, method_name,
+					send_msg_setup, NULL, req, l_free) != 0;
+}
+
+static void send_key_setup(struct l_dbus_message *msg, void *user_data)
+{
+	struct key_data *req = user_data;
+	struct l_dbus_message_builder *builder;
+
+	builder = l_dbus_message_builder_new(msg);
+
+	l_dbus_message_builder_append_basic(builder, 'o', req->ele_path);
+	l_dbus_message_builder_append_basic(builder, 'q', &req->dst);
+	l_dbus_message_builder_append_basic(builder, 'q', &req->idx);
+	l_dbus_message_builder_append_basic(builder, 'q', &req->net_idx);
+	l_dbus_message_builder_append_basic(builder, 'b', &req->update);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+}
+
+static bool send_key(void *user_data, uint16_t dst, uint16_t key_idx,
+						bool is_appkey, bool update)
+{
+	struct key_data *req;
+	uint16_t net_idx;
+	const char *method_name = (!is_appkey) ? "AddNetKey" : "AddAppKey";
+
+	net_idx = get_primary_subnet_idx(dst);
+	if (net_idx == NET_IDX_INVALID)
+		return false;
+
+	req = l_new(struct key_data, 1);
+	req->ele_path = user_data;
+	req->dst = dst;
+	req->idx = key_idx;
+	req->net_idx = net_idx;
+	req->update = update;
+
+	return l_dbus_proxy_method_call(local->proxy, method_name,
+				send_key_setup, NULL, req, l_free) != 0;
+}
+
+static void client_init(void)
+{
+	cfgcli = cfgcli_init(send_key, (void *) app.ele.path);
+	cfgcli->ops.set_send_func(send_msg, (void *) app.ele.path);
+}
+
+static bool caps_getter(struct l_dbus *dbus,
+				struct l_dbus_message *message,
+				struct l_dbus_message_builder *builder,
+				void *user_data)
+{
+	uint32_t i;
+
+	if (!l_dbus_message_builder_enter_array(builder, "s"))
+		return false;
+	for (i = 0; i < L_ARRAY_SIZE(caps); i++)
+		l_dbus_message_builder_append_basic(builder, 's', caps[i]);
+
+	l_dbus_message_builder_leave_array(builder);
+
+	return true;
+}
+
+static struct l_dbus_message *disp_numeric_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	char *str;
+	uint32_t n;
+
+	if (!l_dbus_message_get_arguments(msg, "su", &str, &n)) {
+		l_error("Cannot parse \"DisplayNumeric\" arguments");
+		return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
+	}
+
+	if (!str || strlen(str) != strlen("in-numeric") ||
+			strncmp(str, "in-numeric", strlen("in-numeric")))
+		return l_dbus_message_new_error(msg, dbus_err_support, NULL);
+
+	bt_shell_printf(COLOR_YELLOW "Enter %u on remote device" COLOR_OFF, n);
+
+	return l_dbus_message_new_method_return(msg);
+}
+
+static void agent_input_done(oob_type_t type, void *buf, uint16_t len,
+								void *user_data)
+{
+	struct l_dbus_message *msg = user_data;
+	struct l_dbus_message *reply;
+	uint32_t val_u32;
+
+	switch (type) {
+	case NONE:
+	case OUTPUT:
+	case ASCII:
+	case HEXADECIMAL:
+	default:
+		return;
+	case DECIMAL:
+		if (len >= 8) {
+			bt_shell_printf("Bad input length");
+			return;
+		}
+
+		val_u32 = l_get_be32(buf);
+		reply = l_dbus_message_new_method_return(msg);
+		l_dbus_message_set_arguments(reply, "u", val_u32);
+		l_dbus_send(dbus, reply);
+		break;
+	}
+}
+
+static struct l_dbus_message *prompt_numeric_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	char *str;
+
+	if (!l_dbus_message_get_arguments(msg, "s", &str)) {
+		l_error("Cannot parse \"PromptNumeric\" arguments");
+		return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
+	}
+
+	if (!str || strlen(str) != strlen("out-numeric") ||
+			strncmp(str, "out-numeric", strlen("out-numeric")))
+		return l_dbus_message_new_error(msg, dbus_err_support, NULL);
+
+	l_dbus_message_ref(msg);
+	agent_input_request(DECIMAL, 8, agent_input_done, msg);
+
+	return NULL;
+}
+
+static void setup_agent_iface(struct l_dbus_interface *iface)
+{
+	l_dbus_interface_property(iface, "Capabilities", 0, "as", caps_getter,
+								NULL);
+	/* TODO: Other properties */
+	l_dbus_interface_method(iface, "DisplayNumeric", 0, disp_numeric_call,
+						"", "su", "type", "number");
+	l_dbus_interface_method(iface, "PromptNumeric", 0, prompt_numeric_call,
+						"u", "s", "number", "type");
+
+}
+
+static bool register_agent(void)
+{
+	if (!l_dbus_register_interface(dbus, MESH_PROVISION_AGENT_INTERFACE,
+					setup_agent_iface, NULL, false)) {
+		l_error("Unable to register agent interface");
+		return false;
+	}
+
+	if (!l_dbus_register_object(dbus, app.agent_path, NULL, NULL,
+				MESH_PROVISION_AGENT_INTERFACE, NULL, NULL)) {
+		l_error("Failed to register object %s", app.agent_path);
+		return false;
+	}
+
+	return true;
+}
+
+static void try_set_node_proxy(void *a, void *b)
+{
+	struct l_dbus_proxy *proxy = a;
+	const char *interface = l_dbus_proxy_get_interface(proxy);
+	const char *path = l_dbus_proxy_get_path(proxy);
+
+	if (strcmp(local->path, path))
+		return;
+
+	if (!strcmp(interface, MESH_MANAGEMENT_INTERFACE))
+		local->mgmt_proxy = proxy;
+	else if (!strcmp(interface, MESH_NODE_INTERFACE))
+		local->proxy = proxy;
+}
+
+static void attach_node_reply(struct l_dbus_proxy *proxy,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct meshcfg_node *node = user_data;
+	struct l_dbus_message_iter iter_cfg;
+
+	if (l_dbus_message_is_error(msg)) {
+		const char *name;
+
+		l_dbus_message_get_error(msg, &name, NULL);
+		l_error("Failed to attach node: %s", name);
+		goto fail;
+
+	}
+
+	if (!l_dbus_message_get_arguments(msg, "oa(ya(qa{sv}))",
+						&local->path, &iter_cfg))
+		goto fail;
+
+	bt_shell_printf("Attached with path %s\n", local->path);
+
+	/* Populate node's proxies */
+	l_queue_foreach(node_proxies, try_set_node_proxy, node);
+
+	/* Remove from orphaned proxies list */
+	if (local->proxy)
+		l_queue_remove(node_proxies, local->proxy);
+
+	if (local->mgmt_proxy)
+		l_queue_remove(node_proxies, local->mgmt_proxy);
+
+	/* Inititalize config client model */
+	client_init();
+
+	return;
+
+fail:
+	l_free(node);
+	node = NULL;
+}
+
+static void attach_node_setup(struct l_dbus_message *msg, void *user_data)
+{
+	l_dbus_message_set_arguments(msg, "ot", app.path,
+						l_get_be64(local->token.u8));
+}
+
+static void create_net_reply(struct l_dbus_proxy *proxy,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	char *str;
+	uint64_t tmp;
+
+	if (l_dbus_message_is_error(msg)) {
+		const char *name;
+
+		l_dbus_message_get_error(msg, &name, NULL);
+		l_error("Failed to create network: %s", name);
+		return;
+
+	}
+
+	if (!l_dbus_message_get_arguments(msg, "t", &tmp))
+		return;
+
+	local = l_new(struct meshcfg_node, 1);
+	local->token.u64 = l_get_be64(&tmp);
+	str = l_util_hexstring(&local->token.u8[0], 8);
+	bt_shell_printf("Created new node with token %s\n", str);
+	l_free(str);
+
+	keys_add_net_key(PRIMARY_NET_IDX);
+
+	l_dbus_proxy_method_call(net_proxy, "Attach", attach_node_setup,
+						attach_node_reply, NULL,
+						NULL);
+}
+
+static void create_net_setup(struct l_dbus_message *msg, void *user_data)
+{
+	uint i;
+	struct l_dbus_message_builder *builder;
+
+	/* Generate random UUID */
+	l_getrandom(app.uuid, sizeof(app.uuid));
+
+	builder = l_dbus_message_builder_new(msg);
+
+	l_dbus_message_builder_append_basic(builder, 'o', app.path);
+	l_dbus_message_builder_enter_array(builder, "y");
+
+	for (i = 0; i < L_ARRAY_SIZE(app.uuid); i++)
+		l_dbus_message_builder_append_basic(builder, 'y',
+								&(app.uuid[i]));
+
+	l_dbus_message_builder_leave_array(builder);
+
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+}
+
+static void cmd_create_network(int argc, char *argv[])
+{
+	l_dbus_proxy_method_call(net_proxy, "CreateNetwork", create_net_setup,
+						create_net_reply, NULL,
+						NULL);
+
+}
+
+static void scan_reply(struct l_dbus_proxy *proxy, struct l_dbus_message *msg,
+								void *user_data)
+{
+	if (l_dbus_message_is_error(msg)) {
+		const char *name;
+
+		l_dbus_message_get_error(msg, &name, NULL);
+		l_error("Failed to start unprovisioned scan: %s", name);
+		return;
+	}
+
+	bt_shell_printf("Unprovisioned scan started\n");
+}
+
+static void scan_setup(struct l_dbus_message *msg, void *user_data)
+{
+	int32_t secs = L_PTR_TO_UINT(user_data);
+
+	l_dbus_message_set_arguments(msg, "q", (uint16_t) secs);
+}
+
+static void cmd_scan_unprov(int argc, char *argv[])
+{
+	uint32_t secs = 0;
+	bool enable;
+
+	if (!local || !local->proxy || !local->mgmt_proxy) {
+		bt_shell_printf("Node is not attached\n");
+		return;
+	}
+
+	if (parse_argument_on_off(argc, argv, &enable) == FALSE) {
+		bt_shell_printf("Failed to parse input\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (argc == 3)
+		sscanf(argv[2], "%u", &secs);
+
+	if (secs > UNPROV_SCAN_MAX_SECS)
+		secs = UNPROV_SCAN_MAX_SECS;
+
+	if (enable)
+		l_dbus_proxy_method_call(local->mgmt_proxy, "UnprovisionedScan",
+						scan_setup, scan_reply,
+						L_UINT_TO_PTR(secs), NULL);
+	else
+		l_dbus_proxy_method_call(local->mgmt_proxy,
+						"UnprovisionedScanCancel",
+						NULL, NULL, NULL, NULL);
+
+}
+
+static void cmd_list_unprov(int argc, char *argv[])
+{
+	bt_shell_printf(COLOR_YELLOW "Unprovisioned devices:\n" COLOR_OFF);
+	l_queue_foreach(devices, print_device, NULL);
+}
+
+static void print_node(void *a, void *b)
+{
+	struct remote_node *node = a;
+	char *str;
+
+	bt_shell_printf(COLOR_YELLOW "Mesh node:\n" COLOR_OFF);
+	str = l_util_hexstring_upper(node->uuid, 16);
+	bt_shell_printf("\t" COLOR_GREEN "UUID = %s\n" COLOR_OFF, str);
+	l_free(str);
+	bt_shell_printf("\t" COLOR_GREEN "primary = %4.4x\n" COLOR_OFF,
+								node->unicast);
+	bt_shell_printf("\t" COLOR_GREEN "elements = %u\n" COLOR_OFF,
+								node->num_ele);
+	bt_shell_printf("\t" COLOR_GREEN "net_key_idx = %3.3x\n" COLOR_OFF,
+								node->net_idx);
+}
+
+static void cmd_list_nodes(int argc, char *argv[])
+{
+	l_queue_foreach(nodes, print_node, NULL);
+}
+
+static void add_node_reply(struct l_dbus_proxy *proxy,
+				struct l_dbus_message *msg, void *user_data)
+{
+	if (l_dbus_message_is_error(msg)) {
+		const char *name;
+
+		prov_in_progress = false;
+		l_dbus_message_get_error(msg, &name, NULL);
+		l_error("Failed to start provisioning: %s", name);
+		return;
+	}
+
+	bt_shell_printf("Provisioning started\n");
+}
+
+static void add_node_setup(struct l_dbus_message *msg, void *user_data)
+{
+	int i;
+	char *str = user_data;
+	size_t sz;
+	unsigned char *uuid;
+	struct l_dbus_message_builder *builder;
+
+	uuid = l_util_from_hexstring(str, &sz);
+	if (!uuid || sz != 16) {
+		l_error("Failed to generate UUID array from %s", str);
+		return;
+	}
+
+	builder = l_dbus_message_builder_new(msg);
+
+	l_dbus_message_builder_enter_array(builder, "y");
+
+	for (i = 0; i < (int)sz; i++)
+		l_dbus_message_builder_append_basic(builder, 'y', &(uuid[i]));
+
+	l_dbus_message_builder_leave_array(builder);
+
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+
+	l_free(uuid);
+}
+
+static void cmd_start_prov(int argc, char *argv[])
+{
+	if (!local || !local->proxy || !local->mgmt_proxy) {
+		bt_shell_printf("Node is not attached\n");
+		return;
+	}
+
+	if (prov_in_progress) {
+		bt_shell_printf("Provisioning is already in progress\n");
+		return;
+	}
+
+	if (!argv[1] || (strlen(argv[1]) != 32)) {
+		bt_shell_printf(COLOR_RED "Requires UUID\n" COLOR_RED);
+		return;
+	}
+
+	if (l_dbus_proxy_method_call(local->mgmt_proxy, "AddNode",
+						add_node_setup, add_node_reply,
+						argv[1], NULL))
+		prov_in_progress = true;
+}
+
+static const struct bt_shell_menu main_menu = {
+	.name = "main",
+	.entries = {
+	{ "create", NULL, cmd_create_network,
+			"Create new mesh network with one initial node" },
+	{ "discover-unprovisioned", "<on/off> [seconds]", cmd_scan_unprov,
+			"Look for devices to provision" },
+	{ "list-unprovisioned", NULL, cmd_list_unprov,
+			"List unprovisioned devices" },
+	{ "provision", "<uuid>", cmd_start_prov,
+			"Initiate provisioning"},
+	{ "list-nodes", NULL, cmd_list_nodes,
+			"List remote mesh nodes"},
+	{ } },
+};
+
+static void proxy_added(struct l_dbus_proxy *proxy, void *user_data)
+{
+	const char *interface = l_dbus_proxy_get_interface(proxy);
+	const char *path = l_dbus_proxy_get_path(proxy);
+
+	bt_shell_printf("Proxy added: %s (%s)\n", interface, path);
+
+	if (!strcmp(interface, MESH_NETWORK_INTERFACE)) {
+		net_proxy = proxy;
+		return;
+	}
+
+	if (!strcmp(interface, MESH_MANAGEMENT_INTERFACE)) {
+
+		if (local && local->path) {
+			if (!strcmp(local->path, path))
+				local->mgmt_proxy = proxy;
+		} else
+			l_queue_push_tail(node_proxies, proxy);
+		return;
+	}
+
+	if (!strcmp(interface, MESH_NODE_INTERFACE)) {
+
+		if (local && local->path) {
+			if (!strcmp(local->path, path))
+				local->proxy = proxy;
+		} else
+			l_queue_push_tail(node_proxies, proxy);
+	}
+}
+
+static void proxy_removed(struct l_dbus_proxy *proxy, void *user_data)
+{
+	const char *interface = l_dbus_proxy_get_interface(proxy);
+	const char *path = l_dbus_proxy_get_path(proxy);
+
+	bt_shell_printf("Proxy removed: %s (%s)\n", interface, path);
+
+	if (!strcmp(interface, MESH_NETWORK_INTERFACE)) {
+		bt_shell_printf("Mesh removed, terminating.\n");
+		l_main_quit();
+		return;
+	}
+
+	if (!strcmp(interface, MESH_NODE_INTERFACE)) {
+		if (local && local->path && !strcmp(local->path, path))
+			local->proxy = NULL;
+
+		l_queue_remove(node_proxies, proxy);
+		return;
+	}
+
+	if (!strcmp(interface, MESH_MANAGEMENT_INTERFACE)) {
+		if (local && local->path && !strcmp(local->path, path))
+			local->mgmt_proxy = NULL;
+
+		l_queue_remove(node_proxies, proxy);
+	}
+}
+
+static bool mod_getter(struct l_dbus *dbus,
+				struct l_dbus_message *message,
+				struct l_dbus_message_builder *builder,
+				void *user_data)
+{
+	l_dbus_message_builder_enter_array(builder, "q");
+	l_dbus_message_builder_append_basic(builder, 'q', &app.ele.mods[0]);
+	l_dbus_message_builder_append_basic(builder, 'q', &app.ele.mods[1]);
+	l_dbus_message_builder_leave_array(builder);
+
+	return true;
+}
+
+static bool vmod_getter(struct l_dbus *dbus,
+				struct l_dbus_message *message,
+				struct l_dbus_message_builder *builder,
+				void *user_data)
+{
+	l_dbus_message_builder_enter_array(builder, "(qq)");
+	l_dbus_message_builder_leave_array(builder);
+
+	return true;
+}
+
+static bool ele_idx_getter(struct l_dbus *dbus,
+				struct l_dbus_message *message,
+				struct l_dbus_message_builder *builder,
+				void *user_data)
+{
+	l_dbus_message_builder_append_basic(builder, 'y', &app.ele.index);
+
+	return true;
+}
+
+static struct l_dbus_message *dev_msg_recv_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct l_dbus_message_iter iter;
+	uint16_t src, idx;
+	uint8_t *data;
+	uint32_t n;
+	bool rmt;
+
+	if (!l_dbus_message_get_arguments(msg, "qbqay", &src, &rmt, &idx,
+								&iter)) {
+		l_error("Cannot parse received message");
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+	}
+
+	if (!l_dbus_message_iter_get_fixed_array(&iter, &data, &n)) {
+		l_error("Cannot parse received message: data");
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+	}
+
+	bt_shell_printf("Received dev key message (len %u):", n);
+
+	/* Pass to the configuration client */
+	if (cfgcli && cfgcli->ops.recv)
+		cfgcli->ops.recv(src, APP_IDX_DEV_REMOTE, data, n);
+
+	return l_dbus_message_new_method_return(msg);
+}
+
+static void setup_ele_iface(struct l_dbus_interface *iface)
+{
+	/* Properties */
+	l_dbus_interface_property(iface, "Index", 0, "y", ele_idx_getter,
+									NULL);
+	l_dbus_interface_property(iface, "VendorModels", 0, "a(qq)",
+							vmod_getter, NULL);
+	l_dbus_interface_property(iface, "Models", 0, "aq", mod_getter, NULL);
+
+	/* Methods */
+	l_dbus_interface_method(iface, "DevKeyMessageReceived", 0,
+				dev_msg_recv_call, "", "qbqay", "source",
+				"remote", "net_index", "data");
+
+	/* TODO: Other methods */
+}
+
+static struct l_dbus_message *scan_result_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct l_dbus_message_iter iter;
+	int16_t rssi;
+	uint32_t n;
+	uint8_t *prov_data;
+	char *str;
+	struct unprov_device *dev;
+
+	if (!l_dbus_message_get_arguments(msg, "nay", &rssi, &iter)) {
+		l_error("Cannot parse scan results");
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+
+	}
+
+	if (!l_dbus_message_iter_get_fixed_array(&iter, &prov_data, &n) ||
+								n < 16) {
+		l_error("Cannot parse scan result: data");
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+	}
+
+	bt_shell_printf("Scan result:\n");
+	bt_shell_printf("\t" COLOR_GREEN "rssi = %d\n" COLOR_OFF, rssi);
+	str = l_util_hexstring_upper(prov_data, 16);
+	bt_shell_printf("\t" COLOR_GREEN "UUID = %s\n" COLOR_OFF, str);
+	l_free(str);
+
+	/* TODO: Handle the rest of provisioning data if present */
+
+	dev = l_queue_find(devices, match_device_uuid, prov_data);
+	if (!dev) {
+		dev = l_new(struct unprov_device, 1);
+		memcpy(dev->uuid, prov_data, sizeof(dev->uuid));
+		/* TODO: timed self-destructor */
+		l_queue_push_tail(devices, dev);
+	}
+
+	/* Update with the latest rssi */
+	dev->rssi = rssi;
+	dev->last_seen = time(NULL);
+
+	return l_dbus_message_new_method_return(msg);
+}
+
+static struct l_dbus_message *req_prov_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	uint8_t cnt;
+	struct l_dbus_message *reply;
+
+	if (!l_dbus_message_get_arguments(msg, "y", &cnt)) {
+		l_error("Cannot parse request for prov data");
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+
+	}
+
+	bt_shell_printf("Assign addresses for %u elements\n", cnt);
+	reply = l_dbus_message_new_method_return(msg);
+
+	l_dbus_message_set_arguments(reply, "qq", prov_net_idx, prov_address);
+
+	return reply;
+}
+
+static void remove_device(uint8_t *uuid)
+{
+	struct unprov_device *dev;
+
+	dev = l_queue_remove_if(devices, match_device_uuid, uuid);
+	l_free(dev);
+}
+
+static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct l_dbus_message_iter iter;
+	int16_t unicast;
+	uint8_t cnt;
+	uint32_t n;
+	uint8_t *uuid;
+	struct remote_node *node;
+
+	if (!prov_in_progress)
+		return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
+
+	prov_in_progress = false;
+
+	if (!l_dbus_message_get_arguments(msg, "ayqy", &iter, &unicast, &cnt)) {
+		l_error("Cannot parse add node complete message");
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+
+	}
+
+	if (!l_dbus_message_iter_get_fixed_array(&iter, &uuid, &n) ||
+								n != 16) {
+		l_error("Cannot parse add node complete message: uuid");
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+	}
+
+	node = l_new(struct remote_node, 1);
+	memcpy(node->uuid, uuid, 16);
+	node->unicast = unicast;
+	node->num_ele = cnt;
+	node->net_idx = prov_net_idx;
+
+	bt_shell_printf("Provisioning done:\n");
+	print_node(node, NULL);
+	l_queue_push_tail(nodes, node);
+	remove_device(uuid);
+
+	prov_address = unicast + cnt;
+
+	return l_dbus_message_new_method_return(msg);
+}
+
+static struct l_dbus_message *add_node_fail_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct l_dbus_message_iter iter;
+	uint32_t n;
+	uint8_t *uuid;
+	char *str, *reason;
+
+	if (!prov_in_progress)
+		return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
+
+	prov_in_progress = false;
+
+	if (!l_dbus_message_get_arguments(msg, "ays", &iter, &reason)) {
+		l_error("Cannot parse add node failed message");
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+
+	}
+
+	if (!l_dbus_message_iter_get_fixed_array(&iter, &uuid, &n) ||
+								n != 16) {
+		l_error("Cannot parse add node failed message: uuid");
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+	}
+
+	bt_shell_printf("Provisioning failed:\n");
+	str = l_util_hexstring_upper(uuid, 16);
+	bt_shell_printf("\t" COLOR_RED "UUID = %s\n" COLOR_OFF, str);
+	l_free(str);
+	bt_shell_printf("\t" COLOR_RED "%s\n" COLOR_OFF, reason);
+
+	remove_device(uuid);
+
+	return l_dbus_message_new_method_return(msg);
+}
+
+static void setup_prov_iface(struct l_dbus_interface *iface)
+{
+	l_dbus_interface_method(iface, "ScanResult", 0, scan_result_call, "",
+							"nay", "rssi", "data");
+
+	l_dbus_interface_method(iface, "RequestProvData", 0, req_prov_call,
+				"qq", "y", "net_index", "unicast", "count");
+
+	l_dbus_interface_method(iface, "AddNodeComplete", 0,
+					add_node_cmplt_call, "", "ayqy",
+					"uuid", "unicast", "count");
+
+	l_dbus_interface_method(iface, "AddNodeFailed", 0, add_node_fail_call,
+					"", "ays", "uuid", "reason");
+}
+
+static bool cid_getter(struct l_dbus *dbus,
+				struct l_dbus_message *message,
+				struct l_dbus_message_builder *builder,
+				void *user_data)
+{
+	l_dbus_message_builder_append_basic(builder, 'q', &app.cid);
+
+	return true;
+}
+
+static bool pid_getter(struct l_dbus *dbus,
+				struct l_dbus_message *message,
+				struct l_dbus_message_builder *builder,
+				void *user_data)
+{
+	l_dbus_message_builder_append_basic(builder, 'q', &app.pid);
+
+	return true;
+}
+
+static bool vid_getter(struct l_dbus *dbus,
+				struct l_dbus_message *message,
+				struct l_dbus_message_builder *builder,
+				void *user_data)
+{
+	l_dbus_message_builder_append_basic(builder, 'q', &app.vid);
+
+	return true;
+}
+static bool crpl_getter(struct l_dbus *dbus,
+				struct l_dbus_message *message,
+				struct l_dbus_message_builder *builder,
+				void *user_data)
+{
+	l_dbus_message_builder_append_basic(builder, 'q', &app.crpl);
+
+	return true;
+}
+
+static void setup_app_iface(struct l_dbus_interface *iface)
+{
+	l_dbus_interface_property(iface, "CompanyID", 0, "q", cid_getter,
+									NULL);
+	l_dbus_interface_property(iface, "VersionID", 0, "q", vid_getter,
+									NULL);
+	l_dbus_interface_property(iface, "ProductID", 0, "q", pid_getter,
+									NULL);
+	l_dbus_interface_property(iface, "CRPL", 0, "q", crpl_getter, NULL);
+
+	/* TODO: Methods */
+}
+
+static bool register_app(void)
+{
+	if (!l_dbus_register_interface(dbus, MESH_APPLICATION_INTERFACE,
+						setup_app_iface, NULL, false)) {
+		l_error("Failed to register interface %s",
+						MESH_APPLICATION_INTERFACE);
+		return false;
+	}
+
+	if (!l_dbus_register_interface(dbus, MESH_PROVISIONER_INTERFACE,
+					setup_prov_iface, NULL, false)) {
+		l_error("Failed to register interface %s",
+						MESH_PROVISIONER_INTERFACE);
+		return false;
+	}
+
+	if (!l_dbus_register_object(dbus, app.path, NULL, NULL,
+					MESH_APPLICATION_INTERFACE, NULL,
+					MESH_PROVISIONER_INTERFACE, NULL,
+									NULL)) {
+		l_error("Failed to register object %s", app.path);
+		return false;
+	}
+
+	if (!register_agent())
+		return false;
+
+	if (!l_dbus_register_interface(dbus, MESH_ELEMENT_INTERFACE,
+						setup_ele_iface, NULL, false)) {
+		l_error("Failed to register interface %s",
+						MESH_ELEMENT_INTERFACE);
+		return false;
+	}
+
+	if (!l_dbus_register_object(dbus, app.ele.path, NULL, NULL,
+				    MESH_ELEMENT_INTERFACE, NULL, NULL)) {
+		l_error("Failed to register object %s", app.ele.path);
+		return false;
+	}
+
+	if (!l_dbus_object_add_interface(dbus, app.path,
+					 L_DBUS_INTERFACE_OBJECT_MANAGER,
+									NULL)) {
+		l_error("Failed to add interface %s",
+					L_DBUS_INTERFACE_OBJECT_MANAGER);
+		return false;
+	}
+
+	return true;
+}
+
+static void client_ready(struct l_dbus_client *client, void *user_data)
+{
+	bt_shell_printf("D-Bus client ready\n");
+	if (!register_app())
+		bt_shell_quit(EXIT_FAILURE);
+
+	bt_shell_attach(fileno(stdin));
+}
+
+static void client_connected(struct l_dbus *dbus, void *user_data)
+{
+	bt_shell_printf("D-Bus client connected\n");
+	bt_shell_set_prompt(PROMPT_ON);
+}
+
+static void client_disconnected(struct l_dbus *dbus, void *user_data)
+{
+	bt_shell_printf("D-Bus client disconnected, exit\n");
+	bt_shell_quit(EXIT_SUCCESS);
+}
+
+static void ready_callback(void *user_data)
+{
+	bt_shell_printf("Connected to D-Bus\n");
+	if (!l_dbus_object_manager_enable(dbus))
+		bt_shell_printf("Failed to register the ObjectManager\n");
+}
+
+int main(int argc, char *argv[])
+{
+	struct l_dbus_client *client;
+	uint32_t val;
+	int status;
+
+	l_log_set_stderr();
+
+	bt_shell_init(argc, argv, &opt);
+	bt_shell_set_menu(&main_menu);
+	bt_shell_set_prompt(PROMPT_OFF);
+
+	if (address_opt && sscanf(address_opt, "%04x", &val) == 1)
+		prov_address = (uint16_t) val;
+	else
+		prov_address = DEFAULT_START_ADDRESS;
+
+	if (net_idx_opt && sscanf(net_idx_opt, "%04x", &val) == 1)
+		prov_net_idx = (uint16_t) val;
+	else
+		prov_net_idx = DEFAULT_NET_INDEX;
+
+	dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS);
+
+	l_dbus_set_ready_handler(dbus, ready_callback, NULL, NULL);
+	client = l_dbus_client_new(dbus, BLUEZ_MESH_NAME, "/org/bluez/mesh");
+
+	l_dbus_client_set_connect_handler(client, client_connected, NULL, NULL);
+	l_dbus_client_set_disconnect_handler(client, client_disconnected, NULL,
+									NULL);
+	l_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
+							NULL, NULL, NULL);
+	l_dbus_client_set_ready_handler(client, client_ready, NULL, NULL);
+
+	node_proxies = l_queue_new();
+	devices = l_queue_new();
+	nodes = l_queue_new();
+
+	status = bt_shell_run();
+
+	l_dbus_client_destroy(client);
+	l_dbus_destroy(dbus);
+
+	return status;
+}
diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
new file mode 100644
index 000000000..9e83f7b69
--- /dev/null
+++ b/tools/mesh/cfgcli.c
@@ -0,0 +1,1104 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2019  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdbool.h>
+
+#include <ell/ell.h>
+
+#include "src/shared/shell.h"
+#include "src/shared/util.h"
+
+#include "mesh/mesh-defs.h"
+
+#include "tools/mesh/util.h"
+#include "tools/mesh/model.h"
+#include "tools/mesh/config-model.h"
+#include "tools/mesh/cfgcli.h"
+
+#define MIN_COMPOSITION_LEN 16
+
+static void *send_data;
+static model_send_msg_func_t send_msg;
+
+static void *key_data;
+static key_send_func_t send_key_msg;
+
+static uint16_t target = UNASSIGNED_ADDRESS;
+static uint32_t parms[8];
+
+static uint32_t print_mod_id(uint8_t *data, bool vid, const char *offset)
+{
+	uint32_t mod_id;
+
+	if (!vid) {
+		mod_id = get_le16(data);
+		bt_shell_printf("%sModel Id\t%4.4x\n", offset, mod_id);
+		mod_id = 0xffff0000 | mod_id;
+	} else {
+		mod_id = get_le16(data + 2);
+		bt_shell_printf("%sModel Id\t%4.4x %4.4x\n", offset,
+							get_le16(data), mod_id);
+		mod_id = get_le16(data) << 16 | mod_id;
+	}
+	return mod_id;
+}
+
+static void print_composition(uint8_t *data, uint16_t len)
+{
+	uint16_t features;
+	int i = 0;
+
+	bt_shell_printf("Received composion:\n");
+
+	/* skip page -- We only support Page Zero */
+	data++;
+	len--;
+
+	bt_shell_printf("\tCID: %4.4x", get_le16(&data[0]));
+	bt_shell_printf("\tPID: %4.4x", get_le16(&data[2]));
+	bt_shell_printf("\tVID: %4.4x", get_le16(&data[4]));
+	bt_shell_printf("\tCRPL: %4.4x", get_le16(&data[6]));
+
+	features = get_le16(&data[8]);
+	data += 10;
+	len -= 10;
+
+	bt_shell_printf("\tFeature support:\n");
+	bt_shell_printf("\t\trelay: %s\n", (features & FEATURE_RELAY) ?
+								"yes" : "no");
+	bt_shell_printf("\t\tproxy: %s\n", (features & FEATURE_PROXY) ?
+								"yes" : "no");
+	bt_shell_printf("\t\tfriend: %s\n", (features & FEATURE_FRIEND) ?
+								"yes" : "no");
+	bt_shell_printf("\t\tlpn: %s\n", (features & FEATURE_LPN) ?
+								"yes" : "no");
+
+	while (len) {
+		uint8_t m, v;
+
+		bt_shell_printf("\t Element %d:\n", i);
+		bt_shell_printf("\t\tlocation: %4.4x\n", get_le16(data));
+		data += 2;
+		len -= 2;
+
+		m = *data++;
+		v = *data++;
+		len -= 2;
+
+		if (m)
+			bt_shell_printf("\t\tSIG defined models:\n");
+
+		while (len >= 2 && m--) {
+			print_mod_id(data, false, "\t\t  ");
+			data += 2;
+			len -= 2;
+		}
+
+		if (v)
+			bt_shell_printf("\t\t Vendor defined models:\n");
+
+		while (len >= 4 && v--) {
+			print_mod_id(data, true, "\t\t  ");
+			data += 4;
+			len -= 4;
+		}
+
+		i++;
+	}
+}
+
+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);
+
+	if (mod_id > 0xffff0000)
+		bt_shell_printf("\tModel: %8.8x\n", mod_id);
+	else
+		bt_shell_printf("\tModel: %4.4x\n",
+				(uint16_t) (mod_id & 0xffff));
+
+	bt_shell_printf("\tApp Key Idx: %4.4x\n", pub->app_idx);
+	bt_shell_printf("\tTTL: %2.2x\n", pub->ttl);
+}
+
+static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
+							uint16_t len)
+{
+	uint32_t opcode;
+	uint16_t app_idx, net_idx, addr;
+	uint16_t ele_addr;
+	uint32_t mod_id;
+	struct model_pub pub;
+	int n;
+	uint16_t i;
+
+	if (mesh_opcode_get(data, len, &opcode, &n)) {
+		len -= n;
+		data += n;
+	} else
+		return false;
+
+	switch (opcode & ~OP_UNRELIABLE) {
+	default:
+		return false;
+
+	case OP_DEV_COMP_STATUS:
+		if (len < MIN_COMPOSITION_LEN)
+			break;
+
+		print_composition(data, len);
+
+		break;
+
+	case OP_APPKEY_STATUS:
+		if (len != 4)
+			break;
+
+		bt_shell_printf("Node %4.4x AppKey status %s\n", src,
+						mesh_status_str(data[0]));
+		net_idx = get_le16(data + 1) & 0xfff;
+		app_idx = get_le16(data + 2) >> 4;
+
+		bt_shell_printf("NetKey\t%3.3x\n", net_idx);
+		bt_shell_printf("AppKey\t%3.3x\n", app_idx);
+
+		break;
+
+	case OP_NETKEY_STATUS:
+		if (len != 3)
+			break;
+
+		bt_shell_printf("Node %4.4x NetKey status %s\n", src,
+						mesh_status_str(data[0]));
+		net_idx = get_le16(data + 1) & 0xfff;
+
+		bt_shell_printf("\tNetKey %3.3x\n", net_idx);
+
+		break;
+
+	case OP_MODEL_APP_STATUS:
+		if (len != 7 && len != 9)
+			break;
+
+		bt_shell_printf("Node %4.4x: Model App status %s\n", src,
+						mesh_status_str(data[0]));
+		addr = get_le16(data + 1);
+		app_idx = get_le16(data + 3);
+
+		bt_shell_printf("Element Addr\t%4.4x\n", addr);
+
+		print_mod_id(data + 5, len == 9, "");
+
+		bt_shell_printf("AppIdx\t\t%3.3x\n ", app_idx);
+
+		break;
+
+	case OP_NODE_IDENTITY_STATUS:
+		if (len != 4)
+			return true;
+
+		bt_shell_printf("NetIdx %4.4x, NodeIdState 0x%02x, status %s\n",
+				get_le16(data + 1), data[3],
+				mesh_status_str(data[0]));
+		break;
+
+	case OP_CONFIG_BEACON_STATUS:
+		if (len != 1)
+			return true;
+
+		bt_shell_printf("Node %4.4x: Config Beacon Status 0x%02x\n",
+				src, data[0]);
+		break;
+
+	case OP_CONFIG_RELAY_STATUS:
+		if (len != 2)
+			return true;
+
+		bt_shell_printf("Node %4.4x: Relay 0x%02x, cnt %d, steps %d\n",
+				src, data[0], data[1]>>5, data[1] & 0x1f);
+		break;
+
+	case OP_CONFIG_PROXY_STATUS:
+		if (len != 1)
+			return true;
+
+		bt_shell_printf("Node %4.4x Proxy state 0x%02x\n",
+				src, data[0]);
+		break;
+
+	case OP_CONFIG_DEFAULT_TTL_STATUS:
+		if (len != 1)
+			return true;
+
+		bt_shell_printf("Node %4.4x Default TTL %d\n", src, data[0]);
+
+		break;
+
+	case OP_CONFIG_MODEL_PUB_STATUS:
+		if (len != 12 && len != 14)
+			return true;
+
+		bt_shell_printf("\nNode %4.4x Publication status %s\n",
+				src, mesh_status_str(data[0]));
+
+		if (data[0] != MESH_STATUS_SUCCESS)
+			return true;
+
+		ele_addr = get_le16(data + 1);
+
+		mod_id = print_mod_id(data + 10, len == 14, "");
+
+		pub.u.addr16 = get_le16(data + 3);
+		pub.app_idx = get_le16(data + 5);
+		pub.ttl = data[7];
+		pub.period = data[8];
+		n = (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);
+			break;
+		case 2:
+			n *= 10;
+			/* fall through */
+		case 1:
+			bt_shell_printf("Period\t\t%d sec\n", n);
+			break;
+		case 3:
+			bt_shell_printf("Period\t\t%d min\n", n * 10);
+			break;
+		}
+
+		bt_shell_printf("Rexmit count\t%d\n", data[9] >> 5);
+		bt_shell_printf("Rexmit steps\t%d\n", data[9] & 0x1f);
+
+		break;
+
+	/* Per Mesh Profile 4.3.2.19 */
+	case OP_CONFIG_MODEL_SUB_STATUS:
+		bt_shell_printf("\nNode %4.4x Subscription status %s\n",
+				src, mesh_status_str(data[0]));
+
+		if (data[0] != MESH_STATUS_SUCCESS)
+			return true;
+
+		ele_addr = get_le16(data + 1);
+		addr = get_le16(data + 3);
+		bt_shell_printf("Element Addr\t%4.4x\n", ele_addr);
+
+		print_mod_id(data + 5, len == 9, "");
+
+		bt_shell_printf("Subscr Addr\t%4.4x\n", addr);
+
+		break;
+
+	/* Per Mesh Profile 4.3.2.27 */
+	case OP_CONFIG_MODEL_SUB_LIST:
+
+		bt_shell_printf("\nNode %4.4x Subscription List status %s\n",
+				src, mesh_status_str(data[0]));
+
+		if (data[0] != MESH_STATUS_SUCCESS)
+			return true;
+
+		bt_shell_printf("Element Addr\t%4.4x\n", get_le16(data + 1));
+		bt_shell_printf("Model ID\t%4.4x\n", get_le16(data + 3));
+
+		for (i = 5; i < len; i += 2)
+			bt_shell_printf("Subscr Addr\t%4.4x\n",
+							get_le16(data + i));
+		break;
+
+	/* Per Mesh Profile 4.3.2.50 */
+	case OP_MODEL_APP_LIST:
+		bt_shell_printf("\nNode %4.4x Model AppIdx status %s\n",
+						src, mesh_status_str(data[0]));
+
+		if (data[0] != MESH_STATUS_SUCCESS)
+			return true;
+
+		bt_shell_printf("Element Addr\t%4.4x\n", get_le16(data + 1));
+		bt_shell_printf("Model ID\t%4.4x\n", get_le16(data + 3));
+
+		for (i = 5; i < len; i += 2)
+			bt_shell_printf("Model AppIdx\t%4.4x\n",
+							get_le16(data + i));
+		break;
+
+	/* Per Mesh Profile 4.3.2.63 */
+	case OP_CONFIG_HEARTBEAT_PUB_STATUS:
+		bt_shell_printf("\nNode %4.4x Heartbeat publish status %s\n",
+				src, mesh_status_str(data[0]));
+
+		if (data[0] != MESH_STATUS_SUCCESS)
+			return true;
+
+		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]);
+		bt_shell_printf("TTL\t\t%2.2x\n", data[5]);
+		bt_shell_printf("Features\t%4.4x\n", get_le16(data + 6));
+		bt_shell_printf("Net_Idx\t%4.4x\n", get_le16(data + 8));
+		break;
+
+	/* Per Mesh Profile 4.3.2.66 */
+	case OP_CONFIG_HEARTBEAT_SUB_STATUS:
+		bt_shell_printf("\nNode %4.4x Heartbeat subscribe status %s\n",
+				src, mesh_status_str(data[0]));
+
+		if (data[0] != MESH_STATUS_SUCCESS)
+			return true;
+
+		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));
+		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]);
+		break;
+
+	/* Per Mesh Profile 4.3.2.54 */
+	case OP_NODE_RESET_STATUS:
+		bt_shell_printf("Node %4.4x reset status %s\n",
+				src, mesh_status_str(data[0]));
+
+		break;
+	}
+
+	return true;
+}
+
+static uint32_t read_input_parameters(int argc, char *argv[])
+{
+	uint32_t i;
+
+	if (!argc)
+		return 0;
+
+	--argc;
+	++argv;
+
+	if (!argc || argv[0][0] == '\0')
+		return 0;
+
+	for (i = 0; i < L_ARRAY_SIZE(parms) && i < (uint32_t) argc; i++) {
+		if (sscanf(argv[i], "%x", &parms[i]) != 1)
+			break;
+	}
+
+	return i;
+}
+
+static void cmd_dst_set(int argc, char *argv[])
+{
+	uint32_t dst;
+	char *end;
+
+	dst = strtol(argv[1], &end, 16);
+
+	if (end != (argv[1] + 4)) {
+		bt_shell_printf("Bad unicast address %s: "
+				"expected format 4 digit hex\n", argv[1]);
+		target = UNASSIGNED_ADDRESS;
+
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	bt_shell_printf("Configuring node %4.4x\n", dst);
+	target = dst;
+	set_menu_prompt("config", argv[1]);
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static bool config_send(uint8_t *buf, uint16_t len)
+{
+	if (IS_UNASSIGNED(target)) {
+		bt_shell_printf("Destination not set\n");
+		return false;
+	}
+
+	return send_msg(send_data, target, APP_IDX_DEV_REMOTE, buf, len);
+}
+
+static void cmd_default(uint32_t opcode)
+{
+	uint16_t n;
+	uint8_t msg[32];
+
+	n = mesh_opcode_set(opcode, msg);
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send command (opcode 0x%x)\n",
+								opcode);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_composition_get(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[32];
+
+	n = mesh_opcode_set(OP_DEV_COMP_GET, msg);
+
+	/* By default, use page 0 */
+	msg[n++] = (read_input_parameters(argc, argv) == 1) ? parms[0] : 0;
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"GET NODE COMPOSITION\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_netkey_del(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[32];
+
+	n = mesh_opcode_set(OP_NETKEY_DELETE, msg);
+
+	if (read_input_parameters(argc, argv) != 1) {
+		bt_shell_printf("Bad arguments %s\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	put_le16(target + parms[0], msg + n);
+	n += 2;
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"DEL_NET KEY\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_netkey_add(int argc, char *argv[])
+{
+	/*TODO*/
+	bt_shell_printf("Not implemented\n");
+}
+
+static void cmd_netkey_update(int argc, char *argv[])
+{
+	/*TODO*/
+	bt_shell_printf("Not implemented\n");
+}
+
+static void cmd_appkey_add(int argc, char *argv[])
+{
+	/*TODO*/
+	bt_shell_printf("Not implemented\n");
+}
+
+static void cmd_appkey_update(int argc, char *argv[])
+{
+	/*TODO*/
+	bt_shell_printf("Not implemented\n");
+}
+
+static void cmd_appkey_del(int argc, char *argv[])
+{
+	/*TODO*/
+	bt_shell_printf("Not implemented\n");
+}
+
+static void cmd_bind(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[32];
+	int parm_cnt;
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 3 && parm_cnt != 4) {
+		bt_shell_printf("Bad arguments\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	n = mesh_opcode_set(OP_MODEL_APP_BIND, msg);
+
+	put_le16(target + parms[0], msg + n);
+	n += 2;
+	put_le16(parms[1], msg + n);
+	n += 2;
+
+	if (parm_cnt == 4) {
+		put_le16(parms[3], msg + n);
+		put_le16(parms[2], msg + n + 2);
+		n += 4;
+	} else {
+		put_le16(parms[2], msg + n);
+		n += 2;
+	}
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"MODEL APP BIND\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_beacon_set(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[2 + 1];
+	int parm_cnt;
+
+	n = mesh_opcode_set(OP_CONFIG_BEACON_SET, msg);
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 1) {
+		bt_shell_printf("bad arguments\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	msg[n++] = parms[0];
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"SET BEACON\"\n");
+		return;
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_beacon_get(int argc, char *argv[])
+{
+	cmd_default(OP_CONFIG_BEACON_GET);
+}
+
+static void cmd_ident_set(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[2 + 3 + 4];
+	int parm_cnt;
+
+	n = mesh_opcode_set(OP_NODE_IDENTITY_SET, msg);
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 2) {
+		bt_shell_printf("bad arguments\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	put_le16(parms[0], msg + n);
+	n += 2;
+	msg[n++] = parms[1];
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"SET IDENTITY\"\n");
+		return;
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_ident_get(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[2 + 2 + 4];
+	int parm_cnt;
+
+	n = mesh_opcode_set(OP_NODE_IDENTITY_GET, msg);
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 1) {
+		bt_shell_printf("bad arguments\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	put_le16(parms[0], msg + n);
+	n += 2;
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"GET IDENTITY\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_proxy_set(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[2 + 1];
+	int parm_cnt;
+
+	n = mesh_opcode_set(OP_CONFIG_PROXY_SET, msg);
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 1) {
+		bt_shell_printf("bad arguments");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	msg[n++] = parms[0];
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"SET PROXY\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_proxy_get(int argc, char *argv[])
+{
+	cmd_default(OP_CONFIG_PROXY_GET);
+}
+
+static void cmd_relay_set(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[2 + 2 + 4];
+	int parm_cnt;
+
+	n = mesh_opcode_set(OP_CONFIG_RELAY_SET, msg);
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 3) {
+		bt_shell_printf("bad arguments\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	msg[n++] = parms[0];
+	msg[n++] = (parms[1] << 5) | parms[2];
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"SET RELAY\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_relay_get(int argc, char *argv[])
+{
+	cmd_default(OP_CONFIG_RELAY_GET);
+}
+
+static void cmd_ttl_set(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[32];
+	int parm_cnt;
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (!parm_cnt || parms[0] > TTL_MASK) {
+		bt_shell_printf("Bad TTL value\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	n = mesh_opcode_set(OP_CONFIG_DEFAULT_TTL_SET, msg);
+	msg[n++] = parms[0];
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"SET_DEFAULT TTL\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_pub_set(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[32];
+	int parm_cnt;
+
+	n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_SET, msg);
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 6 && parm_cnt != 7) {
+		bt_shell_printf("Bad arguments\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	put_le16(parms[0], msg + n);
+	n += 2;
+	/* Publish address */
+	put_le16(parms[1], msg + n);
+	n += 2;
+	/* AppKey index + credential (set to 0) */
+	put_le16(parms[2], msg + n);
+	n += 2;
+	/* TTL */
+	msg[n++] = DEFAULT_TTL;
+	/* Publish period  step count and step resolution */
+	msg[n++] = parms[3];
+	/* Publish retransmit count & interval steps */
+	msg[n++] = parms[4];
+
+	/* Model Id */
+	if (parm_cnt == 7) {
+		put_le16(parms[6], msg + n);
+		put_le16(parms[5], msg + n + 2);
+		n += 4;
+	} else {
+		put_le16(parms[5], msg + n);
+		n += 2;
+	}
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"SET MODEL PUBLICATION\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_pub_get(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[32];
+	int parm_cnt;
+
+	n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_GET, msg);
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 2 && parm_cnt != 3) {
+		bt_shell_printf("Bad arguments: %s\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	/* Element Address */
+	put_le16(parms[0], msg + n);
+	n += 2;
+
+	/* Model Id */
+	if (parm_cnt == 3) {
+		put_le16(parms[2], msg + n);
+		put_le16(parms[1], msg + n + 2);
+		n += 4;
+	} else {
+		put_le16(parms[1], msg + n);
+		n += 2;
+	}
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"GET MODEL PUBLICATION\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_sub_add(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[32];
+	int parm_cnt;
+
+	n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_ADD, msg);
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 3) {
+		bt_shell_printf("Bad arguments: %s\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	/* Per Mesh Profile 4.3.2.19 */
+	/* Element Address */
+	put_le16(parms[0], msg + n);
+	n += 2;
+	/* Subscription Address */
+	put_le16(parms[1], msg + n);
+	n += 2;
+	/* SIG Model ID */
+	put_le16(parms[2], msg + n);
+	n += 2;
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"ADD SUBSCRIPTION\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_sub_get(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[32];
+	int parm_cnt;
+
+	n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_GET, msg);
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 2) {
+		bt_shell_printf("Bad arguments: %s\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	/* Per Mesh Profile 4.3.2.27 */
+	/* Element Address */
+	put_le16(parms[0], msg + n);
+	n += 2;
+	/* Model ID */
+	put_le16(parms[1], msg + n);
+	n += 2;
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"GET SUB GET\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_mod_appidx_get(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[32];
+	int parm_cnt;
+
+	n = mesh_opcode_set(OP_MODEL_APP_GET, msg);
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 2) {
+		bt_shell_printf("Bad arguments: %s\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	/* Per Mesh Profile 4.3.2.49 */
+	/* Element Address */
+	put_le16(parms[0], msg + n);
+	n += 2;
+	/* Model ID */
+	put_le16(parms[1], msg + n);
+	n += 2;
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"GET APP GET\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_hb_pub_set(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[32];
+	int parm_cnt;
+
+	n = mesh_opcode_set(OP_CONFIG_HEARTBEAT_PUB_SET, msg);
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 6) {
+		bt_shell_printf("Bad arguments: %s\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	/* Per Mesh Profile 4.3.2.62 */
+	/* Publish address */
+	put_le16(parms[0], msg + n);
+	n += 2;
+	/* Count Log */
+	msg[n++] = parms[1];
+	/* Period Log */
+	msg[n++] = parms[2];
+	/* Heartbeat TTL */
+	msg[n++] = parms[3];
+	/* Features */
+	put_le16(parms[4], msg + n);
+	n += 2;
+	/* NetKey Index */
+	put_le16(parms[5], msg + n);
+	n += 2;
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"SET HEARTBEAT PUBLISH\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_hb_pub_get(int argc, char *argv[])
+{
+	cmd_default(OP_CONFIG_HEARTBEAT_PUB_GET);
+}
+
+static void cmd_hb_sub_set(int argc, char *argv[])
+{
+	uint16_t n;
+	uint8_t msg[32];
+	int parm_cnt;
+
+	n = mesh_opcode_set(OP_CONFIG_HEARTBEAT_SUB_SET, msg);
+
+	parm_cnt = read_input_parameters(argc, argv);
+	if (parm_cnt != 3) {
+		bt_shell_printf("Bad arguments: %s\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	/* Per Mesh Profile 4.3.2.65 */
+	/* Source address */
+	put_le16(parms[0], msg + n);
+	n += 2;
+	/* Destination address */
+	put_le16(parms[1], msg + n);
+	n += 2;
+	/* Period log */
+	msg[n++] = parms[2];
+
+	if (!config_send(msg, n)) {
+		bt_shell_printf("Failed to send \"SET HEARTBEAT SUBSCRIBE\"\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_hb_sub_get(int argc, char *argv[])
+{
+	cmd_default(OP_CONFIG_HEARTBEAT_SUB_GET);
+}
+
+static void cmd_ttl_get(int argc, char *argv[])
+{
+	cmd_default(OP_CONFIG_DEFAULT_TTL_GET);
+}
+
+static void cmd_node_reset(int argc, char *argv[])
+{
+	cmd_default(OP_NODE_RESET);
+}
+
+static bool tx_setup(model_send_msg_func_t send_func, void *user_data)
+{
+	if (!send_func)
+		return false;
+
+	send_msg = send_func;
+	send_data = user_data;
+
+	return true;
+}
+
+static const struct bt_shell_menu cfg_menu = {
+	.name = "config",
+	.desc = "Configuration Model Submenu",
+	.entries = {
+	{"target", "<unicast>", cmd_dst_set,
+				"Set target node to configure"},
+	{"composition-get", "[page_num]", cmd_composition_get,
+				"Get composition data"},
+	{"netkey-add", "<net_idx>", cmd_netkey_add,
+				"Add network key"},
+	{"netkey-update", "<net_idx>", cmd_netkey_update,
+				"Update network key"},
+	{"netkey-del", "<net_idx>", cmd_netkey_del,
+				"Delete network key"},
+	{"appkey-add", "<app_idx>", cmd_appkey_add,
+				"Add application key"},
+	{"appkey-update", "<app_idx>", cmd_appkey_update,
+				"Add application key"},
+	{"appkey-del", "<app_idx>", cmd_appkey_del,
+				"Delete application key"},
+	{"bind", "<ele_idx> <app_idx> <mod_id> [vendor_id]", cmd_bind,
+				"Bind app key to a model"},
+	{"mod-appidx-get", "<ele_addr> <model id>", cmd_mod_appidx_get,
+				"Get model app_idx"},
+	{"ttl-set", "<ttl>", cmd_ttl_set,
+				"Set default TTL"},
+	{"ttl-get", NULL, cmd_ttl_get,
+				"Get default TTL"},
+	{"pub-set", "<ele_addr> <pub_addr> <app_idx> <per (step|res)> "
+			"<re-xmt (cnt|per)> <mod id> [vendor_id]",
+				cmd_pub_set,
+				"Set publication"},
+	{"pub-get", "<ele_addr> <model>", cmd_pub_get,
+				"Get publication"},
+	{"proxy-set", "<proxy>", cmd_proxy_set,
+				"Set proxy state"},
+	{"proxy-get", NULL, cmd_proxy_get,
+				"Get proxy state"},
+	{"ident-set", "<net_idx> <state>", cmd_ident_set,
+				"Set node identity state"},
+	{"ident-get", "<net_idx>", cmd_ident_get,
+				"Get node identity state"},
+	{"beacon-set", "<state>", cmd_beacon_set,
+				"Set node identity state"},
+	{"beacon-get", NULL, cmd_beacon_get,
+				"Get node beacon state"},
+	{"relay-set", "<relay> <rexmt count> <rexmt steps>", cmd_relay_set,
+				"Set relay"},
+	{"relay-get", NULL, cmd_relay_get,
+				"Get relay"},
+	{"hb-pub-set", "<pub_addr> <count> <period> <ttl> <features> <net_idx>",
+				cmd_hb_pub_set,
+				"Set heartbeat publish"},
+	{"hb-pub-get", NULL, cmd_hb_pub_get,
+				"Get heartbeat publish"},
+	{"hb-sub-set", "<src_addr> <dst_addr> <period>", cmd_hb_sub_set,
+				"Set heartbeat subscribe"},
+	{"hb-sub-get", NULL, cmd_hb_sub_get,
+				"Get heartbeat subscribe"},
+	{"sub-add", "<ele_addr> <sub_addr> <model id>", cmd_sub_add,
+				"Add subscription"},
+	{"sub-get", "<ele_addr> <model id>", cmd_sub_get,
+				"Get subscription"},
+	{"node-reset", NULL, cmd_node_reset,
+				"Reset a node and remove it from network"},
+	{} },
+};
+
+static struct model_info cli_info = {
+	.ops = {
+		.set_send_func = tx_setup,
+		.set_pub_func = NULL,
+		.recv = msg_recvd,
+		.bind = NULL,
+		.pub = NULL
+	},
+	.mod_id = CONFIG_CLIENT_MODEL_ID,
+	.vendor_id = VENDOR_ID_INVALID
+};
+
+struct model_info *cfgcli_init(key_send_func_t key_send, void *user_data)
+{
+	if (!key_send)
+		return NULL;
+
+	send_key_msg = key_send;
+	key_data = user_data;
+
+	bt_shell_add_submenu(&cfg_menu);
+
+	return &cli_info;
+}
diff --git a/tools/mesh/cfgcli.h b/tools/mesh/cfgcli.h
new file mode 100644
index 000000000..077c340e5
--- /dev/null
+++ b/tools/mesh/cfgcli.h
@@ -0,0 +1,24 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2019  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *
+ */
+
+typedef bool (*key_send_func_t) (void *user_data, uint16_t dst,
+				 uint16_t idx, bool is_appkey, bool update);
+
+struct model_info *cfgcli_init(key_send_func_t key_func, void *user_data);
diff --git a/tools/mesh/config-model.h b/tools/mesh/config-model.h
index a5b811365..f08d3b890 100644
--- a/tools/mesh/config-model.h
+++ b/tools/mesh/config-model.h
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2017  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2017, 2019  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
@@ -97,5 +97,6 @@
 #define OP_VEND_MODEL_APP_LIST			0x804E
 
 bool config_server_init(void);
+
 bool config_client_init(void);
 void config_client_get_composition(uint32_t dst);
diff --git a/tools/mesh/keys.c b/tools/mesh/keys.c
new file mode 100644
index 000000000..3c887b319
--- /dev/null
+++ b/tools/mesh/keys.c
@@ -0,0 +1,166 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2019  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ell/ell.h>
+
+#include "src/shared/shell.h"
+#include "mesh/mesh-defs.h"
+#include "tools/mesh/keys.h"
+
+struct net_key {
+	uint16_t idx;
+	struct l_queue *app_keys;
+};
+
+static struct l_queue *net_keys;
+
+static bool simple_match(const void *a, const void *b)
+{
+	return a == b;
+}
+
+static bool net_idx_match(const void *a, const void *b)
+{
+	const struct net_key *key = a;
+	uint32_t idx = L_PTR_TO_UINT(b);
+
+	return key->idx == idx;
+}
+
+void keys_add_net_key(uint16_t net_idx)
+{
+	struct net_key *key;
+
+	if (!net_keys)
+		net_keys = l_queue_new();
+
+	if (l_queue_find(net_keys, net_idx_match, L_UINT_TO_PTR(net_idx)))
+		return;
+
+	key = l_new(struct net_key, 1);
+	key->idx = net_idx;
+
+	l_queue_push_tail(net_keys, key);
+}
+
+void keys_del_net_key(uint16_t idx)
+{
+	struct net_key *key;
+
+	if (!net_keys)
+		return;
+
+	key = l_queue_remove_if(net_keys, net_idx_match, L_UINT_TO_PTR(idx));
+	if (!key)
+		return;
+
+	l_queue_destroy(key->app_keys, NULL);
+	l_free(key);
+}
+
+void keys_add_app_key(uint16_t net_idx, uint16_t app_idx)
+{
+	struct net_key *key;
+
+	if (!net_keys)
+		return;
+
+	key = l_queue_find(net_keys, net_idx_match, L_UINT_TO_PTR(net_idx));
+	if (!key)
+		return;
+
+	if (!key->app_keys)
+		key->app_keys = l_queue_new();
+
+	if (l_queue_find(key->app_keys, simple_match, L_UINT_TO_PTR(app_idx)))
+		return;
+
+	l_queue_push_tail(key->app_keys, L_UINT_TO_PTR(app_idx));
+}
+
+void keys_del_app_key(uint16_t app_idx)
+{
+	const struct l_queue_entry *l;
+
+	if (!net_keys)
+		return;
+
+	for (l = l_queue_get_entries(net_keys); l; l = l->next) {
+		struct net_key *key = l->data;
+
+		if (!key->app_keys)
+			continue;
+
+		if (l_queue_remove_if(key->app_keys, simple_match,
+							L_UINT_TO_PTR(app_idx)))
+			return;
+	}
+}
+
+uint16_t keys_get_bound_key(uint16_t app_idx)
+{
+	const struct l_queue_entry *l;
+
+	if (!net_keys)
+		return NET_IDX_INVALID;
+
+	for (l = l_queue_get_entries(net_keys); l; l = l->next) {
+		struct net_key *key = l->data;
+
+		if (!key->app_keys)
+			continue;
+
+		if (l_queue_find(key->app_keys, simple_match,
+							L_UINT_TO_PTR(app_idx)))
+			return key->idx;
+	}
+
+	return NET_IDX_INVALID;
+}
+
+static void print_appkey(void *app_key, void *user_data)
+{
+	uint16_t app_idx = L_PTR_TO_UINT(app_key);
+
+	bt_shell_printf("%3.3x ", app_idx);
+}
+
+static void print_netkey(void *net_key, void *user_data)
+{
+	struct net_key *key = net_key;
+
+	bt_shell_printf(COLOR_YELLOW "NetKey: %3.3x\n" COLOR_OFF, key->idx);
+
+	if (!key->app_keys || l_queue_isempty(key->app_keys))
+		return;
+
+	bt_shell_printf("\t" COLOR_GREEN "app_keys = ");
+	l_queue_foreach(key->app_keys, print_appkey, NULL);
+	bt_shell_printf("\n" COLOR_OFF);
+}
+
+void keys_print_keys(void)
+{
+	l_queue_foreach(net_keys, print_netkey, NULL);
+}
diff --git a/tools/mesh/keys.h b/tools/mesh/keys.h
new file mode 100644
index 000000000..2a9faede6
--- /dev/null
+++ b/tools/mesh/keys.h
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2019  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *
+ */
+
+void keys_add_net_key(uint16_t net_idx);
+void keys_del_net_key(uint16_t net_idx);
+void keys_add_app_key(uint16_t net_idx, uint16_t app_idx);
+void keys_del_app_key(uint16_t app_idx);
+uint16_t keys_get_bound_key(uint16_t app_idx);
+void keys_print_keys(void);
diff --git a/tools/mesh/model.h b/tools/mesh/model.h
new file mode 100644
index 000000000..449fe19b2
--- /dev/null
+++ b/tools/mesh/model.h
@@ -0,0 +1,63 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2019  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *
+ */
+
+#define OP_UNRELIABLE	0x0100
+#define VENDOR_ID_INVALID	0xFFFF
+
+typedef bool (*model_send_msg_func_t) (void *user_data, uint16_t dst,
+				uint16_t app_idx, uint8_t *data, uint16_t len);
+typedef bool (*model_send_pub_func_t) (void *user_data, uint16_t vendor_id,
+				uint16_t mod_id, uint8_t *data, uint16_t len);
+typedef bool (*model_set_send_func_t)(model_send_msg_func_t func,
+							void *user_data);
+typedef bool (*model_set_pub_func_t)(model_send_pub_func_t func,
+							void *user_data);
+
+typedef bool (*model_recv_func_t)(uint16_t src, uint16_t app_idx,
+						uint8_t *data, uint16_t len);
+typedef int (*model_bind_func_t)(uint16_t app_idx, int action);
+
+struct model_pub {
+	uint16_t app_idx;
+	union {
+		uint16_t addr16;
+		uint8_t va_128[16];
+	} u;
+	uint8_t ttl;
+	uint8_t credential;
+	uint8_t period;
+	uint8_t retransmit;
+};
+
+typedef int (*model_pub_func_t)(struct model_pub *pub);
+
+struct model_ops {
+	model_set_send_func_t set_send_func;
+	model_set_pub_func_t set_pub_func;
+	model_recv_func_t recv;
+	model_bind_func_t bind;
+	model_pub_func_t pub;
+};
+
+struct model_info {
+	struct model_ops ops;
+	uint16_t mod_id;
+	uint16_t vendor_id;
+};
diff --git a/tools/mesh/util.c b/tools/mesh/util.c
new file mode 100644
index 000000000..7176cc562
--- /dev/null
+++ b/tools/mesh/util.c
@@ -0,0 +1,140 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017, 2019  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <ell/ell.h>
+
+#include "src/shared/shell.h"
+#include "src/shared/util.h"
+
+#include "mesh/mesh-defs.h"
+
+#include "tools/mesh/util.h"
+
+void set_menu_prompt(const char *name, const char *id)
+{
+	char *prompt;
+
+	prompt = l_strdup_printf(COLOR_BLUE "[%s%s%s]" COLOR_OFF "# ", name,
+					id ? ": Target = " : "", id ? id : "");
+	bt_shell_set_prompt(prompt);
+	l_free(prompt);
+}
+
+uint16_t mesh_opcode_set(uint32_t opcode, uint8_t *buf)
+{
+	if (opcode <= 0x7e) {
+		buf[0] = opcode;
+		return 1;
+	} else if (opcode >= 0x8000 && opcode <= 0xbfff) {
+		put_be16(opcode, buf);
+		return 2;
+	} else if (opcode >= 0xc00000 && opcode <= 0xffffff) {
+		buf[0] = (opcode >> 16) & 0xff;
+		put_be16(opcode, buf + 1);
+		return 3;
+	}
+
+	bt_shell_printf("Illegal Opcode %x", opcode);
+	return 0;
+}
+
+bool mesh_opcode_get(const uint8_t *buf, uint16_t sz, uint32_t *opcode, int *n)
+{
+	if (!n || !opcode || sz < 1)
+		return false;
+
+	switch (buf[0] & 0xc0) {
+	case 0x00:
+	case 0x40:
+		/* RFU */
+		if (buf[0] == 0x7f)
+			return false;
+
+		*n = 1;
+		*opcode = buf[0];
+		break;
+
+	case 0x80:
+		if (sz < 2)
+			return false;
+
+		*n = 2;
+		*opcode = get_be16(buf);
+		break;
+
+	case 0xc0:
+		if (sz < 3)
+			return false;
+
+		*n = 3;
+		*opcode = get_be16(buf + 1);
+		*opcode |= buf[0] << 16;
+		break;
+
+	default:
+		bt_shell_printf("Bad opcode");
+		return false;
+	}
+
+	return true;
+}
+
+const char *mesh_status_str(uint8_t status)
+{
+	switch (status) {
+	case MESH_STATUS_SUCCESS: return "Success";
+	case MESH_STATUS_INVALID_ADDRESS: return "Invalid Address";
+	case MESH_STATUS_INVALID_MODEL: return "Invalid Model";
+	case MESH_STATUS_INVALID_APPKEY: return "Invalid AppKey";
+	case MESH_STATUS_INVALID_NETKEY: return "Invalid NetKey";
+	case MESH_STATUS_INSUFF_RESOURCES: return "Insufficient Resources";
+	case MESH_STATUS_IDX_ALREADY_STORED: return "Key Idx Already Stored";
+	case MESH_STATUS_INVALID_PUB_PARAM: return "Invalid Publish Parameters";
+	case MESH_STATUS_NOT_SUB_MOD: return "Not a Subscribe Model";
+	case MESH_STATUS_STORAGE_FAIL: return "Storage Failure";
+	case MESH_STATUS_FEATURE_NO_SUPPORT: return "Feature Not Supported";
+	case MESH_STATUS_CANNOT_UPDATE: return "Cannot Update";
+	case MESH_STATUS_CANNOT_REMOVE: return "Cannot Remove";
+	case MESH_STATUS_CANNOT_BIND: return "Cannot bind";
+	case MESH_STATUS_UNABLE_CHANGE_STATE: return "Unable to change state";
+	case MESH_STATUS_CANNOT_SET: return "Cannot set";
+	case MESH_STATUS_UNSPECIFIED_ERROR: return "Unspecified error";
+	case MESH_STATUS_INVALID_BINDING: return "Invalid Binding";
+
+	default: return "Unknown";
+	}
+}
+
+void swap_u256_bytes(uint8_t *u256)
+{
+	int i;
+
+	/* End-to-End byte reflection of 32 octet buffer */
+	for (i = 0; i < 16; i++) {
+		u256[i] ^= u256[31 - i];
+		u256[31 - i] ^= u256[i];
+		u256[i] ^= u256[31 - i];
+	}
+}
diff --git a/tools/mesh/util.h b/tools/mesh/util.h
new file mode 100644
index 000000000..cca07cf96
--- /dev/null
+++ b/tools/mesh/util.h
@@ -0,0 +1,29 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017, 2019  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#include <stdbool.h>
+
+struct mesh_publication;
+
+void set_menu_prompt(const char *name, const char *id);
+void print_byte_array(const char *prefix, const void *ptr, int len);
+uint16_t mesh_opcode_set(uint32_t opcode, uint8_t *buf);
+bool mesh_opcode_get(const uint8_t *buf, uint16_t sz, uint32_t *opcode, int *n);
+const char *mesh_status_str(uint8_t status);
+void swap_u256_bytes(uint8_t *u256);
-- 
2.21.0


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

* [PATCH BlueZ 04/10] tools/mesh: move remote node processing to a separate file
  2019-10-29 20:40 [PATCH BlueZ 00/10] Mesh provisioning and configuration tool Inga Stotland
                   ` (2 preceding siblings ...)
  2019-10-29 20:40 ` [PATCH BlueZ 03/10] tools: Add mesh-cfgclient tool Inga Stotland
@ 2019-10-29 20:40 ` Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 05/10] tools/mesh-cfgclient: Commands for Management iface methods Inga Stotland
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Inga Stotland @ 2019-10-29 20:40 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This confines the functionality related to remote node
housekeeping into a separate file remote.c
---
 Makefile.tools         |   2 +
 tools/mesh-cfgclient.c |  76 +++----------------------
 tools/mesh/remote.c    | 124 +++++++++++++++++++++++++++++++++++++++++
 tools/mesh/remote.h    |  31 +++++++++++
 4 files changed, 165 insertions(+), 68 deletions(-)
 create mode 100644 tools/mesh/remote.c
 create mode 100644 tools/mesh/remote.h

diff --git a/Makefile.tools b/Makefile.tools
index 12687e495..0b4ddf576 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -332,7 +332,9 @@ tools_mesh_cfgclient_SOURCES = tools/mesh-cfgclient.c \
 				tools/mesh/cfgcli.h tools/mesh/cfgcli.c \
 				tools/mesh/keys.h tools/mesh/keys.c \
 				tools/mesh/util.h tools/mesh/util.c \
+				tools/mesh/remote.h tools/mesh/remote.c \
 				tools/mesh/agent.h tools/mesh/agent.c
+
 tools_mesh_cfgclient_LDADD = lib/libbluetooth-internal.la src/libshared-ell.la \
 						$(ell_ldadd) -lreadline
 endif
diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
index f225d021f..d21e55fac 100644
--- a/tools/mesh-cfgclient.c
+++ b/tools/mesh-cfgclient.c
@@ -39,6 +39,7 @@
 #include "tools/mesh/cfgcli.h"
 #include "tools/mesh/keys.h"
 #include "tools/mesh/model.h"
+#include "tools/mesh/remote.h"
 
 #define PROMPT_ON	COLOR_BLUE "[mesh-cfgclient]" COLOR_OFF "# "
 #define PROMPT_OFF	"Waiting to connect to bluetooth-meshd..."
@@ -84,13 +85,6 @@ struct unprov_device {
 	uint8_t uuid[16];
 };
 
-struct remote_node {
-	uint16_t unicast;
-	uint16_t net_idx;
-	uint8_t uuid[16];
-	uint8_t num_ele;
-};
-
 static struct l_dbus *dbus;
 
 static struct l_queue *node_proxies;
@@ -99,7 +93,6 @@ static struct meshcfg_node *local;
 static struct model_info *cfgcli;
 
 static struct l_queue *devices;
-static struct l_queue *nodes;
 
 static bool prov_in_progress;
 static const char *caps[2] = {"out-numeric", "in-numeric"};
@@ -192,37 +185,6 @@ static void print_device(void *a, void *b)
 	l_free(str);
 }
 
-static bool match_node_addr(const void *a, const void *b)
-{
-	const struct remote_node *rmt = a;
-	uint16_t addr = L_PTR_TO_UINT(b);
-
-	if (addr >= rmt->unicast &&
-				addr <= (rmt->unicast + rmt->num_ele - 1))
-		return true;
-
-	return false;
-}
-
-static uint16_t get_primary_subnet_idx(uint16_t addr)
-{
-	struct remote_node *rmt;
-
-	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
-
-	if (!rmt) {
-		bt_shell_printf("Net key not found: trying %4.4x\n",
-			PRIMARY_NET_IDX);
-		return prov_net_idx;
-		/*
-		 * TODO: When the remote node recovery from storage is
-		 * implemented, return NET_IDX_INVALID" here.
-		 */
-	}
-
-	return rmt->net_idx;
-}
-
 struct send_data {
 	const char *ele_path;
 	bool rmt;
@@ -278,7 +240,7 @@ static bool send_msg(void *user_data, uint16_t dst, uint16_t idx,
 	method_name = is_dev_key ? "DevKeySend" : "Send";
 
 	if (is_dev_key) {
-		net_idx_tx = get_primary_subnet_idx(dst);
+		net_idx_tx = remote_get_subnet_idx(dst);
 		if (net_idx_tx == NET_IDX_INVALID)
 			return false;
 	}
@@ -319,7 +281,7 @@ static bool send_key(void *user_data, uint16_t dst, uint16_t key_idx,
 	uint16_t net_idx;
 	const char *method_name = (!is_appkey) ? "AddNetKey" : "AddAppKey";
 
-	net_idx = get_primary_subnet_idx(dst);
+	net_idx = remote_get_subnet_idx(dst);
 	if (net_idx == NET_IDX_INVALID)
 		return false;
 
@@ -544,6 +506,7 @@ static void create_net_reply(struct l_dbus_proxy *proxy,
 	bt_shell_printf("Created new node with token %s\n", str);
 	l_free(str);
 
+	remote_add_node(app.uuid, 0x0001, 1, PRIMARY_NET_IDX);
 	keys_add_net_key(PRIMARY_NET_IDX);
 
 	l_dbus_proxy_method_call(net_proxy, "Attach", attach_node_setup,
@@ -641,26 +604,9 @@ static void cmd_list_unprov(int argc, char *argv[])
 	l_queue_foreach(devices, print_device, NULL);
 }
 
-static void print_node(void *a, void *b)
-{
-	struct remote_node *node = a;
-	char *str;
-
-	bt_shell_printf(COLOR_YELLOW "Mesh node:\n" COLOR_OFF);
-	str = l_util_hexstring_upper(node->uuid, 16);
-	bt_shell_printf("\t" COLOR_GREEN "UUID = %s\n" COLOR_OFF, str);
-	l_free(str);
-	bt_shell_printf("\t" COLOR_GREEN "primary = %4.4x\n" COLOR_OFF,
-								node->unicast);
-	bt_shell_printf("\t" COLOR_GREEN "elements = %u\n" COLOR_OFF,
-								node->num_ele);
-	bt_shell_printf("\t" COLOR_GREEN "net_key_idx = %3.3x\n" COLOR_OFF,
-								node->net_idx);
-}
-
 static void cmd_list_nodes(int argc, char *argv[])
 {
-	l_queue_foreach(nodes, print_node, NULL);
+	remote_print_all();
 }
 
 static void add_node_reply(struct l_dbus_proxy *proxy,
@@ -972,7 +918,6 @@ static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus,
 	uint8_t cnt;
 	uint32_t n;
 	uint8_t *uuid;
-	struct remote_node *node;
 
 	if (!prov_in_progress)
 		return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
@@ -991,15 +936,11 @@ static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus,
 		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
 	}
 
-	node = l_new(struct remote_node, 1);
-	memcpy(node->uuid, uuid, 16);
-	node->unicast = unicast;
-	node->num_ele = cnt;
-	node->net_idx = prov_net_idx;
+	remote_add_node(uuid, unicast, cnt, prov_net_idx);
 
 	bt_shell_printf("Provisioning done:\n");
-	print_node(node, NULL);
-	l_queue_push_tail(nodes, node);
+	remote_print_node(unicast);
+
 	remove_device(uuid);
 
 	prov_address = unicast + cnt;
@@ -1227,7 +1168,6 @@ int main(int argc, char *argv[])
 
 	node_proxies = l_queue_new();
 	devices = l_queue_new();
-	nodes = l_queue_new();
 
 	status = bt_shell_run();
 
diff --git a/tools/mesh/remote.c b/tools/mesh/remote.c
new file mode 100644
index 000000000..46839772f
--- /dev/null
+++ b/tools/mesh/remote.c
@@ -0,0 +1,124 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2019  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ell/ell.h>
+
+#include "src/shared/shell.h"
+#include "src/shared/util.h"
+
+#include "mesh/mesh-defs.h"
+#include "tools/mesh/remote.h"
+
+static struct l_queue *nodes;
+
+void remote_add_node(const uint8_t uuid[16], uint16_t unicast,
+					uint8_t ele_cnt, uint16_t net_idx)
+{
+	struct remote_node *rmt = l_new(struct remote_node, 1);
+
+	memcpy(rmt->uuid, uuid, 16);
+	rmt->unicast = 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));
+
+	if (!nodes)
+		nodes = l_queue_new();
+
+	l_queue_push_tail(nodes, rmt);
+}
+
+static bool match_node_addr(const void *a, const void *b)
+{
+	const struct remote_node *rmt = a;
+	uint16_t addr = L_PTR_TO_UINT(b);
+
+	if (addr >= rmt->unicast &&
+				addr <= (rmt->unicast + rmt->num_ele - 1))
+		return true;
+
+	return false;
+}
+
+uint16_t remote_get_subnet_idx(uint16_t addr)
+{
+	struct remote_node *rmt;
+	uint32_t net_idx;
+
+	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));
+
+	return (uint16_t) net_idx;
+}
+
+static void print_subnet(void *net_key, void *user_data)
+{
+	uint16_t net_idx = L_PTR_TO_UINT(net_key);
+
+	bt_shell_printf("%3.3x ", net_idx);
+}
+
+static void print_node(void *rmt, void *user_data)
+{
+	struct remote_node *node = rmt;
+	char *str;
+
+	bt_shell_printf(COLOR_YELLOW "Mesh node:\n" COLOR_OFF);
+	str = l_util_hexstring_upper(node->uuid, 16);
+	bt_shell_printf("\t" COLOR_GREEN "UUID = %s\n" COLOR_OFF, str);
+	l_free(str);
+	bt_shell_printf("\t" COLOR_GREEN "primary = %4.4x\n" COLOR_OFF,
+								node->unicast);
+	bt_shell_printf("\t" COLOR_GREEN "elements = %u\n" COLOR_OFF,
+								node->num_ele);
+	bt_shell_printf("\t" COLOR_GREEN "net_keys = ");
+	l_queue_foreach(node->net_keys, print_subnet, NULL);
+	bt_shell_printf("\n" COLOR_OFF);
+}
+
+void remote_print_node(uint16_t addr)
+{
+	struct remote_node *rmt;
+
+	if (!nodes)
+		return;
+
+	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
+	if (!rmt)
+		return;
+
+	print_node(rmt, NULL);
+}
+
+void remote_print_all(void)
+{
+	if (!nodes)
+		return;
+
+	l_queue_foreach(nodes, print_node, NULL);
+}
diff --git a/tools/mesh/remote.h b/tools/mesh/remote.h
new file mode 100644
index 000000000..dce1619c0
--- /dev/null
+++ b/tools/mesh/remote.h
@@ -0,0 +1,31 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2019  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+struct remote_node {
+	uint16_t unicast;
+	struct l_queue *net_keys;
+	uint8_t uuid[16];
+	uint8_t num_ele;
+};
+
+void remote_add_node(const uint8_t uuid[16], uint16_t unicast,
+					uint8_t ele_cnt, 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.21.0


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

* [PATCH BlueZ 05/10] tools/mesh-cfgclient: Commands for Management iface methods
  2019-10-29 20:40 [PATCH BlueZ 00/10] Mesh provisioning and configuration tool Inga Stotland
                   ` (3 preceding siblings ...)
  2019-10-29 20:40 ` [PATCH BlueZ 04/10] tools/mesh: move remote node processing to a separate file Inga Stotland
@ 2019-10-29 20:40 ` Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 06/10] tools/mesh-cfgclient: Add config menu key commands Inga Stotland
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Inga Stotland @ 2019-10-29 20:40 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This adds complete list of commenads that excercise calls to
org.bluez.mesh.Manager1 methods:
"appkey-create": CreateAppKey()
"appkey-import": ImportAppKey()
"appkey-update": UpdateAppKey()
"appkey-delete": DeleteAppKey()
"subnet-create": CreateSubnet()
"subnet-import": SubnetImport()
"subnet-update": SubnetUpdate()
"subnet-delete": SubnetDelete()
"subnet-set-phase": SetKeyPhase()
"node-import": ImportRemoteNode()
"node-delete": DeleteRemoteNode()
---
 tools/mesh-cfgclient.c | 551 ++++++++++++++++++++++++++++++++++++++---
 1 file changed, 520 insertions(+), 31 deletions(-)

diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
index d21e55fac..5d74be9dd 100644
--- a/tools/mesh-cfgclient.c
+++ b/tools/mesh-cfgclient.c
@@ -85,6 +85,15 @@ struct unprov_device {
 	uint8_t uuid[16];
 };
 
+struct generic_request {
+	uint32_t arg1;
+	uint32_t arg2;
+	uint32_t arg3;
+	uint8_t *data1;
+	uint8_t *data2;
+	const char *str;
+};
+
 static struct l_dbus *dbus;
 
 static struct l_queue *node_proxies;
@@ -203,6 +212,19 @@ struct key_data {
 	bool update;
 };
 
+static void append_byte_array(struct l_dbus_message_builder *builder,
+					unsigned char *data, unsigned int len)
+{
+	unsigned int i;
+
+	l_dbus_message_builder_enter_array(builder, "y");
+
+	for (i = 0; i < len; i++)
+		l_dbus_message_builder_append_basic(builder, 'y', &(data[i]));
+
+	l_dbus_message_builder_leave_array(builder);
+}
+
 static void send_msg_setup(struct l_dbus_message *msg, void *user_data)
 {
 	struct send_data *req = user_data;
@@ -215,15 +237,7 @@ static void send_msg_setup(struct l_dbus_message *msg, void *user_data)
 	if (req->is_dev_key)
 		l_dbus_message_builder_append_basic(builder, 'b', &req->rmt);
 	l_dbus_message_builder_append_basic(builder, 'q', &req->idx);
-	l_dbus_message_builder_enter_array(builder, "y");
-
-	while (req->len) {
-		l_dbus_message_builder_append_basic(builder, 'y', req->data);
-		req->data++;
-		req->len--;
-	}
-
-	l_dbus_message_builder_leave_array(builder);
+	append_byte_array(builder, req->data, req->len);
 	l_dbus_message_builder_finalize(builder);
 	l_dbus_message_builder_destroy(builder);
 }
@@ -434,8 +448,7 @@ static void try_set_node_proxy(void *a, void *b)
 }
 
 static void attach_node_reply(struct l_dbus_proxy *proxy,
-						struct l_dbus_message *msg,
-						void *user_data)
+				struct l_dbus_message *msg, void *user_data)
 {
 	struct meshcfg_node *node = user_data;
 	struct l_dbus_message_iter iter_cfg;
@@ -482,8 +495,7 @@ static void attach_node_setup(struct l_dbus_message *msg, void *user_data)
 }
 
 static void create_net_reply(struct l_dbus_proxy *proxy,
-						struct l_dbus_message *msg,
-						void *user_data)
+				struct l_dbus_message *msg, void *user_data)
 {
 	char *str;
 	uint64_t tmp;
@@ -516,7 +528,6 @@ static void create_net_reply(struct l_dbus_proxy *proxy,
 
 static void create_net_setup(struct l_dbus_message *msg, void *user_data)
 {
-	uint i;
 	struct l_dbus_message_builder *builder;
 
 	/* Generate random UUID */
@@ -525,14 +536,7 @@ static void create_net_setup(struct l_dbus_message *msg, void *user_data)
 	builder = l_dbus_message_builder_new(msg);
 
 	l_dbus_message_builder_append_basic(builder, 'o', app.path);
-	l_dbus_message_builder_enter_array(builder, "y");
-
-	for (i = 0; i < L_ARRAY_SIZE(app.uuid); i++)
-		l_dbus_message_builder_append_basic(builder, 'y',
-								&(app.uuid[i]));
-
-	l_dbus_message_builder_leave_array(builder);
-
+	append_byte_array(builder, app.uuid, 16);
 	l_dbus_message_builder_finalize(builder);
 	l_dbus_message_builder_destroy(builder);
 }
@@ -609,6 +613,476 @@ static void cmd_list_nodes(int argc, char *argv[])
 	remote_print_all();
 }
 
+static void free_generic_request(void *data)
+{
+	struct generic_request *req = data;
+
+	l_free(req->data1);
+	l_free(req->data2);
+	l_free(req);
+}
+
+static void delete_node_setup(struct l_dbus_message *msg, void *user_data)
+{
+	struct generic_request *req = user_data;
+	uint16_t primary;
+	uint8_t ele_cnt;
+
+	primary = (uint16_t) req->arg1;
+	ele_cnt = (uint8_t) req->arg2;
+
+	l_dbus_message_set_arguments(msg, "qy", primary, ele_cnt);
+}
+
+static void cmd_delete_node(int argc, char *argv[])
+{
+	struct generic_request *req;
+
+	if (!node || !node->proxy || !node->mgmt_proxy) {
+		bt_shell_printf("Node is not attached\n");
+		return;
+	}
+
+	if (argc < 3) {
+		bt_shell_printf("Unicast and element count are required\n");
+		return;
+	}
+
+	req = l_new(struct generic_request, 1);
+
+	if (sscanf(argv[1], "%04x", &req->arg1) != 1)
+		goto fail;
+
+	if (sscanf(argv[2], "%u", &req->arg2) != 1)
+		goto fail;
+
+	l_dbus_proxy_method_call(node->mgmt_proxy, "DeleteRemoteNode",
+				delete_node_setup, NULL, req, l_free);
+
+	/* TODO:: Delete node from configuration */
+	return;
+
+fail:
+	l_free(req);
+}
+
+static void import_node_reply(struct l_dbus_proxy *proxy,
+				struct l_dbus_message *msg, void *user_data)
+{
+	struct generic_request *req = user_data;
+	uint16_t primary, net_idx;
+	uint8_t ele_cnt;
+
+	if (l_dbus_message_is_error(msg)) {
+		const char *name;
+
+		l_dbus_message_get_error(msg, &name, NULL);
+		l_error("Failed to import remote node: %s", name);
+		return;
+	}
+
+	net_idx = (uint16_t) req->arg1;
+	primary = (uint16_t) req->arg2;
+	ele_cnt = (uint8_t) req->arg3;
+
+	remote_add_node(req->data1, primary, ele_cnt, net_idx);
+}
+
+static void import_node_setup(struct l_dbus_message *msg, void *user_data)
+{
+	struct generic_request *req = user_data;
+	uint16_t primary;
+	uint8_t ele_cnt;
+	struct l_dbus_message_builder *builder;
+
+	primary = (uint16_t) req->arg2;
+	ele_cnt = (uint8_t) req->arg3;
+
+	builder = l_dbus_message_builder_new(msg);
+	l_dbus_message_builder_append_basic(builder, 'q', &primary);
+	l_dbus_message_builder_append_basic(builder, 'y', &ele_cnt);
+	append_byte_array(builder, req->data2, 16);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+}
+
+static void cmd_import_node(int argc, char *argv[])
+{
+	struct generic_request *req;
+	size_t sz;
+
+	if (!node || !node->proxy || !node->mgmt_proxy) {
+		bt_shell_printf("Node is not attached\n");
+		return;
+	}
+
+	if (argc < 6) {
+		bt_shell_printf("UUID, element count and device key");
+		bt_shell_printf("Unicast, element count and device key");
+		bt_shell_printf("are required\n");
+		return;
+	}
+
+	req = l_new(struct generic_request, 1);
+
+	/* Device UUID */
+	req->data1 = l_util_from_hexstring(argv[1], &sz);
+	if (!req->data1 || sz != 16) {
+		l_error("Failed to generate UUID array from %s", argv[1]);
+		goto fail;
+	}
+
+	/* NetKey Index*/
+	if (sscanf(argv[2], "%04x", &req->arg1) != 1)
+		goto fail;
+
+	/* Unicast of the primary element */
+	if (sscanf(argv[3], "%04x", &req->arg2) != 1)
+		goto fail;
+
+	/* Number of elements */
+	if (sscanf(argv[4], "%u", &req->arg3) != 1)
+		return;
+
+	/* DevKey */
+	req->data2 = l_util_from_hexstring(argv[5], &sz);
+	if (!req->data2 || sz != 16) {
+		l_error("Failed to generate DevKey array from %s", argv[5]);
+		goto fail;
+	}
+
+	l_dbus_proxy_method_call(node->mgmt_proxy, "ImportRemoteNode",
+					import_node_setup, import_node_reply,
+					req, free_generic_request);
+
+	return;
+
+fail:
+	free_generic_request(req);
+}
+
+static void subnet_set_phase_reply(struct l_dbus_proxy *proxy,
+				struct l_dbus_message *msg, void *user_data)
+{
+	if (l_dbus_message_is_error(msg)) {
+		const char *name;
+
+		l_dbus_message_get_error(msg, &name, NULL);
+		l_error("Failed to set subnet phase: %s", name);
+	}
+
+	/* TODO: Set key phase in configuration */
+}
+
+static void subnet_set_phase_setup(struct l_dbus_message *msg, void *user_data)
+{
+	struct generic_request *req = user_data;
+	uint16_t net_idx;
+	uint8_t phase;
+
+	net_idx = (uint16_t) req->arg1;
+	phase = (uint8_t) req->arg2;
+
+	l_dbus_message_set_arguments(msg, "qy", net_idx, phase);
+}
+
+static void cmd_subnet_set_phase(int argc, char *argv[])
+{
+	struct generic_request *req;
+
+	if (!node || !node->proxy || !node->mgmt_proxy) {
+		bt_shell_printf("Node is not attached\n");
+		return;
+	}
+
+	if (argc < 3) {
+		bt_shell_printf("NetKey index and phase are required\n");
+		return;
+	}
+
+	req = l_new(struct generic_request, 1);
+
+	if (sscanf(argv[1], "%04x", &req->arg1) != 1)
+		goto fail;
+
+	if (sscanf(argv[2], "%d", &req->arg2) != 1)
+		goto fail;
+
+	l_dbus_proxy_method_call(node->mgmt_proxy, "SetKeyPhase",
+					subnet_set_phase_setup,
+					subnet_set_phase_reply, req, l_free);
+
+	return;
+
+fail:
+	l_free(req);
+}
+
+static void mgr_key_reply(struct l_dbus_proxy *proxy,
+				struct l_dbus_message *msg, void *user_data)
+{
+	struct generic_request *req = user_data;
+	uint16_t idx = (uint16_t) req->arg1;
+	const char *method = req->str;
+
+	if (l_dbus_message_is_error(msg)) {
+		const char *name;
+
+		l_dbus_message_get_error(msg, &name, NULL);
+		l_error("Method %s returned error: %s", method, name);
+		return;
+	}
+
+	if (!strcmp("CreateSubnet", method))
+		keys_add_net_key(idx);
+	else if (!strcmp("DeleteSubnet", method))
+		keys_del_net_key(idx);
+	else if (!strcmp("DeleteAppKey", method))
+		keys_del_app_key(idx);
+}
+
+static void mgr_key_setup(struct l_dbus_message *msg, void *user_data)
+{
+	struct generic_request *req = user_data;
+	uint16_t idx = (uint16_t) req->arg1;
+
+	l_dbus_message_set_arguments(msg, "q", idx);
+}
+
+static void mgr_key_cmd(int argc, char *argv[], const char *method_name)
+{
+	struct generic_request *req;
+
+	if (!node || !node->proxy || !node->mgmt_proxy) {
+		bt_shell_printf("Node is not attached\n");
+		return;
+	}
+
+	if (argc < 2) {
+		bt_shell_printf("Missing required arguments\n");
+		return;
+	}
+
+	req = l_new(struct generic_request, 1);
+
+	if (sscanf(argv[1], "%04x", &req->arg1) != 1) {
+		l_free(req);
+		return;
+	}
+
+	req->str = method_name;
+
+	l_dbus_proxy_method_call(node->mgmt_proxy, method_name,
+					mgr_key_setup, mgr_key_reply,
+					req, l_free);
+}
+
+static void cmd_delete_appkey(int argc, char *argv[])
+{
+	mgr_key_cmd(argc, argv, "DeleteAppKey");
+}
+
+static void cmd_update_appkey(int argc, char *argv[])
+{
+	mgr_key_cmd(argc, argv, "UpdateAppKey");
+}
+
+static void cmd_delete_subnet(int argc, char *argv[])
+{
+	mgr_key_cmd(argc, argv, "DeleteSubnet");
+}
+
+static void cmd_update_subnet(int argc, char *argv[])
+{
+	mgr_key_cmd(argc, argv, "UpdateSubnet");
+}
+
+static void cmd_create_subnet(int argc, char *argv[])
+{
+	mgr_key_cmd(argc, argv, "CreateSubnet");
+}
+
+static void add_key_reply(struct l_dbus_proxy *proxy,
+				struct l_dbus_message *msg, void *user_data)
+{
+	struct generic_request *req = user_data;
+	uint16_t net_idx, app_idx;
+	const char *method = req->str;
+
+	if (l_dbus_message_is_error(msg)) {
+		const char *name;
+
+		l_dbus_message_get_error(msg, &name, NULL);
+		l_error("%s failed: %s", method, name);
+		return;
+	}
+
+	net_idx = (uint16_t) req->arg1;
+
+	if (!strcmp(method, "ImportSubnet")) {
+		keys_add_net_key(net_idx);
+		return;
+	}
+
+	app_idx = (uint16_t) req->arg2;
+	keys_add_app_key(net_idx, app_idx);
+}
+
+static void import_appkey_setup(struct l_dbus_message *msg, void *user_data)
+{
+	struct generic_request *req = user_data;
+	uint16_t net_idx, app_idx;
+	struct l_dbus_message_builder *builder;
+
+	net_idx = (uint16_t) req->arg1;
+	app_idx = (uint16_t) req->arg2;
+
+	builder = l_dbus_message_builder_new(msg);
+	l_dbus_message_builder_append_basic(builder, 'q', &net_idx);
+	l_dbus_message_builder_append_basic(builder, 'q', &app_idx);
+	append_byte_array(builder, req->data1, 16);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+}
+
+static void cmd_import_appkey(int argc, char *argv[])
+{
+	struct generic_request *req;
+	size_t sz;
+
+	if (!node || !node->proxy || !node->mgmt_proxy) {
+		bt_shell_printf("Node is not attached\n");
+		return;
+	}
+
+	if (argc < 4) {
+		bt_shell_printf("Netkey and AppKey indices and");
+		bt_shell_printf("key value are required\n");
+		return;
+	}
+
+	req = l_new(struct generic_request, 1);
+
+	if (sscanf(argv[1], "%04x", &req->arg1) != 1)
+		goto fail;
+
+	if (sscanf(argv[2], "%04x", &req->arg2) != 1)
+		goto fail;
+
+	req->data1 = l_util_from_hexstring(argv[3], &sz);
+	if (!req->data1 || sz != 16) {
+		l_error("Failed to generate key array from %s", argv[3]);
+		goto fail;
+	}
+
+	req->str = "ImportAppKey";
+
+	l_dbus_proxy_method_call(node->mgmt_proxy, "ImportAppKey",
+					import_appkey_setup, add_key_reply,
+					req, free_generic_request);
+
+	return;
+
+fail:
+	free_generic_request(req);
+}
+
+static void import_subnet_setup(struct l_dbus_message *msg, void *user_data)
+{
+	struct generic_request *req = user_data;
+	uint16_t net_idx;
+	struct l_dbus_message_builder *builder;
+
+	net_idx = (uint16_t) req->arg1;
+
+	builder = l_dbus_message_builder_new(msg);
+	l_dbus_message_builder_append_basic(builder, 'q', &net_idx);
+	append_byte_array(builder, req->data1, 16);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+}
+
+static void cmd_import_subnet(int argc, char *argv[])
+{
+	struct generic_request *req;
+	size_t sz;
+
+	if (!node || !node->proxy || !node->mgmt_proxy) {
+		bt_shell_printf("Node is not attached\n");
+		return;
+	}
+
+	if (argc < 3) {
+		bt_shell_printf("NetKey index and value are required\n");
+		return;
+	}
+
+	req = l_new(struct generic_request, 1);
+
+	if (sscanf(argv[1], "%04x", &req->arg1) != 1)
+		goto fail;
+
+	req->data1 = l_util_from_hexstring(argv[2], &sz);
+	if (!req->data1 || sz != 16) {
+		l_error("Failed to generate key array from %s", argv[2]);
+		goto fail;
+	}
+
+	req->str = "ImportSubnet";
+
+	l_dbus_proxy_method_call(node->mgmt_proxy, "ImportSubnet",
+					import_subnet_setup, add_key_reply,
+					req, free_generic_request);
+	return;
+
+fail:
+	free_generic_request(req);
+}
+
+static void create_appkey_setup(struct l_dbus_message *msg, void *user_data)
+{
+	struct generic_request *req = user_data;
+	uint16_t net_idx, app_idx;
+
+	net_idx = (uint16_t) req->arg1;
+	app_idx = (uint16_t) req->arg2;
+
+	l_dbus_message_set_arguments(msg, "qq", net_idx, app_idx);
+}
+
+static void cmd_create_appkey(int argc, char *argv[])
+{
+	struct generic_request *req;
+
+	if (!node || !node->proxy || !node->mgmt_proxy) {
+		bt_shell_printf("Node is not attached\n");
+		return;
+	}
+
+	if (argc < 3) {
+		bt_shell_printf("AppKey index is required\n");
+		return;
+	}
+
+	req = l_new(struct generic_request, 1);
+
+	if (sscanf(argv[1], "%04x", &req->arg1) != 1)
+		goto fail;
+
+	if (sscanf(argv[2], "%04x", &req->arg2) != 1)
+		goto fail;
+
+	req->str = "CreateAppKey";
+
+	l_dbus_proxy_method_call(node->mgmt_proxy, "CreateAppKey",
+					create_appkey_setup, add_key_reply,
+					req, l_free);
+	return;
+
+fail:
+	l_free(req);
+}
+
 static void add_node_reply(struct l_dbus_proxy *proxy,
 				struct l_dbus_message *msg, void *user_data)
 {
@@ -626,7 +1100,6 @@ static void add_node_reply(struct l_dbus_proxy *proxy,
 
 static void add_node_setup(struct l_dbus_message *msg, void *user_data)
 {
-	int i;
 	char *str = user_data;
 	size_t sz;
 	unsigned char *uuid;
@@ -639,14 +1112,7 @@ static void add_node_setup(struct l_dbus_message *msg, void *user_data)
 	}
 
 	builder = l_dbus_message_builder_new(msg);
-
-	l_dbus_message_builder_enter_array(builder, "y");
-
-	for (i = 0; i < (int)sz; i++)
-		l_dbus_message_builder_append_basic(builder, 'y', &(uuid[i]));
-
-	l_dbus_message_builder_leave_array(builder);
-
+	append_byte_array(builder, uuid, 16);
 	l_dbus_message_builder_finalize(builder);
 	l_dbus_message_builder_destroy(builder);
 
@@ -683,10 +1149,33 @@ static const struct bt_shell_menu main_menu = {
 			"Create new mesh network with one initial node" },
 	{ "discover-unprovisioned", "<on/off> [seconds]", cmd_scan_unprov,
 			"Look for devices to provision" },
+	{ "appkey-create", "<net_idx> <app_idx>", cmd_create_appkey,
+			"Create a new local AppKey" },
+	{ "appkey-import", "<net_idx> <app_idx> <key>", cmd_import_appkey,
+			"Import a new local AppKey" },
+	{ "appkey-update", "<app_idx>", cmd_update_appkey,
+			"Update local AppKey" },
+	{ "appkey-delete", "<app_idx>", cmd_delete_appkey,
+			"Delete local AppKey" },
+	{ "subnet-create", "<net_idx>", cmd_create_subnet,
+			"Create a new local subnet (NetKey)" },
+	{ "subnet-import", "<net_idx> <key>", cmd_import_subnet,
+			"Import a new local subnet (NetKey)" },
+	{ "subnet-update", "<net_idx>", cmd_update_subnet,
+			"Update local subnet (NetKey)" },
+	{ "subnet-delete", "<net_idx>", cmd_delete_subnet,
+			"Delete local subnet (NetKey)" },
+	{ "subnet-set-phase", "<net_idx> <phase>", cmd_subnet_set_phase,
+			"Set subnet (NetKey) phase" },
 	{ "list-unprovisioned", NULL, cmd_list_unprov,
 			"List unprovisioned devices" },
 	{ "provision", "<uuid>", cmd_start_prov,
 			"Initiate provisioning"},
+	{ "node-import", "<uuid> <net_idx> <primary> <ele_count> <dev_key>",
+			cmd_import_node,
+			"Import an externally provisioned remote node"},
+	{ "node-delete", "<primary> <ele_count>", cmd_delete_node,
+			"Delete a remote node"},
 	{ "list-nodes", NULL, cmd_list_nodes,
 			"List remote mesh nodes"},
 	{ } },
-- 
2.21.0


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

* [PATCH BlueZ 06/10] tools/mesh-cfgclient: Add config menu key commands
  2019-10-29 20:40 [PATCH BlueZ 00/10] Mesh provisioning and configuration tool Inga Stotland
                   ` (4 preceding siblings ...)
  2019-10-29 20:40 ` [PATCH BlueZ 05/10] tools/mesh-cfgclient: Commands for Management iface methods Inga Stotland
@ 2019-10-29 20:40 ` Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 07/10] tools/mesh-cfgclient: Add timeout for expected response Inga Stotland
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Inga Stotland @ 2019-10-29 20:40 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This adds implementation for config client commands to add/update
NetKeys and AppKeys on a remote node.
---
 tools/mesh/cfgcli.c | 267 ++++++++++++++++++++++++++++++++------------
 1 file changed, 196 insertions(+), 71 deletions(-)

diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index 9e83f7b69..ae8644049 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -38,6 +38,13 @@
 #include "tools/mesh/cfgcli.h"
 
 #define MIN_COMPOSITION_LEN 16
+#define NO_RESPONSE 0xFFFFFFFF
+
+struct cfg_cmd {
+	uint32_t opcode;
+	uint32_t resp;
+	const char *desc;
+};
 
 static void *send_data;
 static model_send_msg_func_t send_msg;
@@ -48,6 +55,113 @@ static key_send_func_t send_key_msg;
 static uint16_t target = UNASSIGNED_ADDRESS;
 static uint32_t parms[8];
 
+static struct cfg_cmd cmds[] = {
+	{ OP_APPKEY_ADD, OP_APPKEY_STATUS, "AppKeyAdd" },
+	{ OP_APPKEY_DELETE, OP_APPKEY_STATUS, "AppKeyDelete" },
+	{ OP_APPKEY_GET, OP_APPKEY_LIST, "AppKeyGet"},
+	{ OP_APPKEY_LIST, NO_RESPONSE, "AppKeyList"},
+	{ OP_APPKEY_STATUS, NO_RESPONSE, "AppKeyStatus"},
+	{ OP_APPKEY_UPDATE, OP_APPKEY_STATUS, "AppKeyUpdate" },
+	{ OP_DEV_COMP_GET, OP_DEV_COMP_STATUS, "DeviceCompositionGet" },
+	{ OP_DEV_COMP_STATUS, NO_RESPONSE, "DeviceCompositionStatus" },
+	{ OP_CONFIG_BEACON_GET, OP_CONFIG_BEACON_STATUS, "BeaconGet" },
+	{ OP_CONFIG_BEACON_SET, OP_CONFIG_BEACON_STATUS, "BeaconSet" },
+	{ OP_CONFIG_BEACON_STATUS, NO_RESPONSE, "BeaconStatus" },
+	{ OP_CONFIG_DEFAULT_TTL_GET, OP_CONFIG_DEFAULT_TTL_STATUS,
+							"DefaultTTLGet" },
+	{ OP_CONFIG_DEFAULT_TTL_SET, OP_CONFIG_DEFAULT_TTL_STATUS,
+							"DefaultTTLSet" },
+	{ OP_CONFIG_DEFAULT_TTL_STATUS, NO_RESPONSE, "DefaultTTLStatus" },
+	{ OP_CONFIG_FRIEND_GET, OP_CONFIG_FRIEND_STATUS, "FriendGet" },
+	{ OP_CONFIG_FRIEND_SET, OP_CONFIG_FRIEND_STATUS, "FrienSet" },
+	{ OP_CONFIG_FRIEND_STATUS, NO_RESPONSE, "FriendStatus" },
+	{ OP_CONFIG_PROXY_GET, OP_CONFIG_PROXY_STATUS, "ProxyGet" },
+	{ OP_CONFIG_PROXY_SET, OP_CONFIG_PROXY_STATUS, "ProxySet" },
+	{ OP_CONFIG_PROXY_STATUS, NO_RESPONSE, "ProxyStatus" },
+	{ OP_CONFIG_KEY_REFRESH_PHASE_GET, OP_CONFIG_KEY_REFRESH_PHASE_STATUS,
+							"KeyRefreshPhaseGet" },
+	{ OP_CONFIG_KEY_REFRESH_PHASE_SET, OP_CONFIG_KEY_REFRESH_PHASE_STATUS,
+							"KeyRefreshPhaseSet" },
+	{ OP_CONFIG_KEY_REFRESH_PHASE_STATUS, NO_RESPONSE,
+						"KeyRefreshPhaseStatus" },
+	{ OP_CONFIG_MODEL_PUB_GET, OP_CONFIG_MODEL_PUB_STATUS, "ModelPubGet" },
+	{ OP_CONFIG_MODEL_PUB_SET, OP_CONFIG_MODEL_PUB_STATUS, "ModelPubSet" },
+	{ OP_CONFIG_MODEL_PUB_STATUS, NO_RESPONSE, "ModelPubStatus" },
+	{ OP_CONFIG_MODEL_PUB_VIRT_SET, OP_CONFIG_MODEL_PUB_STATUS,
+							"ModelPubVirtualSet" },
+	{ OP_CONFIG_MODEL_SUB_ADD, OP_CONFIG_MODEL_SUB_STATUS, "ModelSubAdd" },
+	{ OP_CONFIG_MODEL_SUB_DELETE, OP_CONFIG_MODEL_SUB_STATUS,
+							"ModelSubDelete" },
+	{ OP_CONFIG_MODEL_SUB_DELETE_ALL, OP_CONFIG_MODEL_SUB_STATUS,
+							"ModelSubDeleteAll" },
+	{ OP_CONFIG_MODEL_SUB_OVERWRITE, OP_CONFIG_MODEL_SUB_STATUS,
+							"ModelSubOverwrite" },
+	{ OP_CONFIG_MODEL_SUB_STATUS, NO_RESPONSE, "ModelSubStatus" },
+	{ OP_CONFIG_MODEL_SUB_VIRT_ADD, OP_CONFIG_MODEL_SUB_STATUS,
+							"ModelSubVirtAdd" },
+	{ OP_CONFIG_MODEL_SUB_VIRT_DELETE, OP_CONFIG_MODEL_SUB_STATUS,
+							"ModelSubVirtDelete" },
+	{ OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE, OP_CONFIG_MODEL_SUB_STATUS,
+						"ModelSubVirtOverwrite" },
+	{ OP_CONFIG_NETWORK_TRANSMIT_GET, OP_CONFIG_NETWORK_TRANSMIT_STATUS,
+							"NetworkTransmitGet" },
+	{ OP_CONFIG_NETWORK_TRANSMIT_SET, OP_CONFIG_NETWORK_TRANSMIT_STATUS,
+							"NetworkTransmitSet" },
+	{ OP_CONFIG_NETWORK_TRANSMIT_STATUS, NO_RESPONSE,
+						"NetworkTransmitStatus" },
+	{ OP_CONFIG_RELAY_GET, OP_CONFIG_RELAY_STATUS, "RelayGet" },
+	{ OP_CONFIG_RELAY_SET, OP_CONFIG_RELAY_STATUS, "RelaySet" },
+	{ OP_CONFIG_RELAY_STATUS, NO_RESPONSE, "RelayStatus" },
+	{ OP_CONFIG_MODEL_SUB_GET, OP_CONFIG_MODEL_SUB_LIST, "ModelSubGet" },
+	{ OP_CONFIG_MODEL_SUB_LIST, NO_RESPONSE, "ModelSubList" },
+	{ OP_CONFIG_VEND_MODEL_SUB_GET, OP_CONFIG_VEND_MODEL_SUB_LIST,
+							"VendorModelSubGet" },
+	{ OP_CONFIG_VEND_MODEL_SUB_LIST, NO_RESPONSE, "VendorModelSubList" },
+	{ OP_CONFIG_POLL_TIMEOUT_LIST, OP_CONFIG_POLL_TIMEOUT_STATUS,
+							"PollTimeoutList" },
+	{ OP_CONFIG_POLL_TIMEOUT_STATUS, NO_RESPONSE, "PollTimeoutStatus" },
+	{ OP_CONFIG_HEARTBEAT_PUB_GET, OP_CONFIG_HEARTBEAT_PUB_STATUS,
+							"HeartbeatPubGet" },
+	{ OP_CONFIG_HEARTBEAT_PUB_SET, OP_CONFIG_HEARTBEAT_PUB_STATUS,
+							"HeartbeatPubSet" },
+	{ OP_CONFIG_HEARTBEAT_PUB_STATUS, NO_RESPONSE, "HeartbeatPubStatus" },
+	{ OP_CONFIG_HEARTBEAT_SUB_GET, OP_CONFIG_HEARTBEAT_SUB_GET,
+							"HeartbeatSubGet" },
+	{ OP_CONFIG_HEARTBEAT_SUB_SET, OP_CONFIG_HEARTBEAT_SUB_GET,
+							"HeartbeatSubSet" },
+	{ OP_CONFIG_HEARTBEAT_SUB_STATUS, NO_RESPONSE, "HeartbeatSubStatus" },
+	{ OP_MODEL_APP_BIND, OP_MODEL_APP_STATUS, "ModelAppBind" },
+	{ OP_MODEL_APP_STATUS, NO_RESPONSE, "ModelAppStatus" },
+	{ OP_MODEL_APP_UNBIND, OP_MODEL_APP_STATUS, "ModelAppUnbind" },
+	{ OP_NETKEY_ADD, OP_NETKEY_STATUS, "NetKeyAdd" },
+	{ OP_NETKEY_DELETE, OP_NETKEY_STATUS, "NetKeyDelete" },
+	{ OP_NETKEY_GET, OP_NETKEY_LIST, "NetKeyGet" },
+	{ OP_NETKEY_LIST, NO_RESPONSE, "NetKeyList" },
+	{ OP_NETKEY_STATUS, NO_RESPONSE, "NetKeyStatus" },
+	{ OP_NETKEY_UPDATE, OP_NETKEY_STATUS, "NetKeyUpdate" },
+	{ OP_NODE_IDENTITY_GET, OP_NODE_IDENTITY_STATUS, "NodeIdentityGet" },
+	{ OP_NODE_IDENTITY_SET, OP_NODE_IDENTITY_STATUS, "NodeIdentitySet" },
+	{ OP_NODE_IDENTITY_STATUS, NO_RESPONSE, "NodeIdentityStatus" },
+	{ OP_NODE_RESET, OP_NODE_RESET_STATUS, "NodeReset" },
+	{ OP_NODE_RESET_STATUS, NO_RESPONSE, "NodeResetStatus" },
+	{ OP_MODEL_APP_GET, OP_MODEL_APP_LIST, "ModelAppGet" },
+	{ OP_MODEL_APP_LIST, NO_RESPONSE, "ModelAppList" },
+	{ OP_VEND_MODEL_APP_GET, OP_VEND_MODEL_APP_LIST, "VendorModelAppGet" },
+	{ OP_VEND_MODEL_APP_LIST, NO_RESPONSE, "VendorModelAppList" }
+};
+
+static const char *opcode_str(uint32_t opcode)
+{
+	uint32_t n;
+
+	for (n = 0; n < L_ARRAY_SIZE(cmds); n++) {
+		if (opcode == cmds[n].opcode)
+			return cmds[n].desc;
+	}
+
+	return "Unknown";
+}
+
 static uint32_t print_mod_id(uint8_t *data, bool vid, const char *offset)
 {
 	uint32_t mod_id;
@@ -162,6 +276,8 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 	} else
 		return false;
 
+	bt_shell_printf("Received %s\n", opcode_str(opcode));
+
 	switch (opcode & ~OP_UNRELIABLE) {
 	default:
 		return false;
@@ -437,14 +553,20 @@ static void cmd_dst_set(int argc, char *argv[])
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
-static bool config_send(uint8_t *buf, uint16_t len)
+static bool config_send(uint8_t *buf, uint16_t len, uint32_t opcode)
 {
+	bool res;
+
 	if (IS_UNASSIGNED(target)) {
 		bt_shell_printf("Destination not set\n");
 		return false;
 	}
 
-	return send_msg(send_data, target, APP_IDX_DEV_REMOTE, buf, len);
+	res = send_msg(send_data, target, APP_IDX_DEV_REMOTE, buf, len);
+	if (!res)
+		bt_shell_printf("Failed to send \"%s\"\n", opcode_str(opcode));
+
+	return res;
 }
 
 static void cmd_default(uint32_t opcode)
@@ -454,11 +576,8 @@ static void cmd_default(uint32_t opcode)
 
 	n = mesh_opcode_set(opcode, msg);
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send command (opcode 0x%x)\n",
-								opcode);
+	if (!config_send(msg, n, opcode))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -473,20 +592,25 @@ static void cmd_composition_get(int argc, char *argv[])
 	/* By default, use page 0 */
 	msg[n++] = (read_input_parameters(argc, argv) == 1) ? parms[0] : 0;
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"GET NODE COMPOSITION\"\n");
+	if (!config_send(msg, n, OP_DEV_COMP_GET))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void cmd_netkey_del(int argc, char *argv[])
+static void cmd_key_del(int argc, char *argv[], bool is_appkey)
 {
+	uint32_t opcode;
 	uint16_t n;
 	uint8_t msg[32];
 
-	n = mesh_opcode_set(OP_NETKEY_DELETE, msg);
+	if (IS_UNASSIGNED(target)) {
+		bt_shell_printf("Destination not set\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	opcode = (is_appkey) ? OP_APPKEY_DELETE : OP_NETKEY_DELETE;
+	n = mesh_opcode_set(opcode, msg);
 
 	if (read_input_parameters(argc, argv) != 1) {
 		bt_shell_printf("Bad arguments %s\n", argv[1]);
@@ -496,42 +620,71 @@ static void cmd_netkey_del(int argc, char *argv[])
 	put_le16(target + parms[0], msg + n);
 	n += 2;
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"DEL_NET KEY\"\n");
+	if (!config_send(msg, n, opcode))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_netkey_del(int argc, char *argv[])
+{
+	cmd_key_del(argc, argv, false);
+}
+
+static void cmd_appkey_del(int argc, char *argv[])
+{
+	cmd_key_del(argc, argv, true);
+}
+
+static void cmd_key_add(uint32_t opcode, int argc, char *argv[])
+{
+	uint16_t key_idx;
+	bool is_appkey, update;
+
+	if (IS_UNASSIGNED(target)) {
+		bt_shell_printf("Destination not set\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (!send_key_msg) {
+		bt_shell_printf("Send key callback not set\n");
+		return;
 	}
 
+	if (read_input_parameters(argc, argv) != 1) {
+		bt_shell_printf("Bad arguments %s\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	key_idx = (uint16_t) parms[0];
+
+	update = (opcode == OP_NETKEY_UPDATE || opcode == OP_APPKEY_UPDATE);
+	is_appkey = (opcode == OP_APPKEY_ADD || opcode == OP_APPKEY_UPDATE);
+
+	if (!send_key_msg(key_data, target, key_idx, is_appkey, update))
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
 static void cmd_netkey_add(int argc, char *argv[])
 {
-	/*TODO*/
-	bt_shell_printf("Not implemented\n");
+	cmd_key_add(OP_NETKEY_ADD, argc, argv);
 }
 
 static void cmd_netkey_update(int argc, char *argv[])
 {
-	/*TODO*/
-	bt_shell_printf("Not implemented\n");
+	cmd_key_add(OP_NETKEY_UPDATE, argc, argv);
 }
 
 static void cmd_appkey_add(int argc, char *argv[])
 {
-	/*TODO*/
-	bt_shell_printf("Not implemented\n");
+	cmd_key_add(OP_APPKEY_ADD, argc, argv);
 }
 
 static void cmd_appkey_update(int argc, char *argv[])
 {
-	/*TODO*/
-	bt_shell_printf("Not implemented\n");
-}
-
-static void cmd_appkey_del(int argc, char *argv[])
-{
-	/*TODO*/
-	bt_shell_printf("Not implemented\n");
+	cmd_key_add(OP_APPKEY_UPDATE, argc, argv);
 }
 
 static void cmd_bind(int argc, char *argv[])
@@ -562,10 +715,8 @@ static void cmd_bind(int argc, char *argv[])
 		n += 2;
 	}
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"MODEL APP BIND\"\n");
+	if (!config_send(msg, n, OP_MODEL_APP_BIND))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -586,10 +737,8 @@ static void cmd_beacon_set(int argc, char *argv[])
 
 	msg[n++] = parms[0];
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"SET BEACON\"\n");
-		return;
-	}
+	if (!config_send(msg, n, OP_CONFIG_BEACON_SET))
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -617,10 +766,8 @@ static void cmd_ident_set(int argc, char *argv[])
 	n += 2;
 	msg[n++] = parms[1];
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"SET IDENTITY\"\n");
-		return;
-	}
+	if (!config_send(msg, n, OP_NODE_IDENTITY_SET))
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -642,10 +789,8 @@ static void cmd_ident_get(int argc, char *argv[])
 	put_le16(parms[0], msg + n);
 	n += 2;
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"GET IDENTITY\"\n");
+	if (!config_send(msg, n, OP_NODE_IDENTITY_GET))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -666,10 +811,8 @@ static void cmd_proxy_set(int argc, char *argv[])
 
 	msg[n++] = parms[0];
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"SET PROXY\"\n");
+	if (!config_send(msg, n, OP_CONFIG_PROXY_SET))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -696,10 +839,8 @@ static void cmd_relay_set(int argc, char *argv[])
 	msg[n++] = parms[0];
 	msg[n++] = (parms[1] << 5) | parms[2];
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"SET RELAY\"\n");
+	if (!config_send(msg, n, OP_CONFIG_RELAY_SET))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -724,10 +865,8 @@ static void cmd_ttl_set(int argc, char *argv[])
 	n = mesh_opcode_set(OP_CONFIG_DEFAULT_TTL_SET, msg);
 	msg[n++] = parms[0];
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"SET_DEFAULT TTL\"\n");
+	if (!config_send(msg, n, OP_CONFIG_DEFAULT_TTL_SET))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -771,10 +910,8 @@ static void cmd_pub_set(int argc, char *argv[])
 		n += 2;
 	}
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"SET MODEL PUBLICATION\"\n");
+	if (!config_send(msg, n, OP_CONFIG_MODEL_PUB_SET))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -807,10 +944,8 @@ static void cmd_pub_get(int argc, char *argv[])
 		n += 2;
 	}
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"GET MODEL PUBLICATION\"\n");
+	if (!config_send(msg, n, OP_CONFIG_MODEL_PUB_GET))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -840,10 +975,8 @@ static void cmd_sub_add(int argc, char *argv[])
 	put_le16(parms[2], msg + n);
 	n += 2;
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"ADD SUBSCRIPTION\"\n");
+	if (!config_send(msg, n, OP_CONFIG_MODEL_SUB_ADD))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -870,10 +1003,8 @@ static void cmd_sub_get(int argc, char *argv[])
 	put_le16(parms[1], msg + n);
 	n += 2;
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"GET SUB GET\"\n");
+	if (!config_send(msg, n, OP_CONFIG_MODEL_SUB_GET))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -900,10 +1031,8 @@ static void cmd_mod_appidx_get(int argc, char *argv[])
 	put_le16(parms[1], msg + n);
 	n += 2;
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"GET APP GET\"\n");
+	if (!config_send(msg, n, OP_MODEL_APP_GET))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -939,10 +1068,8 @@ static void cmd_hb_pub_set(int argc, char *argv[])
 	put_le16(parms[5], msg + n);
 	n += 2;
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"SET HEARTBEAT PUBLISH\"\n");
+	if (!config_send(msg, n, OP_CONFIG_HEARTBEAT_PUB_SET))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
@@ -976,10 +1103,8 @@ static void cmd_hb_sub_set(int argc, char *argv[])
 	/* Period log */
 	msg[n++] = parms[2];
 
-	if (!config_send(msg, n)) {
-		bt_shell_printf("Failed to send \"SET HEARTBEAT SUBSCRIBE\"\n");
+	if (!config_send(msg, n, OP_CONFIG_HEARTBEAT_SUB_SET))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
-	}
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
-- 
2.21.0


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

* [PATCH BlueZ 07/10] tools/mesh-cfgclient: Add timeout for expected response
  2019-10-29 20:40 [PATCH BlueZ 00/10] Mesh provisioning and configuration tool Inga Stotland
                   ` (5 preceding siblings ...)
  2019-10-29 20:40 ` [PATCH BlueZ 06/10] tools/mesh-cfgclient: Add config menu key commands Inga Stotland
@ 2019-10-29 20:40 ` Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 08/10] tools/mesh-cfgclient: Command line option for config Inga Stotland
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Inga Stotland @ 2019-10-29 20:40 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This adds a pending request queue for the issued commands awaiting
response from a remote config server. The tool forces "single-issue"
of a config command, i.e. only one command that has a specified return
type is allowed to be outstanding per a remote node address.
That is, if AppKeyAdd command to a node is pending response, the tool
disallows sending another AppKeyAdd, AppKeyUpdate or AppKeyDelete to
the same address until response timeout expires (notification will
be displayed).
The default response timeout is set to 2 seconds and is configurable
by "timeout" command.
---
 tools/mesh-cfgclient.c |   2 +
 tools/mesh/cfgcli.c    | 136 +++++++++++++++++++++++++++++++++++++++--
 tools/mesh/cfgcli.h    |   1 +
 3 files changed, 135 insertions(+), 4 deletions(-)

diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
index 5d74be9dd..399dc05dd 100644
--- a/tools/mesh-cfgclient.c
+++ b/tools/mesh-cfgclient.c
@@ -1663,5 +1663,7 @@ int main(int argc, char *argv[])
 	l_dbus_client_destroy(client);
 	l_dbus_destroy(dbus);
 
+	cfgcli_cleanup();
+
 	return status;
 }
diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index ae8644049..df2a23b05 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -40,18 +40,30 @@
 #define MIN_COMPOSITION_LEN 16
 #define NO_RESPONSE 0xFFFFFFFF
 
+/* Default timeout for getting a response to a sent config command (seconds) */
+#define DEFAULT_TIMEOUT 2
+
 struct cfg_cmd {
 	uint32_t opcode;
-	uint32_t resp;
+	uint32_t rsp;
 	const char *desc;
 };
 
+struct pending_req {
+	struct l_timeout *timer;
+	const struct cfg_cmd *cmd;
+	uint16_t addr;
+};
+
+static struct l_queue *requests;
+
 static void *send_data;
 static model_send_msg_func_t send_msg;
 
 static void *key_data;
 static key_send_func_t send_key_msg;
 
+static uint32_t rsp_timeout = DEFAULT_TIMEOUT;
 static uint16_t target = UNASSIGNED_ADDRESS;
 static uint32_t parms[8];
 
@@ -150,16 +162,79 @@ static struct cfg_cmd cmds[] = {
 	{ OP_VEND_MODEL_APP_LIST, NO_RESPONSE, "VendorModelAppList" }
 };
 
-static const char *opcode_str(uint32_t opcode)
+static const struct cfg_cmd *get_cmd(uint32_t opcode)
 {
 	uint32_t n;
 
 	for (n = 0; n < L_ARRAY_SIZE(cmds); n++) {
 		if (opcode == cmds[n].opcode)
-			return cmds[n].desc;
+			return &cmds[n];
+	}
+
+	return NULL;
+}
+
+static const char *opcode_str(uint32_t opcode)
+{
+	const struct cfg_cmd *cmd;
+
+	cmd = get_cmd(opcode);
+	if (!cmd)
+		return "Unknown";
+
+	return cmd->desc;
+}
+
+static void free_request(void *a)
+{
+	struct pending_req *req = a;
+
+	l_timeout_remove(req->timer);
+	l_free(req);
+}
+
+static struct pending_req *get_req_by_rsp(uint16_t addr, uint32_t rsp)
+{
+	const struct l_queue_entry *entry;
+
+	entry = l_queue_get_entries(requests);
+
+	for (; entry; entry = entry->next) {
+		struct pending_req *req = entry->data;
+
+		if (req->addr == addr && req->cmd->rsp == rsp)
+			return req;
 	}
 
-	return "Unknown";
+	return NULL;
+}
+
+static void wait_rsp_timeout(struct l_timeout *timeout, void *user_data)
+{
+	struct pending_req *req = user_data;
+
+	bt_shell_printf("No response for \"%s\" from %4.4x\n",
+						req->cmd->desc, req->addr);
+
+	l_queue_remove(requests, req);
+	free_request(req);
+}
+
+static void add_request(uint32_t opcode)
+{
+	struct pending_req *req;
+	const struct cfg_cmd *cmd;
+
+	cmd = get_cmd(opcode);
+	if (!cmd)
+		return;
+
+	req = l_new(struct pending_req, 1);
+	req->cmd = cmd;
+	req->addr = target;
+	req->timer = l_timeout_create(rsp_timeout,
+				wait_rsp_timeout, req, NULL);
+	l_queue_push_tail(requests, req);
 }
 
 static uint32_t print_mod_id(uint8_t *data, bool vid, const char *offset)
@@ -269,6 +344,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 	struct model_pub pub;
 	int n;
 	uint16_t i;
+	struct pending_req *req;
 
 	if (mesh_opcode_get(data, len, &opcode, &n)) {
 		len -= n;
@@ -278,6 +354,12 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 	bt_shell_printf("Received %s\n", opcode_str(opcode));
 
+	req = get_req_by_rsp(src, (opcode & ~OP_UNRELIABLE));
+	if (req) {
+		free_request(req);
+		l_queue_remove(requests, req);
+	}
+
 	switch (opcode & ~OP_UNRELIABLE) {
 	default:
 		return false;
@@ -531,6 +613,19 @@ static uint32_t read_input_parameters(int argc, char *argv[])
 	return i;
 }
 
+static void cmd_timeout_set(int argc, char *argv[])
+{
+	if (read_input_parameters(argc, argv) != 1)
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+
+	rsp_timeout = parms[0];
+
+	bt_shell_printf("Timeout to wait for remote node's response: %d secs\n",
+								rsp_timeout);
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
 static void cmd_dst_set(int argc, char *argv[])
 {
 	uint32_t dst;
@@ -555,6 +650,7 @@ static void cmd_dst_set(int argc, char *argv[])
 
 static bool config_send(uint8_t *buf, uint16_t len, uint32_t opcode)
 {
+	const struct cfg_cmd *cmd;
 	bool res;
 
 	if (IS_UNASSIGNED(target)) {
@@ -562,10 +658,22 @@ static bool config_send(uint8_t *buf, uint16_t len, uint32_t opcode)
 		return false;
 	}
 
+	cmd = get_cmd(opcode);
+	if (!cmd)
+		return false;
+
+	if (get_req_by_rsp(target, cmd->rsp)) {
+		bt_shell_printf("Another command is pending\n");
+		return false;
+	}
+
 	res = send_msg(send_data, target, APP_IDX_DEV_REMOTE, buf, len);
 	if (!res)
 		bt_shell_printf("Failed to send \"%s\"\n", opcode_str(opcode));
 
+	if (cmd->rsp != NO_RESPONSE)
+		add_request(opcode);
+
 	return res;
 }
 
@@ -640,6 +748,7 @@ static void cmd_key_add(uint32_t opcode, int argc, char *argv[])
 {
 	uint16_t key_idx;
 	bool is_appkey, update;
+	const struct cfg_cmd *cmd;
 
 	if (IS_UNASSIGNED(target)) {
 		bt_shell_printf("Destination not set\n");
@@ -656,6 +765,15 @@ static void cmd_key_add(uint32_t opcode, int argc, char *argv[])
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 	}
 
+	cmd = get_cmd(opcode);
+	if (!cmd)
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+
+	if (get_req_by_rsp(target, cmd->rsp)) {
+		bt_shell_printf("Another key command is pending\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
 	key_idx = (uint16_t) parms[0];
 
 	update = (opcode == OP_NETKEY_UPDATE || opcode == OP_APPKEY_UPDATE);
@@ -664,6 +782,8 @@ static void cmd_key_add(uint32_t opcode, int argc, char *argv[])
 	if (!send_key_msg(key_data, target, key_idx, is_appkey, update))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 
+	add_request(opcode);
+
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 }
 
@@ -1141,6 +1261,8 @@ static const struct bt_shell_menu cfg_menu = {
 	.entries = {
 	{"target", "<unicast>", cmd_dst_set,
 				"Set target node to configure"},
+	{"timeout", "<seconds>", cmd_timeout_set,
+				"Set response timeout (seconds)"},
 	{"composition-get", "[page_num]", cmd_composition_get,
 				"Get composition data"},
 	{"netkey-add", "<net_idx>", cmd_netkey_add,
@@ -1222,8 +1344,14 @@ struct model_info *cfgcli_init(key_send_func_t key_send, void *user_data)
 
 	send_key_msg = key_send;
 	key_data = user_data;
+	requests = l_queue_new();
 
 	bt_shell_add_submenu(&cfg_menu);
 
 	return &cli_info;
 }
+
+void cfgcli_cleanup(void)
+{
+	l_queue_destroy(requests, free_request);
+}
diff --git a/tools/mesh/cfgcli.h b/tools/mesh/cfgcli.h
index 077c340e5..16d2e0a61 100644
--- a/tools/mesh/cfgcli.h
+++ b/tools/mesh/cfgcli.h
@@ -22,3 +22,4 @@ typedef bool (*key_send_func_t) (void *user_data, uint16_t dst,
 				 uint16_t idx, bool is_appkey, bool update);
 
 struct model_info *cfgcli_init(key_send_func_t key_func, void *user_data);
+void cfgcli_cleanup(void);
-- 
2.21.0


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

* [PATCH BlueZ 08/10] tools/mesh-cfgclient: Command line option for config
  2019-10-29 20:40 [PATCH BlueZ 00/10] Mesh provisioning and configuration tool Inga Stotland
                   ` (6 preceding siblings ...)
  2019-10-29 20:40 ` [PATCH BlueZ 07/10] tools/mesh-cfgclient: Add timeout for expected response Inga Stotland
@ 2019-10-29 20:40 ` Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 09/10] tools/mesh: add initial support for config storage Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 10/10] tools/mesh-cfgclient: Add README file Inga Stotland
  9 siblings, 0 replies; 11+ messages in thread
From: Inga Stotland @ 2019-10-29 20:40 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This adds "-c" option to specify full path to configuration file.
If the path is not provided, the application will attempt to open
in the following order:
1) $XDG_CONFIG_HOME/meshcfg/config_db.json
2) $HOME/meshcfg/config_db.json

if the file does not exist, it will be generated for a newly created
network, i.e., upon successful completion of "create" command.
---
 tools/mesh-cfgclient.c | 75 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 70 insertions(+), 5 deletions(-)

diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
index 399dc05dd..947fdb01a 100644
--- a/tools/mesh-cfgclient.c
+++ b/tools/mesh-cfgclient.c
@@ -25,8 +25,12 @@
 #include <assert.h>
 #include <ctype.h>
 #include <dbus/dbus.h>
+#include <errno.h>
 #include <stdio.h>
 #include <time.h>
+
+#include <sys/stat.h>
+
 #include <ell/ell.h>
 
 #include "src/shared/shell.h"
@@ -52,6 +56,8 @@
 #define DEFAULT_START_ADDRESS	0x00aa
 #define DEFAULT_NET_INDEX	0x0000
 
+#define DEFAULT_CFG_FILE	"config_db.json"
+
 struct meshcfg_el {
 	const char *path;
 	uint8_t index;
@@ -106,6 +112,8 @@ static struct l_queue *devices;
 static bool prov_in_progress;
 static const char *caps[2] = {"out-numeric", "in-numeric"};
 
+static bool have_config;
+
 static struct meshcfg_app app = {
 	.path = "/mesh/cfgclient",
 	.agent_path = "/mesh/cfgclient/agent",
@@ -121,23 +129,28 @@ static struct meshcfg_app app = {
 };
 
 static const struct option options[] = {
-	{ "address",	optional_argument, 0, 'a' },
-	{ "net-index",	optional_argument, 0, 'n' },
+	{ "config",	required_argument, 0, 'c' },
+	{ "address",	required_argument, 0, 'a' },
+	{ "net-index",	required_argument, 0, 'n' },
 	{ 0, 0, 0, 0 }
 };
 
 static const char *address_opt;
 static const char *net_idx_opt;
+static const char *config_opt;
 
 static uint16_t prov_address;
 static uint16_t prov_net_idx;
+static const char *cfg_fname;
 
 static const char **optargs[] = {
+	&config_opt,
 	&address_opt,
 	&net_idx_opt,
 };
 
 static const char *help[] = {
+	"Configuration file",
 	"Starting unicast address for remote nodes",
 	"Net index for provisioning subnet"
 };
@@ -145,7 +158,7 @@ static const char *help[] = {
 static const struct bt_shell_opt opt = {
 	.options = options,
 	.optno = sizeof(options) / sizeof(struct option),
-	.optstr = "a:n:",
+	.optstr = "c:a:n:",
 	.optarg = optargs,
 	.help = help,
 };
@@ -1621,18 +1634,65 @@ static void ready_callback(void *user_data)
 		bt_shell_printf("Failed to register the ObjectManager\n");
 }
 
+static bool setup_cfg_storage(void)
+{
+	struct stat st;
+
+	if (!config_opt) {
+		char *home;
+		char *mesh_dir;
+
+		home = getenv("XDG_CONFIG_HOME");
+
+		if (home) {
+			mesh_dir = l_strdup_printf("%s/meshcfg", home);
+		} else {
+			home = getenv("HOME");
+			if (!home) {
+				l_error("\"HOME\" not set\n");
+				return false;
+			}
+
+			mesh_dir = l_strdup_printf("%s/.config/meshcfg", home);
+		}
+
+		if (!mesh_dir)
+			return false;
+
+		cfg_fname = l_strdup_printf("mesh_dir/%s", DEFAULT_CFG_FILE);
+		l_free(mesh_dir);
+
+	} else {
+		cfg_fname = l_strdup_printf("%s", config_opt);
+	}
+
+	if (stat(cfg_fname, &st) == -1) {
+		if (errno == ENOENT) {
+			l_warn("\nWarning: config file \"%s\" not found",
+								cfg_fname);
+			return true;
+		}
+
+		perror("\nFailed to open config file");
+		return false;
+	}
+
+	have_config = true;
+	return true;
+}
+
 int main(int argc, char *argv[])
 {
 	struct l_dbus_client *client;
 	uint32_t val;
 	int status;
 
-	l_log_set_stderr();
-
 	bt_shell_init(argc, argv, &opt);
 	bt_shell_set_menu(&main_menu);
 	bt_shell_set_prompt(PROMPT_OFF);
 
+	l_log_set_stderr();
+
 	if (address_opt && sscanf(address_opt, "%04x", &val) == 1)
 		prov_address = (uint16_t) val;
 	else
@@ -1643,6 +1703,11 @@ int main(int argc, char *argv[])
 	else
 		prov_net_idx = DEFAULT_NET_INDEX;
 
+	if (!setup_cfg_storage()) {
+		bt_shell_cleanup();
+		return EXIT_FAILURE;
+	}
+
 	dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS);
 
 	l_dbus_set_ready_handler(dbus, ready_callback, NULL, NULL);
-- 
2.21.0


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

* [PATCH BlueZ 09/10] tools/mesh: add initial support for config storage
  2019-10-29 20:40 [PATCH BlueZ 00/10] Mesh provisioning and configuration tool Inga Stotland
                   ` (7 preceding siblings ...)
  2019-10-29 20:40 ` [PATCH BlueZ 08/10] tools/mesh-cfgclient: Command line option for config Inga Stotland
@ 2019-10-29 20:40 ` Inga Stotland
  2019-10-29 20:40 ` [PATCH BlueZ 10/10] tools/mesh-cfgclient: Add README file Inga Stotland
  9 siblings, 0 replies; 11+ messages in thread
From: Inga Stotland @ 2019-10-29 20:40 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

This adds support for storing the state of a mesh network.
The configuration is written out in JSON format. The initial
configuration file is generated automatically upon the
successful completion of "create" command form the main menu.
---
 Makefile.tools         |   7 +-
 tools/mesh-cfgclient.c | 189 +++++++++--
 tools/mesh/cfgcli.c    |  23 +-
 tools/mesh/keys.c      |   2 +-
 tools/mesh/mesh-db.c   | 742 +++++++++++++++++++++++++++++++++++++++++
 tools/mesh/mesh-db.h   |  54 +++
 tools/mesh/remote.c    | 131 +++++++-
 tools/mesh/remote.h    |  12 +-
 8 files changed, 1104 insertions(+), 56 deletions(-)
 create mode 100644 tools/mesh/mesh-db.c
 create mode 100644 tools/mesh/mesh-db.h

diff --git a/Makefile.tools b/Makefile.tools
index 0b4ddf576..006554cf7 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -333,10 +333,13 @@ tools_mesh_cfgclient_SOURCES = tools/mesh-cfgclient.c \
 				tools/mesh/keys.h tools/mesh/keys.c \
 				tools/mesh/util.h tools/mesh/util.c \
 				tools/mesh/remote.h tools/mesh/remote.c \
-				tools/mesh/agent.h tools/mesh/agent.c
+				tools/mesh/agent.h tools/mesh/agent.c \
+				tools/mesh/mesh-db.h tools/mesh/mesh-db.c \
+				mesh/util.h mesh/util.c \
+				mesh/mesh-config.h mesh/mesh-config-json.c
 
 tools_mesh_cfgclient_LDADD = lib/libbluetooth-internal.la src/libshared-ell.la \
-						$(ell_ldadd) -lreadline
+						$(ell_ldadd) -ljson-c -lreadline
 endif
 
 EXTRA_DIST += tools/mesh-gatt/local_node.json tools/mesh-gatt/prov_db.json
diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
index 947fdb01a..4701f5ab1 100644
--- a/tools/mesh-cfgclient.c
+++ b/tools/mesh-cfgclient.c
@@ -42,6 +42,7 @@
 #include "tools/mesh/agent.h"
 #include "tools/mesh/cfgcli.h"
 #include "tools/mesh/keys.h"
+#include "tools/mesh/mesh-db.h"
 #include "tools/mesh/model.h"
 #include "tools/mesh/remote.h"
 
@@ -54,6 +55,7 @@
 #define UNPROV_SCAN_MAX_SECS	300
 
 #define DEFAULT_START_ADDRESS	0x00aa
+#define DEFAULT_MAX_ADDRESS	(VIRTUAL_ADDRESS_LOW - 1)
 #define DEFAULT_NET_INDEX	0x0000
 
 #define DEFAULT_CFG_FILE	"config_db.json"
@@ -129,23 +131,27 @@ static struct meshcfg_app app = {
 };
 
 static const struct option options[] = {
-	{ "config",	required_argument, 0, 'c' },
-	{ "address",	required_argument, 0, 'a' },
-	{ "net-index",	required_argument, 0, 'n' },
+	{ "config",		required_argument, 0, 'c' },
+	{ "address-start",	required_argument, 0, 'a' },
+	{ "address-range",	required_argument, 0, 'r' },
+	{ "net-index",		required_argument, 0, 'n' },
 	{ 0, 0, 0, 0 }
 };
 
 static const char *address_opt;
+static const char *range_opt;
 static const char *net_idx_opt;
 static const char *config_opt;
 
-static uint16_t prov_address;
+static uint16_t low_addr;
+static uint16_t high_addr;
 static uint16_t prov_net_idx;
 static const char *cfg_fname;
 
 static const char **optargs[] = {
 	&config_opt,
 	&address_opt,
+	&range_opt,
 	&net_idx_opt,
 };
 
@@ -249,6 +255,7 @@ static void send_msg_setup(struct l_dbus_message *msg, void *user_data)
 	l_dbus_message_builder_append_basic(builder, 'q', &req->dst);
 	if (req->is_dev_key)
 		l_dbus_message_builder_append_basic(builder, 'b', &req->rmt);
+
 	l_dbus_message_builder_append_basic(builder, 'q', &req->idx);
 	append_byte_array(builder, req->data, req->len);
 	l_dbus_message_builder_finalize(builder);
@@ -531,8 +538,19 @@ static void create_net_reply(struct l_dbus_proxy *proxy,
 	bt_shell_printf("Created new node with token %s\n", str);
 	l_free(str);
 
-	remote_add_node(app.uuid, 0x0001, 1, PRIMARY_NET_IDX);
+	if (!mesh_db_create(cfg_fname, local->token.u8,
+						"Mesh Config Client Network")) {
+		l_free(local);
+		local = NULL;
+		return;
+	}
+
+	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);
 
 	l_dbus_proxy_method_call(net_proxy, "Attach", attach_node_setup,
 						attach_node_reply, NULL,
@@ -556,6 +574,11 @@ static void create_net_setup(struct l_dbus_message *msg, void *user_data)
 
 static void cmd_create_network(int argc, char *argv[])
 {
+	if (have_config) {
+		l_error("Mesh network configuration exists (%s)", cfg_fname);
+		return;
+	}
+
 	l_dbus_proxy_method_call(net_proxy, "CreateNetwork", create_net_setup,
 						create_net_reply, NULL,
 						NULL);
@@ -626,6 +649,11 @@ static void cmd_list_nodes(int argc, char *argv[])
 	remote_print_all();
 }
 
+static void cmd_keys(int argc, char *argv[])
+{
+	keys_print_keys();
+}
+
 static void free_generic_request(void *data)
 {
 	struct generic_request *req = data;
@@ -651,7 +679,7 @@ static void cmd_delete_node(int argc, char *argv[])
 {
 	struct generic_request *req;
 
-	if (!node || !node->proxy || !node->mgmt_proxy) {
+	if (!local || !local->proxy || !local->mgmt_proxy) {
 		bt_shell_printf("Node is not attached\n");
 		return;
 	}
@@ -669,7 +697,7 @@ static void cmd_delete_node(int argc, char *argv[])
 	if (sscanf(argv[2], "%u", &req->arg2) != 1)
 		goto fail;
 
-	l_dbus_proxy_method_call(node->mgmt_proxy, "DeleteRemoteNode",
+	l_dbus_proxy_method_call(local->mgmt_proxy, "DeleteRemoteNode",
 				delete_node_setup, NULL, req, l_free);
 
 	/* TODO:: Delete node from configuration */
@@ -724,7 +752,7 @@ static void cmd_import_node(int argc, char *argv[])
 	struct generic_request *req;
 	size_t sz;
 
-	if (!node || !node->proxy || !node->mgmt_proxy) {
+	if (!local || !local->proxy || !local->mgmt_proxy) {
 		bt_shell_printf("Node is not attached\n");
 		return;
 	}
@@ -764,7 +792,7 @@ static void cmd_import_node(int argc, char *argv[])
 		goto fail;
 	}
 
-	l_dbus_proxy_method_call(node->mgmt_proxy, "ImportRemoteNode",
+	l_dbus_proxy_method_call(local->mgmt_proxy, "ImportRemoteNode",
 					import_node_setup, import_node_reply,
 					req, free_generic_request);
 
@@ -803,7 +831,7 @@ static void cmd_subnet_set_phase(int argc, char *argv[])
 {
 	struct generic_request *req;
 
-	if (!node || !node->proxy || !node->mgmt_proxy) {
+	if (!local || !local->proxy || !local->mgmt_proxy) {
 		bt_shell_printf("Node is not attached\n");
 		return;
 	}
@@ -821,7 +849,7 @@ static void cmd_subnet_set_phase(int argc, char *argv[])
 	if (sscanf(argv[2], "%d", &req->arg2) != 1)
 		goto fail;
 
-	l_dbus_proxy_method_call(node->mgmt_proxy, "SetKeyPhase",
+	l_dbus_proxy_method_call(local->mgmt_proxy, "SetKeyPhase",
 					subnet_set_phase_setup,
 					subnet_set_phase_reply, req, l_free);
 
@@ -846,12 +874,16 @@ static void mgr_key_reply(struct l_dbus_proxy *proxy,
 		return;
 	}
 
-	if (!strcmp("CreateSubnet", method))
+	if (!strcmp("CreateSubnet", method)) {
 		keys_add_net_key(idx);
-	else if (!strcmp("DeleteSubnet", method))
+		mesh_db_net_key_add(idx);
+	} else if (!strcmp("DeleteSubnet", method)) {
 		keys_del_net_key(idx);
-	else if (!strcmp("DeleteAppKey", method))
+		/* TODO: mesh_db_net_key_del */
+	} else if (!strcmp("DeleteAppKey", method)) {
 		keys_del_app_key(idx);
+		/* TODO: mesh_db_app_key_del */
+	}
 }
 
 static void mgr_key_setup(struct l_dbus_message *msg, void *user_data)
@@ -866,7 +898,7 @@ static void mgr_key_cmd(int argc, char *argv[], const char *method_name)
 {
 	struct generic_request *req;
 
-	if (!node || !node->proxy || !node->mgmt_proxy) {
+	if (!local || !local->proxy || !local->mgmt_proxy) {
 		bt_shell_printf("Node is not attached\n");
 		return;
 	}
@@ -885,7 +917,7 @@ static void mgr_key_cmd(int argc, char *argv[], const char *method_name)
 
 	req->str = method_name;
 
-	l_dbus_proxy_method_call(node->mgmt_proxy, method_name,
+	l_dbus_proxy_method_call(local->mgmt_proxy, method_name,
 					mgr_key_setup, mgr_key_reply,
 					req, l_free);
 }
@@ -934,11 +966,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);
 		return;
 	}
 
 	app_idx = (uint16_t) req->arg2;
 	keys_add_app_key(net_idx, app_idx);
+	mesh_db_app_key_add(net_idx, app_idx);
 }
 
 static void import_appkey_setup(struct l_dbus_message *msg, void *user_data)
@@ -963,7 +997,7 @@ static void cmd_import_appkey(int argc, char *argv[])
 	struct generic_request *req;
 	size_t sz;
 
-	if (!node || !node->proxy || !node->mgmt_proxy) {
+	if (!local || !local->proxy || !local->mgmt_proxy) {
 		bt_shell_printf("Node is not attached\n");
 		return;
 	}
@@ -990,7 +1024,7 @@ static void cmd_import_appkey(int argc, char *argv[])
 
 	req->str = "ImportAppKey";
 
-	l_dbus_proxy_method_call(node->mgmt_proxy, "ImportAppKey",
+	l_dbus_proxy_method_call(local->mgmt_proxy, "ImportAppKey",
 					import_appkey_setup, add_key_reply,
 					req, free_generic_request);
 
@@ -1020,7 +1054,7 @@ static void cmd_import_subnet(int argc, char *argv[])
 	struct generic_request *req;
 	size_t sz;
 
-	if (!node || !node->proxy || !node->mgmt_proxy) {
+	if (!local || !local->proxy || !local->mgmt_proxy) {
 		bt_shell_printf("Node is not attached\n");
 		return;
 	}
@@ -1043,7 +1077,7 @@ static void cmd_import_subnet(int argc, char *argv[])
 
 	req->str = "ImportSubnet";
 
-	l_dbus_proxy_method_call(node->mgmt_proxy, "ImportSubnet",
+	l_dbus_proxy_method_call(local->mgmt_proxy, "ImportSubnet",
 					import_subnet_setup, add_key_reply,
 					req, free_generic_request);
 	return;
@@ -1067,7 +1101,7 @@ static void cmd_create_appkey(int argc, char *argv[])
 {
 	struct generic_request *req;
 
-	if (!node || !node->proxy || !node->mgmt_proxy) {
+	if (!local || !local->proxy || !local->mgmt_proxy) {
 		bt_shell_printf("Node is not attached\n");
 		return;
 	}
@@ -1087,7 +1121,7 @@ static void cmd_create_appkey(int argc, char *argv[])
 
 	req->str = "CreateAppKey";
 
-	l_dbus_proxy_method_call(node->mgmt_proxy, "CreateAppKey",
+	l_dbus_proxy_method_call(local->mgmt_proxy, "CreateAppKey",
 					create_appkey_setup, add_key_reply,
 					req, l_free);
 	return;
@@ -1158,7 +1192,7 @@ static void cmd_start_prov(int argc, char *argv[])
 static const struct bt_shell_menu main_menu = {
 	.name = "main",
 	.entries = {
-	{ "create", NULL, cmd_create_network,
+	{ "create", "[unicast_range_low]", cmd_create_network,
 			"Create new mesh network with one initial node" },
 	{ "discover-unprovisioned", "<on/off> [seconds]", cmd_scan_unprov,
 			"Look for devices to provision" },
@@ -1191,6 +1225,8 @@ static const struct bt_shell_menu main_menu = {
 			"Delete a remote node"},
 	{ "list-nodes", NULL, cmd_list_nodes,
 			"List remote mesh nodes"},
+	{ "keys", NULL, cmd_keys,
+			"List available keys"},
 	{ } },
 };
 
@@ -1203,6 +1239,16 @@ static void proxy_added(struct l_dbus_proxy *proxy, void *user_data)
 
 	if (!strcmp(interface, MESH_NETWORK_INTERFACE)) {
 		net_proxy = proxy;
+
+		/*
+		 * If mesh network configuration has been read from
+		 * storage, attach the provisioner/config-client node.
+		 */
+		if (local)
+			l_dbus_proxy_method_call(net_proxy, "Attach",
+						attach_node_setup,
+						attach_node_reply, NULL,
+						NULL);
 		return;
 	}
 
@@ -1387,6 +1433,7 @@ static struct l_dbus_message *req_prov_call(struct l_dbus *dbus,
 						void *user_data)
 {
 	uint8_t cnt;
+	uint16_t unicast;
 	struct l_dbus_message *reply;
 
 	if (!l_dbus_message_get_arguments(msg, "y", &cnt)) {
@@ -1395,10 +1442,19 @@ static struct l_dbus_message *req_prov_call(struct l_dbus *dbus,
 
 	}
 
+	unicast = remote_get_next_unicast(low_addr, high_addr, cnt);
+
+	if (unicast == 0) {
+		l_error("Failed to allocate addresses for %u elements\n", cnt);
+		return l_dbus_message_new_error(msg,
+					"org.freedesktop.DBus.Error."
+					"Failed to allocate address", NULL);
+	}
+
 	bt_shell_printf("Assign addresses for %u elements\n", cnt);
-	reply = l_dbus_message_new_method_return(msg);
 
-	l_dbus_message_set_arguments(reply, "qq", prov_net_idx, prov_address);
+	reply = l_dbus_message_new_method_return(msg);
+	l_dbus_message_set_arguments(reply, "qq", prov_net_idx, unicast);
 
 	return reply;
 }
@@ -1445,7 +1501,8 @@ static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus,
 
 	remove_device(uuid);
 
-	prov_address = unicast + cnt;
+	if (!mesh_db_add_node(uuid, cnt, unicast, prov_net_idx))
+		l_error("Failed to store new remote node");
 
 	return l_dbus_message_new_method_return(msg);
 }
@@ -1659,7 +1716,21 @@ static bool setup_cfg_storage(void)
 		if (!mesh_dir)
 			return false;
 
-		cfg_fname = l_strdup_printf("mesh_dir/%s", DEFAULT_CFG_FILE);
+		if (stat(mesh_dir, &st) == 0) {
+			if (!S_ISDIR(st.st_mode)) {
+				l_error("%s not a directory", mesh_dir);
+				return false;
+			}
+		} else if (errno == ENOENT) {
+			if (mkdir(mesh_dir, 0700) != 0)
+				return false;
+		} else {
+			perror("Cannot open config directory");
+			return false;
+		}
+
+		cfg_fname = l_strdup_printf("%s/%s", mesh_dir,
+							DEFAULT_CFG_FILE);
 		l_free(mesh_dir);
 
 	} else {
@@ -1681,6 +1752,35 @@ static bool setup_cfg_storage(void)
 	return true;
 }
 
+static bool read_mesh_config(void)
+{
+	uint16_t range_l, range_h;
+
+	if (!mesh_db_load(cfg_fname)) {
+		l_error("Failed to load config from %s", cfg_fname);
+		return false;
+	}
+
+	local = l_new(struct meshcfg_node, 1);
+
+	if (!mesh_db_get_token(local->token.u8)) {
+		l_error("Failed to read the provisioner's token ID");
+		l_error("Check config file %s", cfg_fname);
+		l_free(local);
+		local = NULL;
+
+		return false;
+	}
+
+	l_info("Mesh configuration loaded from %s", cfg_fname);
+	if (mesh_db_get_addr_range(&range_l, &range_h)) {
+		low_addr = range_l;
+		high_addr = range_h;
+	}
+
+	return true;
+}
+
 int main(int argc, char *argv[])
 {
 	struct l_dbus_client *client;
@@ -1689,14 +1789,34 @@ int main(int argc, char *argv[])
 
 	bt_shell_init(argc, argv, &opt);
 	bt_shell_set_menu(&main_menu);
-	bt_shell_set_prompt(PROMPT_OFF);
 
 	l_log_set_stderr();
 
 	if (address_opt && sscanf(address_opt, "%04x", &val) == 1)
-		prov_address = (uint16_t) val;
-	else
-		prov_address = DEFAULT_START_ADDRESS;
+		low_addr = (uint16_t) val;
+
+	if (low_addr > DEFAULT_MAX_ADDRESS) {
+		l_error("Invalid start address");
+			bt_shell_cleanup();
+			return EXIT_FAILURE;
+	}
+
+	if (!low_addr)
+		low_addr = DEFAULT_START_ADDRESS;
+
+	if (range_opt && sscanf(address_opt, "%04x", &val) == 1) {
+		if (val == 0) {
+			l_error("Invalid address range");
+			bt_shell_cleanup();
+			return EXIT_FAILURE;
+		}
+
+		/* Inclusive */
+		high_addr = low_addr + val - 1;
+	}
+
+	if (!high_addr || high_addr > DEFAULT_MAX_ADDRESS)
+		high_addr = DEFAULT_MAX_ADDRESS;
 
 	if (net_idx_opt && sscanf(net_idx_opt, "%04x", &val) == 1)
 		prov_net_idx = (uint16_t) val;
@@ -1708,6 +1828,13 @@ int main(int argc, char *argv[])
 		return EXIT_FAILURE;
 	}
 
+	if (have_config && !read_mesh_config()) {
+		bt_shell_cleanup();
+		return EXIT_FAILURE;
+	}
+
+	bt_shell_set_prompt(PROMPT_OFF);
+
 	dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS);
 
 	l_dbus_set_ready_handler(dbus, ready_callback, NULL, NULL);
diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index df2a23b05..f36f98ecc 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -34,6 +34,8 @@
 
 #include "tools/mesh/util.h"
 #include "tools/mesh/model.h"
+#include "tools/mesh/mesh-db.h"
+#include "tools/mesh/remote.h"
 #include "tools/mesh/config-model.h"
 #include "tools/mesh/cfgcli.h"
 
@@ -338,6 +340,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 							uint16_t len)
 {
 	uint32_t opcode;
+	const struct cfg_cmd *cmd;
 	uint16_t app_idx, net_idx, addr;
 	uint16_t ele_addr;
 	uint32_t mod_id;
@@ -356,9 +359,11 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 	req = get_req_by_rsp(src, (opcode & ~OP_UNRELIABLE));
 	if (req) {
+		cmd = req->cmd;
 		free_request(req);
 		l_queue_remove(requests, req);
-	}
+	} else
+		cmd = NULL;
 
 	switch (opcode & ~OP_UNRELIABLE) {
 	default:
@@ -384,6 +389,14 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		bt_shell_printf("NetKey\t%3.3x\n", net_idx);
 		bt_shell_printf("AppKey\t%3.3x\n", app_idx);
 
+		if (data[0] != MESH_STATUS_SUCCESS)
+			break;
+
+		if (cmd && cmd->opcode == OP_APPKEY_ADD) {
+			if (remote_add_app_key(src, app_idx))
+				mesh_db_node_app_key_add(src, app_idx);
+		}
+
 		break;
 
 	case OP_NETKEY_STATUS:
@@ -396,6 +409,14 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 
 		bt_shell_printf("\tNetKey %3.3x\n", net_idx);
 
+		if (data[0] != MESH_STATUS_SUCCESS)
+			break;
+
+		if (cmd && cmd->opcode == OP_NETKEY_ADD) {
+			if (remote_add_net_key(src, net_idx))
+				mesh_db_node_net_key_add(src, net_idx);
+		}
+
 		break;
 
 	case OP_MODEL_APP_STATUS:
diff --git a/tools/mesh/keys.c b/tools/mesh/keys.c
index 3c887b319..c14820c81 100644
--- a/tools/mesh/keys.c
+++ b/tools/mesh/keys.c
@@ -143,7 +143,7 @@ static void print_appkey(void *app_key, void *user_data)
 {
 	uint16_t app_idx = L_PTR_TO_UINT(app_key);
 
-	bt_shell_printf("%3.3x ", app_idx);
+	bt_shell_printf("%3.3x, ", app_idx);
 }
 
 static void print_netkey(void *net_key, void *user_data)
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
new file mode 100644
index 000000000..e6119c15d
--- /dev/null
+++ b/tools/mesh/mesh-db.c
@@ -0,0 +1,742 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2019  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/time.h>
+
+#include <ell/ell.h>
+#include <json-c/json.h>
+
+#include "mesh/mesh-defs.h"
+#include "mesh/util.h"
+
+#include "tools/mesh/keys.h"
+#include "tools/mesh/remote.h"
+#include "tools/mesh/mesh-db.h"
+
+#define KEY_IDX_INVALID NET_IDX_INVALID
+
+struct mesh_db {
+	json_object *jcfg;
+	char *cfg_fname;
+	uint8_t token[8];
+	uint8_t pad[12];
+	struct timeval write_time;
+};
+
+struct mesh_db *cfg;
+
+static json_object *get_node_by_unicast(uint16_t unicast)
+{
+	json_object *jarray;
+	int i, sz;
+
+	if (!json_object_object_get_ex(cfg->jcfg, "nodes", &jarray))
+		return NULL;
+
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return NULL;
+
+	sz = json_object_array_length(jarray);
+
+	for (i = 0; i < sz; ++i) {
+		json_object *jentry, *jval;
+		uint16_t addr;
+		const char *str;
+
+		jentry = json_object_array_get_idx(jarray, i);
+		if (!json_object_object_get_ex(jentry, "unicastAddress",
+								&jval))
+			return NULL;
+
+		str = json_object_get_string(jval);
+		if (sscanf(str, "%04hx", &addr) != 1)
+			continue;
+
+		if (addr == unicast)
+			return jentry;
+	}
+
+	return NULL;
+}
+
+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;
+
+	json_object_object_add(jobj, keyword, jval);
+	return true;
+}
+
+static bool write_uint16_hex(json_object *jobj, const char *desc,
+								uint16_t value)
+{
+	json_object *jstring;
+	char buf[5];
+
+	snprintf(buf, 5, "%4.4x", value);
+	jstring = json_object_new_string(buf);
+	if (!jstring)
+		return false;
+
+	json_object_object_add(jobj, desc, jstring);
+	return true;
+}
+
+static json_object *get_node_by_uuid(json_object *jcfg, uint8_t uuid[16])
+{
+	json_object *jarray = NULL;
+	char buf[33];
+	int i, sz;
+
+	hex2str(uuid, 16, buf, sizeof(buf));
+
+	json_object_object_get_ex(jcfg, "nodes", &jarray);
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return NULL;
+
+	sz = json_object_array_length(jarray);
+
+	for (i = 0; i < sz; ++i) {
+		json_object *jentry, *jval;
+		const char *str;
+
+		jentry = json_object_array_get_idx(jarray, i);
+		if (!json_object_object_get_ex(jentry, "uuid", &jval))
+			return NULL;
+
+		str = json_object_get_string(jval);
+		if (strlen(str) != 32)
+			continue;
+
+		if (!strcmp(buf, str))
+			return jentry;
+	}
+
+	return NULL;
+}
+
+static bool add_u8_8(json_object *jobj, const uint8_t value[8],
+							const char *desc)
+{
+	json_object *jstring;
+	char buf[17];
+
+	hex2str((uint8_t *) value, 8, buf, 17);
+	jstring = json_object_new_string(buf);
+	if (!jstring)
+		return false;
+
+	json_object_object_add(jobj, desc, jstring);
+	return true;
+}
+
+static bool add_u8_16(json_object *jobj, const uint8_t value[16],
+							const char *desc)
+{
+	json_object *jstring;
+	char buf[33];
+
+	hex2str((uint8_t *) value, 16, buf, 33);
+	jstring = json_object_new_string(buf);
+	if (!jstring)
+		return false;
+
+	json_object_object_add(jobj, desc, jstring);
+	return true;
+}
+
+static bool add_string(json_object *jobj, const char *str, const char *desc)
+{
+	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;
+	const char *str;
+
+	if (!token)
+		return false;
+
+	if (!json_object_object_get_ex(jobj, "token", &jval))
+		return false;
+
+	str = json_object_get_string(jval);
+	if (!str2hex(str, strlen(str), token, 8))
+		return false;
+
+	return true;
+}
+
+static uint16_t node_parse_key(json_object *jarray, int i)
+{
+	json_object *jkey;
+	const char *str;
+	uint16_t idx;
+
+	jkey = json_object_array_get_idx(jarray, i);
+	if (!jkey)
+		return -1;
+
+	str = json_object_get_string(jkey);
+
+	if (sscanf(str, "%04hx", &idx) != 1)
+		return KEY_IDX_INVALID;
+
+	return idx;
+}
+
+static void load_remotes(json_object *jcfg)
+{
+	json_object *jnodes;
+	int i, sz, node_count = 0;
+
+	json_object_object_get_ex(jcfg, "nodes", &jnodes);
+	if (!jnodes || json_object_get_type(jnodes) != json_type_array)
+		return;
+
+	sz = json_object_array_length(jnodes);
+
+	for (i = 0; i < sz; ++i) {
+		json_object *jnode, *jval, *jarray;
+		uint8_t uuid[16];
+		uint16_t unicast, key_idx;
+		const char *str;
+		int ele_cnt, key_cnt;
+		int j;
+
+		jnode = json_object_array_get_idx(jnodes, i);
+		if (!jnode)
+			continue;
+
+		if (!json_object_object_get_ex(jnode, "uuid", &jval))
+			continue;
+
+		str = json_object_get_string(jval);
+		if (strlen(str) != 32)
+			continue;
+
+		str2hex(str, 32, uuid, 16);
+
+		if (!json_object_object_get_ex(jnode, "unicastAddress", &jval))
+			continue;
+
+		str = json_object_get_string(jval);
+		if (sscanf(str, "%04hx", &unicast) != 1)
+			continue;
+
+		json_object_object_get_ex(jnode, "elements", &jarray);
+		if (!jarray || json_object_get_type(jarray) != json_type_array)
+			continue;
+
+		ele_cnt = json_object_array_length(jarray);
+
+		if (ele_cnt > MAX_ELE_COUNT)
+			continue;
+
+		json_object_object_get_ex(jnode, "netKeys", &jarray);
+		if (!jarray || json_object_get_type(jarray) != json_type_array)
+			continue;
+
+		key_cnt = json_object_array_length(jarray);
+		if (key_cnt < 0)
+			continue;
+
+		key_idx = node_parse_key(jarray, 0);
+		if (key_idx == KEY_IDX_INVALID)
+			continue;
+
+		remote_add_node((const uint8_t *)uuid, unicast, ele_cnt,
+								key_idx);
+		for (j = 1; j < key_cnt; j++) {
+			key_idx = node_parse_key(jarray, j);
+
+			if (key_idx != KEY_IDX_INVALID)
+				remote_add_net_key(unicast, key_idx);
+		}
+
+		json_object_object_get_ex(jnode, "appKeys", &jarray);
+		if (!jarray || json_object_get_type(jarray) != json_type_array)
+			continue;
+
+		key_cnt = json_object_array_length(jarray);
+
+		for (j = 0; j < key_cnt; j++) {
+			key_idx = node_parse_key(jarray, j);
+
+			if (key_idx != KEY_IDX_INVALID)
+				remote_add_app_key(unicast, key_idx);
+		}
+
+		node_count++;
+
+		/* TODO: Add the rest of the configuration */
+	}
+
+	if (node_count != sz)
+		l_warn("The remote node configuration load is incomplete!");
+
+}
+
+static bool add_app_key(json_object *jobj, uint16_t net_idx, uint16_t app_idx)
+{
+	json_object *jval, *jkey, *jarray;
+	char buf[5];
+
+	json_object_object_get_ex(jobj, "appKeys", &jarray);
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	jkey = json_object_new_object();
+
+	snprintf(buf, 5, "%4.4x", net_idx);
+	jval = json_object_new_string(buf);
+	if (!jval)
+		goto fail;
+
+	json_object_object_add(jkey, "bound", jval);
+
+	snprintf(buf, 5, "%4.4x", app_idx);
+	jval = json_object_new_string(buf);
+	if (!jval)
+		goto fail;
+
+	json_object_object_add(jkey, "index", jval);
+
+	json_object_array_add(jarray, jkey);
+
+	return true;
+fail:
+	json_object_put(jkey);
+	return false;
+}
+
+static bool add_key_idx(json_object *jobj, const char *desc, uint16_t idx)
+{
+	json_object *jval, *jarray;
+	char buf[5];
+
+	json_object_object_get_ex(jobj, desc, &jarray);
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	snprintf(buf, 5, "%4.4x", idx);
+
+	jval = json_object_new_string(buf);
+	if (!jval)
+		return false;
+
+	json_object_array_add(jarray, jval);
+
+	return true;
+}
+
+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;
+
+	if (!add_key_idx(jnode, "netKeys", idx))
+		return false;
+
+	return mesh_config_save((struct mesh_config *) cfg, true,
+								NULL, NULL);
+}
+
+bool mesh_db_node_app_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;
+
+	if (!add_key_idx(jnode, "appKeys", idx))
+		return false;
+
+	return mesh_config_save((struct mesh_config *) cfg, true,
+								NULL, NULL);
+}
+
+static bool load_keys(json_object *jobj)
+{
+	json_object *jarray, *jentry, *jvalue;
+	uint16_t net_idx, app_idx;
+	int i, key_cnt;
+
+	json_object_object_get_ex(jobj, "netKeys", &jarray);
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	key_cnt = json_object_array_length(jarray);
+	if (key_cnt < 0)
+		return false;
+
+	for (i = 0; i < key_cnt; ++i) {
+		const char *str;
+
+		jentry = json_object_array_get_idx(jarray, i);
+		str = json_object_get_string(jentry);
+
+		if (sscanf(str, "%04hx", &net_idx) != 1)
+			return false;
+
+		keys_add_net_key(net_idx);
+	}
+
+	json_object_object_get_ex(jobj, "appKeys", &jarray);
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	key_cnt = json_object_array_length(jarray);
+	if (key_cnt < 0)
+		return false;
+
+	for (i = 0; i < key_cnt; ++i) {
+		const char *str;
+
+		jentry = json_object_array_get_idx(jarray, i);
+
+		if (!json_object_object_get_ex(jentry, "bound", &jvalue))
+			return false;
+
+		str = json_object_get_string(jvalue);
+
+		if (sscanf(str, "%04hx", &net_idx) != 1)
+			return false;
+
+		if (!json_object_object_get_ex(jentry, "index", &jvalue))
+			return false;
+
+		str = json_object_get_string(jvalue);
+
+		if (sscanf(str, "%04hx", &app_idx) != 1)
+			return false;
+		keys_add_app_key(net_idx, app_idx);
+	}
+
+	return true;
+}
+
+bool mesh_db_net_key_add(uint16_t idx)
+{
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	if (!add_key_idx(cfg->jcfg, "netKeys", idx))
+		return false;
+
+	return mesh_config_save((struct mesh_config *) cfg, true,
+								NULL, NULL);
+}
+
+bool mesh_db_net_key_del(uint16_t idx)
+{
+	/* TODO */
+	return true;
+}
+
+bool mesh_db_app_key_add(uint16_t net_idx, uint16_t app_idx)
+{
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	if (!add_app_key(cfg->jcfg, net_idx, app_idx))
+		return false;
+
+	return mesh_config_save((struct mesh_config *) cfg, true,
+								NULL, NULL);
+}
+
+bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
+							uint16_t net_idx)
+{
+	json_object *jnode;
+	json_object *jelements, *jnodes, *jnetkeys, *jappkeys;
+	int i;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jnode = get_node_by_uuid(cfg->jcfg, uuid);
+	if (jnode) {
+		l_error("Node already exists");
+		return false;
+	}
+
+	jnode = json_object_new_object();
+	if (!jnode)
+		return false;
+
+	if (!add_u8_16(jnode, uuid, "uuid"))
+		goto fail;
+
+	jelements = json_object_new_array();
+	if (!jelements)
+		goto fail;
+
+	for (i = 0; i < num_els; ++i) {
+		json_object *jelement = json_object_new_object();
+
+		if (!jelement) {
+			json_object_put(jelements);
+			goto fail;
+		}
+
+		write_int(jelement, "elementIndex", i);
+		json_object_array_add(jelements, jelement);
+	}
+
+	json_object_object_add(jnode, "elements", jelements);
+
+	jnetkeys = json_object_new_array();
+	if (!jnetkeys)
+		goto fail;
+
+	json_object_object_add(jnode, "netKeys", jnetkeys);
+
+	if (!add_key_idx(jnode, "netKeys", net_idx))
+		goto fail;
+
+	jappkeys = json_object_new_array();
+	if (!jappkeys)
+		goto fail;
+
+	json_object_object_add(jnode, "appKeys", jappkeys);
+
+	if (!write_uint16_hex(jnode, "unicastAddress", unicast))
+		goto fail;
+
+	if (!json_object_object_get_ex(cfg->jcfg, "nodes", &jnodes))
+		goto fail;
+
+	json_object_array_add(jnodes, jnode);
+
+	if (!mesh_config_save((struct mesh_config *) cfg, true, NULL, NULL))
+		goto fail;
+
+	return true;
+
+fail:
+	json_object_put(jnode);
+	return false;
+}
+
+bool mesh_db_get_token(uint8_t token[8])
+{
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	memcpy(token, cfg->token, 8);
+
+	return true;
+}
+
+bool mesh_db_get_addr_range(uint16_t *low, uint16_t *high)
+{
+	json_object *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))
+		return false;
+
+	str = json_object_get_string(jlow);
+	if (sscanf(str, "%04hx", low) != 1)
+		return false;
+
+	str = json_object_get_string(jhigh);
+	if (sscanf(str, "%04hx", high) != 1)
+		return false;
+
+	return true;
+}
+
+bool mesh_db_set_addr_range(uint16_t low, uint16_t high)
+{
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	if (!write_uint16_hex(cfg->jcfg, "low", low))
+		return false;
+
+	if (!write_uint16_hex(cfg->jcfg, "high", high))
+		return false;
+
+	return mesh_config_save((struct mesh_config *) cfg, true, NULL, NULL);
+}
+
+bool mesh_db_create(const char *fname, const uint8_t token[8],
+							const char *mesh_name)
+{
+	json_object *jcfg, *jarray;
+	uint8_t uuid[16];
+
+	if (cfg)
+		return false;
+
+	if (!fname)
+		return false;
+
+	jcfg = json_object_new_object();
+	if (!jcfg)
+		return false;
+
+	cfg = l_new(struct mesh_db, 1);
+	cfg->jcfg = jcfg;
+	cfg->cfg_fname = l_strdup(fname);
+	memcpy(cfg->token, token, 8);
+
+	if (!add_u8_8(jcfg, token, "token"))
+		goto fail;
+
+	l_getrandom(uuid, 16);
+
+	if (!add_u8_16(jcfg, uuid, "uuid"))
+		goto fail;
+
+	if (mesh_name && !add_string(jcfg, mesh_name, "name"))
+		goto fail;
+
+	jarray = json_object_new_array();
+	if (!jarray)
+		goto fail;
+
+	json_object_object_add(jcfg, "nodes", jarray);
+
+	jarray = json_object_new_array();
+	if (!jarray)
+		goto fail;
+
+	json_object_object_add(jcfg, "netKeys", jarray);
+
+	jarray = json_object_new_array();
+	if (!jarray)
+		goto fail;
+
+	json_object_object_add(jcfg, "appKeys", jarray);
+
+	if (!mesh_config_save((struct mesh_config *) cfg, true, NULL, NULL))
+		goto fail;
+
+	return true;
+
+fail:
+	mesh_config_release((struct mesh_config *)cfg);
+	cfg = NULL;
+
+	return false;
+}
+
+bool mesh_db_load(const char *fname)
+{
+	int fd;
+	char *str;
+	struct stat st;
+	ssize_t sz;
+	json_object *jcfg;
+
+	fd = open(fname, O_RDONLY);
+	if (fd < 0)
+		return false;
+
+	if (fstat(fd, &st) == -1) {
+		close(fd);
+		return false;
+	}
+
+	str = (char *) l_new(char, st.st_size + 1);
+	if (!str) {
+		close(fd);
+		return false;
+	}
+
+	sz = read(fd, str, st.st_size);
+	if (sz != st.st_size) {
+		l_error("Failed to read configuration file %s", fname);
+		return false;
+	}
+
+	jcfg = json_tokener_parse(str);
+
+	close(fd);
+	l_free(str);
+
+	if (!jcfg)
+		return false;
+
+	cfg = l_new(struct mesh_db, 1);
+
+	cfg->jcfg = jcfg;
+	cfg->cfg_fname = l_strdup(fname);
+
+	if (!get_token(jcfg, cfg->token)) {
+		l_error("Configuration file missing token");
+		goto fail;
+	}
+
+	if (!load_keys(jcfg))
+		goto fail;
+
+	load_remotes(jcfg);
+
+	return true;
+fail:
+	mesh_config_release((struct mesh_config *)cfg);
+	cfg = NULL;
+	return false;
+}
diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h
new file mode 100644
index 000000000..4a7b16ab4
--- /dev/null
+++ b/tools/mesh/mesh-db.h
@@ -0,0 +1,54 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2019  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#include "mesh/mesh-config.h"
+
+bool mesh_db_create(const char *fname, const uint8_t token[8],
+							const char *name);
+bool mesh_db_load(const char *fname);
+
+bool mesh_db_get_token(uint8_t token[8]);
+
+bool mesh_db_net_key_add(uint16_t idx);
+bool mesh_db_net_key_del(uint16_t idx);
+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,
+							uint16_t net_idx);
+bool mesh_db_del_node(uint16_t unicast);
+bool mesh_db_node_set_composition(uint16_t unicast, uint16_t cid, uint16_t pid,
+						uint16_t vid, uint16_t crpl,
+						struct mesh_config_modes modes,
+						struct l_queue *elements);
+
+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_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_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,
+					uint32_t mod_id, uint16_t app_idx);
+bool mesh_db_node_model_binding_del(uint16_t unicast, uint8_t ele, bool vendor,
+					uint32_t mod_id, uint16_t app_idx);
diff --git a/tools/mesh/remote.c b/tools/mesh/remote.c
index 46839772f..7a598a546 100644
--- a/tools/mesh/remote.c
+++ b/tools/mesh/remote.c
@@ -29,13 +29,57 @@
 #include "mesh/mesh-defs.h"
 #include "tools/mesh/remote.h"
 
+struct remote_node {
+	uint16_t unicast;
+	struct l_queue *net_keys;
+	struct l_queue *app_keys;
+	uint8_t uuid[16];
+	uint8_t num_ele;
+};
+
 static struct l_queue *nodes;
 
-void remote_add_node(const uint8_t uuid[16], uint16_t unicast,
+static bool simple_match(const void *a, const void *b)
+{
+	return a == b;
+}
+
+static int compare_unicast(const void *a, const void *b, void *user_data)
+{
+	const struct remote_node *a_rmt = a;
+	const struct remote_node *b_rmt = b;
+
+	if (a_rmt->unicast < b_rmt->unicast)
+		return -1;
+
+	if (a_rmt->unicast > b_rmt->unicast)
+		return 1;
+
+	return 0;
+}
+
+static bool match_node_addr(const void *a, const void *b)
+{
+	const struct remote_node *rmt = a;
+	uint16_t addr = L_PTR_TO_UINT(b);
+
+	if (addr >= rmt->unicast &&
+				addr <= (rmt->unicast + rmt->num_ele - 1))
+		return true;
+
+	return false;
+}
+
+bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
 					uint8_t ele_cnt, uint16_t net_idx)
 {
-	struct remote_node *rmt = l_new(struct remote_node, 1);
+	struct remote_node *rmt;
 
+	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(unicast));
+	if (rmt)
+		return false;
+
+	rmt = l_new(struct remote_node, 1);
 	memcpy(rmt->uuid, uuid, 16);
 	rmt->unicast = unicast;
 	rmt->num_ele = ele_cnt;
@@ -46,19 +90,41 @@ void remote_add_node(const uint8_t uuid[16], uint16_t unicast,
 	if (!nodes)
 		nodes = l_queue_new();
 
-	l_queue_push_tail(nodes, rmt);
+	l_queue_insert(nodes, rmt, compare_unicast, NULL);
+	return true;
 }
 
-static bool match_node_addr(const void *a, const void *b)
+bool remote_add_net_key(uint16_t addr, uint16_t net_idx)
 {
-	const struct remote_node *rmt = a;
-	uint16_t addr = L_PTR_TO_UINT(b);
+	struct remote_node *rmt;
 
-	if (addr >= rmt->unicast &&
-				addr <= (rmt->unicast + rmt->num_ele - 1))
-		return true;
+	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
+	if (!rmt)
+		return false;
 
-	return false;
+	if (l_queue_find(rmt->net_keys, simple_match, L_UINT_TO_PTR(net_idx)))
+		return false;
+
+	l_queue_push_tail(rmt->net_keys, L_UINT_TO_PTR(net_idx));
+	return true;
+}
+
+bool remote_add_app_key(uint16_t addr, uint16_t app_idx)
+{
+	struct remote_node *rmt;
+
+	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
+	if (!rmt)
+		return false;
+
+	if (!rmt->app_keys)
+		rmt->app_keys = l_queue_new();
+
+	if (l_queue_find(rmt->app_keys, simple_match, L_UINT_TO_PTR(app_idx)))
+		return false;
+
+	l_queue_push_tail(rmt->app_keys, L_UINT_TO_PTR(app_idx));
+	return true;
 }
 
 uint16_t remote_get_subnet_idx(uint16_t addr)
@@ -76,11 +142,11 @@ uint16_t remote_get_subnet_idx(uint16_t addr)
 	return (uint16_t) net_idx;
 }
 
-static void print_subnet(void *net_key, void *user_data)
+static void print_key(void *net_key, void *user_data)
 {
 	uint16_t net_idx = L_PTR_TO_UINT(net_key);
 
-	bt_shell_printf("%3.3x ", net_idx);
+	bt_shell_printf("%3.3x, ", net_idx);
 }
 
 static void print_node(void *rmt, void *user_data)
@@ -97,8 +163,14 @@ static void print_node(void *rmt, void *user_data)
 	bt_shell_printf("\t" COLOR_GREEN "elements = %u\n" COLOR_OFF,
 								node->num_ele);
 	bt_shell_printf("\t" COLOR_GREEN "net_keys = ");
-	l_queue_foreach(node->net_keys, print_subnet, NULL);
+	l_queue_foreach(node->net_keys, print_key, NULL);
 	bt_shell_printf("\n" COLOR_OFF);
+
+	if (node->app_keys && !l_queue_isempty(node->app_keys)) {
+		bt_shell_printf("\t" COLOR_GREEN "app_keys = ");
+		l_queue_foreach(node->app_keys, print_key, NULL);
+		bt_shell_printf("\n" COLOR_OFF);
+	}
 }
 
 void remote_print_node(uint16_t addr)
@@ -122,3 +194,36 @@ void remote_print_all(void)
 
 	l_queue_foreach(nodes, print_node, NULL);
 }
+
+uint16_t remote_get_next_unicast(uint16_t low, uint16_t high, uint8_t ele_cnt)
+{
+	struct remote_node *rmt;
+	const struct l_queue_entry *l;
+	uint16_t addr;
+
+	/* Note: the address space includes both low and high terminal values */
+	if (ele_cnt > (high - low + 1))
+		return 0;
+
+	if (!nodes || l_queue_isempty(nodes))
+		return low;
+
+	addr = low;
+	l = l_queue_get_entries(nodes);
+
+	/* Cycle through the sorted (by unicast) node list */
+	for (; l; l = l->next) {
+		rmt = l->data;
+
+		if (rmt->unicast >= (addr + ele_cnt))
+			return addr;
+
+		if ((rmt->unicast + rmt->num_ele) > addr)
+			addr = rmt->unicast + rmt->num_ele;
+	}
+
+	if ((addr + ele_cnt - 1) <= high)
+		return addr;
+
+	return 0;
+}
diff --git a/tools/mesh/remote.h b/tools/mesh/remote.h
index dce1619c0..ad0da9f11 100644
--- a/tools/mesh/remote.h
+++ b/tools/mesh/remote.h
@@ -17,15 +17,11 @@
  *
  */
 
-struct remote_node {
-	uint16_t unicast;
-	struct l_queue *net_keys;
-	uint8_t uuid[16];
-	uint8_t num_ele;
-};
-
-void remote_add_node(const uint8_t uuid[16], uint16_t unicast,
+bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
 					uint8_t ele_cnt, uint16_t net_idx);
+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_app_key(uint16_t addr, uint16_t app_idx);
 uint16_t remote_get_subnet_idx(uint16_t addr);
 void remote_print_node(uint16_t addr);
 void remote_print_all(void);
-- 
2.21.0


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

* [PATCH BlueZ 10/10] tools/mesh-cfgclient: Add README file
  2019-10-29 20:40 [PATCH BlueZ 00/10] Mesh provisioning and configuration tool Inga Stotland
                   ` (8 preceding siblings ...)
  2019-10-29 20:40 ` [PATCH BlueZ 09/10] tools/mesh: add initial support for config storage Inga Stotland
@ 2019-10-29 20:40 ` Inga Stotland
  9 siblings, 0 replies; 11+ messages in thread
From: Inga Stotland @ 2019-10-29 20:40 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: brian.gix, Inga Stotland

---
 tools/mesh/README | 54 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 tools/mesh/README

diff --git a/tools/mesh/README b/tools/mesh/README
new file mode 100644
index 000000000..9362d8ff6
--- /dev/null
+++ b/tools/mesh/README
@@ -0,0 +1,54 @@
+MeshCfgclient - BlueZ PB-Adv based Bluetooth Mesh Provisioner
+*************************************************************
+
+Copyright (C) 2019  Intel Corporation. All rights reserved.
+
+Compilation and installation
+============================
+
+In addition to main BlueZ requirements, MeshCfgclient needs the following:
+	- JSON library
+
+Configuration and options
+=========================
+
+	--enable-mesh
+
+		Build mesh-cfgclient and other Bluetooth Mesh based tools
+
+Storage for mesh configuration file
+===================================
+
+The mesh-cfgclient tool generates a mesh configuration file in JSON format:
+	- mesh_db.json
+that contains information about the current state of the configured mesh
+network.
+
+The default directory for mesh-cfgclient configuration file is
+/home/<username>/.config/mesh-cfgcli
+
+To specify a custom storage directory, run mesh-config tool as:
+
+	meshctl -c <config_dir_name>
+
+If a configuration file is not found, it is assumed that a mesh network
+does not exist. In this case, the tool may be used to generate a new  mesh
+network by invoking "create" command from the main menu and, on a successful
+completion of this command, an initial configuration file is written.
+
+If the configuration file is present, then "create" command will fail. This
+is done so that the existing configuration is not accidentally overwritten.
+If the intention is to create a new network then, the existing mesh
+configuration file has to be either moved or dleted prior to running the
+mesh-cfgclient tool. Also, a new custom storage location may be
+specified for a new network on the start up as a command line option.
+
+
+Information
+===========
+
+Mailing lists:
+	linux-bluetooth@vger.kernel.org
+
+For additional information about the project visit BlueZ web site:
+	http://www.bluez.org
-- 
2.21.0


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

end of thread, other threads:[~2019-10-29 20:41 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-29 20:40 [PATCH BlueZ 00/10] Mesh provisioning and configuration tool Inga Stotland
2019-10-29 20:40 ` [PATCH BlueZ 01/10] tools/mesh: Remove extra dependencies from agent.c Inga Stotland
2019-10-29 20:40 ` [PATCH BlueZ 02/10] tools/mesh: Move meshctl specific files to mesh-gatt Inga Stotland
2019-10-29 20:40 ` [PATCH BlueZ 03/10] tools: Add mesh-cfgclient tool Inga Stotland
2019-10-29 20:40 ` [PATCH BlueZ 04/10] tools/mesh: move remote node processing to a separate file Inga Stotland
2019-10-29 20:40 ` [PATCH BlueZ 05/10] tools/mesh-cfgclient: Commands for Management iface methods Inga Stotland
2019-10-29 20:40 ` [PATCH BlueZ 06/10] tools/mesh-cfgclient: Add config menu key commands Inga Stotland
2019-10-29 20:40 ` [PATCH BlueZ 07/10] tools/mesh-cfgclient: Add timeout for expected response Inga Stotland
2019-10-29 20:40 ` [PATCH BlueZ 08/10] tools/mesh-cfgclient: Command line option for config Inga Stotland
2019-10-29 20:40 ` [PATCH BlueZ 09/10] tools/mesh: add initial support for config storage Inga Stotland
2019-10-29 20:40 ` [PATCH BlueZ 10/10] tools/mesh-cfgclient: Add README file Inga Stotland

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).