[PATCH BlueZ v5 02/14] mesh: Add Remote Provisioning

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Brian Gix <brian.gix@xxxxxxxxx>

Add Remote Provisioning Server
Add Remote Provisioning Client
Remove local scanning/provisioning
Add delete-all dev key function
Add NPPI procedures
---
 Makefile.mesh           |   1 +
 mesh/cfgmod-server.c    |   2 +-
 mesh/keyring.c          |  28 +-
 mesh/keyring.h          |   1 +
 mesh/manager.c          | 535 +++++++++++++++++++-----
 mesh/mesh-config-json.c | 380 +++++++++++------
 mesh/mesh-config.h      |   6 +-
 mesh/model.c            |  27 +-
 mesh/node.c             | 255 +++++++++--
 mesh/node.h             |   3 +
 mesh/pb-adv.c           |   4 +-
 mesh/pb-adv.h           |   2 +-
 mesh/prov-acceptor.c    |  87 ++--
 mesh/prov-initiator.c   | 269 +++++++++++-
 mesh/prov.h             |   4 +-
 mesh/provision.h        |  23 +-
 mesh/remprv-server.c    | 907 ++++++++++++++++++++++++++++++++++++++++
 mesh/remprv.h           |  78 ++++
 18 files changed, 2245 insertions(+), 367 deletions(-)
 create mode 100644 mesh/remprv-server.c
 create mode 100644 mesh/remprv.h

diff --git a/Makefile.mesh b/Makefile.mesh
index 3047f362b..e18a169eb 100644
--- a/Makefile.mesh
+++ b/Makefile.mesh
@@ -26,6 +26,7 @@ mesh_sources = mesh/mesh.h mesh/mesh.c \
 				mesh/provision.h mesh/prov.h \
 				mesh/model.h mesh/model.c \
 				mesh/cfgmod.h mesh/cfgmod-server.c \
+				mesh/remprv.h mesh/remprv-server.c \
 				mesh/mesh-config.h mesh/mesh-config-json.c \
 				mesh/util.h mesh/util.c \
 				mesh/dbus.h mesh/dbus.c \
diff --git a/mesh/cfgmod-server.c b/mesh/cfgmod-server.c
index be90ef8c5..3d7efc44b 100644
--- a/mesh/cfgmod-server.c
+++ b/mesh/cfgmod-server.c
@@ -30,8 +30,8 @@
 		(SET_ID(SIG_VENDOR, l_get_le16(pkt))))
 
 /* Supported composition pages, sorted high to low */
-/* Only page 0 is currently supported */
 static const uint8_t supported_pages[] = {
+	128,
 	0
 };
 
diff --git a/mesh/keyring.c b/mesh/keyring.c
index 995a4b88f..1e1de3e54 100644
--- a/mesh/keyring.c
+++ b/mesh/keyring.c
@@ -30,9 +30,9 @@
 #include "mesh/node.h"
 #include "mesh/keyring.h"
 
-const char *dev_key_dir = "/dev_keys";
-const char *app_key_dir = "/app_keys";
-const char *net_key_dir = "/net_keys";
+static const char *dev_key_dir = "/dev_keys";
+static const char *app_key_dir = "/app_keys";
+static const char *net_key_dir = "/net_keys";
 
 static int open_key_file(struct mesh_node *node, const char *key_dir,
 							uint16_t idx, int flags)
@@ -371,6 +371,28 @@ bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast,
 	return true;
 }
 
+bool keyring_del_remote_dev_key_all(struct mesh_node *node, uint16_t unicast)
+{
+	uint8_t dev_key[16];
+	uint8_t test_key[16];
+	uint8_t cnt = 1;
+
+	if (!keyring_get_remote_dev_key(node, unicast, dev_key))
+		return false;
+
+	while (keyring_get_remote_dev_key(node, unicast + cnt, test_key)) {
+		if (memcmp(dev_key, test_key, sizeof(dev_key)))
+			break;
+
+		cnt++;
+	}
+
+	if (cnt > 1)
+		return keyring_del_remote_dev_key(node, unicast + 1, cnt - 1);
+
+	return true;
+}
+
 static DIR *open_key_dir(const char *node_path, const char *key_dir_name)
 {
 	char dir_path[PATH_MAX];
diff --git a/mesh/keyring.h b/mesh/keyring.h
index ecf62cbc1..efc499ac2 100644
--- a/mesh/keyring.h
+++ b/mesh/keyring.h
@@ -39,5 +39,6 @@ bool keyring_put_remote_dev_key(struct mesh_node *node, uint16_t unicast,
 					uint8_t count, uint8_t dev_key[16]);
 bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast,
 								uint8_t count);
+bool keyring_del_remote_dev_key_all(struct mesh_node *node, uint16_t unicast);
 bool keyring_build_export_keys_reply(struct mesh_node *node,
 					struct l_dbus_message_builder *builder);
diff --git a/mesh/manager.c b/mesh/manager.c
index e66b1a45b..e16dbc513 100644
--- a/mesh/manager.c
+++ b/mesh/manager.c
@@ -21,75 +21,137 @@
 #include "mesh/mesh.h"
 #include "mesh/mesh-io.h"
 #include "mesh/node.h"
+#include "mesh/model.h"
 #include "mesh/net.h"
 #include "mesh/keyring.h"
 #include "mesh/agent.h"
 #include "mesh/provision.h"
+#include "mesh/prov.h"
+#include "mesh/remprv.h"
 #include "mesh/manager.h"
 
-struct add_data{
+struct prov_remote_data {
 	struct l_dbus_message *msg;
 	struct mesh_agent *agent;
 	struct mesh_node *node;
 	uint32_t disc_watch;
+	uint16_t original;
 	uint16_t primary;
 	uint16_t net_idx;
+	uint8_t transport;
 	uint8_t num_ele;
 	uint8_t uuid[16];
 };
 
-static int8_t scan_rssi;
-static uint8_t scan_uuid[16];
-static struct mesh_node *scan_node;
-static struct l_timeout *scan_timeout;
-static struct add_data *add_pending;
+struct scan_req {
+	struct mesh_node *node;
+	struct l_timeout *timeout;
+	uint16_t server;
+	uint16_t net_idx;
+	uint8_t uuid[16];
+	int8_t rssi;
+	bool ext;
+};
+
+static struct l_queue *scans;
+static struct prov_remote_data *prov_pending;
 static const uint8_t prvb[2] = {MESH_AD_TYPE_BEACON, 0x00};
 
+static bool by_scan(const void *a, const void *b)
+{
+	return a == b;
+}
+
+static bool by_node(const void *a, const void *b)
+{
+	const struct scan_req *req = a;
+	const struct mesh_node *node = b;
+
+	return req->node == node;
+}
+
+static bool by_node_svr(const void *a, const void *b)
+{
+	const struct scan_req *req = a;
+	const struct scan_req *test = b;
+
+	return req->node == test->node && req->server == test->server;
+}
+
 static void scan_cancel(struct l_timeout *timeout, void *user_data)
 {
-	struct mesh_node *node = user_data;
+	struct scan_req *req = user_data;
 	struct mesh_io *io;
 	struct mesh_net *net;
+	uint8_t msg[4];
+	int n;
 
 	l_debug("");
 
-	if (scan_timeout)
-		l_timeout_remove(scan_timeout);
+	req = l_queue_remove_if(scans, by_scan, req);
+
+	if (!req)
+		return;
+
+	l_timeout_remove(req->timeout);
+
+	if (req->server) {
+		n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STOP, msg);
+		mesh_model_send(req->node, 0, req->server, APP_IDX_DEV_REMOTE,
+						req->net_idx, DEFAULT_TTL,
+						true, n, msg);
+	} else {
+		net = node_get_net(req->node);
+		io = mesh_net_get_io(net);
+		mesh_io_deregister_recv_cb(io, prvb, sizeof(prvb));
+	}
 
-	net = node_get_net(node);
-	io = mesh_net_get_io(net);
-	mesh_io_deregister_recv_cb(io, prvb, sizeof(prvb));
-	scan_node = NULL;
-	scan_timeout = NULL;
+	initiator_scan_unreg(req->node);
+	l_free(req);
 }
 
-static void free_pending_add_call()
+static void free_pending_add_call(void)
 {
-	if (!add_pending)
+	if (!prov_pending)
 		return;
 
-	if (add_pending->disc_watch)
+	if (prov_pending->disc_watch)
 		l_dbus_remove_watch(dbus_get_bus(),
-						add_pending->disc_watch);
+						prov_pending->disc_watch);
 
-	if (add_pending->msg)
-		l_dbus_message_unref(add_pending->msg);
+	if (prov_pending->msg)
+		l_dbus_message_unref(prov_pending->msg);
 
-	l_free(add_pending);
-	add_pending = NULL;
+	l_free(prov_pending);
+	prov_pending = NULL;
 }
 
 static void prov_disc_cb(struct l_dbus *bus, void *user_data)
 {
-	if (!add_pending)
+	if (!prov_pending)
 		return;
 
-	initiator_cancel(add_pending);
-	add_pending->disc_watch = 0;
+	initiator_cancel(prov_pending);
+	prov_pending->disc_watch = 0;
 
 	free_pending_add_call();
 }
 
+static void append_dict_entry_basic(struct l_dbus_message_builder *builder,
+					const char *key, const char *signature,
+					const void *data)
+{
+	if (!builder)
+		return;
+
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', key);
+	l_dbus_message_builder_enter_variant(builder, signature);
+	l_dbus_message_builder_append_basic(builder, signature[0], data);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+}
+
 static void send_add_failed(const char *owner, const char *path,
 							uint8_t status)
 {
@@ -102,7 +164,7 @@ static void send_add_failed(const char *owner, const char *path,
 						"AddNodeFailed");
 
 	builder = l_dbus_message_builder_new(msg);
-	dbus_append_byte_array(builder, add_pending->uuid, 16);
+	dbus_append_byte_array(builder, prov_pending->uuid, 16);
 	l_dbus_message_builder_append_basic(builder, 's',
 						mesh_prov_status_str(status));
 	l_dbus_message_builder_finalize(builder);
@@ -115,14 +177,14 @@ static void send_add_failed(const char *owner, const char *path,
 static bool add_cmplt(void *user_data, uint8_t status,
 					struct mesh_prov_node_info *info)
 {
-	struct add_data *pending = user_data;
+	struct prov_remote_data *pending = user_data;
 	struct mesh_node *node = pending->node;
 	struct l_dbus *dbus = dbus_get_bus();
 	struct l_dbus_message_builder *builder;
 	struct l_dbus_message *msg;
 	bool result;
 
-	if (pending != add_pending)
+	if (pending != prov_pending)
 		return false;
 
 	if (status != PROV_ERR_SUCCESS) {
@@ -131,7 +193,12 @@ static bool add_cmplt(void *user_data, uint8_t status,
 		return false;
 	}
 
-	result = keyring_put_remote_dev_key(add_pending->node, info->unicast,
+	/* If Unicast address changing, delete old dev key */
+	if (pending->transport == PB_NPPI_01)
+		keyring_del_remote_dev_key_all(pending->node,
+							pending->original);
+
+	result = keyring_put_remote_dev_key(pending->node, info->unicast,
 					info->num_ele, info->device_key);
 
 	if (!result) {
@@ -140,13 +207,29 @@ static bool add_cmplt(void *user_data, uint8_t status,
 		return false;
 	}
 
-	msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
+	if (pending->transport > PB_NPPI_02)
+		msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
 						node_get_app_path(node),
 						MESH_PROVISIONER_INTERFACE,
 						"AddNodeComplete");
+	else
+		msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
+						node_get_app_path(node),
+						MESH_PROVISIONER_INTERFACE,
+						"ReprovComplete");
 
 	builder = l_dbus_message_builder_new(msg);
-	dbus_append_byte_array(builder, add_pending->uuid, 16);
+
+	if (pending->transport > PB_NPPI_02)
+		dbus_append_byte_array(builder, pending->uuid, 16);
+	else {
+		uint8_t nppi = (uint8_t) pending->transport;
+
+		l_dbus_message_builder_append_basic(builder, 'q',
+							&pending->original);
+		l_dbus_message_builder_append_basic(builder, 'y', &nppi);
+	}
+
 	l_dbus_message_builder_append_basic(builder, 'q', &info->unicast);
 	l_dbus_message_builder_append_basic(builder, 'y', &info->num_ele);
 	l_dbus_message_builder_finalize(builder);
@@ -161,47 +244,66 @@ static bool add_cmplt(void *user_data, uint8_t status,
 
 static void mgr_prov_data (struct l_dbus_message *reply, void *user_data)
 {
-	struct add_data *pending = user_data;
+	struct prov_remote_data *pending = user_data;
 	uint16_t net_idx;
 	uint16_t primary;
 
-	if (pending != add_pending)
+	if (pending != prov_pending)
 		return;
 
 	if (l_dbus_message_is_error(reply))
 		return;
 
-	if (!l_dbus_message_get_arguments(reply, "qq", &net_idx, &primary))
+	if (pending->transport == PB_NPPI_01) {
+		/* If performing NPPI, we only get new primary unicast here */
+		if (!l_dbus_message_get_arguments(reply, "q", &primary))
+			return;
+
+		net_idx = pending->net_idx;
+
+	} else if (!l_dbus_message_get_arguments(reply, "qq", &net_idx,
+								&primary))
 		return;
 
-	add_pending->primary = primary;
-	add_pending->net_idx = net_idx;
-	initiator_prov_data(net_idx, primary, add_pending);
+	pending->primary = primary;
+	pending->net_idx = net_idx;
+	initiator_prov_data(net_idx, primary, pending);
 }
 
 static bool add_data_get(void *user_data, uint8_t num_ele)
 {
-	struct add_data *pending = user_data;
+	struct prov_remote_data *pending = user_data;
 	struct l_dbus_message *msg;
 	struct l_dbus *dbus;
 	const char *app_path;
 	const char *sender;
 
-	if (pending != add_pending)
+	if (pending != prov_pending)
 		return false;
 
 	dbus = dbus_get_bus();
-	app_path = node_get_app_path(add_pending->node);
-	sender = node_get_owner(add_pending->node);
+	app_path = node_get_app_path(pending->node);
+	sender = node_get_owner(pending->node);
 
-	msg = l_dbus_message_new_method_call(dbus, sender, app_path,
+	if (pending->transport > PB_NPPI_02) {
+		msg = l_dbus_message_new_method_call(dbus, sender, app_path,
 						MESH_PROVISIONER_INTERFACE,
 						"RequestProvData");
 
-	l_dbus_message_set_arguments(msg, "y", num_ele);
-	l_dbus_send_with_reply(dbus, msg, mgr_prov_data, add_pending, NULL);
+		l_dbus_message_set_arguments(msg, "y", num_ele);
+	} else if (pending->transport == PB_NPPI_01) {
+		msg = l_dbus_message_new_method_call(dbus, sender, app_path,
+						MESH_PROVISIONER_INTERFACE,
+						"RequestReprovData");
+
+		l_dbus_message_set_arguments(msg, "qy", pending->original,
+								num_ele);
+	} else
+		return false;
 
-	add_pending->num_ele = num_ele;
+	l_dbus_send_with_reply(dbus, msg, mgr_prov_data, pending, NULL);
+
+	pending->num_ele = num_ele;
 
 	return true;
 }
@@ -213,15 +315,95 @@ static void add_start(void *user_data, int err)
 	l_debug("Start callback");
 
 	if (err == MESH_ERROR_NONE)
-		reply = l_dbus_message_new_method_return(add_pending->msg);
+		reply = l_dbus_message_new_method_return(prov_pending->msg);
 	else
-		reply = dbus_error(add_pending->msg, MESH_ERROR_FAILED,
+		reply = dbus_error(prov_pending->msg, MESH_ERROR_FAILED,
 				"Failed to start provisioning initiator");
 
 	l_dbus_send(dbus_get_bus(), reply);
-	l_dbus_message_unref(add_pending->msg);
+	l_dbus_message_unref(prov_pending->msg);
+
+	prov_pending->msg = NULL;
+}
+
+static struct l_dbus_message *reprovision_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct mesh_node *node = user_data;
+	struct l_dbus_message_iter options, var;
+	struct l_dbus_message *reply;
+	struct mesh_net *net = node_get_net(node);
+	const char *key;
+	uint16_t subidx;
+	uint16_t server = 0;
+	uint8_t nppi = 0;
+
+	l_debug("Reprovision request");
+
+	if (!l_dbus_message_get_arguments(msg, "qa{sv}", &server, &options))
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+	if (!IS_UNICAST(server))
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad Unicast");
+
+	/* Default to nodes primary subnet index */
+	subidx = mesh_net_get_primary_idx(net);
+
+	/* Get Provisioning Options */
+	while (l_dbus_message_iter_next_entry(&options, &key, &var)) {
+		bool failed = true;
+
+		if (!strcmp(key, "NPPI")) {
+			if (l_dbus_message_iter_get_variant(&var, "y", &nppi)) {
+				if (nppi <= 2)
+					failed = false;
+			}
+		} else if (!strcmp(key, "Subnet")) {
+			if (l_dbus_message_iter_get_variant(&var, "q",
+								&subidx)) {
+				if (subidx <= MAX_KEY_IDX)
+					failed = false;
+			}
+		}
+
+		if (failed)
+			return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+							"Invalid options");
+	}
+
+	/* AddNode cancels all outstanding Scanning from node */
+	manager_scan_cancel(node);
 
-	add_pending->msg = NULL;
+	/* Invoke Prov Initiator */
+	prov_pending = l_new(struct prov_remote_data, 1);
+
+	prov_pending->transport = nppi;
+	prov_pending->node = node;
+	prov_pending->original = server;
+	prov_pending->agent = node_get_agent(node);
+
+	if (!node_is_provisioner(node) || (prov_pending->agent == NULL)) {
+		reply = dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED,
+							"Missing Interfaces");
+		goto fail;
+	}
+
+	prov_pending->msg = l_dbus_message_ref(msg);
+	initiator_start(prov_pending->transport, server, subidx, NULL, 99, 60,
+					prov_pending->agent, add_start,
+					add_data_get, add_cmplt, node,
+					prov_pending);
+
+	prov_pending->disc_watch = l_dbus_add_disconnect_watch(dbus,
+						node_get_owner(node),
+						prov_disc_cb, NULL, NULL);
+
+	return NULL;
+fail:
+	l_free(prov_pending);
+	prov_pending = NULL;
+	return reply;
 }
 
 static struct l_dbus_message *add_node_call(struct l_dbus *dbus,
@@ -229,55 +411,101 @@ static struct l_dbus_message *add_node_call(struct l_dbus *dbus,
 						void *user_data)
 {
 	struct mesh_node *node = user_data;
-	struct l_dbus_message_iter iter_uuid, options;
+	struct l_dbus_message_iter iter_uuid, options, var;
 	struct l_dbus_message *reply;
+	struct mesh_net *net = node_get_net(node);
+	const char *key;
 	uint8_t *uuid;
-	uint32_t n = 22;
+	uint32_t n = 0;
+	uint16_t subidx;
+	uint16_t sec = 60;
+	uint16_t server = 0;
 
 	l_debug("AddNode request");
 
 	if (!l_dbus_message_get_arguments(msg, "aya{sv}", &iter_uuid, &options))
 		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
 
-	if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n)
-								|| n != 16)
+	if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n) ||
+									n != 16)
 		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
 							"Bad device UUID");
 
