tools/mesh-cfgclient: Export configuration database
authorInga Stotland <inga.stotland@intel.com>
Thu, 23 Sep 2021 03:26:03 +0000 (20:26 -0700)
committerAyush Garg <ayush.garg@samsung.com>
Fri, 11 Mar 2022 13:38:37 +0000 (19:08 +0530)
This adds main menu command "export-db".
When the command is invoked, JSON configuration object is
cloned and trimmed of extraneous properties.
Information about netkeys, appkeys and device keys are obtained
from bluetooth-meshd by calling ExportKeys() method.
The obtained key values are recorded in the export JSON object.

Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
tools/mesh-cfgclient.c
tools/mesh/mesh-db.c
tools/mesh/mesh-db.h

index 3d06899..c52c015 100644 (file)
@@ -19,6 +19,7 @@
 #include <ctype.h>
 #include <dbus/dbus.h>
 #include <errno.h>
+#include <libgen.h>
 #include <stdio.h>
 #include <time.h>
 
@@ -53,6 +54,7 @@
 #define MAX_CRPL_SIZE          0x7fff
 
 #define DEFAULT_CFG_FILE       "config_db.json"
+#define DEFAULT_EXPORT_FILE    "export_db.json"
 
 struct meshcfg_el {
        const char *path;
@@ -837,6 +839,197 @@ static void cmd_scan_unprov(int argc, char *argv[])
 
 }
 
+static uint8_t *parse_key(struct l_dbus_message_iter *iter, uint16_t id,
+                                                       const char *name)
+{
+       uint8_t *val;
+       uint32_t len;
+
+       if (!l_dbus_message_iter_get_fixed_array(iter, &val, &len)
+                                                               || len != 16) {
+               bt_shell_printf("Failed to parse %s %4.4x\n", name, id);
+               return NULL;
+       }
+
+       return val;
+}
+
+static bool parse_app_keys(struct l_dbus_message_iter *iter, uint16_t net_idx,
+                                                               void *user_data)
+{
+       struct l_dbus_message_iter app_keys, app_key, opts;
+       uint16_t app_idx;
+
+       if (!l_dbus_message_iter_get_variant(iter, "a(qaya{sv})", &app_keys))
+               return false;
+
+       while (l_dbus_message_iter_next_entry(&app_keys, &app_idx, &app_key,
+                                                               &opts)) {
+               struct l_dbus_message_iter var;
+               uint8_t *val, *old_val = NULL;
+               const char *key;
+
+               val = parse_key(&app_key, app_idx, "AppKey");
+               if (!val)
+                       return false;
+
+               while (l_dbus_message_iter_next_entry(&opts, &key, &var)) {
+                       if (!strcmp(key, "OldKey")) {
+                               if (!l_dbus_message_iter_get_variant(&var, "ay",
+                                                               &app_key))
+                                       return false;
+
+                               old_val = parse_key(&app_key, app_idx,
+                                                               "old NetKey");
+
+                               if (!old_val)
+                                       return false;
+                       }
+               }
+
+               mesh_db_set_app_key(user_data, net_idx, app_idx, val, old_val);
+       }
+
+       return true;
+}
+
+static bool parse_net_keys(struct l_dbus_message_iter *iter, void *user_data)
+{
+       struct l_dbus_message_iter net_keys, net_key, opts;
+       uint16_t idx;
+
+       if (!l_dbus_message_iter_get_variant(iter, "a(qaya{sv})", &net_keys))
+               return false;
+
+       while (l_dbus_message_iter_next_entry(&net_keys, &idx, &net_key,
+                                                               &opts)) {
+               struct l_dbus_message_iter var;
+               uint8_t *val, *old_val = NULL;
+               uint8_t phase = KEY_REFRESH_PHASE_NONE;
+               const char *key;
+
+               val = parse_key(&net_key, idx, "NetKey");
+               if (!val)
+                       return false;
+
+               while (l_dbus_message_iter_next_entry(&opts, &key, &var)) {
+                       if (!strcmp(key, "AppKeys")) {
+                               if (!parse_app_keys(&var, idx, user_data))
+                                       return false;
+                       } else if (!strcmp(key, "Phase")) {
+                               if (!l_dbus_message_iter_get_variant(&var, "y",
+                                                                       &phase))
+                                       return false;
+                       } else if (!strcmp(key, "OldKey")) {
+                               if (!l_dbus_message_iter_get_variant(&var, "ay",
+                                                               &net_key))
+                                       return false;
+
+                               old_val = parse_key(&net_key, idx,
+                                                               "old NetKey");
+
+                               if (!old_val)
+                                       return false;
+                       }
+               }
+
+               mesh_db_set_net_key(user_data, idx, val, old_val, phase);
+       }
+
+       return true;
+}
+
+static bool parse_dev_keys(struct l_dbus_message_iter *iter, void *user_data)
+{
+       struct l_dbus_message_iter keys, dev_key;
+       uint16_t unicast;
+
+       if (!l_dbus_message_iter_get_variant(iter, "a(qay)", &keys))
+               return false;
+
+       while (l_dbus_message_iter_next_entry(&keys, &unicast, &dev_key)) {
+               uint8_t *data;
+
+               data = parse_key(&dev_key, unicast, "Device Key");
+               if (!data)
+                       return false;
+
+               mesh_db_set_device_key(user_data, unicast, data);
+       }
+
+       return true;
+}
+
+static void export_keys_reply(struct l_dbus_proxy *proxy,
+                               struct l_dbus_message *msg, void *user_data)
+{
+       struct l_dbus_message_iter iter, var;
+       char *cfg_dir = NULL, *fname = NULL;
+       const char *key;
+       bool is_error = true;
+
+       if (l_dbus_message_is_error(msg)) {
+               const char *name;
+
+               l_dbus_message_get_error(msg, &name, NULL);
+               bt_shell_printf("Failed to export keys: %s", name);
+               goto done;
+
+       }
+
+       if (!l_dbus_message_get_arguments(msg, "a{sv}", &iter)) {
+               bt_shell_printf("Malformed ExportKeys reply");
+               goto done;
+       }
+
+       while (l_dbus_message_iter_next_entry(&iter, &key, &var)) {
+               if (!strcmp(key, "NetKeys")) {
+                       if (!parse_net_keys(&var, user_data))
+                               goto done;
+               } else if (!strcmp(key, "DevKeys")) {
+                       if (!parse_dev_keys(&var, user_data))
+                               goto done;
+               }
+       }
+
+       is_error = false;
+
+       cfg_dir = l_strdup(cfg_fname);
+       cfg_dir = dirname(cfg_dir);
+
+       fname = l_strdup_printf("%s/%s", cfg_dir, DEFAULT_EXPORT_FILE);
+
+done:
+       if (mesh_db_finish_export(is_error, user_data, fname)) {
+               if (!is_error)
+                       bt_shell_printf("Config DB is exported to %s\n", fname);
+       }
+
+       l_free(cfg_dir);
+       l_free(fname);
+}
+
+static void cmd_export_db(int argc, char *argv[])
+{
+       void *cfg_export;
+
+       if (!local || !local->proxy || !local->mgmt_proxy) {
+               bt_shell_printf("Node is not attached\n");
+               return;
+       }
+
+       /* Generate a properly formatted DB from the local config */
+       cfg_export = mesh_db_prepare_export();
+       if (!cfg_export) {
+               bt_shell_printf("Failed to prepare config db\n");
+               return;
+       }
+
+       /* Export the keys from the daemon */
+       l_dbus_proxy_method_call(local->mgmt_proxy, "ExportKeys", NULL,
+                                       export_keys_reply, cfg_export, NULL);
+}
+
 static void cmd_list_unprov(int argc, char *argv[])
 {
        bt_shell_printf(COLOR_YELLOW "Unprovisioned devices:\n" COLOR_OFF);
@@ -1397,6 +1590,8 @@ static const struct bt_shell_menu main_menu = {
                        "List remote mesh nodes"},
        { "keys", NULL, cmd_keys,
                        "List available keys"},
+       { "export-db", NULL, cmd_export_db,
+                       "Export mesh configuration database"},
        { } },
 };
 
