2019-07-29 14:20:14

by Michał Lowas-Rzechonek

[permalink] [raw]
Subject: [PATCH BlueZ v6 0/3] Implement org.bluez.mesh.Network1.Import() D-Bus API

+ Reworked the API to new signature
+ Removed dependencies on mesh-config

Jakub Witowski (1):
mesh: Extract read_* functions in mesh-config-json

Michał Lowas-Rzechonek (2):
mesh: Add documentation for Import() D-Bus API
mesh: Implement Import() D-Bus API of org.bluez.mesh.Network1
interface

doc/mesh-api.txt | 57 ++++++++++++++------
mesh/mesh-config-json.c | 68 +++++++++++++++++-------
mesh/mesh.c | 115 +++++++++++++++++++++++++++++++++++++++-
mesh/node.c | 87 +++++++++++++++++++++++++++++-
mesh/node.h | 5 ++
5 files changed, 295 insertions(+), 37 deletions(-)

--
2.19.1


2019-07-29 14:20:14

by Michał Lowas-Rzechonek

[permalink] [raw]
Subject: [PATCH BlueZ v6 1/3] mesh: Extract read_* functions in mesh-config-json

From: Jakub Witowski <[email protected]>

This is a small improvement of read_node function readability.
---
mesh/mesh-config-json.c | 68 +++++++++++++++++++++++++++++------------
1 file changed, 49 insertions(+), 19 deletions(-)

diff --git a/mesh/mesh-config-json.c b/mesh/mesh-config-json.c
index e3baf5dc6..c63883a3d 100644
--- a/mesh/mesh-config-json.c
+++ b/mesh/mesh-config-json.c
@@ -297,6 +297,51 @@ static json_object *jarray_key_del(json_object *jarray, int16_t idx)
return jarray_new;
}

