linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2] mesh: Added ImportLocalNode call with its API
@ 2019-06-25 14:38 Jakub Witowski
  2019-06-26 17:01 ` Gix, Brian
  0 siblings, 1 reply; 9+ messages in thread
From: Jakub Witowski @ 2019-06-25 14:38 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Inga Stotland

This implements ImportLocalNode() method on org.bluez.mesh.Network1
interface. Invoking this method creates a self-provisioned node based on
passed JSON definition. Also full functionality of import local node has
been implemented
---
 doc/mesh-api.txt |  22 ++++-
 mesh/mesh.c      |  63 ++++++++++++--
 mesh/node.c      | 212 +++++++++++++++++++++++++++++++++++++++++++----
 mesh/node.h      |   2 +
 4 files changed, 276 insertions(+), 23 deletions(-)

diff --git a/doc/mesh-api.txt b/doc/mesh-api.txt
index 4e0a8bff1..539fc3e4f 100644
--- a/doc/mesh-api.txt
+++ b/doc/mesh-api.txt
@@ -151,11 +151,21 @@ Methods:
 			org.bluez.mesh.Error.InvalidArguments
 			org.bluez.mesh.Error.AlreadyExists,
 
-	 uint64 token ImportLocalNode(string json_data)
+	 uint64 token ImportLocalNode(object app_root, string json_data, array{byte}[16] uuid)
 
 		This method creates a local mesh node based on node
 		configuration that has been generated outside bluetooth-meshd.
 
+		The app_root parameter is a D-Bus object root path of the
+		application that implements org.bluez.mesh.Application1
+		interface, and a org.bluez.mesh.Provisioner1 interface. The
+		application represents a node where child mesh elements have
+		their own objects that implement org.bluez.mesh.Element1
+		interface. The application hierarchy also contains a provision
+		agent object that implements org.bluez.mesh.ProvisionAgent1
+		interface. The standard DBus.ObjectManager interface must be
+		available on the app_root path.
+
 		The json_data parameter is a full JSON representation of a node
 		configuration file. The format must conform to the schema
 		defined in "Mesh Node Configuration Schema" section. Any
@@ -1059,4 +1069,12 @@ Properties:
 
 Mesh Node Configuration Schema
 ==============================
-<TBD>
+Example of Json format for ImportLocalNode():
+{
+  "IVindex":0,
+  "IVupdate":0,
+  "unicastAddress":"0012",
+  "deviceKey":"7daa45cd1e9e11a4b86eeef7d01efa11",
+  "netKey":"1234567890abcdef1234567890abcdef",
+  "keyRefresh":0
+}
diff --git a/mesh/mesh.c b/mesh/mesh.c
index 26acfd4dc..e0f0e4bf9 100644
--- a/mesh/mesh.c
+++ b/mesh/mesh.c
@@ -22,6 +22,7 @@
 #endif
 
 #define _GNU_SOURCE
+#include <json-c/json.h>
 #include <ell/ell.h>
 
 #include "mesh/mesh-io.h"
@@ -60,7 +61,7 @@ struct bt_mesh {
 	uint8_t max_filters;
 };
 
-struct join_data{
+struct join_data {
 	struct l_dbus_message *msg;
 	struct mesh_agent *agent;
 	const char *sender;
@@ -365,8 +366,8 @@ static void node_init_cb(struct mesh_node *node, struct mesh_agent *agent)
 
 	if (!acceptor_start(num_ele, join_pending->uuid, mesh.algorithms,
 				mesh.prov_timeout, agent, prov_complete_cb,
-				&mesh))
-	{
+				&mesh)) {
+
 		reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED,
 				"Failed to start provisioning acceptor");
 		goto fail;
@@ -536,7 +537,7 @@ static struct l_dbus_message *leave_call(struct l_dbus *dbus,
 	return l_dbus_message_new_method_return(msg);
 }
 
-static void create_network_ready_cb(void *user_data, int status,
+static void create_node_ready_cb(void *user_data, int status,
 							struct mesh_node *node)
 {
 	struct l_dbus_message *reply;
@@ -593,12 +594,58 @@ static struct l_dbus_message *create_network_call(struct l_dbus *dbus,
 
 	l_queue_push_tail(pending_queue, pending_msg);
 
-	node_create(app_path, sender, uuid, create_network_ready_cb,
+	node_create(app_path, sender, uuid, create_node_ready_cb,
 								pending_msg);
 
 	return NULL;
 }
 
+static struct l_dbus_message *import_local_node_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	const char *app_path, *sender;
+	struct l_dbus_message *pending_msg;
+	struct l_dbus_message_iter iter_uuid;
+	const char *json_data;
+	uint8_t *uuid;
+	uint32_t n;
+	struct json_object *jnode;
+
+	l_debug("Import local node request");
+
+	if (!l_dbus_message_get_arguments(msg, "osay", &app_path,
+							&json_data, &iter_uuid))
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+	if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n) ||
+									n != 16)
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad dev UUID");
+
+	if (node_find_by_uuid(uuid))
+		return dbus_error(msg, MESH_ERROR_ALREADY_EXISTS,
+							"Node already exists");
+
+	jnode = json_tokener_parse(json_data);
+
+	sender = l_dbus_message_get_sender(msg);
+	pending_msg = l_dbus_message_ref(msg);
+
+	if (!pending_queue)
+		pending_queue = l_queue_new();
+
+	l_queue_push_tail(pending_queue, pending_msg);
+
+	if (!node_import(app_path, sender, jnode, uuid, create_node_ready_cb,
+								pending_msg)) {
+		l_dbus_message_unref(msg);
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+							"Node import failed");
+	}
+
+	return NULL;
+}
+
 static void setup_network_interface(struct l_dbus_interface *iface)
 {
 	l_dbus_interface_method(iface, "Join", 0, join_network_call, "",
@@ -612,8 +659,14 @@ static void setup_network_interface(struct l_dbus_interface *iface)
 
 	l_dbus_interface_method(iface, "Leave", 0, leave_call, "", "t",
 								"token");
+
 	l_dbus_interface_method(iface, "CreateNetwork", 0, create_network_call,
 					"t", "oay", "token", "app", "uuid");
+
+	l_dbus_interface_method(iface, "ImportLocalNode", 0,
+					import_local_node_call,
+					"t", "osay", "token",
+					"app", "json_data", "uuid");
 }
 
 bool mesh_dbus_init(struct l_dbus *dbus)
diff --git a/mesh/node.c b/mesh/node.c
index e99858623..991802a6f 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -27,6 +27,7 @@
 
 #include <ell/ell.h>
 #include <json-c/json.h>
+#include <stdio.h>
 
 #include "mesh/mesh-defs.h"
 #include "mesh/mesh.h"
@@ -58,9 +59,12 @@
 #define DEFAULT_CRPL 10
 #define DEFAULT_SEQUENCE_NUMBER 0
 
-#define REQUEST_TYPE_JOIN 0
-#define REQUEST_TYPE_ATTACH 1
-#define REQUEST_TYPE_CREATE 2
+enum request_type {
+	REQUEST_TYPE_JOIN = 0,
+	REQUEST_TYPE_ATTACH,
+	REQUEST_TYPE_CREATE,
+	REQUEST_TYPE_IMPORT,
+};
 
 struct node_element {
 	char *path;
@@ -111,7 +115,18 @@ struct managed_obj_request {
 	void *data;
 	void *cb;
 	void *user_data;
-	uint8_t type;
+	enum request_type type;
+};
+
+struct node_import_request {
+	uint8_t uuid[16];
+	uint8_t dev_key[16];
+	uint8_t net_key[16];
+	bool kr;
+	uint16_t unicast;
+	uint32_t iv_idx;
+	bool iv_update;
+	void *user_data;
 };
 
 static struct l_queue *nodes;
@@ -851,7 +866,7 @@ element_done:
 #define MIN_COMPOSITION_LEN 16
 
 bool node_parse_composition(struct mesh_node *node, uint8_t *data,
-								uint16_t len)
+			    uint16_t len)
 {
 	struct node_composition *comp;
 	uint16_t features;
@@ -946,7 +961,7 @@ bool node_parse_composition(struct mesh_node *node, uint8_t *data,
 			vendor_id = l_get_le16(data);
 			mod_id |= (vendor_id << 16);
 			mod = mesh_model_vendor_new(ele->idx, vendor_id,
-									mod_id);
+						    mod_id);
 			if (!mod) {
 				element_free(ele);
 				goto fail;
@@ -977,7 +992,6 @@ fail:
 
 	return false;
 }
-
 static void attach_io(void *a, void *b)
 {
 	struct mesh_node *node = a;
@@ -1078,6 +1092,7 @@ static bool validate_model_property(struct node_element *ele,
 		while (l_dbus_message_iter_next_entry(&ids, &vendor_id,
 								&mod_id)) {
 			struct mesh_model *mod;
+
 			mod = l_queue_find(ele->models, match_model_id,
 				L_UINT_TO_PTR((vendor_id << 16) | mod_id));
 			if (!mod)
@@ -1366,17 +1381,92 @@ static bool get_app_properties(struct mesh_node *node, const char *path,
 	return true;
 }
 
-static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr,
-				bool ivu, uint32_t iv_idx, uint8_t dev_key[16],
-				uint16_t net_key_idx, uint8_t net_key[16])
+static bool parse_imported_iv_index(json_object *jobj, uint32_t *idx,
+								bool *update)
 {
-	node->net = mesh_net_new(node);
+	int tmp;
+	json_object *jvalue;
 
-	if (!nodes)
-		nodes = l_queue_new();
+	if (!json_object_object_get_ex(jobj, "IVindex", &jvalue))
+		return false;
 
-	l_queue_push_tail(nodes, node);
+	tmp = json_object_get_int(jvalue);
+	*idx = (uint32_t) tmp;
+
+	if (!json_object_object_get_ex(jobj, "IVupdate", &jvalue))
+		return false;
+
+	tmp = json_object_get_int(jvalue);
+	*update = (bool)tmp;
+
+	return true;
+}
+
+static bool parse_imported_unicast_addr(json_object *jobj, uint16_t *unicast)
+{
+	json_object *jvalue;
+	char *str;
+
+	if (!json_object_object_get_ex(jobj, "unicastAddress", &jvalue))
+		return false;
+
+	str = (char *)json_object_get_string(jvalue);
+
+	if (sscanf(str, "%04hx", unicast) != 1)
+		return false;
+
+	return true;
+}
 
+static bool parse_imported_device_key(json_object *jobj, uint8_t key_buf[16])
+{
+	json_object *jvalue;
+	char *str;
+
+	if (!key_buf)
+		return false;
+
+	if (!json_object_object_get_ex(jobj, "deviceKey", &jvalue))
+		return false;
+
+	str = (char *)json_object_get_string(jvalue);
+
+	if (!str2hex(str, strlen(str), key_buf, 16))
+		return false;
+
+	return true;
+}
+
+static bool parse_imported_net_key(json_object *jobj, uint8_t key_buf[16],
+								bool *kr)
+{
+	json_object *jvalue;
+	char *str;
+
+	if (!key_buf)
+		return false;
+
+	if (!json_object_object_get_ex(jobj, "netKey", &jvalue))
+		return false;
+
+	str = (char *)json_object_get_string(jvalue);
+
+	if (!str2hex(str, strlen(str), key_buf, 16))
+		return false;
+
+	/* Get key refresh */
+	if (!json_object_object_get_ex(jobj, "keyRefresh", &jvalue))
+		return false;
+
+	*kr = (bool)json_object_get_boolean(jvalue);
+	return true;
+}
+
+
+static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr,
+				bool ivu, uint32_t iv_idx, uint8_t dev_key[16],
+				uint16_t net_key_idx, uint8_t net_key[16])
+{
 	if (!storage_set_iv_index(node->net, iv_idx, ivu))
 		return false;
 
@@ -1444,14 +1534,13 @@ static void get_managed_objects_cb(struct l_dbus_message *msg, void *user_data)
 	}
 
 	if (is_new) {
-		node = l_new(struct mesh_node, 1);
+		node = node_new(req->data);
 		node->elements = l_queue_new();
 	} else {
 		node = req->data;
 	}
 
 	num_ele = 0;
-
 	while (l_dbus_message_iter_next_entry(&objects, &path, &interfaces)) {
 		struct l_dbus_message_iter properties;
 		const char *interface;
@@ -1547,6 +1636,44 @@ static void get_managed_objects_cb(struct l_dbus_message *msg, void *user_data)
 
 		cb(node, agent);
 
+	} else if (req->type == REQUEST_TYPE_IMPORT) {
+
+		node_ready_func_t cb = req->cb;
+		struct node_import_request *import_data = req->user_data;
+		struct keyring_net_key net_key;
+
+		if (!agent) {
+			l_error("Interface %s not found",
+						MESH_PROVISION_AGENT_INTERFACE);
+			goto fail;
+		}
+
+		node->num_ele = num_ele;
+		set_defaults(node);
+		memcpy(node->uuid, import_data->uuid, 16);
+
+		if (!create_node_config(node))
+			goto fail;
+
+		if (!add_local_node(node, import_data->unicast, import_data->kr,
+				import_data->iv_update, import_data->iv_idx,
+				import_data->dev_key, PRIMARY_NET_IDX,
+							import_data->net_key))
+			goto fail;
+
+		memcpy(net_key.old_key, import_data->net_key, 16);
+		net_key.net_idx = PRIMARY_NET_IDX;
+		net_key.phase = KEY_REFRESH_PHASE_NONE;
+
+		if (!keyring_put_remote_dev_key(node, import_data->unicast,
+						num_ele, import_data->dev_key))
+			goto fail;
+
+		if (!keyring_put_net_key(node, PRIMARY_NET_IDX, &net_key))
+			goto fail;
+
+		cb(import_data->user_data, MESH_ERROR_NONE, node);
+
 	} else {
 		/* Callback for create node request */
 		node_ready_func_t cb = req->cb;
@@ -1672,6 +1799,59 @@ void node_join(const char *app_path, const char *sender, const uint8_t *uuid,
 					req, l_free);
 }
 
+
+bool node_import(const char *app_path, const char *sender, void *json_data,
+		const uint8_t *uuid, node_ready_func_t cb, void *user_data)
+{
+	struct managed_obj_request *req;
+	struct node_import_request *node;
+
+	l_debug("");
+	node = l_new(struct node_import_request, 1);
+	req = l_new(struct managed_obj_request, 1);
+
+	if (!parse_imported_device_key(json_data, node->dev_key)) {
+		l_error("Failed to parse imported device key");
+		goto fail;
+	}
+
+	if (!parse_imported_unicast_addr(json_data, &node->unicast)) {
+		l_error("Failed to parse imported unicast address");
+		goto fail;
+	}
+
+	if (!parse_imported_iv_index(json_data, &node->iv_idx,
+							&node->iv_update)) {
+		l_error("Failed to parse imported iv idx");
+		goto fail;
+	}
+
+
+	if (!parse_imported_net_key(json_data, node->net_key, &node->kr)) {
+		l_error("Failed to parse imported network key");
+		goto fail;
+	}
+
+	node->user_data = user_data;
+
+	memcpy(node->uuid, uuid, 16);
+	req->data = (void *) uuid;
+	req->user_data = node;
+	req->cb = cb;
+	req->type = REQUEST_TYPE_IMPORT;
+
+	l_dbus_method_call(dbus_get_bus(), sender, app_path,
+					L_DBUS_INTERFACE_OBJECT_MANAGER,
+					"GetManagedObjects", NULL,
+					get_managed_objects_cb,
+					req, l_free);
+	return true;
+fail:
+	json_object_put(json_data);
+	l_free(node);
+	return false;
+}
+
 void node_create(const char *app_path, const char *sender, const uint8_t *uuid,
 					node_ready_func_t cb, void *user_data)
 {
diff --git a/mesh/node.h b/mesh/node.h
index 142527b30..9559f9178 100644
--- a/mesh/node.h
+++ b/mesh/node.h
@@ -91,6 +91,8 @@ void node_build_attach_reply(struct mesh_node *node,
 						struct l_dbus_message *reply);
 void node_create(const char *app_path, const char *sender, const uint8_t *uuid,
 					node_ready_func_t cb, void *user_data);
+bool node_import(const char *app_path, const char *sender, void *jnode,
+		const uint8_t *uuid, node_ready_func_t cb, void *user_data);
 void node_id_set(struct mesh_node *node, uint16_t node_id);
 uint16_t node_id_get(struct mesh_node *node);
 bool node_dbus_init(struct l_dbus *bus);
-- 
2.20.1


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

* Re: [PATCH v2] mesh: Added ImportLocalNode call with its API
  2019-06-25 14:38 [PATCH v2] mesh: Added ImportLocalNode call with its API Jakub Witowski
@ 2019-06-26 17:01 ` Gix, Brian
  2019-06-26 17:53   ` Stotland, Inga
  0 siblings, 1 reply; 9+ messages in thread
From: Gix, Brian @ 2019-06-26 17:01 UTC (permalink / raw)
  To: jakub.witowski, linux-bluetooth; +Cc: michal.lowas-rzechonek, Stotland, Inga

Hi Jakub,


On Tue, 2019-06-25 at 16:38 +0200, Jakub Witowski wrote:
> This implements ImportLocalNode() method on org.bluez.mesh.Network1
> interface. Invoking this method creates a self-provisioned node based on
> passed JSON definition. Also full functionality of import local node has
> been implemented
> ---
>  doc/mesh-api.txt |  22 ++++-
>  mesh/mesh.c      |  63 ++++++++++++--
>  mesh/node.c      | 212 +++++++++++++++++++++++++++++++++++++++++++----
>  mesh/node.h      |   2 +
>  4 files changed, 276 insertions(+), 23 deletions(-)
> 
> diff --git a/doc/mesh-api.txt b/doc/mesh-api.txt
> index 4e0a8bff1..539fc3e4f 100644
> --- a/doc/mesh-api.txt
> +++ b/doc/mesh-api.txt
> @@ -151,11 +151,21 @@ Methods:
>  			org.bluez.mesh.Error.InvalidArguments
>  			org.bluez.mesh.Error.AlreadyExists,
>  
> -	 uint64 token ImportLocalNode(string json_data)
> +	 uint64 token ImportLocalNode(object app_root, string json_data, array{byte}[16] uuid)
>  
>  		This method creates a local mesh node based on node
>  		configuration that has been generated outside bluetooth-meshd.
>  
> +		The app_root parameter is a D-Bus object root path of the
> +		application that implements org.bluez.mesh.Application1
> +		interface, and a org.bluez.mesh.Provisioner1 interface. The
> +		application represents a node where child mesh elements have
> +		their own objects that implement org.bluez.mesh.Element1
> +		interface. The application hierarchy also contains a provision
> +		agent object that implements org.bluez.mesh.ProvisionAgent1
> +		interface. The standard DBus.ObjectManager interface must be
> +		available on the app_root path.
> +
>  		The json_data parameter is a full JSON representation of a node
>  		configuration file. The format must conform to the schema
>  		defined in "Mesh Node Configuration Schema" section. Any
> @@ -1059,4 +1069,12 @@ Properties:
>  
>  Mesh Node Configuration Schema
>  ==============================
> -<TBD>
> +Example of Json format for ImportLocalNode():
> +{
> +  "IVindex":0,
> +  "IVupdate":0,
> +  "unicastAddress":"0012",
> +  "deviceKey":"7daa45cd1e9e11a4b86eeef7d01efa11",
> +  "netKey":"1234567890abcdef1234567890abcdef",
> +  "keyRefresh":0
> +}
> diff --git a/mesh/mesh.c b/mesh/mesh.c
> index 26acfd4dc..e0f0e4bf9 100644
> --- a/mesh/mesh.c
> +++ b/mesh/mesh.c
> @@ -22,6 +22,7 @@
>  #endif
>  
>  #define _GNU_SOURCE
> +#include <json-c/json.h>

We are trying to figure out a way to *isolate* JSON dependancies in the Mesh daemon, such that
if it needs to be ported to a platform that does not support the JSON library, it can be easily
pared out while minimizing impact to the rest of the system.

Inga and I have been having conversations about this.

* It should be possible to create a JSON-free daemon, if the internal JSON storage system is replaced with a
custom, more space-efficient node storage, or for any other reason.

* It should be possible to *remove* the ImportLocalNode() entirely if that functionality is not needed, and
with it the JSON dependancy.

* We may want to be able to support *other* formats for this DBUS facing interface (perhaps XML, perhaps
something vendor defined).


I don't think there is any question that the *location* for this method belongs exactly where you have it,
however I think we would like to see the JSON parsing moved to a file that already has the JSON dependancy.  We
may want to let the daemon auto-detect the format (returning an error if the format is not understood). 
However, then perhaps having a "Table of Parsers" that do not live within the mesh.c file...  but perhaps in a
format-parser.h file which can be easily customized to support other parsers.


>  #include <ell/ell.h>
>  
>  #include "mesh/mesh-io.h"
> @@ -60,7 +61,7 @@ struct bt_mesh {
>  	uint8_t max_filters;
>  };
>  
> -struct join_data{
> +struct join_data {
>  	struct l_dbus_message *msg;
>  	struct mesh_agent *agent;
>  	const char *sender;
> @@ -365,8 +366,8 @@ static void node_init_cb(struct mesh_node *node, struct mesh_agent *agent)
>  
>  	if (!acceptor_start(num_ele, join_pending->uuid, mesh.algorithms,
>  				mesh.prov_timeout, agent, prov_complete_cb,
> -				&mesh))
> -	{
> +				&mesh)) {
> +
>  		reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED,
>  				"Failed to start provisioning acceptor");
>  		goto fail;
> @@ -536,7 +537,7 @@ static struct l_dbus_message *leave_call(struct l_dbus *dbus,
>  	return l_dbus_message_new_method_return(msg);
>  }
>  
> -static void create_network_ready_cb(void *user_data, int status,
> +static void create_node_ready_cb(void *user_data, int status,
>  							struct mesh_node *node)
>  {
>  	struct l_dbus_message *reply;
> @@ -593,12 +594,58 @@ static struct l_dbus_message *create_network_call(struct l_dbus *dbus,
>  
>  	l_queue_push_tail(pending_queue, pending_msg);
>  
> -	node_create(app_path, sender, uuid, create_network_ready_cb,
> +	node_create(app_path, sender, uuid, create_node_ready_cb,
>  								pending_msg);
>  
>  	return NULL;
>  }
>  
> +static struct l_dbus_message *import_local_node_call(struct l_dbus *dbus,
> +						struct l_dbus_message *msg,
> +						void *user_data)
> +{
> +	const char *app_path, *sender;
> +	struct l_dbus_message *pending_msg;
> +	struct l_dbus_message_iter iter_uuid;
> +	const char *json_data;
> +	uint8_t *uuid;
> +	uint32_t n;
> +	struct json_object *jnode;
> +
> +	l_debug("Import local node request");
> +
> +	if (!l_dbus_message_get_arguments(msg, "osay", &app_path,
> +							&json_data, &iter_uuid))
> +		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
> +
> +	if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n) ||
> +									n != 16)
> +		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad dev UUID");
> +
> +	if (node_find_by_uuid(uuid))
> +		return dbus_error(msg, MESH_ERROR_ALREADY_EXISTS,
> +							"Node already exists");
> +
> +	jnode = json_tokener_parse(json_data);
> +
> +	sender = l_dbus_message_get_sender(msg);
> +	pending_msg = l_dbus_message_ref(msg);
> +
> +	if (!pending_queue)
> +		pending_queue = l_queue_new();
> +
> +	l_queue_push_tail(pending_queue, pending_msg);
> +
> +	if (!node_import(app_path, sender, jnode, uuid, create_node_ready_cb,
> +								pending_msg)) {
> +		l_dbus_message_unref(msg);
> +		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
> +							"Node import failed");
> +	}
> +
> +	return NULL;
> +}
> +
>  static void setup_network_interface(struct l_dbus_interface *iface)
>  {
>  	l_dbus_interface_method(iface, "Join", 0, join_network_call, "",
> @@ -612,8 +659,14 @@ static void setup_network_interface(struct l_dbus_interface *iface)
>  
>  	l_dbus_interface_method(iface, "Leave", 0, leave_call, "", "t",
>  								"token");
> +
>  	l_dbus_interface_method(iface, "CreateNetwork", 0, create_network_call,
>  					"t", "oay", "token", "app", "uuid");
> +
> +	l_dbus_interface_method(iface, "ImportLocalNode", 0,
> +					import_local_node_call,
> +					"t", "osay", "token",
> +					"app", "json_data", "uuid");
>  }
>  
>  bool mesh_dbus_init(struct l_dbus *dbus)
> diff --git a/mesh/node.c b/mesh/node.c
> index e99858623..991802a6f 100644
> --- a/mesh/node.c
> +++ b/mesh/node.c
> @@ -27,6 +27,7 @@
>  
>  #include <ell/ell.h>
>  #include <json-c/json.h>
> +#include <stdio.h>
>  
>  #include "mesh/mesh-defs.h"
>  #include "mesh/mesh.h"
> @@ -58,9 +59,12 @@
>  #define DEFAULT_CRPL 10
>  #define DEFAULT_SEQUENCE_NUMBER 0
>  
> -#define REQUEST_TYPE_JOIN 0
> -#define REQUEST_TYPE_ATTACH 1
> -#define REQUEST_TYPE_CREATE 2
> +enum request_type {
> +	REQUEST_TYPE_JOIN = 0,
> +	REQUEST_TYPE_ATTACH,
> +	REQUEST_TYPE_CREATE,
> +	REQUEST_TYPE_IMPORT,
> +};
>  
>  struct node_element {
>  	char *path;
> @@ -111,7 +115,18 @@ struct managed_obj_request {
>  	void *data;
>  	void *cb;
>  	void *user_data;
> -	uint8_t type;
> +	enum request_type type;
> +};
> +
> +struct node_import_request {
> +	uint8_t uuid[16];
> +	uint8_t dev_key[16];
> +	uint8_t net_key[16];
> +	bool kr;
> +	uint16_t unicast;
> +	uint32_t iv_idx;
> +	bool iv_update;
> +	void *user_data;
>  };
>  
>  static struct l_queue *nodes;
> @@ -851,7 +866,7 @@ element_done:
>  #define MIN_COMPOSITION_LEN 16
>  
>  bool node_parse_composition(struct mesh_node *node, uint8_t *data,
> -								uint16_t len)
> +			    uint16_t len)
>  {
>  	struct node_composition *comp;
>  	uint16_t features;
> @@ -946,7 +961,7 @@ bool node_parse_composition(struct mesh_node *node, uint8_t *data,
>  			vendor_id = l_get_le16(data);
>  			mod_id |= (vendor_id << 16);
>  			mod = mesh_model_vendor_new(ele->idx, vendor_id,
> -									mod_id);
> +						    mod_id);
>  			if (!mod) {
>  				element_free(ele);
>  				goto fail;
> @@ -977,7 +992,6 @@ fail:
>  
>  	return false;
>  }
> -
>  static void attach_io(void *a, void *b)
>  {
>  	struct mesh_node *node = a;
> @@ -1078,6 +1092,7 @@ static bool validate_model_property(struct node_element *ele,
>  		while (l_dbus_message_iter_next_entry(&ids, &vendor_id,
>  								&mod_id)) {
>  			struct mesh_model *mod;
> +
>  			mod = l_queue_find(ele->models, match_model_id,
>  				L_UINT_TO_PTR((vendor_id << 16) | mod_id));
>  			if (!mod)
> @@ -1366,17 +1381,92 @@ static bool get_app_properties(struct mesh_node *node, const char *path,
>  	return true;
>  }
>  
> -static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr,
> -				bool ivu, uint32_t iv_idx, uint8_t dev_key[16],
> -				uint16_t net_key_idx, uint8_t net_key[16])
> +static bool parse_imported_iv_index(json_object *jobj, uint32_t *idx,
> +								bool *update)
>  {
> -	node->net = mesh_net_new(node);
> +	int tmp;
> +	json_object *jvalue;
>  
> -	if (!nodes)
> -		nodes = l_queue_new();
> +	if (!json_object_object_get_ex(jobj, "IVindex", &jvalue))
> +		return false;
>  
> -	l_queue_push_tail(nodes, node);
> +	tmp = json_object_get_int(jvalue);
> +	*idx = (uint32_t) tmp;
> +
> +	if (!json_object_object_get_ex(jobj, "IVupdate", &jvalue))
> +		return false;
> +
> +	tmp = json_object_get_int(jvalue);
> +	*update = (bool)tmp;
> +
> +	return true;
> +}
> +
> +static bool parse_imported_unicast_addr(json_object *jobj, uint16_t *unicast)
> +{
> +	json_object *jvalue;
> +	char *str;
> +
> +	if (!json_object_object_get_ex(jobj, "unicastAddress", &jvalue))
> +		return false;
> +
> +	str = (char *)json_object_get_string(jvalue);
> +
> +	if (sscanf(str, "%04hx", unicast) != 1)
> +		return false;
> +
> +	return true;
> +}
>  
> +static bool parse_imported_device_key(json_object *jobj, uint8_t key_buf[16])
> +{
> +	json_object *jvalue;
> +	char *str;
> +
> +	if (!key_buf)
> +		return false;
> +
> +	if (!json_object_object_get_ex(jobj, "deviceKey", &jvalue))
> +		return false;
> +
> +	str = (char *)json_object_get_string(jvalue);
> +
> +	if (!str2hex(str, strlen(str), key_buf, 16))
> +		return false;
> +
> +	return true;
> +}
> +
> +static bool parse_imported_net_key(json_object *jobj, uint8_t key_buf[16],
> +								bool *kr)
> +{
> +	json_object *jvalue;
> +	char *str;
> +
> +	if (!key_buf)
> +		return false;
> +
> +	if (!json_object_object_get_ex(jobj, "netKey", &jvalue))
> +		return false;
> +
> +	str = (char *)json_object_get_string(jvalue);
> +
> +	if (!str2hex(str, strlen(str), key_buf, 16))
> +		return false;
> +
> +	/* Get key refresh */
> +	if (!json_object_object_get_ex(jobj, "keyRefresh", &jvalue))
> +		return false;
> +
> +	*kr = (bool)json_object_get_boolean(jvalue);
> +	return true;
> +}
> +
> +
> +static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr,
> +				bool ivu, uint32_t iv_idx, uint8_t dev_key[16],
> +				uint16_t net_key_idx, uint8_t net_key[16])
> +{
>  	if (!storage_set_iv_index(node->net, iv_idx, ivu))
>  		return false;
>  
> @@ -1444,14 +1534,13 @@ static void get_managed_objects_cb(struct l_dbus_message *msg, void *user_data)
>  	}
>  
>  	if (is_new) {
> -		node = l_new(struct mesh_node, 1);
> +		node = node_new(req->data);
>  		node->elements = l_queue_new();
>  	} else {
>  		node = req->data;
>  	}
>  
>  	num_ele = 0;
> -
>  	while (l_dbus_message_iter_next_entry(&objects, &path, &interfaces)) {
>  		struct l_dbus_message_iter properties;
>  		const char *interface;
> @@ -1547,6 +1636,44 @@ static void get_managed_objects_cb(struct l_dbus_message *msg, void *user_data)
>  
>  		cb(node, agent);
>  
> +	} else if (req->type == REQUEST_TYPE_IMPORT) {
> +
> +		node_ready_func_t cb = req->cb;
> +		struct node_import_request *import_data = req->user_data;
> +		struct keyring_net_key net_key;
> +
> +		if (!agent) {
> +			l_error("Interface %s not found",
> +						MESH_PROVISION_AGENT_INTERFACE);
> +			goto fail;
> +		}
> +
> +		node->num_ele = num_ele;
> +		set_defaults(node);
> +		memcpy(node->uuid, import_data->uuid, 16);
> +
> +		if (!create_node_config(node))
> +			goto fail;
> +
> +		if (!add_local_node(node, import_data->unicast, import_data->kr,
> +				import_data->iv_update, import_data->iv_idx,
> +				import_data->dev_key, PRIMARY_NET_IDX,
> +							import_data->net_key))
> +			goto fail;
> +
> +		memcpy(net_key.old_key, import_data->net_key, 16);
> +		net_key.net_idx = PRIMARY_NET_IDX;
> +		net_key.phase = KEY_REFRESH_PHASE_NONE;
> +
> +		if (!keyring_put_remote_dev_key(node, import_data->unicast,
> +						num_ele, import_data->dev_key))
> +			goto fail;
> +
> +		if (!keyring_put_net_key(node, PRIMARY_NET_IDX, &net_key))
> +			goto fail;
> +
> +		cb(import_data->user_data, MESH_ERROR_NONE, node);
> +
>  	} else {
>  		/* Callback for create node request */
>  		node_ready_func_t cb = req->cb;
> @@ -1672,6 +1799,59 @@ void node_join(const char *app_path, const char *sender, const uint8_t *uuid,
>  					req, l_free);
>  }
>  
> +
> +bool node_import(const char *app_path, const char *sender, void *json_data,
> +		const uint8_t *uuid, node_ready_func_t cb, void *user_data)
> +{
> +	struct managed_obj_request *req;
> +	struct node_import_request *node;
> +
> +	l_debug("");
> +	node = l_new(struct node_import_request, 1);
> +	req = l_new(struct managed_obj_request, 1);
> +
> +	if (!parse_imported_device_key(json_data, node->dev_key)) {
> +		l_error("Failed to parse imported device key");
> +		goto fail;
> +	}
> +
> +	if (!parse_imported_unicast_addr(json_data, &node->unicast)) {
> +		l_error("Failed to parse imported unicast address");
> +		goto fail;
> +	}
> +
> +	if (!parse_imported_iv_index(json_data, &node->iv_idx,
> +							&node->iv_update)) {
> +		l_error("Failed to parse imported iv idx");
> +		goto fail;
> +	}
> +
> +
> +	if (!parse_imported_net_key(json_data, node->net_key, &node->kr)) {
> +		l_error("Failed to parse imported network key");
> +		goto fail;
> +	}
> +
> +	node->user_data = user_data;
> +
> +	memcpy(node->uuid, uuid, 16);
> +	req->data = (void *) uuid;
> +	req->user_data = node;
> +	req->cb = cb;
> +	req->type = REQUEST_TYPE_IMPORT;
> +
> +	l_dbus_method_call(dbus_get_bus(), sender, app_path,
> +					L_DBUS_INTERFACE_OBJECT_MANAGER,
> +					"GetManagedObjects", NULL,
> +					get_managed_objects_cb,
> +					req, l_free);
> +	return true;
> +fail:
> +	json_object_put(json_data);
> +	l_free(node);
> +	return false;
> +}
> +
>  void node_create(const char *app_path, const char *sender, const uint8_t *uuid,
>  					node_ready_func_t cb, void *user_data)
>  {
> diff --git a/mesh/node.h b/mesh/node.h
> index 142527b30..9559f9178 100644
> --- a/mesh/node.h
> +++ b/mesh/node.h
> @@ -91,6 +91,8 @@ void node_build_attach_reply(struct mesh_node *node,
>  						struct l_dbus_message *reply);
>  void node_create(const char *app_path, const char *sender, const uint8_t *uuid,
>  					node_ready_func_t cb, void *user_data);
> +bool node_import(const char *app_path, const char *sender, void *jnode,
> +		const uint8_t *uuid, node_ready_func_t cb, void *user_data);
>  void node_id_set(struct mesh_node *node, uint16_t node_id);
>  uint16_t node_id_get(struct mesh_node *node);
>  bool node_dbus_init(struct l_dbus *bus);

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

* Re: [PATCH v2] mesh: Added ImportLocalNode call with its API
  2019-06-26 17:01 ` Gix, Brian