-	/* Allow AddNode to cancel Scanning if from the same node */
-	if (scan_node) {
-		if (scan_node != node)
-			return dbus_error(msg, MESH_ERROR_BUSY, NULL);
+	/* Default to nodes primary subnet index */
+	subidx = mesh_net_get_primary_idx(net);
 
-		scan_cancel(NULL, node);
+	/* Get Provisioning Options */
+	while (l_dbus_message_iter_next_entry(&options, &key, &var)) {
+		bool failed = true;
+
+		if (!strcmp(key, "Seconds")) {
+			if (l_dbus_message_iter_get_variant(&var, "q", &sec))
+				failed = false;
+		} else if (!strcmp(key, "Server")) {
+			if (l_dbus_message_iter_get_variant(&var, "q",
+								&server)) {
+				if (server < 0x8000)
+					failed = false;
+			}
+		} else if (!strcmp(key, "Subnet")) {
+			if (l_dbus_message_iter_get_variant(&var, "q",
+								&subidx)) {
+				if (subidx <= MAX_KEY_IDX)
+					failed = false;
+			}
+		}
+
+		if (failed)
+			return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+							"Invalid options");
 	}
 
+	/* Device Key update/Composition update requires remote server */
+	if (!n && !server)
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+							"Invalid options");
+
+	/* If no server specified, use local */
+	if (!server)
+		server = node_get_primary(node);
+
+	/* AddNode cancels all outstanding Scanning from node */
+	manager_scan_cancel(node);
+
 	/* Invoke Prov Initiator */
-	add_pending = l_new(struct add_data, 1);
-	memcpy(add_pending->uuid, uuid, 16);
-	add_pending->node = node;
-	add_pending->agent = node_get_agent(node);
+	prov_pending = l_new(struct prov_remote_data, 1);
+
+	if (n)
+		memcpy(prov_pending->uuid, uuid, 16);
+	else
+		uuid = NULL;
 
-	if (!node_is_provisioner(node) || (add_pending->agent == NULL)) {
+	prov_pending->transport = PB_ADV;
+	prov_pending->node = node;
+	prov_pending->agent = node_get_agent(node);
+
+	if (!node_is_provisioner(node) || (prov_pending->agent == NULL)) {
 		l_debug("Provisioner: %d", node_is_provisioner(node));
-		l_debug("Agent: %p", add_pending->agent);
+		l_debug("Agent: %p", prov_pending->agent);
 		reply = dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED,
 							"Missing Interfaces");
 		goto fail;
 	}
 
-	add_pending->msg = l_dbus_message_ref(msg);
-	initiator_start(PB_ADV, uuid, 99, 60, add_pending->agent, add_start,
-				add_data_get, add_cmplt, node, add_pending);
+	prov_pending->msg = l_dbus_message_ref(msg);
+	initiator_start(PB_ADV, server, subidx, uuid, 99, sec,
+					prov_pending->agent, add_start,
+					add_data_get, add_cmplt, node,
+					prov_pending);
 
-	add_pending->disc_watch = l_dbus_add_disconnect_watch(dbus,
+	prov_pending->disc_watch = l_dbus_add_disconnect_watch(dbus,
 						node_get_owner(node),
 						prov_disc_cb, NULL, NULL);
 
 	return NULL;
 fail:
-	l_free(add_pending);
-	add_pending = NULL;
+	l_free(prov_pending);
+	prov_pending = NULL;
 	return reply;
 }
 
@@ -337,38 +565,50 @@ static struct l_dbus_message *delete_node_call(struct l_dbus *dbus,
 	return l_dbus_message_new_method_return(msg);
 }
 
