Diff between f3243ecba0a2a062021e461afc4fdc91e480f510 and c50ecca56654f3c1c4b3cc9f9402a64cca9164f8

Changed Files

File Additions Deletions Status
tools/mesh-cfgclient.c +406 -73 modified
tools/mesh/cfgcli.c +31 -6 modified
tools/mesh/mesh-db.c +29 -8 modified
tools/mesh/mesh-db.h +1 -0 modified
tools/mesh/remote.c +122 -0 modified
tools/mesh/remote.h +9 -0 modified

Full Patch

diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
index 237afbb..f3e9af8 100644
--- a/tools/mesh-cfgclient.c
+++ b/tools/mesh-cfgclient.c
@@ -43,6 +43,7 @@
 
 #define CFG_SRV_MODEL	0x0000
 #define CFG_CLI_MODEL	0x0001
+#define RPR_SVR_MODEL	0xFFFF0004
 
 #define UNPROV_SCAN_MAX_SECS	300
 
@@ -83,8 +84,12 @@ struct meshcfg_node {
 
 struct unprov_device {
 	time_t last_seen;
-	int16_t rssi;
+	int id;
+	uint32_t uri_hash;
 	uint8_t uuid[16];
+	int16_t rssi;
+	uint16_t server;
+	uint16_t oob_info;
 };
 
 struct generic_request {
@@ -96,8 +101,16 @@ struct generic_request {
 	const char *str;
 };
 
+struct scan_data {
+	uint16_t dst;
+	uint16_t secs;
+};
+
+static void *finalized = L_UINT_TO_PTR(-1);
+
 static struct l_dbus *dbus;
 
+static struct l_timeout *scan_timeout;
 static struct l_queue *node_proxies;
 static struct l_dbus_proxy *net_proxy;
 static struct meshcfg_node *local;
@@ -197,23 +210,57 @@ static bool parse_argument_on_off(int argc, char *argv[], bool *value)
 static bool match_device_uuid(const void *a, const void *b)
 {
 	const struct unprov_device *dev = a;
-	const uint8_t *uuid = b;
 
-	return (memcmp(dev->uuid, uuid, 16) == 0);
+	if (a == finalized)
+		return false;
+
+	return memcmp(dev->uuid, b, 16) == 0;
 }
 
-static void print_device(void *a, void *b)
+static bool match_by_id(const void *a, const void *b)
 {
 	const struct unprov_device *dev = a;
-	struct tm *tm = localtime(&dev->last_seen);
+	int id = L_PTR_TO_UINT(b);
+
+	if (a == finalized)
+		return false;
+
+	l_info("test %d %d", dev->id, id);
+	return dev->id == id;
+}
+
+static bool match_by_srv_uuid(const void *a, const void *b)
+{
+	const struct unprov_device *dev = a;
+	const struct unprov_device *new_dev = b;
+
+	if (a == finalized)
+		return false;
+
+	return (dev->server == new_dev->server) &&
+				(memcmp(dev->uuid, new_dev->uuid, 16) == 0);
+}
+
+static void print_device(void *a, void *b)
+{
+	struct unprov_device *dev = a;
+	int *cnt = b;
+	struct tm *tm;
 	char buf[80];
 	char *str;
 
+	if (a == finalized)
+		return;
+
+	tm = localtime(&dev->last_seen);
 	assert(strftime(buf, sizeof(buf), "%c", tm));
+	(*cnt)++;
 
+	dev->id = *cnt;
 	str = l_util_hexstring_upper(dev->uuid, sizeof(dev->uuid));
-	bt_shell_printf("UUID: %s, RSSI %d, Seen: %s\n",
-			str, dev->rssi, buf);
+	bt_shell_printf(COLOR_YELLOW "#%d" COLOR_OFF
+			" UUID: %s, RSSI %d, Server: %4.4x\n Seen: %s\n",
+			*cnt, str, dev->rssi, dev->server, buf);
 
 	l_free(str);
 }
@@ -794,15 +841,56 @@ static void scan_reply(struct l_dbus_proxy *proxy, struct l_dbus_message *msg,
 
 static void scan_setup(struct l_dbus_message *msg, void *user_data)
 {
-	uint16_t secs = (uint16_t) L_PTR_TO_UINT(user_data);
+	struct scan_data *data = user_data;
 	struct l_dbus_message_builder *builder;
 
 	builder = l_dbus_message_builder_new(msg);
 	l_dbus_message_builder_enter_array(builder, "{sv}");
-	append_dict_entry_basic(builder, "Seconds", "q", &secs);
+	append_dict_entry_basic(builder, "Seconds", "q", &data->secs);
+	append_dict_entry_basic(builder, "Server", "q", &data->dst);
 	l_dbus_message_builder_leave_array(builder);
 	l_dbus_message_builder_finalize(builder);
 	l_dbus_message_builder_destroy(builder);
+
+	/* Destination info not needed after call */
+	l_free(data);
+}
+
+static void scan_start(void *user_data, uint16_t dst, uint32_t model)
+{
+	struct scan_data *data;
+
+	if (model != RPR_SVR_MODEL)
+		return;
+
+	data = l_malloc(sizeof(struct scan_data));
+	data->secs = L_PTR_TO_UINT(user_data);
+	data->dst = dst;
+
+	if (!l_dbus_proxy_method_call(local->mgmt_proxy, "UnprovisionedScan",
+					scan_setup, scan_reply, data, NULL))
+		l_free(data);
+}
+
+static void scan_to(struct l_timeout *timeout, void *user_data)
+{
+	int cnt = 0;
+
+	if (l_queue_peek_head(devices) != finalized)
+		l_queue_push_head(devices, finalized);
+
+	l_timeout_remove(timeout);
+	scan_timeout = NULL;
+	bt_shell_printf(COLOR_YELLOW "Unprovisioned devices:\n" COLOR_OFF);
+	l_queue_foreach(devices, print_device, &cnt);
+}
+
+static void free_devices(void *a)
+{
+	if (a == finalized)
+		return;
+
+	l_free(a);
 }
 
 static void cmd_scan_unprov(int argc, char *argv[])
@@ -820,21 +908,28 @@ static void cmd_scan_unprov(int argc, char *argv[])
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
 	}
 
-	if (argc == 3)
+	if (argc == 3) {
 		sscanf(argv[2], "%u", &secs);
 
-	if (secs > UNPROV_SCAN_MAX_SECS)
-		secs = UNPROV_SCAN_MAX_SECS;
+		if (secs > UNPROV_SCAN_MAX_SECS)
+			secs = UNPROV_SCAN_MAX_SECS;
+	} else
+		secs = 60;
 
-	if (enable)
-		l_dbus_proxy_method_call(local->mgmt_proxy, "UnprovisionedScan",
-						scan_setup, scan_reply,
-						L_UINT_TO_PTR(secs), NULL);
-	else
+	l_timeout_remove(scan_timeout);
+	scan_timeout = NULL;
+
+	if (enable) {
+		l_queue_clear(devices, free_devices);
+		remote_foreach_model(scan_start, L_UINT_TO_PTR(secs));
+		scan_timeout = l_timeout_create(secs, scan_to, NULL, NULL);
+	} else {
+		/* Mark devices queue as finalized */
+		l_queue_push_head(devices, finalized);
 		l_dbus_proxy_method_call(local->mgmt_proxy,
 						"UnprovisionedScanCancel",
 						NULL, NULL, NULL, NULL);
-
+	}
 }
 
 static uint8_t *parse_key(struct l_dbus_message_iter *iter, uint16_t id,
@@ -1030,8 +1125,10 @@ static void cmd_export_db(int argc, char *argv[])
 
 static void cmd_list_unprov(int argc, char *argv[])
 {
+	int cnt = 0;
+
 	bt_shell_printf(COLOR_YELLOW "Unprovisioned devices:\n" COLOR_OFF);
-	l_queue_foreach(devices, print_device, NULL);
+	l_queue_foreach(devices, print_device, &cnt);
 }
 
 static void cmd_list_nodes(int argc, char *argv[])
@@ -1505,32 +1602,56 @@ static void add_node_reply(struct l_dbus_proxy *proxy,
 	bt_shell_printf("Provisioning started\n");
 }
 
-static void add_node_setup(struct l_dbus_message *msg, void *user_data)
+static void reprov_reply(struct l_dbus_proxy *proxy,
+				struct l_dbus_message *msg, void *user_data)
 {
-	char *str = user_data;
-	size_t sz;
-	unsigned char *uuid;
-	struct l_dbus_message_builder *builder;
+	if (l_dbus_message_is_error(msg)) {
+		const char *name;
 
-	uuid = l_util_from_hexstring(str, &sz);
-	if (!uuid || sz != 16 || !l_uuid_is_valid(uuid)) {
-		l_error("Failed to generate UUID array from %s", str);
+		prov_in_progress = false;
+		l_dbus_message_get_error(msg, &name, NULL);
+		l_error("Failed to start provisioning: %s", name);
 		return;
 	}
 
+	bt_shell_printf("Reprovisioning started\n");
+}
+
+static void reprovision_setup(struct l_dbus_message *msg, void *user_data)
+{
+	uint16_t target = L_PTR_TO_UINT(user_data);
+	uint8_t nppi = L_PTR_TO_UINT(user_data) >> 16;
+	struct l_dbus_message_builder *builder;
+
 	builder = l_dbus_message_builder_new(msg);
-	append_byte_array(builder, uuid, 16);
+	l_dbus_message_builder_append_basic(builder, 'q', &target);
 	l_dbus_message_builder_enter_array(builder, "{sv}");
 	/* TODO: populate with options when defined */
+	append_dict_entry_basic(builder, "NPPI", "y", &nppi);
 	l_dbus_message_builder_leave_array(builder);
 	l_dbus_message_builder_finalize(builder);
 	l_dbus_message_builder_destroy(builder);
+}
 
-	l_free(uuid);
+static void add_node_setup(struct l_dbus_message *msg, void *user_data)
+{
+	struct unprov_device *dev = user_data;
+	struct l_dbus_message_builder *builder;
+
+	builder = l_dbus_message_builder_new(msg);
+	append_byte_array(builder, dev->uuid, 16);
+	l_dbus_message_builder_enter_array(builder, "{sv}");
+	/* TODO: populate with options when defined */
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
 }
 
 static void cmd_start_prov(int argc, char *argv[])
 {
+	struct unprov_device *dev = NULL;
+	int id;
+
 	if (!local || !local->proxy || !local->mgmt_proxy) {
 		bt_shell_printf("Node is not attached\n");
 		return;
@@ -1541,14 +1662,96 @@ static void cmd_start_prov(int argc, char *argv[])
 		return;
 	}
 
-	if (!argv[1] || (strlen(argv[1]) != 32)) {
+	if (!argv[1]) {
+		bt_shell_printf(COLOR_RED "Requires UUID\n" COLOR_RED);
+		return;
+	}
+
+	if (*(argv[1]) == '#') {
+		if (sscanf(argv[1] + 1, "%d", &id) == 1)
+			dev = l_queue_find(devices, match_by_id,
+							L_UINT_TO_PTR(id));
+
+		if (!dev) {
+			bt_shell_printf(COLOR_RED "unknown id\n" COLOR_RED);
+			return;
+		}
+	} else if (strlen(argv[1]) == 32) {
+		size_t sz;
+		uint8_t *uuid = l_util_from_hexstring(argv[1], &sz);
+
+		if (sz != 16) {
+			bt_shell_printf(COLOR_RED "Invalid UUID\n" COLOR_RED);
+			return;
+		}
+
+		dev = l_queue_find(devices, match_device_uuid, uuid);
+
+		if (!dev) {
+			dev = l_new(struct unprov_device, 1);
+			memcpy(dev->uuid, uuid, 16);
+			l_queue_push_tail(devices, dev);
+		}
+
+		l_free(uuid);
+
+	} else {
 		bt_shell_printf(COLOR_RED "Requires UUID\n" COLOR_RED);
 		return;
 	}
 
 	if (l_dbus_proxy_method_call(local->mgmt_proxy, "AddNode",
 						add_node_setup, add_node_reply,
-						argv[1], NULL))
+						dev, NULL))
+		prov_in_progress = true;
+}
+
+static void cmd_start_reprov(int argc, char *argv[])
+{
+	uint16_t target = 0;
+	uint8_t nppi = 0;
+
+	if (!local || !local->proxy || !local->mgmt_proxy) {
+		bt_shell_printf("Node is not attached\n");
+		return;
+	}
+
+	if (prov_in_progress) {
+		bt_shell_printf("Provisioning is already in progress\n");
+		return;
+	}
+
+	if (!argv[1]) {
+		bt_shell_printf(COLOR_RED "Requires Unicast\n" COLOR_RED);
+		return;
+	}
+
+	if (argv[2]) {
+		char *end;
+
+		nppi = strtol(argv[2], &end, 16);
+	}
+
+	if (strlen(argv[1]) == 4) {
+		char *end;
+
+		target = strtol(argv[1], &end, 16);
+
+		if (end != (argv[1] + 4)) {
+			bt_shell_printf(COLOR_RED "Invalid Unicast\n"
+								COLOR_RED);
+			return;
+		}
+
+	} else {
+		bt_shell_printf(COLOR_RED "Requires Unicast\n" COLOR_RED);
+		return;
+	}
+
+	if (l_dbus_proxy_method_call(local->mgmt_proxy, "Reprovision",
+					reprovision_setup, reprov_reply,
+					L_UINT_TO_PTR(target + (nppi << 16)),
+					NULL))
 		prov_in_progress = true;
 }
 
@@ -1581,6 +1784,8 @@ static const struct bt_shell_menu main_menu = {
 			"List unprovisioned devices" },
 	{ "provision", "<uuid>", cmd_start_prov,
 			"Initiate provisioning"},
+	{ "reprovision", "<unicast> [0|1|2]", cmd_start_reprov,
+			"Refresh Device Key"},
 	{ "node-import", "<uuid> <net_idx> <primary> <ele_count> <dev_key>",
 			cmd_import_node,
 			"Import an externally provisioned remote node"},
@@ -1758,18 +1963,34 @@ static void setup_ele_iface(struct l_dbus_interface *iface)
 	/* TODO: Other methods */
 }
 
+static int sort_rssi(const void *a, const void *b, void *user_data)
+{
+	const struct unprov_device *new_dev = a;
+	const struct unprov_device *dev = b;
+
+	if (b == finalized)
+		return 1;
+
+	return dev->rssi - new_dev->rssi;
+}
+
 static struct l_dbus_message *scan_result_call(struct l_dbus *dbus,
 						struct l_dbus_message *msg,
 						void *user_data)
 {
-	struct l_dbus_message_iter iter, opts;
+	struct l_dbus_message_iter iter, opts, var;
+	struct unprov_device result, *dev;
 	int16_t rssi;
+	uint16_t server = 0;
 	uint32_t n;
 	uint8_t *prov_data;
-	char *str;
-	struct unprov_device *dev;
+	const char *key;
 	const char *sig = "naya{sv}";
 
+	if (finalized == l_queue_peek_head(devices))
+		goto done;
+
+
 	if (!l_dbus_message_get_arguments(msg, sig, &rssi, &iter, &opts)) {
 		l_error("Cannot parse scan results");
 		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
@@ -1781,42 +2002,72 @@ static struct l_dbus_message *scan_result_call(struct l_dbus *dbus,
 		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
 	}
 
-	bt_shell_printf("Scan result:\n");
-	bt_shell_printf("\t" COLOR_GREEN "rssi = %d\n" COLOR_OFF, rssi);
-	str = l_util_hexstring_upper(prov_data, 16);
-	bt_shell_printf("\t" COLOR_GREEN "UUID = %s\n" COLOR_OFF, str);
-	l_free(str);
-
-	if (n >= 18) {
-		str = l_util_hexstring_upper(prov_data + 16, 2);
-		bt_shell_printf("\t" COLOR_GREEN "OOB = %s\n" COLOR_OFF, str);
-		l_free(str);
+	while (l_dbus_message_iter_next_entry(&opts, &key, &var)) {
+		if (!strcmp(key, "Server"))
+			l_dbus_message_iter_get_variant(&var, "q", &server);
 	}
 
-	if (n >= 22) {
-		str = l_util_hexstring_upper(prov_data + 18, 4);
-		bt_shell_printf("\t" COLOR_GREEN "URI Hash = %s\n" COLOR_OFF,
-									str);
-		l_free(str);
-	}
+	memcpy(result.uuid, prov_data, 16);
+	result.server = server;
+	result.rssi = rssi;
+	result.id = 0;
 
-	/* TODO: Handle the rest of provisioning data if present */
+	if (n > 16 && n <= 18)
+		result.oob_info = l_get_be16(prov_data + 16);
+	else
+		result.oob_info = 0;
+
+	if (n > 18 && n <= 22)
+		result.uri_hash = l_get_be32(prov_data + 18);
+	else
+		result.uri_hash = 0;
+
+	dev = l_queue_remove_if(devices, match_by_srv_uuid, &result);
 
-	dev = l_queue_find(devices, match_device_uuid, prov_data);
 	if (!dev) {
-		dev = l_new(struct unprov_device, 1);
-		memcpy(dev->uuid, prov_data, sizeof(dev->uuid));
-		/* TODO: timed self-destructor */
-		l_queue_push_tail(devices, dev);
-	}
+		bt_shell_printf("\r" COLOR_YELLOW "Results = %d\n" COLOR_OFF,
+						l_queue_length(devices) + 1);
+		dev = l_malloc(sizeof(struct unprov_device));
+		*dev = result;
+
+	} else if (dev->rssi < result.rssi)
+		*dev = result;
 
-	/* Update with the latest rssi */
-	dev->rssi = rssi;
 	dev->last_seen = time(NULL);
 
+	l_queue_insert(devices, dev, sort_rssi, NULL);
+
+done:
 	return l_dbus_message_new_method_return(msg);
 }
 
+static struct l_dbus_message *req_reprov_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	uint8_t cnt;
+	uint16_t unicast, original;
+	struct l_dbus_message *reply;
+
+
+	if (!l_dbus_message_get_arguments(msg, "qy", &original, &cnt) ||
+							!IS_UNICAST(original)) {
+		l_error("Cannot parse request for reprov data");
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+
+	}
+
+	unicast = remote_get_next_unicast(low_addr, high_addr, cnt);
+
+	bt_shell_printf("Assign addresses for %u elements\n", cnt);
+	bt_shell_printf("Original: %4.4x New: %4.4x\n", original, unicast);
+
+	reply = l_dbus_message_new_method_return(msg);
+	l_dbus_message_set_arguments(reply, "q", unicast);
+
+	return reply;
+}
+
 static struct l_dbus_message *req_prov_call(struct l_dbus *dbus,
 						struct l_dbus_message *msg,
 						void *user_data)
@@ -1825,6 +2076,7 @@ static struct l_dbus_message *req_prov_call(struct l_dbus *dbus,
 	uint16_t unicast;
 	struct l_dbus_message *reply;
 
+	/* Both calls handled identicaly except for parameter list */
 	if (!l_dbus_message_get_arguments(msg, "y", &cnt)) {
 		l_error("Cannot parse request for prov data");
 		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
@@ -1833,14 +2085,14 @@ static struct l_dbus_message *req_prov_call(struct l_dbus *dbus,
 
 	unicast = remote_get_next_unicast(low_addr, high_addr, cnt);
 
-	if (unicast == 0) {
+	if (!IS_UNICAST(unicast)) {
 		l_error("Failed to allocate addresses for %u elements\n", cnt);
 		return l_dbus_message_new_error(msg,
 					"org.freedesktop.DBus.Error."
 					"Failed to allocate address", NULL);
 	}
 
-	bt_shell_printf("Assign addresses for %u elements\n", cnt);
+	bt_shell_printf("Assign addresses: %4.4x (cnt: %d)\n", unicast, cnt);
 
 	reply = l_dbus_message_new_method_return(msg);
 	l_dbus_message_set_arguments(reply, "qq", prov_net_idx, unicast);
@@ -1852,11 +2104,13 @@ static void remove_device(uint8_t *uuid)
 {
 	struct unprov_device *dev;
 
-	dev = l_queue_remove_if(devices, match_device_uuid, uuid);
-	l_free(dev);
+	do {
+		dev = l_queue_remove_if(devices, match_device_uuid, uuid);
+		l_free(dev);
+	} while (dev);
 }
 
-static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus,
+static struct l_dbus_message *prov_cmplt_call(struct l_dbus *dbus,
 						struct l_dbus_message *msg,
 						void *user_data)
 {
@@ -1866,6 +2120,7 @@ static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus,
 	uint32_t n;
 	uint8_t *uuid;
 
+	l_debug("ProvComplete");
 	if (!prov_in_progress)
 		return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
 
@@ -1896,7 +2151,49 @@ static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus,
 	return l_dbus_message_new_method_return(msg);
 }
 
-static struct l_dbus_message *add_node_fail_call(struct l_dbus *dbus,
+static struct l_dbus_message *reprov_cmplt_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	uint16_t unicast, original;
+	uint8_t old_cnt, cnt, nppi;
+
+	l_debug("ReprovComplete");
+	if (!prov_in_progress)
+		return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
+
+	prov_in_progress = false;
+
+	if (!l_dbus_message_get_arguments(msg, "qyqy", &original, &nppi,
+							&unicast, &cnt)) {
+		l_error("Cannot parse reprov complete message");
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+
+	}
+
+	l_debug("ReprovComplete org: %4.4x, nppi: %d, new: %4.4x, cnt: %d",
+						original, nppi, unicast, cnt);
+	old_cnt = remote_ele_cnt(original);
+
+	if (nppi != 1 && (original != unicast || cnt != old_cnt)) {
+		l_error("Invalid reprov complete message (NPPI == %d)", nppi);
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+	}
+
+	if (nppi)
+		remote_reset_node(original, unicast, cnt,
+						mesh_db_get_iv_index());
+
+	bt_shell_printf("Reprovisioning done (nppi: %d):\n", nppi);
+	remote_print_node(unicast);
+
+	if (!mesh_db_reset_node(original, unicast, cnt))
+		l_error("Failed to reset remote node");
+
+	return l_dbus_message_new_method_return(msg);
+}
+
+static struct l_dbus_message *prov_fail_call(struct l_dbus *dbus,
 						struct l_dbus_message *msg,
 						void *user_data)
 {
@@ -1911,24 +2208,49 @@ static struct l_dbus_message *add_node_fail_call(struct l_dbus *dbus,
 	prov_in_progress = false;
 
 	if (!l_dbus_message_get_arguments(msg, "ays", &iter, &reason)) {
-		l_error("Cannot parse add node failed message");
+		l_error("Cannot parse failed message");
 		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
-
 	}
 
-	if (!l_dbus_message_iter_get_fixed_array(&iter, &uuid, &n) ||
-								n != 16) {
-		l_error("Cannot parse add node failed message: uuid");
+	if (!l_dbus_message_iter_get_fixed_array(&iter, &uuid, &n) || n != 16) {
+		l_error("Cannot parse failed message: uuid");
 		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
 	}
 
 	bt_shell_printf("Provisioning failed:\n");
+
 	str = l_util_hexstring_upper(uuid, 16);
 	bt_shell_printf("\t" COLOR_RED "UUID = %s\n" COLOR_OFF, str);
 	l_free(str);
+	remove_device(uuid);
 	bt_shell_printf("\t" COLOR_RED "%s\n" COLOR_OFF, reason);
 
-	remove_device(uuid);
+	return l_dbus_message_new_method_return(msg);
+}
+
+static struct l_dbus_message *reprov_fail_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct l_dbus_message_iter iter;
+	uint16_t original = UNASSIGNED_ADDRESS;
+	char *reason;
+
+	if (!prov_in_progress)
+		return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
+
+	prov_in_progress = false;
+
+	if (!l_dbus_message_get_arguments(msg, "qs", &iter, &reason) ||
+							!IS_UNICAST(original)) {
+
+		l_error("Cannot parse Reprov failed message");
+		return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+	}
+
+	bt_shell_printf("Reprovisioning failed:\n");
+	bt_shell_printf("\t" COLOR_RED "UNICAST = %4.4x\n" COLOR_OFF, original);
+	bt_shell_printf("\t" COLOR_RED "%s\n" COLOR_OFF, reason);
 
 	return l_dbus_message_new_method_return(msg);
 }
@@ -1941,12 +2263,23 @@ static void setup_prov_iface(struct l_dbus_interface *iface)
 	l_dbus_interface_method(iface, "RequestProvData", 0, req_prov_call,
 				"qq", "y", "net_index", "unicast", "count");
 
+	l_dbus_interface_method(iface, "RequestReprovData", 0, req_reprov_call,
+					"q", "qy", "unicast",
+					"original", "count");
+
 	l_dbus_interface_method(iface, "AddNodeComplete", 0,
-					add_node_cmplt_call, "", "ayqy",
+					prov_cmplt_call, "", "ayqy",
 					"uuid", "unicast", "count");
 
-	l_dbus_interface_method(iface, "AddNodeFailed", 0, add_node_fail_call,
+	l_dbus_interface_method(iface, "ReprovComplete", 0,
+					reprov_cmplt_call, "", "qyqy",
+					"original", "nppi", "unicast", "count");
+
+	l_dbus_interface_method(iface, "AddNodeFailed", 0, prov_fail_call,
 					"", "ays", "uuid", "reason");
+
+	l_dbus_interface_method(iface, "ReprovFailed", 0, reprov_fail_call,
+					"", "qs", "unicast", "reason");
 }
 
 static bool cid_getter(struct l_dbus *dbus,
diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index a48eace..ad572f6 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -266,14 +266,21 @@ static uint32_t print_mod_id(uint8_t *data, bool vendor, const char *offset)
 	return mod_id;
 }
 
-static void print_composition(uint8_t *data, uint16_t len)
+static uint8_t print_composition(uint8_t *data, uint16_t len)
 {
 	uint16_t features;
 	int i = 0;
+	bool nppi = false;
 
-	bt_shell_printf("Received composion:\n");
+	bt_shell_printf("Received composition:\n");
+
+	/* We only support Pages 0 && 128 */
+	if (*data == 128) {
+		bt_shell_printf("Dev Key Refresh (NPPI) required\n");
+		nppi = true;
+	} else if (*data != 0)
+		return 0;
 
-	/* skip page -- We only support Page Zero */
 	data++;
 	len--;
 
@@ -328,6 +335,11 @@ static void print_composition(uint8_t *data, uint16_t len)
 
 		i++;
 	}
+
+	if (nppi)
+		return (uint8_t) i;
+	else
+		return 0;
 }
 
 static void print_pub(uint16_t ele_addr, uint32_t mod_id,
@@ -402,6 +414,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 	const struct cfg_cmd *cmd;
 	uint16_t app_idx, net_idx, addr, ele_addr, features;
 	struct mesh_group *grp;
+	uint8_t page128_cnt;
 	struct model_pub pub;
 	int n;
 	struct pending_req *req;
@@ -431,7 +444,19 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
 		if (len < MIN_COMPOSITION_LEN)
 			return true;
 
-		print_composition(data, len);
+		page128_cnt = print_composition(data, len);
+		if (page128_cnt) {
+			if (page128_cnt != remote_ele_cnt(src)) {
+				bt_shell_printf("Ele count was %d, now %d\n",
+					remote_ele_cnt(src), page128_cnt);
+				bt_shell_printf("Reprovision with NPPI-1\n");
+			} else {
+				bt_shell_printf("Models or Features changed\n");
+				bt_shell_printf("Reprovision with NPPI-2\n");
+			}
+
+			break;
+		}
 
 		saved = mesh_db_node_set_composition(src, data, len);
 		if (saved)
@@ -1051,8 +1076,8 @@ static void cmd_composition_get(int argc, char *argv[])
 
 	n = mesh_opcode_set(OP_DEV_COMP_GET, msg);
 
-	/* By default, use page 0 */
-	msg[n++] = (read_input_parameters(argc, argv) == 1) ? parms[0] : 0;
+	/* By default, use page 128 */
+	msg[n++] = (read_input_parameters(argc, argv) == 1) ? parms[0] : 128;
 
 	if (!config_send(msg, n, OP_DEV_COMP_GET))
 		return bt_shell_noninteractive_quit(EXIT_FAILURE);
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 896ff72..c0c05a2 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -1702,6 +1702,29 @@ static json_object *init_elements(uint8_t num_els)
 	return jelements;
 }
 
+bool mesh_db_reset_node(uint16_t original, uint16_t unicast, uint8_t num_els)
+{
+	json_object *jnode, *jelements;
+
+	if (!cfg || !cfg->jcfg)
+		return false;
+
+	jnode = get_node_by_unicast(cfg->jcfg, original);
+	if (!jnode) {
+		l_error("Node %4.4x does not exist", original);
+		return false;
+	}
+
+	if (!write_uint16_hex(jnode, "unicastAddress", unicast))
+		return false;
+
+	json_object_object_del(jnode, "elements");
+	jelements = init_elements(num_els);
+	json_object_object_add(jnode, "elements", jelements);
+
+	return save_config();
+}
+
 bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
 							uint16_t net_idx)
 {
@@ -1864,13 +1887,11 @@ bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data, uint16_t len)
 	if (!jnode)
 		return false;
 
-	/* skip page -- We only support Page Zero */
-	data++;
-	len--;
+	/* This is for page-0 only */
+	if (*data++ != 0)
+		return false;
 
-	/* If "crpl" property is present, composition is already recorded */
-	if (json_object_object_get_ex(jnode, "crpl", &jobj))
-		return true;
+	len--;
 
 	if (!write_uint16_hex(jnode, "cid", l_get_le16(&data[0])))
 		return false;
@@ -1954,7 +1975,6 @@ bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data, uint16_t len)
 		}
 
 		while (len >= 4 && v--) {
-			jobj = json_object_new_object();
 			mod_id = l_get_le16(data + 2);
 			mod_id = l_get_le16(data) << 16 | mod_id;
 
@@ -1984,7 +2004,8 @@ bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data, uint16_t len)
 fail:
 	/* Reset elements array */
 	json_object_object_del(jnode, "elements");
-	init_elements(sz);
+	jelements = init_elements(sz);
+	json_object_object_add(jnode, "elements", jelements);
 
 	return false;
 }
diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h
index 4b6b2ad..0e45112 100644
--- a/tools/mesh/mesh-db.h
+++ b/tools/mesh/mesh-db.h
@@ -29,6 +29,7 @@ bool mesh_db_del_app_key(uint16_t app_idx);
 bool mesh_db_get_addr_range(uint16_t *low, uint16_t *high);
 bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
 							uint16_t net_idx);