@ 2019-06-26 17:53   ` Stotland, Inga
  2019-06-26 18:37     ` Jakub Witowski
       [not found]     ` <CAMCw4t3pXTbtt05RD694jzF_MNT_J9dcFMtA7iuD4ujZT9FDbg@mail.gmail.com>
  0 siblings, 2 replies; 9+ messages in thread
From: Stotland, Inga @ 2019-06-26 17:53 UTC (permalink / raw)
  To: jakub.witowski, linux-bluetooth, Gix, Brian; +Cc: michal.lowas-rzechonek

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

Hi Brian, Jakub,

On Wed, 2019-06-26 at 10:01 -0700, Gix, Brian wrote:
> Hi Jakub,
> 
> 
> On Tue, 2019-06-25 at 16:38 +0200, Jakub Witowski wrote:
> > This implements ImportLocalNode() method on org.bluez.mesh.Network1
> > interface. Invoking this method creates a self-provisioned node
> > based on
> > passed JSON definition. Also full functionality of import local
> > node has
> > been implemented
> > ---
> >  doc/mesh-api.txt |  22 ++++-
> >  mesh/mesh.c      |  63 ++++++++++++--
> >  mesh/node.c      | 212
> > +++++++++++++++++++++++++++++++++++++++++++----
> >  mesh/node.h      |   2 +
> >  4 files changed, 276 insertions(+), 23 deletions(-)
> > 
> > diff --git a/doc/mesh-api.txt b/doc/mesh-api.txt
> > index 4e0a8bff1..539fc3e4f 100644
> > --- a/doc/mesh-api.txt
> > +++ b/doc/mesh-api.txt
> > @@ -151,11 +151,21 @@ Methods:
> >  			org.bluez.mesh.Error.InvalidArguments
> >  			org.bluez.mesh.Error.AlreadyExists,
> >  
> > -	 uint64 token ImportLocalNode(string json_data)
> > +	 uint64 token ImportLocalNode(object app_root, string
> > json_data, array{byte}[16] uuid)
> >  
> >  		This method creates a local mesh node based on node
> >  		configuration that has been generated outside
> > bluetooth-meshd.
> >  
> > +		The app_root parameter is a D-Bus object root path of
> > the
> > +		application that implements org.bluez.mesh.Application1
> > +		interface, and a org.bluez.mesh.Provisioner1 interface.
> > The
> > +		application represents a node where child mesh elements
> > have
> > +		their own objects that implement
> > org.bluez.mesh.Element1
> > +		interface. The application hierarchy also contains a
> > provision
> > +		agent object that implements
> > org.bluez.mesh.ProvisionAgent1
> > +		interface. The standard DBus.ObjectManager interface
> > must be
> > +		available on the app_root path.
> > +
> >  		The json_data parameter is a full JSON representation
> > of a node
> >  		configuration file. The format must conform to the
> > schema
> >  		defined in "Mesh Node Configuration Schema" section.
> > Any
> > @@ -1059,4 +1069,12 @@ Properties:
> >  
> >  Mesh Node Configuration Schema
> >  ==============================
> > -<TBD>
> > +Example of Json format for ImportLocalNode():
> > +{
> > +  "IVindex":0,
> > +  "IVupdate":0,
> > +  "unicastAddress":"0012",
> > +  "deviceKey":"7daa45cd1e9e11a4b86eeef7d01efa11",
> > +  "netKey":"1234567890abcdef1234567890abcdef",
> > +  "keyRefresh":0
> > +}
> > diff --git a/mesh/mesh.c b/mesh/mesh.c
> > index 26acfd4dc..e0f0e4bf9 100644
> > --- a/mesh/mesh.c
> > +++ b/mesh/mesh.c
> > @@ -22,6 +22,7 @@
> >  #endif
> >  
> >  #define _GNU_SOURCE
> > +#include <json-c/json.h>
> 
> We are trying to figure out a way to *isolate* JSON dependancies in
> the Mesh daemon, such that
> if it needs to be ported to a platform that does not support the JSON
> library, it can be easily
> pared out while minimizing impact to the rest of the system.
> 
> Inga and I have been having conversations about this.
> 
> * It should be possible to create a JSON-free daemon, if the internal
> JSON storage system is replaced with a
> custom, more space-efficient node storage, or for any other reason.
> 
> * It should be possible to *remove* the ImportLocalNode() entirely if
> that functionality is not needed, and
> with it the JSON dependancy.
> 
> * We may want to be able to support *other* formats for this DBUS
> facing interface (perhaps XML, perhaps
> something vendor defined).
> 
> 
> I don't think there is any question that the *location* for this
> method belongs exactly where you have it,
> however I think we would like to see the JSON parsing moved to a file
> that already has the JSON dependancy.  We
> may want to let the daemon auto-detect the format (returning an error
> if the format is not understood). 
> However, then perhaps having a "Table of Parsers" that do not live
> within the mesh.c file...  but perhaps in a
> format-parser.h file which can be easily customized to support other
> parsers.

I just want to point out that maintining several parsers at the runtime
may not be a good idea as it would result in unwarranted code
complexity, e.g., loading node configurations at startup from *all*
supported formats and then writing out each node in its corresponding
format.

My preference would be to have a compile time decision to choose a
supported parser. In addition, standalone format conversion tools may
be provided.

Maybe adding "format" property to /org/bluez/mesh? An app can read the
property, perform the appropriate conversion and then invoke Import
method.

Import method may take an additional string argument "type" to indicate
the type of the format of the imported node configuration, e.g., "json"
or "xml".

> 
> >  #include <ell/ell.h>
> >  
> >  #include "mesh/mesh-io.h"
> > @@ -60,7 +61,7 @@ struct bt_mesh {
> >  	uint8_t max_filters;
> >  };
> >  
> > -struct join_data{
> > +struct join_data {
> >  	struct l_dbus_message *msg;
> >  	struct mesh_agent *agent;
> >  	const char *sender;
> > @@ -365,8 +366,8 @@ static void node_init_cb(struct mesh_node
> > *node, struct mesh_agent *agent)
> >  
> >  	if (!acceptor_start(num_ele, join_pending->uuid,
> > mesh.algorithms,
> >  				mesh.prov_timeout, agent,
> > prov_complete_cb,
> > -				&mesh))
> > -	{
> > +				&mesh)) {
> > +
> >  		reply = dbus_error(join_pending->msg,
> > MESH_ERROR_FAILED,
> >  				"Failed to start provisioning
> > acceptor");
> >  		goto fail;
> > @@ -536,7 +537,7 @@ static struct l_dbus_message *leave_call(struct
> > l_dbus *dbus,
> >  	return l_dbus_message_new_method_return(msg);
> >  }
> >  
> > -static void create_network_ready_cb(void *user_data, int status,
> > +static void create_node_ready_cb(void *user_data, int status,
> >  							struct
> > mesh_node *node)
> >  {
> >  	struct l_dbus_message *reply;
> > @@ -593,12 +594,58 @@ static struct l_dbus_message
> > *create_network_call(struct l_dbus *dbus,
> >  
> >  	l_queue_push_tail(pending_queue, pending_msg);
> >  
> > -	node_create(app_path, sender, uuid, create_network_ready_cb,
> > +	node_create(app_path, sender, uuid, create_node_ready_cb,
> >  								pending
> > _msg);
> >  
> >  	return NULL;
> >  }
> >  
> > +static struct l_dbus_message *import_local_node_call(struct l_dbus
> > *dbus,
> > +						struct l_dbus_message
> > *msg,
> > +						void *user_data)
> > +{
> > +	const char *app_path, *sender;
> > +	struct l_dbus_message *pending_msg;
> > +	struct l_dbus_message_iter iter_uuid;
> > +	const char *json_data;
> > +	uint8_t *uuid;
> > +	uint32_t n;
> > +	struct json_object *jnode;
> > +
> > +	l_debug("Import local node request");
> > +
> > +	if (!l_dbus_message_get_arguments(msg, "osay", &app_path,
> > +							&json_data,
> > &iter_uuid))
> > +		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
> > +
> > +	if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n)
> > ||
> > +									
> > n != 16)
> > +		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad
> > dev UUID");
> > +
> > +	if (node_find_by_uuid(uuid))
> > +		return dbus_error(msg, MESH_ERROR_ALREADY_EXISTS,
> > +							"Node already
> > exists");
> > +
> > +	jnode = json_tokener_parse(json_data);
> > +
> > +	sender = l_dbus_message_get_sender(msg);
> > +	pending_msg = l_dbus_message_ref(msg);
> > +
> > +	if (!pending_queue)
> > +		pending_queue = l_queue_new();
> > +
> > +	l_queue_push_tail(pending_queue, pending_msg);
> > +
> > +	if (!node_import(app_path, sender, jnode, uuid,
> > create_node_ready_cb,
> > +								pending
> > _msg)) {
> > +		l_dbus_message_unref(msg);
> > +		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
> > +							"Node import
> > failed");
> > +	}
> > +
> > +	return NULL;
> > +}
> > +
> >  static void setup_network_interface(struct l_dbus_interface
> > *iface)
> >  {
> >  	l_dbus_interface_method(iface, "Join", 0, join_network_call,
> > "",
> > @@ -612,8 +659,14 @@ static void setup_network_interface(struct
> > l_dbus_interface *iface)
> >  
> >  	l_dbus_interface_method(iface, "Leave", 0, leave_call, "", "t",
> >  								"token"
> > );
> > +
> >  	l_dbus_interface_method(iface, "CreateNetwork", 0,
> > create_network_call,
> >  					"t", "oay", "token", "app",
> > "uuid");
> > +
> > +	l_dbus_interface_method(iface, "ImportLocalNode", 0,
> > +					import_local_node_call,
> > +					"t", "osay", "token",
> > +					"app", "json_data", "uuid");
> >  }
> >  
> >  bool mesh_dbus_init(struct l_dbus *dbus)
> > diff --git a/mesh/node.c b/mesh/node.c
> > index e99858623..991802a6f 100644
> > --- a/mesh/node.c
> > +++ b/mesh/node.c
> > @@ -27,6 +27,7 @@
> >  
> >  #include <ell/ell.h>
> >  #include <json-c/json.h>
> > +#include <stdio.h>
> >  
> >  #include "mesh/mesh-defs.h"
> >  #include "mesh/mesh.h"
> > @@ -58,9 +59,12 @@
> >  #define DEFAULT_CRPL 10
> >  #define DEFAULT_SEQUENCE_NUMBER 0
> >  
> > -#define REQUEST_TYPE_JOIN 0
> > -#define REQUEST_TYPE_ATTACH 1
> > -#define REQUEST_TYPE_CREATE 2
> > +enum request_type {
> > +	REQUEST_TYPE_JOIN = 0,
> > +	REQUEST_TYPE_ATTACH,
> > +	REQUEST_TYPE_CREATE,
> > +	REQUEST_TYPE_IMPORT,
> > +};
> >  
> >  struct node_element {
> >  	char *path;
> > @@ -111,7 +115,18 @@ struct managed_obj_request {
> >  	void *data;
> >  	void *cb;
> >  	void *user_data;
> > -	uint8_t type;
> > +	enum request_type type;
> > +};
> > +
> > +struct node_import_request {
> > +	uint8_t uuid[16];
> > +	uint8_t dev_key[16];
> > +	uint8_t net_key[16];
> > +	bool kr;
> > +	uint16_t unicast;
> > +	uint32_t iv_idx;
> > +	bool iv_update;
> > +	void *user_data;
> >  };
> >  
> >  static struct l_queue *nodes;
> > @@ -851,7 +866,7 @@ element_done:
> >  #define MIN_COMPOSITION_LEN 16
> >  
> >  bool node_parse_composition(struct mesh_node *node, uint8_t *data,
> > -								uint16_
> > t len)
> > +			    uint16_t len)
> >  {
> >  	struct node_composition *comp;
> >  	uint16_t features;
> > @@ -946,7 +961,7 @@ bool node_parse_composition(struct mesh_node
> > *node, uint8_t *data,
> >  			vendor_id = l_get_le16(data);
> >  			mod_id |= (vendor_id << 16);
> >  			mod = mesh_model_vendor_new(ele->idx,
> > vendor_id,
> > -									
> > mod_id);
> > +						    mod_id);
> >  			if (!mod) {
> >  				element_free(ele);
> >  				goto fail;
> > @@ -977,7 +992,6 @@ fail:
> >  
> >  	return false;
> >  }
> > -
> >  static void attach_io(void *a, void *b)
> >  {
> >  	struct mesh_node *node = a;
> > @@ -1078,6 +1092,7 @@ static bool validate_model_property(struct
> > node_element *ele,
> >  		while (l_dbus_message_iter_next_entry(&ids, &vendor_id,
> >  								&mod_id
> > )) {
> >  			struct mesh_model *mod;
> > +
> >  			mod = l_queue_find(ele->models, match_model_id,
> >  				L_UINT_TO_PTR((vendor_id << 16) |
> > mod_id));
> >  			if (!mod)
> > @@ -1366,17 +1381,92 @@ static bool get_app_properties(struct
> > mesh_node *node, const char *path,
> >  	return true;
> >  }
> >  
> > -static bool add_local_node(struct mesh_node *node, uint16_t
> > unicast, bool kr,
> > -				bool ivu, uint32_t iv_idx, uint8_t
> > dev_key[16],
> > -				uint16_t net_key_idx, uint8_t
> > net_key[16])
> > +static bool parse_imported_iv_index(json_object *jobj, uint32_t
> > *idx,
> > +								bool
> > *update)
> >  {
> > -	node->net = mesh_net_new(node);
> > +	int tmp;
> > +	json_object *jvalue;
> >  
> > -	if (!nodes)
> > -		nodes = l_queue_new();
> > +	if (!json_object_object_get_ex(jobj, "IVindex", &jvalue))
> > +		return false;
> >  
> > -	l_queue_push_tail(nodes, node);
> > +	tmp = json_object_get_int(jvalue);
> > +	*idx = (uint32_t) tmp;
> > +
> > +	if (!json_object_object_get_ex(jobj, "IVupdate", &jvalue))
> > +		return false;
> > +
> > +	tmp = json_object_get_int(jvalue);
> > +	*update = (bool)tmp;
> > +
> > +	return true;
> > +}
> > +
> > +static bool parse_imported_unicast_addr(json_object *jobj,
> > uint16_t *unicast)
> > +{
> > +	json_object *jvalue;
> > +	char *str;
> > +
> > +	if (!json_object_object_get_ex(jobj, "unicastAddress",
> > &jvalue))
> > +		return false;
> > +
> > +	str = (char *)json_object_get_string(jvalue);
> > +
> > +	if (sscanf(str, "%04hx", unicast) != 1)
> > +		return false;
> > +
> > +	return true;
> > +}
> >  
> > +static bool parse_imported_device_key(json_object *jobj, uint8_t
> > key_buf[16])
> > +{
> > +	json_object *jvalue;
> > +	char *str;
> > +
> > +	if (!key_buf)
> > +		return false;
> > +
> > +	if (!json_object_object_get_ex(jobj, "deviceKey", &jvalue))
> > +		return false;
> > +
> > +	str = (char *)json_object_get_string(jvalue);
> > +
> > +	if (!str2hex(str, strlen(str), key_buf, 16))
> > +		return false;
> > +
> > +	return true;
> > +}
> > +
> > +static bool parse_imported_net_key(json_object *jobj, uint8_t
> > key_buf[16],
> > +								bool
> > *kr)
> > +{
> > +	json_object *jvalue;
> > +	char *str;
> > +
> > +	if (!key_buf)
> > +		return false;
> > +
> > +	if (!json_object_object_get_ex(jobj, "netKey", &jvalue))
> > +		return false;
> > +
> > +	str = (char *)json_object_get_string(jvalue);
> > +
> > +	if (!str2hex(str, strlen(str), key_buf, 16))
> > +		return false;
> > +
> > +	/* Get key refresh */
> > +	if (!json_object_object_get_ex(jobj, "keyRefresh", &jvalue))
> > +		return false;
> > +
> > +	*kr = (bool)json_object_get_boolean(jvalue);
> > +	return true;
> > +}
> > +
> > +
> > +static bool add_local_node(struct mesh_node *node, uint16_t
> > unicast, bool kr,
> > +				bool ivu, uint32_t iv_idx, uint8_t
> > dev_key[16],
> > +				uint16_t net_key_idx, uint8_t
> > net_key[16])
> > +{
> >  	if (!storage_set_iv_index(node->net, iv_idx, ivu))
> >  		return false;
> >  
> > @@ -1444,14 +1534,13 @@ static void get_managed_objects_cb(struct
> > l_dbus_message *msg, void *user_data)
> >  	}
> >  
> >  	if (is_new) {
> > -		node = l_new(struct mesh_node, 1);
> > +		node = node_new(req->data);
> >  		node->elements = l_queue_new();
> >  	} else {
> >  		node = req->data;
> >  	}
> >  
> >  	num_ele = 0;
> > -
> >  	while (l_dbus_message_iter_next_entry(&objects, &path,
> > &interfaces)) {
> >  		struct l_dbus_message_iter properties;
> >  		const char *interface;
> > @@ -1547,6 +1636,44 @@ static void get_managed_objects_cb(struct
> > l_dbus_message *msg, void *user_data)
> >  
> >  		cb(node, agent);
> >  
> > +	} else if (req->type == REQUEST_TYPE_IMPORT) {
> > +
> > +		node_ready_func_t cb = req->cb;
> > +		struct node_import_request *import_data = req-
> > >user_data;
> > +		struct keyring_net_key net_key;
> > +
> > +		if (!agent) {
> > +			l_error("Interface %s not found",
> > +						MESH_PROVISION_AGENT_IN
> > TERFACE);
> > +			goto fail;
> > +		}
> > +
> > +		node->num_ele = num_ele;
> > +		set_defaults(node);
> > +		memcpy(node->uuid, import_data->uuid, 16);
> > +
> > +		if (!create_node_config(node))
> > +			goto fail;
> > +
> > +		if (!add_local_node(node, import_data->unicast,
> > import_data->kr,
> > +				import_data->iv_update, import_data-
> > >iv_idx,
> > +				import_data->dev_key, PRIMARY_NET_IDX,
> > +							import_data-
> > >net_key))
> > +			goto fail;
> > +
> > +		memcpy(net_key.old_key, import_data->net_key, 16);
> > +		net_key.net_idx = PRIMARY_NET_IDX;
> > +		net_key.phase = KEY_REFRESH_PHASE_NONE;
> > +
> > +		if (!keyring_put_remote_dev_key(node, import_data-
> > >unicast,
> > +						num_ele, import_data-
> > >dev_key))
> > +			goto fail;
> > +
> > +		if (!keyring_put_net_key(node, PRIMARY_NET_IDX,
> > &net_key))
> > +			goto fail;
> > +
> > +		cb(import_data->user_data, MESH_ERROR_NONE, node);
> > +
> >  	} else {
> >  		/* Callback for create node request */
> >  		node_ready_func_t cb = req->cb;
> > @@ -1672,6 +1799,59 @@ void node_join(const char *app_path, const
> > char *sender, const uint8_t *uuid,
> >  					req, l_free);
> >  }
> >  
> > +
> > +bool node_import(const char *app_path, const char *sender, void
> > *json_data,
> > +		const uint8_t *uuid, node_ready_func_t cb, void
> > *user_data)
> > +{
> > +	struct managed_obj_request *req;
> > +	struct node_import_request *node;
> > +
> > +	l_debug("");
> > +	node = l_new(struct node_import_request, 1);
> > +	req = l_new(struct managed_obj_request, 1);
> > +
> > +	if (!parse_imported_device_key(json_data, node->dev_key)) {
> > +		l_error("Failed to parse imported device key");
> > +		goto fail;
> > +	}
> > +
> > +	if (!parse_imported_unicast_addr(json_data, &node->unicast)) {
> > +		l_error("Failed to parse imported unicast address");
> > +		goto fail;
> > +	}
> > +
> > +	if (!parse_imported_iv_index(json_data, &node->iv_idx,
> > +							&node-
> > >iv_update)) {
> > +		l_error("Failed to parse imported iv idx");
> > +		goto fail;
> > +	}
> > +
> > +
> > +	if (!parse_imported_net_key(json_data, node->net_key, &node-
> > >kr)) {
> > +		l_error("Failed to parse imported network key");
> > +		goto fail;
> > +	}
> > +
> > +	node->user_data = user_data;
> > +
> > +	memcpy(node->uuid, uuid, 16);
> > +	req->data = (void *) uuid;
> > +	req->user_data = node;
> > +	req->cb = cb;
> > +	req->type = REQUEST_TYPE_IMPORT;
> > +
> > +	l_dbus_method_call(dbus_get_bus(), sender, app_path,
> > +					L_DBUS_INTERFACE_OBJECT_MANAGER
> > ,
> > +					"GetManagedObjects", NULL,
> > +					get_managed_objects_cb,
> > +					req, l_free);
> > +	return true;
> > +fail:
> > +	json_object_put(json_data);
> > +	l_free(node);
> > +	return false;
> > +}
> > +
> >  void node_create(const char *app_path, const char *sender, const
> > uint8_t *uuid,
> >  					node_ready_func_t cb, void
> > *user_data)
> >  {
> > diff --git a/mesh/node.h b/mesh/node.h
> > index 142527b30..9559f9178 100644
> > --- a/mesh/node.h
> > +++ b/mesh/node.h
> > @@ -91,6 +91,8 @@ void node_build_attach_reply(struct mesh_node
> > *node,
> >  						struct l_dbus_message
> > *reply);
> >  void node_create(const char *app_path, const char *sender, const
> > uint8_t *uuid,
> >  					node_ready_func_t cb, void
> > *user_data);
> > +bool node_import(const char *app_path, const char *sender, void
> > *jnode,
> > +		const uint8_t *uuid, node_ready_func_t cb, void
> > *user_data);
> >  void node_id_set(struct mesh_node *node, uint16_t node_id);
> >  uint16_t node_id_get(struct mesh_node *node);
> >  bool node_dbus_init(struct l_dbus *bus);


[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 3265 bytes --]

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

* Re: [PATCH v2] mesh: Added ImportLocalNode call with its API
  2019-06-26 17:53   ` Stotland, Inga