-static void prov_beacon_recv(void *user_data, struct mesh_io_recv_info *info,
+static void manager_scan_result(void *user_data, uint16_t server, bool ext,
 					const uint8_t *data, uint16_t len)
 {
-	struct mesh_node *node = user_data;
+	struct scan_req node_svr = {
+		.node = user_data,
+		.server = server,
+	};
+	struct scan_req *req;
 	struct l_dbus_message_builder *builder;
 	struct l_dbus_message *msg;
 	struct l_dbus *dbus;
 	int16_t rssi;
 
-	if (scan_node != node || len < sizeof(scan_uuid) + 2 || data[1] != 0x00)
+	l_debug("scan_result %4.4x %p", server, user_data);
+	req = l_queue_find(scans, by_node_svr, &node_svr);
+	if (!req) {
+		l_debug("No scan_result req");
 		return;
+	}
 
-	if (!memcmp(data + 2, scan_uuid, sizeof(scan_uuid))) {
-		if (info->rssi <= scan_rssi)
+	/* Filter repeats with weaker signal */
+	if (!memcmp(data + 1, req->uuid, sizeof(req->uuid))) {
+		if (!ext && ((int8_t) data[0] <= req->rssi)) {
+			l_debug("Already Seen");
 			return;
+		}
 	}
 
-	memcpy(scan_uuid, data + 2, sizeof(scan_uuid));
-	scan_rssi = info->rssi;
-	rssi = info->rssi;
+	if (!ext && ((int8_t) data[0] > req->rssi))
+		req->rssi = (int8_t) data[0];
 
+	rssi = req->rssi;
+	memcpy(req->uuid, data + 1, sizeof(req->uuid));
 	dbus = dbus_get_bus();
-	msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
-						node_get_app_path(node),
+	msg = l_dbus_message_new_method_call(dbus, node_get_owner(req->node),
+						node_get_app_path(req->node),
 						MESH_PROVISIONER_INTERFACE,
 						"ScanResult");
 
 	builder = l_dbus_message_builder_new(msg);
 	l_dbus_message_builder_append_basic(builder, 'n', &rssi);
-	dbus_append_byte_array(builder, data + 2, len -2);
+	dbus_append_byte_array(builder, data + 1, len - 1);
 	l_dbus_message_builder_enter_array(builder, "{sv}");
-	/* TODO: populate with options when defined */
+	append_dict_entry_basic(builder, "Server", "q", &server);
 	l_dbus_message_builder_leave_array(builder);
 	l_dbus_message_builder_finalize(builder);
 	l_dbus_message_builder_destroy(builder);
@@ -380,27 +620,71 @@ static struct l_dbus_message *start_scan_call(struct l_dbus *dbus,
 						struct l_dbus_message *msg,
 						void *user_data)
 {
-	struct mesh_node *node = user_data;
-	uint16_t duration = 0;
-	struct mesh_io *io;
+	struct scan_req new_req = {
+		.node = user_data,
+		.server = 0,
+		.timeout = NULL,
+		.ext = false,
+	};
+	struct scan_req *req;
 	struct mesh_net *net;
+	uint8_t *uuid, *ext = NULL;
+	uint8_t scan_req[21];
+	int n;
+	uint32_t ext_len;
+	uint32_t flen = 0;
+	uint16_t sec = 60;
 	const char *key;
 	struct l_dbus_message_iter options, var;
 	const char *sender = l_dbus_message_get_sender(msg);
 
-	if (strcmp(sender, node_get_owner(node)))
+	if (strcmp(sender, node_get_owner(new_req.node)))
 		return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
 
 	if (!l_dbus_message_get_arguments(msg, "a{sv}", &options))
 		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
 
+	if (!node_is_provisioner(new_req.node))
+		return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+
+	net = node_get_net(new_req.node);
+	new_req.net_idx = mesh_net_get_primary_idx(net);
+	memset(new_req.uuid, 0, sizeof(new_req.uuid));
+
 	while (l_dbus_message_iter_next_entry(&options, &key, &var)) {
 		bool failed = true;
 
 		if (!strcmp(key, "Seconds")) {
-			if (l_dbus_message_iter_get_variant(&var, "q",
-							    &duration)) {
+			if (l_dbus_message_iter_get_variant(&var, "q", &sec))
 				failed = false;
+		} else if (!strcmp(key, "Subnet")) {
+			if (l_dbus_message_iter_get_variant(&var, "q",
+							&new_req.net_idx)) {
+				if (new_req.net_idx <= MAX_KEY_IDX)
+					failed = false;
+			}
+		} else if (!strcmp(key, "Server")) {
+			if (l_dbus_message_iter_get_variant(&var, "q",
+							&new_req.server)) {
+				if (new_req.server < 0x8000)
+					failed = false;
+			}
+		} else if (!strcmp(key, "Filter")) {
+			if (l_dbus_message_iter_get_variant(&var, "ay", &var)) {
+				if (l_dbus_message_iter_get_fixed_array(&var,
+								&uuid, &flen)) {
+					if (flen == 16) {
+						memcpy(new_req.uuid, uuid,
+									flen);
+						failed = false;
+					}
+				}
+			}
+		} else if (!strcmp(key, "Extended")) {
+			if (l_dbus_message_iter_get_variant(&var, "ay", &var)) {
+				if (l_dbus_message_iter_get_fixed_array(&var,
+								&ext, &ext_len))
+					failed = false;
 			}
 		}
 
@@ -409,27 +693,51 @@ static struct l_dbus_message *start_scan_call(struct l_dbus *dbus,
 							"Invalid options");
 	}
 
-	if (scan_node && scan_node != node)
-		return dbus_error(msg, MESH_ERROR_BUSY, NULL);
+	if (!scans)
+		scans = l_queue_new();
 
-	if (!node_is_provisioner(node))
-		return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+	if (new_req.server) {
+		if (!sec || sec > 60)
+			return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+							"Invalid options");
+	} else {
+		new_req.server = node_get_primary(new_req.node);
+		if (!sec || sec > 60)
+			sec = 60;
+	}
+
+	req = l_queue_remove_if(scans, by_node_svr, &new_req);
+
+	if (!req)
+		req = l_malloc(sizeof(new_req));
+
+	if (req->timeout) {
+		l_timeout_remove(req->timeout);
+		req->timeout = NULL;
+	}
+
+	*req = new_req;
+	req->rssi = -128;
+
+	if (sec)
+		req->timeout = l_timeout_create(sec, scan_cancel, req, NULL);
 
-	if (scan_timeout)
-		l_timeout_remove(scan_timeout);
 
-	memset(scan_uuid, 0, sizeof(scan_uuid));
-	scan_rssi = -128;
-	scan_timeout = NULL;
-	net = node_get_net(node);
-	io = mesh_net_get_io(net);
-	scan_node = node;
-	mesh_io_register_recv_cb(io, prvb, sizeof(prvb),
-						prov_beacon_recv, node);
+	n = mesh_model_opcode_set(OP_REM_PROV_SCAN_START, scan_req);
+	scan_req[n++] = 5;
+	scan_req[n++] = sec;
+	if (flen) {
+		memcpy(scan_req + n, req->uuid, flen);
+		n += flen;
+	}
+
+	mesh_model_send(req->node, 0, req->server, APP_IDX_DEV_REMOTE,
+						req->net_idx, DEFAULT_TTL,
+						true, n, scan_req);
 
-	if (duration)
-		scan_timeout = l_timeout_create(duration, scan_cancel,
-								node, NULL);
+	initiator_scan_reg(manager_scan_result, req->node);
+
+	l_queue_push_tail(scans, req);
 
 	return l_dbus_message_new_method_return(msg);
 }
@@ -444,12 +752,7 @@ static struct l_dbus_message *cancel_scan_call(struct l_dbus *dbus,
 	if (strcmp(sender, node_get_owner(node)) || !node_is_provisioner(node))
 		return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
 
-	if (scan_node) {
-		if (scan_node != node)
-			return dbus_error(msg, MESH_ERROR_BUSY, NULL);
-
-		scan_cancel(NULL, node);
-	}
+	manager_scan_cancel(node);
 
 	return l_dbus_message_new_method_return(msg);
 }
@@ -814,6 +1117,8 @@ static void setup_management_interface(struct l_dbus_interface *iface)
 						"aya{sv}", "uuid", "options");
 	l_dbus_interface_method(iface, "ImportRemoteNode", 0, import_node_call,
 				"", "qyay", "primary", "count", "dev_key");
+	l_dbus_interface_method(iface, "Reprovision", 0, reprovision_call,
+					"", "qa{sv}", "unicast", "options");
 	l_dbus_interface_method(iface, "DeleteRemoteNode", 0, delete_node_call,
 						"", "qy", "primary", "count");
 	l_dbus_interface_method(iface, "UnprovisionedScan", 0, start_scan_call,
@@ -849,7 +1154,7 @@ bool manager_dbus_init(struct l_dbus *bus)
 	if (!l_dbus_register_interface(bus, MESH_MANAGEMENT_INTERFACE,
 						setup_management_interface,
 						NULL, false)) {
-		l_info("Unable to register %s interface",
+		l_debug("Unable to register %s interface",
 						MESH_MANAGEMENT_INTERFACE);
 		return false;
 	}
@@ -859,8 +1164,8 @@ bool manager_dbus_init(struct l_dbus *bus)
 
 void manager_scan_cancel(struct mesh_node *node)
 {
-	if (scan_node != node)
-		return;
+	struct scan_req *req;
 
-	scan_cancel(NULL, node);
+	while ((req = l_queue_find(scans, by_node, node)))
+		scan_cancel(NULL, req);
 }
diff --git a/mesh/mesh-config-json.c b/mesh/mesh-config-json.c
index 7f46c8582..8f321a731 100644
--- a/mesh/mesh-config-json.c
+++ b/mesh/mesh-config-json.c
@@ -58,6 +58,33 @@ static const char *cfgnode_name = "/node.json";
 static const char *bak_ext = ".bak";
 static const char *tmp_ext = ".tmp";
 
+/* JSON key words */
+static const char *unicastAddress = "unicastAddress";
+static const char *deviceCan = "deviceCan";
+static const char *deviceKey = "deviceKey";
+static const char *defaultTTL = "defaultTTL";
+static const char *sequenceNumber = "sequenceNumber";
+static const char *netKeys = "netKeys";
+static const char *appKeys = "appKeys";
+static const char *elements = "elements";
+static const char *models = "models";
+static const char *modelId = "modelId";
+static const char *address = "address";
+static const char *bind = "bind";
+static const char *publish = "publish";
+static const char *subscribe = "subscribe";
+static const char *boundNetKey = "boundNetKey";
+static const char *keyRefresh = "keyRefresh";
+static const char *subEnabled = "subEnabled";
+static const char *pubEnabled = "pubEnabled";
+static const char *retransmit = "retransmit";
+
+/* Common JSON values */
+static const char *enabled = "enabled";
+static const char *disabled = "disabled";
+static const char *unsupported = "unsupported";
+
+
 static bool save_config(json_object *jnode, const char *fname)
 {
 	FILE *outfile;
@@ -134,14 +161,14 @@ static int get_element_index(json_object *jnode, uint16_t ele_addr)
 	uint16_t addr, num_ele;
 	char *str;
 
-	if (!json_object_object_get_ex(jnode, "unicastAddress", &jvalue))
+	if (!json_object_object_get_ex(jnode, unicastAddress, &jvalue))
 		return -1;
 
 	str = (char *)json_object_get_string(jvalue);
 	if (sscanf(str, "%04hx", &addr) != 1)
 		return -1;
 
-	if (!json_object_object_get_ex(jnode, "elements", &jelements))
+	if (!json_object_object_get_ex(jnode, elements, &jelements))
 		return -1;
 
 	num_ele = json_object_array_length(jelements);
@@ -160,14 +187,14 @@ static json_object *get_element_model(json_object *jnode, int ele_idx,
 	size_t len;
 	char buf[9];
 
-	if (!json_object_object_get_ex(jnode, "elements", &jelements))
+	if (!json_object_object_get_ex(jnode, elements, &jelements))
 		return NULL;
 
 	jelement = json_object_array_get_idx(jelements, ele_idx);
 	if (!jelement)
 		return NULL;
 
-	if (!json_object_object_get_ex(jelement, "models", &jmodels))
+	if (!json_object_object_get_ex(jelement, models, &jmodels))
 		return NULL;
 
 	num_mods = json_object_array_length(jmodels);
@@ -189,7 +216,7 @@ static json_object *get_element_model(json_object *jnode, int ele_idx,
 		char *str;
 
 		jmodel = json_object_array_get_idx(jmodels, i);
-		if (!json_object_object_get_ex(jmodel, "modelId", &jvalue))
+		if (!json_object_object_get_ex(jmodel, modelId, &jvalue))
 			return NULL;
 
 		str = (char *)json_object_get_string(jvalue);
@@ -298,7 +325,7 @@ static bool read_unicast_address(json_object *jobj, uint16_t *unicast)
 	json_object *jvalue;
 	char *str;
 
-	if (!json_object_object_get_ex(jobj, "unicastAddress", &jvalue))
+	if (!json_object_object_get_ex(jobj, unicastAddress, &jvalue))
 		return false;
 
 	str = (char *)json_object_get_string(jvalue);
@@ -314,7 +341,7 @@ static bool read_default_ttl(json_object *jobj, uint8_t *ttl)
 	int val;
 
 	/* defaultTTL is optional */
-	if (!json_object_object_get_ex(jobj, "defaultTTL", &jvalue))
+	if (!json_object_object_get_ex(jobj, defaultTTL, &jvalue))
 		return true;
 
 	val = json_object_get_int(jvalue);
@@ -336,7 +363,7 @@ static bool read_seq_number(json_object *jobj, uint32_t *seq_number)
 	int val;
 
 	/* sequenceNumber is optional */
-	if (!json_object_object_get_ex(jobj, "sequenceNumber", &jvalue))
+	if (!json_object_object_get_ex(jobj, sequenceNumber, &jvalue))
 		return true;
 
 	val = json_object_get_int(jvalue);
@@ -396,7 +423,25 @@ static bool read_device_key(json_object *jobj, uint8_t key_buf[16])
 	if (!key_buf)
 		return false;
 
-	if (!json_object_object_get_ex(jobj, "deviceKey", &jvalue))
+	if (!json_object_object_get_ex(jobj, deviceKey, &jvalue))
+		return false;
+
+	str = (char *)json_object_get_string(jvalue);
+	if (!str2hex(str, strlen(str), key_buf, 16))
+		return false;
+
+	return true;
+}
+
+static bool read_candidate(json_object *jobj, uint8_t key_buf[16])
+{
+	json_object *jvalue;
+	char *str;
+
+	if (!key_buf)
+		return false;
+
+	if (!json_object_object_get_ex(jobj, deviceCan, &jvalue))
 		return false;
 
 	str = (char *)json_object_get_string(jvalue);
@@ -460,7 +505,7 @@ static bool read_app_keys(json_object *jobj, struct mesh_config_node *node)
 	int len;
 	int i;
 
-	if (!json_object_object_get_ex(jobj, "appKeys", &jarray))
+	if (!json_object_object_get_ex(jobj, appKeys, &jarray))
 		return true;
 
 	if (json_object_get_type(jarray) != json_type_array)
@@ -484,7 +529,7 @@ static bool read_app_keys(json_object *jobj, struct mesh_config_node *node)
 		if (!get_key_index(jtemp, "index", &appkey->app_idx))
 			goto fail;
 
-		if (!get_key_index(jtemp, "boundNetKey", &appkey->net_idx))
+		if (!get_key_index(jtemp, boundNetKey, &appkey->net_idx))
 			goto fail;
 
 		if (!json_object_object_get_ex(jtemp, "key", &jvalue))
@@ -516,7 +561,7 @@ static bool read_net_keys(json_object *jobj, struct mesh_config_node *node)
 	int i;
 
 	/* At least one NetKey must be present for a provisioned node */
-	if (!json_object_object_get_ex(jobj, "netKeys", &jarray))
+	if (!json_object_object_get_ex(jobj, netKeys, &jarray))
 		return false;
 
 	if (json_object_get_type(jarray) != json_type_array)
@@ -547,7 +592,7 @@ static bool read_net_keys(json_object *jobj, struct mesh_config_node *node)
 		if (!str2hex(str, strlen(str), netkey->new_key, 16))
 			goto fail;
 
-		if (!json_object_object_get_ex(jtemp, "keyRefresh", &jvalue))
+		if (!json_object_object_get_ex(jtemp, keyRefresh, &jvalue))
 			netkey->phase = KEY_REFRESH_PHASE_NONE;
 		else
 			netkey->phase = (uint8_t) json_object_get_int(jvalue);
@@ -598,7 +643,7 @@ bool mesh_config_net_key_add(struct mesh_config *cfg, uint16_t idx,
 	jnode = cfg->jnode;
 
 	l_debug("netKey %4.4x", idx);
-	json_object_object_get_ex(jnode, "netKeys", &jarray);
+	json_object_object_get_ex(jnode, netKeys, &jarray);
 	if (jarray)
 		jentry = get_key_object(jarray, idx);
 
@@ -616,14 +661,14 @@ bool mesh_config_net_key_add(struct mesh_config *cfg, uint16_t idx,
 	if (!add_key_value(jentry, "key", key))
 		goto fail;
 
-	json_object_object_add(jentry, "keyRefresh",
+	json_object_object_add(jentry, keyRefresh,
 				json_object_new_int(KEY_REFRESH_PHASE_NONE));
 
 	if (!jarray) {
 		jarray = json_object_new_array();
 		if (!jarray)
 			goto fail;
-		json_object_object_add(jnode, "netKeys", jarray);
+		json_object_object_add(jnode, netKeys, jarray);
 	}
 
 	json_object_array_add(jarray, jentry);
@@ -648,7 +693,7 @@ bool mesh_config_net_key_update(struct mesh_config *cfg, uint16_t idx,
 
 	jnode = cfg->jnode;
 
-	if (!json_object_object_get_ex(jnode, "netKeys", &jarray))
+	if (!json_object_object_get_ex(jnode, netKeys, &jarray))
 		return false;
 
 	jentry = get_key_object(jarray, idx);
@@ -667,7 +712,7 @@ bool mesh_config_net_key_update(struct mesh_config *cfg, uint16_t idx,
 	if (!add_key_value(jentry, "key", key))
 		return false;
 
-	json_object_object_add(jentry, "keyRefresh",
+	json_object_object_add(jentry, keyRefresh,
 				json_object_new_int(KEY_REFRESH_PHASE_ONE));
 
 	return save_config(jnode, cfg->node_dir_path);
@@ -682,20 +727,55 @@ bool mesh_config_net_key_del(struct mesh_config *cfg, uint16_t idx)
 
 	jnode = cfg->jnode;
 
-	if (!json_object_object_get_ex(jnode, "netKeys", &jarray))
+	if (!json_object_object_get_ex(jnode, netKeys, &jarray))
 		return true;
 
 	jarray_key_del(jarray, idx);
 
 	if (!json_object_array_length(jarray))
-		json_object_object_del(jnode, "netKeys");
+		json_object_object_del(jnode, netKeys);
 
 	return save_config(jnode, cfg->node_dir_path);
 }
 
 bool mesh_config_write_device_key(struct mesh_config *cfg, uint8_t *key)
 {
-	if (!cfg || !add_key_value(cfg->jnode, "deviceKey", key))
+	if (!cfg || !add_key_value(cfg->jnode, deviceKey, key))
+		return false;
+
+	return save_config(cfg->jnode, cfg->node_dir_path);
+}
+
+bool mesh_config_write_candidate(struct mesh_config *cfg, uint8_t *key)
+{
+	if (!cfg || !add_key_value(cfg->jnode, deviceCan, key))
+		return false;
+
+	return save_config(cfg->jnode, cfg->node_dir_path);
+}
+
+bool mesh_config_read_candidate(struct mesh_config *cfg, uint8_t *key)
+{
+	if (!cfg)
+		return false;
+
+	return read_candidate(cfg->jnode, key);
+}
+
+bool mesh_config_finalize_candidate(struct mesh_config *cfg)
+{
+	uint8_t key[16];
+
+	if (!cfg)
+		return false;
+
+	if (!read_candidate(cfg->jnode, key))
+		return false;
+
+	json_object_object_del(cfg->jnode, deviceCan);
+	json_object_object_del(cfg->jnode, deviceKey);
+
+	if (!add_key_value(cfg->jnode, deviceKey, key))
 		return false;
 
 	return save_config(cfg->jnode, cfg->node_dir_path);
@@ -719,7 +799,7 @@ bool mesh_config_app_key_add(struct mesh_config *cfg, uint16_t net_idx,
 
 	jnode = cfg->jnode;
 
-	json_object_object_get_ex(jnode, "appKeys", &jarray);
+	json_object_object_get_ex(jnode, appKeys, &jarray);
 	if (jarray)
 		jentry = get_key_object(jarray, app_idx);
 
@@ -734,7 +814,7 @@ bool mesh_config_app_key_add(struct mesh_config *cfg, uint16_t net_idx,
 	if (!write_int(jentry, "index", app_idx))
 		goto fail;
 
-	if (!write_int(jentry, "boundNetKey", net_idx))
+	if (!write_int(jentry, boundNetKey, net_idx))
 		goto fail;
 
 	if (!add_key_value(jentry, "key", key))
@@ -744,7 +824,7 @@ bool mesh_config_app_key_add(struct mesh_config *cfg, uint16_t net_idx,
 		jarray = json_object_new_array();
 		if (!jarray)
 			goto fail;
-		json_object_object_add(jnode, "appKeys", jarray);
+		json_object_object_add(jnode, appKeys, jarray);
 	}
 
 	json_object_array_add(jarray, jentry);
@@ -770,7 +850,7 @@ bool mesh_config_app_key_update(struct mesh_config *cfg, uint16_t app_idx,
 
 	jnode = cfg->jnode;
 
-	if (!json_object_object_get_ex(jnode, "appKeys", &jarray))
+	if (!json_object_object_get_ex(jnode, appKeys, &jarray))
 		return false;
 
 	/* The key entry should exist if the key is updated */
@@ -804,13 +884,13 @@ bool mesh_config_app_key_del(struct mesh_config *cfg, uint16_t net_idx,
 
 	jnode = cfg->jnode;
 
-	if (!json_object_object_get_ex(jnode, "appKeys", &jarray))
+	if (!json_object_object_get_ex(jnode, appKeys, &jarray))
 		return true;
 
 	jarray_key_del(jarray, idx);
 
 	if (!json_object_array_length(jarray))
-		json_object_object_del(jnode, "appKeys");
+		json_object_object_del(jnode, appKeys);
 
 	return save_config(jnode, cfg->node_dir_path);
 }
@@ -840,7 +920,7 @@ bool mesh_config_model_binding_add(struct mesh_config *cfg, uint16_t ele_addr,
 	if (!jmodel)
 		return false;
 
-	json_object_object_get_ex(jmodel, "bind", &jarray);
+	json_object_object_get_ex(jmodel, bind, &jarray);
 	if (jarray && jarray_has_string(jarray, buf, 4))
 		return true;
 
@@ -854,7 +934,7 @@ bool mesh_config_model_binding_add(struct mesh_config *cfg, uint16_t ele_addr,
 			json_object_put(jstring);
 			return false;
 		}
-		json_object_object_add(jmodel, "bind", jarray);
+		json_object_object_add(jmodel, bind, jarray);
 	}
 
 	json_object_array_add(jarray, jstring);
@@ -887,13 +967,13 @@ bool mesh_config_model_binding_del(struct mesh_config *cfg, uint16_t ele_addr,
 	if (!jmodel)
 		return false;
 
-	if (!json_object_object_get_ex(jmodel, "bind", &jarray))
+	if (!json_object_object_get_ex(jmodel, bind, &jarray))
 		return true;
 
 	jarray_string_del(jarray, buf, 4);
 
 	if (!json_object_array_length(jarray))
-		json_object_object_del(jmodel, "bind");
+		json_object_object_del(jmodel, bind);
 
 	return save_config(jnode, cfg->node_dir_path);
 }
@@ -963,7 +1043,7 @@ static struct mesh_config_pub *parse_model_publication(json_object *jpub)
 	int len, value;
 	char *str;
 
-	if (!json_object_object_get_ex(jpub, "address", &jvalue))
+	if (!json_object_object_get_ex(jpub, address, &jvalue))
 		return NULL;
 
 	str = (char *)json_object_get_string(jvalue);
@@ -998,9 +1078,10 @@ static struct mesh_config_pub *parse_model_publication(json_object *jpub)
 
 	if (!get_int(jpub, "credentials", &value))
 		goto fail;
+
 	pub->credential = (uint8_t) value;
 
-	if (!json_object_object_get_ex(jpub, "retransmit", &jvalue))
+	if (!json_object_object_get_ex(jpub, retransmit, &jvalue))
 		goto fail;
 
 	if (!get_int(jvalue, "count", &value))
@@ -1093,7 +1174,7 @@ static bool parse_models(json_object *jmodels, struct mesh_config_element *ele)
 
 		l_queue_push_tail(ele->models, mod);
 
-		if (!json_object_object_get_ex(jmodel, "modelId", &jvalue))
+		if (!json_object_object_get_ex(jmodel, modelId, &jvalue))
 			goto fail;
 
 		str = (char *)json_object_get_string(jvalue);
@@ -1112,29 +1193,32 @@ static bool parse_models(json_object *jmodels, struct mesh_config_element *ele)
 
 		mod->id = id;
 
-		if (json_object_object_get_ex(jmodel, "bind", &jarray)) {
+		if (len == 8)
+			mod->vendor = true;
+
+		if (json_object_object_get_ex(jmodel, bind, &jarray)) {
 			if (json_object_get_type(jarray) != json_type_array ||
 					!parse_bindings(jarray, mod))
 				goto fail;
 		}
 
-		if (json_object_object_get_ex(jmodel, "pubEnabled", &jvalue))
+		if (json_object_object_get_ex(jmodel, pubEnabled, &jvalue))
 			mod->pub_enabled = json_object_get_boolean(jvalue);
 		else
 			mod->pub_enabled = true;
 
-		if (json_object_object_get_ex(jmodel, "subEnabled", &jvalue))
+		if (json_object_object_get_ex(jmodel, subEnabled, &jvalue))
 			mod->sub_enabled = json_object_get_boolean(jvalue);
 		else
 			mod->sub_enabled = true;
 
-		if (json_object_object_get_ex(jmodel, "publish", &jvalue)) {
+		if (json_object_object_get_ex(jmodel, publish, &jvalue)) {
 			mod->pub = parse_model_publication(jvalue);
 			if (!mod->pub)
 				goto fail;
 		}
 
-		if (json_object_object_get_ex(jmodel, "subscribe", &jarray)) {
+		if (json_object_object_get_ex(jmodel, subscribe, &jarray)) {
 			if (!parse_model_subscriptions(jarray, mod))
 				goto fail;
 		}
@@ -1187,7 +1271,7 @@ static bool parse_elements(json_object *jelems, struct mesh_config_node *node)
 		if (sscanf(str, "%04hx", &(ele->location)) != 1)
 			goto fail;
 
-		if (json_object_object_get_ex(jelement, "models", &jmodels)) {
+		if (json_object_object_get_ex(jelement, models, &jmodels)) {
 			if (json_object_get_type(jmodels) != json_type_array ||
 						!parse_models(jmodels, ele))
 				goto fail;
@@ -1211,13 +1295,13 @@ static int get_mode(json_object *jvalue)
 	if (!str)
 		return 0xffffffff;
 
-	if (!strncasecmp(str, "disabled", strlen("disabled")))
+	if (!strncasecmp(str, disabled, strlen(disabled)))
 		return MESH_MODE_DISABLED;
 
-	if (!strncasecmp(str, "enabled", strlen("enabled")))
+	if (!strncasecmp(str, enabled, strlen(enabled)))
 		return MESH_MODE_ENABLED;
 
-	if (!strncasecmp(str, "unsupported", strlen("unsupported")))
+	if (!strncasecmp(str, unsupported, strlen(unsupported)))
 		return MESH_MODE_UNSUPPORTED;
 
 	return 0xffffffff;
@@ -1323,7 +1407,7 @@ static bool read_net_transmit(json_object *jobj, struct mesh_config_node *node)
 	uint16_t interval;
 	uint8_t cnt;
 
-	if (!json_object_object_get_ex(jobj, "retransmit", &jrtx))
+	if (!json_object_object_get_ex(jobj, retransmit, &jrtx))
 		return true;
 
 	if (!json_object_object_get_ex(jrtx, "count", &jvalue))
@@ -1386,7 +1470,7 @@ static bool read_node(json_object *jnode, struct mesh_config_node *node)
 	}
 
 	/* Check for required "elements" property */
-	if (!json_object_object_get_ex(jnode, "elements", &jvalue))
+	if (!json_object_object_get_ex(jnode, elements, &jvalue))
 		return false;
 
 	if (!read_net_transmit(jnode, node)) {
@@ -1460,11 +1544,11 @@ static const char *mode_to_string(int mode)
 {
 	switch (mode) {
 	case MESH_MODE_DISABLED:
-		return "disabled";
+		return disabled;
 	case MESH_MODE_ENABLED:
-		return "enabled";
+		return enabled;
 	default:
-		return "unsupported";
+		return unsupported;
 	}
 }
 
@@ -1522,7 +1606,7 @@ fail:
 
 bool mesh_config_write_unicast(struct mesh_config *cfg, uint16_t unicast)
 {
-	if (!cfg || !write_uint16_hex(cfg->jnode, "unicastAddress", unicast))
+	if (!cfg || !write_uint16_hex(cfg->jnode, unicastAddress, unicast))
 		return false;
 
 	return save_config(cfg->jnode, cfg->node_dir_path);
@@ -1558,8 +1642,8 @@ bool mesh_config_write_net_transmit(struct mesh_config *cfg, uint8_t cnt,
 	if (!write_int(jrtx, "interval", interval))
 		goto fail;
 
-	json_object_object_del(jnode, "retransmit");
-	json_object_object_add(jnode, "retransmit", jrtx);
+	json_object_object_del(jnode, retransmit);
+	json_object_object_add(jnode, retransmit, jrtx);
 
 	return save_config(cfg->jnode, cfg->node_dir_path);
 
@@ -1599,8 +1683,8 @@ static void add_model(void *a, void *b)
 	if (!jmodel)
 		return;
 
-	result = (mod->vendor) ? write_uint32_hex(jmodel, "modelId", mod->id) :
-			write_uint16_hex(jmodel, "modelId", (uint16_t) mod->id);
+	result = (mod->vendor) ? write_uint32_hex(jmodel, modelId, mod->id) :
+			write_uint16_hex(jmodel, modelId, (uint16_t) mod->id);
 
 	if (!result) {
 		json_object_put(jmodel);
@@ -1608,10 +1692,10 @@ static void add_model(void *a, void *b)
 	}
 
 	jval = json_object_new_boolean(mod->sub_enabled);
-	json_object_object_add(jmodel, "subEnabled", jval);
+	json_object_object_add(jmodel, subEnabled, jval);
 
 	jval = json_object_new_boolean(mod->pub_enabled);
-	json_object_object_add(jmodel, "pubEnabled", jval);
+	json_object_object_add(jmodel, pubEnabled, jval);
 
 	json_object_array_add(jmodels, jmodel);
 }
@@ -1663,11 +1747,11 @@ static struct mesh_config *create_config(const char *cfg_path,
 		return NULL;
 
 	/* Sequence number */
-	json_object_object_add(jnode, "sequenceNumber",
+	json_object_object_add(jnode, sequenceNumber,
 					json_object_new_int(node->seq_number));
 
 	/* Default TTL */
-	json_object_object_add(jnode, "defaultTTL",
+	json_object_object_add(jnode, defaultTTL,
 						json_object_new_int(node->ttl));
 
 	/* Elements */
@@ -1702,11 +1786,11 @@ static struct mesh_config *create_config(const char *cfg_path,
 		if (!jmodels)
 			goto fail;
 
-		json_object_object_add(jelement, "models", jmodels);
+		json_object_object_add(jelement, models, jmodels);
 		l_queue_foreach(ele->models, add_model, jmodels);
 	}
 
-	json_object_object_add(jnode, "elements", jelems);
+	json_object_object_add(jnode, elements, jelems);
 
 	cfg = l_new(struct mesh_config, 1);
 
@@ -1724,6 +1808,55 @@ fail:
 		return NULL;
 }
 
+void mesh_config_reset(struct mesh_config *cfg, struct mesh_config_node *node)
+{
+	json_object *jelems;
+	const struct l_queue_entry *entry;
+
+	if (!cfg || !cfg->jnode)
+		return;
+
+	/* TODO: Recreate Element Array */
+	jelems = json_object_new_array();
+	if (!jelems)
+		return;
+
+	entry = l_queue_get_entries(node->elements);
+
+	for (; entry; entry = entry->next) {
+		struct mesh_config_element *ele = entry->data;
+		json_object *jelement, *jmodels;
+
+		jelement = json_object_new_object();
+
+		if (!jelement) {
+			json_object_put(jelems);
+			return;
+		}
+
+		write_int(jelement, "elementIndex", ele->index);
+		write_uint16_hex(jelement, "location", ele->location);
+		json_object_array_add(jelems, jelement);
+
+		/* Models */
+		if (l_queue_isempty(ele->models))
+			continue;
+
+		jmodels = json_object_new_array();
+		if (!jmodels) {
+			json_object_put(jelems);
+			return;
+		}
+
+		json_object_object_add(jelement, models, jmodels);
+		l_queue_foreach(ele->models, add_model, jmodels);
+	}
+
+	/* Replace element array */
+	json_object_object_del(cfg->jnode, elements);
+	json_object_object_add(cfg->jnode, elements, jelems);
+}
+
 struct mesh_config *mesh_config_create(const char *cfgdir_name,
 		const uint8_t uuid[16], struct mesh_config_node *db_node)
 {
@@ -1768,7 +1901,7 @@ static void finish_key_refresh(json_object *jobj, uint16_t net_idx)
 	int i, len;
 
 	/* Clean up all the bound appkeys */
-	if (!json_object_object_get_ex(jobj, "appKeys", &jarray))
+	if (!json_object_object_get_ex(jobj, appKeys, &jarray))
 		return;
 
 	len = json_object_array_length(jarray);
@@ -1779,7 +1912,7 @@ static void finish_key_refresh(json_object *jobj, uint16_t net_idx)
 
 		jentry = json_object_array_get_idx(jarray, i);
 
-		if (!get_key_index(jentry, "boundNetKey", &idx))
+		if (!get_key_index(jentry, boundNetKey, &idx))
 			continue;
 
 		if (idx != net_idx)
@@ -1803,14 +1936,14 @@ bool mesh_config_net_key_set_phase(struct mesh_config *cfg, uint16_t idx,
 
 	jnode = cfg->jnode;
 
-	if (json_object_object_get_ex(jnode, "netKeys", &jarray))
+	if (json_object_object_get_ex(jnode, netKeys, &jarray))
 		jentry = get_key_object(jarray, idx);
 
 	if (!jentry)
 		return false;
 
-	json_object_object_del(jentry, "keyRefresh");
-	json_object_object_add(jentry, "keyRefresh",
+	json_object_object_del(jentry, keyRefresh);
+	json_object_object_add(jentry, keyRefresh,
 					json_object_new_int(phase));
 
 	if (phase == KEY_REFRESH_PHASE_NONE) {
@@ -1842,16 +1975,16 @@ bool mesh_config_model_pub_add(struct mesh_config *cfg, uint16_t ele_addr,
 	if (!jmodel)
 		return false;
 
-	json_object_object_del(jmodel, "publish");
+	json_object_object_del(jmodel, publish);
 
 	jpub = json_object_new_object();
 	if (!jpub)
 		return false;
 
 	if (pub->virt)
-		res = add_key_value(jpub, "address", pub->virt_addr);
+		res = add_key_value(jpub, address, pub->virt_addr);
 	else
-		res = write_uint16_hex(jpub, "address", pub->addr);
+		res = write_uint16_hex(jpub, address, pub->addr);
 
 	if (!res)
 		goto fail;
@@ -1878,8 +2011,8 @@ bool mesh_config_model_pub_add(struct mesh_config *cfg, uint16_t ele_addr,
 	if (!write_int(jrtx, "interval", pub->interval))
 		goto fail;
 
-	json_object_object_add(jpub, "retransmit", jrtx);
-	json_object_object_add(jmodel, "publish", jpub);
+	json_object_object_add(jpub, retransmit, jrtx);
+	json_object_object_add(jmodel, publish, jpub);
 
 	return save_config(jnode, cfg->node_dir_path);
 
@@ -1911,23 +2044,23 @@ bool mesh_config_model_pub_del(struct mesh_config *cfg, uint16_t addr,
 						uint32_t mod_id, bool vendor)
 {
 	if (!cfg || !delete_model_property(cfg->jnode, addr, mod_id, vendor,
-								"publish"))
+								publish))
 		return false;
 
 	return save_config(cfg->jnode, cfg->node_dir_path);
 }
 
-static void del_page(json_object *jarray, uint8_t page)
+static bool del_page(json_object *jarray, uint8_t page)
 {
 	char buf[3];
 	int i, len, ret;
 
 	if (!jarray)
-		return;
+		return false;
 
 	ret = snprintf(buf, 3, "%2.2x", page);
 	if (ret < 0)
-		return;
+		return false;
 
 	len = json_object_array_length(jarray);
 
@@ -1938,10 +2071,29 @@ static void del_page(json_object *jarray, uint8_t page)
 		jentry = json_object_array_get_idx(jarray, i);
 		str = (char *)json_object_get_string(jentry);
 
-		/* Delete matching page(s) */
-		if (!memcmp(str, buf, 2))
+		/* Delete matching page */
+		if (!memcmp(str, buf, 2)) {
 			json_object_array_del_idx(jarray, i, 1);
+			break;
+		}
 	}
+
+	return true;
+}
+
+void mesh_config_comp_page_del(struct mesh_config *cfg, uint8_t page)
+{
+	json_object *jnode, *jarray = NULL;
+
+	if (!cfg)
+		return;
+
+	jnode = cfg->jnode;
+
+	json_object_object_get_ex(jnode, "pages", &jarray);
+
+	if (del_page(jarray, page))
+		save_config(jnode, cfg->node_dir_path);
 }
 
 bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page,
@@ -1984,56 +2136,6 @@ bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page,
 	return save_config(jnode, cfg->node_dir_path);
 }
 
-bool mesh_config_comp_page_mv(struct mesh_config *cfg, uint8_t old, uint8_t nw)
-{
-	json_object *jnode, *jarray = NULL;
-	uint8_t *data;
-	char *str;
-	char old_buf[3];
-	int i, len, ret, dlen = 0;
-	bool status = true;
-
-	if (!cfg || old == nw)
-		return false;
-
-	ret = snprintf(old_buf, 3, "%2.2x", old);
-	if (ret < 0)
-		return false;
-
-	jnode = cfg->jnode;
-
-	json_object_object_get_ex(jnode, "pages", &jarray);
-
-	if (!jarray)
-		return false;
-
-	data = l_malloc(MAX_MSG_LEN);
-
-	len = json_object_array_length(jarray);
-
-	for (i = 0; i < len; i++) {
-		json_object *jentry;
-
-		jentry = json_object_array_get_idx(jarray, i);
-		str = (char *)json_object_get_string(jentry);
-
-		/* Delete matching page(s) but save data*/
-		if (!memcmp(str, old_buf, 2)) {
-			dlen = strlen(str + 2);
-			str2hex(str + 2, dlen, data, MAX_MSG_LEN);
-			dlen /= 2;
-			json_object_array_del_idx(jarray, i, 1);
-		}
-	}
-
-	if (dlen)
-		status = mesh_config_comp_page_add(cfg, nw, data, dlen);
-
-	l_free(data);
-
-	return status;
-}
-
 bool mesh_config_model_sub_add(struct mesh_config *cfg, uint16_t ele_addr,
 						uint32_t mod_id, bool vendor,
 						struct mesh_config_sub *sub)
@@ -2064,7 +2166,7 @@ bool mesh_config_model_sub_add(struct mesh_config *cfg, uint16_t ele_addr,
 		len = 32;
 	}
 
-	json_object_object_get_ex(jmodel, "subscribe", &jarray);
+	json_object_object_get_ex(jmodel, subscribe, &jarray);
 	if (jarray && jarray_has_string(jarray, buf, len))
 		return true;
 
@@ -2078,7 +2180,7 @@ bool mesh_config_model_sub_add(struct mesh_config *cfg, uint16_t ele_addr,
 			json_object_put(jstring);
 			return false;
 		}
-		json_object_object_add(jmodel, "subscribe", jarray);
+		json_object_object_add(jmodel, subscribe, jarray);
 	}
 
 	json_object_array_add(jarray, jstring);
@@ -2107,7 +2209,7 @@ bool mesh_config_model_sub_del(struct mesh_config *cfg, uint16_t ele_addr,
 	if (!jmodel)
 		return false;
 
-	if (!json_object_object_get_ex(jmodel, "subscribe", &jarray))
+	if (!json_object_object_get_ex(jmodel, subscribe, &jarray))
 		return true;
 
 	if (!sub->virt) {
@@ -2122,7 +2224,7 @@ bool mesh_config_model_sub_del(struct mesh_config *cfg, uint16_t ele_addr,
 	jarray_string_del(jarray, buf, len);
 
 	if (!json_object_array_length(jarray))
-		json_object_object_del(jmodel, "subscribe");
+		json_object_object_del(jmodel, subscribe);
 
 	return save_config(jnode, cfg->node_dir_path);
 }
@@ -2131,7 +2233,7 @@ bool mesh_config_model_sub_del_all(struct mesh_config *cfg, uint16_t addr,
 						uint32_t mod_id, bool vendor)
 {
 	if (!cfg || !delete_model_property(cfg->jnode, addr, mod_id, vendor,
-								"subscribe"))
+								subscribe))
 		return false;
 
 	return save_config(cfg->jnode, cfg->node_dir_path);
@@ -2161,7 +2263,7 @@ bool mesh_config_model_pub_enable(struct mesh_config *cfg, uint16_t ele_addr,
 	json_object_object_add(jmodel, "pubDisabled", jval);
 
 	if (!enable)
-		json_object_object_del(jmodel, "publish");
+		json_object_object_del(jmodel, publish);
 
 	return save_config(cfg->jnode, cfg->node_dir_path);
 }
@@ -2184,13 +2286,13 @@ bool mesh_config_model_sub_enable(struct mesh_config *cfg, uint16_t ele_addr,
 	if (!jmodel)
 		return false;
 
-	json_object_object_del(jmodel, "subEnabled");
+	json_object_object_del(jmodel, subEnabled);
 
 	jval = json_object_new_boolean(enable);
-	json_object_object_add(jmodel, "subEnabled", jval);
+	json_object_object_add(jmodel, subEnabled, jval);
 
 	if (!enable)
-		json_object_object_del(jmodel, "subscribe");
+		json_object_object_del(jmodel, subscribe);
 
 	return save_config(cfg->jnode, cfg->node_dir_path);
 }
@@ -2205,14 +2307,14 @@ bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq,
 		return false;
 
 	if (!cache) {
-		if (!write_int(cfg->jnode, "sequenceNumber", seq))
+		if (!write_int(cfg->jnode, sequenceNumber, seq))
 			return false;
 
 		return mesh_config_save(cfg, true, NULL, NULL);
 	}
 
 	/* If resetting seq to Zero, make sure cached value reset as well */
-	if (seq && get_int(cfg->jnode, "sequenceNumber", &value))
+	if (seq && get_int(cfg->jnode, sequenceNumber, &value))
 		cached = (uint32_t)value;
 
 	/*
@@ -2262,8 +2364,8 @@ bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq,
 
 		l_debug("Seq Cache: %d -> %d", seq, cached);
 
-		if (!write_int(cfg->jnode, "sequenceNumber", cached))
-		    return false;
+		if (!write_int(cfg->jnode, sequenceNumber, cached))
+			return false;
 
 		return mesh_config_save(cfg, false, NULL, NULL);
 	}
@@ -2273,7 +2375,7 @@ bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq,
 
 bool mesh_config_write_ttl(struct mesh_config *cfg, uint8_t ttl)
 {
-	if (!cfg || !write_int(cfg->jnode, "defaultTTL", ttl))
+	if (!cfg || !write_int(cfg->jnode, defaultTTL, ttl))
 		return false;
 
 	return save_config(cfg->jnode, cfg->node_dir_path);
diff --git a/mesh/mesh-config.h b/mesh/mesh-config.h
index 420775829..ed1b610de 100644
--- a/mesh/mesh-config.h
+++ b/mesh/mesh-config.h
@@ -119,6 +119,7 @@ void mesh_config_release(struct mesh_config *cfg);
 void mesh_config_destroy_nvm(struct mesh_config *cfg);
 bool mesh_config_save(struct mesh_config *cfg, bool no_wait,
 				mesh_config_status_func_t cb, void *user_data);
+void mesh_config_reset(struct mesh_config *cfg, struct mesh_config_node *node);
 struct mesh_config *mesh_config_create(const char *cfgdir_name,
 						const uint8_t uuid[16],
 						struct mesh_config_node *node);
@@ -126,6 +127,9 @@ struct mesh_config *mesh_config_create(const char *cfgdir_name,
 bool mesh_config_write_net_transmit(struct mesh_config *cfg, uint8_t cnt,
 							uint16_t interval);
 bool mesh_config_write_device_key(struct mesh_config *cfg, uint8_t *key);
+bool mesh_config_write_candidate(struct mesh_config *cfg, uint8_t *key);
+bool mesh_config_read_candidate(struct mesh_config *cfg, uint8_t *key);
+bool mesh_config_finalize_candidate(struct mesh_config *cfg);
 bool mesh_config_write_token(struct mesh_config *cfg, uint8_t *token);
 bool mesh_config_write_network_key(struct mesh_config *cfg, uint16_t idx,
 				uint8_t *key, uint8_t *new_key, int phase);
@@ -141,7 +145,7 @@ bool mesh_config_write_mode(struct mesh_config *cfg, const char *keyword,
 								int value);
 bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page,
 						uint8_t *data, uint16_t size);
-bool mesh_config_comp_page_mv(struct mesh_config *cfg, uint8_t old, uint8_t nw);
+void mesh_config_comp_page_del(struct mesh_config *cfg, uint8_t page);
 bool mesh_config_model_binding_add(struct mesh_config *cfg, uint16_t ele_addr,
 						uint32_t mod_id, bool vendor,
 							uint16_t app_idx);
diff --git a/mesh/model.c b/mesh/model.c
index d48e6ef12..e2babea10 100644
--- a/mesh/model.c
+++ b/mesh/model.c
@@ -24,6 +24,8 @@
 #include "mesh/net.h"
 #include "mesh/appkey.h"
 #include "mesh/cfgmod.h"
+#include "mesh/prov.h"
+#include "mesh/remprv.h"
 #include "mesh/error.h"
 #include "mesh/dbus.h"
 #include "mesh/util.h"
@@ -76,6 +78,9 @@ static bool is_internal(uint32_t id)
 	if (id == CONFIG_SRV_MODEL || id == CONFIG_CLI_MODEL)
 		return true;
 
+	if (id == REM_PROV_SRV_MODEL || id == REM_PROV_CLI_MODEL)
+		return true;
+
 	return false;
 }
 
@@ -457,13 +462,25 @@ static int dev_packet_decrypt(struct mesh_node *node, const uint8_t *data,
 					dst, key_aid, seq, iv_idx, out, key))
 		return APP_IDX_DEV_LOCAL;
 
-	if (!keyring_get_remote_dev_key(node, src, dev_key))
+	key = dev_key;
+
+	if (keyring_get_remote_dev_key(node, src, dev_key)) {
+		if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict,
+				src, dst, key_aid, seq, iv_idx, out, key))
+			return APP_IDX_DEV_REMOTE;
+	}
+
+	/* See if there is a local Device Key Candidate as last resort */
+	if (!node_get_device_key_candidate(node, dev_key))
 		return -1;
 
-	key = dev_key;
-	if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict, src,
-					dst, key_aid, seq, iv_idx, out, key))
-		return APP_IDX_DEV_REMOTE;
+	if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict,
+				src, dst, key_aid, seq, iv_idx, out, key)) {
+
+		/* If candidate dev_key worked, it is considered finalized */
+		node_finalize_candidate(node);
+		return APP_IDX_DEV_LOCAL;
+	}
 
 	return -1;
 }
diff --git a/mesh/node.c b/mesh/node.c
index cf4ed140e..5150a085a 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -27,9 +27,11 @@
 #include "mesh/appkey.h"
 #include "mesh/mesh-config.h"
 #include "mesh/provision.h"
+#include "mesh/prov.h"
 #include "mesh/keyring.h"
 #include "mesh/model.h"
 #include "mesh/cfgmod.h"
+#include "mesh/remprv.h"
 #include "mesh/util.h"
 #include "mesh/error.h"
 #include "mesh/dbus.h"
@@ -347,6 +349,15 @@ static bool add_elements_from_storage(struct mesh_node *node,
 		if (!add_element_from_storage(node, entry->data))
 			return false;
 
+	/* Add configuration server model on the primary element */
+	mesh_model_add(node, PRIMARY_ELE_IDX, CONFIG_SRV_MODEL, NULL);
+
+	/* Add remote provisioning models on the primary element */
+	mesh_model_add(node, PRIMARY_ELE_IDX, REM_PROV_SRV_MODEL, NULL);
+
+	if (node->provisioner)
+		mesh_model_add(node, PRIMARY_ELE_IDX, REM_PROV_CLI_MODEL, NULL);
+
 	return true;
 }
 
@@ -489,6 +500,10 @@ static bool init_from_storage(struct mesh_config_node *db_node,
 	/* Initialize configuration server model */
 	cfgmod_server_init(node, PRIMARY_ELE_IDX);
 
+	/* Initialize remote provisioning models */
+	remote_prov_server_init(node, PRIMARY_ELE_IDX);
+	remote_prov_client_init(node, PRIMARY_ELE_IDX);
+
 	node->cfg = cfg;
 
 	return true;
@@ -550,12 +565,78 @@ uint16_t node_get_primary(struct mesh_node *node)
 		return node->primary;
 }
 
+bool node_refresh(struct mesh_node *node, bool hard, void *prov_info)
+{
+	struct mesh_prov_node_info *info = prov_info;
+	bool res = true;
+
+	if (!node || !info)
+		return false;
+
+	if (!IS_UNICAST(info->unicast))
+		return false;
+
+	/* Changing Unicast addresses requires a hard node reset */
+	if (!hard && info->unicast != node->primary)
+		return false;
+
+	/*
+	 * Hard refresh results in immediate use of new Device Key.
+	 * Soft refresh saves new device key as Candidate until we
+	 * successfully receive new incoming message on that key.
+	 */
+	if (hard) {
+		if (!mesh_config_write_device_key(node->cfg, info->device_key))
+			return false;
+
+		memcpy(node->dev_key, info->device_key, sizeof(node->dev_key));
+
+	} else if (!mesh_config_write_candidate(node->cfg, info->device_key))
+		return false;
+
+	/* Replace Primary Unicast address if it has changed */
+	if (node->primary != info->unicast) {
+		res = mesh_config_write_unicast(node->cfg, info->unicast);
+		if (res) {
+			node->primary = info->unicast;
+			node->num_ele = info->num_ele;
+			mesh_net_register_unicast(node->net, node->primary,
+								node->num_ele);
+		}
+	}
+
+	/* Replace Page 0 with Page 128 if it exists */
+	if (res) {
+		if (node_replace_comp(node, 0, 128))
+			return true;
+	}
+
+	return res;
+}
+
 const uint8_t *node_get_device_key(struct mesh_node *node)
 {
 	if (!node)
 		return NULL;
-	else
-		return node->dev_key;
+
+	return node->dev_key;
+}
+
+bool node_get_device_key_candidate(struct mesh_node *node, uint8_t *key)
+{
+	if (!node)
+		return false;
+
+	return mesh_config_read_candidate(node->cfg, key);
+}
+
+void node_finalize_candidate(struct mesh_node *node)
+{
+	if (!node)
+		return;
+
+	if (mesh_config_read_candidate(node->cfg, node->dev_key))
+		mesh_config_finalize_candidate(node->cfg);
 }
 
 void node_set_token(struct mesh_node *node, uint8_t token[8])
@@ -785,7 +866,7 @@ uint8_t node_friend_mode_get(struct mesh_node *node)
 	return node->friend;
 }
 
-static uint16_t generate_node_comp(struct mesh_node *node, uint8_t *buf,
+static uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf,
 								uint16_t sz)
 {
 	uint16_t n, features, num_ele = 0;
@@ -895,6 +976,21 @@ static void convert_node_to_storage(struct mesh_node *node,
 
 }
 
+static void free_db_storage(struct mesh_config_node *db_node)
+{
+	const struct l_queue_entry *entry;
+
+	/* Free temporarily allocated resources */
+	entry = l_queue_get_entries(db_node->elements);
+	for (; entry; entry = entry->next) {
+		struct mesh_config_element *db_ele = entry->data;
+
+		l_queue_destroy(db_ele->models, l_free);
+	}
+
+	l_queue_destroy(db_node->elements, l_free);
+}
+
 static bool create_node_config(struct mesh_node *node, const uint8_t uuid[16])
 {
 	struct mesh_config_node db_node;
@@ -922,7 +1018,22 @@ static bool create_node_config(struct mesh_node *node, const uint8_t uuid[16])
 	return node->cfg != NULL;
 }
 
-static bool set_node_comp(struct mesh_node *node, uint8_t page_num,
+static void node_del_comp(struct mesh_node *node, uint8_t page_num)
+{
+	struct mesh_config_comp_page *page;
+
+	if (!node)
+		return;
+
+	page = l_queue_remove_if(node->pages, match_page,
+						L_UINT_TO_PTR(page_num));
+
+	l_free(page);
+
+	mesh_config_comp_page_del(node->cfg, page_num);
+}
+
+static bool node_set_comp(struct mesh_node *node, uint8_t page_num,
 					const uint8_t *data, uint16_t len)
 {
 	struct mesh_config_comp_page *page;
@@ -944,16 +1055,6 @@ static bool set_node_comp(struct mesh_node *node, uint8_t page_num,
 	return mesh_config_comp_page_add(node->cfg, page_num, page->data, len);
 }
 
-static bool create_node_comp(struct mesh_node *node)
-{
-	uint16_t len;
-	uint8_t comp[MAX_MSG_LEN - 2];
-
-	len = generate_node_comp(node, comp, sizeof(comp));
-
-	return set_node_comp(node, 0, comp, len);
-}
-
 const uint8_t *node_get_comp(struct mesh_node *node, uint8_t page_num,
 								uint16_t *len)
 {
@@ -975,6 +1076,7 @@ const uint8_t *node_get_comp(struct mesh_node *node, uint8_t page_num,
 bool node_replace_comp(struct mesh_node *node, uint8_t retire, uint8_t with)
 {
 	struct mesh_config_comp_page *old_page, *keep;
+	bool status;
 
 	if (!node)
 		return false;
@@ -989,9 +1091,13 @@ bool node_replace_comp(struct mesh_node *node, uint8_t retire, uint8_t with)
 
 	l_free(old_page);
 	keep->page_num = retire;
-	mesh_config_comp_page_mv(node->cfg, with, retire);
+	status = mesh_config_comp_page_add(node->cfg, keep->page_num,
+							keep->data, keep->len);
 
-	return true;
+	if (with != retire)
+		mesh_config_comp_page_del(node->cfg, with);
+
+	return status;
 }
 
 static void attach_io(void *a, void *b)
@@ -1170,8 +1276,13 @@ static bool get_element_properties(struct mesh_node *node, const char *path,
 	 * daemon. If the model is present in the application properties,
 	 * the operation below will be a "no-op".
 	 */
-	if (ele->idx == PRIMARY_ELE_IDX)
+	if (ele->idx == PRIMARY_ELE_IDX) {
 		mesh_model_add(node, ele->models, CONFIG_SRV_MODEL, NULL);
+		mesh_model_add(node, ele->models, REM_PROV_SRV_MODEL, NULL);
+		if (node->provisioner)
+			mesh_model_add(node, ele->models, REM_PROV_CLI_MODEL,
+									NULL);
+	}
 
 	return true;
 fail:
@@ -1232,6 +1343,15 @@ static bool get_app_properties(struct mesh_node *node, const char *path,
 	return true;
 }
 
+static void save_pages(void *data, void *user_data)
+{
+	struct mesh_config_comp_page *page = data;
+	struct mesh_node *node = user_data;
+
+	mesh_config_comp_page_add(node->cfg, page->page_num, page->data,
+								page->len);
+}
+
 static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr,
 				bool ivu, uint32_t iv_idx, uint8_t dev_key[16],
 				uint16_t net_key_idx, uint8_t net_key[16])
@@ -1275,10 +1395,14 @@ static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr,
 			return false;
 	}
 
+	l_queue_foreach(node->pages, save_pages, node);
+
 	update_net_settings(node);
 
-	/* Initialize configuration server model */
+	/* Initialize internal server models */
 	cfgmod_server_init(node, PRIMARY_ELE_IDX);
+	remote_prov_server_init(node, PRIMARY_ELE_IDX);
+	remote_prov_client_init(node, PRIMARY_ELE_IDX);
 
 	node->busy = true;
 
@@ -1326,39 +1450,59 @@ static void update_model_options(struct mesh_node *node,
 
 static bool check_req_node(struct managed_obj_request *req)
 {
+	struct mesh_node *node;
 	const int offset = 8;
 	uint16_t node_len, len;
 	uint8_t comp[MAX_MSG_LEN - 2];
 	const uint8_t *node_comp;
 
-	len = generate_node_comp(req->node, comp, sizeof(comp));
+	if (req->type != REQUEST_TYPE_ATTACH) {
+		node = req->node;
 
-	if (len < MIN_COMP_SIZE)
-		return false;
+		if (!create_node_config(node, node->uuid))
+			return false;
+	} else
+		node = req->attach;
 
-	node_comp = node_get_comp(req->attach, 0, &node_len);
+	node_comp = node_get_comp(node, 0, &node_len);
+	len = node_generate_comp(req->node, comp, sizeof(comp));
 
-	/* If no page 0 exists, create it and accept */
-	if (!node_len || !node_comp)
-		return set_node_comp(req->attach, 0, comp, len);
+	/* If no page 0 exists, then current composition as valid */
+	if (req->type != REQUEST_TYPE_ATTACH || !node_len)
+		goto page_zero_valid;
 
-	/* Test Element/Model part of composition and reject if changed */
+	/*
+	 * If composition has materially changed, save new composition
+	 * in page 128 until next NPPI procedure. But we do allow
+	 * for CID, PID, VID and/or CRPL to freely change without
+	 * requiring a NPPI procedure.
+	 */
 	if (node_len != len || memcmp(&node_comp[offset], &comp[offset],
 							node_len - offset))
-		return false;
+		return node_set_comp(node, 128, comp, len);
 
-	/* If comp has changed, but not Element/Models, resave and accept */
-	else if (memcmp(node_comp, comp, node_len))
-		return set_node_comp(req->attach, 0, comp, len);
+page_zero_valid:
+	/* If page 0 represents current App, ensure page 128 doesn't exist */
+	node_del_comp(node, 128);
 
-	/* Nothing has changed */
-	return true;
+	if (len == node_len && !memcmp(node_comp, comp, len))
+		return true;
+
+	return node_set_comp(node, 0, comp, len);
+}
+
+static bool is_zero(const void *a, const void *b)
+{
+	const struct node_element *element = a;
+
+	return !element->idx;
 }
 
 static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node)
 {
 	const struct l_queue_entry *attach_entry;
 	const struct l_queue_entry *node_entry;
+	bool comp_changed = false;
 
 	attach->obj_path = node->obj_path;
 	node->obj_path = NULL;
@@ -1368,6 +1512,34 @@ static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node)
 		return false;
 	}
 
+	if (attach->num_ele != node->num_ele) {
+		struct mesh_config_node db_node;
+		struct node_element *old_ele, *new_ele;
+
+		convert_node_to_storage(node, &db_node);
+
+		/*
+		 * If composition has materially changed, we need to discard
+		 * everything we knew about elements in the old application,
+		 * and start from what they are telling us now.
+		 */
+		old_ele = l_queue_remove_if(attach->elements, is_zero, NULL);
+		new_ele = l_queue_remove_if(node->elements, is_zero, NULL);
+		element_free(new_ele);
+
+		l_queue_destroy(attach->elements, element_free);
+		attach->elements = node->elements;
+		attach->num_ele = node->num_ele;
+
+		/* Restore primary elements */
+		l_queue_push_head(attach->elements, old_ele);
+
+		comp_changed = true;
+
+		mesh_config_reset(attach->cfg, &db_node);
+		free_db_storage(&db_node);
+	}
+
 	attach_entry = l_queue_get_entries(attach->elements);
 	node_entry = l_queue_get_entries(node->elements);
 
@@ -1384,6 +1556,10 @@ static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node)
 
 		attach_entry = attach_entry->next;
 		node_entry = node_entry->next;
+
+		/* Only need the Primary element during Composition change */
+		if (comp_changed)
+			break;
 	}
 
 	mesh_agent_remove(attach->agent);
@@ -1399,8 +1575,12 @@ static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node)
 	node->owner = NULL;
 
 	update_composition(node, attach);
+
 	update_model_options(node, attach);
 
+	if (comp_changed)
+		node->elements = NULL;
+
 	node_remove(node);
 
 	return true;
@@ -1499,16 +1679,7 @@ static void get_managed_objects_cb(struct l_dbus_message *msg, void *user_data)
 
 	node->num_ele = num_ele;
 
-	if (req->type != REQUEST_TYPE_ATTACH) {
-		/* Generate node configuration for a brand new node */
-		if (!create_node_config(node, node->uuid))
-			goto fail;
-
-		/* Create node composition */
-		if (!create_node_comp(node))
-			goto fail;
-	} else if (!check_req_node(req))
-		/* Check the integrity of the node composition */
+	if (!check_req_node(req))
 		goto fail;
 
 	switch (req->type) {
diff --git a/mesh/node.h b/mesh/node.h
index 2e3d89812..a98945223 100644
--- a/mesh/node.h
+++ b/mesh/node.h
@@ -38,6 +38,8 @@ uint16_t node_get_primary_net_idx(struct mesh_node *node);
 void node_set_token(struct mesh_node *node, uint8_t token[8]);
 const uint8_t *node_get_token(struct mesh_node *node);
 const uint8_t *node_get_device_key(struct mesh_node *node);
+bool node_get_device_key_candidate(struct mesh_node *node, uint8_t *key);
+void node_finalize_candidate(struct mesh_node *node);
 void node_set_num_elements(struct mesh_node *node, uint8_t num_ele);
 uint8_t node_get_num_elements(struct mesh_node *node);
 uint8_t node_default_ttl_get(struct mesh_node *node);
@@ -89,3 +91,4 @@ const char *node_get_storage_dir(struct mesh_node *node);
 bool node_load_from_storage(const char *storage_dir);
 void node_finalize_new_node(struct mesh_node *node, struct mesh_io *io);
 void node_property_changed(struct mesh_node *node, const char *property);
+bool node_refresh(struct mesh_node *node, bool hard, void *prov_info);
diff --git a/mesh/pb-adv.c b/mesh/pb-adv.c
index 180b16258..385d81d65 100644
--- a/mesh/pb-adv.c
+++ b/mesh/pb-adv.c
@@ -219,7 +219,7 @@ static void tx_timeout(struct l_timeout *timeout, void *user_data)
 	cb(user_data, 1);
 }
 
-static void pb_adv_tx(void *user_data, void *data, uint16_t len)
+static void pb_adv_tx(void *user_data, const void *data, uint16_t len)
 {
 	struct pb_adv_session *session = user_data;
 
@@ -478,7 +478,7 @@ static void pb_adv_packet(void *user_data, const uint8_t *pkt, uint16_t len)
 bool pb_adv_reg(bool initiator, mesh_prov_open_func_t open_cb,
 		mesh_prov_close_func_t close_cb,
 		mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb,
-		uint8_t uuid[16], void *user_data)
+		const uint8_t *uuid, void *user_data)
 {
 	struct pb_adv_session *session, *old_session;
 
diff --git a/mesh/pb-adv.h b/mesh/pb-adv.h
index 5b1e03dae..e33ba8e35 100644
--- a/mesh/pb-adv.h
+++ b/mesh/pb-adv.h
@@ -11,5 +11,5 @@
 bool pb_adv_reg(bool initiator, mesh_prov_open_func_t open_cb,
 		mesh_prov_close_func_t close_cb,
 		mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb,
-		uint8_t uuid[16], void *user_data);
+		const uint8_t *uuid, void *user_data);
 void pb_adv_unreg(void *user_data);
diff --git a/mesh/prov-acceptor.c b/mesh/prov-acceptor.c
index bf8c573da..fd9d4cd5d 100644
--- a/mesh/prov-acceptor.c
+++ b/mesh/prov-acceptor.c
@@ -22,6 +22,7 @@
 #include "mesh/net.h"
 #include "mesh/prov.h"
 #include "mesh/provision.h"
+#include "mesh/remprv.h"
 #include "mesh/pb-adv.h"
 #include "mesh/mesh.h"
 #include "mesh/agent.h"
@@ -169,9 +170,6 @@ static void acp_prov_open(void *user_data, prov_trans_tx_t trans_tx,
 					prov->transport != transport)
 		return;
 
-	if (transport != PB_ADV)
-		return;
-
 	prov->trans_tx = trans_tx;
 	prov->transport = transport;
 	prov->trans_data = trans_data;
@@ -425,9 +423,10 @@ static bool prov_start_check(struct prov_start *start,
 	return true;
 }
 
-static void acp_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
+static void acp_prov_rx(void *user_data, const void *dptr, uint16_t len)
 {
 	struct mesh_prov_acceptor *rx_prov = user_data;
+	const uint8_t *data = dptr;
 	struct mesh_prov_node_info *info;
 	struct prov_fail_msg fail;
 	uint8_t type = *data++;
@@ -654,14 +653,19 @@ static void acp_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
 		info->flags = prov->rand_auth_workspace[18];
 		info->iv_index = l_get_be32(prov->rand_auth_workspace + 19);
 		info->unicast = l_get_be16(prov->rand_auth_workspace + 23);
+		info->num_ele = prov->conf_inputs.caps.num_ele;
+
+		/* Send prov complete */
+		prov->rand_auth_workspace[0] = PROV_COMPLETE;
+		prov->trans_tx(prov->trans_data,
+				prov->rand_auth_workspace, 1);
 
 		result = prov->cmplt(prov->caller_data, PROV_ERR_SUCCESS, info);
 		prov->cmplt = NULL;
 		l_free(info);
 
 		if (result) {
-			prov->rand_auth_workspace[0] = PROV_COMPLETE;
-			prov_send(prov, prov->rand_auth_workspace, 1);
+			l_debug("PROV_COMPLETE");
 			goto cleanup;
 		} else {
 			fail.reason = PROV_ERR_UNEXPECTED_ERR;
@@ -721,7 +725,7 @@ static void acp_prov_ack(void *user_data, uint8_t msg_num)
 
 
 /* This starts unprovisioned device beacon */
-bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],
+bool acceptor_start(uint8_t num_ele, uint8_t *uuid,
 		uint16_t algorithms, uint32_t timeout,
 		struct mesh_agent *agent,
 		mesh_prov_acceptor_complete_func_t complete_cb,
@@ -733,8 +737,10 @@ bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],
 	uint8_t len = sizeof(beacon) - sizeof(uint32_t);
 	bool result;
 
-	/* Invoked from Join() method in mesh-api.txt, to join a
-	 * remote mesh network.
+	/*
+	 * Invoked from Join() method in mesh-api.txt, to join a
+	 * remote mesh network. May also be invoked with a NULL
+	 * uuid to perform a Device Key Refresh procedure.
 	 */
 
 	if (prov)
@@ -752,37 +758,50 @@ bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],
 
 	caps = mesh_agent_get_caps(agent);
 
-	/* TODO: Should we sanity check values here or elsewhere? */
 	prov->conf_inputs.caps.num_ele = num_ele;
-	prov->conf_inputs.caps.pub_type = caps->pub_type;
-	prov->conf_inputs.caps.static_type = caps->static_type;
-	prov->conf_inputs.caps.output_size = caps->output_size;
-	prov->conf_inputs.caps.input_size = caps->input_size;
-
-	/* Store UINT16 values in Over-the-Air order, in packed structure
-	 * for crypto inputs
-	 */
 	l_put_be16(algorithms, &prov->conf_inputs.caps.algorithms);
-	l_put_be16(caps->output_action, &prov->conf_inputs.caps.output_action);
-	l_put_be16(caps->input_action, &prov->conf_inputs.caps.input_action);
-
-	/* Compose Unprovisioned Beacon */
-	memcpy(beacon + 2, uuid, 16);
-	l_put_be16(caps->oob_info, beacon + 18);
-	if (caps->oob_info & OOB_INFO_URI_HASH){
-		l_put_be32(caps->uri_hash, beacon + 20);
-		len += sizeof(uint32_t);
+
+	if (caps) {
+		/* TODO: Should we sanity check values here or elsewhere? */
+		prov->conf_inputs.caps.pub_type = caps->pub_type;
+		prov->conf_inputs.caps.static_type = caps->static_type;
+		prov->conf_inputs.caps.output_size = caps->output_size;
+		prov->conf_inputs.caps.input_size = caps->input_size;
+
+		/* Store UINT16 values in Over-the-Air order, in packed
+		 * structure for crypto inputs
+		 */
+		l_put_be16(caps->output_action,
+					&prov->conf_inputs.caps.output_action);
+		l_put_be16(caps->input_action,
+					&prov->conf_inputs.caps.input_action);
+
+		/* Populate Caps fields of beacon */
+		l_put_be16(caps->oob_info, beacon + 18);
+		if (caps->oob_info & OOB_INFO_URI_HASH) {
+			l_put_be32(caps->uri_hash, beacon + 20);
+			len += sizeof(uint32_t);
+		}
 	}
 
-	/* Infinitely Beacon until Canceled, or Provisioning Starts */
-	result = mesh_send_pkt(0, 500, beacon, len);
+	if (uuid) {
+		/* Compose Unprovisioned Beacon */
+		memcpy(beacon + 2, uuid, 16);
+
+		/* Infinitely Beacon until Canceled, or Provisioning Starts */
+		result = mesh_send_pkt(0, 500, beacon, len);
 
-	if (!result)
-		goto error_fail;
+		if (!result)
+			goto error_fail;
 
-	/* Always register for PB-ADV */
-	result = pb_adv_reg(false, acp_prov_open, acp_prov_close, acp_prov_rx,
-						acp_prov_ack, uuid, prov);
+		/* Always register for PB-ADV */
+		result = pb_adv_reg(false, acp_prov_open, acp_prov_close,
+					acp_prov_rx, acp_prov_ack, uuid, prov);
+	} else {
+		/* Run Device Key Refresh Procedure */
+		result = register_nppi_acceptor(acp_prov_open, acp_prov_close,
+					acp_prov_rx, acp_prov_ack, prov);
+	}
 
 	if (result)
 		return true;
diff --git a/mesh/prov-initiator.c b/mesh/prov-initiator.c
index c62577523..653f3ae3e 100644
--- a/mesh/prov-initiator.c
+++ b/mesh/prov-initiator.c
@@ -21,10 +21,12 @@
 #include "mesh/crypto.h"
 #include "mesh/net.h"
 #include "mesh/node.h"
+#include "mesh/model.h"
 #include "mesh/keyring.h"
 #include "mesh/prov.h"
 #include "mesh/provision.h"
 #include "mesh/pb-adv.h"
+#include "mesh/remprv.h"
 #include "mesh/mesh.h"
 #include "mesh/agent.h"
 #include "mesh/error.h"
@@ -82,12 +84,16 @@ struct mesh_prov_initiator {
 	struct l_timeout *timeout;
 	uint32_t to_secs;
 	enum int_state	state;
-	enum trans_type transport;
 	uint16_t net_idx;
+	uint16_t svr_idx;
 	uint16_t unicast;
+	uint16_t server;
+	uint8_t transport;
 	uint8_t material;
 	uint8_t expected;
 	int8_t previous;
+	uint8_t out_num;
+	uint8_t rpr_state;
 	struct conf_input conf_inputs;
 	uint8_t calc_key[16];
 	uint8_t salt[16];
@@ -100,14 +106,23 @@ struct mesh_prov_initiator {
 	uint8_t uuid[16];
 };
 
+struct scan_req {
+	mesh_prov_initiator_scan_result_t scan_result;
+	struct mesh_node *node;
+	int count;
+};
+
 static struct mesh_prov_initiator *prov = NULL;
+static struct l_queue *scans;
 
 static void initiator_free(void)
 {
-	if (prov)
+	if (prov) {
 		l_timeout_remove(prov->timeout);
 
-	mesh_send_cancel(&pkt_filter, sizeof(pkt_filter));
+		if (!prov->server)
+			mesh_send_cancel(&pkt_filter, sizeof(pkt_filter));
+	}
 
 	pb_adv_unreg(prov);
 
@@ -119,6 +134,15 @@ static void int_prov_close(void *user_data, uint8_t reason)
 {
 	struct mesh_prov_initiator *prov = user_data;
 	struct mesh_prov_node_info info;
+	uint8_t msg[4];
+	int n;
+
+	if (prov->server) {
+		n = mesh_model_opcode_set(OP_REM_PROV_LINK_CLOSE, msg);
+		msg[n++] = reason == PROV_ERR_SUCCESS ? 0x00 : 0x02;
+		mesh_model_send(prov->node, 0, prov->server, APP_IDX_DEV_REMOTE,
+				prov->svr_idx, DEFAULT_TTL, true, n, msg);
+	}
 
 	if (reason != PROV_ERR_SUCCESS) {
 		prov->complete_cb(prov->caller_data, reason, NULL);
@@ -626,9 +650,10 @@ static void int_prov_start_auth(const struct mesh_agent_prov_caps *prov_caps,
 	}
 }
 
-static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
+static void int_prov_rx(void *user_data, const void *dptr, uint16_t len)
 {
 	struct mesh_prov_initiator *rx_prov = user_data;
+	const uint8_t *data = dptr;
 	uint8_t *out;
 	uint8_t type = *data++;
 	uint8_t fail_code[2];
@@ -651,7 +676,7 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
 	if (type >= L_ARRAY_SIZE(expected_pdu_size) ||
 					len != expected_pdu_size[type]) {
 		l_error("Expected PDU size %d, Got %d (type: %2.2x)",
-			len, expected_pdu_size[type], type);
+			expected_pdu_size[type], len, type);
 		fail_code[1] = PROV_ERR_INVALID_FORMAT;
 		goto failure;
 	}
@@ -773,7 +798,12 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
 			goto failure;
 		}
 
-		if (!prov->data_req_cb(prov->caller_data,
+		if (prov->transport == PB_NPPI_00 ||
+						prov->transport == PB_NPPI_02) {
+			/* No App data needed */
+			initiator_prov_data(prov->svr_idx, prov->server,
+							prov->caller_data);
+		} else if (!prov->data_req_cb(prov->caller_data,
 					prov->conf_inputs.caps.num_ele)) {
 			l_error("Provisioning Failed-Data Get");
 			fail_code[1] = PROV_ERR_CANT_ASSIGN_ADDR;
@@ -851,6 +881,8 @@ static void int_prov_ack(void *user_data, uint8_t msg_num)
 
 static void initiator_open_cb(void *user_data, int err)
 {
+	uint8_t msg[20];
+	int n;
 	bool result;
 
 	if (!prov)
@@ -859,18 +891,30 @@ static void initiator_open_cb(void *user_data, int err)
 	if (err != MESH_ERROR_NONE)
 		goto fail;
 
-	/* Always register for PB-ADV */
-	result = pb_adv_reg(true, int_prov_open, int_prov_close, int_prov_rx,
-						int_prov_ack, prov->uuid, prov);
+	if (prov->server) {
+		n = mesh_model_opcode_set(OP_REM_PROV_LINK_OPEN, msg);
+
+		if (prov->transport <= PB_NPPI_02) {
+			msg[n++] = prov->transport;
+		} else {
+			memcpy(msg + n, prov->uuid, 16);
+			n += 16;
+		}
+
+		result = mesh_model_send(prov->node, 0, prov->server,
+					APP_IDX_DEV_REMOTE, prov->svr_idx,
+					DEFAULT_TTL, true, n, msg);
+	} else {
+		/* Always register for PB-ADV */
+		result = pb_adv_reg(true, int_prov_open, int_prov_close,
+				int_prov_rx, int_prov_ack, prov->uuid, prov);
+	}
 
 	if (!result) {
 		err = MESH_ERROR_FAILED;
 		goto fail;
 	}
 
-	if (!prov)
-		return;
-
 	prov->start_cb(prov->caller_data, MESH_ERROR_NONE);
 	return;
 fail:
@@ -878,10 +922,20 @@ fail:
 	initiator_free();
 }
 
-bool initiator_start(enum trans_type transport,
-		uint8_t uuid[16],
-		uint16_t max_ele,
-		uint32_t timeout, /* in seconds from mesh.conf */
+static void initiate_to(struct l_timeout *timeout, void *user_data)
+{
+	struct mesh_prov_initiator *rx_prov = user_data;
+
+	if (rx_prov != prov) {
+		l_timeout_remove(timeout);
+		return;
+	}
+
+	int_prov_close(user_data, PROV_ERR_TIMEOUT);
+}
+
+bool initiator_start(uint8_t transport, uint16_t server, uint16_t svr_idx,
+		uint8_t uuid[16], uint16_t max_ele, uint32_t timeout,
 		struct mesh_agent *agent,
 		mesh_prov_initiator_start_func_t start_cb,
 		mesh_prov_initiator_data_req_func_t data_req_cb,
@@ -904,6 +958,10 @@ bool initiator_start(enum trans_type transport,
 	prov->data_req_cb = data_req_cb;
 	prov->caller_data = caller_data;
 	prov->previous = -1;
+	prov->server = server;
+	prov->svr_idx = svr_idx;
+	prov->transport = transport;
+	prov->timeout = l_timeout_create(timeout, initiate_to, prov, NULL);
 	memcpy(prov->uuid, uuid, 16);
 
 	mesh_agent_refresh(prov->agent, initiator_open_cb, prov);
@@ -915,3 +973,182 @@ void initiator_cancel(void *user_data)
 {
 	initiator_free();
 }
+
+static void rpr_tx(void *user_data, const void *data, uint16_t len)
+{
+	struct mesh_prov_initiator *prov = user_data;
+	uint8_t msg[72];
+	int n;
+
+	n = mesh_model_opcode_set(OP_REM_PROV_PDU_SEND, msg);
+	msg[n++] = ++prov->out_num;
+	memcpy(msg + n, data, len);
+	l_debug("Send OB %2.2x, with packet type %d", msg[n], prov->out_num);
+	n += len;
+
+	prov->rpr_state = PB_REMOTE_STATE_OB_PKT_TX;
+	mesh_model_send(prov->node, 0, prov->server, APP_IDX_DEV_REMOTE,
+				prov->svr_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static bool match_req_node(const void *a, const void *b)
+{
+	const struct scan_req *req = a;
+	const struct mesh_node *node = b;
+
+	return req->node == node;
+}
+
+static bool remprv_cli_pkt(uint16_t src, uint16_t unicast, uint16_t app_idx,
+					uint16_t net_idx, const uint8_t *data,
+					uint16_t size, const void *user_data)
+{
+	struct mesh_node *node = (struct mesh_node *) user_data;
+	const uint8_t *pkt = data;
+	struct scan_req *req;
+	uint32_t opcode;
+	uint16_t n;
+
+	if (mesh_model_opcode_get(pkt, size, &opcode, &n)) {
+		size -= n;
+		pkt += n;
+	} else
+		return false;
+
+	if (opcode < OP_REM_PROV_SCAN_CAP_GET ||
+					opcode > OP_REM_PROV_PDU_REPORT)
+		return false;
+
+	if (app_idx != APP_IDX_DEV_REMOTE && app_idx != APP_IDX_DEV_LOCAL)
+		return true;
+
+	/* Local Dev key only allowed for Loop-backs */
+	if (app_idx == APP_IDX_DEV_LOCAL && unicast != src)
+		return true;
+
+	if (prov && (prov->server != src || prov->node != node))
+		return true;
+
+	n = 0;
+
+	switch (opcode) {
+	default:
+		return false;
+
+	/* Provisioning Opcodes */
+	case OP_REM_PROV_LINK_STATUS:
+		if (size != 2 || !prov)
+			break;
+
+		if (pkt[0] == PB_REM_ERR_SUCCESS)
+			prov->rpr_state = pkt[1];
+
+		break;
+
+	case OP_REM_PROV_LINK_REPORT:
+		if (size != 2 || !prov)
+			return true;
+
+		if (pkt[0] != PB_REM_ERR_SUCCESS) {
+			if (pkt[0] == PB_REM_ERR_CLOSED_BY_DEVICE ||
+				pkt[0] == PB_REM_ERR_CLOSED_BY_SERVER)
+				int_prov_close(prov, pkt[1]);
+
+			break;
+		}
+
+
+		if (prov->rpr_state == PB_REMOTE_STATE_LINK_OPENING)
+			int_prov_open(prov, rpr_tx, prov, prov->transport);
+		else if (prov->rpr_state == PB_REMOTE_STATE_LINK_CLOSING) {
+			prov->rpr_state = PB_REMOTE_STATE_IDLE;
+			int_prov_close(prov, pkt[1]);
+			break;
+		}
+
+		prov->rpr_state = pkt[1];
+
+		break;
+
+	case OP_REM_PROV_PDU_REPORT:
+		int_prov_rx(prov, pkt + 1, size - 1);
+		break;
+
+	case OP_REM_PROV_PDU_OB_REPORT:
+		if (size != 1 || !prov)
+			break;
+
+		l_debug("Got Ack for OB %d", pkt[0]);
+		if (prov->rpr_state == PB_REMOTE_STATE_OB_PKT_TX &&
+							pkt[0] == prov->out_num)
+			int_prov_ack(prov, pkt[0]);
+
+		break;
+
+	/* Scan Opcodes */
+	case OP_REM_PROV_SCAN_CAP_STATUS:
+	case OP_REM_PROV_SCAN_STATUS:
+		break;
+
+	case OP_REM_PROV_SCAN_REPORT:
+	case OP_REM_PROV_EXT_SCAN_REPORT:
+		req = l_queue_find(scans, match_req_node, node);
+		if (req) {
+			req->scan_result(node, src,
+				opcode == OP_REM_PROV_EXT_SCAN_REPORT,
+				pkt, size);
+		}
+	}
+
+	return true;
+}
+
+void initiator_scan_reg(mesh_prov_initiator_scan_result_t scan_result,
+								void *user_data)
+{
+	struct scan_req *req;
+
+	if (!scans)
+		scans = l_queue_new();
+
+	req = l_queue_find(scans, match_req_node, user_data);
+	if (!req) {
+		req = l_new(struct scan_req, 1);
+		l_queue_push_head(scans, req);
+	}
+
+	req->scan_result = scan_result;
+	req->node = user_data;
+	req->count++;
+}
+
+void initiator_scan_unreg(void *user_data)
+{
+	struct scan_req *req;
+
+	req = l_queue_find(scans, match_req_node, user_data);
+	if (req) {
+		req->count--;
+		if (!req->count) {
+			l_queue_remove(scans, req);
+			l_free(req);
+		}
+	}
+}
+
+static void remprv_cli_unregister(void *user_data)
+{
+}
+
+static const struct mesh_model_ops ops = {
+	.unregister = remprv_cli_unregister,
+	.recv = remprv_cli_pkt,
+	.bind = NULL,
+	.sub = NULL,
+	.pub = NULL
+};
+
+void remote_prov_client_init(struct mesh_node *node, uint8_t ele_idx)
+{
+	mesh_model_register(node, ele_idx, REM_PROV_CLI_MODEL, &ops, node);
+}
diff --git a/mesh/prov.h b/mesh/prov.h
index 99e864c50..e86668fe4 100644
--- a/mesh/prov.h
+++ b/mesh/prov.h
@@ -39,14 +39,14 @@ enum mesh_prov_mode {
 
 struct mesh_prov;
 
-typedef void (*prov_trans_tx_t)(void *trans_data, void *data, uint16_t len);
+typedef void (*prov_trans_tx_t)(void *tx_data, const void *data, uint16_t len);
 typedef void (*mesh_prov_open_func_t)(void *user_data, prov_trans_tx_t trans_tx,
 					void *trans_data, uint8_t trans_type);
 
 typedef void (*mesh_prov_close_func_t)(void *user_data, uint8_t reason);
 typedef void (*mesh_prov_send_func_t)(bool success, struct mesh_prov *prov);
 typedef void (*mesh_prov_ack_func_t)(void *user_data, uint8_t msg_num);
-typedef void (*mesh_prov_receive_func_t)(void *user_data, const uint8_t *data,
+typedef void (*mesh_prov_receive_func_t)(void *user_data, const void *data,
 								uint16_t size);
 
 
diff --git a/mesh/provision.h b/mesh/provision.h
index 1634c4d40..cfeb6deba 100644
--- a/mesh/provision.h
+++ b/mesh/provision.h
@@ -70,10 +70,11 @@ struct mesh_agent;
 #define OOB_INFO_URI_HASH	0x0002
 
 /* PB_REMOTE not supported from unprovisioned state */
-enum trans_type {
-	PB_ADV = 0,
-	PB_GATT,
-};
+#define PB_NPPI_00	0x00
+#define PB_NPPI_01	0x01
+#define PB_NPPI_02	0x02
+#define PB_ADV		0x03 /* Internal only, and may be reassigned */
+#define PB_GATT		0x04 /* Internal only, and may be reassigned */
 
 #define PROV_FLAG_KR	0x01
 #define PROV_FLAG_IVU	0x02
@@ -101,15 +102,21 @@ typedef bool (*mesh_prov_initiator_complete_func_t)(void *user_data,
 					uint8_t status,
 					struct mesh_prov_node_info *info);
 
+typedef void (*mesh_prov_initiator_scan_result_t)(void *user_data,
+					uint16_t server, bool extended,
+					const uint8_t *data, uint16_t len);
+
 /* This starts unprovisioned device beacon */
-bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],
+bool acceptor_start(uint8_t num_ele, uint8_t *uuid,
 			uint16_t algorithms, uint32_t timeout,
 			struct mesh_agent *agent,
 			mesh_prov_acceptor_complete_func_t complete_cb,
 			void *caller_data);
 void acceptor_cancel(void *user_data);
 
-bool initiator_start(enum trans_type transport,
+bool initiator_start(uint8_t transport,
+		uint16_t server,
+		uint16_t svr_idx,
 		uint8_t uuid[16],
 		uint16_t max_ele,
 		uint32_t timeout, /* in seconds from mesh.conf */
@@ -120,3 +127,7 @@ bool initiator_start(enum trans_type transport,
 		void *node, void *caller_data);
 void initiator_prov_data(uint16_t net_idx, uint16_t primary, void *caller_data);
 void initiator_cancel(void *caller_data);
+
+void initiator_scan_reg(mesh_prov_initiator_scan_result_t scan_result,
+							void *user_data);
+void initiator_scan_unreg(void *caller_data);
diff --git a/mesh/remprv-server.c b/mesh/remprv-server.c
new file mode 100644
index 000000000..85af65dcc
--- /dev/null
+++ b/mesh/remprv-server.c
@@ -0,0 +1,907 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2023  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 <sys/time.h>
+#include <ell/ell.h>
+
+#include "src/shared/ad.h"
+
+#include "mesh/mesh-defs.h"
+#include "mesh/mesh-io.h"
+#include "mesh/util.h"
+#include "mesh/node.h"
+#include "mesh/net.h"
+#include "mesh/appkey.h"
+#include "mesh/model.h"
+#include "mesh/prov.h"
+#include "mesh/provision.h"
+#include "mesh/pb-adv.h"
+#include "mesh/remprv.h"
+
+#define EXT_LIST_SIZE	60
+
+#define RPR_DEV_KEY	0x00
+#define RPR_ADDR	0x01
+#define RPR_COMP	0x02
+#define RPR_ADV		0xFF	/* Internal use only*/
+
+struct rem_scan_data {
+	struct mesh_node *node;
+	struct l_timeout *timeout;
+	uint8_t *list;
+	uint16_t client;
+	uint16_t oob_info;
+	uint16_t net_idx;
+	uint8_t state;
+	uint8_t scanned_limit;
+	uint8_t addr[6];
+	uint8_t uuid[16];
+	uint8_t to_secs;
+	uint8_t rxed_ads;
+	uint8_t ext_cnt;
+	bool fltr;
+	uint8_t ext[0];
+};
+
+static struct rem_scan_data *rpb_scan;
+
+struct rem_prov_data {
+	struct mesh_node *node;
+	struct l_timeout *timeout;
+	void *trans_data;
+	uint16_t client;
+	uint16_t net_idx;
+	uint8_t svr_pdu_num;
+	uint8_t cli_pdu_num;
+	uint8_t state;
+	uint8_t nppi_proc;
+	union {
+		struct {
+			mesh_prov_open_func_t open_cb;
+			mesh_prov_close_func_t close_cb;
+			mesh_prov_receive_func_t rx_cb;
+			mesh_prov_ack_func_t ack_cb;
+			struct mesh_prov_node_info info;
+		} nppi;
+		struct {
+			uint8_t uuid[17];
+			prov_trans_tx_t tx;
+		} adv;
+	} u;
+};
+
+static struct rem_prov_data *rpb_prov;
+
+static const uint8_t prvb[2] = {BT_AD_MESH_BEACON, 0x00};
+static const uint8_t pkt_filter = BT_AD_MESH_PROV;
+static const char *name = "Test Name";
+
+static const uint8_t zero[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static void srv_open(void *user_data, prov_trans_tx_t adv_tx,
+					void *trans_data, uint8_t nppi_proc)
+{
+	struct rem_prov_data *prov = user_data;
+	uint8_t msg[5];
+	int n;
+
+	if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_LINK_OPENING)
+		return;
+
+	l_debug("Remote Link open confirmed");
+	prov->u.adv.tx = adv_tx;
+	prov->trans_data = trans_data;
+	prov->state = PB_REMOTE_STATE_LINK_ACTIVE;
+
+	n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg);
+	msg[n++] = PB_REM_ERR_SUCCESS;
+	msg[n++] = prov->state;
+
+	mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+				prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static void srv_rx(void *user_data, const void *dptr, uint16_t len)
+{
+	struct rem_prov_data *prov = user_data;
+	const uint8_t *data = dptr;
+	uint8_t msg[69];
+	int n;
+
+	if (prov != rpb_prov || prov->state < PB_REMOTE_STATE_LINK_ACTIVE ||
+								len > 65)
+		return;
+
+	l_debug("Remote PB IB-PDU");
+
+	prov->svr_pdu_num++;
+	n = mesh_model_opcode_set(OP_REM_PROV_PDU_REPORT, msg);
+	msg[n++] = prov->svr_pdu_num;
+	memcpy(msg + n, data, len);
+	n += len;
+
+	mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+				prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static void srv_ack(void *user_data, uint8_t msg_num)
+{
+	struct rem_prov_data *prov = user_data;
+	uint8_t msg[4];
+	int n;
+
+	if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_OB_PKT_TX)
+		return;
+
+	l_debug("Remote PB ACK");
+
+	prov->state = PB_REMOTE_STATE_LINK_ACTIVE;
+	n = mesh_model_opcode_set(OP_REM_PROV_PDU_OB_REPORT, msg);
+	msg[n++] = prov->cli_pdu_num;
+
+	mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+				prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static void srv_close(void *user_data, uint8_t reason)
+{
+	struct rem_prov_data *prov = user_data;
+	uint8_t msg[4];
+	int n;
+
+	if (prov != rpb_prov || prov->state < PB_REMOTE_STATE_LINK_ACTIVE)
+		return;
+
+	l_debug("Remote PB Close");
+
+	prov->state = PB_REMOTE_STATE_LINK_CLOSING;
+	n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg);
+	msg[n++] = prov->state;
+	msg[n++] = reason;
+
+	mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+				prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static void send_prov_status(struct rem_prov_data *prov, uint8_t status)
+{
+	uint16_t n;
+	uint8_t msg[5];
+	bool segmented = prov->state == PB_REMOTE_STATE_LINK_CLOSING ?
+								true : false;
+
+	n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg);
+	msg[n++] = status;
+	msg[n++] = prov->state;
+
+	l_info("RPB-Link Status(%d): dst %4.4x", prov->state, prov->client);
+
+	mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+				prov->net_idx, DEFAULT_TTL, segmented, n, msg);
+}
+
+static void remprv_prov_cancel(struct l_timeout *timeout,
+						void *user_data)
+{
+	struct rem_prov_data *prov = user_data;
+
+	if (prov != rpb_prov)
+		return;
+
+	l_timeout_remove(prov->timeout);
+	l_free(prov);
+	rpb_prov = NULL;
+}
+
+static void deregister_ext_ad_type(uint8_t ad_type)
+{
+	uint8_t short_ad;
+
+	switch (ad_type) {
+	case BT_AD_MESH_BEACON:
+	case BT_AD_MESH_DATA:
+	case BT_AD_MESH_PROV:
+	case BT_AD_UUID16_SOME:
+	case BT_AD_UUID32_SOME:
+	case BT_AD_UUID128_SOME:
+	case BT_AD_NAME_SHORT:
+		return;
+
+	case BT_AD_UUID16_ALL:
+	case BT_AD_UUID32_ALL:
+	case BT_AD_UUID128_ALL:
+	case BT_AD_NAME_COMPLETE:
+		/* Automatically get short versions */
+		short_ad = ad_type - 1;
+		mesh_io_deregister_recv_cb(NULL, &short_ad, 1);
+
+		/* fall through */
+	default:
+		mesh_io_deregister_recv_cb(NULL, &ad_type, 1);
+		break;
+	}
+}
+
+static void remprv_scan_cancel(struct l_timeout *timeout,
+						void *user_data)
+{
+	struct rem_scan_data *scan = user_data;
+	uint8_t msg[22 + EXT_LIST_SIZE];
+	uint16_t i, n;
+
+	if (!scan || scan != rpb_scan)
+		return;
+
+	for (n = 0; n < scan->ext_cnt; n++)
+		deregister_ext_ad_type(scan->ext[n]);
+
+	if (scan->timeout == timeout) {
+		/* Return Extended Results */
+		if (scan->ext_cnt) {
+			/* Return Extended Result */
+			n = mesh_model_opcode_set(
+					OP_REM_PROV_EXT_SCAN_REPORT, msg);
+			msg[n++] = PB_REM_ERR_SUCCESS;
+			memcpy(msg + n, scan->uuid, 16);
+			n += 16;
+
+			if (scan->oob_info) {
+				l_put_le16(0, msg + n);
+				n += 2;
+			}
+
+			i = 0;
+			while (scan->list[i]) {
+				msg[n++] = scan->list[i];
+				memcpy(msg + n, &scan->list[i + 1],
+								scan->list[i]);
+				n += scan->list[i];
+				i += scan->list[i] + 1;
+			}
+		}
+	}
+
+	l_timeout_remove(scan->timeout);
+	l_free(scan->list);
+	l_free(scan);
+	rpb_scan = NULL;
+}
+
+static void scan_pkt(void *user_data, struct mesh_io_recv_info *info,
+					const uint8_t *data, uint16_t len)
+{
+	struct rem_scan_data *scan = user_data;
+	uint8_t msg[22 + EXT_LIST_SIZE];
+	uint16_t i, n;
+	uint8_t filled = 0;
+	bool report = false;
+
+	if (scan != rpb_scan)
+		return;
+
+	if (scan->ext_cnt)
+		goto extended_scan;
+
+	/* RX Unprovisioned Beacon */
+	if (data[0] != BT_AD_MESH_BEACON || data[1] ||
+			(len != 18 && len != 20 && len != 24))
+		return;
+
+	data += 2;
+	len -= 2;
+
+	for (n = 0; !report && n < scan->scanned_limit; n++) {
+		if (!memcmp(&scan->list[n * 17 + 1], data, 16)) {
+
+			/* Repeat UUID, check RSSI */
+			if ((int8_t) scan->list[n * 17] < info->rssi) {
+				report = true;
+				scan->list[n * 17] = (uint8_t) info->rssi;
+			}
+
+		} else if (!memcmp(&scan->list[n * 17 + 1], zero, 16)) {
+
+			/* Found Empty slot */
+			report = true;
+			scan->list[n * 17] = (uint8_t) info->rssi;
+			memcpy(&scan->list[n * 17 + 1], data, 16);
+		}
+
+		filled++;
+	}
+
+	if (!report)
+		return;
+
+	n = mesh_model_opcode_set(OP_REM_PROV_SCAN_REPORT, msg);
+	msg[n++] = (uint8_t) info->rssi;
+	memcpy(msg + n, data, len);
+	n += len;
+
+	/* Always return oob_info, even if it wasn't in beacon */
+	if (len == 16) {
+		l_put_le16(0, msg + n);
+		n += 2;
+	}
+
+	goto send_report;
+
+extended_scan:
+	if (data[0] == BT_AD_MESH_BEACON && !data[1]) {
+		if (len != 18 && len != 20 && len != 24)
+			return;
+
+		/* Check UUID */
+		if (memcmp(data + 2, scan->uuid, 16))
+			return;
+
+		/* Zero AD list if prior data RXed from different bd_addr */
+		if (memcmp(scan->addr, info->addr, 6)) {
+			scan->list[0] = 0;
+			scan->rxed_ads = 0;
+		}
+
+		memcpy(scan->addr, info->addr, 6);
+		scan->fltr = true;
+
+		if (len >= 20)
+			scan->oob_info = l_get_le16(data + 18);
+
+		if (scan->rxed_ads != scan->ext_cnt)
+			return;
+
+
+	} else if (data[0] != BT_AD_MESH_BEACON) {
+		if (!scan->fltr || !memcmp(scan->addr, info->addr, 6)) {
+			i = 0;
+			while (scan->list[i]) {
+				/* check if seen */
+				if (scan->list[i + 1] == data[0])
+					return;
+
+				i += scan->list[i] + 1;
+			}
+
+			/* Overflow Protection */
+			if (i + len + 1 > EXT_LIST_SIZE)
+				return;
+
+			scan->list[i] = len;
+			scan->list[i + len + 1] = 0;
+			memcpy(scan->list + i + 1, data, len);
+			scan->rxed_ads++;
+		}
+
+		if (scan->rxed_ads != scan->ext_cnt)
+			return;
+
+	} else
+		return;
+
+	n = mesh_model_opcode_set(OP_REM_PROV_EXT_SCAN_REPORT, msg);
+	msg[n++] = PB_REM_ERR_SUCCESS;
+	memcpy(msg + n, scan->uuid, 16);
+	n += 16;
+	l_put_le16(scan->oob_info, msg + n);
+	n += 2;
+
+	i = 0;
+	while (scan->list[i]) {
+		msg[n++] = scan->list[i];
+		memcpy(msg + n, &scan->list[i + 1], scan->list[i]);
+		n += scan->list[i];
+		i += scan->list[i];
+	}
+
+send_report:
+	print_packet("App Tx", msg, n);
+	mesh_model_send(scan->node, 0, scan->client, APP_IDX_DEV_LOCAL,
+				scan->net_idx, DEFAULT_TTL, true, n, msg);
+
+	/* Clean-up if we are done reporting*/
+	if (filled == scan->scanned_limit || scan->ext_cnt)
+		remprv_scan_cancel(NULL, scan);
+}
+
+static bool register_ext_ad_type(uint8_t ad_type, struct rem_scan_data *scan)
+{
+	uint8_t short_ad;
+
+	switch (ad_type) {
+	case BT_AD_MESH_PROV:
+	case BT_AD_UUID16_SOME:
+	case BT_AD_UUID32_SOME:
+	case BT_AD_UUID128_SOME:
+	case BT_AD_NAME_SHORT:
+		/* Illegal Requests */
+		return false;
+
+	case BT_AD_UUID16_ALL:
+	case BT_AD_UUID32_ALL:
+	case BT_AD_UUID128_ALL:
+	case BT_AD_NAME_COMPLETE:
+		/* Automatically get short versions */
+		short_ad = ad_type - 1;
+		mesh_io_register_recv_cb(NULL, &short_ad, 1, scan_pkt, scan);
+
+		/* fall through */
+	default:
+		mesh_io_register_recv_cb(NULL, &ad_type, 1, scan_pkt, scan);
+
+		/* fall through */
+
+	case BT_AD_MESH_BEACON:
+		/* Ignored/auto request */
+		break;
+	}
+
+	return true;
+}
+
+static void link_active(void *user_data)
+{
+	struct rem_prov_data *prov = user_data;
+	uint8_t msg[5];
+	int n;
+
+	if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_LINK_OPENING)
+		return;
+
+	l_debug("Remote Link open confirmed");
+	prov->state = PB_REMOTE_STATE_LINK_ACTIVE;
+
+	n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg);
+	msg[n++] = PB_REM_ERR_SUCCESS;
+	msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE;
+
+	mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+				prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+bool register_nppi_acceptor(mesh_prov_open_func_t open_cb,
+					mesh_prov_close_func_t close_cb,
+					mesh_prov_receive_func_t rx_cb,
+					mesh_prov_ack_func_t ack_cb,
+					void *user_data)
+{
+	struct rem_prov_data *prov = rpb_prov;
+
+	if (!prov || prov->nppi_proc == RPR_ADV)
+		return false;
+
+	prov->u.nppi.open_cb = open_cb;
+	prov->u.nppi.close_cb = close_cb;
+	prov->u.nppi.rx_cb = rx_cb;
+	prov->u.nppi.ack_cb = ack_cb;
+	prov->trans_data = user_data;
+
+	open_cb(user_data, srv_rx, prov, prov->nppi_proc);
+
+	l_idle_oneshot(link_active, prov, NULL);
+
+	return true;
+}
+
+static bool nppi_cmplt(void *user_data, uint8_t status,
+					struct mesh_prov_node_info *info)
+{
+	struct rem_prov_data *prov = user_data;
+
+	if (prov != rpb_prov)
+		return false;
+
+	/* Save new info to apply on Link Close */
+	prov->u.nppi.info = *info;
+	return true;
+}
+
+static bool start_dev_key_refresh(struct mesh_node *node, uint8_t nppi_proc,
+						struct rem_prov_data *prov)
+{
+	uint8_t num_ele = node_get_num_elements(node);
+
+	prov->nppi_proc = nppi_proc;
+	return acceptor_start(num_ele, NULL, 0x0001, 60, NULL, nppi_cmplt,
+									prov);
+}
+
+static bool remprv_srv_pkt(uint16_t src, uint16_t unicast, uint16_t app_idx,
+					uint16_t net_idx, const uint8_t *data,
+					uint16_t size, const void *user_data)
+{
+	struct rem_prov_data *prov = rpb_prov;
+	struct rem_scan_data *scan = rpb_scan;
+	struct mesh_node *node = (struct mesh_node *) user_data;
+	const uint8_t *pkt = data;
+	bool segmented = false;
+	uint32_t opcode;
+	uint8_t msg[69];
+	uint8_t status;
+	uint16_t n;
+
+	if (app_idx != APP_IDX_DEV_LOCAL)
+		return false;
+
+	if (mesh_model_opcode_get(pkt, size, &opcode, &n)) {
+		size -= n;
+		pkt += n;
+	} else
+		return false;
+
+	n = 0;
+
+	switch (opcode) {
+	default:
+		return false;
+
+	case OP_REM_PROV_SCAN_CAP_GET:
+		if (size != 0)
+			return true;
+
+		/* Compose Scan Info Status */
+		n = mesh_model_opcode_set(OP_REM_PROV_SCAN_CAP_STATUS, msg);
+		msg[n++] = PB_REMOTE_MAX_SCAN_QUEUE_SIZE;
+		msg[n++] = 1; /* Active Scanning Supported */
+		break;
+
+	case OP_REM_PROV_EXT_SCAN_START:
+		if (!size || !pkt[0])
+			return true;
+
+		/* Size check the message */
+		if (pkt[0] + 18 == size) {
+			/* Range check the Timeout */
+			if (!pkt[size - 1] || pkt[size - 1] > 5)
+				return true;
+		} else if (pkt[0] + 1 != size)
+			return true;
+
+		/* Get local device extended info */
+		if (pkt[0] + 18 != size) {
+			n = mesh_model_opcode_set(
+					OP_REM_PROV_EXT_SCAN_REPORT, msg);
+			msg[n++] = PB_REM_ERR_SUCCESS;
+			memcpy(msg + n, node_uuid_get(node), 16);
+			n += 16;
+			l_put_le16(0, msg + n);
+			n += 2;
+			size--;
+			pkt++;
+
+			while (size--) {
+				if (*pkt++ == BT_AD_NAME_COMPLETE) {
+					msg[n] = strlen(name) + 1;
+					if (msg[n] > sizeof(msg) - n - 1)
+						msg[n] = sizeof(msg) - n - 1;
+					n++;
+					msg[n++] = BT_AD_NAME_COMPLETE;
+					memcpy(&msg[n], name, msg[n - 2] - 1);
+					n += msg[n - 2] - 1;
+					goto send_pkt;
+				}
+			}
+
+			/* Send internal report */
+			l_debug("Send internal extended info %d", n);
+			goto send_pkt;
+		}
+
+		status = PB_REM_ERR_SUCCESS;
+		if (scan) {
+			if (scan->client != src || scan->node != node ||
+						scan->ext_cnt != pkt[0])
+				status = PB_REM_ERR_SCANNING_CANNOT_START;
+			else if (memcmp(scan->ext, pkt + 1, pkt[0]))
+				status = PB_REM_ERR_SCANNING_CANNOT_START;
+			else if (memcmp(scan->uuid, pkt + 2, 16))
+				status = PB_REM_ERR_SCANNING_CANNOT_START;
+		}
+
+		if (status != PB_REM_ERR_SUCCESS) {
+			n = mesh_model_opcode_set(OP_REM_PROV_EXT_SCAN_REPORT,
+									msg);
+			msg[n++] = status;
+			memset(msg + n, 0, 16);
+			n += 16;
+			segmented = true;
+			break;
+		}
+
+		/* Ignore extended requests while already scanning */
+		if (scan)
+			return true;
+
+		scan = (void *) l_new(uint8_t,
+					sizeof(struct rem_scan_data) + pkt[0]);
+
+		/* Validate and register Extended AD types */
+		for (n = 0; n < pkt[0]; n++) {
+			if (!register_ext_ad_type(pkt[1 + n], scan)) {
+				/* Invalid AD type detected -- Undo */
+				while (n--)
+					deregister_ext_ad_type(pkt[1 + n]);
+
+				l_free(scan);
+				return true;
+			}
+		}
+
+		rpb_scan = scan;
+		scan->client = src;
+		scan->net_idx = net_idx;
+		memcpy(scan->uuid, pkt + size - 17, 16);
+		scan->ext_cnt = pkt[0];
+		memcpy(scan->ext, pkt + 1, pkt[0]);
+		scan->list = l_malloc(EXT_LIST_SIZE);
+		scan->list[0] = 0;
+
+		mesh_io_register_recv_cb(NULL, prvb, sizeof(prvb),
+								scan_pkt, scan);
+
+		scan->timeout = l_timeout_create(pkt[size-1],
+						remprv_scan_cancel, scan, NULL);
+		return true;
+
+	case OP_REM_PROV_SCAN_START:
+		if (size != 2 && size != 18)
+			return true;
+
+		/* Reject Timeout of Zero */
+		if (!pkt[1])
+			return true;
+
+		status = PB_REM_ERR_SUCCESS;
+		if (scan) {
+			if (scan->ext_cnt || scan->client != src ||
+							scan->node != node)
+				status = PB_REM_ERR_SCANNING_CANNOT_START;
+			else if (!!(scan->fltr) != !!(size != 18))
+				status = PB_REM_ERR_SCANNING_CANNOT_START;
+			else if (scan->fltr && memcmp(scan->uuid, pkt + 2, 16))
+				status = PB_REM_ERR_SCANNING_CANNOT_START;
+		}
+
+		if (status != PB_REM_ERR_SUCCESS) {
+			n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STATUS, msg);
+			msg[n++] = status;
+			msg[n++] = scan ? scan->state : 0;
+			msg[n++] = scan ? scan->scanned_limit :
+						PB_REMOTE_MAX_SCAN_QUEUE_SIZE;
+			msg[n++] = scan ? scan->to_secs : 0;
+			break;
+		}
+
+		if (!scan)
+			scan = l_new(struct rem_scan_data, 1);
+
+		rpb_scan = scan;
+
+		if (size == 18) {
+			memcpy(scan->uuid, pkt + 2, 16);
+			scan->fltr = true;
+			scan->state = 0x02; /* Limited */
+		} else {
+			memset(scan->uuid, 0, 16);
+			scan->fltr = false;
+			scan->state = 0x01; /* Unlimited */
+		}
+
+		scan->client = src;
+		scan->net_idx = net_idx;
+		scan->node = node;
+
+		if (!scan->list)
+			scan->list = l_new(uint8_t,
+					23 * PB_REMOTE_MAX_SCAN_QUEUE_SIZE);
+
+		mesh_io_register_recv_cb(NULL, prvb, 2, scan_pkt, scan);
+
+		scan->to_secs = pkt[1];
+
+		if (pkt[0])
+			scan->scanned_limit = pkt[0];
+		else
+			scan->scanned_limit = PB_REMOTE_MAX_SCAN_QUEUE_SIZE;
+
+		scan->timeout = l_timeout_create(pkt[1],
+					remprv_scan_cancel, scan, NULL);
+
+		/* fall through */
+
+	case OP_REM_PROV_SCAN_GET:
+		/* Compose Scan Status */
+		n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STATUS, msg);
+		msg[n++] = PB_REM_ERR_SUCCESS;
+		msg[n++] = scan ? scan->state : 0;
+		msg[n++] = scan ? scan->scanned_limit :
+						PB_REMOTE_MAX_SCAN_QUEUE_SIZE;
+		msg[n++] = scan ? scan->to_secs : 0;
+		break;
+
+	case OP_REM_PROV_SCAN_STOP:
+		if (size != 0 || !scan)
+			return true;
+
+		remprv_scan_cancel(NULL, scan);
+		return true;
+
+	case OP_REM_PROV_LINK_GET:
+		if (size != 0 || !prov)
+			return true;
+
+		send_prov_status(prov, PB_REM_ERR_SUCCESS);
+		return true;
+
+	case OP_REM_PROV_LINK_OPEN:
+		/* Sanity check args */
+		if (size != 16 && size != 17 && size != 1)
+			return true;
+
+		if (size == 17 && (pkt[16] == 0 || pkt[16] > 0x3c))
+			return true;
+
+		if (size == 1 && pkt[0] > 0x02)
+			return true;
+
+		if (prov) {
+			if (prov->client != src || prov->node != node ||
+				(size == 1 && prov->nppi_proc != pkt[0]) ||
+				(size >= 16 && (prov->nppi_proc != RPR_ADV ||
+					memcmp(prov->u.adv.uuid, pkt, 16)))) {
+
+				/* Send Reject (in progress) */
+				send_prov_status(prov, PB_REM_ERR_CANNOT_OPEN);
+				n = mesh_model_opcode_set(
+						OP_REM_PROV_LINK_STATUS, msg);
+				msg[n++] = PB_REM_ERR_CANNOT_OPEN;
+				msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE;
+				break;
+			}
+
+			/* Send redundant  Success */
+			send_prov_status(prov, PB_REM_ERR_SUCCESS);
+			return true;
+		}
+
+		if (scan && scan->client != src && scan->node != node) {
+			n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg);
+			msg[n++] = PB_REM_ERR_CANNOT_OPEN;
+			msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE;
+			break;
+		}
+
+		print_packet("Remote Prov Link Open", pkt, size);
+
+		remprv_scan_cancel(NULL, scan);
+
+		rpb_prov = prov = l_new(struct rem_prov_data, 1);
+		prov->client = src;
+		prov->net_idx = net_idx;
+		prov->node = node;
+		prov->state = PB_REMOTE_STATE_LINK_OPENING;
+
+		if (size == 1) {
+			status = start_dev_key_refresh(node, pkt[0], prov);
+
+		} else {
+			if (size == 17)
+				prov->timeout = l_timeout_create(pkt[16],
+						remprv_prov_cancel, prov, NULL);
+
+
+			prov->nppi_proc = RPR_ADV;
+			memcpy(prov->u.adv.uuid, pkt, 16);
+			status = pb_adv_reg(true, srv_open, srv_close, srv_rx,
+							srv_ack, pkt, prov);
+		}
+
+		if (status)
+			send_prov_status(prov, PB_REM_ERR_SUCCESS);
+		else {
+			n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg);
+			msg[n++] = PB_REM_ERR_CANNOT_OPEN;
+			msg[n++] = PB_REMOTE_STATE_IDLE;
+			remprv_prov_cancel(NULL, prov);
+		}
+
+		return true;
+
+	case OP_REM_PROV_LINK_CLOSE:
+		if (size != 1)
+			return true;
+
+		if (!prov || prov->node != node || prov->client != src)
+			return true;
+
+		prov->state = PB_REMOTE_STATE_LINK_CLOSING;
+		mesh_io_send_cancel(NULL, &pkt_filter, sizeof(pkt_filter));
+		send_prov_status(prov, PB_REM_ERR_SUCCESS);
+		if (pkt[0] == 0x02) {
+			msg[0] = PROV_FAILED;
+			msg[1] = PROV_ERR_CANT_ASSIGN_ADDR;
+			if (prov->nppi_proc == RPR_ADV)
+				prov->u.adv.tx(prov->trans_data, msg, 2);
+			else
+				prov->u.nppi.rx_cb(prov->trans_data, msg, 2);
+		}
+
+		if (prov->nppi_proc == RPR_ADV)
+			pb_adv_unreg(prov);
+
+		else if (prov->nppi_proc <= RPR_COMP) {
+			/* Hard or Soft refresh of local node, based on NPPI */
+			node_refresh(prov->node, (prov->nppi_proc == RPR_ADDR),
+							&prov->u.nppi.info);
+		}
+
+		remprv_prov_cancel(NULL, prov);
+
+		return true;
+
+	case OP_REM_PROV_PDU_SEND:
+		if (!prov || prov->node != node || prov->client != src)
+			return true;
+
+		if (size < 2)
+			return true;
+
+
+		prov->cli_pdu_num = *pkt++;
+		size--;
+		prov->state = PB_REMOTE_STATE_OB_PKT_TX;
+
+		if (prov->nppi_proc == RPR_ADV)
+			prov->u.adv.tx(prov->trans_data, pkt, size);
+		else {
+			srv_ack(prov, prov->cli_pdu_num);
+			prov->u.nppi.rx_cb(prov->trans_data, pkt, size);
+		}
+
+		return true;
+	}
+
+send_pkt:
+	l_info("PB-SVR: src %4.4x dst %4.4x", unicast, src);
+	print_packet("App Tx", msg, n);
+	mesh_model_send(node, 0, src, APP_IDX_DEV_LOCAL,
+				net_idx, DEFAULT_TTL, segmented, n, msg);
+
+	return true;
+}
+
+static void remprv_srv_unregister(void *user_data)
+{
+}
+
+static const struct mesh_model_ops ops = {
+	.unregister = remprv_srv_unregister,
+	.recv = remprv_srv_pkt,
+	.bind = NULL,
+	.sub = NULL,
+	.pub = NULL
+};
+
+void remote_prov_server_init(struct mesh_node *node, uint8_t ele_idx)
+{
+	mesh_model_register(node, ele_idx, REM_PROV_SRV_MODEL, &ops, node);
+}
diff --git a/mesh/remprv.h b/mesh/remprv.h
new file mode 100644
index 000000000..ab7e32b2d
--- /dev/null
+++ b/mesh/remprv.h
@@ -0,0 +1,78 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2023  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.
+ *
+ */
+
+#define REM_PROV_SRV_MODEL	SET_ID(SIG_VENDOR, 0x0004)
+#define REM_PROV_CLI_MODEL	SET_ID(SIG_VENDOR, 0x0005)
+
+#define PB_REMOTE_MAX_SCAN_QUEUE_SIZE	5
+
+#define PB_REMOTE_STATE_IDLE		0x00
+#define PB_REMOTE_STATE_LINK_OPENING	0x01
+#define PB_REMOTE_STATE_LINK_ACTIVE	0x02
+#define PB_REMOTE_STATE_OB_PKT_TX	0x03
+#define PB_REMOTE_STATE_LINK_CLOSING	0x04
+
+#define PB_REMOTE_TYPE_LOCAL	0x01
+#define PB_REMOTE_TYPE_ADV	0x02
+#define PB_REMOTE_TYPE_GATT	0x04
+
+#define PB_REMOTE_SCAN_TYPE_NONE	0x00
+#define PB_REMOTE_SCAN_TYPE_UNLIMITED	0x01
+#define PB_REMOTE_SCAN_TYPE_LIMITED	0x02
+#define PB_REMOTE_SCAN_TYPE_DETAILED	0x03
+
+/* Remote Provisioning Opcode List */
+#define OP_REM_PROV_SCAN_CAP_GET	0x804F
+#define OP_REM_PROV_SCAN_CAP_STATUS	0x8050
+#define OP_REM_PROV_SCAN_GET		0x8051
+#define OP_REM_PROV_SCAN_START		0x8052
+#define OP_REM_PROV_SCAN_STOP		0x8053
+#define OP_REM_PROV_SCAN_STATUS		0x8054
+#define OP_REM_PROV_SCAN_REPORT		0x8055
+#define OP_REM_PROV_EXT_SCAN_START	0x8056
+#define OP_REM_PROV_EXT_SCAN_REPORT	0x8057
+#define OP_REM_PROV_LINK_GET		0x8058
+#define OP_REM_PROV_LINK_OPEN		0x8059
+#define OP_REM_PROV_LINK_CLOSE		0x805A
+#define OP_REM_PROV_LINK_STATUS		0x805B
+#define OP_REM_PROV_LINK_REPORT		0x805C
+#define OP_REM_PROV_PDU_SEND		0x805D
+#define OP_REM_PROV_PDU_OB_REPORT	0x805E
+#define OP_REM_PROV_PDU_REPORT		0x805F
+
+/* Remote Provisioning Errors */
+#define PB_REM_ERR_SUCCESS			0x00
+#define PB_REM_ERR_SCANNING_CANNOT_START	0x01
+#define PB_REM_ERR_INVALID_STATE		0x02
+#define PB_REM_ERR_LIMITED_RESOURCES		0x03
+#define PB_REM_ERR_CANNOT_OPEN			0x04
+#define PB_REM_ERR_OPEN_FAILED			0x05
+#define PB_REM_ERR_CLOSED_BY_DEVICE		0x06
+#define PB_REM_ERR_CLOSED_BY_SERVER		0x07
+#define PB_REM_ERR_CLOSED_BY_CLIENT		0x08
+#define PB_REM_ERR_CLOSED_CANNOT_RX_PDU		0x09
+#define PB_REM_ERR_CLOSED_CANNOT_TX_PDU		0x0A
+
+void remote_prov_server_init(struct mesh_node *node, uint8_t ele_idx);
+void remote_prov_client_init(struct mesh_node *node, uint8_t ele_idx);
+bool register_nppi_acceptor(mesh_prov_open_func_t open_cb,
+					mesh_prov_close_func_t close_cb,
+					mesh_prov_receive_func_t rx_cb,
+					mesh_prov_ack_func_t ack_cb,
+					void *user_data);
-- 
2.39.1




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux