| /* |
| * |
| * 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); |
| } |