blob: 937f801a6b4c6cd327fb2002aa4c514aa28599d6 [file] [log] [blame]
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2017-2018 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.
*
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <json-c/json.h>
#include <ell/ell.h>
#include "mesh/mesh-defs.h"
#include "mesh/mesh.h"
#include "mesh/node.h"
#include "mesh/net.h"
#include "mesh/appkey.h"
#include "mesh/model.h"
#include "mesh/storage.h"
/*
* TODO: figure out naming convention to store alternative nodes
* Mesh storage dir wil be in configure.ac
*/
#define DEVICE_COMPOSITION_FILE "../config/composition.json"
#define NODE_CONGIGURATION_FILE "../config/configuration.json"
static bool read_local_node_cb(struct mesh_db_node *db_node, void *user_data)
{
struct mesh_net *net = user_data;
struct mesh_node *node;
uint32_t seq_number;
uint16_t crpl;
uint8_t ttl, mode, cnt, num_ele;
uint16_t unicast, interval;
uint8_t *uuid;
if (!net)
return false;
node = node_create_from_storage(net, db_node, true);
if (!node)
return false;
mesh_net_local_node_set(net, node, db_node->provisioner);
seq_number = node_get_sequence_number(node);
mesh_net_set_seq_num(net, seq_number);
ttl = node_default_ttl_get(node);
mesh_net_set_default_ttl(net, ttl);
crpl = node_get_crpl(node);
mesh_net_set_crpl(net, crpl);
mode = node_proxy_mode_get(node);
if (mode == MESH_MODE_ENABLED || mode == MESH_MODE_DISABLED)
mesh_net_set_proxy_mode(net, mode == MESH_MODE_ENABLED);
mode = node_friend_mode_get(node);
if (mode == MESH_MODE_ENABLED || mode == MESH_MODE_DISABLED)
mesh_net_set_friend_mode(net, mode == MESH_MODE_ENABLED);
mode = node_relay_mode_get(node, &cnt, &interval);
if (mode == MESH_MODE_ENABLED || mode == MESH_MODE_DISABLED)
mesh_net_set_relay_mode(net, mode == MESH_MODE_ENABLED, cnt,
interval);
mode = node_beacon_mode_get(node);
if (mode == MESH_MODE_ENABLED || mode == MESH_MODE_DISABLED)
mesh_net_set_beacon_mode(net, mode == MESH_MODE_ENABLED);
unicast = db_node->unicast;
num_ele = node_get_num_elements(node);
if (!IS_UNASSIGNED(unicast) &&
!mesh_net_register_unicast(net, unicast, num_ele))
return false;
uuid = node_uuid_get(node);
if (uuid)
mesh_net_id_uuid_set(net, uuid);
return true;
}
static bool read_net_keys_cb(uint16_t idx, uint8_t *key, uint8_t *new_key,
int phase, void *user_data)
{
struct mesh_net *net = user_data;
if (!net)
return false;
if (mesh_net_add_key(net, false, idx, key) != MESH_STATUS_SUCCESS)
return false;
/* TODO: handle restoring key refresh phase and new keys */
return true;
}
static bool read_app_keys_cb(uint16_t net_idx, uint16_t app_idx, uint8_t *key,
uint8_t *new_key, void *user_data)
{
struct mesh_net *net = user_data;
if (!net)
return false;
return appkey_key_init(net, net_idx, app_idx, key, new_key);
}
static bool parse_local_node(struct mesh_net *net, json_object *jnode)
{
bool bvalue;
uint32_t iv_index;
uint8_t key_buf[16];
uint8_t cnt;
uint16_t interval;
if (mesh_db_read_iv_index(jnode, &iv_index, &bvalue))
mesh_net_set_iv_index(net, iv_index, bvalue);
if (mesh_db_read_net_transmit(jnode, &cnt, &interval))
mesh_net_transmit_params_set(net, cnt, interval);
/* Node composition/configuration info */
if (!mesh_db_read_node(jnode, read_local_node_cb, net))
return false;
if (!mesh_db_read_net_keys(jnode, read_net_keys_cb, net))
return false;
/* TODO: use the actual "primary" network index for this node */
if (mesh_db_read_device_key(jnode, key_buf) &&
!node_set_device_key(mesh_net_local_node_get(net), key_buf))
return false;
mesh_db_read_app_keys(jnode, read_app_keys_cb, net);
return true;
}
static bool read_unprov_device_cb(struct mesh_db_node *db_node, void *user_data)
{
struct mesh_net *net = user_data;
struct mesh_node *node;
uint16_t crpl;
uint8_t *uuid;
if (!net)
return false;
node = node_create_from_storage(net, db_node, true);
if (!node)
return false;
mesh_net_local_node_set(net, node, db_node->provisioner);
crpl = node_get_crpl(node);
mesh_net_set_crpl(net, crpl);
uuid = node_uuid_get(node);
if (uuid)
mesh_net_id_uuid_set(net, uuid);
return true;
}
static bool parse_unprovisioned_device(struct mesh_net *net, json_object *jnode)
{
struct mesh_db_prov prov;
struct mesh_net_prov_caps *caps;
struct mesh_node *node;
/* Node composition/configuration info */
if (!mesh_db_read_unprovisioned_device(jnode,
read_unprov_device_cb, net))
return false;
if (!mesh_db_read_prov_info(jnode, &prov))
return false;
caps = mesh_net_prov_caps_get(net);
if (!caps)
return false;
node = mesh_net_local_node_get(net);
if (!node)
return false;
caps->num_ele = node_get_num_elements(node);
l_put_le16(prov.algorithm, &caps->algorithms);
caps->pub_type = prov.pub_type;
caps->static_type = prov.static_type;
caps->output_size = prov.output_oob.size;
l_put_le16(prov.output_oob.actions, &caps->output_action);
caps->input_size = prov.input_oob.size;
l_put_le16(prov.input_oob.actions, &caps->input_action);
return mesh_net_priv_key_set(net, prov.priv_key);
}
static bool parse_config(struct mesh_net *net, const char *config_name,
bool unprovisioned)
{
int fd;
char *str;
const char *out;
struct stat st;
ssize_t sz;
json_object *jnode = NULL;
bool result = false;
if (!config_name)
return false;
fd = open(config_name, O_RDONLY);
if (!fd)
return false;
if (fstat(fd, &st) == -1) {
close(fd);
return false;
}
str = (char *) l_new(char, st.st_size + 1);
if (!str) {
close(fd);
return false;
}
sz = read(fd, str, st.st_size);
if (sz != st.st_size) {
l_error("Failed to read configuration file");
goto done;
}
jnode = json_tokener_parse(str);
if (!jnode)
goto done;
mesh_net_jconfig_set(net, jnode);
if (!unprovisioned)
result = parse_local_node(net, jnode);
else
result = parse_unprovisioned_device(net, jnode);
if (!result) {
storage_release(net);
goto done;
}
mesh_net_cfg_file_get(net, &out);
if (!out)
mesh_net_cfg_file_set(net, !unprovisioned ?
config_name : NODE_CONGIGURATION_FILE);
done:
close(fd);
if (str)
l_free(str);
return result;
}
bool storage_parse_config(struct mesh_net *net, const char *config_name)
{
bool result = false;
bool unprovisioned = !config_name;
if (unprovisioned) {
result = parse_config(net, DEVICE_COMPOSITION_FILE, true);
goto done;
}
result = parse_config(net, config_name, false);
if (!result) {
size_t len = strlen(config_name) + 5;
char *bak = l_malloc(len);
/* Fall-back to Backup version */
strncpy(bak, config_name, len);
bak = strncat(bak, ".bak", 5);
remove(config_name);
rename(bak, config_name);
result = parse_config(net, config_name, false);
l_free(bak);
}
/* If configuration read fails, try as unprovisioned device */
if (!result) {
l_info("Parse configuration failed, trying unprovisioned");
unprovisioned = true;
result = parse_config(net, DEVICE_COMPOSITION_FILE, true);
}
done:
if (result)
mesh_net_provisioned_set(net, !unprovisioned);
return result;
}
bool storage_local_set_ttl(struct mesh_net *net, uint8_t ttl)
{
json_object *jnode;
if (!net)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
return mesh_db_write_int(jnode, "defaultTTL", ttl);
}
bool storage_local_set_relay(struct mesh_net *net, bool enable,
uint8_t count, uint8_t interval)
{
json_object *jnode;
if (!net)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
return mesh_db_write_relay_mode(jnode, enable, count, interval);
}
bool storage_local_set_transmit_params(struct mesh_net *net, uint8_t count,
uint8_t interval)
{
json_object *jnode;
if (!net)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
return mesh_db_write_net_transmit(jnode, count, interval);
}
bool storage_local_set_mode(struct mesh_net *net, uint8_t mode,
const char *mode_name)
{
json_object *jnode;
if (!net || !mode_name)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
return mesh_db_write_mode(jnode, mode_name, mode);
}
bool storage_model_bind(struct mesh_net *net, uint16_t addr, uint32_t mod_id,
uint16_t app_idx, bool unbind)
{
json_object *jnode;
bool is_local;
if (!net)
return false;
is_local = mesh_net_is_local_address(net, addr);
if (is_local) {
int ele_idx;
bool is_vendor = (mod_id > 0xffff);
ele_idx = node_get_element_idx(mesh_net_local_node_get(net),
addr);
if (ele_idx < 0)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
if (unbind)
return mesh_db_model_binding_del(jnode, ele_idx,
is_vendor, mod_id, app_idx);
else
return mesh_db_model_binding_add(jnode, ele_idx,
is_vendor, mod_id, app_idx);
}
/* TODO: write remote node bindings to provisioner DB */
return false;
}
bool storage_local_app_key_add(struct mesh_net *net, uint16_t net_idx,
uint16_t app_idx, const uint8_t key[16], bool update)
{
json_object *jnode;
if (!net)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
return mesh_db_app_key_add(jnode, net_idx, app_idx, key, update);
}
bool storage_local_app_key_del(struct mesh_net *net, uint16_t net_idx,
uint16_t app_idx)
{
json_object *jnode;
if (!net)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
return mesh_db_app_key_del(jnode, net_idx, app_idx);
}
bool storage_local_net_key_add(struct mesh_net *net, uint16_t net_idx,
const uint8_t key[16], int phase)
{
json_object *jnode;
if (!net)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
return mesh_db_net_key_add(jnode, net_idx, key, phase);
}
bool storage_local_net_key_del(struct mesh_net *net, uint16_t net_idx)
{
json_object *jnode;
if (!net)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
return mesh_db_net_key_del(jnode, net_idx);
}
bool storage_local_set_iv_index(struct mesh_net *net, uint32_t iv_index,
bool update)
{
json_object *jnode;
if (!net)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
return mesh_db_write_iv_index(jnode, iv_index, update);
}
bool storage_local_set_device_key(struct mesh_net *net, uint8_t dev_key[16])
{
json_object *jnode;
if (!net)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
return mesh_db_write_device_key(jnode, dev_key);
}
bool storage_local_set_unicast(struct mesh_net *net, uint16_t unicast)
{
json_object *jnode;
if (!net)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
return mesh_db_write_uint16_hex(jnode, "unicastAddress", unicast);
}
bool storage_local_write_sequence_number(struct mesh_net *net, uint32_t seq)
{
json_object *jnode;
const char *cfg_file;
bool result;
if (!net)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
result = mesh_db_write_int(jnode, "sequenceNumber", seq);
if (!result)
return false;
result = mesh_net_cfg_file_get(net, &cfg_file);
if (result && cfg_file)
result = storage_save_config(net, cfg_file, false, NULL, NULL);
return result;
}
static bool save_config(struct mesh_net *net, const char *config_name)
{
FILE *outfile;
const char *str;
json_object *jnode;
bool result = false;
if (!net || !config_name)
return false;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
outfile = fopen(config_name, "w");
if (!outfile) {
l_error("Failed to save configuration to %s", config_name);
return false;
}
str = json_object_to_json_string_ext(jnode, JSON_C_TO_STRING_PRETTY);
if (fwrite(str, sizeof(char), strlen(str), outfile) < strlen(str))
l_warn("Incomplete write of mesh configuration");
else
result = true;
fclose(outfile);
return result;
}
struct write_info {
const char *config_name;
struct mesh_net *net;
void *user_data;
mesh_status_func_t cb;
};
static void idle_save_config(void *user_data)
{
struct write_info *info = user_data;
size_t len = strlen(info->config_name) + 5;
char *tmp = l_malloc(len);
char *bak = l_malloc(len);
bool result = false;
strncpy(tmp, info->config_name, len);
strncpy(bak, info->config_name, len);
tmp = strncat(tmp, ".tmp", 5);
bak = strncat(bak, ".bak", 5);
remove(tmp);
l_debug("Storage-Wrote");
result = save_config(info->net, tmp);
if (result) {
remove(bak);
rename(info->config_name, bak);
rename(tmp, info->config_name);
}
remove(tmp);
l_free(tmp);
l_free(bak);
if (info->cb)
info->cb(info->user_data, result);
l_free(info);
}
bool storage_save_config(struct mesh_net *net, const char *config_name,
bool no_wait, mesh_status_func_t cb, void *user_data)
{
struct write_info *info;
info = l_new(struct write_info, 1);
if (!info)
return false;
info->net = net;
info->config_name = config_name;
info->cb = cb;
info->user_data = user_data;
if (no_wait)
idle_save_config(info);
l_idle_oneshot(idle_save_config, info, NULL);
return true;
}
bool storage_save_new_config(struct mesh_net *net, const char *config_name,
mesh_status_func_t cb, void *user_data)
{
json_object *jnode;
jnode = mesh_net_jconfig_get(net);
if (!jnode)
return false;
mesh_db_remove_property(jnode, "provision");
return storage_save_config(net, config_name, false, cb, user_data);
}
void storage_release(struct mesh_net *net)
{
json_object *jnode;
jnode = mesh_net_jconfig_get(net);
if (jnode)
json_object_put(jnode);
mesh_net_jconfig_set(net, NULL);
}