+static bool read_unicast_address(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 read_default_ttl(json_object *jobj,
+ uint8_t *ttl)
+{
+ json_object *jvalue;
+ int val;
+
+ if (!json_object_object_get_ex(jobj, "defaultTTL", &jvalue))
+ return false;
+
+ val = json_object_get_int(jvalue);
+
+ if (val < 0 || val == 1 || val > DEFAULT_TTL)
+ return false;
+ *ttl = (uint8_t) val;
+
+ return true;
+}
+
+static bool read_seq_number(json_object *jobj, uint32_t *seq_number)
+{
+ json_object *jvalue;
+
+ if (!json_object_object_get_ex(jobj, "sequenceNumber", &jvalue))
+ return false;
+
+ *seq_number = json_object_get_int(jvalue);
+ return true;
+}
+
static bool read_iv_index(json_object *jobj, uint32_t *idx, bool *update)
{
int tmp;
@@ -424,7 +469,7 @@ fail:
return false;
}

-static bool read_net_keys(json_object *jobj, struct mesh_config_node *node)
+static bool read_net_keys(json_object *jobj, struct mesh_config_node *node)
{
json_object *jarray;
int len;
@@ -1294,7 +1339,6 @@ static bool read_net_transmit(json_object *jobj, struct mesh_config_node *node)
static bool read_node(json_object *jnode, struct mesh_config_node *node)
{
json_object *jvalue;
- char *str;

if (!read_iv_index(jnode, &node->iv_index, &node->iv_update)) {
l_info("Failed to read IV index");
@@ -1318,25 +1362,11 @@ static bool read_node(json_object *jnode, struct mesh_config_node *node)

parse_features(jnode, node);

- if (!json_object_object_get_ex(jnode, "unicastAddress", &jvalue)) {
- l_info("Bad config: Unicast address must be present");
- return false;
- }
-
- str = (char *)json_object_get_string(jvalue);
- if (sscanf(str, "%04hx", &node->unicast) != 1)
- return false;
-
- if (json_object_object_get_ex(jnode, "defaultTTL", &jvalue)) {
- int ttl = json_object_get_int(jvalue);
+ read_unicast_address(jnode, &node->unicast);

- if (ttl < 0 || ttl == 1 || ttl > DEFAULT_TTL)
- return false;
- node->ttl = (uint8_t) ttl;
- }
+ read_default_ttl(jnode, &node->ttl);

- if (json_object_object_get_ex(jnode, "sequenceNumber", &jvalue))
- node->seq_number = json_object_get_int(jvalue);
+ read_seq_number(jnode, &node->seq_number);

/* Check for required "elements" property */
if (!json_object_object_get_ex(jnode, "elements", &jvalue))
--
2.19.1

2019-07-29 14:20:14

by Michał Lowas-Rzechonek

[permalink] [raw]
Subject: [PATCH BlueZ v6 3/3] mesh: Implement Import() D-Bus API of org.bluez.mesh.Network1 interface

This method allows local nodes to be imported from an external
provisioning database, enabling applications to create mesh nodes
without using provisioning procedure.

The procedure is similar to provisioning procedure, but all data
exchange happens over D-Bus.
---
mesh/mesh.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++-
mesh/node.c | 87 ++++++++++++++++++++++++++++++++++++++-
mesh/node.h | 5 +++
3 files changed, 204 insertions(+), 3 deletions(-)

diff --git a/mesh/mesh.c b/mesh/mesh.c
index 9c6b9a70e..b660a7ef2 100644
--- a/mesh/mesh.c
+++ b/mesh/mesh.c
@@ -33,6 +33,7 @@
#include "mesh/error.h"
#include "mesh/agent.h"
#include "mesh/mesh.h"
+#include "mesh/mesh-defs.h"

/*
* The default values for mesh configuration. Can be
@@ -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,114 @@ 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_call(struct l_dbus *dbus,
+ struct l_dbus_message *msg,
+ void *user_data)
+{
+ const char *app_path, *sender;
+ struct l_dbus_message *pending_msg = NULL;
+ struct l_dbus_message_iter iter_uuid;
+ struct l_dbus_message_iter iter_dev_key;
+ struct l_dbus_message_iter iter_net_key;
+ struct l_dbus_message_iter iter_flags;
+ const char *key;
+ struct l_dbus_message_iter var;
+
+ uint8_t *uuid;
+ uint8_t *dev_key;
+ uint8_t *net_key;
+ uint16_t net_idx;
+ bool kr = false;
+ bool ivu = false;
+ uint32_t iv_index;
+ uint16_t unicast;
+ uint32_t n;
+
+ l_debug("Import local node request");
+
+ if (!l_dbus_message_get_arguments(msg, "oayayayqa{sv}uq",
+ &app_path, &iter_uuid,
+ &iter_dev_key, &iter_net_key,
+ &net_idx, &iter_flags,
+ &iv_index,
+ &unicast))
+ 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");
+
+ if (!l_dbus_message_iter_get_fixed_array(&iter_dev_key, &dev_key, &n) ||
+ n != 16)
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+ "Bad dev key");
+
+ if (!l_dbus_message_iter_get_fixed_array(&iter_net_key, &net_key, &n) ||
+ n != 16)
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+ "Bad net key");
+
+ if (net_idx > MAX_KEY_IDX)
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+ "Bad net index");
+
+ while (l_dbus_message_iter_next_entry(&iter_flags, &key, &var)) {
+ if (!strcmp(key, "IVUpdate")) {
+ if (!l_dbus_message_iter_get_variant(&var, "b",
+ &ivu))
+ goto fail;
+ continue;
+ }
+
+ if (!strcmp(key, "KeyRefresh")) {
+ if (!l_dbus_message_iter_get_variant(&var, "b",
+ &kr))
+ goto fail;
+ continue;
+ }
+
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+ "Bad flags");
+ }
+
+ if (!IS_UNICAST(unicast))
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+ "Bad address");
+
+ 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, uuid, dev_key, net_key, net_idx,
+ kr, ivu, iv_index, unicast,
+ create_node_ready_cb, pending_msg))
+ goto fail;
+
+ return NULL;
+
+fail:
+ if (pending_msg) {
+ l_dbus_message_unref(msg);
+ l_queue_remove(pending_queue, pending_msg);
+ }
+
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Node import failed");
+}
+
static void setup_network_interface(struct l_dbus_interface *iface)
{
l_dbus_interface_method(iface, "Join", 0, join_network_call, "",
@@ -612,8 +715,16 @@ 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, "Import", 0,
+ import_call,
+ "t", "oayayayqa{sv}uq", "token",
+ "app", "uuid", "dev_key", "net_key",
+ "net_index", "flags", "iv_index",
+ "unicast");
}

bool mesh_dbus_init(struct l_dbus *dbus)
diff --git a/mesh/node.c b/mesh/node.c
index 579e69892..bff73cfc7 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -62,6 +62,7 @@ enum request_type {
REQUEST_TYPE_JOIN,
REQUEST_TYPE_ATTACH,
REQUEST_TYPE_CREATE,
+ REQUEST_TYPE_IMPORT,
};

struct node_element {
@@ -111,6 +112,19 @@ struct mesh_node {
uint8_t beacon;
};

+struct node_import {
+ uint8_t dev_key[16];
+
+ uint8_t net_key[16];
+ uint16_t net_idx;
+ struct {
+ bool ivu;
+ bool kr;
+ } flags;
+ uint32_t iv_index;
+ uint16_t unicast;
+};
+
struct managed_obj_request {
struct mesh_node *node;
union {
@@ -121,6 +135,7 @@ struct managed_obj_request {
enum request_type type;
union {
struct mesh_node *attach;
+ struct node_import *import;
};
};

@@ -1712,6 +1727,36 @@ static void get_managed_objects_cb(struct l_dbus_message *msg, void *user_data)
if (!create_node_config(node, node->uuid))
goto fail;

+ } else if (req->type == REQUEST_TYPE_IMPORT) {
+ struct node_import *import = req->import;
+ struct keyring_net_key net_key;
+
+ if (!create_node_config(node, node->uuid))
+ goto fail;
+
+ if (!add_local_node(node, import->unicast, import->flags.kr,
+ import->flags.ivu,
+ import->iv_index, import->dev_key,
+ import->net_idx, import->net_key))
+ goto fail;
+
+ memcpy(net_key.old_key, import->net_key, 16);
+ net_key.net_idx = import->net_idx;
+ if (import->flags.kr)
+ net_key.phase = KEY_REFRESH_PHASE_TWO;
+ else
+ net_key.phase = KEY_REFRESH_PHASE_NONE;
+
+ /* Initialize directory for storing keyring info */
+ init_storage_dir(node);
+
+ if (!keyring_put_remote_dev_key(node, import->unicast,
+ num_ele, import->dev_key))
+ goto fail;
+
+ if (!keyring_put_net_key(node, import->net_idx, &net_key))
+ goto fail;
+
} else {
/* Callback for create node request */
struct keyring_net_key net_key;
@@ -1749,7 +1794,8 @@ static void get_managed_objects_cb(struct l_dbus_message *msg, void *user_data)
else
req->ready_cb(req->pending_msg, MESH_ERROR_NONE, node);

- return;
+ goto done;
+
fail:
if (agent)
mesh_agent_remove(agent);
@@ -1762,6 +1808,10 @@ fail:
req->join_ready_cb(NULL, NULL);
else
req->ready_cb(req->pending_msg, MESH_ERROR_FAILED, NULL);
+
+done:
+ if (req->type == REQUEST_TYPE_IMPORT)
+ l_free(req->import);
}

/* Establish relationship between application and mesh node */
@@ -1825,6 +1875,41 @@ 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, const uint8_t *uuid,
+ const uint8_t dev_key[16], const uint8_t net_key[16],
+ uint16_t net_idx, bool kr, bool ivu,
+ uint32_t iv_index, uint16_t unicast,
+ node_ready_func_t cb, void *user_data)
+{
+ struct managed_obj_request *req;
+
+ l_debug("");
+
+ req = l_new(struct managed_obj_request, 1);
+
+ req->node = node_new(uuid);
+ req->ready_cb = cb;
+ req->pending_msg = user_data;
+
+ req->import = l_new(struct node_import, 1);
+ memcpy(req->import->dev_key, dev_key, 16);
+ memcpy(req->import->net_key, net_key, 16);
+ req->import->net_idx = net_idx;
+ req->import->flags.kr = kr;
+ req->import->flags.ivu = ivu;
+ req->import->iv_index = iv_index;
+ req->import->unicast = unicast;
+
+ 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;
+}
+
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 56ca796cd..a4cac028d 100644
--- a/mesh/node.h
+++ b/mesh/node.h
@@ -95,6 +95,11 @@ 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, const uint8_t *uuid,
+ const uint8_t dev_key[16], const uint8_t net_key[16],
+ uint16_t net_idx, bool kr, bool ivu,
+ uint32_t iv_index, uint16_t unicast,
+ 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.19.1

2019-07-29 14:22:07

by Michał Lowas-Rzechonek

[permalink] [raw]
Subject: [PATCH BlueZ v6 2/3] mesh: Add documentation for Import() D-Bus API

This replaces previously specified ImportLocalNode() call
---
doc/mesh-api.txt | 57 +++++++++++++++++++++++++++++++++++-------------
1 file changed, 42 insertions(+), 15 deletions(-)

diff --git a/doc/mesh-api.txt b/doc/mesh-api.txt
index 7b62c0af4..255104ab6 100644
--- a/doc/mesh-api.txt
+++ b/doc/mesh-api.txt
@@ -151,16 +151,50 @@ Methods:
org.bluez.mesh.Error.InvalidArguments
org.bluez.mesh.Error.AlreadyExists,

- uint64 token ImportLocalNode(string json_data)
+ uint64 token Import(object app_root, array{byte}[16] uuid,
+ array{byte}[16] dev_key,
+ array{byte}[16] net_key, uint16 net_index,
+ dict flags, uint32 iv_index, uint16 unicast)

This method creates a local mesh node based on node
configuration that has been generated outside bluetooth-meshd.

- 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
- included token will be ignored in favor of a locally generated
- token value.
+ The app_root parameter is a D-Bus object root path of the
+ application that implements org.bluez.mesh.Application1
+ interface.
+
+ The uuid parameter is a 16-byte array that contains Device UUID.
+ This UUID must be unique (at least from the daemon perspective),
+ therefore attempting to call this function using already
+ registered UUID results in an error.
+
+ The dev_key parameter is the 16-byte value of the dev key of
+ the imported mesh node.
+
+ Remaining parameters correspond to provisioning data:
+
+ The net_key and net_index parameters describe the network (or a
+ subnet, if net_index is not 0) the imported mesh node belongs
+ to.
+
+ The flags parameter is a dictionary containing provisioning
+ flags. Supported values are:
+
+ boolean IVUpdate
+
+ When true, indicates that the network is in the
+ middle of IV Index Update procedure.
+
+ boolean KeyRefresh
+
+ When true, indicates that the specified net key
+ is in the middle of a key refresh procedure.
+
+ The iv_index parameter is the current IV Index value used by
+ the network. This value is known by the provisioner.
+
+ The unicast parameter is the primary unicast address of the
+ imported node.

The returned token must be preserved by the application in
order to authenticate itself to the mesh daemon and attach to
@@ -168,13 +202,10 @@ Methods:
permanently remove the identity of the mesh node by calling
Leave() method.

- It is an error to attempt importing a node with already
- registered Device UUID.
-
PossibleErrors:
org.bluez.mesh.Error.InvalidArguments,
- org.bluez.mesh.Error.AlreadyExists
- org.bluez.mesh.Error.NotFound,
+ org.bluez.mesh.Error.AlreadyExists,
+ org.bluez.mesh.Error.NotSupported,
org.bluez.mesh.Error.Failed

Mesh Node Hierarchy
@@ -1069,7 +1100,3 @@ Properties:

Uniform Resource Identifier points to out-of-band (OOB)
information (e.g., a public key)
-
-Mesh Node Configuration Schema
-==============================
-<TBD>
--
2.19.1

2019-07-31 00:23:39

by Stotland, Inga

[permalink] [raw]
Subject: Re: [PATCH BlueZ v6 1/3] mesh: Extract read_* functions in mesh-config-json

Hi Jakub,

On Mon, 2019-07-29 at 15:40 +0200, Michał Lowas-Rzechonek wrote:
> From: Jakub Witowski <[email protected]>
>
> This is a small improvement of read_node function readability.
> ---
> mesh/mesh-config-json.c | 68 +++++++++++++++++++++++++++++--------
> ----
> 1 file changed, 49 insertions(+), 19 deletions(-)
>
> diff --git a/mesh/mesh-config-json.c b/mesh/mesh-config-json.c
> index e3baf5dc6..c63883a3d 100644
> --- a/mesh/mesh-config-json.c
> +++ b/mesh/mesh-config-json.c
> @@ -297,6 +297,51 @@ static json_object *jarray_key_del(json_object
> *jarray, int16_t idx)
> return jarray_new;
> }
>
> +static bool read_unicast_address(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 read_default_ttl(json_object *jobj,
> + uint8_t
> *ttl)
> +{
> + json_object *jvalue;
> + int val;
> +
> + if (!json_object_object_get_ex(jobj, "defaultTTL", &jvalue))
> + return false;
> +
> + val = json_object_get_int(jvalue);
> +
> + if (val < 0 || val == 1 || val > DEFAULT_TTL)
> + return false;

New line here to separate from "if" statement

> + *ttl = (uint8_t) val;
> +
> + return true;
> +}
> +
> +static bool read_seq_number(json_object *jobj, uint32_t *seq_number)
> +{
> + json_object *jvalue;
> +
> + if (!json_object_object_get_ex(jobj, "sequenceNumber",
> &jvalue))
> + return false;
> +
> + *seq_number = json_object_get_int(jvalue);
> + return true;
> +}
> +
> static bool read_iv_index(json_object *jobj, uint32_t *idx, bool
> *update)
> {
> int tmp;
> @@ -424,7 +469,7 @@ fail:
> return false;
> }
>
> -static bool read_net_keys(json_object *jobj, struct
> mesh_config_node *node)
> +static bool read_net_keys(json_object *jobj, struct mesh_config_node
> *node)
> {
> json_object *jarray;
> int len;
> @@ -1294,7 +1339,6 @@ static bool read_net_transmit(json_object
> *jobj, struct mesh_config_node *node)
> static bool read_node(json_object *jnode, struct mesh_config_node
> *node)
> {
> json_object *jvalue;
> - char *str;
>
> if (!read_iv_index(jnode, &node->iv_index, &node->iv_update)) {
> l_info("Failed to read IV index");
> @@ -1318,25 +1362,11 @@ static bool read_node(json_object *jnode,
> struct mesh_config_node *node)
>
> parse_features(jnode, node);
>
> - if (!json_object_object_get_ex(jnode, "unicastAddress",
> &jvalue)) {
> - l_info("Bad config: Unicast address must be present");
> - return false;
> - }
> -
> - str = (char *)json_object_get_string(jvalue);
> - if (sscanf(str, "%04hx", &node->unicast) != 1)
> - return false;
> -
> - if (json_object_object_get_ex(jnode, "defaultTTL", &jvalue)) {
> - int ttl = json_object_get_int(jvalue);
> + read_unicast_address(jnode, &node->unicast);
>

The return status needs to be checked: unicast must be present


> - if (ttl < 0 || ttl == 1 || ttl > DEFAULT_TTL)
> - return false;
> - node->ttl = (uint8_t) ttl;
> - }
> + read_default_ttl(jnode, &node->ttl);
>
I would prefer to keep ttl and sequesnce number parsing as they are
now:
they are not mandatory, but we don't want to allow for bad format,
i.e., we dont want to fail if the fields are absent, but we do want to
fail if the format is wrong.

> - if (json_object_object_get_ex(jnode, "sequenceNumber",
> &jvalue))
> - node->seq_number = json_object_get_int(jvalue);
> + read_seq_number(jnode, &node->seq_number);
>
> /* Check for required "elements" property */
> if (!json_object_object_get_ex(jnode, "elements", &jvalue))

Regards,
Inga


Attachments:
smime.p7s (3.19 kB)