+bool mesh_db_reset_node(uint16_t original, uint16_t unicast, uint8_t num_els);
 bool mesh_db_del_node(uint16_t unicast);
 bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data,
 								uint16_t len);
diff --git a/tools/mesh/remote.c b/tools/mesh/remote.c
index 6ec220a..cee711d 100644
--- a/tools/mesh/remote.c
+++ b/tools/mesh/remote.c
@@ -30,6 +30,12 @@ struct remote_key {
 	bool updated;
 };
 
+struct foreach_data {
+	remote_foreach_t each;
+	void *user_data;
+	uint16_t dst;
+};
+
 struct remote_node {
 	uint16_t unicast;
 	struct l_queue *net_keys;
@@ -138,6 +144,40 @@ uint8_t remote_del_node(uint16_t unicast)
 	return num_ele;
 }
 
+bool remote_reset_node(uint16_t original, uint16_t unicast, uint8_t ele_cnt,
+							uint32_t iv_index)
+{
+	struct remote_node *rmt;
+	bool reject = true;
+	int i;
+
+	rmt = l_queue_remove_if(nodes, match_node_addr,
+						L_UINT_TO_PTR(original));
+	if (!rmt)
+		return false;
+
+	if (unicast == rmt->unicast)
+		reject = false;
+
+	for (i = 0; i < rmt->num_ele; ++i) {
+		l_queue_destroy(rmt->els[i], NULL);
+		if (reject)
+			remote_add_rejected_address(rmt->unicast + i,
+								iv_index, true);
+	}
+
+	if (ele_cnt != rmt->num_ele) {
+		l_free(rmt->els);
+		rmt->els = l_new(struct l_queue *, ele_cnt);
+	} else
+		memset(rmt->els, 0, sizeof(struct l_queue *) * ele_cnt);
+
+	rmt->unicast = unicast;
+	rmt->num_ele = ele_cnt;
+	l_queue_insert(nodes, rmt, compare_unicast, NULL);
+	return true;
+}
+
 bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
 					uint8_t ele_cnt, uint16_t net_idx)
 {
@@ -526,6 +566,76 @@ void remote_print_all(void)
 	l_queue_foreach(nodes, print_node, NULL);
 }
 
