blob: 019b4e173bbe078dfeccd9c623d330ed438a954c [file] [log] [blame]
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2017 Intel Corporation. All rights reserved.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <glib.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <json-c/json.h>
#include <sys/stat.h>
#include <glib.h>
#include "src/shared/util.h"
#include "src/shared/shell.h"
#include "mesh/mesh-net.h"
#include "mesh/crypto.h"
#include "mesh/keys.h"
#include "mesh/net.h"
#include "mesh/node.h"
#include "mesh/util.h"
#include "mesh/prov-db.h"
#define CHECK_KEY_IDX_RANGE(x) (((x) >= 0) && ((x) <= 4095))
static const char *prov_filename;
static const char *local_filename;
static char* prov_file_read(const char *filename)
{
int fd;
char *str;
struct stat st;
ssize_t sz;
if (!filename)
return NULL;
fd = open(filename,O_RDONLY);
if (!fd)
return NULL;
if (fstat(fd, &st) == -1) {
close(fd);
return NULL;
}
str = (char *) g_malloc0(st.st_size + 1);
if (!str) {
close(fd);
return NULL;
}
sz = read(fd, str, st.st_size);
if (sz != st.st_size)
bt_shell_printf("Incomplete read: %d vs %d\n", (int)sz,
(int)(st.st_size));
close(fd);
return str;
}
static void prov_file_write(json_object *jmain, bool local)
{
FILE *outfile;
const char *out_str;
const char *out_filename;
if (local)
out_filename = local_filename;
else
out_filename = prov_filename;
outfile = fopen(out_filename, "wr");
if (!outfile) {
bt_shell_printf("Failed to open file %s for writing\n", out_filename);
return;
}
out_str = json_object_to_json_string_ext(jmain,
JSON_C_TO_STRING_PRETTY);
fwrite(out_str, sizeof(char), strlen(out_str), outfile);
fclose(outfile);
}
static void put_uint16(json_object *jobject, const char *desc, uint16_t value)
{
json_object *jstring;
char buf[5];
snprintf(buf, 5, "%4.4x", value);
jstring = json_object_new_string(buf);
json_object_object_add(jobject, desc, jstring);
}
static void put_uint32(json_object *jobject, const char *desc, uint32_t value)
{
json_object *jstring;
char buf[9];
snprintf(buf, 9, "%8.8x", value);
jstring = json_object_new_string(buf);
json_object_object_add(jobject, desc, jstring);
}
static void put_uint16_array_entry(json_object *jarray, uint16_t value)
{
json_object *jstring;
char buf[5];
snprintf(buf, 5, "%4.4x", value);
jstring = json_object_new_string(buf);
json_object_array_add(jarray, jstring);
}
static void put_uint32_array_entry(json_object *jarray, uint32_t value)
{
json_object *jstring;
char buf[9];
snprintf(buf, 9, "%8.8x", value);
jstring = json_object_new_string(buf);
json_object_array_add(jarray, jstring);
}
static void put_uint16_list(json_object *jarray, GList *list)
{
GList *l;
if (!list)
return;
for (l = list; l; l = l->next) {
uint32_t ivalue = GPOINTER_TO_UINT(l->data);
put_uint16_array_entry(jarray, ivalue);
}
}
static void add_node_idxs(json_object *jnode, const char *desc,
GList *idxs)
{
json_object *jarray;
jarray = json_object_new_array();
put_uint16_list(jarray, idxs);
json_object_object_add(jnode, desc, jarray);
}
static bool parse_unicast_range(json_object *jobject)
{
int cnt;
int i;
cnt = json_object_array_length(jobject);
for (i = 0; i < cnt; ++i) {
json_object *jrange;
json_object *jvalue;
uint16_t low, high;
char *str;
jrange = json_object_array_get_idx(jobject, i);
json_object_object_get_ex(jrange, "lowAddress", &jvalue);
str = (char *)json_object_get_string(jvalue);
if (sscanf(str, "%04hx", &low) != 1)
return false;
json_object_object_get_ex(jrange, "highAddress", &jvalue);
str = (char *)json_object_get_string(jvalue);
if (sscanf(str, "%04hx", &high) != 1)
return false;
if(high < low)
return false;
net_add_address_pool(low, high);
}
return true;
}
static int parse_node_keys(struct mesh_node *node, json_object *jidxs,
bool is_app_key)
{
int idx_cnt;
int i;
idx_cnt = json_object_array_length(jidxs);
for (i = 0; i < idx_cnt; ++i) {
int idx;
json_object *jvalue;
jvalue = json_object_array_get_idx(jidxs, i);
if (!jvalue)
break;
idx = json_object_get_int(jvalue);
if (!CHECK_KEY_IDX_RANGE(idx))
break;
if (is_app_key)
node_app_key_add(node, idx);
else
node_net_key_add(node, idx);
}
return i;
}
static bool parse_composition_models(struct mesh_node *node, int index,
json_object *jmodels)
{
int model_cnt;
int i;
model_cnt = json_object_array_length(jmodels);
for (i = 0; i < model_cnt; ++i) {
json_object *jmodel;
char *str;
uint32_t model_id;
int len;
jmodel = json_object_array_get_idx(jmodels, i);
str = (char *)json_object_get_string(jmodel);
len = strlen(str);
if (len != 4 && len != 8)
return false;
if (sscanf(str, "%08x", &model_id) != 1)
return false;
if (len == 4)
model_id += 0xffff0000;
node_set_model(node, index, model_id);
}
return true;
}
static bool parse_composition_elements(struct mesh_node *node,
json_object *jelements)
{
int el_cnt;
int i;
el_cnt = json_object_array_length(jelements);
node_set_num_elements(node, el_cnt);
for (i = 0; i < el_cnt; ++i) {
json_object *jelement;
json_object *jmodels;
json_object *jvalue;
int index;
jelement = json_object_array_get_idx(jelements, i);
json_object_object_get_ex(jelement, "elementIndex", &jvalue);
if (jvalue) {
index = json_object_get_int(jvalue);
if (index >= el_cnt) {
return false;
}
} else
return false;
if (!node_set_element(node, index))
return false;
json_object_object_get_ex(jelement, "models", &jmodels);
if (!jmodels)
continue;
if(!parse_composition_models(node, index, jmodels))
return false;
}
return true;
}
static bool parse_model_pub(struct mesh_node *node, int ele_idx,
uint32_t model_id, json_object *jpub)
{
json_object *jvalue;
struct mesh_publication pub;
char *str;
memset(&pub, 0, sizeof(struct mesh_publication));
/* Read only required fields */
json_object_object_get_ex(jpub, "address", &jvalue);
if (!jvalue)
return false;
str = (char *)json_object_get_string(jvalue);
if (sscanf(str, "%04hx", &pub.u.addr16) != 1)
return false;
json_object_object_get_ex(jpub, "index", &jvalue);
if (!jvalue)
return false;
str = (char *)json_object_get_string(jvalue);
if (sscanf(str, "%04hx", &pub.app_idx) != 1)
return false;
json_object_object_get_ex(jpub, "ttl", &jvalue);
pub.ttl = json_object_get_int(jvalue);
if (!node_model_pub_set(node, ele_idx, model_id, &pub))
return false;
return true;
}
static bool parse_bindings(struct mesh_node *node, int ele_idx,
uint32_t model_id, json_object *jbindings)
{
int cnt;
int i;
cnt = json_object_array_length(jbindings);
for (i = 0; i < cnt; ++i) {
int key_idx;
json_object *jvalue;
jvalue = json_object_array_get_idx(jbindings, i);
if (!jvalue)
return true;
key_idx = json_object_get_int(jvalue);
if (!CHECK_KEY_IDX_RANGE(key_idx))
return false;
if (!node_add_binding(node, ele_idx, model_id, key_idx))
return false;
}
return true;
}
static json_object* find_configured_model(struct mesh_node *node, int ele_idx,
json_object *jmodels, uint32_t target_id)
{
int model_cnt;
int i;
model_cnt = json_object_array_length(jmodels);
for (i = 0; i < model_cnt; ++i) {
json_object *jmodel;
json_object *jvalue;
char *str;
int len;
uint32_t model_id;
jmodel = json_object_array_get_idx(jmodels, i);
json_object_object_get_ex(jmodel, "modelId", &jvalue);
str = (char *)json_object_get_string(jvalue);
len = strlen(str);
if (len != 4 && len != 8)
return NULL;
if (sscanf(str, "%08x", &model_id) != 1)
return NULL;
if (len == 4)
model_id += 0xffff0000;
if (model_id == target_id)
return jmodel;
}
return NULL;
}
static bool parse_subscriptions(struct mesh_node *node, int ele_idx,
uint32_t model_id, json_object *jsubscriptions)
{
int cnt;
int i;
int addr;
cnt = json_object_array_length(jsubscriptions);
for (i = 0; i < cnt; ++i) {
char *str;
json_object *jsubscription;
jsubscription = json_object_array_get_idx(jsubscriptions, i);
if (!jsubscription)
return false;
str = (char *)json_object_get_string(jsubscription);
if (sscanf(str, "%04x", &addr) != 1)
return false;
if (!node_add_subscription(node, ele_idx, model_id, addr))
return false;
}
return true;
}
static bool parse_configuration_models(struct mesh_node *node, int ele_idx,
json_object *jmodels)
{
int model_cnt;
int i;
model_cnt = json_object_array_length(jmodels);
for (i = 0; i < model_cnt; ++i) {
json_object *jmodel;
json_object *jvalue;
json_object *jarray;
char *str;
int len;
uint32_t model_id;
jmodel = json_object_array_get_idx(jmodels, i);
json_object_object_get_ex(jmodel, "modelId", &jvalue);
str = (char *)json_object_get_string(jvalue);
len = strlen(str);
if (len != 4 && len != 8)
return false;
if (sscanf(str, "%08x", &model_id) != 1)
return false;
if (len == 4)
model_id += 0xffff0000;
json_object_object_get_ex(jmodel, "bind", &jarray);
if (jarray && !parse_bindings(node, ele_idx, model_id, jarray))
return false;
json_object_object_get_ex(jmodel, "subscribe", &jarray);
if (jarray && !parse_subscriptions(node, ele_idx, model_id, jarray))
return false;
json_object_object_get_ex(jmodel, "publish", &jvalue);
if (jvalue && !parse_model_pub(node, ele_idx, model_id, jvalue))
return false;
}
return true;
}
static bool parse_configuration_elements(struct mesh_node *node,
json_object *jelements, bool local)
{
int el_cnt;
int i;
el_cnt = json_object_array_length(jelements);
node_set_num_elements(node, el_cnt);
for (i = 0; i < el_cnt; ++i) {
json_object *jelement;
json_object *jmodels;
json_object *jvalue;
int index;
uint16_t addr;
jelement = json_object_array_get_idx(jelements, i);
json_object_object_get_ex(jelement, "elementIndex", &jvalue);
if (jvalue) {
index = json_object_get_int(jvalue);
if (index >= el_cnt) {
return false;
}
} else
return false;
if (index == 0) {
char *str;
json_object_object_get_ex(jelement, "unicastAddress",
&jvalue);
str = (char *)json_object_get_string(jvalue);
if (sscanf(str, "%04hx", &addr) != 1)
return false;
if (!local && !net_reserve_address_range(addr, el_cnt))
return false;
node_set_primary(node, addr);
}
json_object_object_get_ex(jelement, "models", &jmodels);
if (!jmodels)
continue;
if(!parse_configuration_models(node, index, jmodels))
return false;
}
return true;
}
static void add_key(json_object *jobject, const char *desc, uint8_t* key)
{
json_object *jstring;
char hexstr[33];
hex2str(key, 16, hexstr, 33);
jstring = json_object_new_string(hexstr);
json_object_object_add(jobject, desc, jstring);
}
static json_object *find_node_by_primary(json_object *jmain, uint16_t primary)
{
json_object *jarray;
int i, len;
json_object_object_get_ex(jmain, "nodes", &jarray);
if (!jarray)
return NULL;
len = json_object_array_length(jarray);
for (i = 0; i < len; ++i) {
json_object *jnode;
json_object *jconfig;
json_object *jelements;
json_object *jelement;
json_object *jvalue;
char *str;
uint16_t addr;
jnode = json_object_array_get_idx(jarray, i);
if (!jnode)
return NULL;
json_object_object_get_ex(jnode, "configuration", &jconfig);
if (!jconfig)
return NULL;
json_object_object_get_ex(jconfig, "elements", &jelements);
if (!jelements)
return NULL;
jelement = json_object_array_get_idx(jelements, 0);
if (!jelement)
return NULL;
json_object_object_get_ex(jelement, "unicastAddress",
&jvalue);
str = (char *)json_object_get_string(jvalue);
if (sscanf(str, "%04hx", &addr) != 1)
return NULL;
if (addr == primary)
return jnode;
}
return NULL;
}
void prov_db_print_node_composition(struct mesh_node *node)
{
char *in_str;
const char *comp_str;
json_object *jmain;
json_object *jnode;
json_object *jcomp;
uint16_t primary = node_get_primary(node);
const char *filename;
bool res = false;
if (!node || !node_get_composition(node))
return;
if (node == node_get_local_node())
filename = local_filename;
else
filename = prov_filename;
in_str = prov_file_read(filename);
if (!in_str)
return;
jmain = json_tokener_parse(in_str);
if (!jmain)
goto done;
jnode = find_node_by_primary(jmain, primary);
if (!jnode)
goto done;
json_object_object_get_ex(jnode, "composition", &jcomp);
if (!jcomp)
goto done;
comp_str = json_object_to_json_string_ext(jcomp,
JSON_C_TO_STRING_PRETTY);
res = true;
done:
if (res)
bt_shell_printf("\tComposition data for node %4.4x %s\n",
primary, comp_str);
else
bt_shell_printf("\tComposition data for node %4.4x not present\n",
primary);
g_free(in_str);
if (jmain)
json_object_put(jmain);
}
bool prov_db_add_node_composition(struct mesh_node *node, uint8_t *data,
uint16_t len)
{
char *in_str;
json_object *jmain;
json_object *jnode;
json_object *jcomp;
json_object *jbool;
json_object *jfeatures;
json_object *jelements;
struct mesh_node_composition *comp;
uint8_t num_ele;
int i;
uint16_t primary = node_get_primary(node);
bool res = NULL;
comp = node_get_composition(node);
if (!comp)
return false;
in_str = prov_file_read(prov_filename);
if (!in_str)
return false;
jmain = json_tokener_parse(in_str);
if (!jmain)
goto done;
jnode = find_node_by_primary(jmain, primary);
if (!jnode)
goto done;
jcomp = json_object_new_object();
put_uint16(jcomp, "cid", comp->cid);
put_uint16(jcomp, "pid", comp->pid);
put_uint16(jcomp, "vid", comp->vid);
put_uint16(jcomp, "crpl", comp->crpl);
jfeatures = json_object_new_object();
jbool = json_object_new_boolean(comp->relay);
json_object_object_add(jfeatures, "relay", jbool);
jbool = json_object_new_boolean(comp->proxy);
json_object_object_add(jfeatures, "proxy", jbool);
jbool = json_object_new_boolean(comp->friend);
json_object_object_add(jfeatures, "friend", jbool);
jbool = json_object_new_boolean(comp->lpn);
json_object_object_add(jfeatures, "lpn", jbool);
json_object_object_add(jcomp, "features", jfeatures);
data += 11;
len -= 11;
num_ele = node_get_num_elements(node);
jelements = json_object_new_array();
for (i = 0; i < num_ele; ++i) {
json_object *jelement;
json_object *jmodels;
json_object *jint;
uint32_t mod_id;
uint16_t vendor_id;
uint8_t m, v;
jelement = json_object_new_object();
/* Element Index */
jint = json_object_new_int(i);
json_object_object_add(jelement, "elementIndex", jint);
/* Location */
put_uint16(jelement, "location", get_le16(data));
data += 2;
m = *data++;
v = *data++;
len -= 4;
/* Models */
jmodels = json_object_new_array();
while (len >= 2 && m--) {
mod_id = get_le16(data);
data += 2;
len -= 2;
put_uint16_array_entry(jmodels, (uint16_t) mod_id);
}
while (len >= 4 && v--) {
mod_id = get_le16(data + 2);
vendor_id = get_le16(data);
mod_id |= (vendor_id << 16);
data += 4;
len -= 4;
put_uint32_array_entry(jmodels, mod_id);
}
json_object_object_add(jelement, "models", jmodels);
json_object_array_add(jelements, jelement);
}
json_object_object_add(jcomp, "elements", jelements);
json_object_object_add(jnode, "composition", jcomp);
prov_file_write(jmain, false);
res = true;;
done:
g_free(in_str);
if(jmain)
json_object_put(jmain);
return res;
}
bool prov_db_node_set_ttl(struct mesh_node *node, uint8_t ttl)
{
char *in_str;
json_object *jmain;
json_object *jnode;
json_object *jconfig;
json_object *jvalue;
uint16_t primary = node_get_primary(node);
const char *filename;
bool local = node == node_get_local_node();
bool res = false;
if (local)
filename = local_filename;
else
filename = prov_filename;
in_str = prov_file_read(filename);
if (!in_str)
return false;
jmain = json_tokener_parse(in_str);
if (!jmain)
goto done;
if (local)
json_object_object_get_ex(jmain, "node", &jnode);
else
jnode = find_node_by_primary(jmain, primary);
if (!jnode)
goto done;
json_object_object_get_ex(jnode, "configuration", &jconfig);
if (!jconfig)
goto done;
json_object_object_del(jconfig, "defaultTTL");
jvalue = json_object_new_int(ttl);
json_object_object_add(jconfig, "defaultTTL", jvalue);
prov_file_write(jmain, local);
res = true;
done:
g_free(in_str);
if(jmain)
json_object_put(jmain);
return res;
}
static void set_local_iv_index(json_object *jobj, uint32_t idx, bool update)
{
json_object *jvalue;
json_object_object_del(jobj, "IVindex");
jvalue = json_object_new_int(idx);
json_object_object_add(jobj, "IVindex", jvalue);
json_object_object_del(jobj, "IVupdate");
jvalue = json_object_new_int((update) ? 1 : 0);
json_object_object_add(jobj, "IVupdate", jvalue);
}
bool prov_db_local_set_iv_index(uint32_t iv_index, bool update, bool prov)
{
char *in_str;
json_object *jmain;
json_object *jnode;
bool res = false;
in_str = prov_file_read(local_filename);
if (!in_str)
return false;
jmain = json_tokener_parse(in_str);
if (!jmain)
goto done;
json_object_object_get_ex(jmain, "node", &jnode);
set_local_iv_index(jnode, iv_index, update);
prov_file_write(jmain, true);
g_free(in_str);
json_object_put(jmain);
/* If provisioner, save to global DB as well */
if (prov) {
in_str = prov_file_read(prov_filename);
if (!in_str)
return false;
jmain = json_tokener_parse(in_str);
if (!jmain)
goto done;
set_local_iv_index(jmain, iv_index, update);
prov_file_write(jmain, false);
}
res = true;
done:
g_free(in_str);
if(jmain)
json_object_put(jmain);
return res;
}
bool prov_db_local_set_seq_num(uint32_t seq_num)
{
char *in_str;
json_object *jmain;
json_object *jnode;
json_object *jvalue;
bool res = false;
in_str = prov_file_read(local_filename);
if (!in_str)
return false;
jmain = json_tokener_parse(in_str);
if (!jmain)
goto done;
json_object_object_get_ex(jmain, "node", &jnode);
json_object_object_del(jnode, "sequenceNumber");
jvalue = json_object_new_int(seq_num);
json_object_object_add(jnode, "sequenceNumber", jvalue);
prov_file_write(jmain, true);
res = true;
done:
g_free(in_str);
if(jmain)
json_object_put(jmain);
return res;
}
bool prov_db_node_set_iv_seq(struct mesh_node *node, uint32_t iv, uint32_t seq)
{
char *in_str;
json_object *jmain;
json_object *jnode;
json_object *jvalue;
uint16_t primary = node_get_primary(node);
bool res = false;
in_str = prov_file_read(prov_filename);
if (!in_str)
return false;
jmain = json_tokener_parse(in_str);
if (!jmain)
goto done;
jnode = find_node_by_primary(jmain, primary);
if (!jnode)
goto done;
json_object_object_del(jnode, "IVindex");
jvalue = json_object_new_int(iv);
json_object_object_add(jnode, "IVindex", jvalue);
json_object_object_del(jnode, "sequenceNumber");
jvalue = json_object_new_int(seq);
json_object_object_add(jnode, "sequenceNumber", jvalue);
prov_file_write(jmain, false);
res = true;
done:
g_free(in_str);
if(jmain)
json_object_put(jmain);
return res;
}
bool prov_db_node_keys(struct mesh_node *node, GList *idxs, const char *desc)
{
char *in_str;
json_object *jmain;
json_object *jnode;
json_object *jconfig;
json_object *jidxs;
uint16_t primary = node_get_primary(node);
const char *filename;
bool local = (node == node_get_local_node());
bool res = false;
if (local)
filename = local_filename;
else
filename = prov_filename;
in_str = prov_file_read(filename);
if (!in_str)
return false;
jmain = json_tokener_parse(in_str);
if (!jmain)
goto done;
jnode = find_node_by_primary(jmain, primary);
if (!jnode)
goto done;
json_object_object_get_ex(jnode, "configuration", &jconfig);
if (!jconfig)
goto done;
json_object_object_del(jconfig, desc);
if (idxs) {
jidxs = json_object_new_array();
put_uint16_list(jidxs, idxs);
json_object_object_add(jconfig, desc, jidxs);
}
prov_file_write(jmain, local);
res = true;
done:
g_free(in_str);
if(jmain)
json_object_put(jmain);
return res;
}
static json_object *get_jmodel_obj(struct mesh_node *node, uint8_t ele_idx,
uint32_t model_id, json_object **jmain)
{
char *in_str;
json_object *jnode;
json_object *jconfig;
json_object *jelements, *jelement;
json_object *jmodels, *jmodel = NULL;
uint16_t primary = node_get_primary(node);
const char *filename;
bool local = (node == node_get_local_node());
if (local)
filename = local_filename;
else
filename = prov_filename;
in_str = prov_file_read(filename);
if (!in_str)
return NULL;
*jmain = json_tokener_parse(in_str);
if (!(*jmain))
goto done;
if (local)
json_object_object_get_ex(*jmain, "node", &jnode);
else
jnode = find_node_by_primary(*jmain, primary);
if (!jnode)
goto done;
/* Configuration is mandatory for nodes in provisioning database */
json_object_object_get_ex(jnode, "configuration", &jconfig);
if (!jconfig)
goto done;
json_object_object_get_ex(jconfig, "elements", &jelements);
if (!jelements) {
goto done;
}
jelement = json_object_array_get_idx(jelements, ele_idx);
if (!jelement) {
goto done;
}
json_object_object_get_ex(jelement, "models", &jmodels);
if (!jmodels) {
jmodels = json_object_new_array();
json_object_object_add(jelement, "models", jmodels);
} else {
jmodel = find_configured_model(node, ele_idx, jmodels,
model_id);
}
if (!jmodel) {
jmodel = json_object_new_object();
if ((model_id & 0xffff0000) == 0xffff0000)
put_uint16(jmodel, "modelId", model_id & 0xffff);
else
put_uint32(jmodel, "modelId", model_id);
json_object_array_add(jmodels, jmodel);
}
done:
g_free(in_str);
if(!jmodel && *jmain)
json_object_put(*jmain);
return jmodel;
}
bool prov_db_add_binding(struct mesh_node *node, uint8_t ele_idx,
uint32_t model_id, uint16_t app_idx)
{
bool local = (node == node_get_local_node());
json_object *jbindings = NULL;
json_object *jmodel;
json_object *jvalue;
json_object *jmain;
jmodel = get_jmodel_obj(node, ele_idx, model_id, &jmain);
if (!jmodel)
return false;
json_object_object_get_ex(jmodel, "bind", &jbindings);
if (!jbindings) {
jbindings = json_object_new_array();
json_object_object_add(jmodel, "bind", jbindings);
}
jvalue = json_object_new_int(app_idx);
json_object_array_add(jbindings, jvalue);
prov_file_write(jmain, local);
json_object_put(jmain);
return true;
}
bool prov_db_add_subscription(struct mesh_node *node, uint8_t ele_idx,
uint32_t model_id, uint16_t addr)
{
bool local = (node == node_get_local_node());
json_object *jsubscriptions = NULL;
json_object *jmodel;
json_object *jmain;
jmodel = get_jmodel_obj(node, ele_idx, model_id, &jmain);
if (!jmodel)
return false;
json_object_object_get_ex(jmodel, "subscribe", &jsubscriptions);
if (!jsubscriptions) {
jsubscriptions = json_object_new_array();
json_object_object_add(jmodel, "subscribe", jsubscriptions);
}
put_uint16_array_entry(jsubscriptions, addr);
prov_file_write(jmain, local);
json_object_put(jmain);
return true;
}
bool prov_db_node_set_model_pub(struct mesh_node *node, uint8_t ele_idx,
uint32_t model_id,
struct mesh_publication *pub)
{
json_object *jmain;
json_object *jmodel;
json_object *jpub;
json_object *jvalue;
bool local = (node == node_get_local_node());
jmodel = get_jmodel_obj(node, ele_idx, model_id, &jmain);
if (!jmodel)
return false;
json_object_object_del(jmodel, "publish");
if (!pub)
goto done;
jpub = json_object_new_object();
/* Save only required fields */
put_uint16(jpub, "address", pub->u.addr16);
put_uint16(jpub, "index", pub->app_idx);
jvalue = json_object_new_int(pub->ttl);
json_object_object_add(jpub, "ttl", jvalue);
json_object_object_add(jmodel, "publish", jpub);
done:
prov_file_write(jmain, local);
json_object_put(jmain);
return true;
}
bool prov_db_add_new_node(struct mesh_node *node)
{
char *in_str;
json_object *jmain;
json_object *jarray;
json_object *jnode;
json_object *jconfig;
json_object *jelements;
uint8_t num_ele;
uint16_t primary;
int i;
bool first_node;
bool res = false;
in_str = prov_file_read(prov_filename);
if (!in_str)
return false;
jmain = json_tokener_parse(in_str);
if (!jmain)
goto done;
json_object_object_get_ex(jmain, "nodes", &jarray);
if (!jarray) {
jarray = json_object_new_array();
first_node = true;
} else
first_node = false;
jnode = json_object_new_object();
/* Device key */
add_key(jnode, "deviceKey", node_get_device_key(node));
/* Net key */
jconfig = json_object_new_object();
add_node_idxs(jconfig, "netKeys", node_get_net_keys(node));
num_ele = node_get_num_elements(node);
if (num_ele == 0)
goto done;
jelements = json_object_new_array();
primary = node_get_primary(node);
if (IS_UNASSIGNED(primary))
goto done;
for (i = 0; i < num_ele; ++i) {
json_object *jelement;
json_object *jint;
jelement = json_object_new_object();
/* Element Index */
jint = json_object_new_int(i);
json_object_object_add(jelement, "elementIndex", jint);
/* Unicast */
put_uint16(jelement, "unicastAddress", primary + i);
json_object_array_add(jelements, jelement);
}
json_object_object_add(jconfig, "elements", jelements);
json_object_object_add(jnode, "configuration", jconfig);
json_object_array_add(jarray, jnode);
if (first_node)
json_object_object_add(jmain, "nodes", jarray);
prov_file_write(jmain, false);
res = true;
done:
g_free(in_str);
if (jmain)
json_object_put(jmain);
return res;
}
static bool parse_node_composition(struct mesh_node *node, json_object *jcomp)
{
json_object *jvalue;
json_object *jelements;
json_object *jfeatures;
json_bool enable;
char *str;
struct mesh_node_composition comp;
json_object_object_get_ex(jcomp, "cid", &jvalue);
if (!jvalue)
return false;
str = (char *)json_object_get_string(jvalue);
if (sscanf(str, "%04hx", &comp.cid) != 1)
return false;
json_object_object_get_ex(jcomp, "pid", &jvalue);
if (!jvalue)
return false;
str = (char *)json_object_get_string(jvalue);
if (sscanf(str, "%04hx", &comp.pid) != 1)
return false;
json_object_object_get_ex(jcomp, "vid", &jvalue);
if (!jvalue)
return false;
str = (char *)json_object_get_string(jvalue);
if (sscanf(str, "%04hx", &comp.vid) != 1)
return false;
json_object_object_get_ex(jcomp, "crpl", &jvalue);
if (!jvalue)
return false;
str = (char *)json_object_get_string(jvalue);
if (sscanf(str, "%04hx", &comp.crpl) != 1)
return false;
/* Extract features */
json_object_object_get_ex(jcomp, "features", &jfeatures);
if (!jfeatures)
return false;
json_object_object_get_ex(jfeatures, "relay", &jvalue);
enable = json_object_get_boolean(jvalue);
comp.relay = (enable) ? true : false;
json_object_object_get_ex(jfeatures, "proxy", &jvalue);
enable = json_object_get_boolean(jvalue);
comp.proxy = (enable) ? true : false;
json_object_object_get_ex(jfeatures, "friend", &jvalue);
enable = json_object_get_boolean(jvalue);
comp.friend = (enable) ? true : false;
json_object_object_get_ex(jfeatures, "lowPower", &jvalue);
enable = json_object_get_boolean(jvalue);
comp.lpn = (enable) ? true : false;
if (!node_set_composition(node, &comp))
return false;
json_object_object_get_ex(jcomp, "elements", &jelements);
if (!jelements)
return false;
return parse_composition_elements(node, jelements);
}
static bool parse_node(json_object *jnode, bool local)
{
json_object *jconfig;
json_object *jelements;
json_object *jidxs;
json_object *jvalue;
json_object *jint;
uint8_t key[16];
char *value_str;
uint32_t idx;
struct mesh_node *node;
/* Device key */
if (!json_object_object_get_ex(jnode, "deviceKey", &jvalue) ||
!jvalue) {
if (!mesh_get_random_bytes(key, 16))
return false;
add_key(jnode, "deviceKey", key);
} else {
value_str = (char *)json_object_get_string(jvalue);
if (!str2hex(value_str, strlen(value_str), key, 16))
return false;;
}
node = node_new();
if (!node)
return false;
node_set_device_key(node, key);
json_object_object_get_ex(jnode, "IVindex", &jint);
if (jint)
idx = json_object_get_int(jint);
else
idx = 0;
node_set_iv_index(node, idx);
if (local) {
bool update = false;
json_object_object_get_ex(jnode, "IVupdate", &jint);
if (jint)
update = json_object_get_int(jint) ? true : false;
net_set_iv_index(idx, update);
}
if (json_object_object_get_ex(jnode, "sequenceNumber", &jint) &&
jint) {
int seq = json_object_get_int(jint);
node_set_sequence_number(node, seq);
}
/* Composition is mandatory for local node */
json_object_object_get_ex(jnode, "composition", &jconfig);
if ((jconfig && !parse_node_composition(node, jconfig)) ||
(!jconfig && local)) {
node_free(node);
return false;
}
/* Configuration is mandatory for nodes in provisioning database */
json_object_object_get_ex(jnode, "configuration", &jconfig);
if (!jconfig) {
if (local) {
/* This is an unprovisioned local device */
goto done;
} else {
node_free(node);
return false;
}
}
json_object_object_get_ex(jconfig, "elements", &jelements);
if (!jelements) {
node_free(node);
return false;
}
if (!parse_configuration_elements(node, jelements, local)) {
node_free(node);
return false;;
}
json_object_object_get_ex(jconfig, "netKeys", &jidxs);
if (!jidxs || (parse_node_keys(node, jidxs, false) == 0)) {
node_free(node);
return false;
}
json_object_object_get_ex(jconfig, "appKeys", &jidxs);
if (jidxs)
parse_node_keys(node, jidxs, true);
json_object_object_get_ex(jconfig, "defaultTTL", &jvalue);
if (jvalue) {
int ttl = json_object_get_int(jvalue);
node_set_default_ttl(node, ttl &TTL_MASK);
}
done:
if (local && !node_set_local_node(node)) {
node_free(node);
return false;
}
return true;
}
bool prov_db_show(const char *filename)
{
char *str;
str = prov_file_read(filename);
if (!str)
return false;
bt_shell_printf("%s\n", str);
g_free(str);
return true;
}
static bool read_json_db(const char *filename, bool provisioner, bool local)
{
char *str;
json_object *jmain;
json_object *jarray;
json_object *jprov;
json_object *jvalue;
json_object *jtemp;
uint8_t key[16];
int value_int;
char *value_str;
int len;
int i;
uint32_t index;
bool refresh = false;
bool res = false;
str = prov_file_read(filename);
if (!str) return false;
jmain = json_tokener_parse(str);
if (!jmain)
goto done;
if (local) {
json_object *jnode;
bool result;
json_object_object_get_ex(jmain, "node", &jnode);
if (!jnode) {
bt_shell_printf("Cannot find \"node\" object");
goto done;
} else
result = parse_node(jnode, true);
/*
* If local node is provisioner, the rest of mesh settings
* are read from provisioning database.
*/
if (provisioner) {
res = result;
goto done;
}
}
/* IV index */
json_object_object_get_ex(jmain, "IVindex", &jvalue);
if (!jvalue)
goto done;
index = json_object_get_int(jvalue);
json_object_object_get_ex(jmain, "IVupdate", &jvalue);
if (!jvalue)
goto done;
value_int = json_object_get_int(jvalue);
net_set_iv_index(index, value_int);
/* Network key(s) */
json_object_object_get_ex(jmain, "netKeys", &jarray);
if (!jarray)
goto done;
len = json_object_array_length(jarray);
bt_shell_printf("# netkeys = %d\n", len);
for (i = 0; i < len; ++i) {
uint32_t idx;
jtemp = json_object_array_get_idx(jarray, i);
json_object_object_get_ex(jtemp, "index", &jvalue);
if (!jvalue)
goto done;
idx = json_object_get_int(jvalue);
json_object_object_get_ex(jtemp, "key", &jvalue);
if (!jvalue) {
if (!mesh_get_random_bytes(key, 16))
goto done;
add_key(jtemp, "key", key);
refresh = true;
} else {
value_str = (char *)json_object_get_string(jvalue);
if (!str2hex(value_str, strlen(value_str), key, 16)) {
goto done;
}
}
if (!keys_net_key_add(idx, key, false))
goto done;
json_object_object_get_ex(jtemp, "keyRefresh", &jvalue);
if (!jvalue)
goto done;
keys_set_kr_phase(idx, (uint8_t) json_object_get_int(jvalue));
}
/* App keys */
json_object_object_get_ex(jmain, "appKeys", &jarray);
if (jarray) {
len = json_object_array_length(jarray);
bt_shell_printf("# appkeys = %d\n", len);
for (i = 0; i < len; ++i) {
int app_idx;
int net_idx;
jtemp = json_object_array_get_idx(jarray, i);
json_object_object_get_ex(jtemp, "index",
&jvalue);
if (!jvalue)
goto done;
app_idx = json_object_get_int(jvalue);
if (!CHECK_KEY_IDX_RANGE(app_idx))
goto done;
json_object_object_get_ex(jtemp, "key", &jvalue);
if (!jvalue) {
if (!mesh_get_random_bytes(key, 16))
goto done;
add_key(jtemp, "key", key);
refresh = true;
} else {
value_str =
(char *)json_object_get_string(jvalue);
str2hex(value_str, strlen(value_str), key, 16);
}
json_object_object_get_ex(jtemp, "boundNetKey",
&jvalue);
if (!jvalue)
goto done;
net_idx = json_object_get_int(jvalue);
if (!CHECK_KEY_IDX_RANGE(net_idx))
goto done;
keys_app_key_add(net_idx, app_idx, key, false);
}
}
/* Provisioner info */
json_object_object_get_ex(jmain, "provisioners", &jarray);
if (!jarray)
goto done;
len = json_object_array_length(jarray);
bt_shell_printf("# provisioners = %d\n", len);
for (i = 0; i < len; ++i) {
jprov = json_object_array_get_idx(jarray, i);
/* Allocated unicast range */
json_object_object_get_ex(jprov, "allocatedUnicastRange",
&jtemp);
if (!jtemp) {
goto done;
}
if (!parse_unicast_range(jtemp)) {
bt_shell_printf("Doneed to parse unicast range\n");
goto done;
}
}
json_object_object_get_ex(jmain, "nodes", &jarray);
if (!jarray) {
res = true;
goto done;
}
len = json_object_array_length(jarray);
bt_shell_printf("# provisioned nodes = %d\n", len);
for (i = 0; i < len; ++i) {
json_object *jnode;
jnode = json_object_array_get_idx(jarray, i);
if (!jnode || !parse_node(jnode, false))
goto done;
}
res = true;
done:
g_free(str);
if (res && refresh)
prov_file_write(jmain, false);
if (jmain)
json_object_put(jmain);
return res;
}
bool prov_db_read(const char *filename)
{
prov_filename = filename;
return read_json_db(filename, true, false);
}
bool prov_db_read_local_node(const char *filename, bool provisioner)
{
local_filename = filename;
return read_json_db(filename, provisioner, true);
}