diff mbox series

[BlueZ,v4,02/13] mesh: Add Remote Provisioning

Message ID 20230124202616.310544-3-brian.gix@gmail.com
State Superseded
Headers show
Series Mesh v1.1 additions | expand

Commit Message

Brian Gix Jan. 24, 2023, 8:26 p.m. UTC
From: Brian Gix <brian.gix@intel.com>

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          |  29 +-
 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, 2246 insertions(+), 367 deletions(-)
 create mode 100644 mesh/remprv-server.c
 create mode 100644 mesh/remprv.h

Comments

Inga Stotland Jan. 25, 2023, 10:27 p.m. UTC | #1
Tiny fix and a question below

On Tue, 2023-01-24 at 12:26 -0800, Brian Gix wrote:
> From: Brian Gix <brian.gix@intel.com>
> 
> 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          |  29 +-
>  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, 2246 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..894fb14fa 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)
> @@ -295,6 +295,7 @@ bool keyring_get_remote_dev_key(struct mesh_node
> *node, uint16_t unicast,
>                 close(fd);
>         }
>  
> +

ingas: Did you accidentally add an extra empty line?

>         return result;
>  }
>  
> @@ -371,6 +372,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);
> +

ingas: Just checking if this is your intent here: delete all the DevKey
entries from the secondary elements and keep the one associated with
the primary element?

> +       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(nod
> e),
>                                                 MESH_PROVISIONER_INTE
> RFACE,
>                                                 "AddNodeComplete");
> +       else
> +               msg = l_dbus_message_new_method_call(dbus,
> node_get_owner(node),
> +                                               node_get_app_path(nod
> e),
> +                                               MESH_PROVISIONER_INTE
> RFACE,
> +                                               "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,
> +                                                               &prim
> ary))
>                 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_INTE
> RFACE,
>                                                 "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_INTE
> RFACE,
> +                                               "RequestReprovData");
> +
> +               l_dbus_message_set_arguments(msg, "qy", pending-
> >original,
> +                                                               num_e
> le);
> +       } 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",
> +                                                               &subi
> dx)) {
> +                               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",
> +                                                               &serv
> er)) {
> +                               if (server < 0x8000)
> +                                       failed = false;
> +                       }
> +               } else if (!strcmp(key, "Subnet")) {
> +                       if (l_dbus_message_iter_get_variant(&var,
> "q",
> +                                                               &subi
> dx)) {
> +                               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_INTE
> RFACE,
>                                                 "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.serv
> er)) {
> +                               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_inte
> rface,
>                                                 NULL, false)) {
> -               l_info("Unable to register %s interface",
> +               l_debug("Unable to register %s interface",
>                                                 MESH_MANAGEMENT_INTER
> FACE);
>                 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(n
> ode->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,
> -
>                                                                "publis
> h"))
> +                                                               publi
> sh))
>                 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,
> -
>                                                                "subscr
> ibe"))
> +                                                               subsc
> ribe))
>                 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,
>                                                                 uint1
> 6_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_nu
> m));
> +
> +       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,
>                                                                 uint1
> 6_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,
>                                                                 uint1
> 6_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..e24758093
> --- /dev/null
> +++ b/mesh/remprv-server.c
> @@ -0,0 +1,907 @@
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright (C) 2020  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_QU
> EUE_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_QU
> EUE_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_STAT
> US, 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..49b4e2c7c
> --- /dev/null
> +++ b/mesh/remprv.h
> @@ -0,0 +1,78 @@
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright (C) 2020  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);
diff mbox series

Patch

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..894fb14fa 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)
@@ -295,6 +295,7 @@  bool keyring_get_remote_dev_key(struct mesh_node *node, uint16_t unicast,
 		close(fd);
 	}
 
+
 	return result;
 }
 
@@ -371,6 +372,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..e24758093
--- /dev/null
+++ b/mesh/remprv-server.c
@@ -0,0 +1,907 @@ 
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2020  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..49b4e2c7c
--- /dev/null
+++ b/mesh/remprv.h
@@ -0,0 +1,78 @@ 
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2020  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);