summaryrefslogtreecommitdiff
path: root/mesh/remprv-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'mesh/remprv-server.c')
-rw-r--r--mesh/remprv-server.c907
1 files changed, 907 insertions, 0 deletions
diff --git a/mesh/remprv-server.c b/mesh/remprv-server.c
new file mode 100644
index 000000000..85af65dcc
--- /dev/null
+++ b/mesh/remprv-server.c
@@ -0,0 +1,907 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2023 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/time.h>
+#include <ell/ell.h>
+
+#include "src/shared/ad.h"
+
+#include "mesh/mesh-defs.h"
+#include "mesh/mesh-io.h"
+#include "mesh/util.h"
+#include "mesh/node.h"
+#include "mesh/net.h"
+#include "mesh/appkey.h"
+#include "mesh/model.h"
+#include "mesh/prov.h"
+#include "mesh/provision.h"
+#include "mesh/pb-adv.h"
+#include "mesh/remprv.h"
+
+#define EXT_LIST_SIZE 60
+
+#define RPR_DEV_KEY 0x00
+#define RPR_ADDR 0x01
+#define RPR_COMP 0x02
+#define RPR_ADV 0xFF /* Internal use only*/
+
+struct rem_scan_data {
+ struct mesh_node *node;
+ struct l_timeout *timeout;
+ uint8_t *list;
+ uint16_t client;
+ uint16_t oob_info;
+ uint16_t net_idx;
+ uint8_t state;
+ uint8_t scanned_limit;
+ uint8_t addr[6];
+ uint8_t uuid[16];
+ uint8_t to_secs;
+ uint8_t rxed_ads;
+ uint8_t ext_cnt;
+ bool fltr;
+ uint8_t ext[0];
+};
+
+static struct rem_scan_data *rpb_scan;
+
+struct rem_prov_data {
+ struct mesh_node *node;
+ struct l_timeout *timeout;
+ void *trans_data;
+ uint16_t client;
+ uint16_t net_idx;
+ uint8_t svr_pdu_num;
+ uint8_t cli_pdu_num;
+ uint8_t state;
+ uint8_t nppi_proc;
+ union {
+ struct {
+ mesh_prov_open_func_t open_cb;
+ mesh_prov_close_func_t close_cb;
+ mesh_prov_receive_func_t rx_cb;
+ mesh_prov_ack_func_t ack_cb;
+ struct mesh_prov_node_info info;
+ } nppi;
+ struct {
+ uint8_t uuid[17];
+ prov_trans_tx_t tx;
+ } adv;
+ } u;
+};
+
+static struct rem_prov_data *rpb_prov;
+
+static const uint8_t prvb[2] = {BT_AD_MESH_BEACON, 0x00};
+static const uint8_t pkt_filter = BT_AD_MESH_PROV;
+static const char *name = "Test Name";
+
+static const uint8_t zero[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static void srv_open(void *user_data, prov_trans_tx_t adv_tx,
+ void *trans_data, uint8_t nppi_proc)
+{
+ struct rem_prov_data *prov = user_data;
+ uint8_t msg[5];
+ int n;
+
+ if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_LINK_OPENING)
+ return;
+
+ l_debug("Remote Link open confirmed");
+ prov->u.adv.tx = adv_tx;
+ prov->trans_data = trans_data;
+ prov->state = PB_REMOTE_STATE_LINK_ACTIVE;
+
+ n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg);
+ msg[n++] = PB_REM_ERR_SUCCESS;
+ msg[n++] = prov->state;
+
+ mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+ prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static void srv_rx(void *user_data, const void *dptr, uint16_t len)
+{
+ struct rem_prov_data *prov = user_data;
+ const uint8_t *data = dptr;
+ uint8_t msg[69];
+ int n;
+
+ if (prov != rpb_prov || prov->state < PB_REMOTE_STATE_LINK_ACTIVE ||
+ len > 65)
+ return;
+
+ l_debug("Remote PB IB-PDU");
+
+ prov->svr_pdu_num++;
+ n = mesh_model_opcode_set(OP_REM_PROV_PDU_REPORT, msg);
+ msg[n++] = prov->svr_pdu_num;
+ memcpy(msg + n, data, len);
+ n += len;
+
+ mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+ prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static void srv_ack(void *user_data, uint8_t msg_num)
+{
+ struct rem_prov_data *prov = user_data;
+ uint8_t msg[4];
+ int n;
+
+ if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_OB_PKT_TX)
+ return;
+
+ l_debug("Remote PB ACK");
+
+ prov->state = PB_REMOTE_STATE_LINK_ACTIVE;
+ n = mesh_model_opcode_set(OP_REM_PROV_PDU_OB_REPORT, msg);
+ msg[n++] = prov->cli_pdu_num;
+
+ mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+ prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static void srv_close(void *user_data, uint8_t reason)
+{
+ struct rem_prov_data *prov = user_data;
+ uint8_t msg[4];
+ int n;
+
+ if (prov != rpb_prov || prov->state < PB_REMOTE_STATE_LINK_ACTIVE)
+ return;
+
+ l_debug("Remote PB Close");
+
+ prov->state = PB_REMOTE_STATE_LINK_CLOSING;
+ n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg);
+ msg[n++] = prov->state;
+ msg[n++] = reason;
+
+ mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+ prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static void send_prov_status(struct rem_prov_data *prov, uint8_t status)
+{
+ uint16_t n;
+ uint8_t msg[5];
+ bool segmented = prov->state == PB_REMOTE_STATE_LINK_CLOSING ?
+ true : false;
+
+ n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg);
+ msg[n++] = status;
+ msg[n++] = prov->state;
+
+ l_info("RPB-Link Status(%d): dst %4.4x", prov->state, prov->client);
+
+ mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+ prov->net_idx, DEFAULT_TTL, segmented, n, msg);
+}
+
+static void remprv_prov_cancel(struct l_timeout *timeout,
+ void *user_data)
+{
+ struct rem_prov_data *prov = user_data;
+
+ if (prov != rpb_prov)
+ return;
+
+ l_timeout_remove(prov->timeout);
+ l_free(prov);
+ rpb_prov = NULL;
+}
+
+static void deregister_ext_ad_type(uint8_t ad_type)
+{
+ uint8_t short_ad;
+
+ switch (ad_type) {
+ case BT_AD_MESH_BEACON:
+ case BT_AD_MESH_DATA:
+ case BT_AD_MESH_PROV:
+ case BT_AD_UUID16_SOME:
+ case BT_AD_UUID32_SOME:
+ case BT_AD_UUID128_SOME:
+ case BT_AD_NAME_SHORT:
+ return;
+
+ case BT_AD_UUID16_ALL:
+ case BT_AD_UUID32_ALL:
+ case BT_AD_UUID128_ALL:
+ case BT_AD_NAME_COMPLETE:
+ /* Automatically get short versions */
+ short_ad = ad_type - 1;
+ mesh_io_deregister_recv_cb(NULL, &short_ad, 1);
+
+ /* fall through */
+ default:
+ mesh_io_deregister_recv_cb(NULL, &ad_type, 1);
+ break;
+ }
+}
+
+static void remprv_scan_cancel(struct l_timeout *timeout,
+ void *user_data)
+{
+ struct rem_scan_data *scan = user_data;
+ uint8_t msg[22 + EXT_LIST_SIZE];
+ uint16_t i, n;
+
+ if (!scan || scan != rpb_scan)
+ return;
+
+ for (n = 0; n < scan->ext_cnt; n++)
+ deregister_ext_ad_type(scan->ext[n]);
+
+ if (scan->timeout == timeout) {
+ /* Return Extended Results */
+ if (scan->ext_cnt) {
+ /* Return Extended Result */
+ n = mesh_model_opcode_set(
+ OP_REM_PROV_EXT_SCAN_REPORT, msg);
+ msg[n++] = PB_REM_ERR_SUCCESS;
+ memcpy(msg + n, scan->uuid, 16);
+ n += 16;
+
+ if (scan->oob_info) {
+ l_put_le16(0, msg + n);
+ n += 2;
+ }
+
+ i = 0;
+ while (scan->list[i]) {
+ msg[n++] = scan->list[i];
+ memcpy(msg + n, &scan->list[i + 1],
+ scan->list[i]);
+ n += scan->list[i];
+ i += scan->list[i] + 1;
+ }
+ }
+ }
+
+ l_timeout_remove(scan->timeout);
+ l_free(scan->list);
+ l_free(scan);
+ rpb_scan = NULL;
+}
+
+static void scan_pkt(void *user_data, struct mesh_io_recv_info *info,
+ const uint8_t *data, uint16_t len)
+{
+ struct rem_scan_data *scan = user_data;
+ uint8_t msg[22 + EXT_LIST_SIZE];
+ uint16_t i, n;
+ uint8_t filled = 0;
+ bool report = false;
+
+ if (scan != rpb_scan)
+ return;
+
+ if (scan->ext_cnt)
+ goto extended_scan;
+
+ /* RX Unprovisioned Beacon */
+ if (data[0] != BT_AD_MESH_BEACON || data[1] ||
+ (len != 18 && len != 20 && len != 24))
+ return;
+
+ data += 2;
+ len -= 2;
+
+ for (n = 0; !report && n < scan->scanned_limit; n++) {
+ if (!memcmp(&scan->list[n * 17 + 1], data, 16)) {
+
+ /* Repeat UUID, check RSSI */
+ if ((int8_t) scan->list[n * 17] < info->rssi) {
+ report = true;
+ scan->list[n * 17] = (uint8_t) info->rssi;
+ }
+
+ } else if (!memcmp(&scan->list[n * 17 + 1], zero, 16)) {
+
+ /* Found Empty slot */
+ report = true;
+ scan->list[n * 17] = (uint8_t) info->rssi;
+ memcpy(&scan->list[n * 17 + 1], data, 16);
+ }
+
+ filled++;
+ }
+
+ if (!report)
+ return;
+
+ n = mesh_model_opcode_set(OP_REM_PROV_SCAN_REPORT, msg);
+ msg[n++] = (uint8_t) info->rssi;
+ memcpy(msg + n, data, len);
+ n += len;
+
+ /* Always return oob_info, even if it wasn't in beacon */
+ if (len == 16) {
+ l_put_le16(0, msg + n);
+ n += 2;
+ }
+
+ goto send_report;
+
+extended_scan:
+ if (data[0] == BT_AD_MESH_BEACON && !data[1]) {
+ if (len != 18 && len != 20 && len != 24)
+ return;
+
+ /* Check UUID */
+ if (memcmp(data + 2, scan->uuid, 16))
+ return;
+
+ /* Zero AD list if prior data RXed from different bd_addr */
+ if (memcmp(scan->addr, info->addr, 6)) {
+ scan->list[0] = 0;
+ scan->rxed_ads = 0;
+ }
+
+ memcpy(scan->addr, info->addr, 6);
+ scan->fltr = true;
+
+ if (len >= 20)
+ scan->oob_info = l_get_le16(data + 18);
+
+ if (scan->rxed_ads != scan->ext_cnt)
+ return;
+
+
+ } else if (data[0] != BT_AD_MESH_BEACON) {
+ if (!scan->fltr || !memcmp(scan->addr, info->addr, 6)) {
+ i = 0;
+ while (scan->list[i]) {
+ /* check if seen */
+ if (scan->list[i + 1] == data[0])
+ return;
+
+ i += scan->list[i] + 1;
+ }
+
+ /* Overflow Protection */
+ if (i + len + 1 > EXT_LIST_SIZE)
+ return;
+
+ scan->list[i] = len;
+ scan->list[i + len + 1] = 0;
+ memcpy(scan->list + i + 1, data, len);
+ scan->rxed_ads++;
+ }
+
+ if (scan->rxed_ads != scan->ext_cnt)
+ return;
+
+ } else
+ return;
+
+ n = mesh_model_opcode_set(OP_REM_PROV_EXT_SCAN_REPORT, msg);
+ msg[n++] = PB_REM_ERR_SUCCESS;
+ memcpy(msg + n, scan->uuid, 16);
+ n += 16;
+ l_put_le16(scan->oob_info, msg + n);
+ n += 2;
+
+ i = 0;
+ while (scan->list[i]) {
+ msg[n++] = scan->list[i];
+ memcpy(msg + n, &scan->list[i + 1], scan->list[i]);
+ n += scan->list[i];
+ i += scan->list[i];
+ }
+
+send_report:
+ print_packet("App Tx", msg, n);
+ mesh_model_send(scan->node, 0, scan->client, APP_IDX_DEV_LOCAL,
+ scan->net_idx, DEFAULT_TTL, true, n, msg);
+
+ /* Clean-up if we are done reporting*/
+ if (filled == scan->scanned_limit || scan->ext_cnt)
+ remprv_scan_cancel(NULL, scan);
+}
+
+static bool register_ext_ad_type(uint8_t ad_type, struct rem_scan_data *scan)
+{
+ uint8_t short_ad;
+
+ switch (ad_type) {
+ case BT_AD_MESH_PROV:
+ case BT_AD_UUID16_SOME:
+ case BT_AD_UUID32_SOME:
+ case BT_AD_UUID128_SOME:
+ case BT_AD_NAME_SHORT:
+ /* Illegal Requests */
+ return false;
+
+ case BT_AD_UUID16_ALL:
+ case BT_AD_UUID32_ALL:
+ case BT_AD_UUID128_ALL:
+ case BT_AD_NAME_COMPLETE:
+ /* Automatically get short versions */
+ short_ad = ad_type - 1;
+ mesh_io_register_recv_cb(NULL, &short_ad, 1, scan_pkt, scan);
+
+ /* fall through */
+ default:
+ mesh_io_register_recv_cb(NULL, &ad_type, 1, scan_pkt, scan);
+
+ /* fall through */
+
+ case BT_AD_MESH_BEACON:
+ /* Ignored/auto request */
+ break;
+ }
+
+ return true;
+}
+
+static void link_active(void *user_data)
+{
+ struct rem_prov_data *prov = user_data;
+ uint8_t msg[5];
+ int n;
+
+ if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_LINK_OPENING)
+ return;
+
+ l_debug("Remote Link open confirmed");
+ prov->state = PB_REMOTE_STATE_LINK_ACTIVE;
+
+ n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg);
+ msg[n++] = PB_REM_ERR_SUCCESS;
+ msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE;
+
+ mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL,
+ prov->net_idx, DEFAULT_TTL, true, n, msg);
+}
+
+bool register_nppi_acceptor(mesh_prov_open_func_t open_cb,
+ mesh_prov_close_func_t close_cb,
+ mesh_prov_receive_func_t rx_cb,
+ mesh_prov_ack_func_t ack_cb,
+ void *user_data)
+{
+ struct rem_prov_data *prov = rpb_prov;
+
+ if (!prov || prov->nppi_proc == RPR_ADV)
+ return false;
+
+ prov->u.nppi.open_cb = open_cb;
+ prov->u.nppi.close_cb = close_cb;
+ prov->u.nppi.rx_cb = rx_cb;
+ prov->u.nppi.ack_cb = ack_cb;
+ prov->trans_data = user_data;
+
+ open_cb(user_data, srv_rx, prov, prov->nppi_proc);
+
+ l_idle_oneshot(link_active, prov, NULL);
+
+ return true;
+}
+
+static bool nppi_cmplt(void *user_data, uint8_t status,
+ struct mesh_prov_node_info *info)
+{
+ struct rem_prov_data *prov = user_data;
+
+ if (prov != rpb_prov)
+ return false;
+
+ /* Save new info to apply on Link Close */
+ prov->u.nppi.info = *info;
+ return true;
+}
+
+static bool start_dev_key_refresh(struct mesh_node *node, uint8_t nppi_proc,
+ struct rem_prov_data *prov)
+{
+ uint8_t num_ele = node_get_num_elements(node);
+
+ prov->nppi_proc = nppi_proc;
+ return acceptor_start(num_ele, NULL, 0x0001, 60, NULL, nppi_cmplt,
+ prov);
+}
+
+static bool remprv_srv_pkt(uint16_t src, uint16_t unicast, uint16_t app_idx,
+ uint16_t net_idx, const uint8_t *data,
+ uint16_t size, const void *user_data)
+{
+ struct rem_prov_data *prov = rpb_prov;
+ struct rem_scan_data *scan = rpb_scan;
+ struct mesh_node *node = (struct mesh_node *) user_data;
+ const uint8_t *pkt = data;
+ bool segmented = false;
+ uint32_t opcode;
+ uint8_t msg[69];
+ uint8_t status;
+ uint16_t n;
+
+ if (app_idx != APP_IDX_DEV_LOCAL)
+ return false;
+
+ if (mesh_model_opcode_get(pkt, size, &opcode, &n)) {
+ size -= n;
+ pkt += n;
+ } else
+ return false;
+
+ n = 0;
+
+ switch (opcode) {
+ default:
+ return false;
+
+ case OP_REM_PROV_SCAN_CAP_GET:
+ if (size != 0)
+ return true;
+
+ /* Compose Scan Info Status */
+ n = mesh_model_opcode_set(OP_REM_PROV_SCAN_CAP_STATUS, msg);
+ msg[n++] = PB_REMOTE_MAX_SCAN_QUEUE_SIZE;
+ msg[n++] = 1; /* Active Scanning Supported */
+ break;
+
+ case OP_REM_PROV_EXT_SCAN_START:
+ if (!size || !pkt[0])
+ return true;
+
+ /* Size check the message */
+ if (pkt[0] + 18 == size) {
+ /* Range check the Timeout */
+ if (!pkt[size - 1] || pkt[size - 1] > 5)
+ return true;
+ } else if (pkt[0] + 1 != size)
+ return true;
+
+ /* Get local device extended info */
+ if (pkt[0] + 18 != size) {
+ n = mesh_model_opcode_set(
+ OP_REM_PROV_EXT_SCAN_REPORT, msg);
+ msg[n++] = PB_REM_ERR_SUCCESS;
+ memcpy(msg + n, node_uuid_get(node), 16);
+ n += 16;
+ l_put_le16(0, msg + n);
+ n += 2;
+ size--;
+ pkt++;
+
+ while (size--) {
+ if (*pkt++ == BT_AD_NAME_COMPLETE) {
+ msg[n] = strlen(name) + 1;
+ if (msg[n] > sizeof(msg) - n - 1)
+ msg[n] = sizeof(msg) - n - 1;
+ n++;
+ msg[n++] = BT_AD_NAME_COMPLETE;
+ memcpy(&msg[n], name, msg[n - 2] - 1);
+ n += msg[n - 2] - 1;
+ goto send_pkt;
+ }
+ }
+
+ /* Send internal report */
+ l_debug("Send internal extended info %d", n);
+ goto send_pkt;
+ }
+
+ status = PB_REM_ERR_SUCCESS;
+ if (scan) {
+ if (scan->client != src || scan->node != node ||
+ scan->ext_cnt != pkt[0])
+ status = PB_REM_ERR_SCANNING_CANNOT_START;
+ else if (memcmp(scan->ext, pkt + 1, pkt[0]))
+ status = PB_REM_ERR_SCANNING_CANNOT_START;
+ else if (memcmp(scan->uuid, pkt + 2, 16))
+ status = PB_REM_ERR_SCANNING_CANNOT_START;
+ }
+
+ if (status != PB_REM_ERR_SUCCESS) {
+ n = mesh_model_opcode_set(OP_REM_PROV_EXT_SCAN_REPORT,
+ msg);
+ msg[n++] = status;
+ memset(msg + n, 0, 16);
+ n += 16;
+ segmented = true;
+ break;
+ }
+
+ /* Ignore extended requests while already scanning */
+ if (scan)
+ return true;
+
+ scan = (void *) l_new(uint8_t,
+ sizeof(struct rem_scan_data) + pkt[0]);
+
+ /* Validate and register Extended AD types */
+ for (n = 0; n < pkt[0]; n++) {
+ if (!register_ext_ad_type(pkt[1 + n], scan)) {
+ /* Invalid AD type detected -- Undo */
+ while (n--)
+ deregister_ext_ad_type(pkt[1 + n]);
+
+ l_free(scan);
+ return true;
+ }
+ }
+
+ rpb_scan = scan;
+ scan->client = src;
+ scan->net_idx = net_idx;
+ memcpy(scan->uuid, pkt + size - 17, 16);
+ scan->ext_cnt = pkt[0];
+ memcpy(scan->ext, pkt + 1, pkt[0]);
+ scan->list = l_malloc(EXT_LIST_SIZE);
+ scan->list[0] = 0;
+
+ mesh_io_register_recv_cb(NULL, prvb, sizeof(prvb),
+ scan_pkt, scan);
+
+ scan->timeout = l_timeout_create(pkt[size-1],
+ remprv_scan_cancel, scan, NULL);
+ return true;
+
+ case OP_REM_PROV_SCAN_START:
+ if (size != 2 && size != 18)
+ return true;
+
+ /* Reject Timeout of Zero */
+ if (!pkt[1])
+ return true;
+
+ status = PB_REM_ERR_SUCCESS;
+ if (scan) {
+ if (scan->ext_cnt || scan->client != src ||
+ scan->node != node)
+ status = PB_REM_ERR_SCANNING_CANNOT_START;
+ else if (!!(scan->fltr) != !!(size != 18))
+ status = PB_REM_ERR_SCANNING_CANNOT_START;
+ else if (scan->fltr && memcmp(scan->uuid, pkt + 2, 16))
+ status = PB_REM_ERR_SCANNING_CANNOT_START;
+ }
+
+ if (status != PB_REM_ERR_SUCCESS) {
+ n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STATUS, msg);
+ msg[n++] = status;
+ msg[n++] = scan ? scan->state : 0;
+ msg[n++] = scan ? scan->scanned_limit :
+ PB_REMOTE_MAX_SCAN_QUEUE_SIZE;
+ msg[n++] = scan ? scan->to_secs : 0;
+ break;
+ }
+
+ if (!scan)
+ scan = l_new(struct rem_scan_data, 1);
+
+ rpb_scan = scan;
+
+ if (size == 18) {
+ memcpy(scan->uuid, pkt + 2, 16);
+ scan->fltr = true;
+ scan->state = 0x02; /* Limited */
+ } else {
+ memset(scan->uuid, 0, 16);
+ scan->fltr = false;
+ scan->state = 0x01; /* Unlimited */
+ }
+
+ scan->client = src;
+ scan->net_idx = net_idx;
+ scan->node = node;
+
+ if (!scan->list)
+ scan->list = l_new(uint8_t,
+ 23 * PB_REMOTE_MAX_SCAN_QUEUE_SIZE);
+
+ mesh_io_register_recv_cb(NULL, prvb, 2, scan_pkt, scan);
+
+ scan->to_secs = pkt[1];
+
+ if (pkt[0])
+ scan->scanned_limit = pkt[0];
+ else
+ scan->scanned_limit = PB_REMOTE_MAX_SCAN_QUEUE_SIZE;
+
+ scan->timeout = l_timeout_create(pkt[1],
+ remprv_scan_cancel, scan, NULL);
+
+ /* fall through */
+
+ case OP_REM_PROV_SCAN_GET:
+ /* Compose Scan Status */
+ n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STATUS, msg);
+ msg[n++] = PB_REM_ERR_SUCCESS;
+ msg[n++] = scan ? scan->state : 0;
+ msg[n++] = scan ? scan->scanned_limit :
+ PB_REMOTE_MAX_SCAN_QUEUE_SIZE;
+ msg[n++] = scan ? scan->to_secs : 0;
+ break;
+
+ case OP_REM_PROV_SCAN_STOP:
+ if (size != 0 || !scan)
+ return true;
+
+ remprv_scan_cancel(NULL, scan);
+ return true;
+
+ case OP_REM_PROV_LINK_GET:
+ if (size != 0 || !prov)
+ return true;
+
+ send_prov_status(prov, PB_REM_ERR_SUCCESS);
+ return true;
+
+ case OP_REM_PROV_LINK_OPEN:
+ /* Sanity check args */
+ if (size != 16 && size != 17 && size != 1)
+ return true;
+
+ if (size == 17 && (pkt[16] == 0 || pkt[16] > 0x3c))
+ return true;
+
+ if (size == 1 && pkt[0] > 0x02)
+ return true;
+
+ if (prov) {
+ if (prov->client != src || prov->node != node ||
+ (size == 1 && prov->nppi_proc != pkt[0]) ||
+ (size >= 16 && (prov->nppi_proc != RPR_ADV ||
+ memcmp(prov->u.adv.uuid, pkt, 16)))) {
+
+ /* Send Reject (in progress) */
+ send_prov_status(prov, PB_REM_ERR_CANNOT_OPEN);
+ n = mesh_model_opcode_set(
+ OP_REM_PROV_LINK_STATUS, msg);
+ msg[n++] = PB_REM_ERR_CANNOT_OPEN;
+ msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE;
+ break;
+ }
+
+ /* Send redundant Success */
+ send_prov_status(prov, PB_REM_ERR_SUCCESS);
+ return true;
+ }
+
+ if (scan && scan->client != src && scan->node != node) {
+ n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg);
+ msg[n++] = PB_REM_ERR_CANNOT_OPEN;
+ msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE;
+ break;
+ }
+
+ print_packet("Remote Prov Link Open", pkt, size);
+
+ remprv_scan_cancel(NULL, scan);
+
+ rpb_prov = prov = l_new(struct rem_prov_data, 1);
+ prov->client = src;
+ prov->net_idx = net_idx;
+ prov->node = node;
+ prov->state = PB_REMOTE_STATE_LINK_OPENING;
+
+ if (size == 1) {
+ status = start_dev_key_refresh(node, pkt[0], prov);
+
+ } else {
+ if (size == 17)
+ prov->timeout = l_timeout_create(pkt[16],
+ remprv_prov_cancel, prov, NULL);
+
+
+ prov->nppi_proc = RPR_ADV;
+ memcpy(prov->u.adv.uuid, pkt, 16);
+ status = pb_adv_reg(true, srv_open, srv_close, srv_rx,
+ srv_ack, pkt, prov);
+ }
+
+ if (status)
+ send_prov_status(prov, PB_REM_ERR_SUCCESS);
+ else {
+ n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg);
+ msg[n++] = PB_REM_ERR_CANNOT_OPEN;
+ msg[n++] = PB_REMOTE_STATE_IDLE;
+ remprv_prov_cancel(NULL, prov);
+ }
+
+ return true;
+
+ case OP_REM_PROV_LINK_CLOSE:
+ if (size != 1)
+ return true;
+
+ if (!prov || prov->node != node || prov->client != src)
+ return true;
+
+ prov->state = PB_REMOTE_STATE_LINK_CLOSING;
+ mesh_io_send_cancel(NULL, &pkt_filter, sizeof(pkt_filter));
+ send_prov_status(prov, PB_REM_ERR_SUCCESS);
+ if (pkt[0] == 0x02) {
+ msg[0] = PROV_FAILED;
+ msg[1] = PROV_ERR_CANT_ASSIGN_ADDR;
+ if (prov->nppi_proc == RPR_ADV)
+ prov->u.adv.tx(prov->trans_data, msg, 2);
+ else
+ prov->u.nppi.rx_cb(prov->trans_data, msg, 2);
+ }
+
+ if (prov->nppi_proc == RPR_ADV)
+ pb_adv_unreg(prov);
+
+ else if (prov->nppi_proc <= RPR_COMP) {
+ /* Hard or Soft refresh of local node, based on NPPI */
+ node_refresh(prov->node, (prov->nppi_proc == RPR_ADDR),
+ &prov->u.nppi.info);
+ }
+
+ remprv_prov_cancel(NULL, prov);
+
+ return true;
+
+ case OP_REM_PROV_PDU_SEND:
+ if (!prov || prov->node != node || prov->client != src)
+ return true;
+
+ if (size < 2)
+ return true;
+
+
+ prov->cli_pdu_num = *pkt++;
+ size--;
+ prov->state = PB_REMOTE_STATE_OB_PKT_TX;
+
+ if (prov->nppi_proc == RPR_ADV)
+ prov->u.adv.tx(prov->trans_data, pkt, size);
+ else {
+ srv_ack(prov, prov->cli_pdu_num);
+ prov->u.nppi.rx_cb(prov->trans_data, pkt, size);
+ }
+
+ return true;
+ }
+
+send_pkt:
+ l_info("PB-SVR: src %4.4x dst %4.4x", unicast, src);
+ print_packet("App Tx", msg, n);
+ mesh_model_send(node, 0, src, APP_IDX_DEV_LOCAL,
+ net_idx, DEFAULT_TTL, segmented, n, msg);
+
+ return true;
+}
+
+static void remprv_srv_unregister(void *user_data)
+{
+}
+
+static const struct mesh_model_ops ops = {
+ .unregister = remprv_srv_unregister,
+ .recv = remprv_srv_pkt,
+ .bind = NULL,
+ .sub = NULL,
+ .pub = NULL
+};
+
+void remote_prov_server_init(struct mesh_node *node, uint8_t ele_idx)
+{
+ mesh_model_register(node, ele_idx, REM_PROV_SRV_MODEL, &ops, node);
+}