+static void each_node(void *rmt, void *user_data)
+{
+	struct remote_node *node = rmt;
+	struct foreach_data *data = user_data;
+
+	data->each(data->user_data, node->unicast, (uint32_t) -1);
+}
+
+static void each_addr(void *rmt, void *user_data)
+{
+	struct remote_node *node = rmt;
+	struct foreach_data *data = user_data;
+	uint16_t cnt;
+
+	for (cnt = 0; cnt <= node->num_ele; cnt++)
+		data->each(data->user_data, node->unicast + cnt, (uint32_t) -1);
+}
+
+static void parse_model(void *model, void *user_data)
+{
+	struct foreach_data *data = user_data;
+
+	data->each(data->user_data, data->dst, L_PTR_TO_UINT(model));
+}
+
+static void each_model(void *rmt, void *user_data)
+{
+	struct remote_node *node = rmt;
+	struct foreach_data *data = user_data;
+	uint16_t cnt;
+
+	for (cnt = 0; cnt < node->num_ele; cnt++) {
+		data->dst = node->unicast + cnt;
+		l_queue_foreach(node->els[cnt], parse_model, data);
+	}
+}
+
+void remote_foreach(remote_foreach_t each, void *user_data)
+{
+	struct foreach_data data = {
+		.each = each,
+		.user_data = user_data
+	};
+
+	if (each)
+		l_queue_foreach(nodes, each_node, &data);
+}
+
+void remote_foreach_unicast(remote_foreach_t each, void *user_data)
+{
+	struct foreach_data data = {
+		.each = each,
+		.user_data = user_data
+	};
+
+	if (each)
+		l_queue_foreach(nodes, each_addr, &data);
+}
+
+void remote_foreach_model(remote_foreach_t each, void *user_data)
+{
+	struct foreach_data data = {
+		.each = each,
+		.user_data = user_data
+	};
+
+	if (each)
+		l_queue_foreach(nodes, each_model, &data);
+}
+
 uint16_t remote_get_next_unicast(uint16_t low, uint16_t high, uint8_t ele_cnt)
 {
 	struct remote_node *rmt;
@@ -598,3 +708,15 @@ void remote_clear_rejected_addresses(uint32_t iv_index)
 
 	mesh_db_clear_rejected(iv_index);
 }
