From: "Gix, Brian" <brian.gix@intel.com>
To: "jakub.witowski@silvair.com" <jakub.witowski@silvair.com>,
"linux-bluetooth@vger.kernel.org"
<linux-bluetooth@vger.kernel.org>
Cc: "michal.lowas-rzechonek@silvair.com"
<michal.lowas-rzechonek@silvair.com>,
"Stotland, Inga" <inga.stotland@intel.com>
Subject: Re: [PATCH v2] mesh: Added ImportLocalNode call with its API
Date: Wed, 26 Jun 2019 17:01:10 +0000 [thread overview]
Message-ID: <1561568468.22940.16.camel@intel.com> (raw)
In-Reply-To: <20190625143855.29889-1-jakub.witowski@silvair.com>
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);
next prev parent reply other threads:[~2019-06-26 17:01 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-06-25 14:38 [PATCH v2] mesh: Added ImportLocalNode call with its API Jakub Witowski
2019-06-26 17:01 ` Gix, Brian [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1561568468.22940.16.camel@intel.com \
--to=brian.gix@intel.com \
--cc=inga.stotland@intel.com \
--cc=jakub.witowski@silvair.com \
--cc=linux-bluetooth@vger.kernel.org \
--cc=michal.lowas-rzechonek@silvair.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).