index b2c0ae6..195029d 100644 (file)
@@ -50,6 +50,13 @@ static struct mesh_db *cfg;
 static const char *bak_ext = ".bak";
 static const char *tmp_ext = ".tmp";
 
+static const char *js_schema = "http://json-schema.org/draft-04/schema#";
+static const char *schema_id = "http://www.bluetooth.com/specifications/"
+                               "assigned-numbers/mesh-profile/"
+                               "cdb-schema.json#";
+const char *schema_version = "1.0.0";
+
+
 static bool add_string(json_object *jobj, const char *desc, const char *str)
 {
        json_object *jstring = json_object_new_string(str);
@@ -2414,3 +2421,187 @@ fail:
 
        return false;
 }
+
+bool mesh_db_set_device_key(void *expt_cfg, uint16_t unicast, uint8_t key[16])
+{
+       json_object *jnode;
+
+       if (!expt_cfg)
+               return false;
+
+       jnode = get_node_by_unicast(expt_cfg, unicast);
+       if (!jnode)
+               return false;
+
+       return add_u8_16(jnode, "deviceKey", key);
+}
+
+bool mesh_db_set_net_key(void *expt_cfg, uint16_t idx, uint8_t key[16],
+                                       uint8_t *old_key, uint8_t phase)
+{
+       json_object *jarray, *jkey;
+
+       if (!expt_cfg)
+               return false;
+
+       json_object_object_get_ex(expt_cfg, "netKeys", &jarray);
+       if (!jarray || json_object_get_type(jarray) != json_type_array)
+               return false;
+
+       jkey = get_key_object(jarray, idx);
+       if (!jkey)
+               return false;
+
+       if (!write_int(jkey, "phase", phase))
+               return false;
+
+       if (!add_u8_16(jkey, "key", key))
+               return false;
+
+       if (old_key && !(!add_u8_16(jkey, "oldKey", old_key)))
+               return false;
+
+       return true;
+}
+
+
+bool mesh_db_set_app_key(void *expt_cfg, uint16_t net_idx, uint16_t app_idx,
+                                       uint8_t key[16], uint8_t *old_key)
+{
+       json_object *jarray, *jkey;
+
+       if (!expt_cfg)
+               return false;
+
+       json_object_object_get_ex(expt_cfg, "appKeys", &jarray);
+       if (!jarray || json_object_get_type(jarray) != json_type_array)
+               return false;
+
+       jkey = get_key_object(jarray, app_idx);
+       if (!jkey)
+               return false;
+
+       if (!add_u8_16(jkey, "key", key))
+               return false;
+
+       if (old_key && !(!add_u8_16(jkey, "oldKey", old_key)))
+               return false;
+
+       return true;
+}
+
+void *mesh_db_prepare_export(void)
+{
+       json_object *export = NULL, *jarray;
+
+       if (!cfg || !cfg->jcfg)
+               return false;
+
+       if (json_object_deep_copy(cfg->jcfg, &export, NULL) != 0)
+               return NULL;
+
+       /* Delete token */
+       json_object_object_del(export, "token");
+
+       /* Delete IV index */
+       json_object_object_del(export, "ivIndex");
+
+       /* Scenes are not supported. Just add an empty array */
+       jarray = json_object_new_array();
+       json_object_object_add(export, "scenes", jarray);
+
+       write_bool(export, "partial", false);
+
+       return export;
+}
+
+bool mesh_db_finish_export(bool is_error, void *expt_cfg, const char *fname)
+{
+       FILE *outfile = NULL;
+       const char *str, *hdr;
+       json_object *jhdr = NULL;
+       bool result = false;
+       char *pos;
+
+       uint32_t sz;
+
+       if (!expt_cfg)
+               return false;
+
+       if (is_error) {
+               json_object_put(expt_cfg);
+               return true;
+       }
+
+       if (!fname)
+               goto done;
+
+       outfile = fopen(fname, "w");
+       if (!outfile) {
+               l_error("Failed to save configuration to %s", fname);
+               goto done;
+       }
+
+       jhdr = json_object_new_object();
+       if (!add_string(jhdr, "$schema", js_schema))
+               goto done;
+
+       if (!add_string(jhdr, "id", schema_id))
+               goto done;
+
+       if (!add_string(jhdr, "version", schema_version))
+               goto done;
+
+       hdr = json_object_to_json_string_ext(jhdr, JSON_C_TO_STRING_PRETTY |
+                                               JSON_C_TO_STRING_NOSLASHESCAPE);
+
+       str = json_object_to_json_string_ext(expt_cfg, JSON_C_TO_STRING_PRETTY |
+                                               JSON_C_TO_STRING_NOSLASHESCAPE);
+
+       if (!hdr || !str)
+               goto done;
+
+       /*
+        * Write two strings to the output while stripping closing "}" from the
+        * header string and opening "{" from the config object.
+        */
+
+       pos = strrchr(hdr, '}');
+       if (!pos)
+               goto done;
+
+       *pos = '\0';
+
+       pos = strrchr(hdr, '"');
+       if (!pos)
+               goto done;
+
+       pos[1] = ',';
+
+       if (fwrite(hdr, sizeof(char), strlen(hdr), outfile) < strlen(hdr))
+               goto done;
+
+       pos = strchr(str, '{');
+       if (!pos || pos[1] == '\0')
+               goto done;
+
+       pos++;
+
+       sz = strlen(pos);
+
+       if (fwrite(pos, sizeof(char), sz, outfile) < sz)
+               goto done;
+
+       result = true;
+
+done:
+       if (outfile)
+               fclose(outfile);
+
+       json_object_put(expt_cfg);
+
+       if (jhdr)
+               json_object_put(jhdr);
+
+       return result;
+}
index 84b8c95..9d0346a 100644 (file)
@@ -82,3 +82,10 @@ struct l_queue *mesh_db_load_groups(void);
 bool mesh_db_add_group(struct mesh_group *grp);
 bool mesh_db_add_rejected_addr(uint16_t unicast, uint32_t iv_index);
 bool mesh_db_clear_rejected(uint32_t iv_index);
+bool mesh_db_set_device_key(void *expt_cfg, uint16_t unicast, uint8_t key[16]);
+bool mesh_db_set_net_key(void *expt_cfg, uint16_t idx, uint8_t key[16],
+                                       uint8_t *old_key, uint8_t phase);
+bool mesh_db_set_app_key(void *expt_cfg, uint16_t net_idx, uint16_t app_idx,
+                                       uint8_t key[16], uint8_t *old_key);
+void *mesh_db_prepare_export(void);
+bool mesh_db_finish_export(bool is_error, void *expt_cfg, const char *fname);