+
+uint8_t remote_ele_cnt(uint16_t unicast)
+{
+	struct remote_node *rmt;
+
+	rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(unicast));
+
+	if (rmt)
+		return rmt->num_ele;
+
+	return 0;
+}
diff --git a/tools/mesh/remote.h b/tools/mesh/remote.h
index 6645723..2a3947b 100644
--- a/tools/mesh/remote.h
+++ b/tools/mesh/remote.h
@@ -8,8 +8,13 @@
  *
  */
 
+typedef void (*remote_foreach_t)(void *user_data, uint16_t dst,
+								uint32_t model);
+
 bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
 					uint8_t ele_cnt, uint16_t net_idx);
+bool remote_reset_node(uint16_t original, uint16_t unicast, uint8_t ele_cnt,
+							uint32_t iv_index);
 uint8_t remote_del_node(uint16_t unicast);
 bool remote_set_model(uint16_t unicast, uint8_t ele_idx, uint32_t mod_id,
 								bool vendor);
@@ -30,3 +35,7 @@ bool remote_has_composition(uint16_t addr);
 uint16_t remote_get_subnet_idx(uint16_t addr);
 void remote_print_node(uint16_t addr);
 void remote_print_all(void);
+void remote_foreach(remote_foreach_t each, void *user_data);
+void remote_foreach_unicast(remote_foreach_t each, void *user_data);
+void remote_foreach_model(remote_foreach_t each, void *user_data);
+uint8_t remote_ele_cnt(uint16_t unicast);