mesh: Add Remote Provisioning
authorBrian Gix <brian.gix@intel.com>
Tue, 25 Oct 2022 21:33:48 +0000 (14:33 -0700)
committerAyush Garg <ayush.garg@samsung.com>
Fri, 5 Jan 2024 10:11:34 +0000 (15:41 +0530)
Add Remote Provisioning Server
Add Remote Provisioning Client
Remove local scanning/provisioning
Add delete-all dev key function
Add NPPI procedures

18 files changed:
Makefile.mesh
mesh/cfgmod-server.c
mesh/keyring.c
mesh/keyring.h
mesh/manager.c
mesh/mesh-config-json.c
mesh/mesh-config.h
mesh/model.c
mesh/node.c
mesh/node.h
mesh/pb-adv.c
mesh/pb-adv.h
mesh/prov-acceptor.c
mesh/prov-initiator.c
mesh/prov.h
mesh/provision.h
mesh/remprv-server.c [new file with mode: 0644]
mesh/remprv.h [new file with mode: 0644]

index 327eb3f..026b481 100644 (file)
@@ -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 \
index be90ef8..3d7efc4 100644 (file)
@@ -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
 };
 
index 6847ab5..70691d0 100644 (file)
@@ -31,9 +31,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)
@@ -372,6 +372,28 @@ bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast,
        return true;
 }
 
+bool keyring_del_remote_dev_key_all(struct mesh_node *node, uint16_t unicast)
+{
+       uint8_t dev_key[16];
+       uint8_t test_key[16];
+       uint8_t cnt = 1;
+
+       if (!keyring_get_remote_dev_key(node, unicast, dev_key))
+               return false;
+
+       while (keyring_get_remote_dev_key(node, unicast + cnt, test_key)) {
+               if (memcmp(dev_key, test_key, sizeof(dev_key)))
+                       break;
+
+               cnt++;
+       }
+
+       if (cnt > 1)
+               return keyring_del_remote_dev_key(node, unicast + 1, cnt - 1);
+
+       return true;
+}
+
 static DIR *open_key_dir(const char *node_path, const char *key_dir_name)
 {
        char dir_path[PATH_MAX];
index ecf62cb..efc499a 100644 (file)
@@ -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);
index b1817a3..958c719 100644 (file)
 #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)
 {
@@ -104,7 +166,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);
@@ -117,14 +179,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) {
@@ -133,7 +195,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) {
@@ -142,13 +209,29 @@ static bool add_cmplt(void *user_data, uint8_t status,
                return false;
        }
 
-       msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
+       if (pending->transport > PB_NPPI_02)
+               msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
                                                node_get_app_path(node),
                                                MESH_PROVISIONER_INTERFACE,
                                                "AddNodeComplete");
+       else
+               msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
+                                               node_get_app_path(node),
+                                               MESH_PROVISIONER_INTERFACE,
+                                               "ReprovComplete");
 
        builder = l_dbus_message_builder_new(msg);
-       dbus_append_byte_array(builder, add_pending->uuid, 16);
+
+       if (pending->transport > PB_NPPI_02)
+               dbus_append_byte_array(builder, pending->uuid, 16);
+       else {
+               uint8_t nppi = (uint8_t) pending->transport;
+
+               l_dbus_message_builder_append_basic(builder, 'q',
+                                                       &pending->original);
+               l_dbus_message_builder_append_basic(builder, 'y', &nppi);
+       }
+
        l_dbus_message_builder_append_basic(builder, 'q', &info->unicast);
        l_dbus_message_builder_append_basic(builder, 'y', &info->num_ele);
        l_dbus_message_builder_finalize(builder);
@@ -163,47 +246,66 @@ static bool add_cmplt(void *user_data, uint8_t status,
 
 static void mgr_prov_data (struct l_dbus_message *reply, void *user_data)
 {
-       struct add_data *pending = user_data;
+       struct prov_remote_data *pending = user_data;
        uint16_t net_idx;
        uint16_t primary;
 
-       if (pending != add_pending)
+       if (pending != prov_pending)
                return;
 
        if (l_dbus_message_is_error(reply))
                return;
 
-       if (!l_dbus_message_get_arguments(reply, "qq", &net_idx, &primary))
+       if (pending->transport == PB_NPPI_01) {
+               /* If performing NPPI, we only get new primary unicast here */
+               if (!l_dbus_message_get_arguments(reply, "q", &primary))
+                       return;
+
+               net_idx = pending->net_idx;
+
+       } else if (!l_dbus_message_get_arguments(reply, "qq", &net_idx,
+                                                               &primary))
                return;
 
-       add_pending->primary = primary;
-       add_pending->net_idx = net_idx;
-       initiator_prov_data(net_idx, primary, add_pending);
+       pending->primary = primary;
+       pending->net_idx = net_idx;
+       initiator_prov_data(net_idx, primary, pending);
 }
 
 static bool add_data_get(void *user_data, uint8_t num_ele)
 {
-       struct add_data *pending = user_data;
+       struct prov_remote_data *pending = user_data;
        struct l_dbus_message *msg;
        struct l_dbus *dbus;
        const char *app_path;
        const char *sender;
 
-       if (pending != add_pending)
+       if (pending != prov_pending)
                return false;
 
        dbus = dbus_get_bus();
-       app_path = node_get_app_path(add_pending->node);
-       sender = node_get_owner(add_pending->node);
+       app_path = node_get_app_path(pending->node);
+       sender = node_get_owner(pending->node);
 
-       msg = l_dbus_message_new_method_call(dbus, sender, app_path,
+       if (pending->transport > PB_NPPI_02) {
+               msg = l_dbus_message_new_method_call(dbus, sender, app_path,
                                                MESH_PROVISIONER_INTERFACE,
                                                "RequestProvData");
 
-       l_dbus_message_set_arguments(msg, "y", num_ele);
-       l_dbus_send_with_reply(dbus, msg, mgr_prov_data, add_pending, NULL);
+               l_dbus_message_set_arguments(msg, "y", num_ele);
+       } else if (pending->transport == PB_NPPI_01) {
+               msg = l_dbus_message_new_method_call(dbus, sender, app_path,
+                                               MESH_PROVISIONER_INTERFACE,
+                                               "RequestReprovData");
+
+               l_dbus_message_set_arguments(msg, "qy", pending->original,
+                                                               num_ele);
+       } else
+               return false;
 
-       add_pending->num_ele = num_ele;
+       l_dbus_send_with_reply(dbus, msg, mgr_prov_data, pending, NULL);
+
+       pending->num_ele = num_ele;
 
        return true;
 }
@@ -215,15 +317,95 @@ static void add_start(void *user_data, int err)
        l_debug("Start callback");
 
        if (err == MESH_ERROR_NONE)
-               reply = l_dbus_message_new_method_return(add_pending->msg);
+               reply = l_dbus_message_new_method_return(prov_pending->msg);
        else
-               reply = dbus_error(add_pending->msg, MESH_ERROR_FAILED,
+               reply = dbus_error(prov_pending->msg, MESH_ERROR_FAILED,
                                "Failed to start provisioning initiator");
 
        l_dbus_send(dbus_get_bus(), reply);
-       l_dbus_message_unref(add_pending->msg);
+       l_dbus_message_unref(prov_pending->msg);
+
+       prov_pending->msg = NULL;
+}
+
+static struct l_dbus_message *reprovision_call(struct l_dbus *dbus,
+                                               struct l_dbus_message *msg,
+                                               void *user_data)
+{
+       struct mesh_node *node = user_data;
+       struct l_dbus_message_iter options, var;
+       struct l_dbus_message *reply;
+       struct mesh_net *net = node_get_net(node);
+       const char *key;
+       uint16_t subidx;
+       uint16_t server = 0;
+       uint8_t nppi = 0;
+
+       l_debug("Reprovision request");
+
+       if (!l_dbus_message_get_arguments(msg, "qa{sv}", &server, &options))
+               return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+       if (!IS_UNICAST(server))
+               return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad Unicast");
+
+       /* Default to nodes primary subnet index */
+       subidx = mesh_net_get_primary_idx(net);
+
+       /* Get Provisioning Options */
+       while (l_dbus_message_iter_next_entry(&options, &key, &var)) {
+               bool failed = true;
+
+               if (!strcmp(key, "NPPI")) {
+                       if (l_dbus_message_iter_get_variant(&var, "y", &nppi)) {
+                               if (nppi <= 2)
+                                       failed = false;
+                       }
+               } else if (!strcmp(key, "Subnet")) {
+                       if (l_dbus_message_iter_get_variant(&var, "q",
+                                                               &subidx)) {
+                               if (subidx <= MAX_KEY_IDX)
+                                       failed = false;
+                       }
+               }
+
+               if (failed)
+                       return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+                                                       "Invalid options");
+       }
+
+       /* AddNode cancels all outstanding Scanning from node */
+       manager_scan_cancel(node);
 
-       add_pending->msg = NULL;
+       /* Invoke Prov Initiator */
+       prov_pending = l_new(struct prov_remote_data, 1);
+
+       prov_pending->transport = nppi;
+       prov_pending->node = node;
+       prov_pending->original = server;
+       prov_pending->agent = node_get_agent(node);
+
+       if (!node_is_provisioner(node) || (prov_pending->agent == NULL)) {
+               reply = dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED,
+                                                       "Missing Interfaces");
+               goto fail;
+       }
+
+       prov_pending->msg = l_dbus_message_ref(msg);
+       initiator_start(prov_pending->transport, server, subidx, NULL, 99, 60,
+                                       prov_pending->agent, add_start,
+                                       add_data_get, add_cmplt, node,
+                                       prov_pending);
+
+       prov_pending->disc_watch = l_dbus_add_disconnect_watch(dbus,
+                                               node_get_owner(node),
+                                               prov_disc_cb, NULL, NULL);
+
+       return NULL;
+fail:
+       l_free(prov_pending);
+       prov_pending = NULL;
+       return reply;
 }
 
 static struct l_dbus_message *add_node_call(struct l_dbus *dbus,
@@ -231,55 +413,101 @@ static struct l_dbus_message *add_node_call(struct l_dbus *dbus,
                                                void *user_data)
 {
        struct mesh_node *node = user_data;
-       struct l_dbus_message_iter iter_uuid, options;
+       struct l_dbus_message_iter iter_uuid, options, var;
        struct l_dbus_message *reply;
+       struct mesh_net *net = node_get_net(node);
+       const char *key;
        uint8_t *uuid;
-       uint32_t n = 22;
+       uint32_t n = 0;
+       uint16_t subidx;
+       uint16_t sec = 60;
+       uint16_t server = 0;
 
        l_debug("AddNode request");
 
        if (!l_dbus_message_get_arguments(msg, "aya{sv}", &iter_uuid, &options))
                return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
 
-       if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n)
-                                                               || n != 16)
+       if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n) ||
+                                                                       n != 16)
                return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
                                                        "Bad device UUID");
 
