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