@ 2019-06-26 18:37     ` Jakub Witowski
       [not found]     ` <CAMCw4t3pXTbtt05RD694jzF_MNT_J9dcFMtA7iuD4ujZT9FDbg@mail.gmail.com>
  1 sibling, 0 replies; 9+ messages in thread
From: Jakub Witowski @ 2019-06-26 18:37 UTC (permalink / raw)
  To: Stotland, Inga, linux-bluetooth, Gix, Brian; +Cc: michal.lowas-rzechonek


On 26.06.2019 19:53, Stotland, Inga wrote:
> Hi Brian, Jakub,
>
> On Wed, 2019-06-26 at 10:01 -0700, Gix, Brian wrote:
>> Hi Jakub,
>>
>>
>> On Tue, 2019-06-25 at 16:38 +0200, Jakub Witowski wrote:
>>> This implements ImportLocalNode() method on org.bluez.mesh.Network1
>>> interface. Invoking this method creates a self-provisioned node
>>> based on
>>> passed JSON definition. Also full functionality of import local
>>> node has
>>> been implemented
>>> ---
>>>   doc/mesh-api.txt |  22 ++++-
>>>   mesh/mesh.c      |  63 ++++++++++++--
>>>   mesh/node.c      | 212
>>> +++++++++++++++++++++++++++++++++++++++++++----
>>>   mesh/node.h      |   2 +
>>>   4 files changed, 276 insertions(+), 23 deletions(-)
>>>
>>> diff --git a/doc/mesh-api.txt b/doc/mesh-api.txt
>>> index 4e0a8bff1..539fc3e4f 100644
>>> --- a/doc/mesh-api.txt
>>> +++ b/doc/mesh-api.txt
>>> @@ -151,11 +151,21 @@ Methods:
>>>   			org.bluez.mesh.Error.InvalidArguments
>>>   			org.bluez.mesh.Error.AlreadyExists,
>>>   
>>> -	 uint64 token ImportLocalNode(string json_data)
>>> +	 uint64 token ImportLocalNode(object app_root, string
>>> json_data, array{byte}[16] uuid)
>>>   
>>>   		This method creates a local mesh node based on node
>>>   		configuration that has been generated outside
>>> bluetooth-meshd.
>>>   
>>> +		The app_root parameter is a D-Bus object root path of
>>> the
>>> +		application that implements org.bluez.mesh.Application1
>>> +		interface, and a org.bluez.mesh.Provisioner1 interface.
>>> The
>>> +		application represents a node where child mesh elements
>>> have
>>> +		their own objects that implement
>>> org.bluez.mesh.Element1
>>> +		interface. The application hierarchy also contains a
>>> provision
>>> +		agent object that implements
>>> org.bluez.mesh.ProvisionAgent1
>>> +		interface. The standard DBus.ObjectManager interface
>>> must be
>>> +		available on the app_root path.
>>> +
>>>   		The json_data parameter is a full JSON representation
>>> of a node
>>>   		configuration file. The format must conform to the
>>> schema
>>>   		defined in "Mesh Node Configuration Schema" section.
>>> Any
>>> @@ -1059,4 +1069,12 @@ Properties:
>>>   
>>>   Mesh Node Configuration Schema
>>>   ==============================
>>> -<TBD>
>>> +Example of Json format for ImportLocalNode():
>>> +{
>>> +  "IVindex":0,
>>> +  "IVupdate":0,
>>> +  "unicastAddress":"0012",
>>> +  "deviceKey":"7daa45cd1e9e11a4b86eeef7d01efa11",
>>> +  "netKey":"1234567890abcdef1234567890abcdef",
>>> +  "keyRefresh":0
>>> +}
>>> diff --git a/mesh/mesh.c b/mesh/mesh.c
>>> index 26acfd4dc..e0f0e4bf9 100644
>>> --- a/mesh/mesh.c
>>> +++ b/mesh/mesh.c
>>> @@ -22,6 +22,7 @@
>>>   #endif
>>>   
>>>   #define _GNU_SOURCE
>>> +#include <json-c/json.h>
>> We are trying to figure out a way to *isolate* JSON dependancies in
>> the Mesh daemon, such that
>> if it needs to be ported to a platform that does not support the JSON
>> library, it can be easily
>> pared out while minimizing impact to the rest of the system.
>>
>> Inga and I have been having conversations about this.
>>
>> * It should be possible to create a JSON-free daemon, if the internal
>> JSON storage system is replaced with a
>> custom, more space-efficient node storage, or for any other reason.
>>
>> * It should be possible to *remove* the ImportLocalNode() entirely if
>> that functionality is not needed, and
>> with it the JSON dependancy.
>>
>> * We may want to be able to support *other* formats for this DBUS
>> facing interface (perhaps XML, perhaps
>> something vendor defined).
>>
>>
>> I don't think there is any question that the *location* for this
>> method belongs exactly where you have it,
>> however I think we would like to see the JSON parsing moved to a file
>> that already has the JSON dependancy.  We
>> may want to let the daemon auto-detect the format (returning an error
>> if the format is not understood).
>> However, then perhaps having a "Table of Parsers" that do not live
>> within the mesh.c file...  but perhaps in a
>> format-parser.h file which can be easily customized to support other
>> parsers.
> I just want to point out that maintining several parsers at the runtime
> may not be a good idea as it would result in unwarranted code
> complexity, e.g., loading node configurations at startup from *all*
> supported formats and then writing out each node in its corresponding
> format.
>
> My preference would be to have a compile time decision to choose a
> supported parser. In addition, standalone format conversion tools may
> be provided.
>
> Maybe adding "format" property to /org/bluez/mesh? An app can read the
> property, perform the appropriate conversion and then invoke Import
> method.
>
> Import method may take an additional string argument "type" to indicate
> the type of the format of the imported node configuration, e.g., "json"
> or "xml".
>
>>>   #include <ell/ell.h>
>>>   
>>>   #include "mesh/mesh-io.h"
>>> @@ -60,7 +61,7 @@ struct bt_mesh {
>>>   	uint8_t max_filters;
>>>   };
>>>   
>>> -struct join_data{
>>> +struct join_data {
>>>   	struct l_dbus_message *msg;
>>>   	struct mesh_agent *agent;
>>>   	const char *sender;
>>> @@ -365,8 +366,8 @@ static void node_init_cb(struct mesh_node
>>> *node, struct mesh_agent *agent)
>>>   
>>>   	if (!acceptor_start(num_ele, join_pending->uuid,
>>> mesh.algorithms,
>>>   				mesh.prov_timeout, agent,
>>> prov_complete_cb,
>>> -				&mesh))
>>> -	{
>>> +				&mesh)) {
>>> +
>>>   		reply = dbus_error(join_pending->msg,
>>> MESH_ERROR_FAILED,
>>>   				"Failed to start provisioning
>>> acceptor");
>>>   		goto fail;
>>> @@ -536,7 +537,7 @@ static struct l_dbus_message *leave_call(struct
>>> l_dbus *dbus,
>>>   	return l_dbus_message_new_method_return(msg);
>>>   }
>>>   
>>> -static void create_network_ready_cb(void *user_data, int status,
>>> +static void create_node_ready_cb(void *user_data, int status,
>>>   							struct
>>> mesh_node *node)
>>>   {
>>>   	struct l_dbus_message *reply;
>>> @@ -593,12 +594,58 @@ static struct l_dbus_message
>>> *create_network_call(struct l_dbus *dbus,
>>>   
>>>   	l_queue_push_tail(pending_queue, pending_msg);
>>>   
>>> -	node_create(app_path, sender, uuid, create_network_ready_cb,
>>> +	node_create(app_path, sender, uuid, create_node_ready_cb,
>>>   								pending
>>> _msg);
>>>   
>>>   	return NULL;
>>>   }
>>>   
>>> +static struct l_dbus_message *import_local_node_call(struct l_dbus
>>> *dbus,
>>> +						struct l_dbus_message
>>> *msg,
>>> +						void *user_data)
>>> +{
>>> +	const char *app_path, *sender;
>>> +	struct l_dbus_message *pending_msg;
>>> +	struct l_dbus_message_iter iter_uuid;
>>> +	const char *json_data;
>>> +	uint8_t *uuid;
>>> +	uint32_t n;
>>> +	struct json_object *jnode;
>>> +
>>> +	l_debug("Import local node request");
>>> +
>>> +	if (!l_dbus_message_get_arguments(msg, "osay", &app_path,
>>> +							&json_data,
>>> &iter_uuid))
>>> +		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
>>> +
>>> +	if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n)
>>> ||
>>> +									
>>> n != 16)
>>> +		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad
>>> dev UUID");
>>> +
>>> +	if (node_find_by_uuid(uuid))
>>> +		return dbus_error(msg, MESH_ERROR_ALREADY_EXISTS,
>>> +							"Node already
>>> exists");
>>> +
>>> +	jnode = json_tokener_parse(json_data);
>>> +
>>> +	sender = l_dbus_message_get_sender(msg);
>>> +	pending_msg = l_dbus_message_ref(msg);
>>> +
>>> +	if (!pending_queue)
>>> +		pending_queue = l_queue_new();
>>> +
>>> +	l_queue_push_tail(pending_queue, pending_msg);
>>> +
>>> +	if (!node_import(app_path, sender, jnode, uuid,
>>> create_node_ready_cb,
>>> +								pending
>>> _msg)) {
>>> +		l_dbus_message_unref(msg);
>>> +		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
>>> +							"Node import
>>> failed");
>>> +	}
>>> +
>>> +	return NULL;
>>> +}
>>> +
>>>   static void setup_network_interface(struct l_dbus_interface
>>> *iface)
>>>   {
>>>   	l_dbus_interface_method(iface, "Join", 0, join_network_call,
>>> "",
>>> @@ -612,8 +659,14 @@ static void setup_network_interface(struct
>>> l_dbus_interface *iface)
>>>   
>>>   	l_dbus_interface_method(iface, "Leave", 0, leave_call, "", "t",
>>>   								"token"
>>> );
>>> +
>>>   	l_dbus_interface_method(iface, "CreateNetwork", 0,
>>> create_network_call,
>>>   					"t", "oay", "token", "app",
>>> "uuid");
>>> +
>>> +	l_dbus_interface_method(iface, "ImportLocalNode", 0,
>>> +					import_local_node_call,
>>> +					"t", "osay", "token",
>>> +					"app", "json_data", "uuid");
>>>   }
>>>   
>>>   bool mesh_dbus_init(struct l_dbus *dbus)
>>> diff --git a/mesh/node.c b/mesh/node.c
>>> index e99858623..991802a6f 100644
>>> --- a/mesh/node.c
>>> +++ b/mesh/node.c
>>> @@ -27,6 +27,7 @@
>>>   
>>>   #include <ell/ell.h>
>>>   #include <json-c/json.h>
>>> +#include <stdio.h>
>>>   
>>>   #include "mesh/mesh-defs.h"
>>>   #include "mesh/mesh.h"
>>> @@ -58,9 +59,12 @@
>>>   #define DEFAULT_CRPL 10
>>>   #define DEFAULT_SEQUENCE_NUMBER 0
>>>   
>>> -#define REQUEST_TYPE_JOIN 0
>>> -#define REQUEST_TYPE_ATTACH 1
>>> -#define REQUEST_TYPE_CREATE 2
>>> +enum request_type {
>>> +	REQUEST_TYPE_JOIN = 0,
>>> +	REQUEST_TYPE_ATTACH,
>>> +	REQUEST_TYPE_CREATE,
>>> +	REQUEST_TYPE_IMPORT,
>>> +};
>>>   
>>>   struct node_element {
>>>   	char *path;
>>> @@ -111,7 +115,18 @@ struct managed_obj_request {
>>>   	void *data;
>>>   	void *cb;
>>>   	void *user_data;
>>> -	uint8_t type;
>>> +	enum request_type type;
>>> +};
>>> +
>>> +struct node_import_request {
>>> +	uint8_t uuid[16];
>>> +	uint8_t dev_key[16];
>>> +	uint8_t net_key[16];
>>> +	bool kr;
>>> +	uint16_t unicast;
>>> +	uint32_t iv_idx;
>>> +	bool iv_update;
>>> +	void *user_data;
>>>   };
>>>   
>>>   static struct l_queue *nodes;
>>> @@ -851,7 +866,7 @@ element_done:
>>>   #define MIN_COMPOSITION_LEN 16
>>>   
>>>   bool node_parse_composition(struct mesh_node *node, uint8_t *data,
>>> -								uint16_
>>> t len)
>>> +			    uint16_t len)
>>>   {
>>>   	struct node_composition *comp;
>>>   	uint16_t features;
>>> @@ -946,7 +961,7 @@ bool node_parse_composition(struct mesh_node
>>> *node, uint8_t *data,
>>>   			vendor_id = l_get_le16(data);
>>>   			mod_id |= (vendor_id << 16);
>>>   			mod = mesh_model_vendor_new(ele->idx,
>>> vendor_id,
>>> -									
>>> mod_id);
>>> +						    mod_id);
>>>   			if (!mod) {
>>>   				element_free(ele);
>>>   				goto fail;
>>> @@ -977,7 +992,6 @@ fail:
>>>   
>>>   	return false;
>>>   }
>>> -
>>>   static void attach_io(void *a, void *b)
>>>   {
>>>   	struct mesh_node *node = a;
>>> @@ -1078,6 +1092,7 @@ static bool validate_model_property(struct
>>> node_element *ele,
>>>   		while (l_dbus_message_iter_next_entry(&ids, &vendor_id,
>>>   								&mod_id
>>> )) {
>>>   			struct mesh_model *mod;
>>> +
>>>   			mod = l_queue_find(ele->models, match_model_id,
>>>   				L_UINT_TO_PTR((vendor_id << 16) |
>>> mod_id));
>>>   			if (!mod)
>>> @@ -1366,17 +1381,92 @@ static bool get_app_properties(struct
>>> mesh_node *node, const char *path,
>>>   	return true;
>>>   }
>>>   
>>> -static bool add_local_node(struct mesh_node *node, uint16_t
>>> unicast, bool kr,
>>> -				bool ivu, uint32_t iv_idx, uint8_t
>>> dev_key[16],
>>> -				uint16_t net_key_idx, uint8_t
>>> net_key[16])
>>> +static bool parse_imported_iv_index(json_object *jobj, uint32_t
>>> *idx,
>>> +								bool
>>> *update)
>>>   {
>>> -	node->net = mesh_net_new(node);
>>> +	int tmp;
>>> +	json_object *jvalue;
>>>   
>>> -	if (!nodes)
>>> -		nodes = l_queue_new();
>>> +	if (!json_object_object_get_ex(jobj, "IVindex", &jvalue))
>>> +		return false;
>>>   
>>> -	l_queue_push_tail(nodes, node);
>>> +	tmp = json_object_get_int(jvalue);
>>> +	*idx = (uint32_t) tmp;
>>> +
>>> +	if (!json_object_object_get_ex(jobj, "IVupdate", &jvalue))
>>> +		return false;
>>> +
>>> +	tmp = json_object_get_int(jvalue);
>>> +	*update = (bool)tmp;
>>> +
>>> +	return true;
>>> +}
>>> +
>>> +static bool parse_imported_unicast_addr(json_object *jobj,
>>> uint16_t *unicast)
>>> +{
>>> +	json_object *jvalue;
>>> +	char *str;
>>> +
>>> +	if (!json_object_object_get_ex(jobj, "unicastAddress",
>>> &jvalue))
>>> +		return false;
>>> +
>>> +	str = (char *)json_object_get_string(jvalue);
>>> +
>>> +	if (sscanf(str, "%04hx", unicast) != 1)
>>> +		return false;
>>> +
>>> +	return true;
>>> +}
>>>   
>>> +static bool parse_imported_device_key(json_object *jobj, uint8_t
>>> key_buf[16])
>>> +{
>>> +	json_object *jvalue;
>>> +	char *str;
>>> +
>>> +	if (!key_buf)
>>> +		return false;
>>> +
>>> +	if (!json_object_object_get_ex(jobj, "deviceKey", &jvalue))
>>> +		return false;
>>> +
>>> +	str = (char *)json_object_get_string(jvalue);
>>> +
>>> +	if (!str2hex(str, strlen(str), key_buf, 16))
>>> +		return false;
>>> +
>>> +	return true;
>>> +}
>>> +
>>> +static bool parse_imported_net_key(json_object *jobj, uint8_t
>>> key_buf[16],
>>> +								bool
>>> *kr)
>>> +{
>>> +	json_object *jvalue;
>>> +	char *str;
>>> +
>>> +	if (!key_buf)
>>> +		return false;
>>> +
>>> +	if (!json_object_object_get_ex(jobj, "netKey", &jvalue))
>>> +		return false;
>>> +
>>> +	str = (char *)json_object_get_string(jvalue);
>>> +
>>> +	if (!str2hex(str, strlen(str), key_buf, 16))
>>> +		return false;
>>> +
>>> +	/* Get key refresh */
>>> +	if (!json_object_object_get_ex(jobj, "keyRefresh", &jvalue))
>>> +		return false;
>>> +
>>> +	*kr = (bool)json_object_get_boolean(jvalue);
>>> +	return true;
>>> +}
>>> +
>>> +
>>> +static bool add_local_node(struct mesh_node *node, uint16_t
>>> unicast, bool kr,
>>> +				bool ivu, uint32_t iv_idx, uint8_t
>>> dev_key[16],
>>> +				uint16_t net_key_idx, uint8_t
>>> net_key[16])
>>> +{
>>>   	if (!storage_set_iv_index(node->net, iv_idx, ivu))
>>>   		return false;
>>>   
>>> @@ -1444,14 +1534,13 @@ static void get_managed_objects_cb(struct
>>> l_dbus_message *msg, void *user_data)
>>>   	}
>>>   
>>>   	if (is_new) {
>>> -		node = l_new(struct mesh_node, 1);
>>> +		node = node_new(req->data);
>>>   		node->elements = l_queue_new();
>>>   	} else {
>>>   		node = req->data;
>>>   	}
>>>   
>>>   	num_ele = 0;
>>> -
>>>   	while (l_dbus_message_iter_next_entry(&objects, &path,
>>> &interfaces)) {
>>>   		struct l_dbus_message_iter properties;
>>>   		const char *interface;
>>> @@ -1547,6 +1636,44 @@ static void get_managed_objects_cb(struct
>>> l_dbus_message *msg, void *user_data)
>>>   
>>>   		cb(node, agent);
>>>   
>>> +	} else if (req->type == REQUEST_TYPE_IMPORT) {
>>> +
>>> +		node_ready_func_t cb = req->cb;
>>> +		struct node_import_request *import_data = req-
>>>> user_data;
>>> +		struct keyring_net_key net_key;
>>> +
>>> +		if (!agent) {
>>> +			l_error("Interface %s not found",
>>> +						MESH_PROVISION_AGENT_IN
>>> TERFACE);
>>> +			goto fail;
>>> +		}
>>> +
>>> +		node->num_ele = num_ele;
>>> +		set_defaults(node);
>>> +		memcpy(node->uuid, import_data->uuid, 16);
>>> +
>>> +		if (!create_node_config(node))
>>> +			goto fail;
>>> +
>>> +		if (!add_local_node(node, import_data->unicast,
>>> import_data->kr,
>>> +				import_data->iv_update, import_data-
>>>> iv_idx,
>>> +				import_data->dev_key, PRIMARY_NET_IDX,
>>> +							import_data-
>>>> net_key))
>>> +			goto fail;
>>> +
>>> +		memcpy(net_key.old_key, import_data->net_key, 16);
>>> +		net_key.net_idx = PRIMARY_NET_IDX;
>>> +		net_key.phase = KEY_REFRESH_PHASE_NONE;
>>> +
>>> +		if (!keyring_put_remote_dev_key(node, import_data-
>>>> unicast,
>>> +						num_ele, import_data-
>>>> dev_key))
>>> +			goto fail;
>>> +
>>> +		if (!keyring_put_net_key(node, PRIMARY_NET_IDX,
>>> &net_key))
>>> +			goto fail;
>>> +
>>> +		cb(import_data->user_data, MESH_ERROR_NONE, node);
>>> +
>>>   	} else {
>>>   		/* Callback for create node request */
>>>   		node_ready_func_t cb = req->cb;
>>> @@ -1672,6 +1799,59 @@ void node_join(const char *app_path, const
>>> char *sender, const uint8_t *uuid,
>>>   					req, l_free);
>>>   }
>>>   
>>> +
>>> +bool node_import(const char *app_path, const char *sender, void
>>> *json_data,
>>> +		const uint8_t *uuid, node_ready_func_t cb, void
>>> *user_data)
>>> +{
>>> +	struct managed_obj_request *req;
>>> +	struct node_import_request *node;
>>> +
>>> +	l_debug("");
>>> +	node = l_new(struct node_import_request, 1);
>>> +	req = l_new(struct managed_obj_request, 1);
>>> +
>>> +	if (!parse_imported_device_key(json_data, node->dev_key)) {
>>> +		l_error("Failed to parse imported device key");
>>> +		goto fail;
>>> +	}
>>> +
>>> +	if (!parse_imported_unicast_addr(json_data, &node->unicast)) {
>>> +		l_error("Failed to parse imported unicast address");
>>> +		goto fail;
>>> +	}
>>> +
>>> +	if (!parse_imported_iv_index(json_data, &node->iv_idx,
>>> +							&node-
>>>> iv_update)) {
>>> +		l_error("Failed to parse imported iv idx");
>>> +		goto fail;
>>> +	}
>>> +
>>> +
>>> +	if (!parse_imported_net_key(json_data, node->net_key, &node-
>>>> kr)) {
>>> +		l_error("Failed to parse imported network key");
>>> +		goto fail;
>>> +	}
>>> +
>>> +	node->user_data = user_data;
>>> +
>>> +	memcpy(node->uuid, uuid, 16);
>>> +	req->data = (void *) uuid;
>>> +	req->user_data = node;
>>> +	req->cb = cb;
>>> +	req->type = REQUEST_TYPE_IMPORT;
>>> +
>>> +	l_dbus_method_call(dbus_get_bus(), sender, app_path,
>>> +					L_DBUS_INTERFACE_OBJECT_MANAGER
>>> ,
>>> +					"GetManagedObjects", NULL,
>>> +					get_managed_objects_cb,
>>> +					req, l_free);
>>> +	return true;
>>> +fail:
>>> +	json_object_put(json_data);
>>> +	l_free(node);
>>> +	return false;
>>> +}
>>> +
>>>   void node_create(const char *app_path, const char *sender, const
>>> uint8_t *uuid,
>>>   					node_ready_func_t cb, void
>>> *user_data)
>>>   {
>>> diff --git a/mesh/node.h b/mesh/node.h
>>> index 142527b30..9559f9178 100644
>>> --- a/mesh/node.h
>>> +++ b/mesh/node.h
>>> @@ -91,6 +91,8 @@ void node_build_attach_reply(struct mesh_node
>>> *node,
>>>   						struct l_dbus_message
>>> *reply);
>>>   void node_create(const char *app_path, const char *sender, const
>>> uint8_t *uuid,
>>>   					node_ready_func_t cb, void
>>> *user_data);
>>> +bool node_import(const char *app_path, const char *sender, void
>>> *jnode,
>>> +		const uint8_t *uuid, node_ready_func_t cb, void
>>> *user_data);
>>>   void node_id_set(struct mesh_node *node, uint16_t node_id);
>>>   uint16_t node_id_get(struct mesh_node *node);
>>>   bool node_dbus_init(struct l_dbus *bus);


Hello,

I fully agree that maintaining several parsers at the runtime is a quite 
bad idea.

Maybe we should think about using "raw" dbus dictionary for passing data 
for import like it's done with model configuration in Attach() return type.
What do You think ?

BR,
Jakub

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

* Re: Was: mesh: Added ImportLocalNode call with its API --> Multiple Methods?
       [not found]       ` <1561660267.7802.29.camel@intel.com>
@ 2019-06-27 19:51         ` michal.lowas-rzechonek
  2019-06-28 14:29           ` Gix, Brian
  0 siblings, 1 reply; 9+ messages in thread
From: michal.lowas-rzechonek @ 2019-06-27 19:51 UTC (permalink / raw)
  To: Gix, Brian; +Cc: jakub.witowski, Stotland, Inga, linux-bluetooth

Hi everyone,

On 06/27, Gix, Brian wrote:
> I am starting to think we may need multiple methods here to deal with
> the desired use cases:

I don't like this. Back in the day we discussed that we'd like to avoid
D-Bus API bloat...

> * ImportNodeProvData()
> (...)
> The daemon method would perform a GetManagedObject of the calling
> application, to create a node.json with all of the Composition data,
> elements, models, features, etc.

While the initial idea was indeed about importing the whole node,
including configuration, models etc., during implementation we figured
that the reason to do it is to eventually Attach() to such a node - it
doesn't make sense to import something you wouldn't use.

If so, then you need to have an appropriate application anyway, which
the daemon can simply query for all the needed info, as it does at the
moment during Join() and CreateNetwork() calls. This nicely fits into
REQUEST_TYPE processing in get_managed_objects_cb().

This results in smaller API and *significantly* simpler code. As you
mentioned, doing a full migration is complicated.

> * MigrateNode()
> This method would be what Inga and I had originally envisioned for the
> ImportLocalNode() call... It would contain *all* the information that
> a pre-existing node had... including preconfigured pub/sub, features,
> Sequence value that reflected that this node already existed elsewhere
> on the mesh, but was simply being migrated to this device, etc.

The application can do all the configuration using loopback Config
Server messages, so I don't think we need a Migrate() call at all. The
application already receives current node configuration when
Attach()ing, so it can determine if something needs to be reconfigured.

And again, you would need an Attach()able application anyway, so all the
information would need to be duplicated in application's D-Bus
interfaces and in the JSON passed to Migrate() call. This seems like a
violation of DRY principle.

regards
-- 
Michał Lowas-Rzechonek <michal.lowas-rzechonek@silvair.com>
Silvair http://silvair.com
Jasnogórska 44, 31-358 Krakow, POLAND

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

* Re: Was: mesh: Added ImportLocalNode call with its API --> Multiple Methods?
  2019-06-27 19:51         ` Was: mesh: Added ImportLocalNode call with its API --> Multiple Methods? michal.lowas-rzechonek
@ 2019-06-28 14:29           ` Gix, Brian
  2019-07-01  9:20             ` michal.lowas-rzechonek
  0 siblings, 1 reply; 9+ messages in thread
From: Gix, Brian @ 2019-06-28 14:29 UTC (permalink / raw)
  To: michal.lowas-rzechonek; +Cc: jakub.witowski, linux-bluetooth, Stotland, Inga

Hi All,

On Thu, 2019-06-27 at 21:51 +0200, michal.lowas-rzechonek@silvair.com wrote:
> Hi everyone,
> 
> On 06/27, Gix, Brian wrote:
> > I am starting to think we may need multiple methods here to deal with
> > the desired use cases:
> 
> I don't like this. Back in the day we discussed that we'd like to avoid
> D-Bus API bloat...
> 
> > * ImportNodeProvData()
> > (...)
> > The daemon method would perform a GetManagedObject of the calling
> > application, to create a node.json with all of the Composition data,
> > elements, models, features, etc.
> 
> While the initial idea was indeed about importing the whole node,
> including configuration, models etc., during implementation we figured
> that the reason to do it is to eventually Attach() to such a node - it
> doesn't make sense to import something you wouldn't use.
> 
> If so, then you need to have an appropriate application anyway, which
> the daemon can simply query for all the needed info, as it does at the
> moment during Join() and CreateNetwork() calls. This nicely fits into
> REQUEST_TYPE processing in get_managed_objects_cb().
> 
> This results in smaller API and *significantly* simpler code. As you
> mentioned, doing a full migration is complicated.
> 
> > * MigrateNode()
> > This method would be what Inga and I had originally envisioned for the
> > ImportLocalNode() call... It would contain *all* the information that
> > a pre-existing node had... including preconfigured pub/sub, features,
> > Sequence value that reflected that this node already existed elsewhere
> > on the mesh, but was simply being migrated to this device, etc.
> 
> The application can do all the configuration using loopback Config
> Server messages, so I don't think we need a Migrate() call at all. The
> application already receives current node configuration when
> Attach()ing, so it can determine if something needs to be reconfigured.


I don't see this actually working very well.  Firstly, these "Loop-back" Config Server messages do not all
actually work unless the node is a Provisioner, which will *not* be required of most nodes.

And However, even if the App went through all of the restoration of all the settings under the control of the
Provisioner via this Config Server loop-back method, there is still the critical issue of Sequence numbers.

A node that is being migrated needs to pick up where it was prior to the migration, assuming that it has
already had conversations with other nodes, and has entries in their Replay Protection List.


If we want a single method (avoid API bloat) I don't think we have any choice but to use a well developed
structure (like JSON or XML) that faithfully sets up the node in the state it was in prior to Migration.  We
can perhaps "Overload" this functionality by allowing a minimal JSON with only Prov Data parts, if we are
looking for a Provisioning shortcut, and always requiring the ObjectManager calls fetch the Composition (if the
JSON was minimal) and to Sanity check the Composition (if the JSON contains a fully developed/configured
Migrated node).

> 
> And again, you would need an Attach()able application anyway, so all the
> information would need to be duplicated in application's D-Bus
> interfaces and in the JSON passed to Migrate() call. This seems like a
> violation of DRY principle.
> 
> regards

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

* Re: Was: mesh: Added ImportLocalNode call with its API --> Multiple Methods?
  2019-06-28 14:29           ` Gix, Brian
@ 2019-07-01  9:20             ` michal.lowas-rzechonek
  2019-07-01 15:57               ` Gix, Brian
  0 siblings, 1 reply; 9+ messages in thread
From: michal.lowas-rzechonek @ 2019-07-01  9:20 UTC (permalink / raw)
  To: Gix, Brian; +Cc: jakub.witowski, linux-bluetooth, Stotland, Inga

Hi,

On 06/28, Gix, Brian wrote:
> > The application can do all the configuration using loopback Config
> > Server messages, so I don't think we need a Migrate() call at all. The
> > application already receives current node configuration when
> > Attach()ing, so it can determine if something needs to be reconfigured.
>
> I don't see this actually working very well.  Firstly, these
> "Loop-back" Config Server messages do not all actually work unless the
> node is a Provisioner, which will *not* be required of most nodes.

I don't think this is true. Application can send messages from the
application to its node using straightforward Send() call with key index
0x7fff, and they are processed correctly. While this might be considered
a "hack", you've seen that I am currently working on DevKeySend() API.

As far as I can tell, a node always has its own Device Key in the
keyring, so loopback messaging via DevKeySend() is going to work even on
non-provisioner nodes.

> And However, even if the App went through all of the restoration of
> all the settings under the control of the Provisioner via this Config
> Server loop-back method, there is still the critical issue of Sequence
> numbers.

Indeed. I would propose simply adding starting sequence number to the
dictionary passed to ImportLocalNode() call. It can be made optional,
i.e. when the application doesn't provide it, the imported node starts
counting from 0.

> If we want a single method (avoid API bloat) I don't think we have any
> choice but to use a well developed structure (like JSON or XML) that
> faithfully sets up the node in the state it was in prior to Migration.
>
> We can perhaps "Overload" this functionality by allowing a minimal
> JSON with only Prov Data parts, if we are looking for a Provisioning
> shortcut, and always requiring the ObjectManager calls fetch the
> Composition (if the JSON was minimal) and to Sanity check the
> Composition (if the JSON contains a fully developed/configured
> Migrated node).

Ok, that sounds better. We could start by implementing the "Provisoining
shortcut" variant, and add full-blown migration when it's needed.

Would that be OK from your POV?

regards
-- 
Michał Lowas-Rzechonek <michal.lowas-rzechonek@silvair.com>
Silvair http://silvair.com
Jasnogórska 44, 31-358 Krakow, POLAND

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

* RE: Was: mesh: Added ImportLocalNode call with its API --> Multiple Methods?
  2019-07-01  9:20             ` michal.lowas-rzechonek
@ 2019-07-01 15:57               ` Gix, Brian
  2019-07-02  5:43                 ` Stotland, Inga
  0 siblings, 1 reply; 9+ messages in thread
From: Gix, Brian @ 2019-07-01 15:57 UTC (permalink / raw)
  To: michal.lowas-rzechonek; +Cc: jakub.witowski, linux-bluetooth, Stotland, Inga

Hi Michal.

> > We can perhaps "Overload" this functionality by allowing a minimal
> > JSON with only Prov Data parts, if we are looking for a Provisioning
> > shortcut, and always requiring the ObjectManager calls fetch the
> > Composition (if the JSON was minimal) and to Sanity check the
> > Composition (if the JSON contains a fully developed/configured
> > Migrated node).
> 
> Ok, that sounds better. We could start by implementing the "Provisoining
> shortcut" variant, and add full-blown migration when it's needed.
> 
> Would that be OK from your POV?

This would be OK for me.  How about Inga?

BR, 
Brian

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

* Re: Was: mesh: Added ImportLocalNode call with its API --> Multiple Methods?
  2019-07-01 15:57               ` Gix, Brian
@ 2019-07-02  5:43                 ` Stotland, Inga
  0 siblings, 0 replies; 9+ messages in thread
From: Stotland, Inga @ 2019-07-02  5:43 UTC (permalink / raw)
  To: michal.lowas-rzechonek, Gix, Brian; +Cc: jakub.witowski, linux-bluetooth

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

Hi Brian, Michal, Jakub,

On Mon, 2019-07-01 at 08:57 -0700, Gix, Brian wrote:
> Hi Michal.
> 
> > > We can perhaps "Overload" this functionality by allowing a
> > > minimal
> > > JSON with only Prov Data parts, if we are looking for a
> > > Provisioning
> > > shortcut, and always requiring the ObjectManager calls fetch the
> > > Composition (if the JSON was minimal) and to Sanity check the
> > > Composition (if the JSON contains a fully developed/configured
> > > Migrated node).
> > 
> > Ok, that sounds better. We could start by implementing the
> > "Provisoining
> > shortcut" variant, and add full-blown migration when it's needed.
> > 
> > Would that be OK from your POV?
> 
> This would be OK for me.  How about Inga?
> 
> BR, 
> Brian


So what is the final versionof the ImportLocalNode() will look like?

uint64 token ImportLocalNode(object app_path, array{byte} uuid, string
config_data)

or

uint64 token ImportLocalNode(object app_path, array{byte} uuid, byte
config_data_type, string config_data)

where config_data_type indicates the format of config_data (json, xml,
etc)

A bit clunky, but, if we want to keep everything wrapped in one method
call, I don't see a way around this.

Also, my feeling is that app_path = NULL shuold be allowed. In this
case node/app configuration is incurred solely from config_data, daemon
will check for the presence of all the mandatory settings.
In case both app_path and fully fleshed config_data are provided, a
series of checks will need to be performed to validate the coherncy of
the configuration.

Best regards,
Inga
 


[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 3265 bytes --]

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

end of thread, other threads:[~2019-07-02  5:44 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-25 14:38 [PATCH v2] mesh: Added ImportLocalNode call with its API Jakub Witowski
2019-06-26 17:01 ` Gix, Brian
2019-06-26 17:53   ` Stotland, Inga
2019-06-26 18:37     ` Jakub Witowski
     [not found]     ` <CAMCw4t3pXTbtt05RD694jzF_MNT_J9dcFMtA7iuD4ujZT9FDbg@mail.gmail.com>
     [not found]       ` <1561660267.7802.29.camel@intel.com>
2019-06-27 19:51         ` Was: mesh: Added ImportLocalNode call with its API --> Multiple Methods? michal.lowas-rzechonek
2019-06-28 14:29           ` Gix, Brian
2019-07-01  9:20             ` michal.lowas-rzechonek
2019-07-01 15:57               ` Gix, Brian
2019-07-02  5:43                 ` Stotland, Inga

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).