-       /* Allow AddNode to cancel Scanning if from the same node */
-       if (scan_node) {
-               if (scan_node != node)
-                       return dbus_error(msg, MESH_ERROR_BUSY, NULL);
+       /* Default to nodes primary subnet index */
+       subidx = mesh_net_get_primary_idx(net);
 
-               scan_cancel(NULL, node);
+       /* Get Provisioning Options */
+       while (l_dbus_message_iter_next_entry(&options, &key, &var)) {
+               bool failed = true;
+
+               if (!strcmp(key, "Seconds")) {
+                       if (l_dbus_message_iter_get_variant(&var, "q", &sec))
+                               failed = false;
+               } else if (!strcmp(key, "Server")) {
+                       if (l_dbus_message_iter_get_variant(&var, "q",
+                                                               &server)) {
+                               if (server < 0x8000)
+                                       failed = false;
+                       }
+               } else if (!strcmp(key, "Subnet")) {
+                       if (l_dbus_message_iter_get_variant(&var, "q",
+                                                               &subidx)) {
+                               if (subidx <= MAX_KEY_IDX)
+                                       failed = false;
+                       }
+               }
+
+               if (failed)
+                       return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+                                                       "Invalid options");
        }
 
+       /* Device Key update/Composition update requires remote server */
+       if (!n && !server)
+               return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+                                                       "Invalid options");
+
+       /* If no server specified, use local */
+       if (!server)
+               server = node_get_primary(node);
+
+       /* AddNode cancels all outstanding Scanning from node */
+       manager_scan_cancel(node);
+
        /* Invoke Prov Initiator */
-       add_pending = l_new(struct add_data, 1);
-       memcpy(add_pending->uuid, uuid, 16);
-       add_pending->node = node;
-       add_pending->agent = node_get_agent(node);
+       prov_pending = l_new(struct prov_remote_data, 1);
+
+       if (n)
+               memcpy(prov_pending->uuid, uuid, 16);
+       else
+               uuid = NULL;
 
-       if (!node_is_provisioner(node) || (add_pending->agent == NULL)) {
+       prov_pending->transport = PB_ADV;
+       prov_pending->node = node;
+       prov_pending->agent = node_get_agent(node);
+
+       if (!node_is_provisioner(node) || (prov_pending->agent == NULL)) {
                l_debug("Provisioner: %d", node_is_provisioner(node));
-               l_debug("Agent: %p", add_pending->agent);
+               l_debug("Agent: %p", prov_pending->agent);
                reply = dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED,
                                                        "Missing Interfaces");
                goto fail;
        }
 
-       add_pending->msg = l_dbus_message_ref(msg);
-       initiator_start(PB_ADV, uuid, 99, 60, add_pending->agent, add_start,
-                               add_data_get, add_cmplt, node, add_pending);
+       prov_pending->msg = l_dbus_message_ref(msg);
+       initiator_start(PB_ADV, server, subidx, uuid, 99, sec,
+                                       prov_pending->agent, add_start,
+                                       add_data_get, add_cmplt, node,
+                                       prov_pending);
 
-       add_pending->disc_watch = l_dbus_add_disconnect_watch(dbus,
+       prov_pending->disc_watch = l_dbus_add_disconnect_watch(dbus,
                                                node_get_owner(node),
                                                prov_disc_cb, NULL, NULL);
 
        return NULL;
 fail:
-       l_free(add_pending);
-       add_pending = NULL;
+       l_free(prov_pending);
+       prov_pending = NULL;
        return reply;
 }
 
@@ -339,38 +567,50 @@ static struct l_dbus_message *delete_node_call(struct l_dbus *dbus,
        return l_dbus_message_new_method_return(msg);
 }
 
