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

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

 



Tiny fix and a question below

On Tue, 2023-01-24 at 12:26 -0800, Brian Gix wrote:
> From: Brian Gix <brian.gix@xxxxxxxxx>
> 
> Add Remote Provisioning Server
> Add Remote Provisioning Client
> Remove local scanning/provisioning
> Add delete-all dev key function
> Add NPPI procedures
> ---
>  Makefile.mesh           |   1 +
>  mesh/cfgmod-server.c    |   2 +-
>  mesh/keyring.c          |  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);





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

  Powered by Linux