-static void prov_beacon_recv(void *user_data, struct mesh_io_recv_info *info,
+static void manager_scan_result(void *user_data, uint16_t server, bool ext,
                                        const uint8_t *data, uint16_t len)
 {
-       struct mesh_node *node = user_data;
+       struct scan_req node_svr = {
+               .node = user_data,
+               .server = server,
+       };
+       struct scan_req *req;
        struct l_dbus_message_builder *builder;
        struct l_dbus_message *msg;
        struct l_dbus *dbus;
        int16_t rssi;
 
-       if (scan_node != node || len < sizeof(scan_uuid) + 2 || data[1] != 0x00)
+       l_debug("scan_result %4.4x %p", server, user_data);
+       req = l_queue_find(scans, by_node_svr, &node_svr);
+       if (!req) {
+               l_debug("No scan_result req");
                return;
+       }
 
-       if (!memcmp(data + 2, scan_uuid, sizeof(scan_uuid))) {
-               if (info->rssi <= scan_rssi)
+       /* Filter repeats with weaker signal */
+       if (!memcmp(data + 1, req->uuid, sizeof(req->uuid))) {
+               if (!ext && ((int8_t) data[0] <= req->rssi)) {
+                       l_debug("Already Seen");
                        return;
+               }
        }
 
-       memcpy(scan_uuid, data + 2, sizeof(scan_uuid));
-       scan_rssi = info->rssi;
-       rssi = info->rssi;
+       if (!ext && ((int8_t) data[0] > req->rssi))
+               req->rssi = (int8_t) data[0];
 
+       rssi = req->rssi;
+       memcpy(req->uuid, data + 1, sizeof(req->uuid));
        dbus = dbus_get_bus();
-       msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
-                                               node_get_app_path(node),
+       msg = l_dbus_message_new_method_call(dbus, node_get_owner(req->node),
+                                               node_get_app_path(req->node),
                                                MESH_PROVISIONER_INTERFACE,
                                                "ScanResult");
 
        builder = l_dbus_message_builder_new(msg);
        l_dbus_message_builder_append_basic(builder, 'n', &rssi);
-       dbus_append_byte_array(builder, data + 2, len -2);
+       dbus_append_byte_array(builder, data + 1, len - 1);
        l_dbus_message_builder_enter_array(builder, "{sv}");
-       /* TODO: populate with options when defined */
+       append_dict_entry_basic(builder, "Server", "q", &server);
        l_dbus_message_builder_leave_array(builder);
        l_dbus_message_builder_finalize(builder);
        l_dbus_message_builder_destroy(builder);
@@ -382,27 +622,71 @@ static struct l_dbus_message *start_scan_call(struct l_dbus *dbus,
                                                struct l_dbus_message *msg,
                                                void *user_data)
 {
-       struct mesh_node *node = user_data;
-       uint16_t duration = 0;
-       struct mesh_io *io;
+       struct scan_req new_req = {
+               .node = user_data,
+               .server = 0,
+               .timeout = NULL,
+               .ext = false,
+       };
+       struct scan_req *req;
        struct mesh_net *net;
+       uint8_t *uuid, *ext = NULL;
+       uint8_t scan_req[21];
+       int n;
+       uint32_t ext_len;
+       uint32_t flen = 0;
+       uint16_t sec = 60;
        const char *key;
        struct l_dbus_message_iter options, var;
        const char *sender = l_dbus_message_get_sender(msg);
 
-       if (strcmp(sender, node_get_owner(node)))
+       if (strcmp(sender, node_get_owner(new_req.node)))
                return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
 
        if (!l_dbus_message_get_arguments(msg, "a{sv}", &options))
                return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
 
+       if (!node_is_provisioner(new_req.node))
+               return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+
+       net = node_get_net(new_req.node);
+       new_req.net_idx = mesh_net_get_primary_idx(net);
+       memset(new_req.uuid, 0, sizeof(new_req.uuid));
+
        while (l_dbus_message_iter_next_entry(&options, &key, &var)) {
                bool failed = true;
 
                if (!strcmp(key, "Seconds")) {
-                       if (l_dbus_message_iter_get_variant(&var, "q",
-                                                           &duration)) {
+                       if (l_dbus_message_iter_get_variant(&var, "q", &sec))
                                failed = false;
+               } else if (!strcmp(key, "Subnet")) {
+                       if (l_dbus_message_iter_get_variant(&var, "q",
+                                                       &new_req.net_idx)) {
+                               if (new_req.net_idx <= MAX_KEY_IDX)
+                                       failed = false;
+                       }
+               } else if (!strcmp(key, "Server")) {
+                       if (l_dbus_message_iter_get_variant(&var, "q",
+                                                       &new_req.server)) {
+                               if (new_req.server < 0x8000)
+                                       failed = false;
+                       }
+               } else if (!strcmp(key, "Filter")) {
+                       if (l_dbus_message_iter_get_variant(&var, "ay", &var)) {
+                               if (l_dbus_message_iter_get_fixed_array(&var,
+                                                               &uuid, &flen)) {
+                                       if (flen == 16) {
+                                               memcpy(new_req.uuid, uuid,
+                                                                       flen);
+                                               failed = false;
+                                       }
+                               }
+                       }
+               } else if (!strcmp(key, "Extended")) {
+                       if (l_dbus_message_iter_get_variant(&var, "ay", &var)) {
+                               if (l_dbus_message_iter_get_fixed_array(&var,
+                                                               &ext, &ext_len))
+                                       failed = false;
                        }
                }
 
@@ -411,27 +695,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);
 }
@@ -446,12 +754,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);
 }
@@ -816,6 +1119,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,
@@ -851,7 +1156,7 @@ bool manager_dbus_init(struct l_dbus *bus)
        if (!l_dbus_register_interface(bus, MESH_MANAGEMENT_INTERFACE,
                                                setup_management_interface,
                                                NULL, false)) {
-               l_info("Unable to register %s interface",
+               l_debug("Unable to register %s interface",
                                                MESH_MANAGEMENT_INTERFACE);
                return false;
        }
@@ -861,8 +1166,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);
 }
index af30c17..ce4d328 100644 (file)
@@ -59,6 +59,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;
@@ -135,14 +162,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);
@@ -161,14 +188,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);
@@ -190,7 +217,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);
@@ -299,7 +326,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);
@@ -315,7 +342,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);
@@ -337,7 +364,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);
@@ -397,7 +424,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);
@@ -461,7 +506,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)
@@ -485,7 +530,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))
@@ -517,7 +562,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)
@@ -548,7 +593,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);
@@ -599,7 +644,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);
 
@@ -617,14 +662,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);
@@ -649,7 +694,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);
@@ -668,7 +713,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);
@@ -683,20 +728,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);
@@ -720,7 +800,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);
 
@@ -735,7 +815,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))
@@ -745,7 +825,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);
@@ -771,7 +851,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 */
@@ -805,13 +885,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);
 }
@@ -841,7 +921,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;
 
@@ -855,7 +935,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);
@@ -888,13 +968,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);
 }
@@ -964,7 +1044,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);
@@ -999,9 +1079,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))
@@ -1094,7 +1175,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);
@@ -1113,29 +1194,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;
                }
@@ -1188,7 +1272,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;
@@ -1212,13 +1296,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;
@@ -1324,7 +1408,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))
@@ -1387,7 +1471,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)) {
@@ -1461,11 +1545,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;
        }
 }
 
@@ -1523,7 +1607,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);
@@ -1559,8 +1643,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);
 
@@ -1600,8 +1684,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);
@@ -1609,10 +1693,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);
 }
@@ -1664,11 +1748,11 @@ static struct mesh_config *create_config(const char *cfg_path,
                return NULL;
 
        /* Sequence number */
-       json_object_object_add(jnode, "sequenceNumber",
+       json_object_object_add(jnode, sequenceNumber,
                                        json_object_new_int(node->seq_number));
 
        /* Default TTL */
-       json_object_object_add(jnode, "defaultTTL",
+       json_object_object_add(jnode, defaultTTL,
                                                json_object_new_int(node->ttl));
 
        /* Elements */
@@ -1703,11 +1787,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);
 
@@ -1725,6 +1809,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)
 {
@@ -1769,7 +1902,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);
@@ -1780,7 +1913,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)
@@ -1804,14 +1937,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) {
@@ -1843,16 +1976,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;
@@ -1879,8 +2012,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);
 
@@ -1912,23 +2045,23 @@ bool mesh_config_model_pub_del(struct mesh_config *cfg, uint16_t addr,
                                                uint32_t mod_id, bool vendor)
 {
        if (!cfg || !delete_model_property(cfg->jnode, addr, mod_id, vendor,
-                                                               "publish"))
+                                                               publish))
                return false;
 
        return save_config(cfg->jnode, cfg->node_dir_path);
 }
 
-static void del_page(json_object *jarray, uint8_t page)
+static bool del_page(json_object *jarray, uint8_t page)
 {
        char buf[3];
        int i, len, ret;
 
        if (!jarray)
-               return;
+               return false;
 
        ret = snprintf(buf, 3, "%2.2x", page);
        if (ret < 0)
-               return;
+               return false;
 
        len = json_object_array_length(jarray);
 
@@ -1939,10 +2072,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,
@@ -1985,56 +2137,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)
@@ -2065,7 +2167,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;
 
@@ -2079,7 +2181,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);
@@ -2108,7 +2210,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) {
@@ -2123,7 +2225,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);
 }
@@ -2132,7 +2234,7 @@ bool mesh_config_model_sub_del_all(struct mesh_config *cfg, uint16_t addr,
                                                uint32_t mod_id, bool vendor)
 {
        if (!cfg || !delete_model_property(cfg->jnode, addr, mod_id, vendor,
-                                                               "subscribe"))
+                                                               subscribe))
                return false;
 
        return save_config(cfg->jnode, cfg->node_dir_path);
@@ -2162,7 +2264,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);
 }
@@ -2185,13 +2287,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);
 }
@@ -2206,14 +2308,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;
 
        /*
@@ -2263,8 +2365,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);
        }
@@ -2274,7 +2376,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);
index 4207758..ed1b610 100644 (file)
@@ -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);
index d48e6ef..e2babea 100644 (file)
@@ -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;
 }
index a2c8254..071f74e 100644 (file)
 #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"
@@ -364,6 +366,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;
 }
 
@@ -506,6 +517,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;
@@ -567,12 +582,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])
@@ -802,7 +883,7 @@ uint8_t node_friend_mode_get(struct mesh_node *node)
        return node->friend;
 }
 
-static uint16_t generate_node_comp(struct mesh_node *node, uint8_t *buf,
+static uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf,
                                                                uint16_t sz)
 {
        uint16_t n, features, num_ele = 0;
@@ -912,6 +993,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;
@@ -939,7 +1035,22 @@ static bool create_node_config(struct mesh_node *node, const uint8_t uuid[16])
        return node->cfg != NULL;
 }
 
-static bool set_node_comp(struct mesh_node *node, uint8_t page_num,
+static void node_del_comp(struct mesh_node *node, uint8_t page_num)
+{
+       struct mesh_config_comp_page *page;
+
+       if (!node)
+               return;
+
+       page = l_queue_remove_if(node->pages, match_page,
+                                               L_UINT_TO_PTR(page_num));
+
+       l_free(page);
+
+       mesh_config_comp_page_del(node->cfg, page_num);
+}
+
+static bool node_set_comp(struct mesh_node *node, uint8_t page_num,
                                        const uint8_t *data, uint16_t len)
 {
        struct mesh_config_comp_page *page;
@@ -961,16 +1072,6 @@ static bool set_node_comp(struct mesh_node *node, uint8_t page_num,
        return mesh_config_comp_page_add(node->cfg, page_num, page->data, len);
 }
 
-static bool create_node_comp(struct mesh_node *node)
-{
-       uint16_t len;
-       uint8_t comp[MAX_MSG_LEN - 2];
-
-       len = generate_node_comp(node, comp, sizeof(comp));
-
-       return set_node_comp(node, 0, comp, len);
-}
-
 const uint8_t *node_get_comp(struct mesh_node *node, uint8_t page_num,
                                                                uint16_t *len)
 {
@@ -992,6 +1093,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;
@@ -1006,9 +1108,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)
@@ -1187,8 +1293,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:
@@ -1249,6 +1360,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])
@@ -1292,10 +1412,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;
 
@@ -1343,39 +1467,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;
@@ -1385,6 +1529,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);
 
@@ -1401,6 +1573,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);
@@ -1416,8 +1592,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;
@@ -1516,16 +1696,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) {
index 1283c08..b4a44ee 100644 (file)
@@ -41,6 +41,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);
@@ -92,3 +94,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);
index 180b162..385d81d 100644 (file)
@@ -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;
 
index 5b1e03d..e33ba8e 100644 (file)
@@ -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);
index bf8c573..fd9d4cd 100644 (file)
@@ -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;
index c625775..653f3ae 100644 (file)
 #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);
+}
index 99e864c..e86668f 100644 (file)
@@ -39,14 +39,14 @@ enum mesh_prov_mode {
 
 struct mesh_prov;
 
-typedef void (*prov_trans_tx_t)(void *trans_data, void *data, uint16_t len);
+typedef void (*prov_trans_tx_t)(void *tx_data, const void *data, uint16_t len);
 typedef void (*mesh_prov_open_func_t)(void *user_data, prov_trans_tx_t trans_tx,
                                        void *trans_data, uint8_t trans_type);
 
 typedef void (*mesh_prov_close_func_t)(void *user_data, uint8_t reason);
 typedef void (*mesh_prov_send_func_t)(bool success, struct mesh_prov *prov);
 typedef void (*mesh_prov_ack_func_t)(void *user_data, uint8_t msg_num);
-typedef void (*mesh_prov_receive_func_t)(void *user_data, const uint8_t *data,
+typedef void (*mesh_prov_receive_func_t)(void *user_data, const void *data,
                                                                uint16_t size);
 
 
index 1634c4d..cfeb6de 100644 (file)
@@ -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 (file)
index 0000000..85af65d
--- /dev/null
@@ -0,0 +1,907 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2023  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/time.h>
+#include <ell/ell.h>
+
+#include "src/shared/ad.h"
+
+#include "mesh/mesh-defs.h"
+#include "mesh/mesh-io.h"
+#include "mesh/util.h"
+#include "mesh/node.h"
+#include "mesh/net.h"
+#include "mesh/appkey.h"
+#include "mesh/model.h"
+#include "mesh/prov.h"
+#include "mesh/provision.h"
+#include "mesh/pb-adv.h"
+#include "mesh/remprv.h"
+
+#define EXT_LIST_SIZE  60
+
+#define RPR_DEV_KEY    0x00
+#define RPR_ADDR       0x01
+#define RPR_COMP       0x02
+#define RPR_ADV                0xFF    /* Internal use only*/
+
+struct rem_scan_data {
+       struct mesh_node *node;
+       struct l_timeout *timeout;
+       uint8_t *list;
+       uint16_t client;
+       uint16_t oob_info;
+       uint16_t net_idx;
+       uint8_t state;
+       uint8_t scanned_limit;
+       uint8_t addr[6];
+       uint8_t uuid[16];
+       uint8_t to_secs;
+       uint8_t rxed_ads;
+       uint8_t ext_cnt;
+       bool fltr;
+       uint8_t ext[0];
+};
+
+static struct rem_scan_data *rpb_scan;
+
+struct rem_prov_data {
+       struct mesh_node *node;
+       struct l_timeout *timeout;
+       void *trans_data;
+       uint16_t client;
+       uint16_t net_idx;
+       uint8_t svr_pdu_num;
+       uint8_t cli_pdu_num;
+       uint8_t state;
+       uint8_t nppi_proc;
+       union {
+               struct {
+                       mesh_prov_open_func_t open_cb;
+                       mesh_prov_close_func_t close_cb;
+                       mesh_prov_receive_func_t rx_cb;
+                       mesh_prov_ack_func_t ack_cb;
+                       struct mesh_prov_node_info info;
+               } nppi;
+               struct {
+                       uint8_t uuid[17];
+                       prov_trans_tx_t tx;
+               } adv;
+       } u;
+};
+
+static struct rem_prov_data *rpb_prov;
+
+static const uint8_t prvb[2] = {BT_AD_MESH_BEACON, 0x00};
+static const uint8_t pkt_filter = BT_AD_MESH_PROV;
+static const char *name = "Test Name";
+
+static const uint8_t zero[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static void srv_open(void *user_data, prov_trans_tx_t adv_tx,
+                                       void *trans_data, uint8_t nppi_proc)
+{
+       struct rem_prov_data *prov = user_data;
+       uint8_t msg[5];
+       int n;
+
+       if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_LINK_OPENING)
+               return;
+
+       l_debug("Remote Link open confirmed");
+       prov->u.adv.tx = adv_tx;
+       prov->trans_data = trans_data;
+       prov->state = PB_REMOTE_STATE_LINK_ACTIVE;
+
+       n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg);
+       msg[n++] = PB_REM_ERR_SUCCESS;
+       msg[n++] = prov->state;
+
+       mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+                               prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static void srv_rx(void *user_data, const void *dptr, uint16_t len)
+{
+       struct rem_prov_data *prov = user_data;
+       const uint8_t *data = dptr;
+       uint8_t msg[69];
+       int n;
+
+       if (prov != rpb_prov || prov->state < PB_REMOTE_STATE_LINK_ACTIVE ||
+                                                               len > 65)
+               return;
+
+       l_debug("Remote PB IB-PDU");
+
+       prov->svr_pdu_num++;
+       n = mesh_model_opcode_set(OP_REM_PROV_PDU_REPORT, msg);
+       msg[n++] = prov->svr_pdu_num;
+       memcpy(msg + n, data, len);
+       n += len;
+
+       mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+                               prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static void srv_ack(void *user_data, uint8_t msg_num)
+{
+       struct rem_prov_data *prov = user_data;
+       uint8_t msg[4];
+       int n;
+
+       if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_OB_PKT_TX)
+               return;
+
+       l_debug("Remote PB ACK");
+
+       prov->state = PB_REMOTE_STATE_LINK_ACTIVE;
+       n = mesh_model_opcode_set(OP_REM_PROV_PDU_OB_REPORT, msg);
+       msg[n++] = prov->cli_pdu_num;
+
+       mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+                               prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static void srv_close(void *user_data, uint8_t reason)
+{
+       struct rem_prov_data *prov = user_data;
+       uint8_t msg[4];
+       int n;
+
+       if (prov != rpb_prov || prov->state < PB_REMOTE_STATE_LINK_ACTIVE)
+               return;
+
+       l_debug("Remote PB Close");
+
+       prov->state = PB_REMOTE_STATE_LINK_CLOSING;
+       n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg);
+       msg[n++] = prov->state;
+       msg[n++] = reason;
+
+       mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+                               prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static void send_prov_status(struct rem_prov_data *prov, uint8_t status)
+{
+       uint16_t n;
+       uint8_t msg[5];
+       bool segmented = prov->state == PB_REMOTE_STATE_LINK_CLOSING ?
+                                                               true : false;
+
+       n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg);
+       msg[n++] = status;
+       msg[n++] = prov->state;
+
+       l_info("RPB-Link Status(%d): dst %4.4x", prov->state, prov->client);
+
+       mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+                               prov->net_idx, DEFAULT_TTL, segmented, n, msg);
+}
+
+static void remprv_prov_cancel(struct l_timeout *timeout,
+                                               void *user_data)
+{
+       struct rem_prov_data *prov = user_data;
+
+       if (prov != rpb_prov)
+               return;
+
+       l_timeout_remove(prov->timeout);
+       l_free(prov);
+       rpb_prov = NULL;
+}
+
+static void deregister_ext_ad_type(uint8_t ad_type)
+{
+       uint8_t short_ad;
+
+       switch (ad_type) {
+       case BT_AD_MESH_BEACON:
+       case BT_AD_MESH_DATA:
+       case BT_AD_MESH_PROV:
+       case BT_AD_UUID16_SOME:
+       case BT_AD_UUID32_SOME:
+       case BT_AD_UUID128_SOME:
+       case BT_AD_NAME_SHORT:
+               return;
+
+       case BT_AD_UUID16_ALL:
+       case BT_AD_UUID32_ALL:
+       case BT_AD_UUID128_ALL:
+       case BT_AD_NAME_COMPLETE:
+               /* Automatically get short versions */
+               short_ad = ad_type - 1;
+               mesh_io_deregister_recv_cb(NULL, &short_ad, 1);
+
+               /* fall through */
+       default:
+               mesh_io_deregister_recv_cb(NULL, &ad_type, 1);
+               break;
+       }
+}
+
+static void remprv_scan_cancel(struct l_timeout *timeout,
+                                               void *user_data)
+{
+       struct rem_scan_data *scan = user_data;
+       uint8_t msg[22 + EXT_LIST_SIZE];
+       uint16_t i, n;
+
+       if (!scan || scan != rpb_scan)
+               return;
+
+       for (n = 0; n < scan->ext_cnt; n++)
+               deregister_ext_ad_type(scan->ext[n]);
+
+       if (scan->timeout == timeout) {
+               /* Return Extended Results */
+               if (scan->ext_cnt) {
+                       /* Return Extended Result */
+                       n = mesh_model_opcode_set(
+                                       OP_REM_PROV_EXT_SCAN_REPORT, msg);
+                       msg[n++] = PB_REM_ERR_SUCCESS;
+                       memcpy(msg + n, scan->uuid, 16);
+                       n += 16;
+
+                       if (scan->oob_info) {
+                               l_put_le16(0, msg + n);
+                               n += 2;
+                       }
+
+                       i = 0;
+                       while (scan->list[i]) {
+                               msg[n++] = scan->list[i];
+                               memcpy(msg + n, &scan->list[i + 1],
+                                                               scan->list[i]);
+                               n += scan->list[i];
+                               i += scan->list[i] + 1;
+                       }
+               }
+       }
+
+       l_timeout_remove(scan->timeout);
+       l_free(scan->list);
+       l_free(scan);
+       rpb_scan = NULL;
+}
+
+static void scan_pkt(void *user_data, struct mesh_io_recv_info *info,
+                                       const uint8_t *data, uint16_t len)
+{
+       struct rem_scan_data *scan = user_data;
+       uint8_t msg[22 + EXT_LIST_SIZE];
+       uint16_t i, n;
+       uint8_t filled = 0;
+       bool report = false;
+
+       if (scan != rpb_scan)
+               return;
+
+       if (scan->ext_cnt)
+               goto extended_scan;
+
+       /* RX Unprovisioned Beacon */
+       if (data[0] != BT_AD_MESH_BEACON || data[1] ||
+                       (len != 18 && len != 20 && len != 24))
+               return;
+
+       data += 2;
+       len -= 2;
+
+       for (n = 0; !report && n < scan->scanned_limit; n++) {
+               if (!memcmp(&scan->list[n * 17 + 1], data, 16)) {
+
+                       /* Repeat UUID, check RSSI */
+                       if ((int8_t) scan->list[n * 17] < info->rssi) {
+                               report = true;
+                               scan->list[n * 17] = (uint8_t) info->rssi;
+                       }
+
+               } else if (!memcmp(&scan->list[n * 17 + 1], zero, 16)) {
+
+                       /* Found Empty slot */
+                       report = true;
+                       scan->list[n * 17] = (uint8_t) info->rssi;
+                       memcpy(&scan->list[n * 17 + 1], data, 16);
+               }
+
+               filled++;
+       }
+
+       if (!report)
+               return;
+
+       n = mesh_model_opcode_set(OP_REM_PROV_SCAN_REPORT, msg);
+       msg[n++] = (uint8_t) info->rssi;
+       memcpy(msg + n, data, len);
+       n += len;
+
+       /* Always return oob_info, even if it wasn't in beacon */
+       if (len == 16) {
+               l_put_le16(0, msg + n);
+               n += 2;
+       }
+
+       goto send_report;
+
+extended_scan:
+       if (data[0] == BT_AD_MESH_BEACON && !data[1]) {
+               if (len != 18 && len != 20 && len != 24)
+                       return;
+
+               /* Check UUID */
+               if (memcmp(data + 2, scan->uuid, 16))
+                       return;
+
+               /* Zero AD list if prior data RXed from different bd_addr */
+               if (memcmp(scan->addr, info->addr, 6)) {
+                       scan->list[0] = 0;
+                       scan->rxed_ads = 0;
+               }
+
+               memcpy(scan->addr, info->addr, 6);
+               scan->fltr = true;
+
+               if (len >= 20)
+                       scan->oob_info = l_get_le16(data + 18);
+
+               if (scan->rxed_ads != scan->ext_cnt)
+                       return;
+
+
+       } else if (data[0] != BT_AD_MESH_BEACON) {
+               if (!scan->fltr || !memcmp(scan->addr, info->addr, 6)) {
+                       i = 0;
+                       while (scan->list[i]) {
+                               /* check if seen */
+                               if (scan->list[i + 1] == data[0])
+                                       return;
+
+                               i += scan->list[i] + 1;
+                       }
+
+                       /* Overflow Protection */
+                       if (i + len + 1 > EXT_LIST_SIZE)
+                               return;
+
+                       scan->list[i] = len;
+                       scan->list[i + len + 1] = 0;
+                       memcpy(scan->list + i + 1, data, len);
+                       scan->rxed_ads++;
+               }
+
+               if (scan->rxed_ads != scan->ext_cnt)
+                       return;
+
+       } else
+               return;
+
+       n = mesh_model_opcode_set(OP_REM_PROV_EXT_SCAN_REPORT, msg);
+       msg[n++] = PB_REM_ERR_SUCCESS;
+       memcpy(msg + n, scan->uuid, 16);
+       n += 16;
+       l_put_le16(scan->oob_info, msg + n);
+       n += 2;
+
+       i = 0;
+       while (scan->list[i]) {
+               msg[n++] = scan->list[i];
+               memcpy(msg + n, &scan->list[i + 1], scan->list[i]);
+               n += scan->list[i];
+               i += scan->list[i];
+       }
+
+send_report:
+       print_packet("App Tx", msg, n);
+       mesh_model_send(scan->node, 0, scan->client, APP_IDX_DEV_LOCAL,
+                               scan->net_idx, DEFAULT_TTL, true, n, msg);
+
+       /* Clean-up if we are done reporting*/
+       if (filled == scan->scanned_limit || scan->ext_cnt)
+               remprv_scan_cancel(NULL, scan);
+}
+
+static bool register_ext_ad_type(uint8_t ad_type, struct rem_scan_data *scan)
+{
+       uint8_t short_ad;
+
+       switch (ad_type) {
+       case BT_AD_MESH_PROV:
+       case BT_AD_UUID16_SOME:
+       case BT_AD_UUID32_SOME:
+       case BT_AD_UUID128_SOME:
+       case BT_AD_NAME_SHORT:
+               /* Illegal Requests */
+               return false;
+
+       case BT_AD_UUID16_ALL:
+       case BT_AD_UUID32_ALL:
+       case BT_AD_UUID128_ALL:
+       case BT_AD_NAME_COMPLETE:
+               /* Automatically get short versions */
+               short_ad = ad_type - 1;
+               mesh_io_register_recv_cb(NULL, &short_ad, 1, scan_pkt, scan);
+
+               /* fall through */
+       default:
+               mesh_io_register_recv_cb(NULL, &ad_type, 1, scan_pkt, scan);
+
+               /* fall through */
+
+       case BT_AD_MESH_BEACON:
+               /* Ignored/auto request */
+               break;
+       }
+
+       return true;
+}
+
+static void link_active(void *user_data)
+{
+       struct rem_prov_data *prov = user_data;
+       uint8_t msg[5];
+       int n;
+
+       if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_LINK_OPENING)
+               return;
+
+       l_debug("Remote Link open confirmed");
+       prov->state = PB_REMOTE_STATE_LINK_ACTIVE;
+
+       n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg);
+       msg[n++] = PB_REM_ERR_SUCCESS;
+       msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE;
+
+       mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+                               prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+bool register_nppi_acceptor(mesh_prov_open_func_t open_cb,
+                                       mesh_prov_close_func_t close_cb,
+                                       mesh_prov_receive_func_t rx_cb,
+                                       mesh_prov_ack_func_t ack_cb,
+                                       void *user_data)
+{
+       struct rem_prov_data *prov = rpb_prov;
+
+       if (!prov || prov->nppi_proc == RPR_ADV)
+               return false;
+
+       prov->u.nppi.open_cb = open_cb;
+       prov->u.nppi.close_cb = close_cb;
+       prov->u.nppi.rx_cb = rx_cb;
+       prov->u.nppi.ack_cb = ack_cb;
+       prov->trans_data = user_data;
+
+       open_cb(user_data, srv_rx, prov, prov->nppi_proc);
+
+       l_idle_oneshot(link_active, prov, NULL);
+
+       return true;
+}
+
+static bool nppi_cmplt(void *user_data, uint8_t status,
+                                       struct mesh_prov_node_info *info)
+{
+       struct rem_prov_data *prov = user_data;
+
+       if (prov != rpb_prov)
+               return false;
+
+       /* Save new info to apply on Link Close */
+       prov->u.nppi.info = *info;
+       return true;
+}
+
+static bool start_dev_key_refresh(struct mesh_node *node, uint8_t nppi_proc,
+                                               struct rem_prov_data *prov)
+{
+       uint8_t num_ele = node_get_num_elements(node);
+
+       prov->nppi_proc = nppi_proc;
+       return acceptor_start(num_ele, NULL, 0x0001, 60, NULL, nppi_cmplt,
+                                                                       prov);
+}
+
+static bool remprv_srv_pkt(uint16_t src, uint16_t unicast, uint16_t app_idx,
+                                       uint16_t net_idx, const uint8_t *data,
+                                       uint16_t size, const void *user_data)
+{
+       struct rem_prov_data *prov = rpb_prov;
+       struct rem_scan_data *scan = rpb_scan;
+       struct mesh_node *node = (struct mesh_node *) user_data;
+       const uint8_t *pkt = data;
+       bool segmented = false;
+       uint32_t opcode;
+       uint8_t msg[69];
+       uint8_t status;
+       uint16_t n;
+
+       if (app_idx != APP_IDX_DEV_LOCAL)
+               return false;
+
+       if (mesh_model_opcode_get(pkt, size, &opcode, &n)) {
+               size -= n;
+               pkt += n;
+       } else
+               return false;
+
+       n = 0;
+
+       switch (opcode) {
+       default:
+               return false;
+
+       case OP_REM_PROV_SCAN_CAP_GET:
+               if (size != 0)
+                       return true;
+
+               /* Compose Scan Info Status */
+               n = mesh_model_opcode_set(OP_REM_PROV_SCAN_CAP_STATUS, msg);
+               msg[n++] = PB_REMOTE_MAX_SCAN_QUEUE_SIZE;
+               msg[n++] = 1; /* Active Scanning Supported */
+               break;
+
+       case OP_REM_PROV_EXT_SCAN_START:
+               if (!size || !pkt[0])
+                       return true;
+
+               /* Size check the message */
+               if (pkt[0] + 18 == size) {
+                       /* Range check the Timeout */
+                       if (!pkt[size - 1] || pkt[size - 1] > 5)
+                               return true;
+               } else if (pkt[0] + 1 != size)
+                       return true;
+
+               /* Get local device extended info */
+               if (pkt[0] + 18 != size) {
+                       n = mesh_model_opcode_set(
+                                       OP_REM_PROV_EXT_SCAN_REPORT, msg);
+                       msg[n++] = PB_REM_ERR_SUCCESS;
+                       memcpy(msg + n, node_uuid_get(node), 16);
+                       n += 16;
+                       l_put_le16(0, msg + n);
+                       n += 2;
+                       size--;
+                       pkt++;
+
+                       while (size--) {
+                               if (*pkt++ == BT_AD_NAME_COMPLETE) {
+                                       msg[n] = strlen(name) + 1;
+                                       if (msg[n] > sizeof(msg) - n - 1)
+                                               msg[n] = sizeof(msg) - n - 1;
+                                       n++;
+                                       msg[n++] = BT_AD_NAME_COMPLETE;
+                                       memcpy(&msg[n], name, msg[n - 2] - 1);
+                                       n += msg[n - 2] - 1;
+                                       goto send_pkt;
+                               }
+                       }
+
+                       /* Send internal report */
+                       l_debug("Send internal extended info %d", n);
+                       goto send_pkt;
+               }
+
+               status = PB_REM_ERR_SUCCESS;
+               if (scan) {
+                       if (scan->client != src || scan->node != node ||
+                                               scan->ext_cnt != pkt[0])
+                               status = PB_REM_ERR_SCANNING_CANNOT_START;
+                       else if (memcmp(scan->ext, pkt + 1, pkt[0]))
+                               status = PB_REM_ERR_SCANNING_CANNOT_START;
+                       else if (memcmp(scan->uuid, pkt + 2, 16))
+                               status = PB_REM_ERR_SCANNING_CANNOT_START;
+               }
+
+               if (status != PB_REM_ERR_SUCCESS) {
+                       n = mesh_model_opcode_set(OP_REM_PROV_EXT_SCAN_REPORT,
+                                                                       msg);
+                       msg[n++] = status;
+                       memset(msg + n, 0, 16);
+                       n += 16;
+                       segmented = true;
+                       break;
+               }
+
+               /* Ignore extended requests while already scanning */
+               if (scan)
+                       return true;
+
+               scan = (void *) l_new(uint8_t,
+                                       sizeof(struct rem_scan_data) + pkt[0]);
+
+               /* Validate and register Extended AD types */
+               for (n = 0; n < pkt[0]; n++) {
+                       if (!register_ext_ad_type(pkt[1 + n], scan)) {
+                               /* Invalid AD type detected -- Undo */
+                               while (n--)
+                                       deregister_ext_ad_type(pkt[1 + n]);
+
+                               l_free(scan);
+                               return true;
+                       }
+               }
+
+               rpb_scan = scan;
+               scan->client = src;
+               scan->net_idx = net_idx;
+               memcpy(scan->uuid, pkt + size - 17, 16);
+               scan->ext_cnt = pkt[0];
+               memcpy(scan->ext, pkt + 1, pkt[0]);
+               scan->list = l_malloc(EXT_LIST_SIZE);
+               scan->list[0] = 0;
+
+               mesh_io_register_recv_cb(NULL, prvb, sizeof(prvb),
+                                                               scan_pkt, scan);
+
+               scan->timeout = l_timeout_create(pkt[size-1],
+                                               remprv_scan_cancel, scan, NULL);
+               return true;
+
+       case OP_REM_PROV_SCAN_START:
+               if (size != 2 && size != 18)
+                       return true;
+
+               /* Reject Timeout of Zero */
+               if (!pkt[1])
+                       return true;
+
+               status = PB_REM_ERR_SUCCESS;
+               if (scan) {
+                       if (scan->ext_cnt || scan->client != src ||
+                                                       scan->node != node)
+                               status = PB_REM_ERR_SCANNING_CANNOT_START;
+                       else if (!!(scan->fltr) != !!(size != 18))
+                               status = PB_REM_ERR_SCANNING_CANNOT_START;
+                       else if (scan->fltr && memcmp(scan->uuid, pkt + 2, 16))
+                               status = PB_REM_ERR_SCANNING_CANNOT_START;
+               }
+
+               if (status != PB_REM_ERR_SUCCESS) {
+                       n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STATUS, msg);
+                       msg[n++] = status;
+                       msg[n++] = scan ? scan->state : 0;
+                       msg[n++] = scan ? scan->scanned_limit :
+                                               PB_REMOTE_MAX_SCAN_QUEUE_SIZE;
+                       msg[n++] = scan ? scan->to_secs : 0;
+                       break;
+               }
+
+               if (!scan)
+                       scan = l_new(struct rem_scan_data, 1);
+
+               rpb_scan = scan;
+
+               if (size == 18) {
+                       memcpy(scan->uuid, pkt + 2, 16);
+                       scan->fltr = true;
+                       scan->state = 0x02; /* Limited */
+               } else {
+                       memset(scan->uuid, 0, 16);
+                       scan->fltr = false;
+                       scan->state = 0x01; /* Unlimited */
+               }
+
+               scan->client = src;
+               scan->net_idx = net_idx;
+               scan->node = node;
+
+               if (!scan->list)
+                       scan->list = l_new(uint8_t,
+                                       23 * PB_REMOTE_MAX_SCAN_QUEUE_SIZE);
+
+               mesh_io_register_recv_cb(NULL, prvb, 2, scan_pkt, scan);
+
+               scan->to_secs = pkt[1];
+
+               if (pkt[0])
+                       scan->scanned_limit = pkt[0];
+               else
+                       scan->scanned_limit = PB_REMOTE_MAX_SCAN_QUEUE_SIZE;
+
+               scan->timeout = l_timeout_create(pkt[1],
+                                       remprv_scan_cancel, scan, NULL);
+
+               /* fall through */
+
+       case OP_REM_PROV_SCAN_GET:
+               /* Compose Scan Status */
+               n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STATUS, msg);
+               msg[n++] = PB_REM_ERR_SUCCESS;
+               msg[n++] = scan ? scan->state : 0;
+               msg[n++] = scan ? scan->scanned_limit :
+                                               PB_REMOTE_MAX_SCAN_QUEUE_SIZE;
+               msg[n++] = scan ? scan->to_secs : 0;
+               break;
+
+       case OP_REM_PROV_SCAN_STOP:
+               if (size != 0 || !scan)
+                       return true;
+
+               remprv_scan_cancel(NULL, scan);
+               return true;
+
+       case OP_REM_PROV_LINK_GET:
+               if (size != 0 || !prov)
+                       return true;
+
+               send_prov_status(prov, PB_REM_ERR_SUCCESS);
+               return true;
+
+       case OP_REM_PROV_LINK_OPEN:
+               /* Sanity check args */
+               if (size != 16 && size != 17 && size != 1)
+                       return true;
+
+               if (size == 17 && (pkt[16] == 0 || pkt[16] > 0x3c))
+                       return true;
+
+               if (size == 1 && pkt[0] > 0x02)
+                       return true;
+
+               if (prov) {
+                       if (prov->client != src || prov->node != node ||
+                               (size == 1 && prov->nppi_proc != pkt[0]) ||
+                               (size >= 16 && (prov->nppi_proc != RPR_ADV ||
+                                       memcmp(prov->u.adv.uuid, pkt, 16)))) {
+
+                               /* Send Reject (in progress) */
+                               send_prov_status(prov, PB_REM_ERR_CANNOT_OPEN);
+                               n = mesh_model_opcode_set(
+                                               OP_REM_PROV_LINK_STATUS, msg);
+                               msg[n++] = PB_REM_ERR_CANNOT_OPEN;
+                               msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE;
+                               break;
+                       }
+
+                       /* Send redundant  Success */
+                       send_prov_status(prov, PB_REM_ERR_SUCCESS);
+                       return true;
+               }
+
+               if (scan && scan->client != src && scan->node != node) {
+                       n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg);
+                       msg[n++] = PB_REM_ERR_CANNOT_OPEN;
+                       msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE;
+                       break;
+               }
+
+               print_packet("Remote Prov Link Open", pkt, size);
+
+               remprv_scan_cancel(NULL, scan);
+
+               rpb_prov = prov = l_new(struct rem_prov_data, 1);
+               prov->client = src;
+               prov->net_idx = net_idx;
+               prov->node = node;
+               prov->state = PB_REMOTE_STATE_LINK_OPENING;
+
+               if (size == 1) {
+                       status = start_dev_key_refresh(node, pkt[0], prov);
+
+               } else {
+                       if (size == 17)
+                               prov->timeout = l_timeout_create(pkt[16],
+                                               remprv_prov_cancel, prov, NULL);
+
+
+                       prov->nppi_proc = RPR_ADV;
+                       memcpy(prov->u.adv.uuid, pkt, 16);
+                       status = pb_adv_reg(true, srv_open, srv_close, srv_rx,
+                                                       srv_ack, pkt, prov);
+               }
+
+               if (status)
+                       send_prov_status(prov, PB_REM_ERR_SUCCESS);
+               else {
+                       n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg);
+                       msg[n++] = PB_REM_ERR_CANNOT_OPEN;
+                       msg[n++] = PB_REMOTE_STATE_IDLE;
+                       remprv_prov_cancel(NULL, prov);
+               }
+
+               return true;
+
+       case OP_REM_PROV_LINK_CLOSE:
+               if (size != 1)
+                       return true;
+
+               if (!prov || prov->node != node || prov->client != src)
+                       return true;
+
+               prov->state = PB_REMOTE_STATE_LINK_CLOSING;
+               mesh_io_send_cancel(NULL, &pkt_filter, sizeof(pkt_filter));
+               send_prov_status(prov, PB_REM_ERR_SUCCESS);
+               if (pkt[0] == 0x02) {
+                       msg[0] = PROV_FAILED;
+                       msg[1] = PROV_ERR_CANT_ASSIGN_ADDR;
+                       if (prov->nppi_proc == RPR_ADV)
+                               prov->u.adv.tx(prov->trans_data, msg, 2);
+                       else
+                               prov->u.nppi.rx_cb(prov->trans_data, msg, 2);
+               }
+
+               if (prov->nppi_proc == RPR_ADV)
+                       pb_adv_unreg(prov);
+
+               else if (prov->nppi_proc <= RPR_COMP) {
+                       /* Hard or Soft refresh of local node, based on NPPI */
+                       node_refresh(prov->node, (prov->nppi_proc == RPR_ADDR),
+                                                       &prov->u.nppi.info);
+               }
+
+               remprv_prov_cancel(NULL, prov);
+
+               return true;
+
+       case OP_REM_PROV_PDU_SEND:
+               if (!prov || prov->node != node || prov->client != src)
+                       return true;
+
+               if (size < 2)
+                       return true;
+
+
+               prov->cli_pdu_num = *pkt++;
+               size--;
+               prov->state = PB_REMOTE_STATE_OB_PKT_TX;
+
+               if (prov->nppi_proc == RPR_ADV)
+                       prov->u.adv.tx(prov->trans_data, pkt, size);
+               else {
+                       srv_ack(prov, prov->cli_pdu_num);
+                       prov->u.nppi.rx_cb(prov->trans_data, pkt, size);
+               }
+
+               return true;
+       }
+
+send_pkt:
+       l_info("PB-SVR: src %4.4x dst %4.4x", unicast, src);
+       print_packet("App Tx", msg, n);
+       mesh_model_send(node, 0, src, APP_IDX_DEV_LOCAL,
+                               net_idx, DEFAULT_TTL, segmented, n, msg);
+
+       return true;
+}
+
+static void remprv_srv_unregister(void *user_data)
+{
+}
+
+static const struct mesh_model_ops ops = {
+       .unregister = remprv_srv_unregister,
+       .recv = remprv_srv_pkt,
+       .bind = NULL,
+       .sub = NULL,
+       .pub = NULL
+};
+
+void remote_prov_server_init(struct mesh_node *node, uint8_t ele_idx)
+{
+       mesh_model_register(node, ele_idx, REM_PROV_SRV_MODEL, &ops, node);
+}
diff --git a/mesh/remprv.h b/mesh/remprv.h
new file mode 100644 (file)
index 0000000..ab7e32b
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2023  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#define REM_PROV_SRV_MODEL     SET_ID(SIG_VENDOR, 0x0004)
+#define REM_PROV_CLI_MODEL     SET_ID(SIG_VENDOR, 0x0005)
+
+#define PB_REMOTE_MAX_SCAN_QUEUE_SIZE  5
+
+#define PB_REMOTE_STATE_IDLE           0x00
+#define PB_REMOTE_STATE_LINK_OPENING   0x01
+#define PB_REMOTE_STATE_LINK_ACTIVE    0x02
+#define PB_REMOTE_STATE_OB_PKT_TX      0x03
+#define PB_REMOTE_STATE_LINK_CLOSING   0x04
+
+#define PB_REMOTE_TYPE_LOCAL   0x01
+#define PB_REMOTE_TYPE_ADV     0x02
+#define PB_REMOTE_TYPE_GATT    0x04
+
+#define PB_REMOTE_SCAN_TYPE_NONE       0x00
+#define PB_REMOTE_SCAN_TYPE_UNLIMITED  0x01
+#define PB_REMOTE_SCAN_TYPE_LIMITED    0x02
+#define PB_REMOTE_SCAN_TYPE_DETAILED   0x03
+
+/* Remote Provisioning Opcode List */
+#define OP_REM_PROV_SCAN_CAP_GET       0x804F
+#define OP_REM_PROV_SCAN_CAP_STATUS    0x8050
+#define OP_REM_PROV_SCAN_GET           0x8051
+#define OP_REM_PROV_SCAN_START         0x8052
+#define OP_REM_PROV_SCAN_STOP          0x8053
+#define OP_REM_PROV_SCAN_STATUS                0x8054
+#define OP_REM_PROV_SCAN_REPORT                0x8055
+#define OP_REM_PROV_EXT_SCAN_START     0x8056
+#define OP_REM_PROV_EXT_SCAN_REPORT    0x8057
+#define OP_REM_PROV_LINK_GET           0x8058
+#define OP_REM_PROV_LINK_OPEN          0x8059
+#define OP_REM_PROV_LINK_CLOSE         0x805A
+#define OP_REM_PROV_LINK_STATUS                0x805B
+#define OP_REM_PROV_LINK_REPORT                0x805C
+#define OP_REM_PROV_PDU_SEND           0x805D
+#define OP_REM_PROV_PDU_OB_REPORT      0x805E
+#define OP_REM_PROV_PDU_REPORT         0x805F
+
+/* Remote Provisioning Errors */
+#define PB_REM_ERR_SUCCESS                     0x00
+#define PB_REM_ERR_SCANNING_CANNOT_START       0x01
+#define PB_REM_ERR_INVALID_STATE               0x02
+#define PB_REM_ERR_LIMITED_RESOURCES           0x03
+#define PB_REM_ERR_CANNOT_OPEN                 0x04
+#define PB_REM_ERR_OPEN_FAILED                 0x05
+#define PB_REM_ERR_CLOSED_BY_DEVICE            0x06
+#define PB_REM_ERR_CLOSED_BY_SERVER            0x07
+#define PB_REM_ERR_CLOSED_BY_CLIENT            0x08
+#define PB_REM_ERR_CLOSED_CANNOT_RX_PDU                0x09
+#define PB_REM_ERR_CLOSED_CANNOT_TX_PDU                0x0A
+
+void remote_prov_server_init(struct mesh_node *node, uint8_t ele_idx);
+void remote_prov_client_init(struct mesh_node *node, uint8_t ele_idx);
+bool register_nppi_acceptor(mesh_prov_open_func_t open_cb,
+                                       mesh_prov_close_func_t close_cb,
+                                       mesh_prov_receive_func_t rx_cb,
+                                       mesh_prov_ack_func_t ack_cb,
+                                       void *user_data);