2023-01-24 20:26:43

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 00/13] Mesh v1.1 additions

This patch-set includes implementations for Client/Server Remote
Provisioning, and Client/Server Mesh Private Beacons

v2 - v4: Clean-up checkpatch warnings.

Brian Gix (13):
doc/mesh: Add Remote Provisioning DBus APIs
mesh: Add Remote Provisioning
tools/mesh: Optimize for multiple RPR servers and NPPI
mesh: Rename parameter list per crypto usage
unit/mesh: Add unit testing of Mesh Private Beaconing
mesh: Add storage of Mesh Private Beacon settings
mesh: Add Mesh Private Beacon server
mesh: Add Tx/Rx support of Mesh Private Beacons
mesh: Add internal Mesh Private Beacon model
tools/mesh: Add support for Mesh Private Beacons
mesh: Switch beaconing net key
mesh: Fix Checksmatch warning
mesh: Remove unused byte swap for ScanBuild

Makefile.mesh | 2 +
doc/mesh-api.txt | 140 ++++++-
mesh/cfgmod-server.c | 2 +-
mesh/crypto.c | 13 +-
mesh/crypto.h | 2 +-
mesh/keyring.c | 29 +-
mesh/keyring.h | 1 +
mesh/manager.c | 535 +++++++++++++++++++-----
mesh/mesh-config-json.c | 428 +++++++++++++------
mesh/mesh-config.h | 12 +-
mesh/model.c | 37 +-
mesh/net-keys.c | 506 +++++++++++++++++-----
mesh/net-keys.h | 11 +-
mesh/net.c | 188 ++++++---
mesh/net.h | 6 +-
mesh/node.c | 326 ++++++++++++---
mesh/node.h | 5 +
mesh/pb-adv.c | 4 +-
mesh/pb-adv.h | 2 +-
mesh/prov-acceptor.c | 87 ++--
mesh/prov-initiator.c | 269 +++++++++++-
mesh/prov.h | 4 +-
mesh/provision.h | 23 +-
mesh/prv-beacon.h | 36 ++
mesh/prvbeac-server.c | 128 ++++++
mesh/remprv-server.c | 907 ++++++++++++++++++++++++++++++++++++++++
mesh/remprv.h | 78 ++++
tools/mesh-cfgclient.c | 488 +++++++++++++++++----
tools/mesh/cfgcli.c | 99 ++++-
tools/mesh/mesh-db.c | 37 +-
tools/mesh/mesh-db.h | 1 +
tools/mesh/remote.c | 122 ++++++
tools/mesh/remote.h | 9 +
tools/mesh/util.c | 3 +
unit/test-mesh-crypto.c | 128 ++++--
35 files changed, 3979 insertions(+), 689 deletions(-)
create mode 100644 mesh/prv-beacon.h
create mode 100644 mesh/prvbeac-server.c
create mode 100644 mesh/remprv-server.c
create mode 100644 mesh/remprv.h

--
2.39.1



2023-01-24 20:26:45

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 01/13] doc/mesh: Add Remote Provisioning DBus APIs

From: Brian Gix <[email protected]>

Remote Provisioning (introduced in Mesh Profile Specification v1.1)

* Allows Provisioners to use a remote server to scan for and
provision devices.

* Allows Config managers to reprovision existing nodes to:
* Refresh Device Keys
* Reassign Node Addresses
* Refresh Node Composition
---
doc/mesh-api.txt | 140 +++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 134 insertions(+), 6 deletions(-)

diff --git a/doc/mesh-api.txt b/doc/mesh-api.txt
index 85de6705e..393ae566f 100644
--- a/doc/mesh-api.txt
+++ b/doc/mesh-api.txt
@@ -484,7 +484,28 @@ Methods:
Specifies number of seconds for scanning to be active.
If set to 0 or if this key is not present, then the
scanning will continue until UnprovisionedScanCancel()
- or AddNode() methods are called.
+ or AddNode() methods are called. If not present, and a
+ remote server is specified, the default timeout will be
+ 60 seconds.
+
+ uint16 Server
+ Specifies a remote server on which to perform scanning.
+ If not present, scanning will be local. If present,
+ the timeout must be between 1 and 60 seconds.
+
+ uint16 Subnet
+ Specifies a subnet for the remote server. If not
+ present, primary subnet will be used. If Server not
+ present, the Subnet will be ignored.
+
+ array{byte}[16] Filter
+ Specifies a specific UUID to search for. If not
+ present, all found UUIDs will be returned.
+
+ uint8 array Extended
+ Specifies variable number of Bluetooth AD types to
+ return with scan result. Only valid if a Filter has been
+ specified.

Each time a unique unprovisioned beacon is heard, the
ScanResult() method on the app will be called with the result.
@@ -514,8 +535,48 @@ Methods:
of the unprovisioned device to be added to the network.

The options parameter is a dictionary that may contain
- additional configuration info (currently an empty placeholder
- for forward compatibility).
+ additional optional configuration info:
+
+ uint16 Server
+ Specifies a remote server to perform provisioning on. If
+ not present, provisioning will be done locally.
+
+ uint16 Subnet
+ Specifies a subnet for the remote server. If not
+ present, primary subnet will be used. If Server not
+ present Subnet will be ignored.
+
+ PossibleErrors:
+ org.bluez.mesh.Error.InvalidArguments
+ org.bluez.mesh.Error.NotAuthorized
+
+ void Reprovision(uint16 unicast, dict options)
+
+ This method is used by the application that supports
+ org.bluez.mesh.Provisioner1 interface to perform one of the
+ Node Provisioning Protocol Interface procedures with a remote
+ node to refresh its device key, unicast address, and
+ composition. Remote node being reprovisioned must have the
+ Remote Provisioning Server model.
+
+ The unicast parameter is the 16-bit primary node address of
+ the remote node being reprovisioned.
+
+ The options parameter is a dictionary that may contain
+ additional optional configuration info:
+
+ uint8 NPPI
+ Specifies the Node Provisioning Protocol Interface
+ behavior, as defined in the Mesh Profile Specification:
+ 0 - Device Key Refresh Only
+ 1 - Node Address Refresh
+ 2 - Node Composition Refresh
+
+ If not present, behavior 0 will be used.
+
+ uint16 Subnet
+ Specifies the subnet remote node is on. If not
+ present, primary subnet will be tried.

PossibleErrors:
org.bluez.mesh.Error.InvalidArguments
@@ -1055,11 +1116,19 @@ Object path freely definable
byte remote device UUID (always), a 16 bit mask of OOB
authentication flags (optional), and a 32 bit URI hash (if URI
bit set in OOB mask). Whether these fields exist or not is a
- decision of the remote device.
+ decision of the unprovisioned device.

The options parameter is a dictionary that may contain
- additional scan result info (currently an empty placeholder for
- forward compatibility).
+ additional optional configuration info:
+
+ uint16 Server
+ Specifies the remote server that received the
+ Unprovisioned beacon. If not present, beacon was
+ received locally.
+
+ uint8 array ExtendedData
+ If Extended data was requested during scanning, any
+ received data will be returned here.

If a beacon with a UUID that has already been reported is
recieved by the daemon, it will be silently discarded unless it
@@ -1082,6 +1151,26 @@ Object path freely definable
PossibleErrors:
org.bluez.mesh.Error.Abort

+ uint16 unicast RequestReprovData(uint16 original, uint8 count)
+
+ This method is implemented by a Provisioner capable application
+ and is called when the remote node being reprovisioned has been
+ fully authenticated and confirmed. This method will only be
+ called if the NPPI-1 procedure (Node Address Refresh) is being
+ performed.
+
+ The original parameter is the current unicast address of the
+ node being reprovisioned.
+
+ The count parameter is the number of consecutive unicast
+ addresses the remote node is requesting.
+
+ Return Parameter:
+ unicast - Primary Unicast address of the new node
+
+ PossibleErrors:
+ org.bluez.mesh.Error.Abort
+
void AddNodeComplete(array{byte}[16] uuid, uint16 unicast, uint8 count)

This method is called when the node provisioning initiated
@@ -1096,6 +1185,33 @@ Object path freely definable
The new node may now be sent messages using the credentials
supplied by the RequestProvData method.

+ void ReprovComplete(uint16 original, uint8 nppi, uint16 unicast,
+ uint8 count)
+
+ This method is called when the node Reprovisioning initiated
+ by a Reprovision() method call successfully completed.
+
+ The original parameter is the former primary address of the
+ node that has been reprovisioned.
+
+ The nppi parameter indicates which NPPI behavior was performed.
+ If behavior 1 or 2 was performed, the node is materially
+ different than it was before reprovisioning, and Composition,
+ Bindings, Publication and Subscription settings should be
+ refreshed.
+
+ The unicast parameter is the new primary address that has been
+ assigned to the node, If NPPI behavior 1 was performed this
+ value may be different from the original. If behavior 0 or 2
+ was performed, the original and new primary address should be
+ the same.
+
+ The count parameter is the number of unicast addresses assigned
+ to the node.
+
+ The node may now be sent messages using the credentials
+ supplied by the RequestReprovData method.
+
void AddNodeFailed(array{byte}[16] uuid, string reason)

This method is called when the node provisioning initiated by
@@ -1109,6 +1225,18 @@ Object path freely definable
"decryption-error", "unexpected-error",
"cannot-assign-addresses".

+ void ReprovFailed(uint16 unicast, string reason)
+
+ This method is called when node reprovisioning initiated by
+ Reprovision() has failed. If reprovisioning has failed, the
+ prior credentials of the remote node may still be valid.
+
+ The reason parameter identifies the reason for provisioning
+ failure. The defined values are: "aborted", "timeout",
+ "bad-pdu", "confirmation-failed", "out-of-resources",
+ "decryption-error", "unexpected-error",
+ "cannot-assign-addresses".
+
Provisioning Agent Hierarchy
============================
Service unique name
--
2.39.1


2023-01-24 20:26:47

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 04/13] mesh: Rename parameter list per crypto usage

From: Brian Gix <[email protected]>

The derived key generated by the "nkpk" salt and network master key is
used to create Private Beacons as of Mesh Profile Specification v1.1
---
mesh/crypto.c | 4 ++--
mesh/crypto.h | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/mesh/crypto.c b/mesh/crypto.c
index 668d16877..3754cb012 100644
--- a/mesh/crypto.c
+++ b/mesh/crypto.c
@@ -251,9 +251,9 @@ bool mesh_crypto_nkbk(const uint8_t n[16], uint8_t beacon_key[16])
return crypto_128(n, "nkbk", beacon_key);
}

-bool mesh_crypto_nkpk(const uint8_t n[16], uint8_t proxy_key[16])
+bool mesh_crypto_nkpk(const uint8_t n[16], uint8_t private_key[16])
{
- return crypto_128(n, "nkpk", proxy_key);
+ return crypto_128(n, "nkpk", private_key);
}

bool mesh_crypto_k3(const uint8_t n[16], uint8_t out64[8])
diff --git a/mesh/crypto.h b/mesh/crypto.h
index c31abbbbd..55789886e 100644
--- a/mesh/crypto.h
+++ b/mesh/crypto.h
@@ -26,7 +26,7 @@ bool mesh_aes_ecb_one(const uint8_t key[16],
const uint8_t plaintext[16], uint8_t encrypted[16]);
bool mesh_crypto_nkik(const uint8_t network_key[16], uint8_t identity_key[16]);
bool mesh_crypto_nkbk(const uint8_t network_key[16], uint8_t beacon_key[16]);
-bool mesh_crypto_nkpk(const uint8_t network_key[16], uint8_t proxy_key[16]);
+bool mesh_crypto_nkpk(const uint8_t network_key[16], uint8_t private_key[16]);
bool mesh_crypto_identity(const uint8_t net_key[16], uint16_t addr,
uint8_t id[16]);
bool mesh_crypto_beacon_cmac(const uint8_t encryption_key[16],
--
2.39.1


2023-01-24 20:26:50

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 05/13] unit/mesh: Add unit testing of Mesh Private Beaconing

From: Brian Gix <[email protected]>

This includes the Sample Data from the Mesh Profile specification v1.1,
and validates that the beacon crypto functions work as expected.
---
unit/test-mesh-crypto.c | 128 ++++++++++++++++++++++++++++++++--------
1 file changed, 103 insertions(+), 25 deletions(-)

diff --git a/unit/test-mesh-crypto.c b/unit/test-mesh-crypto.c
index f9b7d81da..79add0c29 100644
--- a/unit/test-mesh-crypto.c
+++ b/unit/test-mesh-crypto.c
@@ -633,6 +633,36 @@ static const struct mesh_crypto_test s8_4_3 = {
.beacon = "01003ecaff672f673370123456788ea261582f364f6f",
};

+static const struct mesh_crypto_test s8_4_6_1 = {
+ .name = "8.4.6.1 Private Beacon IVU",
+
+ .net_key = "f7a2a44f8e8a8029064f173ddc1e2b00",
+ .iv_index = 0x1010abcd,
+
+ .enc_key = "6be76842460b2d3a5850d4698409f1bb",
+ .rand = "435f18f85cf78a3121f58478a5",
+
+ .beacon_type = 0x02,
+ .beacon_flags = 0x02,
+ .beacon_cmac = "f3174f022a514741",
+ .beacon = "02435f18f85cf78a3121f58478a561e488e7cbf3174f022a514741",
+};
+
+static const struct mesh_crypto_test s8_4_6_2 = {
+ .name = "8.4.6.2 Private Beacon IVU Complete",
+
+ .net_key = "3bbb6f1fbd53e157417f308ce7aec58f",
+ .iv_index = 0x00000000,
+
+ .enc_key = "ca478cdac626b7a8522d7272dd124f26",
+ .rand = "1b998f82927535ea6f3076f422",
+
+ .beacon_type = 0x02,
+ .beacon_flags = 0x00,
+ .beacon_cmac = "2f0ffb94cf97f881",
+ .beacon = "021b998f82927535ea6f3076f422ce827408ab2f0ffb94cf97f881",
+};
+
static const struct mesh_crypto_test s8_6_2 = {
.name = "8.6.2 Service Data using Node Identity",

@@ -683,6 +713,17 @@ static void verify_data(const char *label, unsigned int indent,
l_free(str);
}

+static void verify_bool(const char *label, unsigned int indent,
+ bool sample, bool data)
+{
+ l_info("%-20s =%*c%s", label, 1 + (indent * 2), ' ',
+ sample ? "true" : "false");
+ l_info("%-20s %*c%s => %s", "", 1 + (indent * 2), ' ',
+ data ? "true" : "false",
+ EVALNUM(sample, data));
+ EXITNUM(sample, data);
+}
+
static void verify_bool_not_both(const char *label, unsigned int indent,
bool sample, bool data)
{
@@ -926,7 +967,7 @@ static void check_encrypt(const struct mesh_crypto_test *keys)
uint8_t *dev_key;
uint8_t *app_key;
uint8_t *net_key;
- uint8_t nid;
+ uint8_t nid = 0;
uint8_t enc_key[16];
uint8_t priv_key[16];
uint8_t net_nonce[13];
@@ -960,8 +1001,7 @@ static void check_encrypt(const struct mesh_crypto_test *keys)

show_data("NetworkKey", 0, net_key, 16);

- if (keys->akf) {
- mesh_crypto_k4(app_key, &key_aid);
+ if (keys->akf && mesh_crypto_k4(app_key, &key_aid)) {
key_aid |= KEY_ID_AKF;
} else {
key_aid = 0;
@@ -1265,19 +1305,20 @@ static void check_decrypt_segment(const struct mesh_crypto_test *keys,
uint8_t net_clr[29];
uint64_t net_mic64, calc_net_mic64;
uint32_t hdr, net_mic32, calc_net_mic32;
- bool ctl, segmented, relay, szmic, key_akf;
+ bool ctl, segmented, relay, szmic, key_akf, status;
uint8_t ttl, opcode, key_aid, segO, segN;
uint32_t seq;
uint16_t src, dst, seqZero;

memcpy(net_clr, pkt, pkt_len);
show_data("NetworkMessage", 0, pkt, pkt_len);
- mesh_crypto_packet_decode(pkt, pkt_len,
+ status = mesh_crypto_packet_decode(pkt, pkt_len,
false, net_clr, keys->iv_index,
enc_key, priv_key);
show_data("Decoded", 0, net_clr, pkt_len);

- mesh_crypto_packet_parse(net_clr, pkt_len,
+ if (status)
+ status = mesh_crypto_packet_parse(net_clr, pkt_len,
&ctl, &ttl, &seq,
&src, &dst,
NULL, &opcode,
@@ -1286,6 +1327,10 @@ static void check_decrypt_segment(const struct mesh_crypto_test *keys,
&segO, &segN,
&msg, &msg_len);

+ verify_bool("Crypto Decode-Parse", 0, true, status);
+ if (!status)
+ return;
+
if (ctl) {
net_mic64 = l_get_be64(pkt + pkt_len - 8);
show_data("EncryptedPayload", 7, pkt + 7, pkt_len - 7 - 8);
@@ -1416,7 +1461,7 @@ static void check_decrypt(const struct mesh_crypto_test *keys)
uint16_t app_msg_len = 0;
uint32_t calc_net_mic32, net_mic32 = 0;
uint64_t calc_net_mic64, net_mic64 = 0;
- bool net_ctl, net_segmented, net_rly, net_akf;
+ bool net_ctl, net_segmented, net_rly, net_akf, status;
uint8_t net_aid, net_ttl, nid, net_segO, net_segN = 0;
uint32_t net_seq, hdr, seqZero = 0;
uint16_t net_src, net_dst;
@@ -1501,8 +1546,14 @@ static void check_decrypt(const struct mesh_crypto_test *keys)
net_msg = packet + 7;
net_msg_len = packet_len - 7;

- mesh_crypto_network_clarify(packet, priv_key, keys->iv_index,
- &net_ctl, &net_ttl, &net_seq, &net_src);
+ status = mesh_crypto_network_clarify(packet, priv_key,
+ keys->iv_index, &net_ctl, &net_ttl, &net_seq,
+ &net_src);
+
+ verify_bool("Crypto Clarify", 0, true, status);
+ if (!status)
+ return;
+

show_str("Packet", 0, keys->packet[i]);

@@ -1731,42 +1782,67 @@ static void check_beacon(const struct mesh_crypto_test *keys)
{
uint8_t *net_key;
uint8_t *beacon_cmac;
- uint8_t beacon[22];
+ uint8_t *random = NULL;
+ uint8_t beacon[29];
uint8_t enc_key[16];
uint8_t net_id[8];
uint8_t cmac[8];
- uint64_t cmac_tmp;
+ uint64_t cmac_tmp = 0;
+
+ if (keys->beacon_type < 1 || keys->beacon_type > 2)
+ verify_uint8("Unknown Beacon", 0, true,
+ (keys->beacon_type >= 1 || keys->beacon_type <= 2));

net_key = l_util_from_hexstring(keys->net_key, NULL);
beacon_cmac = l_util_from_hexstring(keys->beacon_cmac, NULL);

- mesh_crypto_nkbk(net_key, enc_key);
+ if (keys->beacon_type == 1) {
+ mesh_crypto_nkbk(net_key, enc_key);
+ } else {
+ mesh_crypto_nkpk(net_key, enc_key);
+ random = l_util_from_hexstring(keys->rand, NULL);
+ }
+
mesh_crypto_k3(net_key, net_id);

l_info(COLOR_BLUE "[%s]" COLOR_OFF, keys->name);

verify_data("NetworkKey", 0, keys->net_key, net_key, 16);
+ show_uint8("Beacon Flags", 0, keys->beacon_flags);
show_uint32("IVindex", 0, keys->iv_index);

verify_data("BeaconKey", 0, keys->enc_key, enc_key, 16);
- verify_data("NetworkID", 0, keys->net_id, net_id, 8);

beacon[0] = keys->beacon_type;
- beacon[1] = keys->beacon_flags;
- memcpy(beacon + 2, net_id, 8);
- l_put_be32(keys->iv_index, beacon + 10);
- mesh_crypto_beacon_cmac(enc_key, net_id, keys->iv_index,
- !!(keys->beacon_flags & 0x01),
- !!(keys->beacon_flags & 0x02),
- &cmac_tmp);
-
- l_put_be64(cmac_tmp, cmac);
- l_put_be64(cmac_tmp, beacon + 14);
- verify_data("BeaconCMAC", 0, keys->beacon_cmac, cmac, 8);
- verify_data("Beacon", 0, keys->beacon, beacon, sizeof(beacon));
+ if (keys->beacon_type == 1) {
+ verify_data("NetworkID", 0, keys->net_id, net_id, 8);
+ beacon[1] = keys->beacon_flags;
+ memcpy(beacon + 2, net_id, 8);
+ l_put_be32(keys->iv_index, beacon + 10);
+ mesh_crypto_beacon_cmac(enc_key, net_id, keys->iv_index,
+ !!(keys->beacon_flags & 0x01),
+ !!(keys->beacon_flags & 0x02),
+ &cmac_tmp);
+
+ l_put_be64(cmac_tmp, cmac);
+ l_put_be64(cmac_tmp, beacon + 14);
+ verify_data("BeaconCMAC", 0, keys->beacon_cmac, cmac, 8);
+ verify_data("SNBeacon", 0, keys->beacon, beacon, 22);
+ } else {
+ show_data("Random", 0, random, sizeof(random));
+ memcpy(beacon + 1, random, 13);
+ beacon[14] = keys->beacon_flags;
+ l_put_be32(keys->iv_index, beacon + 15);
+ mesh_crypto_aes_ccm_encrypt(random, enc_key, NULL, 0,
+ beacon + 14, 5,
+ beacon + 14, NULL, 8);
+ verify_data("BeaconMIC", 0, keys->beacon_cmac, beacon + 19, 8);
+ verify_data("PrivBeacon", 0, keys->beacon, beacon, 27);
+ }

l_info("");

+ l_free(random);
l_free(beacon_cmac);
l_free(net_key);
}
@@ -2071,6 +2147,8 @@ int main(int argc, char *argv[])

/* Section 8.4 Beacon Sample Data */
check_beacon(&s8_4_3);
+ check_beacon(&s8_4_6_1);
+ check_beacon(&s8_4_6_2);

/* Section 8.6 Mesh Proxy Service sample data */
check_id_beacon(&s8_6_2);
--
2.39.1


2023-01-24 20:26:48

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 03/13] tools/mesh: Optimize for multiple RPR servers and NPPI

From: Brian Gix <[email protected]>

These changes allow the mesh-cfgclient tool to request remote node
compositions from page 128. Depending on the differences between
there and what is stored in the local configuration database, it may
recomend reprovisioning with NPPI-1 (Address Refresh) or NPPI-2
(Composition Refresh).

Additionally, NPPI-0 may be performed to refresh the Device Key only.
---
tools/mesh-cfgclient.c | 479 ++++++++++++++++++++++++++++++++++-------
tools/mesh/cfgcli.c | 37 +++-
tools/mesh/mesh-db.c | 37 +++-
tools/mesh/mesh-db.h | 1 +
tools/mesh/remote.c | 122 +++++++++++
tools/mesh/remote.h | 9 +
6 files changed, 598 insertions(+), 87 deletions(-)

diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
index 237afbb5f..f3e9af8fb 100644
--- a/tools/mesh-cfgclient.c
+++ b/tools/mesh-cfgclient.c
@@ -43,6 +43,7 @@

#define CFG_SRV_MODEL 0x0000
#define CFG_CLI_MODEL 0x0001
+#define RPR_SVR_MODEL 0xFFFF0004

#define UNPROV_SCAN_MAX_SECS 300

@@ -83,8 +84,12 @@ struct meshcfg_node {

struct unprov_device {
time_t last_seen;
- int16_t rssi;
+ int id;
+ uint32_t uri_hash;
uint8_t uuid[16];
+ int16_t rssi;
+ uint16_t server;
+ uint16_t oob_info;
};

struct generic_request {
@@ -96,8 +101,16 @@ struct generic_request {
const char *str;
};

+struct scan_data {
+ uint16_t dst;
+ uint16_t secs;
+};
+
+static void *finalized = L_UINT_TO_PTR(-1);
+
static struct l_dbus *dbus;

+static struct l_timeout *scan_timeout;
static struct l_queue *node_proxies;
static struct l_dbus_proxy *net_proxy;
static struct meshcfg_node *local;
@@ -197,23 +210,57 @@ static bool parse_argument_on_off(int argc, char *argv[], bool *value)
static bool match_device_uuid(const void *a, const void *b)
{
const struct unprov_device *dev = a;
- const uint8_t *uuid = b;

- return (memcmp(dev->uuid, uuid, 16) == 0);
+ if (a == finalized)
+ return false;
+
+ return memcmp(dev->uuid, b, 16) == 0;
}

-static void print_device(void *a, void *b)
+static bool match_by_id(const void *a, const void *b)
{
const struct unprov_device *dev = a;
- struct tm *tm = localtime(&dev->last_seen);
+ int id = L_PTR_TO_UINT(b);
+
+ if (a == finalized)
+ return false;
+
+ l_info("test %d %d", dev->id, id);
+ return dev->id == id;
+}
+
+static bool match_by_srv_uuid(const void *a, const void *b)
+{
+ const struct unprov_device *dev = a;
+ const struct unprov_device *new_dev = b;
+
+ if (a == finalized)
+ return false;
+
+ return (dev->server == new_dev->server) &&
+ (memcmp(dev->uuid, new_dev->uuid, 16) == 0);
+}
+
+static void print_device(void *a, void *b)
+{
+ struct unprov_device *dev = a;
+ int *cnt = b;
+ struct tm *tm;
char buf[80];
char *str;

+ if (a == finalized)
+ return;
+
+ tm = localtime(&dev->last_seen);
assert(strftime(buf, sizeof(buf), "%c", tm));
+ (*cnt)++;

+ dev->id = *cnt;
str = l_util_hexstring_upper(dev->uuid, sizeof(dev->uuid));
- bt_shell_printf("UUID: %s, RSSI %d, Seen: %s\n",
- str, dev->rssi, buf);
+ bt_shell_printf(COLOR_YELLOW "#%d" COLOR_OFF
+ " UUID: %s, RSSI %d, Server: %4.4x\n Seen: %s\n",
+ *cnt, str, dev->rssi, dev->server, buf);

l_free(str);
}
@@ -794,15 +841,56 @@ static void scan_reply(struct l_dbus_proxy *proxy, struct l_dbus_message *msg,

static void scan_setup(struct l_dbus_message *msg, void *user_data)
{
- uint16_t secs = (uint16_t) L_PTR_TO_UINT(user_data);
+ struct scan_data *data = user_data;
struct l_dbus_message_builder *builder;

builder = l_dbus_message_builder_new(msg);
l_dbus_message_builder_enter_array(builder, "{sv}");
- append_dict_entry_basic(builder, "Seconds", "q", &secs);
+ append_dict_entry_basic(builder, "Seconds", "q", &data->secs);
+ append_dict_entry_basic(builder, "Server", "q", &data->dst);
l_dbus_message_builder_leave_array(builder);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
+
+ /* Destination info not needed after call */
+ l_free(data);
+}
+
+static void scan_start(void *user_data, uint16_t dst, uint32_t model)
+{
+ struct scan_data *data;
+
+ if (model != RPR_SVR_MODEL)
+ return;
+
+ data = l_malloc(sizeof(struct scan_data));
+ data->secs = L_PTR_TO_UINT(user_data);
+ data->dst = dst;
+
+ if (!l_dbus_proxy_method_call(local->mgmt_proxy, "UnprovisionedScan",
+ scan_setup, scan_reply, data, NULL))
+ l_free(data);
+}
+
+static void scan_to(struct l_timeout *timeout, void *user_data)
+{
+ int cnt = 0;
+
+ if (l_queue_peek_head(devices) != finalized)
+ l_queue_push_head(devices, finalized);
+
+ l_timeout_remove(timeout);
+ scan_timeout = NULL;
+ bt_shell_printf(COLOR_YELLOW "Unprovisioned devices:\n" COLOR_OFF);
+ l_queue_foreach(devices, print_device, &cnt);
+}
+
+static void free_devices(void *a)
+{
+ if (a == finalized)
+ return;
+
+ l_free(a);
}

static void cmd_scan_unprov(int argc, char *argv[])
@@ -820,21 +908,28 @@ static void cmd_scan_unprov(int argc, char *argv[])
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}

- if (argc == 3)
+ if (argc == 3) {
sscanf(argv[2], "%u", &secs);

- if (secs > UNPROV_SCAN_MAX_SECS)
- secs = UNPROV_SCAN_MAX_SECS;
+ if (secs > UNPROV_SCAN_MAX_SECS)
+ secs = UNPROV_SCAN_MAX_SECS;
+ } else
+ secs = 60;

- if (enable)
- l_dbus_proxy_method_call(local->mgmt_proxy, "UnprovisionedScan",
- scan_setup, scan_reply,
- L_UINT_TO_PTR(secs), NULL);
- else
+ l_timeout_remove(scan_timeout);
+ scan_timeout = NULL;
+
+ if (enable) {
+ l_queue_clear(devices, free_devices);
+ remote_foreach_model(scan_start, L_UINT_TO_PTR(secs));
+ scan_timeout = l_timeout_create(secs, scan_to, NULL, NULL);
+ } else {
+ /* Mark devices queue as finalized */
+ l_queue_push_head(devices, finalized);
l_dbus_proxy_method_call(local->mgmt_proxy,
"UnprovisionedScanCancel",
NULL, NULL, NULL, NULL);
-
+ }
}

static uint8_t *parse_key(struct l_dbus_message_iter *iter, uint16_t id,
@@ -1030,8 +1125,10 @@ static void cmd_export_db(int argc, char *argv[])

static void cmd_list_unprov(int argc, char *argv[])
{
+ int cnt = 0;
+
bt_shell_printf(COLOR_YELLOW "Unprovisioned devices:\n" COLOR_OFF);
- l_queue_foreach(devices, print_device, NULL);
+ l_queue_foreach(devices, print_device, &cnt);
}

static void cmd_list_nodes(int argc, char *argv[])
@@ -1505,32 +1602,56 @@ static void add_node_reply(struct l_dbus_proxy *proxy,
bt_shell_printf("Provisioning started\n");
}

-static void add_node_setup(struct l_dbus_message *msg, void *user_data)
+static void reprov_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *msg, void *user_data)
{
- char *str = user_data;
- size_t sz;
- unsigned char *uuid;
- struct l_dbus_message_builder *builder;
+ if (l_dbus_message_is_error(msg)) {
+ const char *name;

- uuid = l_util_from_hexstring(str, &sz);
- if (!uuid || sz != 16 || !l_uuid_is_valid(uuid)) {
- l_error("Failed to generate UUID array from %s", str);
+ prov_in_progress = false;
+ l_dbus_message_get_error(msg, &name, NULL);
+ l_error("Failed to start provisioning: %s", name);
return;
}

+ bt_shell_printf("Reprovisioning started\n");
+}
+
+static void reprovision_setup(struct l_dbus_message *msg, void *user_data)
+{
+ uint16_t target = L_PTR_TO_UINT(user_data);
+ uint8_t nppi = L_PTR_TO_UINT(user_data) >> 16;
+ struct l_dbus_message_builder *builder;
+
builder = l_dbus_message_builder_new(msg);
- append_byte_array(builder, uuid, 16);
+ l_dbus_message_builder_append_basic(builder, 'q', &target);
l_dbus_message_builder_enter_array(builder, "{sv}");
/* TODO: populate with options when defined */
+ append_dict_entry_basic(builder, "NPPI", "y", &nppi);
l_dbus_message_builder_leave_array(builder);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
+}

- l_free(uuid);
+static void add_node_setup(struct l_dbus_message *msg, void *user_data)
+{
+ struct unprov_device *dev = user_data;
+ struct l_dbus_message_builder *builder;
+
+ builder = l_dbus_message_builder_new(msg);
+ append_byte_array(builder, dev->uuid, 16);
+ l_dbus_message_builder_enter_array(builder, "{sv}");
+ /* TODO: populate with options when defined */
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
}

static void cmd_start_prov(int argc, char *argv[])
{
+ struct unprov_device *dev = NULL;
+ int id;
+
if (!local || !local->proxy || !local->mgmt_proxy) {
bt_shell_printf("Node is not attached\n");
return;
@@ -1541,14 +1662,96 @@ static void cmd_start_prov(int argc, char *argv[])
return;
}

- if (!argv[1] || (strlen(argv[1]) != 32)) {
+ if (!argv[1]) {
+ bt_shell_printf(COLOR_RED "Requires UUID\n" COLOR_RED);
+ return;
+ }
+
+ if (*(argv[1]) == '#') {
+ if (sscanf(argv[1] + 1, "%d", &id) == 1)
+ dev = l_queue_find(devices, match_by_id,
+ L_UINT_TO_PTR(id));
+
+ if (!dev) {
+ bt_shell_printf(COLOR_RED "unknown id\n" COLOR_RED);
+ return;
+ }
+ } else if (strlen(argv[1]) == 32) {
+ size_t sz;
+ uint8_t *uuid = l_util_from_hexstring(argv[1], &sz);
+
+ if (sz != 16) {
+ bt_shell_printf(COLOR_RED "Invalid UUID\n" COLOR_RED);
+ return;
+ }
+
+ dev = l_queue_find(devices, match_device_uuid, uuid);
+
+ if (!dev) {
+ dev = l_new(struct unprov_device, 1);
+ memcpy(dev->uuid, uuid, 16);
+ l_queue_push_tail(devices, dev);
+ }
+
+ l_free(uuid);
+
+ } else {
bt_shell_printf(COLOR_RED "Requires UUID\n" COLOR_RED);
return;
}

if (l_dbus_proxy_method_call(local->mgmt_proxy, "AddNode",
add_node_setup, add_node_reply,
- argv[1], NULL))
+ dev, NULL))
+ prov_in_progress = true;
+}
+
+static void cmd_start_reprov(int argc, char *argv[])
+{
+ uint16_t target = 0;
+ uint8_t nppi = 0;
+
+ if (!local || !local->proxy || !local->mgmt_proxy) {
+ bt_shell_printf("Node is not attached\n");
+ return;
+ }
+
+ if (prov_in_progress) {
+ bt_shell_printf("Provisioning is already in progress\n");
+ return;
+ }
+
+ if (!argv[1]) {
+ bt_shell_printf(COLOR_RED "Requires Unicast\n" COLOR_RED);
+ return;
+ }
+
+ if (argv[2]) {
+ char *end;
+
+ nppi = strtol(argv[2], &end, 16);
+ }
+
+ if (strlen(argv[1]) == 4) {
+ char *end;
+
+ target = strtol(argv[1], &end, 16);
+
+ if (end != (argv[1] + 4)) {
+ bt_shell_printf(COLOR_RED "Invalid Unicast\n"
+ COLOR_RED);
+ return;
+ }
+
+ } else {
+ bt_shell_printf(COLOR_RED "Requires Unicast\n" COLOR_RED);
+ return;
+ }
+
+ if (l_dbus_proxy_method_call(local->mgmt_proxy, "Reprovision",
+ reprovision_setup, reprov_reply,
+ L_UINT_TO_PTR(target + (nppi << 16)),
+ NULL))
prov_in_progress = true;
}

@@ -1581,6 +1784,8 @@ static const struct bt_shell_menu main_menu = {
"List unprovisioned devices" },
{ "provision", "<uuid>", cmd_start_prov,
"Initiate provisioning"},
+ { "reprovision", "<unicast> [0|1|2]", cmd_start_reprov,
+ "Refresh Device Key"},
{ "node-import", "<uuid> <net_idx> <primary> <ele_count> <dev_key>",
cmd_import_node,
"Import an externally provisioned remote node"},
@@ -1758,18 +1963,34 @@ static void setup_ele_iface(struct l_dbus_interface *iface)
/* TODO: Other methods */
}

+static int sort_rssi(const void *a, const void *b, void *user_data)
+{
+ const struct unprov_device *new_dev = a;
+ const struct unprov_device *dev = b;
+
+ if (b == finalized)
+ return 1;
+
+ return dev->rssi - new_dev->rssi;
+}
+
static struct l_dbus_message *scan_result_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
- struct l_dbus_message_iter iter, opts;
+ struct l_dbus_message_iter iter, opts, var;
+ struct unprov_device result, *dev;
int16_t rssi;
+ uint16_t server = 0;
uint32_t n;
uint8_t *prov_data;
- char *str;
- struct unprov_device *dev;
+ const char *key;
const char *sig = "naya{sv}";

+ if (finalized == l_queue_peek_head(devices))
+ goto done;
+
+
if (!l_dbus_message_get_arguments(msg, sig, &rssi, &iter, &opts)) {
l_error("Cannot parse scan results");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
@@ -1781,42 +2002,72 @@ static struct l_dbus_message *scan_result_call(struct l_dbus *dbus,
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}

- bt_shell_printf("Scan result:\n");
- bt_shell_printf("\t" COLOR_GREEN "rssi = %d\n" COLOR_OFF, rssi);
- str = l_util_hexstring_upper(prov_data, 16);
- bt_shell_printf("\t" COLOR_GREEN "UUID = %s\n" COLOR_OFF, str);
- l_free(str);
-
- if (n >= 18) {
- str = l_util_hexstring_upper(prov_data + 16, 2);
- bt_shell_printf("\t" COLOR_GREEN "OOB = %s\n" COLOR_OFF, str);
- l_free(str);
+ while (l_dbus_message_iter_next_entry(&opts, &key, &var)) {
+ if (!strcmp(key, "Server"))
+ l_dbus_message_iter_get_variant(&var, "q", &server);
}

- if (n >= 22) {
- str = l_util_hexstring_upper(prov_data + 18, 4);
- bt_shell_printf("\t" COLOR_GREEN "URI Hash = %s\n" COLOR_OFF,
- str);
- l_free(str);
- }
+ memcpy(result.uuid, prov_data, 16);
+ result.server = server;
+ result.rssi = rssi;
+ result.id = 0;

- /* TODO: Handle the rest of provisioning data if present */
+ if (n > 16 && n <= 18)
+ result.oob_info = l_get_be16(prov_data + 16);
+ else
+ result.oob_info = 0;
+
+ if (n > 18 && n <= 22)
+ result.uri_hash = l_get_be32(prov_data + 18);
+ else
+ result.uri_hash = 0;
+
+ dev = l_queue_remove_if(devices, match_by_srv_uuid, &result);

- dev = l_queue_find(devices, match_device_uuid, prov_data);
if (!dev) {
- dev = l_new(struct unprov_device, 1);
- memcpy(dev->uuid, prov_data, sizeof(dev->uuid));
- /* TODO: timed self-destructor */
- l_queue_push_tail(devices, dev);
- }
+ bt_shell_printf("\r" COLOR_YELLOW "Results = %d\n" COLOR_OFF,
+ l_queue_length(devices) + 1);
+ dev = l_malloc(sizeof(struct unprov_device));
+ *dev = result;
+
+ } else if (dev->rssi < result.rssi)
+ *dev = result;

- /* Update with the latest rssi */
- dev->rssi = rssi;
dev->last_seen = time(NULL);

+ l_queue_insert(devices, dev, sort_rssi, NULL);
+
+done:
return l_dbus_message_new_method_return(msg);
}

+static struct l_dbus_message *req_reprov_call(struct l_dbus *dbus,
+ struct l_dbus_message *msg,
+ void *user_data)
+{
+ uint8_t cnt;
+ uint16_t unicast, original;
+ struct l_dbus_message *reply;
+
+
+ if (!l_dbus_message_get_arguments(msg, "qy", &original, &cnt) ||
+ !IS_UNICAST(original)) {
+ l_error("Cannot parse request for reprov data");
+ return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+
+ }
+
+ unicast = remote_get_next_unicast(low_addr, high_addr, cnt);
+
+ bt_shell_printf("Assign addresses for %u elements\n", cnt);
+ bt_shell_printf("Original: %4.4x New: %4.4x\n", original, unicast);
+
+ reply = l_dbus_message_new_method_return(msg);
+ l_dbus_message_set_arguments(reply, "q", unicast);
+
+ return reply;
+}
+
static struct l_dbus_message *req_prov_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
@@ -1825,6 +2076,7 @@ static struct l_dbus_message *req_prov_call(struct l_dbus *dbus,
uint16_t unicast;
struct l_dbus_message *reply;

+ /* Both calls handled identicaly except for parameter list */
if (!l_dbus_message_get_arguments(msg, "y", &cnt)) {
l_error("Cannot parse request for prov data");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
@@ -1833,14 +2085,14 @@ static struct l_dbus_message *req_prov_call(struct l_dbus *dbus,

unicast = remote_get_next_unicast(low_addr, high_addr, cnt);

- if (unicast == 0) {
+ if (!IS_UNICAST(unicast)) {
l_error("Failed to allocate addresses for %u elements\n", cnt);
return l_dbus_message_new_error(msg,
"org.freedesktop.DBus.Error."
"Failed to allocate address", NULL);
}

- bt_shell_printf("Assign addresses for %u elements\n", cnt);
+ bt_shell_printf("Assign addresses: %4.4x (cnt: %d)\n", unicast, cnt);

reply = l_dbus_message_new_method_return(msg);
l_dbus_message_set_arguments(reply, "qq", prov_net_idx, unicast);
@@ -1852,11 +2104,13 @@ static void remove_device(uint8_t *uuid)
{
struct unprov_device *dev;

- dev = l_queue_remove_if(devices, match_device_uuid, uuid);
- l_free(dev);
+ do {
+ dev = l_queue_remove_if(devices, match_device_uuid, uuid);
+ l_free(dev);
+ } while (dev);
}

-static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus,
+static struct l_dbus_message *prov_cmplt_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
@@ -1866,6 +2120,7 @@ static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus,
uint32_t n;
uint8_t *uuid;

+ l_debug("ProvComplete");
if (!prov_in_progress)
return l_dbus_message_new_error(msg, dbus_err_fail, NULL);

@@ -1896,7 +2151,49 @@ static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus,
return l_dbus_message_new_method_return(msg);
}

-static struct l_dbus_message *add_node_fail_call(struct l_dbus *dbus,
+static struct l_dbus_message *reprov_cmplt_call(struct l_dbus *dbus,
+ struct l_dbus_message *msg,
+ void *user_data)
+{
+ uint16_t unicast, original;
+ uint8_t old_cnt, cnt, nppi;
+
+ l_debug("ReprovComplete");
+ if (!prov_in_progress)
+ return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
+
+ prov_in_progress = false;
+
+ if (!l_dbus_message_get_arguments(msg, "qyqy", &original, &nppi,
+ &unicast, &cnt)) {
+ l_error("Cannot parse reprov complete message");
+ return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+
+ }
+
+ l_debug("ReprovComplete org: %4.4x, nppi: %d, new: %4.4x, cnt: %d",
+ original, nppi, unicast, cnt);
+ old_cnt = remote_ele_cnt(original);
+
+ if (nppi != 1 && (original != unicast || cnt != old_cnt)) {
+ l_error("Invalid reprov complete message (NPPI == %d)", nppi);
+ return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+ }
+
+ if (nppi)
+ remote_reset_node(original, unicast, cnt,
+ mesh_db_get_iv_index());
+
+ bt_shell_printf("Reprovisioning done (nppi: %d):\n", nppi);
+ remote_print_node(unicast);
+
+ if (!mesh_db_reset_node(original, unicast, cnt))
+ l_error("Failed to reset remote node");
+
+ return l_dbus_message_new_method_return(msg);
+}
+
+static struct l_dbus_message *prov_fail_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
@@ -1911,24 +2208,49 @@ static struct l_dbus_message *add_node_fail_call(struct l_dbus *dbus,
prov_in_progress = false;

if (!l_dbus_message_get_arguments(msg, "ays", &iter, &reason)) {
- l_error("Cannot parse add node failed message");
+ l_error("Cannot parse failed message");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
-
}

- if (!l_dbus_message_iter_get_fixed_array(&iter, &uuid, &n) ||
- n != 16) {
- l_error("Cannot parse add node failed message: uuid");
+ if (!l_dbus_message_iter_get_fixed_array(&iter, &uuid, &n) || n != 16) {
+ l_error("Cannot parse failed message: uuid");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}

bt_shell_printf("Provisioning failed:\n");
+
str = l_util_hexstring_upper(uuid, 16);
bt_shell_printf("\t" COLOR_RED "UUID = %s\n" COLOR_OFF, str);
l_free(str);
+ remove_device(uuid);
bt_shell_printf("\t" COLOR_RED "%s\n" COLOR_OFF, reason);

- remove_device(uuid);
+ return l_dbus_message_new_method_return(msg);
+}
+
+static struct l_dbus_message *reprov_fail_call(struct l_dbus *dbus,
+ struct l_dbus_message *msg,
+ void *user_data)
+{
+ struct l_dbus_message_iter iter;
+ uint16_t original = UNASSIGNED_ADDRESS;
+ char *reason;
+
+ if (!prov_in_progress)
+ return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
+
+ prov_in_progress = false;
+
+ if (!l_dbus_message_get_arguments(msg, "qs", &iter, &reason) ||
+ !IS_UNICAST(original)) {
+
+ l_error("Cannot parse Reprov failed message");
+ return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+ }
+
+ bt_shell_printf("Reprovisioning failed:\n");
+ bt_shell_printf("\t" COLOR_RED "UNICAST = %4.4x\n" COLOR_OFF, original);
+ bt_shell_printf("\t" COLOR_RED "%s\n" COLOR_OFF, reason);

return l_dbus_message_new_method_return(msg);
}
@@ -1941,12 +2263,23 @@ static void setup_prov_iface(struct l_dbus_interface *iface)
l_dbus_interface_method(iface, "RequestProvData", 0, req_prov_call,
"qq", "y", "net_index", "unicast", "count");

+ l_dbus_interface_method(iface, "RequestReprovData", 0, req_reprov_call,
+ "q", "qy", "unicast",
+ "original", "count");
+
l_dbus_interface_method(iface, "AddNodeComplete", 0,
- add_node_cmplt_call, "", "ayqy",
+ prov_cmplt_call, "", "ayqy",
"uuid", "unicast", "count");

- l_dbus_interface_method(iface, "AddNodeFailed", 0, add_node_fail_call,
+ l_dbus_interface_method(iface, "ReprovComplete", 0,
+ reprov_cmplt_call, "", "qyqy",
+ "original", "nppi", "unicast", "count");
+
+ l_dbus_interface_method(iface, "AddNodeFailed", 0, prov_fail_call,
"", "ays", "uuid", "reason");
+
+ l_dbus_interface_method(iface, "ReprovFailed", 0, reprov_fail_call,
+ "", "qs", "unicast", "reason");
}

static bool cid_getter(struct l_dbus *dbus,
diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index a48eace74..ad572f694 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -266,14 +266,21 @@ static uint32_t print_mod_id(uint8_t *data, bool vendor, const char *offset)
return mod_id;
}

-static void print_composition(uint8_t *data, uint16_t len)
+static uint8_t print_composition(uint8_t *data, uint16_t len)
{
uint16_t features;
int i = 0;
+ bool nppi = false;

- bt_shell_printf("Received composion:\n");
+ bt_shell_printf("Received composition:\n");
+
+ /* We only support Pages 0 && 128 */
+ if (*data == 128) {
+ bt_shell_printf("Dev Key Refresh (NPPI) required\n");
+ nppi = true;
+ } else if (*data != 0)
+ return 0;

- /* skip page -- We only support Page Zero */
data++;
len--;

@@ -328,6 +335,11 @@ static void print_composition(uint8_t *data, uint16_t len)

i++;
}
+
+ if (nppi)
+ return (uint8_t) i;
+ else
+ return 0;
}

static void print_pub(uint16_t ele_addr, uint32_t mod_id,
@@ -402,6 +414,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
const struct cfg_cmd *cmd;
uint16_t app_idx, net_idx, addr, ele_addr, features;
struct mesh_group *grp;
+ uint8_t page128_cnt;
struct model_pub pub;
int n;
struct pending_req *req;
@@ -431,7 +444,19 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
if (len < MIN_COMPOSITION_LEN)
return true;

- print_composition(data, len);
+ page128_cnt = print_composition(data, len);
+ if (page128_cnt) {
+ if (page128_cnt != remote_ele_cnt(src)) {
+ bt_shell_printf("Ele count was %d, now %d\n",
+ remote_ele_cnt(src), page128_cnt);
+ bt_shell_printf("Reprovision with NPPI-1\n");
+ } else {
+ bt_shell_printf("Models or Features changed\n");
+ bt_shell_printf("Reprovision with NPPI-2\n");
+ }
+
+ break;
+ }

saved = mesh_db_node_set_composition(src, data, len);
if (saved)
@@ -1051,8 +1076,8 @@ static void cmd_composition_get(int argc, char *argv[])

n = mesh_opcode_set(OP_DEV_COMP_GET, msg);

- /* By default, use page 0 */
- msg[n++] = (read_input_parameters(argc, argv) == 1) ? parms[0] : 0;
+ /* By default, use page 128 */
+ msg[n++] = (read_input_parameters(argc, argv) == 1) ? parms[0] : 128;

if (!config_send(msg, n, OP_DEV_COMP_GET))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c
index 896ff722c..c0c05a29a 100644
--- a/tools/mesh/mesh-db.c
+++ b/tools/mesh/mesh-db.c
@@ -1702,6 +1702,29 @@ static json_object *init_elements(uint8_t num_els)
return jelements;
}

+bool mesh_db_reset_node(uint16_t original, uint16_t unicast, uint8_t num_els)
+{
+ json_object *jnode, *jelements;
+
+ if (!cfg || !cfg->jcfg)
+ return false;
+
+ jnode = get_node_by_unicast(cfg->jcfg, original);
+ if (!jnode) {
+ l_error("Node %4.4x does not exist", original);
+ return false;
+ }
+
+ if (!write_uint16_hex(jnode, "unicastAddress", unicast))
+ return false;
+
+ json_object_object_del(jnode, "elements");
+ jelements = init_elements(num_els);
+ json_object_object_add(jnode, "elements", jelements);
+
+ return save_config();
+}
+
bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
uint16_t net_idx)
{
@@ -1864,13 +1887,11 @@ bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data, uint16_t len)
if (!jnode)
return false;

- /* skip page -- We only support Page Zero */
- data++;
- len--;
+ /* This is for page-0 only */
+ if (*data++ != 0)
+ return false;

- /* If "crpl" property is present, composition is already recorded */
- if (json_object_object_get_ex(jnode, "crpl", &jobj))
- return true;
+ len--;

if (!write_uint16_hex(jnode, "cid", l_get_le16(&data[0])))
return false;
@@ -1954,7 +1975,6 @@ bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data, uint16_t len)
}

while (len >= 4 && v--) {
- jobj = json_object_new_object();
mod_id = l_get_le16(data + 2);
mod_id = l_get_le16(data) << 16 | mod_id;

@@ -1984,7 +2004,8 @@ bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data, uint16_t len)
fail:
/* Reset elements array */
json_object_object_del(jnode, "elements");
- init_elements(sz);
+ jelements = init_elements(sz);
+ json_object_object_add(jnode, "elements", jelements);

return false;
}
diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h
index 4b6b2adb3..0e45112b7 100644
--- a/tools/mesh/mesh-db.h
+++ b/tools/mesh/mesh-db.h
@@ -29,6 +29,7 @@ bool mesh_db_del_app_key(uint16_t app_idx);
bool mesh_db_get_addr_range(uint16_t *low, uint16_t *high);
bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
uint16_t net_idx);
+bool mesh_db_reset_node(uint16_t original, uint16_t unicast, uint8_t num_els);
bool mesh_db_del_node(uint16_t unicast);
bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data,
uint16_t len);
diff --git a/tools/mesh/remote.c b/tools/mesh/remote.c
index 6ec220a6f..cee711dec 100644
--- a/tools/mesh/remote.c
+++ b/tools/mesh/remote.c
@@ -30,6 +30,12 @@ struct remote_key {
bool updated;
};

+struct foreach_data {
+ remote_foreach_t each;
+ void *user_data;
+ uint16_t dst;
+};
+
struct remote_node {
uint16_t unicast;
struct l_queue *net_keys;
@@ -138,6 +144,40 @@ uint8_t remote_del_node(uint16_t unicast)
return num_ele;
}

+bool remote_reset_node(uint16_t original, uint16_t unicast, uint8_t ele_cnt,
+ uint32_t iv_index)
+{
+ struct remote_node *rmt;
+ bool reject = true;
+ int i;
+
+ rmt = l_queue_remove_if(nodes, match_node_addr,
+ L_UINT_TO_PTR(original));
+ if (!rmt)
+ return false;
+
+ if (unicast == rmt->unicast)
+ reject = false;
+
+ for (i = 0; i < rmt->num_ele; ++i) {
+ l_queue_destroy(rmt->els[i], NULL);
+ if (reject)
+ remote_add_rejected_address(rmt->unicast + i,
+ iv_index, true);
+ }
+
+ if (ele_cnt != rmt->num_ele) {
+ l_free(rmt->els);
+ rmt->els = l_new(struct l_queue *, ele_cnt);
+ } else
+ memset(rmt->els, 0, sizeof(struct l_queue *) * ele_cnt);
+
+ rmt->unicast = unicast;
+ rmt->num_ele = ele_cnt;
+ l_queue_insert(nodes, rmt, compare_unicast, NULL);
+ return true;
+}
+
bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
uint8_t ele_cnt, uint16_t net_idx)
{
@@ -526,6 +566,76 @@ void remote_print_all(void)
l_queue_foreach(nodes, print_node, NULL);
}

+static void each_node(void *rmt, void *user_data)
+{
+ struct remote_node *node = rmt;
+ struct foreach_data *data = user_data;
+
+ data->each(data->user_data, node->unicast, (uint32_t) -1);
+}
+
+static void each_addr(void *rmt, void *user_data)
+{
+ struct remote_node *node = rmt;
+ struct foreach_data *data = user_data;
+ uint16_t cnt;
+
+ for (cnt = 0; cnt <= node->num_ele; cnt++)
+ data->each(data->user_data, node->unicast + cnt, (uint32_t) -1);
+}
+
+static void parse_model(void *model, void *user_data)
+{
+ struct foreach_data *data = user_data;
+
+ data->each(data->user_data, data->dst, L_PTR_TO_UINT(model));
+}
+
+static void each_model(void *rmt, void *user_data)
+{
+ struct remote_node *node = rmt;
+ struct foreach_data *data = user_data;
+ uint16_t cnt;
+
+ for (cnt = 0; cnt < node->num_ele; cnt++) {
+ data->dst = node->unicast + cnt;
+ l_queue_foreach(node->els[cnt], parse_model, data);
+ }
+}
+
+void remote_foreach(remote_foreach_t each, void *user_data)
+{
+ struct foreach_data data = {
+ .each = each,
+ .user_data = user_data
+ };
+
+ if (each)
+ l_queue_foreach(nodes, each_node, &data);
+}
+
+void remote_foreach_unicast(remote_foreach_t each, void *user_data)
+{
+ struct foreach_data data = {
+ .each = each,
+ .user_data = user_data
+ };
+
+ if (each)
+ l_queue_foreach(nodes, each_addr, &data);
+}
+
+void remote_foreach_model(remote_foreach_t each, void *user_data)
+{
+ struct foreach_data data = {
+ .each = each,
+ .user_data = user_data
+ };
+
+ if (each)
+ l_queue_foreach(nodes, each_model, &data);
+}
+
uint16_t remote_get_next_unicast(uint16_t low, uint16_t high, uint8_t ele_cnt)
{
struct remote_node *rmt;
@@ -598,3 +708,15 @@ void remote_clear_rejected_addresses(uint32_t iv_index)

mesh_db_clear_rejected(iv_index);
}
+
+uint8_t remote_ele_cnt(uint16_t unicast)
+{
+ struct remote_node *rmt;
+
+ rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(unicast));
+
+ if (rmt)
+ return rmt->num_ele;
+
+ return 0;
+}
diff --git a/tools/mesh/remote.h b/tools/mesh/remote.h
index 66457237e..2a3947b58 100644
--- a/tools/mesh/remote.h
+++ b/tools/mesh/remote.h
@@ -8,8 +8,13 @@
*
*/

+typedef void (*remote_foreach_t)(void *user_data, uint16_t dst,
+ uint32_t model);
+
bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
uint8_t ele_cnt, uint16_t net_idx);
+bool remote_reset_node(uint16_t original, uint16_t unicast, uint8_t ele_cnt,
+ uint32_t iv_index);
uint8_t remote_del_node(uint16_t unicast);
bool remote_set_model(uint16_t unicast, uint8_t ele_idx, uint32_t mod_id,
bool vendor);
@@ -30,3 +35,7 @@ bool remote_has_composition(uint16_t addr);
uint16_t remote_get_subnet_idx(uint16_t addr);
void remote_print_node(uint16_t addr);
void remote_print_all(void);
+void remote_foreach(remote_foreach_t each, void *user_data);
+void remote_foreach_unicast(remote_foreach_t each, void *user_data);
+void remote_foreach_model(remote_foreach_t each, void *user_data);
+uint8_t remote_ele_cnt(uint16_t unicast);
--
2.39.1


2023-01-24 20:26:52

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 06/13] mesh: Add storage of Mesh Private Beacon settings

From: Brian Gix <[email protected]>

If current storage does not exist in node.json, the Mesh Private
Beacon will be disabled.
---
mesh/mesh-config-json.c | 48 +++++++++++++++++++++++++++++++++++++++++
mesh/mesh-config.h | 6 ++++++
2 files changed, 54 insertions(+)

diff --git a/mesh/mesh-config-json.c b/mesh/mesh-config-json.c
index 8f321a731..c198627c6 100644
--- a/mesh/mesh-config-json.c
+++ b/mesh/mesh-config-json.c
@@ -1337,6 +1337,19 @@ static void parse_features(json_object *jconfig, struct mesh_config_node *node)
node->modes.beacon = mode;
}

+ if (json_object_object_get_ex(jconfig, "mpb", &jvalue)) {
+ mode = get_mode(jvalue);
+ if (mode <= MESH_MODE_UNSUPPORTED)
+ node->modes.mpb = mode;
+
+ if (node->modes.mpb == MESH_MODE_ENABLED) {
+ if (json_object_object_get_ex(jconfig, "mpbPeriod",
+ &jvalue))
+ node->modes.mpb_period =
+ json_object_get_int(jvalue);
+ }
+ }
+
if (!json_object_object_get_ex(jconfig, "relay", &jrelay))
return;

@@ -1576,6 +1589,18 @@ bool mesh_config_write_mode(struct mesh_config *cfg, const char *keyword,
return save_config(cfg->jnode, cfg->node_dir_path);
}

+bool mesh_config_write_mode_ex(struct mesh_config *cfg, const char *keyword,
+ int value, bool save)
+{
+ if (!cfg)
+ return false;
+
+ if (save)
+ return mesh_config_write_mode(cfg, keyword, value);
+ else
+ return write_mode(cfg->jnode, keyword, value);
+}
+
static bool write_relay_mode(json_object *jobj, uint8_t mode,
uint8_t count, uint16_t interval)
{
@@ -1622,6 +1647,21 @@ bool mesh_config_write_relay_mode(struct mesh_config *cfg, uint8_t mode,
return save_config(cfg->jnode, cfg->node_dir_path);
}

+bool mesh_config_write_mpb(struct mesh_config *cfg, uint8_t mode,
+ uint8_t period)
+{
+
+ if (!cfg || !write_mode(cfg->jnode, "mpb", mode))
+ return false;
+
+ if (mode) {
+ if (!write_int(cfg->jnode, "mpbPeriod", period))
+ return false;
+ }
+
+ return save_config(cfg->jnode, cfg->node_dir_path);
+}
+
bool mesh_config_write_net_transmit(struct mesh_config *cfg, uint8_t cnt,
uint16_t interval)
{
@@ -1746,6 +1786,14 @@ static struct mesh_config *create_config(const char *cfg_path,
if (!write_mode(jnode, "beacon", modes->beacon))
return NULL;

+ if (!write_mode(jnode, "mpb", modes->mpb))
+ return NULL;
+
+ if (modes->mpb) {
+ if (!write_int(jnode, "mpbPeriod", modes->mpb_period))
+ return NULL;
+ }
+
/* Sequence number */
json_object_object_add(jnode, sequenceNumber,
json_object_new_int(node->seq_number));
diff --git a/mesh/mesh-config.h b/mesh/mesh-config.h
index ed1b610de..3cb20b85d 100644
--- a/mesh/mesh-config.h
+++ b/mesh/mesh-config.h
@@ -60,6 +60,8 @@ struct mesh_config_modes {
uint8_t friend;
uint8_t proxy;
uint8_t beacon;
+ uint8_t mpb;
+ uint8_t mpb_period;
};

struct mesh_config_netkey {
@@ -140,9 +142,13 @@ bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq,
bool mesh_config_write_unicast(struct mesh_config *cfg, uint16_t unicast);
bool mesh_config_write_relay_mode(struct mesh_config *cfg, uint8_t mode,
uint8_t count, uint16_t interval);
+bool mesh_config_write_mpb(struct mesh_config *cfg, uint8_t mode,
+ uint8_t period);
bool mesh_config_write_ttl(struct mesh_config *cfg, uint8_t ttl);
bool mesh_config_write_mode(struct mesh_config *cfg, const char *keyword,
int value);
+bool mesh_config_write_mode_ex(struct mesh_config *cfg, const char *keyword,
+ int value, bool save);
bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page,
uint8_t *data, uint16_t size);
void mesh_config_comp_page_del(struct mesh_config *cfg, uint8_t page);
--
2.39.1


2023-01-24 20:26:54

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 07/13] mesh: Add Mesh Private Beacon server

From: Brian Gix <[email protected]>

This initial server supports only the Mesh Private Beacon and returns
"Not Suppoerted" for Get/Set of Private GATT Proxy and Private Node
Identity beacons.
---
Makefile.mesh | 1 +
mesh/prv-beacon.h | 36 +++++++++++++
mesh/prvbeac-server.c | 123 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 160 insertions(+)
create mode 100644 mesh/prv-beacon.h
create mode 100644 mesh/prvbeac-server.c

diff --git a/Makefile.mesh b/Makefile.mesh
index e18a169eb..63f085de1 100644
--- a/Makefile.mesh
+++ b/Makefile.mesh
@@ -36,6 +36,7 @@ mesh_sources = mesh/mesh.h mesh/mesh.c \
mesh/pb-adv.h mesh/pb-adv.c \
mesh/keyring.h mesh/keyring.c \
mesh/rpl.h mesh/rpl.c \
+ mesh/prv-beacon.h mesh/prvbeac-server.c \
mesh/mesh-defs.h
pkglibexec_PROGRAMS += mesh/bluetooth-meshd

diff --git a/mesh/prv-beacon.h b/mesh/prv-beacon.h
new file mode 100644
index 000000000..7be7a01c8
--- /dev/null
+++ b/mesh/prv-beacon.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2020 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+struct mesh_node;
+
+#define PRV_BEACON_SRV_MODEL SET_ID(SIG_VENDOR, 0x0008)
+#define PRV_BEACON_CLI_MODEL SET_ID(SIG_VENDOR, 0x0009)
+
+/* Private Beacon opcodes */
+#define OP_PRIVATE_BEACON_GET 0x8060
+#define OP_PRIVATE_BEACON_SET 0x8061
+#define OP_PRIVATE_BEACON_STATUS 0x8062
+#define OP_PRIVATE_GATT_PROXY_GET 0x8063
+#define OP_PRIVATE_GATT_PROXY_SET 0x8064
+#define OP_PRIVATE_GATT_PROXY_STATUS 0x8065
+#define OP_PRIVATE_NODE_ID_GET 0x8066
+#define OP_PRIVATE_NODE_ID_SET 0x8067
+#define OP_PRIVATE_NODE_ID_STATUS 0x8068
+
+void prv_beacon_server_init(struct mesh_node *node, uint8_t ele_idx);
diff --git a/mesh/prvbeac-server.c b/mesh/prvbeac-server.c
new file mode 100644
index 000000000..78ab94eb3
--- /dev/null
+++ b/mesh/prvbeac-server.c
@@ -0,0 +1,123 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2020 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/time.h>
+#include <ell/ell.h>
+
+#include "mesh/mesh-defs.h"
+#include "mesh/node.h"
+#include "mesh/net.h"
+#include "mesh/appkey.h"
+#include "mesh/model.h"
+#include "mesh/mesh-config.h"
+#include "mesh/prv-beacon.h"
+
+#define NOT_SUPPORTED 0x02
+
+static bool prvbec_srv_pkt(uint16_t src, uint16_t dst, 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;
+ uint32_t opcode;
+ uint8_t msg[5];
+ uint16_t n;
+ uint8_t period = 0;
+
+ 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;
+
+ l_debug("PRV-BEAC-SRV-opcode 0x%x size %u idx %3.3x", opcode, size,
+ net_idx);
+
+ n = 0;
+
+ switch (opcode) {
+ default:
+ return false;
+
+ case OP_PRIVATE_BEACON_SET:
+ if (size == 1)
+ period = 0xff;
+ else if (size == 2)
+ period = pkt[1];
+ else
+ return true;
+
+ /* fall through */
+
+ case OP_PRIVATE_BEACON_GET:
+ n = mesh_model_opcode_set(OP_PRIVATE_BEACON_STATUS, msg);
+
+ msg[n++] = NOT_SUPPORTED;
+ msg[n++] = period;
+
+ l_debug("Get/Set Private Beacon (%d)", msg[n-2]);
+ break;
+
+ case OP_PRIVATE_GATT_PROXY_SET:
+ /* fall through */
+ case OP_PRIVATE_GATT_PROXY_GET:
+ n = mesh_model_opcode_set(OP_PRIVATE_GATT_PROXY_STATUS, msg);
+ msg[n++] = NOT_SUPPORTED;
+ break;
+
+ case OP_PRIVATE_NODE_ID_SET:
+ /* fall through */
+ case OP_PRIVATE_NODE_ID_GET:
+ n = mesh_model_opcode_set(OP_PRIVATE_NODE_ID_STATUS, msg);
+ msg[n++] = NOT_SUPPORTED;
+ break;
+ }
+
+ if (n)
+ mesh_model_send(node, dst, src, APP_IDX_DEV_LOCAL, net_idx,
+ DEFAULT_TTL, false, n, msg);
+
+ return true;
+}
+
+static void prvbec_srv_unregister(void *user_data)
+{
+}
+
+static const struct mesh_model_ops ops = {
+ .unregister = prvbec_srv_unregister,
+ .recv = prvbec_srv_pkt,
+ .bind = NULL,
+ .sub = NULL,
+ .pub = NULL
+};
+
+void prv_beacon_server_init(struct mesh_node *node, uint8_t ele_idx)
+{
+ l_debug("%2.2x", ele_idx);
+ mesh_model_register(node, ele_idx, PRV_BEACON_SRV_MODEL, &ops, node);
+}
--
2.39.1


2023-01-24 20:26:55

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 10/13] tools/mesh: Add support for Mesh Private Beacons

From: Brian Gix <[email protected]>

This allows generation of Mesh Private Beacon server commands, and
recognition of the resulting Status messages.
---
tools/mesh-cfgclient.c | 9 ++++--
tools/mesh/cfgcli.c | 62 +++++++++++++++++++++++++++++++++++-------
tools/mesh/util.c | 3 ++
3 files changed, 62 insertions(+), 12 deletions(-)

diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c
index f3e9af8fb..50be82bcf 100644
--- a/tools/mesh-cfgclient.c
+++ b/tools/mesh-cfgclient.c
@@ -44,6 +44,8 @@
#define CFG_SRV_MODEL 0x0000
#define CFG_CLI_MODEL 0x0001
#define RPR_SVR_MODEL 0xFFFF0004
+#define PRV_BEACON_SVR 0x0008
+#define PRV_BEACON_CLI 0x0009

#define UNPROV_SCAN_MAX_SECS 300

@@ -58,7 +60,7 @@
struct meshcfg_el {
const char *path;
uint8_t index;
- uint16_t mods[2];
+ uint16_t mods[4];
};

struct meshcfg_app {
@@ -143,7 +145,8 @@ static struct meshcfg_app app = {
.ele = {
.path = "/mesh/cfgclient/ele0",
.index = 0,
- .mods = {CFG_SRV_MODEL, CFG_CLI_MODEL}
+ .mods = {CFG_SRV_MODEL, CFG_CLI_MODEL,
+ PRV_BEACON_SVR, PRV_BEACON_CLI}
}
};

@@ -1889,6 +1892,8 @@ static bool mod_getter(struct l_dbus *dbus,
l_dbus_message_builder_enter_array(builder, "(qa{sv})");
build_model(builder, app.ele.mods[0], false, false);
build_model(builder, app.ele.mods[1], false, false);
+ build_model(builder, app.ele.mods[2], false, false);
+ build_model(builder, app.ele.mods[3], false, false);
l_dbus_message_builder_leave_array(builder);

return true;
diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c
index ad572f694..4f6248e48 100644
--- a/tools/mesh/cfgcli.c
+++ b/tools/mesh/cfgcli.c
@@ -21,6 +21,7 @@
#include "src/shared/util.h"

#include "mesh/mesh-defs.h"
+#include "mesh/prv-beacon.h"
#include "mesh/util.h"
#include "mesh/crypto.h"

@@ -73,9 +74,12 @@ static struct cfg_cmd cmds[] = {
{ OP_APPKEY_UPDATE, OP_APPKEY_STATUS, "AppKeyUpdate" },
{ OP_DEV_COMP_GET, OP_DEV_COMP_STATUS, "DeviceCompositionGet" },
{ OP_DEV_COMP_STATUS, NO_RESPONSE, "DeviceCompositionStatus" },
- { OP_CONFIG_BEACON_GET, OP_CONFIG_BEACON_STATUS, "BeaconGet" },
- { OP_CONFIG_BEACON_SET, OP_CONFIG_BEACON_STATUS, "BeaconSet" },
- { OP_CONFIG_BEACON_STATUS, NO_RESPONSE, "BeaconStatus" },
+ { OP_CONFIG_BEACON_GET, OP_CONFIG_BEACON_STATUS, "SNBGet" },
+ { OP_CONFIG_BEACON_SET, OP_CONFIG_BEACON_STATUS, "SNBSet" },
+ { OP_CONFIG_BEACON_STATUS, NO_RESPONSE, "SNBStatus" },
+ { OP_PRIVATE_BEACON_GET, OP_PRIVATE_BEACON_STATUS, "MPBGet" },
+ { OP_PRIVATE_BEACON_SET, OP_PRIVATE_BEACON_STATUS, "MPBSet" },
+ { OP_PRIVATE_BEACON_STATUS, NO_RESPONSE, "MPBStatus" },
{ OP_CONFIG_DEFAULT_TTL_GET, OP_CONFIG_DEFAULT_TTL_STATUS,
"DefaultTTLGet" },
{ OP_CONFIG_DEFAULT_TTL_SET, OP_CONFIG_DEFAULT_TTL_STATUS,
@@ -617,12 +621,20 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data,
if (len != 1)
return true;

- bt_shell_printf("Node %4.4x: Config Beacon Status 0x%02x\n",
+ bt_shell_printf("Node %4.4x: SecBeacon Status 0x%02x\n",
src, data[0]);

saved = mesh_db_node_set_beacon(src, data[0] != 0);
break;

+ case OP_PRIVATE_BEACON_STATUS:
+ if (len != 2)
+ return true;
+
+ bt_shell_printf("Node %4.4x: PrivBeacon Status 0x%02x 0x%02x\n",
+ src, data[0], data[1]);
+ break;
+
case OP_CONFIG_RELAY_STATUS:
if (len != 2)
return true;
@@ -1345,7 +1357,7 @@ static void cmd_del_binding(int argc, char *argv[])
cmd_bind(OP_MODEL_APP_UNBIND, argc, argv);
}

-static void cmd_beacon_set(int argc, char *argv[])
+static void cmd_snb_set(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[2 + 1];
@@ -1367,11 +1379,41 @@ static void cmd_beacon_set(int argc, char *argv[])
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}

-static void cmd_beacon_get(int argc, char *argv[])
+static void cmd_mpb_set(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 2];
+ uint32_t parm_cnt;
+
+ n = mesh_opcode_set(OP_PRIVATE_BEACON_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 1 && parm_cnt != 2) {
+ bt_shell_printf("bad arguments\n");
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ msg[n++] = parms[0];
+
+ if (parm_cnt == 2)
+ msg[n++] = parms[1];
+
+ if (!config_send(msg, n, OP_PRIVATE_BEACON_SET))
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+
+ return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_snb_get(int argc, char *argv[])
{
cmd_default(OP_CONFIG_BEACON_GET);
}

+static void cmd_mpb_get(int argc, char *argv[])
+{
+ cmd_default(OP_PRIVATE_BEACON_GET);
+}
+
static void cmd_ident_set(int argc, char *argv[])
{
uint16_t n;
@@ -2077,10 +2119,10 @@ static const struct bt_shell_menu cfg_menu = {
"Set node identity state"},
{"ident-get", "<net_idx>", cmd_ident_get,
"Get node identity state"},
- {"beacon-set", "<state>", cmd_beacon_set,
- "Set node identity state"},
- {"beacon-get", NULL, cmd_beacon_get,
- "Get node beacon state"},
+ {"snb-set", "<state>", cmd_snb_set, "Set node SNB state"},
+ {"snb-get", NULL, cmd_snb_get, "Get node SNB state"},
+ {"mpb-set", "<state> <period>", cmd_mpb_set, "Set node MPB state"},
+ {"mpb-get", NULL, cmd_mpb_get, "Get node MPB state"},
{"relay-set", "<relay> <rexmt count> <rexmt steps>", cmd_relay_set,
"Set relay"},
{"relay-get", NULL, cmd_relay_get,
diff --git a/tools/mesh/util.c b/tools/mesh/util.c
index 29641439f..d8c47c0e9 100644
--- a/tools/mesh/util.c
+++ b/tools/mesh/util.c
@@ -20,6 +20,7 @@
#include "src/shared/util.h"

#include "mesh/mesh-defs.h"
+#include "mesh/prv-beacon.h"

#include "tools/mesh/util.h"

@@ -137,6 +138,8 @@ const char *sig_model_string(uint16_t sig_model_id)
case 0x0001: return "Configuration Client";
case 0x0002: return "Health Server";
case 0x0003: return "Health Client";
+ case 0x0008: return "Private Beacon Server";
+ case 0x0009: return "Private Beacon Client";
case 0x1000: return "Generic OnOff Server";
case 0x1001: return "Generic OnOff Client";
case 0x1002: return "Generic Level Server";
--
2.39.1


2023-01-24 20:26:56

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 09/13] mesh: Add internal Mesh Private Beacon model

From: Brian Gix <[email protected]>

Adds recgnition that the Mesh Private Beacon model is internal
and foundational, without bindings.
---
mesh/model.c | 10 ++++++-
mesh/net-keys.c | 5 ++++
mesh/net-keys.h | 1 +
mesh/node.c | 67 +++++++++++++++++++++++++++++++++++++++----
mesh/node.h | 2 ++
mesh/prvbeac-server.c | 11 +++++--
6 files changed, 86 insertions(+), 10 deletions(-)

diff --git a/mesh/model.c b/mesh/model.c
index e2babea10..4ccafa17e 100644
--- a/mesh/model.c
+++ b/mesh/model.c
@@ -26,6 +26,7 @@
#include "mesh/cfgmod.h"
#include "mesh/prov.h"
#include "mesh/remprv.h"
+#include "mesh/prv-beacon.h"
#include "mesh/error.h"
#include "mesh/dbus.h"
#include "mesh/util.h"
@@ -81,6 +82,9 @@ static bool is_internal(uint32_t id)
if (id == REM_PROV_SRV_MODEL || id == REM_PROV_CLI_MODEL)
return true;

+ if (id == PRV_BEACON_SRV_MODEL || id == PRV_BEACON_CLI_MODEL)
+ return true;
+
return false;
}

@@ -647,6 +651,9 @@ static int update_binding(struct mesh_node *node, uint16_t addr, uint32_t id,
if (id == CONFIG_SRV_MODEL || id == CONFIG_CLI_MODEL)
return MESH_STATUS_INVALID_MODEL;

+ if (id == PRV_BEACON_SRV_MODEL || id == PRV_BEACON_CLI_MODEL)
+ return MESH_STATUS_INVALID_MODEL;
+
if (!appkey_have_key(node_get_net(node), app_idx))
return MESH_STATUS_INVALID_APPKEY;

@@ -1655,7 +1662,8 @@ static struct mesh_model *model_setup(struct mesh_net *net, uint8_t ele_idx,
SET_ID(SIG_VENDOR, db_mod->id));

/* Implicitly bind config server model to device key */
- if (db_mod->id == CONFIG_SRV_MODEL) {
+ if (db_mod->id == CONFIG_SRV_MODEL ||
+ db_mod->id == PRV_BEACON_SRV_MODEL) {

if (ele_idx != PRIMARY_ELE_IDX) {
l_free(mod);
diff --git a/mesh/net-keys.c b/mesh/net-keys.c
index 0ba051d79..57a9df04a 100644
--- a/mesh/net-keys.c
+++ b/mesh/net-keys.c
@@ -123,6 +123,7 @@ uint32_t net_key_add(const uint8_t flooding[16])
key = l_new(struct net_key, 1);
memcpy(key->flooding, flooding, 16);
key->ref_cnt++;
+ key->mpb_refresh = NET_MPB_REFRESH_DEFAULT;
result = mesh_crypto_k2(flooding, p, sizeof(p), &key->nid, key->enc_key,
key->prv_key);
if (!result)
@@ -664,6 +665,10 @@ bool net_key_beacon_refresh(uint32_t id, uint32_t ivi, bool kr, bool ivu,

l_debug("Set Beacon: IVI: %8.8x, IVU: %d, KR: %d", ivi, ivu, kr);

+ key->ivi = ivi;
+ key->ivu = ivu;
+ key->kr = kr;
+
/* Propagate changes to all local nodes */
net_local_beacon(id, ivi, ivu, kr);

diff --git a/mesh/net-keys.h b/mesh/net-keys.h
index a3909448c..e73812481 100644
--- a/mesh/net-keys.h
+++ b/mesh/net-keys.h
@@ -12,6 +12,7 @@
#define BEACON_TYPE_MPB 0x02
#define KEY_REFRESH 0x01
#define IV_INDEX_UPDATE 0x02
+#define NET_MPB_REFRESH_DEFAULT 60

void net_key_cleanup(void);
bool net_key_confirm(uint32_t id, const uint8_t flooding[16]);
diff --git a/mesh/node.c b/mesh/node.c
index a2a330518..ed3212685 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -32,6 +32,7 @@
#include "mesh/model.h"
#include "mesh/cfgmod.h"
#include "mesh/remprv.h"
+#include "mesh/prv-beacon.h"
#include "mesh/util.h"
#include "mesh/error.h"
#include "mesh/dbus.h"
@@ -100,6 +101,8 @@ struct mesh_node {
uint8_t proxy;
uint8_t friend;
uint8_t beacon;
+ uint8_t mpb;
+ uint8_t mpb_period;
};

struct node_import {
@@ -206,6 +209,8 @@ static void set_defaults(struct mesh_node *node)
{
node->lpn = MESH_MODE_UNSUPPORTED;
node->proxy = MESH_MODE_UNSUPPORTED;
+ node->mpb = MESH_MODE_DISABLED;
+ node->mpb_period = NET_MPB_REFRESH_DEFAULT;
node->friend = (mesh_friendship_supported()) ? MESH_MODE_DISABLED :
MESH_MODE_UNSUPPORTED;
node->beacon = (mesh_beacon_enabled()) ? MESH_MODE_ENABLED :
@@ -403,7 +408,7 @@ static bool init_storage_dir(struct mesh_node *node)
return rpl_init(node->storage_dir);
}

-static void update_net_settings(struct mesh_node *node)
+static void init_net_settings(struct mesh_node *node)
{
struct mesh_net *net = node->net;

@@ -415,6 +420,8 @@ static void update_net_settings(struct mesh_node *node)
node->relay.cnt, node->relay.interval);

mesh_net_set_snb_mode(net, node->beacon == MESH_MODE_ENABLED);
+ mesh_net_set_mpb_mode(net, node->mpb == MESH_MODE_ENABLED,
+ node->mpb_period, true);
}

static bool init_from_storage(struct mesh_config_node *db_node,
@@ -442,6 +449,8 @@ static bool init_from_storage(struct mesh_config_node *db_node,
node->relay.cnt = db_node->modes.relay.cnt;
node->relay.interval = db_node->modes.relay.interval;
node->beacon = db_node->modes.beacon;
+ node->mpb = db_node->modes.mpb;
+ node->mpb_period = db_node->modes.mpb_period;

l_debug("relay %2.2x, proxy %2.2x, lpn %2.2x, friend %2.2x",
node->relay.mode, node->proxy, node->lpn, node->friend);
@@ -495,7 +504,7 @@ static bool init_from_storage(struct mesh_config_node *db_node,
mesh_net_set_seq_num(node->net, node->seq_number);
mesh_net_set_default_ttl(node->net, node->ttl);

- update_net_settings(node);
+ init_net_settings(node);

/* Initialize configuration server model */
cfgmod_server_init(node, PRIMARY_ELE_IDX);
@@ -504,6 +513,9 @@ static bool init_from_storage(struct mesh_config_node *db_node,
remote_prov_server_init(node, PRIMARY_ELE_IDX);
remote_prov_client_init(node, PRIMARY_ELE_IDX);

+ /* Initialize Private Beacon server model */
+ prv_beacon_server_init(node, PRIMARY_ELE_IDX);
+
node->cfg = cfg;

return true;
@@ -839,6 +851,36 @@ uint8_t node_beacon_mode_get(struct mesh_node *node)
return node->beacon;
}

+bool node_mpb_mode_set(struct mesh_node *node, bool enable, uint8_t period)
+{
+ bool res;
+ uint8_t beacon;
+
+ if (!node)
+ return false;
+
+ beacon = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED;
+ res = mesh_config_write_mpb(node->cfg, beacon, period);
+
+ if (res) {
+ node->mpb = beacon;
+ node->mpb_period = period;
+ mesh_net_set_mpb_mode(node->net, enable, period, false);
+ }
+
+ return res;
+}
+
+uint8_t node_mpb_mode_get(struct mesh_node *node, uint8_t *period)
+{
+ if (!node)
+ return MESH_MODE_DISABLED;
+
+ *period = node->mpb_period;
+
+ return node->mpb;
+}
+
bool node_friend_mode_set(struct mesh_node *node, bool enable)
{
bool res;
@@ -951,6 +993,8 @@ static void convert_node_to_storage(struct mesh_node *node,
db_node->modes.relay.cnt = node->relay.cnt;
db_node->modes.relay.interval = node->relay.interval;
db_node->modes.beacon = node->beacon;
+ db_node->modes.mpb = node->mpb;
+ db_node->modes.mpb_period = node->mpb_period;

db_node->ttl = node->ttl;
db_node->seq_number = node->seq_number;
@@ -1173,9 +1217,16 @@ static bool get_sig_models_from_properties(struct mesh_node *node,
while (l_dbus_message_iter_next_entry(&mods, &m_id, &var)) {
uint32_t id = SET_ID(SIG_VENDOR, m_id);

- /* Allow Config Server Model only on the primary element */
- if (ele->idx != PRIMARY_ELE_IDX && id == CONFIG_SRV_MODEL)
- return false;
+ /*
+ * Allow Config Server & Private Beacon Models only on
+ * the primary element
+ */
+ if (ele->idx != PRIMARY_ELE_IDX) {
+ if (id == CONFIG_SRV_MODEL)
+ return false;
+ if (id == PRV_BEACON_SRV_MODEL)
+ return false;
+ }

if (!mesh_model_add(node, ele->models, id, &var))
return false;
@@ -1278,6 +1329,7 @@ static bool get_element_properties(struct mesh_node *node, const char *path,
*/
if (ele->idx == PRIMARY_ELE_IDX) {
mesh_model_add(node, ele->models, CONFIG_SRV_MODEL, NULL);
+ mesh_model_add(node, ele->models, PRV_BEACON_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,
@@ -1397,13 +1449,16 @@ static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr,

l_queue_foreach(node->pages, save_pages, node);

- update_net_settings(node);
+ init_net_settings(node);

/* 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);

+ /* Initialize Private Beacon server model */
+ prv_beacon_server_init(node, PRIMARY_ELE_IDX);
+
node->busy = true;

return true;
diff --git a/mesh/node.h b/mesh/node.h
index a98945223..4f31c5056 100644
--- a/mesh/node.h
+++ b/mesh/node.h
@@ -63,6 +63,8 @@ uint8_t node_relay_mode_get(struct mesh_node *node, uint8_t *cnt,
bool node_proxy_mode_set(struct mesh_node *node, bool enable);
uint8_t node_proxy_mode_get(struct mesh_node *node);
bool node_beacon_mode_set(struct mesh_node *node, bool enable);
+bool node_mpb_mode_set(struct mesh_node *node, bool enable, uint8_t period);
+uint8_t node_mpb_mode_get(struct mesh_node *node, uint8_t *period);
uint8_t node_beacon_mode_get(struct mesh_node *node);
bool node_friend_mode_set(struct mesh_node *node, bool enable);
uint8_t node_friend_mode_get(struct mesh_node *node);
diff --git a/mesh/prvbeac-server.c b/mesh/prvbeac-server.c
index 78ab94eb3..427af5521 100644
--- a/mesh/prvbeac-server.c
+++ b/mesh/prvbeac-server.c
@@ -43,7 +43,7 @@ static bool prvbec_srv_pkt(uint16_t src, uint16_t dst, uint16_t app_idx,
uint32_t opcode;
uint8_t msg[5];
uint16_t n;
- uint8_t period = 0;
+ uint8_t period;

if (app_idx != APP_IDX_DEV_LOCAL)
return false;
@@ -65,18 +65,23 @@ static bool prvbec_srv_pkt(uint16_t src, uint16_t dst, uint16_t app_idx,

case OP_PRIVATE_BEACON_SET:
if (size == 1)
- period = 0xff;
+ node_mpb_mode_get(node, &period);
else if (size == 2)
period = pkt[1];
else
return true;

+ if (pkt[0] > 1)
+ return true;
+
+ node_mpb_mode_set(node, !!pkt[0], period);
+
/* fall through */

case OP_PRIVATE_BEACON_GET:
n = mesh_model_opcode_set(OP_PRIVATE_BEACON_STATUS, msg);

- msg[n++] = NOT_SUPPORTED;
+ msg[n++] = node_mpb_mode_get(node, &period);
msg[n++] = period;

l_debug("Get/Set Private Beacon (%d)", msg[n-2]);
--
2.39.1


2023-01-24 20:26:58

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 13/13] mesh: Remove unused byte swap for ScanBuild

---
mesh/crypto.c | 7 -------
1 file changed, 7 deletions(-)

diff --git a/mesh/crypto.c b/mesh/crypto.c
index 38dfc5fb5..b712a2654 100644
--- a/mesh/crypto.c
+++ b/mesh/crypto.c
@@ -94,13 +94,6 @@ bool mesh_crypto_aes_ccm_encrypt(const uint8_t nonce[13], const uint8_t key[16],
result = l_aead_cipher_encrypt(cipher, msg, msg_len, aad, aad_len,
nonce, 13, out_msg, msg_len + mic_size);

- if (result && out_mic) {
- if (mic_size == 4)
- *(uint32_t *)out_mic = l_get_be32(out_msg + msg_len);
- else
- *(uint64_t *)out_mic = l_get_be64(out_msg + msg_len);
- }
-
l_aead_cipher_free(cipher);

return result;
--
2.39.1


2023-01-24 20:26:51

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 02/13] mesh: Add Remote Provisioning

From: Brian Gix <[email protected]>

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

diff --git a/Makefile.mesh b/Makefile.mesh
index 3047f362b..e18a169eb 100644
--- a/Makefile.mesh
+++ b/Makefile.mesh
@@ -26,6 +26,7 @@ mesh_sources = mesh/mesh.h mesh/mesh.c \
mesh/provision.h mesh/prov.h \
mesh/model.h mesh/model.c \
mesh/cfgmod.h mesh/cfgmod-server.c \
+ mesh/remprv.h mesh/remprv-server.c \
mesh/mesh-config.h mesh/mesh-config-json.c \
mesh/util.h mesh/util.c \
mesh/dbus.h mesh/dbus.c \
diff --git a/mesh/cfgmod-server.c b/mesh/cfgmod-server.c
index be90ef8c5..3d7efc44b 100644
--- a/mesh/cfgmod-server.c
+++ b/mesh/cfgmod-server.c
@@ -30,8 +30,8 @@
(SET_ID(SIG_VENDOR, l_get_le16(pkt))))

/* Supported composition pages, sorted high to low */
-/* Only page 0 is currently supported */
static const uint8_t supported_pages[] = {
+ 128,
0
};

diff --git a/mesh/keyring.c b/mesh/keyring.c
index 995a4b88f..894fb14fa 100644
--- a/mesh/keyring.c
+++ b/mesh/keyring.c
@@ -30,9 +30,9 @@
#include "mesh/node.h"
#include "mesh/keyring.h"

-const char *dev_key_dir = "/dev_keys";
-const char *app_key_dir = "/app_keys";
-const char *net_key_dir = "/net_keys";
+static const char *dev_key_dir = "/dev_keys";
+static const char *app_key_dir = "/app_keys";
+static const char *net_key_dir = "/net_keys";

static int open_key_file(struct mesh_node *node, const char *key_dir,
uint16_t idx, int flags)
@@ -295,6 +295,7 @@ bool keyring_get_remote_dev_key(struct mesh_node *node, uint16_t unicast,
close(fd);
}

+
return result;
}

@@ -371,6 +372,28 @@ bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast,
return true;
}

+bool keyring_del_remote_dev_key_all(struct mesh_node *node, uint16_t unicast)
+{
+ uint8_t dev_key[16];
+ uint8_t test_key[16];
+ uint8_t cnt = 1;
+
+ if (!keyring_get_remote_dev_key(node, unicast, dev_key))
+ return false;
+
+ while (keyring_get_remote_dev_key(node, unicast + cnt, test_key)) {
+ if (memcmp(dev_key, test_key, sizeof(dev_key)))
+ break;
+
+ cnt++;
+ }
+
+ if (cnt > 1)
+ return keyring_del_remote_dev_key(node, unicast + 1, cnt - 1);
+
+ return true;
+}
+
static DIR *open_key_dir(const char *node_path, const char *key_dir_name)
{
char dir_path[PATH_MAX];
diff --git a/mesh/keyring.h b/mesh/keyring.h
index ecf62cbc1..efc499ac2 100644
--- a/mesh/keyring.h
+++ b/mesh/keyring.h
@@ -39,5 +39,6 @@ bool keyring_put_remote_dev_key(struct mesh_node *node, uint16_t unicast,
uint8_t count, uint8_t dev_key[16]);
bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast,
uint8_t count);
+bool keyring_del_remote_dev_key_all(struct mesh_node *node, uint16_t unicast);
bool keyring_build_export_keys_reply(struct mesh_node *node,
struct l_dbus_message_builder *builder);
diff --git a/mesh/manager.c b/mesh/manager.c
index e66b1a45b..e16dbc513 100644
--- a/mesh/manager.c
+++ b/mesh/manager.c
@@ -21,75 +21,137 @@
#include "mesh/mesh.h"
#include "mesh/mesh-io.h"
#include "mesh/node.h"
+#include "mesh/model.h"
#include "mesh/net.h"
#include "mesh/keyring.h"
#include "mesh/agent.h"
#include "mesh/provision.h"
+#include "mesh/prov.h"
+#include "mesh/remprv.h"
#include "mesh/manager.h"

-struct add_data{
+struct prov_remote_data {
struct l_dbus_message *msg;
struct mesh_agent *agent;
struct mesh_node *node;
uint32_t disc_watch;
+ uint16_t original;
uint16_t primary;
uint16_t net_idx;
+ uint8_t transport;
uint8_t num_ele;
uint8_t uuid[16];
};

-static int8_t scan_rssi;
-static uint8_t scan_uuid[16];
-static struct mesh_node *scan_node;
-static struct l_timeout *scan_timeout;
-static struct add_data *add_pending;
+struct scan_req {
+ struct mesh_node *node;
+ struct l_timeout *timeout;
+ uint16_t server;
+ uint16_t net_idx;
+ uint8_t uuid[16];
+ int8_t rssi;
+ bool ext;
+};
+
+static struct l_queue *scans;
+static struct prov_remote_data *prov_pending;
static const uint8_t prvb[2] = {MESH_AD_TYPE_BEACON, 0x00};

+static bool by_scan(const void *a, const void *b)
+{
+ return a == b;
+}
+
+static bool by_node(const void *a, const void *b)
+{
+ const struct scan_req *req = a;
+ const struct mesh_node *node = b;
+
+ return req->node == node;
+}
+
+static bool by_node_svr(const void *a, const void *b)
+{
+ const struct scan_req *req = a;
+ const struct scan_req *test = b;
+
+ return req->node == test->node && req->server == test->server;
+}
+
static void scan_cancel(struct l_timeout *timeout, void *user_data)
{
- struct mesh_node *node = user_data;
+ struct scan_req *req = user_data;
struct mesh_io *io;
struct mesh_net *net;
+ uint8_t msg[4];
+ int n;

l_debug("");

- if (scan_timeout)
- l_timeout_remove(scan_timeout);
+ req = l_queue_remove_if(scans, by_scan, req);
+
+ if (!req)
+ return;
+
+ l_timeout_remove(req->timeout);
+
+ if (req->server) {
+ n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STOP, msg);
+ mesh_model_send(req->node, 0, req->server, APP_IDX_DEV_REMOTE,
+ req->net_idx, DEFAULT_TTL,
+ true, n, msg);
+ } else {
+ net = node_get_net(req->node);
+ io = mesh_net_get_io(net);
+ mesh_io_deregister_recv_cb(io, prvb, sizeof(prvb));
+ }

- net = node_get_net(node);
- io = mesh_net_get_io(net);
- mesh_io_deregister_recv_cb(io, prvb, sizeof(prvb));
- scan_node = NULL;
- scan_timeout = NULL;
+ initiator_scan_unreg(req->node);
+ l_free(req);
}

-static void free_pending_add_call()
+static void free_pending_add_call(void)
{
- if (!add_pending)
+ if (!prov_pending)
return;

- if (add_pending->disc_watch)
+ if (prov_pending->disc_watch)
l_dbus_remove_watch(dbus_get_bus(),
- add_pending->disc_watch);
+ prov_pending->disc_watch);

- if (add_pending->msg)
- l_dbus_message_unref(add_pending->msg);
+ if (prov_pending->msg)
+ l_dbus_message_unref(prov_pending->msg);

- l_free(add_pending);
- add_pending = NULL;
+ l_free(prov_pending);
+ prov_pending = NULL;
}

static void prov_disc_cb(struct l_dbus *bus, void *user_data)
{
- if (!add_pending)
+ if (!prov_pending)
return;

- initiator_cancel(add_pending);
- add_pending->disc_watch = 0;
+ initiator_cancel(prov_pending);
+ prov_pending->disc_watch = 0;

free_pending_add_call();
}

+static void append_dict_entry_basic(struct l_dbus_message_builder *builder,
+ const char *key, const char *signature,
+ const void *data)
+{
+ if (!builder)
+ return;
+
+ l_dbus_message_builder_enter_dict(builder, "sv");
+ l_dbus_message_builder_append_basic(builder, 's', key);
+ l_dbus_message_builder_enter_variant(builder, signature);
+ l_dbus_message_builder_append_basic(builder, signature[0], data);
+ l_dbus_message_builder_leave_variant(builder);
+ l_dbus_message_builder_leave_dict(builder);
+}
+
static void send_add_failed(const char *owner, const char *path,
uint8_t status)
{
@@ -102,7 +164,7 @@ static void send_add_failed(const char *owner, const char *path,
"AddNodeFailed");

builder = l_dbus_message_builder_new(msg);
- dbus_append_byte_array(builder, add_pending->uuid, 16);
+ dbus_append_byte_array(builder, prov_pending->uuid, 16);
l_dbus_message_builder_append_basic(builder, 's',
mesh_prov_status_str(status));
l_dbus_message_builder_finalize(builder);
@@ -115,14 +177,14 @@ static void send_add_failed(const char *owner, const char *path,
static bool add_cmplt(void *user_data, uint8_t status,
struct mesh_prov_node_info *info)
{
- struct add_data *pending = user_data;
+ struct prov_remote_data *pending = user_data;
struct mesh_node *node = pending->node;
struct l_dbus *dbus = dbus_get_bus();
struct l_dbus_message_builder *builder;
struct l_dbus_message *msg;
bool result;

- if (pending != add_pending)
+ if (pending != prov_pending)
return false;

if (status != PROV_ERR_SUCCESS) {
@@ -131,7 +193,12 @@ static bool add_cmplt(void *user_data, uint8_t status,
return false;
}

- result = keyring_put_remote_dev_key(add_pending->node, info->unicast,
+ /* If Unicast address changing, delete old dev key */
+ if (pending->transport == PB_NPPI_01)
+ keyring_del_remote_dev_key_all(pending->node,
+ pending->original);
+
+ result = keyring_put_remote_dev_key(pending->node, info->unicast,
info->num_ele, info->device_key);

if (!result) {
@@ -140,13 +207,29 @@ static bool add_cmplt(void *user_data, uint8_t status,
return false;
}

- msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
+ if (pending->transport > PB_NPPI_02)
+ msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
node_get_app_path(node),
MESH_PROVISIONER_INTERFACE,
"AddNodeComplete");
+ else
+ msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
+ node_get_app_path(node),
+ MESH_PROVISIONER_INTERFACE,
+ "ReprovComplete");

builder = l_dbus_message_builder_new(msg);
- dbus_append_byte_array(builder, add_pending->uuid, 16);
+
+ if (pending->transport > PB_NPPI_02)
+ dbus_append_byte_array(builder, pending->uuid, 16);
+ else {
+ uint8_t nppi = (uint8_t) pending->transport;
+
+ l_dbus_message_builder_append_basic(builder, 'q',
+ &pending->original);
+ l_dbus_message_builder_append_basic(builder, 'y', &nppi);
+ }
+
l_dbus_message_builder_append_basic(builder, 'q', &info->unicast);
l_dbus_message_builder_append_basic(builder, 'y', &info->num_ele);
l_dbus_message_builder_finalize(builder);
@@ -161,47 +244,66 @@ static bool add_cmplt(void *user_data, uint8_t status,

static void mgr_prov_data (struct l_dbus_message *reply, void *user_data)
{
- struct add_data *pending = user_data;
+ struct prov_remote_data *pending = user_data;
uint16_t net_idx;
uint16_t primary;

- if (pending != add_pending)
+ if (pending != prov_pending)
return;

if (l_dbus_message_is_error(reply))
return;

- if (!l_dbus_message_get_arguments(reply, "qq", &net_idx, &primary))
+ if (pending->transport == PB_NPPI_01) {
+ /* If performing NPPI, we only get new primary unicast here */
+ if (!l_dbus_message_get_arguments(reply, "q", &primary))
+ return;
+
+ net_idx = pending->net_idx;
+
+ } else if (!l_dbus_message_get_arguments(reply, "qq", &net_idx,
+ &primary))
return;

- add_pending->primary = primary;
- add_pending->net_idx = net_idx;
- initiator_prov_data(net_idx, primary, add_pending);
+ pending->primary = primary;
+ pending->net_idx = net_idx;
+ initiator_prov_data(net_idx, primary, pending);
}

static bool add_data_get(void *user_data, uint8_t num_ele)
{
- struct add_data *pending = user_data;
+ struct prov_remote_data *pending = user_data;
struct l_dbus_message *msg;
struct l_dbus *dbus;
const char *app_path;
const char *sender;

- if (pending != add_pending)
+ if (pending != prov_pending)
return false;

dbus = dbus_get_bus();
- app_path = node_get_app_path(add_pending->node);
- sender = node_get_owner(add_pending->node);
+ app_path = node_get_app_path(pending->node);
+ sender = node_get_owner(pending->node);

- msg = l_dbus_message_new_method_call(dbus, sender, app_path,
+ if (pending->transport > PB_NPPI_02) {
+ msg = l_dbus_message_new_method_call(dbus, sender, app_path,
MESH_PROVISIONER_INTERFACE,
"RequestProvData");

- l_dbus_message_set_arguments(msg, "y", num_ele);
- l_dbus_send_with_reply(dbus, msg, mgr_prov_data, add_pending, NULL);
+ l_dbus_message_set_arguments(msg, "y", num_ele);
+ } else if (pending->transport == PB_NPPI_01) {
+ msg = l_dbus_message_new_method_call(dbus, sender, app_path,
+ MESH_PROVISIONER_INTERFACE,
+ "RequestReprovData");
+
+ l_dbus_message_set_arguments(msg, "qy", pending->original,
+ num_ele);
+ } else
+ return false;

- add_pending->num_ele = num_ele;
+ l_dbus_send_with_reply(dbus, msg, mgr_prov_data, pending, NULL);
+
+ pending->num_ele = num_ele;

return true;
}
@@ -213,15 +315,95 @@ static void add_start(void *user_data, int err)
l_debug("Start callback");

if (err == MESH_ERROR_NONE)
- reply = l_dbus_message_new_method_return(add_pending->msg);
+ reply = l_dbus_message_new_method_return(prov_pending->msg);
else
- reply = dbus_error(add_pending->msg, MESH_ERROR_FAILED,
+ reply = dbus_error(prov_pending->msg, MESH_ERROR_FAILED,
"Failed to start provisioning initiator");

l_dbus_send(dbus_get_bus(), reply);
- l_dbus_message_unref(add_pending->msg);
+ l_dbus_message_unref(prov_pending->msg);
+
+ prov_pending->msg = NULL;
+}
+
+static struct l_dbus_message *reprovision_call(struct l_dbus *dbus,
+ struct l_dbus_message *msg,
+ void *user_data)
+{
+ struct mesh_node *node = user_data;
+ struct l_dbus_message_iter options, var;
+ struct l_dbus_message *reply;
+ struct mesh_net *net = node_get_net(node);
+ const char *key;
+ uint16_t subidx;
+ uint16_t server = 0;
+ uint8_t nppi = 0;
+
+ l_debug("Reprovision request");
+
+ if (!l_dbus_message_get_arguments(msg, "qa{sv}", &server, &options))
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+ if (!IS_UNICAST(server))
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad Unicast");
+
+ /* Default to nodes primary subnet index */
+ subidx = mesh_net_get_primary_idx(net);
+
+ /* Get Provisioning Options */
+ while (l_dbus_message_iter_next_entry(&options, &key, &var)) {
+ bool failed = true;
+
+ if (!strcmp(key, "NPPI")) {
+ if (l_dbus_message_iter_get_variant(&var, "y", &nppi)) {
+ if (nppi <= 2)
+ failed = false;
+ }
+ } else if (!strcmp(key, "Subnet")) {
+ if (l_dbus_message_iter_get_variant(&var, "q",
+ &subidx)) {
+ if (subidx <= MAX_KEY_IDX)
+ failed = false;
+ }
+ }
+
+ if (failed)
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+ "Invalid options");
+ }
+
+ /* AddNode cancels all outstanding Scanning from node */
+ manager_scan_cancel(node);

- add_pending->msg = NULL;
+ /* Invoke Prov Initiator */
+ prov_pending = l_new(struct prov_remote_data, 1);
+
+ prov_pending->transport = nppi;
+ prov_pending->node = node;
+ prov_pending->original = server;
+ prov_pending->agent = node_get_agent(node);
+
+ if (!node_is_provisioner(node) || (prov_pending->agent == NULL)) {
+ reply = dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED,
+ "Missing Interfaces");
+ goto fail;
+ }
+
+ prov_pending->msg = l_dbus_message_ref(msg);
+ initiator_start(prov_pending->transport, server, subidx, NULL, 99, 60,
+ prov_pending->agent, add_start,
+ add_data_get, add_cmplt, node,
+ prov_pending);
+
+ prov_pending->disc_watch = l_dbus_add_disconnect_watch(dbus,
+ node_get_owner(node),
+ prov_disc_cb, NULL, NULL);
+
+ return NULL;
+fail:
+ l_free(prov_pending);
+ prov_pending = NULL;
+ return reply;
}

static struct l_dbus_message *add_node_call(struct l_dbus *dbus,
@@ -229,55 +411,101 @@ static struct l_dbus_message *add_node_call(struct l_dbus *dbus,
void *user_data)
{
struct mesh_node *node = user_data;
- struct l_dbus_message_iter iter_uuid, options;
+ struct l_dbus_message_iter iter_uuid, options, var;
struct l_dbus_message *reply;
+ struct mesh_net *net = node_get_net(node);
+ const char *key;
uint8_t *uuid;
- uint32_t n = 22;
+ uint32_t n = 0;
+ uint16_t subidx;
+ uint16_t sec = 60;
+ uint16_t server = 0;

l_debug("AddNode request");

if (!l_dbus_message_get_arguments(msg, "aya{sv}", &iter_uuid, &options))
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);

- if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n)
- || n != 16)
+ if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n) ||
+ n != 16)
return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
"Bad device UUID");

- /* Allow AddNode to cancel Scanning if from the same node */
- if (scan_node) {
- if (scan_node != node)
- return dbus_error(msg, MESH_ERROR_BUSY, NULL);
+ /* Default to nodes primary subnet index */
+ subidx = mesh_net_get_primary_idx(net);

- scan_cancel(NULL, node);
+ /* Get Provisioning Options */
+ while (l_dbus_message_iter_next_entry(&options, &key, &var)) {
+ bool failed = true;
+
+ if (!strcmp(key, "Seconds")) {
+ if (l_dbus_message_iter_get_variant(&var, "q", &sec))
+ failed = false;
+ } else if (!strcmp(key, "Server")) {
+ if (l_dbus_message_iter_get_variant(&var, "q",
+ &server)) {
+ if (server < 0x8000)
+ failed = false;
+ }
+ } else if (!strcmp(key, "Subnet")) {
+ if (l_dbus_message_iter_get_variant(&var, "q",
+ &subidx)) {
+ if (subidx <= MAX_KEY_IDX)
+ failed = false;
+ }
+ }
+
+ if (failed)
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+ "Invalid options");
}

+ /* Device Key update/Composition update requires remote server */
+ if (!n && !server)
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+ "Invalid options");
+
+ /* If no server specified, use local */
+ if (!server)
+ server = node_get_primary(node);
+
+ /* AddNode cancels all outstanding Scanning from node */
+ manager_scan_cancel(node);
+
/* Invoke Prov Initiator */
- add_pending = l_new(struct add_data, 1);
- memcpy(add_pending->uuid, uuid, 16);
- add_pending->node = node;
- add_pending->agent = node_get_agent(node);
+ prov_pending = l_new(struct prov_remote_data, 1);
+
+ if (n)
+ memcpy(prov_pending->uuid, uuid, 16);
+ else
+ uuid = NULL;

- if (!node_is_provisioner(node) || (add_pending->agent == NULL)) {
+ prov_pending->transport = PB_ADV;
+ prov_pending->node = node;
+ prov_pending->agent = node_get_agent(node);
+
+ if (!node_is_provisioner(node) || (prov_pending->agent == NULL)) {
l_debug("Provisioner: %d", node_is_provisioner(node));
- l_debug("Agent: %p", add_pending->agent);
+ l_debug("Agent: %p", prov_pending->agent);
reply = dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED,
"Missing Interfaces");
goto fail;
}

- add_pending->msg = l_dbus_message_ref(msg);
- initiator_start(PB_ADV, uuid, 99, 60, add_pending->agent, add_start,
- add_data_get, add_cmplt, node, add_pending);
+ prov_pending->msg = l_dbus_message_ref(msg);
+ initiator_start(PB_ADV, server, subidx, uuid, 99, sec,
+ prov_pending->agent, add_start,
+ add_data_get, add_cmplt, node,
+ prov_pending);

- add_pending->disc_watch = l_dbus_add_disconnect_watch(dbus,
+ prov_pending->disc_watch = l_dbus_add_disconnect_watch(dbus,
node_get_owner(node),
prov_disc_cb, NULL, NULL);

return NULL;
fail:
- l_free(add_pending);
- add_pending = NULL;
+ l_free(prov_pending);
+ prov_pending = NULL;
return reply;
}

@@ -337,38 +565,50 @@ static struct l_dbus_message *delete_node_call(struct l_dbus *dbus,
return l_dbus_message_new_method_return(msg);
}

-static void prov_beacon_recv(void *user_data, struct mesh_io_recv_info *info,
+static void manager_scan_result(void *user_data, uint16_t server, bool ext,
const uint8_t *data, uint16_t len)
{
- struct mesh_node *node = user_data;
+ struct scan_req node_svr = {
+ .node = user_data,
+ .server = server,
+ };
+ struct scan_req *req;
struct l_dbus_message_builder *builder;
struct l_dbus_message *msg;
struct l_dbus *dbus;
int16_t rssi;

- if (scan_node != node || len < sizeof(scan_uuid) + 2 || data[1] != 0x00)
+ l_debug("scan_result %4.4x %p", server, user_data);
+ req = l_queue_find(scans, by_node_svr, &node_svr);
+ if (!req) {
+ l_debug("No scan_result req");
return;
+ }

- if (!memcmp(data + 2, scan_uuid, sizeof(scan_uuid))) {
- if (info->rssi <= scan_rssi)
+ /* Filter repeats with weaker signal */
+ if (!memcmp(data + 1, req->uuid, sizeof(req->uuid))) {
+ if (!ext && ((int8_t) data[0] <= req->rssi)) {
+ l_debug("Already Seen");
return;
+ }
}

- memcpy(scan_uuid, data + 2, sizeof(scan_uuid));
- scan_rssi = info->rssi;
- rssi = info->rssi;
+ if (!ext && ((int8_t) data[0] > req->rssi))
+ req->rssi = (int8_t) data[0];

+ rssi = req->rssi;
+ memcpy(req->uuid, data + 1, sizeof(req->uuid));
dbus = dbus_get_bus();
- msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
- node_get_app_path(node),
+ msg = l_dbus_message_new_method_call(dbus, node_get_owner(req->node),
+ node_get_app_path(req->node),
MESH_PROVISIONER_INTERFACE,
"ScanResult");

builder = l_dbus_message_builder_new(msg);
l_dbus_message_builder_append_basic(builder, 'n', &rssi);
- dbus_append_byte_array(builder, data + 2, len -2);
+ dbus_append_byte_array(builder, data + 1, len - 1);
l_dbus_message_builder_enter_array(builder, "{sv}");
- /* TODO: populate with options when defined */
+ append_dict_entry_basic(builder, "Server", "q", &server);
l_dbus_message_builder_leave_array(builder);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
@@ -380,27 +620,71 @@ static struct l_dbus_message *start_scan_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
- struct mesh_node *node = user_data;
- uint16_t duration = 0;
- struct mesh_io *io;
+ struct scan_req new_req = {
+ .node = user_data,
+ .server = 0,
+ .timeout = NULL,
+ .ext = false,
+ };
+ struct scan_req *req;
struct mesh_net *net;
+ uint8_t *uuid, *ext = NULL;
+ uint8_t scan_req[21];
+ int n;
+ uint32_t ext_len;
+ uint32_t flen = 0;
+ uint16_t sec = 60;
const char *key;
struct l_dbus_message_iter options, var;
const char *sender = l_dbus_message_get_sender(msg);

- if (strcmp(sender, node_get_owner(node)))
+ if (strcmp(sender, node_get_owner(new_req.node)))
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);

if (!l_dbus_message_get_arguments(msg, "a{sv}", &options))
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);

+ if (!node_is_provisioner(new_req.node))
+ return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+
+ net = node_get_net(new_req.node);
+ new_req.net_idx = mesh_net_get_primary_idx(net);
+ memset(new_req.uuid, 0, sizeof(new_req.uuid));
+
while (l_dbus_message_iter_next_entry(&options, &key, &var)) {
bool failed = true;

if (!strcmp(key, "Seconds")) {
- if (l_dbus_message_iter_get_variant(&var, "q",
- &duration)) {
+ if (l_dbus_message_iter_get_variant(&var, "q", &sec))
failed = false;
+ } else if (!strcmp(key, "Subnet")) {
+ if (l_dbus_message_iter_get_variant(&var, "q",
+ &new_req.net_idx)) {
+ if (new_req.net_idx <= MAX_KEY_IDX)
+ failed = false;
+ }
+ } else if (!strcmp(key, "Server")) {
+ if (l_dbus_message_iter_get_variant(&var, "q",
+ &new_req.server)) {
+ if (new_req.server < 0x8000)
+ failed = false;
+ }
+ } else if (!strcmp(key, "Filter")) {
+ if (l_dbus_message_iter_get_variant(&var, "ay", &var)) {
+ if (l_dbus_message_iter_get_fixed_array(&var,
+ &uuid, &flen)) {
+ if (flen == 16) {
+ memcpy(new_req.uuid, uuid,
+ flen);
+ failed = false;
+ }
+ }
+ }
+ } else if (!strcmp(key, "Extended")) {
+ if (l_dbus_message_iter_get_variant(&var, "ay", &var)) {
+ if (l_dbus_message_iter_get_fixed_array(&var,
+ &ext, &ext_len))
+ failed = false;
}
}

@@ -409,27 +693,51 @@ static struct l_dbus_message *start_scan_call(struct l_dbus *dbus,
"Invalid options");
}

- if (scan_node && scan_node != node)
- return dbus_error(msg, MESH_ERROR_BUSY, NULL);
+ if (!scans)
+ scans = l_queue_new();

- if (!node_is_provisioner(node))
- return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+ if (new_req.server) {
+ if (!sec || sec > 60)
+ return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+ "Invalid options");
+ } else {
+ new_req.server = node_get_primary(new_req.node);
+ if (!sec || sec > 60)
+ sec = 60;
+ }
+
+ req = l_queue_remove_if(scans, by_node_svr, &new_req);
+
+ if (!req)
+ req = l_malloc(sizeof(new_req));
+
+ if (req->timeout) {
+ l_timeout_remove(req->timeout);
+ req->timeout = NULL;
+ }
+
+ *req = new_req;
+ req->rssi = -128;
+
+ if (sec)
+ req->timeout = l_timeout_create(sec, scan_cancel, req, NULL);

- if (scan_timeout)
- l_timeout_remove(scan_timeout);

- memset(scan_uuid, 0, sizeof(scan_uuid));
- scan_rssi = -128;
- scan_timeout = NULL;
- net = node_get_net(node);
- io = mesh_net_get_io(net);
- scan_node = node;
- mesh_io_register_recv_cb(io, prvb, sizeof(prvb),
- prov_beacon_recv, node);
+ n = mesh_model_opcode_set(OP_REM_PROV_SCAN_START, scan_req);
+ scan_req[n++] = 5;
+ scan_req[n++] = sec;
+ if (flen) {
+ memcpy(scan_req + n, req->uuid, flen);
+ n += flen;
+ }
+
+ mesh_model_send(req->node, 0, req->server, APP_IDX_DEV_REMOTE,
+ req->net_idx, DEFAULT_TTL,
+ true, n, scan_req);

- if (duration)
- scan_timeout = l_timeout_create(duration, scan_cancel,
- node, NULL);
+ initiator_scan_reg(manager_scan_result, req->node);
+
+ l_queue_push_tail(scans, req);

return l_dbus_message_new_method_return(msg);
}
@@ -444,12 +752,7 @@ static struct l_dbus_message *cancel_scan_call(struct l_dbus *dbus,
if (strcmp(sender, node_get_owner(node)) || !node_is_provisioner(node))
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);

- if (scan_node) {
- if (scan_node != node)
- return dbus_error(msg, MESH_ERROR_BUSY, NULL);
-
- scan_cancel(NULL, node);
- }
+ manager_scan_cancel(node);

return l_dbus_message_new_method_return(msg);
}
@@ -814,6 +1117,8 @@ static void setup_management_interface(struct l_dbus_interface *iface)
"aya{sv}", "uuid", "options");
l_dbus_interface_method(iface, "ImportRemoteNode", 0, import_node_call,
"", "qyay", "primary", "count", "dev_key");
+ l_dbus_interface_method(iface, "Reprovision", 0, reprovision_call,
+ "", "qa{sv}", "unicast", "options");
l_dbus_interface_method(iface, "DeleteRemoteNode", 0, delete_node_call,
"", "qy", "primary", "count");
l_dbus_interface_method(iface, "UnprovisionedScan", 0, start_scan_call,
@@ -849,7 +1154,7 @@ bool manager_dbus_init(struct l_dbus *bus)
if (!l_dbus_register_interface(bus, MESH_MANAGEMENT_INTERFACE,
setup_management_interface,
NULL, false)) {
- l_info("Unable to register %s interface",
+ l_debug("Unable to register %s interface",
MESH_MANAGEMENT_INTERFACE);
return false;
}
@@ -859,8 +1164,8 @@ bool manager_dbus_init(struct l_dbus *bus)

void manager_scan_cancel(struct mesh_node *node)
{
- if (scan_node != node)
- return;
+ struct scan_req *req;

- scan_cancel(NULL, node);
+ while ((req = l_queue_find(scans, by_node, node)))
+ scan_cancel(NULL, req);
}
diff --git a/mesh/mesh-config-json.c b/mesh/mesh-config-json.c
index 7f46c8582..8f321a731 100644
--- a/mesh/mesh-config-json.c
+++ b/mesh/mesh-config-json.c
@@ -58,6 +58,33 @@ static const char *cfgnode_name = "/node.json";
static const char *bak_ext = ".bak";
static const char *tmp_ext = ".tmp";

+/* JSON key words */
+static const char *unicastAddress = "unicastAddress";
+static const char *deviceCan = "deviceCan";
+static const char *deviceKey = "deviceKey";
+static const char *defaultTTL = "defaultTTL";
+static const char *sequenceNumber = "sequenceNumber";
+static const char *netKeys = "netKeys";
+static const char *appKeys = "appKeys";
+static const char *elements = "elements";
+static const char *models = "models";
+static const char *modelId = "modelId";
+static const char *address = "address";
+static const char *bind = "bind";
+static const char *publish = "publish";
+static const char *subscribe = "subscribe";
+static const char *boundNetKey = "boundNetKey";
+static const char *keyRefresh = "keyRefresh";
+static const char *subEnabled = "subEnabled";
+static const char *pubEnabled = "pubEnabled";
+static const char *retransmit = "retransmit";
+
+/* Common JSON values */
+static const char *enabled = "enabled";
+static const char *disabled = "disabled";
+static const char *unsupported = "unsupported";
+
+
static bool save_config(json_object *jnode, const char *fname)
{
FILE *outfile;
@@ -134,14 +161,14 @@ static int get_element_index(json_object *jnode, uint16_t ele_addr)
uint16_t addr, num_ele;
char *str;

- if (!json_object_object_get_ex(jnode, "unicastAddress", &jvalue))
+ if (!json_object_object_get_ex(jnode, unicastAddress, &jvalue))
return -1;

str = (char *)json_object_get_string(jvalue);
if (sscanf(str, "%04hx", &addr) != 1)
return -1;

- if (!json_object_object_get_ex(jnode, "elements", &jelements))
+ if (!json_object_object_get_ex(jnode, elements, &jelements))
return -1;

num_ele = json_object_array_length(jelements);
@@ -160,14 +187,14 @@ static json_object *get_element_model(json_object *jnode, int ele_idx,
size_t len;
char buf[9];

- if (!json_object_object_get_ex(jnode, "elements", &jelements))
+ if (!json_object_object_get_ex(jnode, elements, &jelements))
return NULL;

jelement = json_object_array_get_idx(jelements, ele_idx);
if (!jelement)
return NULL;

- if (!json_object_object_get_ex(jelement, "models", &jmodels))
+ if (!json_object_object_get_ex(jelement, models, &jmodels))
return NULL;

num_mods = json_object_array_length(jmodels);
@@ -189,7 +216,7 @@ static json_object *get_element_model(json_object *jnode, int ele_idx,
char *str;

jmodel = json_object_array_get_idx(jmodels, i);
- if (!json_object_object_get_ex(jmodel, "modelId", &jvalue))
+ if (!json_object_object_get_ex(jmodel, modelId, &jvalue))
return NULL;

str = (char *)json_object_get_string(jvalue);
@@ -298,7 +325,7 @@ static bool read_unicast_address(json_object *jobj, uint16_t *unicast)
json_object *jvalue;
char *str;

- if (!json_object_object_get_ex(jobj, "unicastAddress", &jvalue))
+ if (!json_object_object_get_ex(jobj, unicastAddress, &jvalue))
return false;

str = (char *)json_object_get_string(jvalue);
@@ -314,7 +341,7 @@ static bool read_default_ttl(json_object *jobj, uint8_t *ttl)
int val;

/* defaultTTL is optional */
- if (!json_object_object_get_ex(jobj, "defaultTTL", &jvalue))
+ if (!json_object_object_get_ex(jobj, defaultTTL, &jvalue))
return true;

val = json_object_get_int(jvalue);
@@ -336,7 +363,7 @@ static bool read_seq_number(json_object *jobj, uint32_t *seq_number)
int val;

/* sequenceNumber is optional */
- if (!json_object_object_get_ex(jobj, "sequenceNumber", &jvalue))
+ if (!json_object_object_get_ex(jobj, sequenceNumber, &jvalue))
return true;

val = json_object_get_int(jvalue);
@@ -396,7 +423,25 @@ static bool read_device_key(json_object *jobj, uint8_t key_buf[16])
if (!key_buf)
return false;

- if (!json_object_object_get_ex(jobj, "deviceKey", &jvalue))
+ if (!json_object_object_get_ex(jobj, deviceKey, &jvalue))
+ return false;
+
+ str = (char *)json_object_get_string(jvalue);
+ if (!str2hex(str, strlen(str), key_buf, 16))
+ return false;
+
+ return true;
+}
+
+static bool read_candidate(json_object *jobj, uint8_t key_buf[16])
+{
+ json_object *jvalue;
+ char *str;
+
+ if (!key_buf)
+ return false;
+
+ if (!json_object_object_get_ex(jobj, deviceCan, &jvalue))
return false;

str = (char *)json_object_get_string(jvalue);
@@ -460,7 +505,7 @@ static bool read_app_keys(json_object *jobj, struct mesh_config_node *node)
int len;
int i;

- if (!json_object_object_get_ex(jobj, "appKeys", &jarray))
+ if (!json_object_object_get_ex(jobj, appKeys, &jarray))
return true;

if (json_object_get_type(jarray) != json_type_array)
@@ -484,7 +529,7 @@ static bool read_app_keys(json_object *jobj, struct mesh_config_node *node)
if (!get_key_index(jtemp, "index", &appkey->app_idx))
goto fail;

- if (!get_key_index(jtemp, "boundNetKey", &appkey->net_idx))
+ if (!get_key_index(jtemp, boundNetKey, &appkey->net_idx))
goto fail;

if (!json_object_object_get_ex(jtemp, "key", &jvalue))
@@ -516,7 +561,7 @@ static bool read_net_keys(json_object *jobj, struct mesh_config_node *node)
int i;

/* At least one NetKey must be present for a provisioned node */
- if (!json_object_object_get_ex(jobj, "netKeys", &jarray))
+ if (!json_object_object_get_ex(jobj, netKeys, &jarray))
return false;

if (json_object_get_type(jarray) != json_type_array)
@@ -547,7 +592,7 @@ static bool read_net_keys(json_object *jobj, struct mesh_config_node *node)
if (!str2hex(str, strlen(str), netkey->new_key, 16))
goto fail;

- if (!json_object_object_get_ex(jtemp, "keyRefresh", &jvalue))
+ if (!json_object_object_get_ex(jtemp, keyRefresh, &jvalue))
netkey->phase = KEY_REFRESH_PHASE_NONE;
else
netkey->phase = (uint8_t) json_object_get_int(jvalue);
@@ -598,7 +643,7 @@ bool mesh_config_net_key_add(struct mesh_config *cfg, uint16_t idx,
jnode = cfg->jnode;

l_debug("netKey %4.4x", idx);
- json_object_object_get_ex(jnode, "netKeys", &jarray);
+ json_object_object_get_ex(jnode, netKeys, &jarray);
if (jarray)
jentry = get_key_object(jarray, idx);

@@ -616,14 +661,14 @@ bool mesh_config_net_key_add(struct mesh_config *cfg, uint16_t idx,
if (!add_key_value(jentry, "key", key))
goto fail;

- json_object_object_add(jentry, "keyRefresh",
+ json_object_object_add(jentry, keyRefresh,
json_object_new_int(KEY_REFRESH_PHASE_NONE));

if (!jarray) {
jarray = json_object_new_array();
if (!jarray)
goto fail;
- json_object_object_add(jnode, "netKeys", jarray);
+ json_object_object_add(jnode, netKeys, jarray);
}

json_object_array_add(jarray, jentry);
@@ -648,7 +693,7 @@ bool mesh_config_net_key_update(struct mesh_config *cfg, uint16_t idx,

jnode = cfg->jnode;

- if (!json_object_object_get_ex(jnode, "netKeys", &jarray))
+ if (!json_object_object_get_ex(jnode, netKeys, &jarray))
return false;

jentry = get_key_object(jarray, idx);
@@ -667,7 +712,7 @@ bool mesh_config_net_key_update(struct mesh_config *cfg, uint16_t idx,
if (!add_key_value(jentry, "key", key))
return false;

- json_object_object_add(jentry, "keyRefresh",
+ json_object_object_add(jentry, keyRefresh,
json_object_new_int(KEY_REFRESH_PHASE_ONE));

return save_config(jnode, cfg->node_dir_path);
@@ -682,20 +727,55 @@ bool mesh_config_net_key_del(struct mesh_config *cfg, uint16_t idx)

jnode = cfg->jnode;

- if (!json_object_object_get_ex(jnode, "netKeys", &jarray))
+ if (!json_object_object_get_ex(jnode, netKeys, &jarray))
return true;

jarray_key_del(jarray, idx);

if (!json_object_array_length(jarray))
- json_object_object_del(jnode, "netKeys");
+ json_object_object_del(jnode, netKeys);

return save_config(jnode, cfg->node_dir_path);
}

bool mesh_config_write_device_key(struct mesh_config *cfg, uint8_t *key)
{
- if (!cfg || !add_key_value(cfg->jnode, "deviceKey", key))
+ if (!cfg || !add_key_value(cfg->jnode, deviceKey, key))
+ return false;
+
+ return save_config(cfg->jnode, cfg->node_dir_path);
+}
+
+bool mesh_config_write_candidate(struct mesh_config *cfg, uint8_t *key)
+{
+ if (!cfg || !add_key_value(cfg->jnode, deviceCan, key))
+ return false;
+
+ return save_config(cfg->jnode, cfg->node_dir_path);
+}
+
+bool mesh_config_read_candidate(struct mesh_config *cfg, uint8_t *key)
+{
+ if (!cfg)
+ return false;
+
+ return read_candidate(cfg->jnode, key);
+}
+
+bool mesh_config_finalize_candidate(struct mesh_config *cfg)
+{
+ uint8_t key[16];
+
+ if (!cfg)
+ return false;
+
+ if (!read_candidate(cfg->jnode, key))
+ return false;
+
+ json_object_object_del(cfg->jnode, deviceCan);
+ json_object_object_del(cfg->jnode, deviceKey);
+
+ if (!add_key_value(cfg->jnode, deviceKey, key))
return false;

return save_config(cfg->jnode, cfg->node_dir_path);
@@ -719,7 +799,7 @@ bool mesh_config_app_key_add(struct mesh_config *cfg, uint16_t net_idx,

jnode = cfg->jnode;

- json_object_object_get_ex(jnode, "appKeys", &jarray);
+ json_object_object_get_ex(jnode, appKeys, &jarray);
if (jarray)
jentry = get_key_object(jarray, app_idx);

@@ -734,7 +814,7 @@ bool mesh_config_app_key_add(struct mesh_config *cfg, uint16_t net_idx,
if (!write_int(jentry, "index", app_idx))
goto fail;

- if (!write_int(jentry, "boundNetKey", net_idx))
+ if (!write_int(jentry, boundNetKey, net_idx))
goto fail;

if (!add_key_value(jentry, "key", key))
@@ -744,7 +824,7 @@ bool mesh_config_app_key_add(struct mesh_config *cfg, uint16_t net_idx,
jarray = json_object_new_array();
if (!jarray)
goto fail;
- json_object_object_add(jnode, "appKeys", jarray);
+ json_object_object_add(jnode, appKeys, jarray);
}

json_object_array_add(jarray, jentry);
@@ -770,7 +850,7 @@ bool mesh_config_app_key_update(struct mesh_config *cfg, uint16_t app_idx,

jnode = cfg->jnode;

- if (!json_object_object_get_ex(jnode, "appKeys", &jarray))
+ if (!json_object_object_get_ex(jnode, appKeys, &jarray))
return false;

/* The key entry should exist if the key is updated */
@@ -804,13 +884,13 @@ bool mesh_config_app_key_del(struct mesh_config *cfg, uint16_t net_idx,

jnode = cfg->jnode;

- if (!json_object_object_get_ex(jnode, "appKeys", &jarray))
+ if (!json_object_object_get_ex(jnode, appKeys, &jarray))
return true;

jarray_key_del(jarray, idx);

if (!json_object_array_length(jarray))
- json_object_object_del(jnode, "appKeys");
+ json_object_object_del(jnode, appKeys);

return save_config(jnode, cfg->node_dir_path);
}
@@ -840,7 +920,7 @@ bool mesh_config_model_binding_add(struct mesh_config *cfg, uint16_t ele_addr,
if (!jmodel)
return false;

- json_object_object_get_ex(jmodel, "bind", &jarray);
+ json_object_object_get_ex(jmodel, bind, &jarray);
if (jarray && jarray_has_string(jarray, buf, 4))
return true;

@@ -854,7 +934,7 @@ bool mesh_config_model_binding_add(struct mesh_config *cfg, uint16_t ele_addr,
json_object_put(jstring);
return false;
}
- json_object_object_add(jmodel, "bind", jarray);
+ json_object_object_add(jmodel, bind, jarray);
}

json_object_array_add(jarray, jstring);
@@ -887,13 +967,13 @@ bool mesh_config_model_binding_del(struct mesh_config *cfg, uint16_t ele_addr,
if (!jmodel)
return false;

- if (!json_object_object_get_ex(jmodel, "bind", &jarray))
+ if (!json_object_object_get_ex(jmodel, bind, &jarray))
return true;

jarray_string_del(jarray, buf, 4);

if (!json_object_array_length(jarray))
- json_object_object_del(jmodel, "bind");
+ json_object_object_del(jmodel, bind);

return save_config(jnode, cfg->node_dir_path);
}
@@ -963,7 +1043,7 @@ static struct mesh_config_pub *parse_model_publication(json_object *jpub)
int len, value;
char *str;

- if (!json_object_object_get_ex(jpub, "address", &jvalue))
+ if (!json_object_object_get_ex(jpub, address, &jvalue))
return NULL;

str = (char *)json_object_get_string(jvalue);
@@ -998,9 +1078,10 @@ static struct mesh_config_pub *parse_model_publication(json_object *jpub)

if (!get_int(jpub, "credentials", &value))
goto fail;
+
pub->credential = (uint8_t) value;

- if (!json_object_object_get_ex(jpub, "retransmit", &jvalue))
+ if (!json_object_object_get_ex(jpub, retransmit, &jvalue))
goto fail;

if (!get_int(jvalue, "count", &value))
@@ -1093,7 +1174,7 @@ static bool parse_models(json_object *jmodels, struct mesh_config_element *ele)

l_queue_push_tail(ele->models, mod);

- if (!json_object_object_get_ex(jmodel, "modelId", &jvalue))
+ if (!json_object_object_get_ex(jmodel, modelId, &jvalue))
goto fail;

str = (char *)json_object_get_string(jvalue);
@@ -1112,29 +1193,32 @@ static bool parse_models(json_object *jmodels, struct mesh_config_element *ele)

mod->id = id;

- if (json_object_object_get_ex(jmodel, "bind", &jarray)) {
+ if (len == 8)
+ mod->vendor = true;
+
+ if (json_object_object_get_ex(jmodel, bind, &jarray)) {
if (json_object_get_type(jarray) != json_type_array ||
!parse_bindings(jarray, mod))
goto fail;
}

- if (json_object_object_get_ex(jmodel, "pubEnabled", &jvalue))
+ if (json_object_object_get_ex(jmodel, pubEnabled, &jvalue))
mod->pub_enabled = json_object_get_boolean(jvalue);
else
mod->pub_enabled = true;

- if (json_object_object_get_ex(jmodel, "subEnabled", &jvalue))
+ if (json_object_object_get_ex(jmodel, subEnabled, &jvalue))
mod->sub_enabled = json_object_get_boolean(jvalue);
else
mod->sub_enabled = true;

- if (json_object_object_get_ex(jmodel, "publish", &jvalue)) {
+ if (json_object_object_get_ex(jmodel, publish, &jvalue)) {
mod->pub = parse_model_publication(jvalue);
if (!mod->pub)
goto fail;
}

- if (json_object_object_get_ex(jmodel, "subscribe", &jarray)) {
+ if (json_object_object_get_ex(jmodel, subscribe, &jarray)) {
if (!parse_model_subscriptions(jarray, mod))
goto fail;
}
@@ -1187,7 +1271,7 @@ static bool parse_elements(json_object *jelems, struct mesh_config_node *node)
if (sscanf(str, "%04hx", &(ele->location)) != 1)
goto fail;

- if (json_object_object_get_ex(jelement, "models", &jmodels)) {
+ if (json_object_object_get_ex(jelement, models, &jmodels)) {
if (json_object_get_type(jmodels) != json_type_array ||
!parse_models(jmodels, ele))
goto fail;
@@ -1211,13 +1295,13 @@ static int get_mode(json_object *jvalue)
if (!str)
return 0xffffffff;

- if (!strncasecmp(str, "disabled", strlen("disabled")))
+ if (!strncasecmp(str, disabled, strlen(disabled)))
return MESH_MODE_DISABLED;

- if (!strncasecmp(str, "enabled", strlen("enabled")))
+ if (!strncasecmp(str, enabled, strlen(enabled)))
return MESH_MODE_ENABLED;

- if (!strncasecmp(str, "unsupported", strlen("unsupported")))
+ if (!strncasecmp(str, unsupported, strlen(unsupported)))
return MESH_MODE_UNSUPPORTED;

return 0xffffffff;
@@ -1323,7 +1407,7 @@ static bool read_net_transmit(json_object *jobj, struct mesh_config_node *node)
uint16_t interval;
uint8_t cnt;

- if (!json_object_object_get_ex(jobj, "retransmit", &jrtx))
+ if (!json_object_object_get_ex(jobj, retransmit, &jrtx))
return true;

if (!json_object_object_get_ex(jrtx, "count", &jvalue))
@@ -1386,7 +1470,7 @@ static bool read_node(json_object *jnode, struct mesh_config_node *node)
}

/* Check for required "elements" property */
- if (!json_object_object_get_ex(jnode, "elements", &jvalue))
+ if (!json_object_object_get_ex(jnode, elements, &jvalue))
return false;

if (!read_net_transmit(jnode, node)) {
@@ -1460,11 +1544,11 @@ static const char *mode_to_string(int mode)
{
switch (mode) {
case MESH_MODE_DISABLED:
- return "disabled";
+ return disabled;
case MESH_MODE_ENABLED:
- return "enabled";
+ return enabled;
default:
- return "unsupported";
+ return unsupported;
}
}

@@ -1522,7 +1606,7 @@ fail:

bool mesh_config_write_unicast(struct mesh_config *cfg, uint16_t unicast)
{
- if (!cfg || !write_uint16_hex(cfg->jnode, "unicastAddress", unicast))
+ if (!cfg || !write_uint16_hex(cfg->jnode, unicastAddress, unicast))
return false;

return save_config(cfg->jnode, cfg->node_dir_path);
@@ -1558,8 +1642,8 @@ bool mesh_config_write_net_transmit(struct mesh_config *cfg, uint8_t cnt,
if (!write_int(jrtx, "interval", interval))
goto fail;

- json_object_object_del(jnode, "retransmit");
- json_object_object_add(jnode, "retransmit", jrtx);
+ json_object_object_del(jnode, retransmit);
+ json_object_object_add(jnode, retransmit, jrtx);

return save_config(cfg->jnode, cfg->node_dir_path);

@@ -1599,8 +1683,8 @@ static void add_model(void *a, void *b)
if (!jmodel)
return;

- result = (mod->vendor) ? write_uint32_hex(jmodel, "modelId", mod->id) :
- write_uint16_hex(jmodel, "modelId", (uint16_t) mod->id);
+ result = (mod->vendor) ? write_uint32_hex(jmodel, modelId, mod->id) :
+ write_uint16_hex(jmodel, modelId, (uint16_t) mod->id);

if (!result) {
json_object_put(jmodel);
@@ -1608,10 +1692,10 @@ static void add_model(void *a, void *b)
}

jval = json_object_new_boolean(mod->sub_enabled);
- json_object_object_add(jmodel, "subEnabled", jval);
+ json_object_object_add(jmodel, subEnabled, jval);

jval = json_object_new_boolean(mod->pub_enabled);
- json_object_object_add(jmodel, "pubEnabled", jval);
+ json_object_object_add(jmodel, pubEnabled, jval);

json_object_array_add(jmodels, jmodel);
}
@@ -1663,11 +1747,11 @@ static struct mesh_config *create_config(const char *cfg_path,
return NULL;

/* Sequence number */
- json_object_object_add(jnode, "sequenceNumber",
+ json_object_object_add(jnode, sequenceNumber,
json_object_new_int(node->seq_number));

/* Default TTL */
- json_object_object_add(jnode, "defaultTTL",
+ json_object_object_add(jnode, defaultTTL,
json_object_new_int(node->ttl));

/* Elements */
@@ -1702,11 +1786,11 @@ static struct mesh_config *create_config(const char *cfg_path,
if (!jmodels)
goto fail;

- json_object_object_add(jelement, "models", jmodels);
+ json_object_object_add(jelement, models, jmodels);
l_queue_foreach(ele->models, add_model, jmodels);
}

- json_object_object_add(jnode, "elements", jelems);
+ json_object_object_add(jnode, elements, jelems);

cfg = l_new(struct mesh_config, 1);

@@ -1724,6 +1808,55 @@ fail:
return NULL;
}

+void mesh_config_reset(struct mesh_config *cfg, struct mesh_config_node *node)
+{
+ json_object *jelems;
+ const struct l_queue_entry *entry;
+
+ if (!cfg || !cfg->jnode)
+ return;
+
+ /* TODO: Recreate Element Array */
+ jelems = json_object_new_array();
+ if (!jelems)
+ return;
+
+ entry = l_queue_get_entries(node->elements);
+
+ for (; entry; entry = entry->next) {
+ struct mesh_config_element *ele = entry->data;
+ json_object *jelement, *jmodels;
+
+ jelement = json_object_new_object();
+
+ if (!jelement) {
+ json_object_put(jelems);
+ return;
+ }
+
+ write_int(jelement, "elementIndex", ele->index);
+ write_uint16_hex(jelement, "location", ele->location);
+ json_object_array_add(jelems, jelement);
+
+ /* Models */
+ if (l_queue_isempty(ele->models))
+ continue;
+
+ jmodels = json_object_new_array();
+ if (!jmodels) {
+ json_object_put(jelems);
+ return;
+ }
+
+ json_object_object_add(jelement, models, jmodels);
+ l_queue_foreach(ele->models, add_model, jmodels);
+ }
+
+ /* Replace element array */
+ json_object_object_del(cfg->jnode, elements);
+ json_object_object_add(cfg->jnode, elements, jelems);
+}
+
struct mesh_config *mesh_config_create(const char *cfgdir_name,
const uint8_t uuid[16], struct mesh_config_node *db_node)
{
@@ -1768,7 +1901,7 @@ static void finish_key_refresh(json_object *jobj, uint16_t net_idx)
int i, len;

/* Clean up all the bound appkeys */
- if (!json_object_object_get_ex(jobj, "appKeys", &jarray))
+ if (!json_object_object_get_ex(jobj, appKeys, &jarray))
return;

len = json_object_array_length(jarray);
@@ -1779,7 +1912,7 @@ static void finish_key_refresh(json_object *jobj, uint16_t net_idx)

jentry = json_object_array_get_idx(jarray, i);

- if (!get_key_index(jentry, "boundNetKey", &idx))
+ if (!get_key_index(jentry, boundNetKey, &idx))
continue;

if (idx != net_idx)
@@ -1803,14 +1936,14 @@ bool mesh_config_net_key_set_phase(struct mesh_config *cfg, uint16_t idx,

jnode = cfg->jnode;

- if (json_object_object_get_ex(jnode, "netKeys", &jarray))
+ if (json_object_object_get_ex(jnode, netKeys, &jarray))
jentry = get_key_object(jarray, idx);

if (!jentry)
return false;

- json_object_object_del(jentry, "keyRefresh");
- json_object_object_add(jentry, "keyRefresh",
+ json_object_object_del(jentry, keyRefresh);
+ json_object_object_add(jentry, keyRefresh,
json_object_new_int(phase));

if (phase == KEY_REFRESH_PHASE_NONE) {
@@ -1842,16 +1975,16 @@ bool mesh_config_model_pub_add(struct mesh_config *cfg, uint16_t ele_addr,
if (!jmodel)
return false;

- json_object_object_del(jmodel, "publish");
+ json_object_object_del(jmodel, publish);

jpub = json_object_new_object();
if (!jpub)
return false;

if (pub->virt)
- res = add_key_value(jpub, "address", pub->virt_addr);
+ res = add_key_value(jpub, address, pub->virt_addr);
else
- res = write_uint16_hex(jpub, "address", pub->addr);
+ res = write_uint16_hex(jpub, address, pub->addr);

if (!res)
goto fail;
@@ -1878,8 +2011,8 @@ bool mesh_config_model_pub_add(struct mesh_config *cfg, uint16_t ele_addr,
if (!write_int(jrtx, "interval", pub->interval))
goto fail;

- json_object_object_add(jpub, "retransmit", jrtx);
- json_object_object_add(jmodel, "publish", jpub);
+ json_object_object_add(jpub, retransmit, jrtx);
+ json_object_object_add(jmodel, publish, jpub);

return save_config(jnode, cfg->node_dir_path);

@@ -1911,23 +2044,23 @@ bool mesh_config_model_pub_del(struct mesh_config *cfg, uint16_t addr,
uint32_t mod_id, bool vendor)
{
if (!cfg || !delete_model_property(cfg->jnode, addr, mod_id, vendor,
- "publish"))
+ publish))
return false;

return save_config(cfg->jnode, cfg->node_dir_path);
}

-static void del_page(json_object *jarray, uint8_t page)
+static bool del_page(json_object *jarray, uint8_t page)
{
char buf[3];
int i, len, ret;

if (!jarray)
- return;
+ return false;

ret = snprintf(buf, 3, "%2.2x", page);
if (ret < 0)
- return;
+ return false;

len = json_object_array_length(jarray);

@@ -1938,10 +2071,29 @@ static void del_page(json_object *jarray, uint8_t page)
jentry = json_object_array_get_idx(jarray, i);
str = (char *)json_object_get_string(jentry);

- /* Delete matching page(s) */
- if (!memcmp(str, buf, 2))
+ /* Delete matching page */
+ if (!memcmp(str, buf, 2)) {
json_object_array_del_idx(jarray, i, 1);
+ break;
+ }
}
+
+ return true;
+}
+
+void mesh_config_comp_page_del(struct mesh_config *cfg, uint8_t page)
+{
+ json_object *jnode, *jarray = NULL;
+
+ if (!cfg)
+ return;
+
+ jnode = cfg->jnode;
+
+ json_object_object_get_ex(jnode, "pages", &jarray);
+
+ if (del_page(jarray, page))
+ save_config(jnode, cfg->node_dir_path);
}

bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page,
@@ -1984,56 +2136,6 @@ bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page,
return save_config(jnode, cfg->node_dir_path);
}

-bool mesh_config_comp_page_mv(struct mesh_config *cfg, uint8_t old, uint8_t nw)
-{
- json_object *jnode, *jarray = NULL;
- uint8_t *data;
- char *str;
- char old_buf[3];
- int i, len, ret, dlen = 0;
- bool status = true;
-
- if (!cfg || old == nw)
- return false;
-
- ret = snprintf(old_buf, 3, "%2.2x", old);
- if (ret < 0)
- return false;
-
- jnode = cfg->jnode;
-
- json_object_object_get_ex(jnode, "pages", &jarray);
-
- if (!jarray)
- return false;
-
- data = l_malloc(MAX_MSG_LEN);
-
- len = json_object_array_length(jarray);
-
- for (i = 0; i < len; i++) {
- json_object *jentry;
-
- jentry = json_object_array_get_idx(jarray, i);
- str = (char *)json_object_get_string(jentry);
-
- /* Delete matching page(s) but save data*/
- if (!memcmp(str, old_buf, 2)) {
- dlen = strlen(str + 2);
- str2hex(str + 2, dlen, data, MAX_MSG_LEN);
- dlen /= 2;
- json_object_array_del_idx(jarray, i, 1);
- }
- }
-
- if (dlen)
- status = mesh_config_comp_page_add(cfg, nw, data, dlen);
-
- l_free(data);
-
- return status;
-}
-
bool mesh_config_model_sub_add(struct mesh_config *cfg, uint16_t ele_addr,
uint32_t mod_id, bool vendor,
struct mesh_config_sub *sub)
@@ -2064,7 +2166,7 @@ bool mesh_config_model_sub_add(struct mesh_config *cfg, uint16_t ele_addr,
len = 32;
}

- json_object_object_get_ex(jmodel, "subscribe", &jarray);
+ json_object_object_get_ex(jmodel, subscribe, &jarray);
if (jarray && jarray_has_string(jarray, buf, len))
return true;

@@ -2078,7 +2180,7 @@ bool mesh_config_model_sub_add(struct mesh_config *cfg, uint16_t ele_addr,
json_object_put(jstring);
return false;
}
- json_object_object_add(jmodel, "subscribe", jarray);
+ json_object_object_add(jmodel, subscribe, jarray);
}

json_object_array_add(jarray, jstring);
@@ -2107,7 +2209,7 @@ bool mesh_config_model_sub_del(struct mesh_config *cfg, uint16_t ele_addr,
if (!jmodel)
return false;

- if (!json_object_object_get_ex(jmodel, "subscribe", &jarray))
+ if (!json_object_object_get_ex(jmodel, subscribe, &jarray))
return true;

if (!sub->virt) {
@@ -2122,7 +2224,7 @@ bool mesh_config_model_sub_del(struct mesh_config *cfg, uint16_t ele_addr,
jarray_string_del(jarray, buf, len);

if (!json_object_array_length(jarray))
- json_object_object_del(jmodel, "subscribe");
+ json_object_object_del(jmodel, subscribe);

return save_config(jnode, cfg->node_dir_path);
}
@@ -2131,7 +2233,7 @@ bool mesh_config_model_sub_del_all(struct mesh_config *cfg, uint16_t addr,
uint32_t mod_id, bool vendor)
{
if (!cfg || !delete_model_property(cfg->jnode, addr, mod_id, vendor,
- "subscribe"))
+ subscribe))
return false;

return save_config(cfg->jnode, cfg->node_dir_path);
@@ -2161,7 +2263,7 @@ bool mesh_config_model_pub_enable(struct mesh_config *cfg, uint16_t ele_addr,
json_object_object_add(jmodel, "pubDisabled", jval);

if (!enable)
- json_object_object_del(jmodel, "publish");
+ json_object_object_del(jmodel, publish);

return save_config(cfg->jnode, cfg->node_dir_path);
}
@@ -2184,13 +2286,13 @@ bool mesh_config_model_sub_enable(struct mesh_config *cfg, uint16_t ele_addr,
if (!jmodel)
return false;

- json_object_object_del(jmodel, "subEnabled");
+ json_object_object_del(jmodel, subEnabled);

jval = json_object_new_boolean(enable);
- json_object_object_add(jmodel, "subEnabled", jval);
+ json_object_object_add(jmodel, subEnabled, jval);

if (!enable)
- json_object_object_del(jmodel, "subscribe");
+ json_object_object_del(jmodel, subscribe);

return save_config(cfg->jnode, cfg->node_dir_path);
}
@@ -2205,14 +2307,14 @@ bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq,
return false;

if (!cache) {
- if (!write_int(cfg->jnode, "sequenceNumber", seq))
+ if (!write_int(cfg->jnode, sequenceNumber, seq))
return false;

return mesh_config_save(cfg, true, NULL, NULL);
}

/* If resetting seq to Zero, make sure cached value reset as well */
- if (seq && get_int(cfg->jnode, "sequenceNumber", &value))
+ if (seq && get_int(cfg->jnode, sequenceNumber, &value))
cached = (uint32_t)value;

/*
@@ -2262,8 +2364,8 @@ bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq,

l_debug("Seq Cache: %d -> %d", seq, cached);

- if (!write_int(cfg->jnode, "sequenceNumber", cached))
- return false;
+ if (!write_int(cfg->jnode, sequenceNumber, cached))
+ return false;

return mesh_config_save(cfg, false, NULL, NULL);
}
@@ -2273,7 +2375,7 @@ bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq,

bool mesh_config_write_ttl(struct mesh_config *cfg, uint8_t ttl)
{
- if (!cfg || !write_int(cfg->jnode, "defaultTTL", ttl))
+ if (!cfg || !write_int(cfg->jnode, defaultTTL, ttl))
return false;

return save_config(cfg->jnode, cfg->node_dir_path);
diff --git a/mesh/mesh-config.h b/mesh/mesh-config.h
index 420775829..ed1b610de 100644
--- a/mesh/mesh-config.h
+++ b/mesh/mesh-config.h
@@ -119,6 +119,7 @@ void mesh_config_release(struct mesh_config *cfg);
void mesh_config_destroy_nvm(struct mesh_config *cfg);
bool mesh_config_save(struct mesh_config *cfg, bool no_wait,
mesh_config_status_func_t cb, void *user_data);
+void mesh_config_reset(struct mesh_config *cfg, struct mesh_config_node *node);
struct mesh_config *mesh_config_create(const char *cfgdir_name,
const uint8_t uuid[16],
struct mesh_config_node *node);
@@ -126,6 +127,9 @@ struct mesh_config *mesh_config_create(const char *cfgdir_name,
bool mesh_config_write_net_transmit(struct mesh_config *cfg, uint8_t cnt,
uint16_t interval);
bool mesh_config_write_device_key(struct mesh_config *cfg, uint8_t *key);
+bool mesh_config_write_candidate(struct mesh_config *cfg, uint8_t *key);
+bool mesh_config_read_candidate(struct mesh_config *cfg, uint8_t *key);
+bool mesh_config_finalize_candidate(struct mesh_config *cfg);
bool mesh_config_write_token(struct mesh_config *cfg, uint8_t *token);
bool mesh_config_write_network_key(struct mesh_config *cfg, uint16_t idx,
uint8_t *key, uint8_t *new_key, int phase);
@@ -141,7 +145,7 @@ bool mesh_config_write_mode(struct mesh_config *cfg, const char *keyword,
int value);
bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page,
uint8_t *data, uint16_t size);
-bool mesh_config_comp_page_mv(struct mesh_config *cfg, uint8_t old, uint8_t nw);
+void mesh_config_comp_page_del(struct mesh_config *cfg, uint8_t page);
bool mesh_config_model_binding_add(struct mesh_config *cfg, uint16_t ele_addr,
uint32_t mod_id, bool vendor,
uint16_t app_idx);
diff --git a/mesh/model.c b/mesh/model.c
index d48e6ef12..e2babea10 100644
--- a/mesh/model.c
+++ b/mesh/model.c
@@ -24,6 +24,8 @@
#include "mesh/net.h"
#include "mesh/appkey.h"
#include "mesh/cfgmod.h"
+#include "mesh/prov.h"
+#include "mesh/remprv.h"
#include "mesh/error.h"
#include "mesh/dbus.h"
#include "mesh/util.h"
@@ -76,6 +78,9 @@ static bool is_internal(uint32_t id)
if (id == CONFIG_SRV_MODEL || id == CONFIG_CLI_MODEL)
return true;

+ if (id == REM_PROV_SRV_MODEL || id == REM_PROV_CLI_MODEL)
+ return true;
+
return false;
}

@@ -457,13 +462,25 @@ static int dev_packet_decrypt(struct mesh_node *node, const uint8_t *data,
dst, key_aid, seq, iv_idx, out, key))
return APP_IDX_DEV_LOCAL;

- if (!keyring_get_remote_dev_key(node, src, dev_key))
+ key = dev_key;
+
+ if (keyring_get_remote_dev_key(node, src, dev_key)) {
+ if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict,
+ src, dst, key_aid, seq, iv_idx, out, key))
+ return APP_IDX_DEV_REMOTE;
+ }
+
+ /* See if there is a local Device Key Candidate as last resort */
+ if (!node_get_device_key_candidate(node, dev_key))
return -1;

- key = dev_key;
- if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict, src,
- dst, key_aid, seq, iv_idx, out, key))
- return APP_IDX_DEV_REMOTE;
+ if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict,
+ src, dst, key_aid, seq, iv_idx, out, key)) {
+
+ /* If candidate dev_key worked, it is considered finalized */
+ node_finalize_candidate(node);
+ return APP_IDX_DEV_LOCAL;
+ }

return -1;
}
diff --git a/mesh/node.c b/mesh/node.c
index cf4ed140e..5150a085a 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -27,9 +27,11 @@
#include "mesh/appkey.h"
#include "mesh/mesh-config.h"
#include "mesh/provision.h"
+#include "mesh/prov.h"
#include "mesh/keyring.h"
#include "mesh/model.h"
#include "mesh/cfgmod.h"
+#include "mesh/remprv.h"
#include "mesh/util.h"
#include "mesh/error.h"
#include "mesh/dbus.h"
@@ -347,6 +349,15 @@ static bool add_elements_from_storage(struct mesh_node *node,
if (!add_element_from_storage(node, entry->data))
return false;

+ /* Add configuration server model on the primary element */
+ mesh_model_add(node, PRIMARY_ELE_IDX, CONFIG_SRV_MODEL, NULL);
+
+ /* Add remote provisioning models on the primary element */
+ mesh_model_add(node, PRIMARY_ELE_IDX, REM_PROV_SRV_MODEL, NULL);
+
+ if (node->provisioner)
+ mesh_model_add(node, PRIMARY_ELE_IDX, REM_PROV_CLI_MODEL, NULL);
+
return true;
}

@@ -489,6 +500,10 @@ static bool init_from_storage(struct mesh_config_node *db_node,
/* Initialize configuration server model */
cfgmod_server_init(node, PRIMARY_ELE_IDX);

+ /* Initialize remote provisioning models */
+ remote_prov_server_init(node, PRIMARY_ELE_IDX);
+ remote_prov_client_init(node, PRIMARY_ELE_IDX);
+
node->cfg = cfg;

return true;
@@ -550,12 +565,78 @@ uint16_t node_get_primary(struct mesh_node *node)
return node->primary;
}

+bool node_refresh(struct mesh_node *node, bool hard, void *prov_info)
+{
+ struct mesh_prov_node_info *info = prov_info;
+ bool res = true;
+
+ if (!node || !info)
+ return false;
+
+ if (!IS_UNICAST(info->unicast))
+ return false;
+
+ /* Changing Unicast addresses requires a hard node reset */
+ if (!hard && info->unicast != node->primary)
+ return false;
+
+ /*
+ * Hard refresh results in immediate use of new Device Key.
+ * Soft refresh saves new device key as Candidate until we
+ * successfully receive new incoming message on that key.
+ */
+ if (hard) {
+ if (!mesh_config_write_device_key(node->cfg, info->device_key))
+ return false;
+
+ memcpy(node->dev_key, info->device_key, sizeof(node->dev_key));
+
+ } else if (!mesh_config_write_candidate(node->cfg, info->device_key))
+ return false;
+
+ /* Replace Primary Unicast address if it has changed */
+ if (node->primary != info->unicast) {
+ res = mesh_config_write_unicast(node->cfg, info->unicast);
+ if (res) {
+ node->primary = info->unicast;
+ node->num_ele = info->num_ele;
+ mesh_net_register_unicast(node->net, node->primary,
+ node->num_ele);
+ }
+ }
+
+ /* Replace Page 0 with Page 128 if it exists */
+ if (res) {
+ if (node_replace_comp(node, 0, 128))
+ return true;
+ }
+
+ return res;
+}
+
const uint8_t *node_get_device_key(struct mesh_node *node)
{
if (!node)
return NULL;
- else
- return node->dev_key;
+
+ return node->dev_key;
+}
+
+bool node_get_device_key_candidate(struct mesh_node *node, uint8_t *key)
+{
+ if (!node)
+ return false;
+
+ return mesh_config_read_candidate(node->cfg, key);
+}
+
+void node_finalize_candidate(struct mesh_node *node)
+{
+ if (!node)
+ return;
+
+ if (mesh_config_read_candidate(node->cfg, node->dev_key))
+ mesh_config_finalize_candidate(node->cfg);
}

void node_set_token(struct mesh_node *node, uint8_t token[8])
@@ -785,7 +866,7 @@ uint8_t node_friend_mode_get(struct mesh_node *node)
return node->friend;
}

-static uint16_t generate_node_comp(struct mesh_node *node, uint8_t *buf,
+static uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf,
uint16_t sz)
{
uint16_t n, features, num_ele = 0;
@@ -895,6 +976,21 @@ static void convert_node_to_storage(struct mesh_node *node,

}

+static void free_db_storage(struct mesh_config_node *db_node)
+{
+ const struct l_queue_entry *entry;
+
+ /* Free temporarily allocated resources */
+ entry = l_queue_get_entries(db_node->elements);
+ for (; entry; entry = entry->next) {
+ struct mesh_config_element *db_ele = entry->data;
+
+ l_queue_destroy(db_ele->models, l_free);
+ }
+
+ l_queue_destroy(db_node->elements, l_free);
+}
+
static bool create_node_config(struct mesh_node *node, const uint8_t uuid[16])
{
struct mesh_config_node db_node;
@@ -922,7 +1018,22 @@ static bool create_node_config(struct mesh_node *node, const uint8_t uuid[16])
return node->cfg != NULL;
}

-static bool set_node_comp(struct mesh_node *node, uint8_t page_num,
+static void node_del_comp(struct mesh_node *node, uint8_t page_num)
+{
+ struct mesh_config_comp_page *page;
+
+ if (!node)
+ return;
+
+ page = l_queue_remove_if(node->pages, match_page,
+ L_UINT_TO_PTR(page_num));
+
+ l_free(page);
+
+ mesh_config_comp_page_del(node->cfg, page_num);
+}
+
+static bool node_set_comp(struct mesh_node *node, uint8_t page_num,
const uint8_t *data, uint16_t len)
{
struct mesh_config_comp_page *page;
@@ -944,16 +1055,6 @@ static bool set_node_comp(struct mesh_node *node, uint8_t page_num,
return mesh_config_comp_page_add(node->cfg, page_num, page->data, len);
}

-static bool create_node_comp(struct mesh_node *node)
-{
- uint16_t len;
- uint8_t comp[MAX_MSG_LEN - 2];
-
- len = generate_node_comp(node, comp, sizeof(comp));
-
- return set_node_comp(node, 0, comp, len);
-}
-
const uint8_t *node_get_comp(struct mesh_node *node, uint8_t page_num,
uint16_t *len)
{
@@ -975,6 +1076,7 @@ const uint8_t *node_get_comp(struct mesh_node *node, uint8_t page_num,
bool node_replace_comp(struct mesh_node *node, uint8_t retire, uint8_t with)
{
struct mesh_config_comp_page *old_page, *keep;
+ bool status;

if (!node)
return false;
@@ -989,9 +1091,13 @@ bool node_replace_comp(struct mesh_node *node, uint8_t retire, uint8_t with)

l_free(old_page);
keep->page_num = retire;
- mesh_config_comp_page_mv(node->cfg, with, retire);
+ status = mesh_config_comp_page_add(node->cfg, keep->page_num,
+ keep->data, keep->len);

- return true;
+ if (with != retire)
+ mesh_config_comp_page_del(node->cfg, with);
+
+ return status;
}

static void attach_io(void *a, void *b)
@@ -1170,8 +1276,13 @@ static bool get_element_properties(struct mesh_node *node, const char *path,
* daemon. If the model is present in the application properties,
* the operation below will be a "no-op".
*/
- if (ele->idx == PRIMARY_ELE_IDX)
+ if (ele->idx == PRIMARY_ELE_IDX) {
mesh_model_add(node, ele->models, CONFIG_SRV_MODEL, NULL);
+ mesh_model_add(node, ele->models, REM_PROV_SRV_MODEL, NULL);
+ if (node->provisioner)
+ mesh_model_add(node, ele->models, REM_PROV_CLI_MODEL,
+ NULL);
+ }

return true;
fail:
@@ -1232,6 +1343,15 @@ static bool get_app_properties(struct mesh_node *node, const char *path,
return true;
}

+static void save_pages(void *data, void *user_data)
+{
+ struct mesh_config_comp_page *page = data;
+ struct mesh_node *node = user_data;
+
+ mesh_config_comp_page_add(node->cfg, page->page_num, page->data,
+ page->len);
+}
+
static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr,
bool ivu, uint32_t iv_idx, uint8_t dev_key[16],
uint16_t net_key_idx, uint8_t net_key[16])
@@ -1275,10 +1395,14 @@ static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr,
return false;
}

+ l_queue_foreach(node->pages, save_pages, node);
+
update_net_settings(node);

- /* Initialize configuration server model */
+ /* Initialize internal server models */
cfgmod_server_init(node, PRIMARY_ELE_IDX);
+ remote_prov_server_init(node, PRIMARY_ELE_IDX);
+ remote_prov_client_init(node, PRIMARY_ELE_IDX);

node->busy = true;

@@ -1326,39 +1450,59 @@ static void update_model_options(struct mesh_node *node,

static bool check_req_node(struct managed_obj_request *req)
{
+ struct mesh_node *node;
const int offset = 8;
uint16_t node_len, len;
uint8_t comp[MAX_MSG_LEN - 2];
const uint8_t *node_comp;

- len = generate_node_comp(req->node, comp, sizeof(comp));
+ if (req->type != REQUEST_TYPE_ATTACH) {
+ node = req->node;

- if (len < MIN_COMP_SIZE)
- return false;
+ if (!create_node_config(node, node->uuid))
+ return false;
+ } else
+ node = req->attach;

- node_comp = node_get_comp(req->attach, 0, &node_len);
+ node_comp = node_get_comp(node, 0, &node_len);
+ len = node_generate_comp(req->node, comp, sizeof(comp));

- /* If no page 0 exists, create it and accept */
- if (!node_len || !node_comp)
- return set_node_comp(req->attach, 0, comp, len);
+ /* If no page 0 exists, then current composition as valid */
+ if (req->type != REQUEST_TYPE_ATTACH || !node_len)
+ goto page_zero_valid;

- /* Test Element/Model part of composition and reject if changed */
+ /*
+ * If composition has materially changed, save new composition
+ * in page 128 until next NPPI procedure. But we do allow
+ * for CID, PID, VID and/or CRPL to freely change without
+ * requiring a NPPI procedure.
+ */
if (node_len != len || memcmp(&node_comp[offset], &comp[offset],
node_len - offset))
- return false;
+ return node_set_comp(node, 128, comp, len);

- /* If comp has changed, but not Element/Models, resave and accept */
- else if (memcmp(node_comp, comp, node_len))
- return set_node_comp(req->attach, 0, comp, len);
+page_zero_valid:
+ /* If page 0 represents current App, ensure page 128 doesn't exist */
+ node_del_comp(node, 128);

- /* Nothing has changed */
- return true;
+ if (len == node_len && !memcmp(node_comp, comp, len))
+ return true;
+
+ return node_set_comp(node, 0, comp, len);
+}
+
+static bool is_zero(const void *a, const void *b)
+{
+ const struct node_element *element = a;
+
+ return !element->idx;
}

static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node)
{
const struct l_queue_entry *attach_entry;
const struct l_queue_entry *node_entry;
+ bool comp_changed = false;

attach->obj_path = node->obj_path;
node->obj_path = NULL;
@@ -1368,6 +1512,34 @@ static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node)
return false;
}

+ if (attach->num_ele != node->num_ele) {
+ struct mesh_config_node db_node;
+ struct node_element *old_ele, *new_ele;
+
+ convert_node_to_storage(node, &db_node);
+
+ /*
+ * If composition has materially changed, we need to discard
+ * everything we knew about elements in the old application,
+ * and start from what they are telling us now.
+ */
+ old_ele = l_queue_remove_if(attach->elements, is_zero, NULL);
+ new_ele = l_queue_remove_if(node->elements, is_zero, NULL);
+ element_free(new_ele);
+
+ l_queue_destroy(attach->elements, element_free);
+ attach->elements = node->elements;
+ attach->num_ele = node->num_ele;
+
+ /* Restore primary elements */
+ l_queue_push_head(attach->elements, old_ele);
+
+ comp_changed = true;
+
+ mesh_config_reset(attach->cfg, &db_node);
+ free_db_storage(&db_node);
+ }
+
attach_entry = l_queue_get_entries(attach->elements);
node_entry = l_queue_get_entries(node->elements);

@@ -1384,6 +1556,10 @@ static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node)

attach_entry = attach_entry->next;
node_entry = node_entry->next;
+
+ /* Only need the Primary element during Composition change */
+ if (comp_changed)
+ break;
}

mesh_agent_remove(attach->agent);
@@ -1399,8 +1575,12 @@ static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node)
node->owner = NULL;

update_composition(node, attach);
+
update_model_options(node, attach);

+ if (comp_changed)
+ node->elements = NULL;
+
node_remove(node);

return true;
@@ -1499,16 +1679,7 @@ static void get_managed_objects_cb(struct l_dbus_message *msg, void *user_data)

node->num_ele = num_ele;

- if (req->type != REQUEST_TYPE_ATTACH) {
- /* Generate node configuration for a brand new node */
- if (!create_node_config(node, node->uuid))
- goto fail;
-
- /* Create node composition */
- if (!create_node_comp(node))
- goto fail;
- } else if (!check_req_node(req))
- /* Check the integrity of the node composition */
+ if (!check_req_node(req))
goto fail;

switch (req->type) {
diff --git a/mesh/node.h b/mesh/node.h
index 2e3d89812..a98945223 100644
--- a/mesh/node.h
+++ b/mesh/node.h
@@ -38,6 +38,8 @@ uint16_t node_get_primary_net_idx(struct mesh_node *node);
void node_set_token(struct mesh_node *node, uint8_t token[8]);
const uint8_t *node_get_token(struct mesh_node *node);
const uint8_t *node_get_device_key(struct mesh_node *node);
+bool node_get_device_key_candidate(struct mesh_node *node, uint8_t *key);
+void node_finalize_candidate(struct mesh_node *node);
void node_set_num_elements(struct mesh_node *node, uint8_t num_ele);
uint8_t node_get_num_elements(struct mesh_node *node);
uint8_t node_default_ttl_get(struct mesh_node *node);
@@ -89,3 +91,4 @@ const char *node_get_storage_dir(struct mesh_node *node);
bool node_load_from_storage(const char *storage_dir);
void node_finalize_new_node(struct mesh_node *node, struct mesh_io *io);
void node_property_changed(struct mesh_node *node, const char *property);
+bool node_refresh(struct mesh_node *node, bool hard, void *prov_info);
diff --git a/mesh/pb-adv.c b/mesh/pb-adv.c
index 180b16258..385d81d65 100644
--- a/mesh/pb-adv.c
+++ b/mesh/pb-adv.c
@@ -219,7 +219,7 @@ static void tx_timeout(struct l_timeout *timeout, void *user_data)
cb(user_data, 1);
}

-static void pb_adv_tx(void *user_data, void *data, uint16_t len)
+static void pb_adv_tx(void *user_data, const void *data, uint16_t len)
{
struct pb_adv_session *session = user_data;

@@ -478,7 +478,7 @@ static void pb_adv_packet(void *user_data, const uint8_t *pkt, uint16_t len)
bool pb_adv_reg(bool initiator, mesh_prov_open_func_t open_cb,
mesh_prov_close_func_t close_cb,
mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb,
- uint8_t uuid[16], void *user_data)
+ const uint8_t *uuid, void *user_data)
{
struct pb_adv_session *session, *old_session;

diff --git a/mesh/pb-adv.h b/mesh/pb-adv.h
index 5b1e03dae..e33ba8e35 100644
--- a/mesh/pb-adv.h
+++ b/mesh/pb-adv.h
@@ -11,5 +11,5 @@
bool pb_adv_reg(bool initiator, mesh_prov_open_func_t open_cb,
mesh_prov_close_func_t close_cb,
mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb,
- uint8_t uuid[16], void *user_data);
+ const uint8_t *uuid, void *user_data);
void pb_adv_unreg(void *user_data);
diff --git a/mesh/prov-acceptor.c b/mesh/prov-acceptor.c
index bf8c573da..fd9d4cd5d 100644
--- a/mesh/prov-acceptor.c
+++ b/mesh/prov-acceptor.c
@@ -22,6 +22,7 @@
#include "mesh/net.h"
#include "mesh/prov.h"
#include "mesh/provision.h"
+#include "mesh/remprv.h"
#include "mesh/pb-adv.h"
#include "mesh/mesh.h"
#include "mesh/agent.h"
@@ -169,9 +170,6 @@ static void acp_prov_open(void *user_data, prov_trans_tx_t trans_tx,
prov->transport != transport)
return;

- if (transport != PB_ADV)
- return;
-
prov->trans_tx = trans_tx;
prov->transport = transport;
prov->trans_data = trans_data;
@@ -425,9 +423,10 @@ static bool prov_start_check(struct prov_start *start,
return true;
}

-static void acp_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
+static void acp_prov_rx(void *user_data, const void *dptr, uint16_t len)
{
struct mesh_prov_acceptor *rx_prov = user_data;
+ const uint8_t *data = dptr;
struct mesh_prov_node_info *info;
struct prov_fail_msg fail;
uint8_t type = *data++;
@@ -654,14 +653,19 @@ static void acp_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
info->flags = prov->rand_auth_workspace[18];
info->iv_index = l_get_be32(prov->rand_auth_workspace + 19);
info->unicast = l_get_be16(prov->rand_auth_workspace + 23);
+ info->num_ele = prov->conf_inputs.caps.num_ele;
+
+ /* Send prov complete */
+ prov->rand_auth_workspace[0] = PROV_COMPLETE;
+ prov->trans_tx(prov->trans_data,
+ prov->rand_auth_workspace, 1);

result = prov->cmplt(prov->caller_data, PROV_ERR_SUCCESS, info);
prov->cmplt = NULL;
l_free(info);

if (result) {
- prov->rand_auth_workspace[0] = PROV_COMPLETE;
- prov_send(prov, prov->rand_auth_workspace, 1);
+ l_debug("PROV_COMPLETE");
goto cleanup;
} else {
fail.reason = PROV_ERR_UNEXPECTED_ERR;
@@ -721,7 +725,7 @@ static void acp_prov_ack(void *user_data, uint8_t msg_num)


/* This starts unprovisioned device beacon */
-bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],
+bool acceptor_start(uint8_t num_ele, uint8_t *uuid,
uint16_t algorithms, uint32_t timeout,
struct mesh_agent *agent,
mesh_prov_acceptor_complete_func_t complete_cb,
@@ -733,8 +737,10 @@ bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],
uint8_t len = sizeof(beacon) - sizeof(uint32_t);
bool result;

- /* Invoked from Join() method in mesh-api.txt, to join a
- * remote mesh network.
+ /*
+ * Invoked from Join() method in mesh-api.txt, to join a
+ * remote mesh network. May also be invoked with a NULL
+ * uuid to perform a Device Key Refresh procedure.
*/

if (prov)
@@ -752,37 +758,50 @@ bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],

caps = mesh_agent_get_caps(agent);

- /* TODO: Should we sanity check values here or elsewhere? */
prov->conf_inputs.caps.num_ele = num_ele;
- prov->conf_inputs.caps.pub_type = caps->pub_type;
- prov->conf_inputs.caps.static_type = caps->static_type;
- prov->conf_inputs.caps.output_size = caps->output_size;
- prov->conf_inputs.caps.input_size = caps->input_size;
-
- /* Store UINT16 values in Over-the-Air order, in packed structure
- * for crypto inputs
- */
l_put_be16(algorithms, &prov->conf_inputs.caps.algorithms);
- l_put_be16(caps->output_action, &prov->conf_inputs.caps.output_action);
- l_put_be16(caps->input_action, &prov->conf_inputs.caps.input_action);
-
- /* Compose Unprovisioned Beacon */
- memcpy(beacon + 2, uuid, 16);
- l_put_be16(caps->oob_info, beacon + 18);
- if (caps->oob_info & OOB_INFO_URI_HASH){
- l_put_be32(caps->uri_hash, beacon + 20);
- len += sizeof(uint32_t);
+
+ if (caps) {
+ /* TODO: Should we sanity check values here or elsewhere? */
+ prov->conf_inputs.caps.pub_type = caps->pub_type;
+ prov->conf_inputs.caps.static_type = caps->static_type;
+ prov->conf_inputs.caps.output_size = caps->output_size;
+ prov->conf_inputs.caps.input_size = caps->input_size;
+
+ /* Store UINT16 values in Over-the-Air order, in packed
+ * structure for crypto inputs
+ */
+ l_put_be16(caps->output_action,
+ &prov->conf_inputs.caps.output_action);
+ l_put_be16(caps->input_action,
+ &prov->conf_inputs.caps.input_action);
+
+ /* Populate Caps fields of beacon */
+ l_put_be16(caps->oob_info, beacon + 18);
+ if (caps->oob_info & OOB_INFO_URI_HASH) {
+ l_put_be32(caps->uri_hash, beacon + 20);
+ len += sizeof(uint32_t);
+ }
}

- /* Infinitely Beacon until Canceled, or Provisioning Starts */
- result = mesh_send_pkt(0, 500, beacon, len);
+ if (uuid) {
+ /* Compose Unprovisioned Beacon */
+ memcpy(beacon + 2, uuid, 16);
+
+ /* Infinitely Beacon until Canceled, or Provisioning Starts */
+ result = mesh_send_pkt(0, 500, beacon, len);

- if (!result)
- goto error_fail;
+ if (!result)
+ goto error_fail;

- /* Always register for PB-ADV */
- result = pb_adv_reg(false, acp_prov_open, acp_prov_close, acp_prov_rx,
- acp_prov_ack, uuid, prov);
+ /* Always register for PB-ADV */
+ result = pb_adv_reg(false, acp_prov_open, acp_prov_close,
+ acp_prov_rx, acp_prov_ack, uuid, prov);
+ } else {
+ /* Run Device Key Refresh Procedure */
+ result = register_nppi_acceptor(acp_prov_open, acp_prov_close,
+ acp_prov_rx, acp_prov_ack, prov);
+ }

if (result)
return true;
diff --git a/mesh/prov-initiator.c b/mesh/prov-initiator.c
index c62577523..653f3ae3e 100644
--- a/mesh/prov-initiator.c
+++ b/mesh/prov-initiator.c
@@ -21,10 +21,12 @@
#include "mesh/crypto.h"
#include "mesh/net.h"
#include "mesh/node.h"
+#include "mesh/model.h"
#include "mesh/keyring.h"
#include "mesh/prov.h"
#include "mesh/provision.h"
#include "mesh/pb-adv.h"
+#include "mesh/remprv.h"
#include "mesh/mesh.h"
#include "mesh/agent.h"
#include "mesh/error.h"
@@ -82,12 +84,16 @@ struct mesh_prov_initiator {
struct l_timeout *timeout;
uint32_t to_secs;
enum int_state state;
- enum trans_type transport;
uint16_t net_idx;
+ uint16_t svr_idx;
uint16_t unicast;
+ uint16_t server;
+ uint8_t transport;
uint8_t material;
uint8_t expected;
int8_t previous;
+ uint8_t out_num;
+ uint8_t rpr_state;
struct conf_input conf_inputs;
uint8_t calc_key[16];
uint8_t salt[16];
@@ -100,14 +106,23 @@ struct mesh_prov_initiator {
uint8_t uuid[16];
};

+struct scan_req {
+ mesh_prov_initiator_scan_result_t scan_result;
+ struct mesh_node *node;
+ int count;
+};
+
static struct mesh_prov_initiator *prov = NULL;
+static struct l_queue *scans;

static void initiator_free(void)
{
- if (prov)
+ if (prov) {
l_timeout_remove(prov->timeout);

- mesh_send_cancel(&pkt_filter, sizeof(pkt_filter));
+ if (!prov->server)
+ mesh_send_cancel(&pkt_filter, sizeof(pkt_filter));
+ }

pb_adv_unreg(prov);

@@ -119,6 +134,15 @@ static void int_prov_close(void *user_data, uint8_t reason)
{
struct mesh_prov_initiator *prov = user_data;
struct mesh_prov_node_info info;
+ uint8_t msg[4];
+ int n;
+
+ if (prov->server) {
+ n = mesh_model_opcode_set(OP_REM_PROV_LINK_CLOSE, msg);
+ msg[n++] = reason == PROV_ERR_SUCCESS ? 0x00 : 0x02;
+ mesh_model_send(prov->node, 0, prov->server, APP_IDX_DEV_REMOTE,
+ prov->svr_idx, DEFAULT_TTL, true, n, msg);
+ }

if (reason != PROV_ERR_SUCCESS) {
prov->complete_cb(prov->caller_data, reason, NULL);
@@ -626,9 +650,10 @@ static void int_prov_start_auth(const struct mesh_agent_prov_caps *prov_caps,
}
}

-static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
+static void int_prov_rx(void *user_data, const void *dptr, uint16_t len)
{
struct mesh_prov_initiator *rx_prov = user_data;
+ const uint8_t *data = dptr;
uint8_t *out;
uint8_t type = *data++;
uint8_t fail_code[2];
@@ -651,7 +676,7 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
if (type >= L_ARRAY_SIZE(expected_pdu_size) ||
len != expected_pdu_size[type]) {
l_error("Expected PDU size %d, Got %d (type: %2.2x)",
- len, expected_pdu_size[type], type);
+ expected_pdu_size[type], len, type);
fail_code[1] = PROV_ERR_INVALID_FORMAT;
goto failure;
}
@@ -773,7 +798,12 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
goto failure;
}

- if (!prov->data_req_cb(prov->caller_data,
+ if (prov->transport == PB_NPPI_00 ||
+ prov->transport == PB_NPPI_02) {
+ /* No App data needed */
+ initiator_prov_data(prov->svr_idx, prov->server,
+ prov->caller_data);
+ } else if (!prov->data_req_cb(prov->caller_data,
prov->conf_inputs.caps.num_ele)) {
l_error("Provisioning Failed-Data Get");
fail_code[1] = PROV_ERR_CANT_ASSIGN_ADDR;
@@ -851,6 +881,8 @@ static void int_prov_ack(void *user_data, uint8_t msg_num)

static void initiator_open_cb(void *user_data, int err)
{
+ uint8_t msg[20];
+ int n;
bool result;

if (!prov)
@@ -859,18 +891,30 @@ static void initiator_open_cb(void *user_data, int err)
if (err != MESH_ERROR_NONE)
goto fail;

- /* Always register for PB-ADV */
- result = pb_adv_reg(true, int_prov_open, int_prov_close, int_prov_rx,
- int_prov_ack, prov->uuid, prov);
+ if (prov->server) {
+ n = mesh_model_opcode_set(OP_REM_PROV_LINK_OPEN, msg);
+
+ if (prov->transport <= PB_NPPI_02) {
+ msg[n++] = prov->transport;
+ } else {
+ memcpy(msg + n, prov->uuid, 16);
+ n += 16;
+ }
+
+ result = mesh_model_send(prov->node, 0, prov->server,
+ APP_IDX_DEV_REMOTE, prov->svr_idx,
+ DEFAULT_TTL, true, n, msg);
+ } else {
+ /* Always register for PB-ADV */
+ result = pb_adv_reg(true, int_prov_open, int_prov_close,
+ int_prov_rx, int_prov_ack, prov->uuid, prov);
+ }

if (!result) {
err = MESH_ERROR_FAILED;
goto fail;
}

- if (!prov)
- return;
-
prov->start_cb(prov->caller_data, MESH_ERROR_NONE);
return;
fail:
@@ -878,10 +922,20 @@ fail:
initiator_free();
}

-bool initiator_start(enum trans_type transport,
- uint8_t uuid[16],
- uint16_t max_ele,
- uint32_t timeout, /* in seconds from mesh.conf */
+static void initiate_to(struct l_timeout *timeout, void *user_data)
+{
+ struct mesh_prov_initiator *rx_prov = user_data;
+
+ if (rx_prov != prov) {
+ l_timeout_remove(timeout);
+ return;
+ }
+
+ int_prov_close(user_data, PROV_ERR_TIMEOUT);
+}
+
+bool initiator_start(uint8_t transport, uint16_t server, uint16_t svr_idx,
+ uint8_t uuid[16], uint16_t max_ele, uint32_t timeout,
struct mesh_agent *agent,
mesh_prov_initiator_start_func_t start_cb,
mesh_prov_initiator_data_req_func_t data_req_cb,
@@ -904,6 +958,10 @@ bool initiator_start(enum trans_type transport,
prov->data_req_cb = data_req_cb;
prov->caller_data = caller_data;
prov->previous = -1;
+ prov->server = server;
+ prov->svr_idx = svr_idx;
+ prov->transport = transport;
+ prov->timeout = l_timeout_create(timeout, initiate_to, prov, NULL);
memcpy(prov->uuid, uuid, 16);

mesh_agent_refresh(prov->agent, initiator_open_cb, prov);
@@ -915,3 +973,182 @@ void initiator_cancel(void *user_data)
{
initiator_free();
}
+
+static void rpr_tx(void *user_data, const void *data, uint16_t len)
+{
+ struct mesh_prov_initiator *prov = user_data;
+ uint8_t msg[72];
+ int n;
+
+ n = mesh_model_opcode_set(OP_REM_PROV_PDU_SEND, msg);
+ msg[n++] = ++prov->out_num;
+ memcpy(msg + n, data, len);
+ l_debug("Send OB %2.2x, with packet type %d", msg[n], prov->out_num);
+ n += len;
+
+ prov->rpr_state = PB_REMOTE_STATE_OB_PKT_TX;
+ mesh_model_send(prov->node, 0, prov->server, APP_IDX_DEV_REMOTE,
+ prov->svr_idx, DEFAULT_TTL, true, n, msg);
+}
+
+static bool match_req_node(const void *a, const void *b)
+{
+ const struct scan_req *req = a;
+ const struct mesh_node *node = b;
+
+ return req->node == node;
+}
+
+static bool remprv_cli_pkt(uint16_t src, uint16_t unicast, uint16_t app_idx,
+ uint16_t net_idx, const uint8_t *data,
+ uint16_t size, const void *user_data)
+{
+ struct mesh_node *node = (struct mesh_node *) user_data;
+ const uint8_t *pkt = data;
+ struct scan_req *req;
+ uint32_t opcode;
+ uint16_t n;
+
+ if (mesh_model_opcode_get(pkt, size, &opcode, &n)) {
+ size -= n;
+ pkt += n;
+ } else
+ return false;
+
+ if (opcode < OP_REM_PROV_SCAN_CAP_GET ||
+ opcode > OP_REM_PROV_PDU_REPORT)
+ return false;
+
+ if (app_idx != APP_IDX_DEV_REMOTE && app_idx != APP_IDX_DEV_LOCAL)
+ return true;
+
+ /* Local Dev key only allowed for Loop-backs */
+ if (app_idx == APP_IDX_DEV_LOCAL && unicast != src)
+ return true;
+
+ if (prov && (prov->server != src || prov->node != node))
+ return true;
+
+ n = 0;
+
+ switch (opcode) {
+ default:
+ return false;
+
+ /* Provisioning Opcodes */
+ case OP_REM_PROV_LINK_STATUS:
+ if (size != 2 || !prov)
+ break;
+
+ if (pkt[0] == PB_REM_ERR_SUCCESS)
+ prov->rpr_state = pkt[1];
+
+ break;
+
+ case OP_REM_PROV_LINK_REPORT:
+ if (size != 2 || !prov)
+ return true;
+
+ if (pkt[0] != PB_REM_ERR_SUCCESS) {
+ if (pkt[0] == PB_REM_ERR_CLOSED_BY_DEVICE ||
+ pkt[0] == PB_REM_ERR_CLOSED_BY_SERVER)
+ int_prov_close(prov, pkt[1]);
+
+ break;
+ }
+
+
+ if (prov->rpr_state == PB_REMOTE_STATE_LINK_OPENING)
+ int_prov_open(prov, rpr_tx, prov, prov->transport);
+ else if (prov->rpr_state == PB_REMOTE_STATE_LINK_CLOSING) {
+ prov->rpr_state = PB_REMOTE_STATE_IDLE;
+ int_prov_close(prov, pkt[1]);
+ break;
+ }
+
+ prov->rpr_state = pkt[1];
+
+ break;
+
+ case OP_REM_PROV_PDU_REPORT:
+ int_prov_rx(prov, pkt + 1, size - 1);
+ break;
+
+ case OP_REM_PROV_PDU_OB_REPORT:
+ if (size != 1 || !prov)
+ break;
+
+ l_debug("Got Ack for OB %d", pkt[0]);
+ if (prov->rpr_state == PB_REMOTE_STATE_OB_PKT_TX &&
+ pkt[0] == prov->out_num)
+ int_prov_ack(prov, pkt[0]);
+
+ break;
+
+ /* Scan Opcodes */
+ case OP_REM_PROV_SCAN_CAP_STATUS:
+ case OP_REM_PROV_SCAN_STATUS:
+ break;
+
+ case OP_REM_PROV_SCAN_REPORT:
+ case OP_REM_PROV_EXT_SCAN_REPORT:
+ req = l_queue_find(scans, match_req_node, node);
+ if (req) {
+ req->scan_result(node, src,
+ opcode == OP_REM_PROV_EXT_SCAN_REPORT,
+ pkt, size);
+ }
+ }
+
+ return true;
+}
+
+void initiator_scan_reg(mesh_prov_initiator_scan_result_t scan_result,
+ void *user_data)
+{
+ struct scan_req *req;
+
+ if (!scans)
+ scans = l_queue_new();
+
+ req = l_queue_find(scans, match_req_node, user_data);
+ if (!req) {
+ req = l_new(struct scan_req, 1);
+ l_queue_push_head(scans, req);
+ }
+
+ req->scan_result = scan_result;
+ req->node = user_data;
+ req->count++;
+}
+
+void initiator_scan_unreg(void *user_data)
+{
+ struct scan_req *req;
+
+ req = l_queue_find(scans, match_req_node, user_data);
+ if (req) {
+ req->count--;
+ if (!req->count) {
+ l_queue_remove(scans, req);
+ l_free(req);
+ }
+ }
+}
+
+static void remprv_cli_unregister(void *user_data)
+{
+}
+
+static const struct mesh_model_ops ops = {
+ .unregister = remprv_cli_unregister,
+ .recv = remprv_cli_pkt,
+ .bind = NULL,
+ .sub = NULL,
+ .pub = NULL
+};
+
+void remote_prov_client_init(struct mesh_node *node, uint8_t ele_idx)
+{
+ mesh_model_register(node, ele_idx, REM_PROV_CLI_MODEL, &ops, node);
+}
diff --git a/mesh/prov.h b/mesh/prov.h
index 99e864c50..e86668fe4 100644
--- a/mesh/prov.h
+++ b/mesh/prov.h
@@ -39,14 +39,14 @@ enum mesh_prov_mode {

struct mesh_prov;

-typedef void (*prov_trans_tx_t)(void *trans_data, void *data, uint16_t len);
+typedef void (*prov_trans_tx_t)(void *tx_data, const void *data, uint16_t len);
typedef void (*mesh_prov_open_func_t)(void *user_data, prov_trans_tx_t trans_tx,
void *trans_data, uint8_t trans_type);

typedef void (*mesh_prov_close_func_t)(void *user_data, uint8_t reason);
typedef void (*mesh_prov_send_func_t)(bool success, struct mesh_prov *prov);
typedef void (*mesh_prov_ack_func_t)(void *user_data, uint8_t msg_num);
-typedef void (*mesh_prov_receive_func_t)(void *user_data, const uint8_t *data,
+typedef void (*mesh_prov_receive_func_t)(void *user_data, const void *data,
uint16_t size);


diff --git a/mesh/provision.h b/mesh/provision.h
index 1634c4d40..cfeb6deba 100644
--- a/mesh/provision.h
+++ b/mesh/provision.h
@@ -70,10 +70,11 @@ struct mesh_agent;
#define OOB_INFO_URI_HASH 0x0002

/* PB_REMOTE not supported from unprovisioned state */
-enum trans_type {
- PB_ADV = 0,
- PB_GATT,
-};
+#define PB_NPPI_00 0x00
+#define PB_NPPI_01 0x01
+#define PB_NPPI_02 0x02
+#define PB_ADV 0x03 /* Internal only, and may be reassigned */
+#define PB_GATT 0x04 /* Internal only, and may be reassigned */

#define PROV_FLAG_KR 0x01
#define PROV_FLAG_IVU 0x02
@@ -101,15 +102,21 @@ typedef bool (*mesh_prov_initiator_complete_func_t)(void *user_data,
uint8_t status,
struct mesh_prov_node_info *info);

+typedef void (*mesh_prov_initiator_scan_result_t)(void *user_data,
+ uint16_t server, bool extended,
+ const uint8_t *data, uint16_t len);
+
/* This starts unprovisioned device beacon */
-bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],
+bool acceptor_start(uint8_t num_ele, uint8_t *uuid,
uint16_t algorithms, uint32_t timeout,
struct mesh_agent *agent,
mesh_prov_acceptor_complete_func_t complete_cb,
void *caller_data);
void acceptor_cancel(void *user_data);

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


2023-01-24 20:26:59

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 11/13] mesh: Switch beaconing net key

From: Brian Gix <[email protected]>

When transitioning from Phase 1 to Phase 2 of a network key and we are
beaconing, we need to halt the beaconing on the old key version, and
begin beaconing on the new key version.
---
mesh/net.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/mesh/net.c b/mesh/net.c
index 81f1e57ee..05ca48326 100644
--- a/mesh/net.c
+++ b/mesh/net.c
@@ -2625,6 +2625,13 @@ static int key_refresh_phase_two(struct mesh_net *net, uint16_t idx)
if (subnet->kr_phase == KEY_REFRESH_PHASE_TWO)
return MESH_STATUS_SUCCESS;

+ /* Stop beaconing on old key */
+ if (net->snb_enable)
+ net_key_beacon_disable(subnet->net_key_tx, false);
+
+ if (net->mpb_enable)
+ net_key_beacon_disable(subnet->net_key_tx, true);
+
subnet->key_refresh = 1;
subnet->net_key_tx = subnet->net_key_upd;
/*
@@ -2632,6 +2639,15 @@ static int key_refresh_phase_two(struct mesh_net *net, uint16_t idx)
* it hears beacons from all the nodes
*/
subnet->kr_phase = KEY_REFRESH_PHASE_TWO;
+
+ /* Start beaconing on new key */
+ if (net->snb_enable)
+ net_key_beacon_enable(subnet->net_key_tx, false, 0);
+
+ if (net->mpb_enable)
+ net_key_beacon_enable(subnet->net_key_tx, true,
+ net->mpb_period);
+
refresh_beacon(subnet, net);
queue_friend_update(net);

--
2.39.1


2023-01-24 20:27:00

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 12/13] mesh: Fix Checksmatch warning

---
mesh/crypto.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mesh/crypto.c b/mesh/crypto.c
index 3754cb012..38dfc5fb5 100644
--- a/mesh/crypto.c
+++ b/mesh/crypto.c
@@ -1020,7 +1020,7 @@ static const uint8_t crypto_test_result[] = {
0x9a, 0x2a, 0xbf, 0x96
};

-bool mesh_crypto_check_avail()
+bool mesh_crypto_check_avail(void)
{
void *cipher;
bool result;
--
2.39.1


2023-01-24 20:27:01

by Brian Gix

[permalink] [raw]
Subject: [PATCH BlueZ v4 08/13] mesh: Add Tx/Rx support of Mesh Private Beacons

From: Brian Gix <[email protected]>

With this change, we start evaluating received Mesh Private Beacons in
addition to the legacy Secure Network Beacons. We also add the ability
to request Tx of Mesh Private Beacons, which are regenerated with new
Random Nonce a minimum of every 0 - 2550 seconds.
---
mesh/net-keys.c | 501 +++++++++++++++++++++++++++++++++++++-----------
mesh/net-keys.h | 10 +-
mesh/net.c | 172 ++++++++++++-----
mesh/net.h | 6 +-
mesh/node.c | 4 +-
5 files changed, 519 insertions(+), 174 deletions(-)

diff --git a/mesh/net-keys.c b/mesh/net-keys.c
index ee7bbf0c0..0ba051d79 100644
--- a/mesh/net-keys.c
+++ b/mesh/net-keys.c
@@ -21,39 +21,55 @@
#include "mesh/net.h"
#include "mesh/net-keys.h"

-#define BEACON_TYPE_SNB 0x01
-#define KEY_REFRESH 0x01
-#define IV_INDEX_UPDATE 0x02
-
#define BEACON_INTERVAL_MIN 10
#define BEACON_INTERVAL_MAX 600

-struct net_beacon {
+/* This allows daemon to skip decryption on recently seen beacons */
+#define BEACON_CACHE_MAX 10
+
+struct beacon_rx {
+ uint8_t data[28];
+ uint32_t id;
+ uint32_t ivi;
+ bool kr;
+ bool ivu;
+};
+
+struct beacon_observe {
struct l_timeout *timeout;
uint32_t ts;
- uint16_t observe_period;
- uint16_t observed;
+ uint16_t period;
+ uint16_t seen;
uint16_t expected;
bool half_period;
- uint8_t beacon[23];
};

struct net_key {
uint32_t id;
- struct net_beacon snb;
+ struct l_timeout *mpb_to;
+ uint8_t *mpb;
+ uint8_t *snb;
+ struct beacon_observe observe;
+ uint32_t ivi;
uint16_t ref_cnt;
- uint16_t beacon_enables;
+ uint16_t mpb_enables;
+ uint16_t snb_enables;
+ uint8_t mpb_refresh;
uint8_t friend_key;
uint8_t nid;
uint8_t flooding[16];
- uint8_t encrypt[16];
- uint8_t privacy[16];
- uint8_t beacon[16];
- uint8_t network[8];
+ uint8_t enc_key[16];
+ uint8_t prv_key[16];
+ uint8_t snb_key[16];
+ uint8_t pvt_key[16];
+ uint8_t net_id[8];
+ bool kr;
+ bool ivu;
};

-static struct l_queue *keys = NULL;
-static uint32_t last_flooding_id = 0;
+static struct l_queue *beacons;
+static struct l_queue *keys;
+static uint32_t last_flooding_id;

/* To avoid re-decrypting same packet for multiple nodes, cache and check */
static uint8_t cache_pkt[29];
@@ -81,9 +97,9 @@ static bool match_id(const void *a, const void *b)
static bool match_network(const void *a, const void *b)
{
const struct net_key *key = a;
- const uint8_t *network = b;
+ const uint8_t *net_id = b;

- return memcmp(key->network, network, sizeof(key->network)) == 0;
+ return memcmp(key->net_id, net_id, sizeof(key->net_id)) == 0;
}

/* Key added from Provisioning, NetKey Add or NetKey update */
@@ -101,19 +117,26 @@ uint32_t net_key_add(const uint8_t flooding[16])
if (!keys)
keys = l_queue_new();

+ if (!beacons)
+ beacons = l_queue_new();
+
key = l_new(struct net_key, 1);
memcpy(key->flooding, flooding, 16);
key->ref_cnt++;
- result = mesh_crypto_k2(flooding, p, sizeof(p), &key->nid, key->encrypt,
- key->privacy);
+ result = mesh_crypto_k2(flooding, p, sizeof(p), &key->nid, key->enc_key,
+ key->prv_key);
if (!result)
goto fail;

- result = mesh_crypto_k3(flooding, key->network);
+ result = mesh_crypto_k3(flooding, key->net_id);
if (!result)
goto fail;

- result = mesh_crypto_nkbk(flooding, key->beacon);
+ result = mesh_crypto_nkbk(flooding, key->snb_key);
+ if (!result)
+ goto fail;
+
+ result = mesh_crypto_nkpk(flooding, key->pvt_key);
if (!result)
goto fail;

@@ -146,7 +169,7 @@ uint32_t net_key_frnd_add(uint32_t flooding_id, uint16_t lpn, uint16_t frnd,
l_put_be16(fn_cnt, p + 7);

result = mesh_crypto_k2(key->flooding, p, sizeof(p), &frnd_key->nid,
- frnd_key->encrypt, frnd_key->privacy);
+ frnd_key->enc_key, frnd_key->prv_key);

if (!result) {
l_free(frnd_key);
@@ -167,7 +190,7 @@ void net_key_unref(uint32_t id)

if (key && key->ref_cnt) {
if (--key->ref_cnt == 0) {
- l_timeout_remove(key->snb.timeout);
+ l_timeout_remove(key->observe.timeout);
l_queue_remove(keys, key);
l_free(key);
}
@@ -206,7 +229,7 @@ static void decrypt_net_pkt(void *a, void *b)

result = mesh_crypto_packet_decode(cache_pkt, cache_len, false,
cache_plain, cache_iv_index,
- key->encrypt, key->privacy);
+ key->enc_key, key->prv_key);

if (result) {
cache_id = key->id;
@@ -254,8 +277,8 @@ bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len)
if (!key)
return false;

- result = mesh_crypto_packet_encode(pkt, len, iv_index, key->encrypt,
- key->privacy);
+ result = mesh_crypto_packet_encode(pkt, len, iv_index, key->enc_key,
+ key->prv_key);

if (!result)
return false;
@@ -265,9 +288,9 @@ bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len)
return result;
}

-uint32_t net_key_network_id(const uint8_t network[8])
+uint32_t net_key_network_id(const uint8_t net_id[8])
{
- struct net_key *key = l_queue_find(keys, match_network, network);
+ struct net_key *key = l_queue_find(keys, match_network, net_id);

if (!key)
return 0;
@@ -275,6 +298,55 @@ uint32_t net_key_network_id(const uint8_t network[8])
return key->id;
}

+struct auth_check {
+ const uint8_t *data;
+ uint32_t id;
+ uint32_t ivi;
+ bool ivu;
+ bool kr;
+};
+
+static void check_auth(void *a, void *b)
+{
+ struct net_key *key = a;
+ struct auth_check *auth = b;
+ uint8_t out[5];
+
+
+ /* Stop checking if already found */
+ if (auth->id)
+ return;
+
+ if (mesh_crypto_aes_ccm_decrypt(auth->data + 1, key->pvt_key, NULL, 0,
+ auth->data + 14, 13,
+ out, NULL, 8)) {
+ auth->id = key->id;
+ auth->ivi = l_get_be32(out + 1);
+ auth->ivu = !!(out[0] & 0x02);
+ auth->kr = !!(out[0] & 0x01);
+ }
+}
+
+static uint32_t private_beacon_check(const void *beacon, uint32_t *ivi,
+ bool *ivu, bool *kr)
+{
+ struct auth_check auth = {
+ .data = beacon,
+ .id = 0,
+ };
+
+ auth.id = 0;
+ l_queue_foreach(keys, check_auth, &auth);
+
+ if (auth.id) {
+ *ivi = auth.ivi;
+ *ivu = auth.ivu;
+ *kr = auth.kr;
+ }
+
+ return auth.id;
+}
+
bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
uint64_t cmac)
{
@@ -285,7 +357,7 @@ bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
return false;

/* Any behavioral changes must pass CMAC test */
- if (!mesh_crypto_beacon_cmac(key->beacon, key->network, iv_index, kr,
+ if (!mesh_crypto_beacon_cmac(key->snb_key, key->net_id, iv_index, kr,
ivu, &cmac_check)) {
l_error("mesh_crypto_beacon_cmac failed");
return false;
@@ -300,39 +372,142 @@ bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
return true;
}

-bool net_key_snb_compose(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
- uint8_t *snb)
+static bool mpb_compose(struct net_key *key, uint32_t ivi, bool kr, bool ivu)
+{
+ uint8_t b_data[5 + 8];
+ uint8_t random[13];
+
+ if (!key)
+ return false;
+
+ b_data[0] = 0;
+ l_put_be32(ivi, b_data + 1);
+
+ if (kr)
+ b_data[0] |= KEY_REFRESH;
+
+ if (ivu)
+ b_data[0] |= IV_INDEX_UPDATE;
+
+ l_getrandom(random, sizeof(random));
+ if (!mesh_crypto_aes_ccm_encrypt(random, key->pvt_key, NULL, 0,
+ b_data, 5, b_data, NULL, 8))
+ return false;
+
+ key->mpb[0] = MESH_AD_TYPE_BEACON;
+ key->mpb[1] = BEACON_TYPE_MPB;
+ memcpy(key->mpb + 2, random, 13);
+ memcpy(key->mpb + 15, b_data, 13);
+
+ return true;
+}
+
+static bool snb_compose(struct net_key *key, uint32_t ivi, bool kr, bool ivu)
{
- struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
uint64_t cmac;

if (!key)
return false;

/* Any behavioral changes must pass CMAC test */
- if (!mesh_crypto_beacon_cmac(key->beacon, key->network, iv_index, kr,
+ if (!mesh_crypto_beacon_cmac(key->snb_key, key->net_id, ivi, kr,
ivu, &cmac)) {
l_error("mesh_crypto_beacon_cmac failed");
return false;
}

- snb[0] = MESH_AD_TYPE_BEACON;
- snb[1] = BEACON_TYPE_SNB;
- snb[2] = 0;
+ key->snb[0] = MESH_AD_TYPE_BEACON;
+ key->snb[1] = BEACON_TYPE_SNB;
+ key->snb[2] = 0;

if (kr)
- snb[2] |= KEY_REFRESH;
+ key->snb[2] |= KEY_REFRESH;

if (ivu)
- snb[2] |= IV_INDEX_UPDATE;
+ key->snb[2] |= IV_INDEX_UPDATE;

- memcpy(snb + 3, key->network, 8);
- l_put_be32(iv_index, snb + 11);
- l_put_be64(cmac, snb + 15);
+ memcpy(key->snb + 3, key->net_id, 8);
+ l_put_be32(ivi, key->snb + 11);
+ l_put_be64(cmac, key->snb + 15);

return true;
}

+static bool match_beacon(const void *a, const void *b)
+{
+ const struct beacon_rx *cached = a;
+ const uint8_t *incoming = b;
+
+ if (incoming[0] == BEACON_TYPE_MPB)
+ return !memcmp(cached->data, incoming, 27);
+
+ if (incoming[0] == BEACON_TYPE_SNB)
+ return !memcmp(cached->data, incoming, 22);
+
+ return false;
+}
+
+uint32_t net_key_beacon(const uint8_t *data, uint16_t len, uint32_t *ivi,
+ bool *ivu, bool *kr)
+{
+ struct net_key *key;
+ struct beacon_rx *beacon;
+ uint32_t b_id, b_ivi;
+ bool b_ivu, b_kr;
+
+ if (data[1] == BEACON_TYPE_SNB && len != 23)
+ return 0;
+
+ if (data[1] == BEACON_TYPE_MPB && len != 28)
+ return 0;
+
+ beacon = l_queue_remove_if(beacons, match_beacon, data + 1);
+
+ if (beacon)
+ goto accept;
+
+ /* Validate beacon data */
+ if (data[1] == BEACON_TYPE_SNB) {
+ key = l_queue_find(keys, match_network, data + 3);
+
+ if (!key)
+ return 0;
+
+ b_id = key->id;
+ b_ivu = !!(data[2] & 0x02);
+ b_kr = !!(data[2] & 0x01);
+ b_ivi = l_get_be32(data + 11);
+
+ if (!net_key_snb_check(b_id, b_ivi, b_kr, b_ivu,
+ l_get_be64(data + 15)))
+ return 0;
+
+ } else if (data[1] == BEACON_TYPE_MPB) {
+ b_id = private_beacon_check(data + 1, &b_ivi, &b_ivu, &b_kr);
+
+ if (!b_id)
+ return 0;
+
+ } else
+ return 0;
+
+ beacon = l_new(struct beacon_rx, 1);
+ memcpy(beacon->data, data + 1, len - 1);
+ beacon->id = b_id;
+ beacon->ivi = b_ivi;
+ beacon->ivu = b_ivu;
+ beacon->kr = b_kr;
+
+accept:
+ *ivi = beacon->ivi;
+ *ivu = beacon->ivu;
+ *kr = beacon->kr;
+
+ l_queue_push_head(beacons, beacon);
+
+ return beacon->id;
+}
+
static void send_network_beacon(struct net_key *key)
{
struct mesh_io_send_info info = {
@@ -343,10 +518,26 @@ static void send_network_beacon(struct net_key *key)
.u.gen.max_delay = DEFAULT_MAX_DELAY
};

- mesh_io_send(NULL, &info, key->snb.beacon, sizeof(key->snb.beacon));
+ if (key->mpb_enables) {
+ /* If Interval steps == 0, refresh key every time */
+ if (!key->mpb_refresh || !key->mpb || !key->mpb[0])
+ net_key_beacon_refresh(key->id, key->ivi, key->kr,
+ key->ivu, true);
+
+ mesh_io_send(NULL, &info, key->mpb, 28);
+ }
+
+ if (key->snb_enables) {
+ if (!key->snb || !key->snb[0]) {
+ net_key_beacon_refresh(key->id, key->ivi, key->kr,
+ key->ivu, true);
+ }
+
+ mesh_io_send(NULL, &info, key->snb, 23);
+ }
}

-static void snb_timeout(struct l_timeout *timeout, void *user_data)
+static void beacon_timeout(struct l_timeout *timeout, void *user_data)
{
struct net_key *key = user_data;
uint32_t interval, scale_factor;
@@ -355,29 +546,29 @@ static void snb_timeout(struct l_timeout *timeout, void *user_data)
send_network_beacon(key);

/* Count our own beacons towards the vicinity total */
- key->snb.observed++;
+ key->observe.seen++;

- if (!key->snb.half_period) {
+ if (!key->observe.half_period) {

l_debug("beacon %d for %d nodes, period %d, obs %d, exp %d",
- key->id,
- key->beacon_enables,
- key->snb.observe_period,
- key->snb.observed,
- key->snb.expected);
+ key->id,
+ key->snb_enables + key->mpb_enables,
+ key->observe.period,
+ key->observe.seen,
+ key->observe.expected);


- interval = (key->snb.observe_period * key->snb.observed)
- / key->snb.expected;
+ interval = (key->observe.period * key->observe.seen)
+ / key->observe.expected;

/* Limit Increases and Decreases by 10 seconds Up and
* 20 seconds down each step, to avoid going nearly silent
* in highly populated environments.
*/
- if (interval - 10 > key->snb.observe_period)
- interval = key->snb.observe_period + 10;
- else if (interval + 20 < key->snb.observe_period)
- interval = key->snb.observe_period - 20;
+ if (interval - 10 > key->observe.period)
+ interval = key->observe.period + 10;
+ else if (interval + 20 < key->observe.period)
+ interval = key->observe.period - 20;

/* Beaconing must be no *slower* than once every 10 minutes,
* and no *faster* than once every 10 seconds, per spec.
@@ -388,26 +579,26 @@ static void snb_timeout(struct l_timeout *timeout, void *user_data)
else if (interval > BEACON_INTERVAL_MAX * 2)
interval = BEACON_INTERVAL_MAX * 2;

- key->snb.observe_period = interval;
- key->snb.observed = 0;
+ key->observe.period = interval;
+ key->observe.seen = 0;

/* To prevent "over slowing" of the beaconing frequency,
* require more significant "over observing" the slower
* our own beaconing frequency.
*/
- key->snb.expected = interval / 10;
+ key->observe.expected = interval / 10;
scale_factor = interval / 60;
- key->snb.expected += scale_factor * 3;
+ key->observe.expected += scale_factor * 3;
}

- interval = key->snb.observe_period / 2;
- key->snb.half_period = !key->snb.half_period;
+ interval = key->observe.period / 2;
+ key->observe.half_period = !key->observe.half_period;

- if (key->beacon_enables)
+ if (key->mpb_enables || key->snb_enables)
l_timeout_modify(timeout, interval);
else {
l_timeout_remove(timeout);
- key->snb.timeout = NULL;
+ key->observe.timeout = NULL;
}
}

@@ -416,8 +607,8 @@ void net_key_beacon_seen(uint32_t id)
struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));

if (key) {
- key->snb.observed++;
- key->snb.ts = get_timestamp_secs();
+ key->observe.seen++;
+ key->observe.ts = get_timestamp_secs();
}
}

@@ -426,12 +617,83 @@ uint32_t net_key_beacon_last_seen(uint32_t id)
struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));

if (key)
- return key->snb.ts;
+ return key->observe.ts;

return 0;
}

-void net_key_beacon_enable(uint32_t id)
+bool net_key_beacon_refresh(uint32_t id, uint32_t ivi, bool kr, bool ivu,
+ bool force)
+{
+ struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
+ bool refresh = force;
+ uint32_t rand_ms;
+
+ if (!key)
+ return false;
+
+ if (key->snb_enables && !key->snb) {
+ key->snb = l_new(uint8_t, 23);
+ refresh = true;
+ }
+
+ if (key->mpb_enables && !key->mpb) {
+ key->mpb = l_new(uint8_t, 28);
+ refresh = true;
+ }
+
+ if (key->ivi != ivi || key->ivu != ivu || key->kr != kr)
+ refresh = true;
+
+ if (!refresh)
+ return true;
+
+ if (key->mpb) {
+ if (!mpb_compose(key, ivi, kr, ivu))
+ return false;
+
+ print_packet("Set MPB to", key->mpb, 28);
+ }
+
+ if (key->snb) {
+ if (!snb_compose(key, ivi, kr, ivu))
+ return false;
+
+ print_packet("Set SNB to", key->snb, 23);
+ }
+
+ l_debug("Set Beacon: IVI: %8.8x, IVU: %d, KR: %d", ivi, ivu, kr);
+
+ /* Propagate changes to all local nodes */
+ net_local_beacon(id, ivi, ivu, kr);
+
+ /* Send one new SNB soon, after all nodes have seen it */
+ l_getrandom(&rand_ms, sizeof(rand_ms));
+ rand_ms %= 1000;
+ key->observe.expected++;
+
+ if (key->observe.timeout)
+ l_timeout_modify_ms(key->observe.timeout, 500 + rand_ms);
+ else
+ key->observe.timeout = l_timeout_create_ms(500 + rand_ms,
+ beacon_timeout, key, NULL);
+
+ return true;
+}
+
+static void mpb_timeout(struct l_timeout *timeout, void *user_data)
+{
+ struct net_key *key = user_data;
+
+ if (key->mpb_refresh) {
+ l_debug("Refresh in %d seconds", key->mpb_refresh * 10);
+ l_timeout_modify(timeout, key->mpb_refresh * 10);
+ }
+
+ net_key_beacon_refresh(key->id, key->ivi, key->kr, key->ivu, true);
+}
+
+void net_key_beacon_enable(uint32_t id, bool mpb, uint8_t refresh_count)
{
struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
bool enabled;
@@ -440,8 +702,19 @@ void net_key_beacon_enable(uint32_t id)
if (!key)
return;

- enabled = !!key->beacon_enables;
- key->beacon_enables++;
+ enabled = !!key->snb_enables || !!key->mpb_enables;
+
+ if (mpb) {
+ key->mpb_enables++;
+ key->mpb_refresh = refresh_count;
+ l_timeout_remove(key->mpb_to);
+ if (refresh_count)
+ key->mpb_to = l_timeout_create(refresh_count * 10,
+ mpb_timeout, key, NULL);
+ else
+ key->mpb_to = NULL;
+ } else
+ key->snb_enables++;

/* If already Enabled, do nothing */
if (enabled)
@@ -453,70 +726,68 @@ void net_key_beacon_enable(uint32_t id)
rand_ms++;

/* Enable Periodic Beaconing on this key */
- key->snb.observe_period = BEACON_INTERVAL_MIN * 2;
- key->snb.expected = 2;
- key->snb.observed = 0;
- key->snb.half_period = true;
- l_timeout_remove(key->snb.timeout);
- key->snb.timeout = l_timeout_create_ms(rand_ms, snb_timeout, key, NULL);
+ key->observe.period = BEACON_INTERVAL_MIN * 2;
+ key->observe.expected = 2;
+ key->observe.seen = 0;
+ key->observe.half_period = true;
+ l_timeout_remove(key->observe.timeout);
+ key->observe.timeout = l_timeout_create_ms(rand_ms, beacon_timeout,
+ key, NULL);
}

-bool net_key_beacon_refresh(uint32_t id, uint32_t iv_index, bool kr, bool ivu)
+void net_key_beacon_disable(uint32_t id, bool mpb)
{
struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
- uint8_t beacon[23];
- uint32_t rand_ms;

if (!key)
- return false;
+ return;

- if (!net_key_snb_compose(id, iv_index, kr, ivu, beacon))
- return false;
+ if (mpb) {
+ if (!key->mpb_enables)
+ return;

- if (memcmp(key->snb.beacon, beacon, sizeof(beacon)))
- memcpy(key->snb.beacon, beacon, sizeof(beacon));
- else
- return false;
+ key->mpb_enables--;

- l_debug("Setting SNB: IVI: %8.8x, IVU: %d, KR: %d", iv_index, ivu, kr);
- print_packet("Set SNB Beacon to", beacon, sizeof(beacon));
+ if (!key->mpb_enables) {
+ l_free(key->mpb);
+ key->mpb = NULL;
+ l_timeout_remove(key->mpb_to);
+ key->mpb_to = NULL;
+ }
+ } else {
+ if (!key->snb_enables)
+ return;

- /* Propagate changes to all local nodes */
- net_local_beacon(id, beacon);
+ key->snb_enables--;

- /* Send one new SNB soon, after all nodes have seen it */
- l_getrandom(&rand_ms, sizeof(rand_ms));
- rand_ms %= 1000;
- key->snb.expected++;
+ if (!key->snb_enables) {
+ l_free(key->snb);
+ key->snb = NULL;
+ }
+ }

- if (key->snb.timeout)
- l_timeout_modify_ms(key->snb.timeout, 500 + rand_ms);
- else
- key->snb.timeout = l_timeout_create_ms(500 + rand_ms,
- snb_timeout, key, NULL);
+ if (key->snb_enables || key->mpb_enables)
+ return;

- return true;
+ /* Disable periodic Beaconing on this key */
+ l_timeout_remove(key->observe.timeout);
+ key->observe.timeout = NULL;
}

-void net_key_beacon_disable(uint32_t id)
+static void free_key(void *data)
{
- struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
-
- if (!key || !key->beacon_enables)
- return;
-
- key->beacon_enables--;
+ struct net_key *key = data;

- if (key->beacon_enables)
- return;
-
- /* Disable periodic Beaconing on this key */
- l_timeout_remove(key->snb.timeout);
- key->snb.timeout = NULL;
+ l_timeout_remove(key->mpb_to);
+ l_free(key->snb);
+ l_free(key->mpb);
+ l_free(key);
}

void net_key_cleanup(void)
{
- l_queue_destroy(keys, l_free);
+ l_queue_destroy(keys, free_key);
keys = NULL;
+ l_queue_destroy(beacons, l_free);
+ beacons = NULL;
}
diff --git a/mesh/net-keys.h b/mesh/net-keys.h
index 420618f71..a3909448c 100644
--- a/mesh/net-keys.h
+++ b/mesh/net-keys.h
@@ -9,6 +9,7 @@
*/

#define BEACON_TYPE_SNB 0x01
+#define BEACON_TYPE_MPB 0x02
#define KEY_REFRESH 0x01
#define IV_INDEX_UPDATE 0x02

@@ -23,12 +24,15 @@ uint32_t net_key_decrypt(uint32_t iv_index, const uint8_t *pkt, size_t len,
uint8_t **plain, size_t *plain_len);
bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len);
uint32_t net_key_network_id(const uint8_t network[8]);
+uint32_t net_key_beacon(const uint8_t *data, uint16_t len, uint32_t *ivi,
+ bool *ivu, bool *kr);
bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
uint64_t cmac);
bool net_key_snb_compose(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
uint8_t *snb);
void net_key_beacon_seen(uint32_t id);
-void net_key_beacon_enable(uint32_t id);
-bool net_key_beacon_refresh(uint32_t id, uint32_t iv_index, bool kr, bool ivu);
-void net_key_beacon_disable(uint32_t id);
+bool net_key_beacon_refresh(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
+ bool force);
+void net_key_beacon_enable(uint32_t id, bool mpb, uint8_t refresh_count);
+void net_key_beacon_disable(uint32_t id, bool mpb);
uint32_t net_key_beacon_last_seen(uint32_t id);
diff --git a/mesh/net.c b/mesh/net.c
index 1d27289bf..81f1e57ee 100644
--- a/mesh/net.c
+++ b/mesh/net.c
@@ -102,7 +102,8 @@ struct mesh_net {
unsigned int sar_id_next;

bool friend_enable;
- bool beacon_enable;
+ bool snb_enable;
+ bool mpb_enable;
bool proxy_enable;
bool friend_seq;
struct l_timeout *iv_update_timeout;
@@ -119,6 +120,7 @@ struct mesh_net {
uint8_t chan; /* Channel of recent Rx */
uint8_t default_ttl;
uint8_t tid;
+ uint8_t mpb_period;

struct {
bool enable;
@@ -217,6 +219,7 @@ struct net_beacon_data {
bool ivu;
bool kr;
bool processed;
+ bool local;
};

static struct l_queue *fast_cache;
@@ -526,6 +529,13 @@ static void mesh_sar_free(void *data)
static void subnet_free(void *data)
{
struct mesh_subnet *subnet = data;
+ struct mesh_net *net = subnet->net;
+
+ if (net->snb_enable)
+ net_key_beacon_disable(subnet->net_key_tx, false);
+
+ if (net->mpb_enable)
+ net_key_beacon_disable(subnet->net_key_tx, true);

net_key_unref(subnet->net_key_cur);
net_key_unref(subnet->net_key_upd);
@@ -545,15 +555,27 @@ static struct mesh_subnet *subnet_new(struct mesh_net *net, uint16_t idx)
return subnet;
}

-static void enable_beacon(void *a, void *b)
+static void enable_snb(void *a, void *b)
{
struct mesh_subnet *subnet = a;
struct mesh_net *net = b;

- if (net->beacon_enable)
- net_key_beacon_enable(subnet->net_key_tx);
+ if (net->snb_enable)
+ net_key_beacon_enable(subnet->net_key_tx, false, 0);
else
- net_key_beacon_disable(subnet->net_key_tx);
+ net_key_beacon_disable(subnet->net_key_tx, false);
+}
+
+static void enable_mpb(void *a, void *b)
+{
+ struct mesh_subnet *subnet = a;
+ struct mesh_net *net = b;
+
+ if (net->mpb_enable)
+ net_key_beacon_enable(subnet->net_key_tx, true,
+ net->mpb_period);
+ else
+ net_key_beacon_disable(subnet->net_key_tx, true);
}

static void enqueue_update(void *a, void *b);
@@ -602,7 +624,8 @@ static void refresh_beacon(void *a, void *b)
struct mesh_net *net = b;

net_key_beacon_refresh(subnet->net_key_tx, net->iv_index,
- !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update);
+ !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update,
+ false);
}

struct mesh_net *mesh_net_new(struct mesh_node *node)
@@ -826,7 +849,7 @@ int mesh_net_del_key(struct mesh_net *net, uint16_t idx)
if (idx == net->hb_pub.net_idx)
net->hb_pub.dst = UNASSIGNED_ADDRESS;

- /* TODO: cancel beacon_enable on this subnet */
+ /* TODO: cancel snb_enable on this subnet */

l_queue_remove(net->subnets, subnet);
subnet_free(subnet);
@@ -853,10 +876,14 @@ static struct mesh_subnet *add_key(struct mesh_net *net, uint16_t idx,
}

net_key_beacon_refresh(subnet->net_key_tx, net->iv_index,
- false, net->iv_update);
+ false, net->iv_update, false);

- if (net->beacon_enable)
- net_key_beacon_enable(subnet->net_key_tx);
+ if (net->snb_enable)
+ net_key_beacon_enable(subnet->net_key_tx, false, 0);
+
+ if (net->mpb_enable)
+ net_key_beacon_enable(subnet->net_key_tx, true,
+ net->mpb_period);

l_queue_push_tail(net->subnets, subnet);

@@ -2794,83 +2821,111 @@ static void process_beacon(void *net_ptr, void *user_data)
beacon_data->processed = true;

/*
- * Ignore the beacon if it doesn't change anything, unless we're
- * doing IV Recovery
+ * Ignore local beacons and beacons that don't change anything,
+ * unless we're doing IV Recovery
*/
- if (net->iv_upd_state == IV_UPD_INIT || ivi != net->iv_index ||
+ if (!beacon_data->local) {
+ if (net->iv_upd_state == IV_UPD_INIT || ivi != net->iv_index ||
ivu != net->iv_update)
- updated |= update_iv_ivu_state(net, ivi, ivu);
+ updated |= update_iv_ivu_state(net, ivi, ivu);
+
+ if (kr != local_kr)
+ updated |= update_kr_state(subnet, kr,
+ beacon_data->net_key_id);

- if (kr != local_kr || beacon_data->net_key_id != subnet->net_key_cur)
- updated |= update_kr_state(subnet, kr, beacon_data->net_key_id);

- if (updated)
- net_key_beacon_refresh(subnet->net_key_tx, net->iv_index,
+ if (updated)
+ net_key_beacon_refresh(beacon_data->net_key_id,
+ net->iv_index,
!!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO),
- net->iv_update);
+ net->iv_update, false);
+ }
}

static void beacon_recv(void *user_data, struct mesh_io_recv_info *info,
const uint8_t *data, uint16_t len)
{
struct net_beacon_data beacon_data = {
+ .local = false,
.processed = false,
};

- if (len != 23 || data[1] != 0x01)
- return;
+ beacon_data.net_key_id = net_key_beacon(data, len, &beacon_data.ivi,
+ &beacon_data.ivu, &beacon_data.kr);

- /* Ignore Network IDs unknown to this daemon */
- beacon_data.net_key_id = net_key_network_id(data + 3);
if (!beacon_data.net_key_id)
return;

- /* Get data bits from beacon */
- beacon_data.ivu = !!(data[2] & 0x02);
- beacon_data.kr = !!(data[2] & 0x01);
- beacon_data.ivi = l_get_be32(data + 11);
-
- /* Validate beacon before accepting */
- if (!net_key_snb_check(beacon_data.net_key_id, beacon_data.ivi,
- beacon_data.kr, beacon_data.ivu,
- l_get_be64(data + 15))) {
- l_error("mesh_crypto_beacon verify failed");
- return;
- }
-
l_queue_foreach(nets, process_beacon, &beacon_data);

if (beacon_data.processed)
net_key_beacon_seen(beacon_data.net_key_id);
}

-void net_local_beacon(uint32_t net_key_id, uint8_t *beacon)
+void net_local_beacon(uint32_t net_key_id, uint32_t ivi, bool ivu, bool kr)
{
struct net_beacon_data beacon_data = {
+ .local = true,
+ .processed = false,
.net_key_id = net_key_id,
- .ivu = !!(beacon[2] & 0x02),
- .kr = !!(beacon[2] & 0x01),
- .ivi = l_get_be32(beacon + 11),
+ .ivu = ivu,
+ .kr = kr,
+ .ivi = ivi,
};

/* Deliver locally generated beacons to all nodes */
l_queue_foreach(nets, process_beacon, &beacon_data);
}

-bool mesh_net_set_beacon_mode(struct mesh_net *net, bool enable)
+bool mesh_net_set_snb_mode(struct mesh_net *net, bool enable)
{
if (!net)
return false;

- if (net->beacon_enable == enable)
+ if (net->snb_enable == enable)
return true;

- net->beacon_enable = enable;
+ net->snb_enable = enable;

if (enable)
l_queue_foreach(net->subnets, refresh_beacon, net);

- l_queue_foreach(net->subnets, enable_beacon, net);
+ l_queue_foreach(net->subnets, enable_snb, net);
+ queue_friend_update(net);
+
+ return true;
+}
+
+bool mesh_net_set_mpb_mode(struct mesh_net *net, bool enable, uint8_t period,
+ bool initialize)
+{
+ uint8_t old_period;
+ bool old_enable;
+
+ if (!net)
+ return false;
+
+ old_enable = net->mpb_enable;
+ old_period = net->mpb_period;
+
+ if (enable)
+ net->mpb_period = period;
+
+ if (old_enable == enable && old_period == net->mpb_period)
+ return true;
+
+ if (enable && !initialize) {
+ /* If enable with different period, disable and re-enable */
+ net->mpb_enable = false;
+ l_queue_foreach(net->subnets, enable_mpb, net);
+ }
+
+ net->mpb_enable = enable;
+
+ if (enable)
+ l_queue_foreach(net->subnets, refresh_beacon, net);
+
+ l_queue_foreach(net->subnets, enable_mpb, net);
queue_friend_update(net);

return true;
@@ -2908,17 +2963,25 @@ bool mesh_net_set_key(struct mesh_net *net, uint16_t idx, const uint8_t *key,
subnet->key_refresh = 1;
subnet->net_key_tx = subnet->net_key_upd;

- if (net->beacon_enable) {
+ if (net->snb_enable) {
/* Switch beaconing key */
- net_key_beacon_disable(subnet->net_key_cur);
- net_key_beacon_enable(subnet->net_key_upd);
+ net_key_beacon_disable(subnet->net_key_cur, false);
+ net_key_beacon_enable(subnet->net_key_upd, false, 0);
+ }
+
+ if (net->mpb_enable) {
+ /* Switch beaconing key */
+ net_key_beacon_disable(subnet->net_key_cur, true);
+ net_key_beacon_enable(subnet->net_key_upd, true,
+ net->mpb_period);
}
}

subnet->kr_phase = phase;

net_key_beacon_refresh(subnet->net_key_tx, net->iv_index,
- !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update);
+ !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update,
+ false);


return true;
@@ -2933,8 +2996,9 @@ bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io)

first = l_queue_isempty(nets);
if (first) {
- uint8_t snb[] = {MESH_AD_TYPE_BEACON, 0x01};
- uint8_t pkt[] = {MESH_AD_TYPE_NETWORK};
+ const uint8_t snb[] = {MESH_AD_TYPE_BEACON, 1};
+ const uint8_t mpb[] = {MESH_AD_TYPE_BEACON, 2};
+ const uint8_t pkt[] = {MESH_AD_TYPE_NETWORK};

if (!nets)
nets = l_queue_new();
@@ -2944,6 +3008,8 @@ bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io)

mesh_io_register_recv_cb(io, snb, sizeof(snb),
beacon_recv, NULL);
+ mesh_io_register_recv_cb(io, mpb, sizeof(mpb),
+ beacon_recv, NULL);
mesh_io_register_recv_cb(io, pkt, sizeof(pkt),
net_msg_recv, NULL);
}
@@ -2960,8 +3026,9 @@ bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io)

struct mesh_io *mesh_net_detach(struct mesh_net *net)
{
- uint8_t snb[] = {MESH_AD_TYPE_BEACON, 0x01};
- uint8_t pkt[] = {MESH_AD_TYPE_NETWORK};
+ const uint8_t snb[] = {MESH_AD_TYPE_BEACON, 1};
+ const uint8_t mpb[] = {MESH_AD_TYPE_BEACON, 2};
+ const uint8_t pkt[] = {MESH_AD_TYPE_NETWORK};
struct mesh_io *io;
uint8_t type = 0;

@@ -2975,6 +3042,7 @@ struct mesh_io *mesh_net_detach(struct mesh_net *net)
/* Only deregister io if this is the last network detached.*/
if (l_queue_length(nets) < 2) {
mesh_io_deregister_recv_cb(io, snb, sizeof(snb));
+ mesh_io_deregister_recv_cb(io, mpb, sizeof(mpb));
mesh_io_deregister_recv_cb(io, pkt, sizeof(pkt));
}

diff --git a/mesh/net.h b/mesh/net.h
index 0bacbbbbf..d385ba16e 100644
--- a/mesh/net.h
+++ b/mesh/net.h
@@ -236,8 +236,10 @@ void mesh_net_set_frnd_seq(struct mesh_net *net, bool seq);
uint16_t mesh_net_get_address(struct mesh_net *net);
bool mesh_net_register_unicast(struct mesh_net *net,
uint16_t unicast, uint8_t num_ele);
-void net_local_beacon(uint32_t net_key_id, uint8_t *beacon);
-bool mesh_net_set_beacon_mode(struct mesh_net *net, bool enable);
+void net_local_beacon(uint32_t key_id, uint32_t ivi, bool ivu, bool kr);
+bool mesh_net_set_snb_mode(struct mesh_net *net, bool enable);
+bool mesh_net_set_mpb_mode(struct mesh_net *net, bool enabla, uint8_t period,
+ bool init);
bool mesh_net_set_proxy_mode(struct mesh_net *net, bool enable);
bool mesh_net_set_relay_mode(struct mesh_net *net, bool enable, uint8_t cnt,
uint8_t interval);
diff --git a/mesh/node.c b/mesh/node.c
index 5150a085a..a2a330518 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -414,7 +414,7 @@ static void update_net_settings(struct mesh_node *node)
mesh_net_set_relay_mode(net, node->relay.mode == MESH_MODE_ENABLED,
node->relay.cnt, node->relay.interval);

- mesh_net_set_beacon_mode(net, node->beacon == MESH_MODE_ENABLED);
+ mesh_net_set_snb_mode(net, node->beacon == MESH_MODE_ENABLED);
}

static bool init_from_storage(struct mesh_config_node *db_node,
@@ -825,7 +825,7 @@ bool node_beacon_mode_set(struct mesh_node *node, bool enable)

if (res) {
node->beacon = beacon;
- mesh_net_set_beacon_mode(node->net, enable);
+ mesh_net_set_snb_mode(node->net, enable);
}

return res;
--
2.39.1


2023-01-24 21:53:07

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ v4 07/13] mesh: Add Mesh Private Beacon server

Hi Brian,

On Tue, Jan 24, 2023 at 12:28 PM Brian Gix <[email protected]> wrote:
>
> From: Brian Gix <[email protected]>
>
> This initial server supports only the Mesh Private Beacon and returns
> "Not Suppoerted" for Get/Set of Private GATT Proxy and Private Node
> Identity beacons.
> ---
> Makefile.mesh | 1 +
> mesh/prv-beacon.h | 36 +++++++++++++
> mesh/prvbeac-server.c | 123 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 160 insertions(+)
> create mode 100644 mesh/prv-beacon.h
> create mode 100644 mesh/prvbeac-server.c
>
> diff --git a/Makefile.mesh b/Makefile.mesh
> index e18a169eb..63f085de1 100644
> --- a/Makefile.mesh
> +++ b/Makefile.mesh
> @@ -36,6 +36,7 @@ mesh_sources = mesh/mesh.h mesh/mesh.c \
> mesh/pb-adv.h mesh/pb-adv.c \
> mesh/keyring.h mesh/keyring.c \
> mesh/rpl.h mesh/rpl.c \
> + mesh/prv-beacon.h mesh/prvbeac-server.c \

Have the .c follow the same naming convention e.g. prv-beacon.c, we
can actually work out the client vs server internally.

> mesh/mesh-defs.h
> pkglibexec_PROGRAMS += mesh/bluetooth-meshd
>
> diff --git a/mesh/prv-beacon.h b/mesh/prv-beacon.h
> new file mode 100644
> index 000000000..7be7a01c8
> --- /dev/null
> +++ b/mesh/prv-beacon.h
> @@ -0,0 +1,36 @@
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2020 Intel Corporation. All rights reserved.

This should probably be 2023

> + *
> + *
> + * 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.
> + *
> + */
> +
> +struct mesh_node;
> +
> +#define PRV_BEACON_SRV_MODEL SET_ID(SIG_VENDOR, 0x0008)
> +#define PRV_BEACON_CLI_MODEL SET_ID(SIG_VENDOR, 0x0009)
> +
> +/* Private Beacon opcodes */
> +#define OP_PRIVATE_BEACON_GET 0x8060
> +#define OP_PRIVATE_BEACON_SET 0x8061
> +#define OP_PRIVATE_BEACON_STATUS 0x8062
> +#define OP_PRIVATE_GATT_PROXY_GET 0x8063
> +#define OP_PRIVATE_GATT_PROXY_SET 0x8064
> +#define OP_PRIVATE_GATT_PROXY_STATUS 0x8065
> +#define OP_PRIVATE_NODE_ID_GET 0x8066
> +#define OP_PRIVATE_NODE_ID_SET 0x8067
> +#define OP_PRIVATE_NODE_ID_STATUS 0x8068
> +
> +void prv_beacon_server_init(struct mesh_node *node, uint8_t ele_idx);
> diff --git a/mesh/prvbeac-server.c b/mesh/prvbeac-server.c
> new file mode 100644
> index 000000000..78ab94eb3
> --- /dev/null
> +++ b/mesh/prvbeac-server.c
> @@ -0,0 +1,123 @@
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2020 Intel Corporation. All rights reserved.
> + *
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <sys/time.h>
> +#include <ell/ell.h>
> +
> +#include "mesh/mesh-defs.h"
> +#include "mesh/node.h"
> +#include "mesh/net.h"
> +#include "mesh/appkey.h"
> +#include "mesh/model.h"
> +#include "mesh/mesh-config.h"
> +#include "mesh/prv-beacon.h"
> +
> +#define NOT_SUPPORTED 0x02
> +
> +static bool prvbec_srv_pkt(uint16_t src, uint16_t dst, 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;
> + uint32_t opcode;
> + uint8_t msg[5];
> + uint16_t n;
> + uint8_t period = 0;
> +
> + 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;
> +
> + l_debug("PRV-BEAC-SRV-opcode 0x%x size %u idx %3.3x", opcode, size,
> + net_idx);
> +
> + n = 0;
> +
> + switch (opcode) {
> + default:
> + return false;
> +
> + case OP_PRIVATE_BEACON_SET:
> + if (size == 1)
> + period = 0xff;
> + else if (size == 2)
> + period = pkt[1];
> + else
> + return true;
> +
> + /* fall through */
> +
> + case OP_PRIVATE_BEACON_GET:
> + n = mesh_model_opcode_set(OP_PRIVATE_BEACON_STATUS, msg);
> +
> + msg[n++] = NOT_SUPPORTED;
> + msg[n++] = period;
> +
> + l_debug("Get/Set Private Beacon (%d)", msg[n-2]);
> + break;
> +
> + case OP_PRIVATE_GATT_PROXY_SET:
> + /* fall through */
> + case OP_PRIVATE_GATT_PROXY_GET:
> + n = mesh_model_opcode_set(OP_PRIVATE_GATT_PROXY_STATUS, msg);
> + msg[n++] = NOT_SUPPORTED;
> + break;
> +
> + case OP_PRIVATE_NODE_ID_SET:
> + /* fall through */
> + case OP_PRIVATE_NODE_ID_GET:
> + n = mesh_model_opcode_set(OP_PRIVATE_NODE_ID_STATUS, msg);
> + msg[n++] = NOT_SUPPORTED;
> + break;
> + }
> +
> + if (n)
> + mesh_model_send(node, dst, src, APP_IDX_DEV_LOCAL, net_idx,
> + DEFAULT_TTL, false, n, msg);
> +
> + return true;
> +}
> +
> +static void prvbec_srv_unregister(void *user_data)
> +{
> +}
> +
> +static const struct mesh_model_ops ops = {
> + .unregister = prvbec_srv_unregister,
> + .recv = prvbec_srv_pkt,
> + .bind = NULL,
> + .sub = NULL,
> + .pub = NULL
> +};
> +
> +void prv_beacon_server_init(struct mesh_node *node, uint8_t ele_idx)
> +{
> + l_debug("%2.2x", ele_idx);
> + mesh_model_register(node, ele_idx, PRV_BEACON_SRV_MODEL, &ops, node);
> +}
> --
> 2.39.1
>


--
Luiz Augusto von Dentz

2023-01-25 00:26:25

by bluez.test.bot

[permalink] [raw]
Subject: RE: Mesh v1.1 additions

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=715272

---Test result---

Test Summary:
CheckPatch FAIL 9.03 seconds
GitLint FAIL 3.65 seconds
BuildEll PASS 32.94 seconds
BluezMake PASS 997.15 seconds
MakeCheck PASS 13.09 seconds
MakeDistcheck PASS 180.02 seconds
CheckValgrind PASS 293.68 seconds
CheckSmatch PASS 388.11 seconds
bluezmakeextell PASS 117.27 seconds
IncrementalBuild PASS 10456.45 seconds
ScanBuild WARNING 1194.97 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ,v4,02/13] mesh: Add Remote Provisioning
WARNING:PREFER_FALLTHROUGH: Prefer 'fallthrough;' over fallthrough comment
#3307: FILE: mesh/remprv-server.c:238:
+ /* fall through */

WARNING:PREFER_FALLTHROUGH: Prefer 'fallthrough;' over fallthrough comment
#3516: FILE: mesh/remprv-server.c:447:
+ /* fall through */

WARNING:PREFER_FALLTHROUGH: Prefer 'fallthrough;' over fallthrough comment
#3520: FILE: mesh/remprv-server.c:451:
+ /* fall through */

WARNING:PREFER_FALLTHROUGH: Prefer 'fallthrough;' over fallthrough comment
#3798: FILE: mesh/remprv-server.c:729:
+ /* fall through */

/github/workspace/src/src/13114772.patch total: 0 errors, 4 warnings, 3742 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/src/13114772.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.


[BlueZ,v4,07/13] mesh: Add Mesh Private Beacon server
WARNING:PREFER_FALLTHROUGH: Prefer 'fallthrough;' over fallthrough comment
#228: FILE: mesh/prvbeac-server.c:74:
+ /* fall through */

WARNING:PREFER_FALLTHROUGH: Prefer 'fallthrough;' over fallthrough comment
#240: FILE: mesh/prvbeac-server.c:86:
+ /* fall through */

WARNING:PREFER_FALLTHROUGH: Prefer 'fallthrough;' over fallthrough comment
#247: FILE: mesh/prvbeac-server.c:93:
+ /* fall through */

/github/workspace/src/src/13114768.patch total: 0 errors, 3 warnings, 166 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/src/13114768.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,v4,01/13] doc/mesh: Add Remote Provisioning DBus APIs

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
11: B3 Line contains hard tab characters (\t): " * Refresh Device Keys"
12: B3 Line contains hard tab characters (\t): " * Reassign Node Addresses"
13: B3 Line contains hard tab characters (\t): " * Refresh Node Composition"
##############################
Test: ScanBuild - WARNING
Desc: Run Scan Build
Output:
In file included from unit/test-mesh-crypto.c:20:
In file included from ./mesh/crypto.c:18:
In file included from ./ell/ell.h:1:
./ell/util.h:187:9: warning: 1st function call argument is an uninitialized value
return L_BE32_TO_CPU(L_GET_UNALIGNED((const uint32_t *) ptr));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./ell/util.h:89:28: note: expanded from macro 'L_BE32_TO_CPU'
#define L_BE32_TO_CPU(val) bswap_32(val)
^~~~~~~~~~~~~
/usr/include/byteswap.h:34:21: note: expanded from macro 'bswap_32'
#define bswap_32(x) __bswap_32 (x)
^~~~~~~~~~~~~~
1 warning generated.



---
Regards,
Linux Bluetooth

2023-01-25 21:54:14

by Stotland, Inga

[permalink] [raw]
Subject: Re: [PATCH BlueZ v4 01/13] doc/mesh: Add Remote Provisioning DBus APIs

Hi Brian,

Just a few terminology comments to make t consistent with the spec.

On Tue, 2023-01-24 at 12:26 -0800, Brian Gix wrote:
> From: Brian Gix <[email protected]>
>
> Remote Provisioning (introduced in Mesh Profile Specification v1.1)
>
> * Allows Provisioners to use a remote server to scan for and
>   provision devices.
>
> * Allows Config managers to reprovision existing nodes to:
>         * Refresh Device Keys
>         * Reassign Node Addresses
>         * Refresh Node Composition
> ---
>  doc/mesh-api.txt | 140
> +++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 134 insertions(+), 6 deletions(-)
>
> diff --git a/doc/mesh-api.txt b/doc/mesh-api.txt
> index 85de6705e..393ae566f 100644
> --- a/doc/mesh-api.txt
> +++ b/doc/mesh-api.txt
> @@ -484,7 +484,28 @@ Methods:
>                         Specifies number of seconds for scanning to
> be active.
>                         If set to 0 or if this key is not present,
> then the
>                         scanning will continue until
> UnprovisionedScanCancel()
> -                       or AddNode() methods are called.
> +                       or AddNode() methods are called. If not
> present, and a
> +                       remote server is specified, the default
> timeout will be
> +                       60 seconds.
> +
> +               uint16 Server
> +                       Specifies a remote server on which to perform
> scanning.
> +                       If not present, scanning will be local. If
> present,
> +                       the timeout must be between 1 and 60 seconds.
> +
> +               uint16 Subnet
> +                       Specifies a subnet for the remote server. If
> not
> +                       present, primary subnet will be used. If
> Server not
> +                       present, the Subnet will be ignored.
> +
> +               array{byte}[16] Filter
> +                       Specifies a specific UUID to search for. If
> not
> +                       present, all found UUIDs will be returned.
> +
> +               uint8 array Extended
> +                       Specifies variable number of Bluetooth AD
> types to
> +                       return with scan result. Only valid if a
> Filter has been
> +                       specified.
>  
>                 Each time a unique unprovisioned beacon is heard, the
>                 ScanResult() method on the app will be called with
> the result.
> @@ -514,8 +535,48 @@ Methods:
>                 of the unprovisioned device to be added to the
> network.
>  
>                 The options parameter is a dictionary that may
> contain
> -               additional configuration info (currently an empty
> placeholder
> -               for forward compatibility).
> +               additional optional configuration info:
> +
> +               uint16 Server
> +                       Specifies a remote server to perform
> provisioning on. If
> +                       not present, provisioning will be done
> locally.
> +
> +               uint16 Subnet
> +                       Specifies a subnet for the remote server. If
> not
> +                       present, primary subnet will be used. If
> Server not
> +                       present Subnet will be ignored.
> +
> +               PossibleErrors:
> +                       org.bluez.mesh.Error.InvalidArguments
> +                       org.bluez.mesh.Error.NotAuthorized
> +
> +       void Reprovision(uint16 unicast, dict options)
> +
> +               This method is used by the application that supports
> +               org.bluez.mesh.Provisioner1 interface to perform one
> of the
> +               Node Provisioning Protocol Interface procedures with
> a remote
> +               node to refresh its device key, unicast address, and
> +               composition. Remote node being reprovisioned must
> have the
> +               Remote Provisioning Server model.
> +
> +               The unicast parameter is the 16-bit primary node
> address of
> +               the remote node being reprovisioned.
> +
> +               The options parameter is a dictionary that may
> contain
> +               additional optional configuration info:
> +
> +               uint8 NPPI
> +                       Specifies the Node Provisioning Protocol
> Interface
> +                       behavior, as defined in the Mesh Profile

ingas: "behavior" -> "procedure"

> Specification:
> +                               0 - Device Key Refresh Only
> +                               1 - Node Address Refresh
> +                               2 - Node Composition Refresh
> +
> +                       If not present, behavior 0 will be used.

ingas: "behavior" -> "procedure"

> +
> +               uint16 Subnet
> +                       Specifies the subnet remote node is on. If
> not
> +                       present, primary subnet will be tried.
>  
>                 PossibleErrors:
>                         org.bluez.mesh.Error.InvalidArguments
> @@ -1055,11 +1116,19 @@ Object path     freely definable
>                 byte remote device UUID (always), a 16 bit mask of
> OOB
>                 authentication flags (optional), and a 32 bit URI
> hash (if URI
>                 bit set in OOB mask). Whether these fields exist or
> not is a
> -               decision of the remote device.
> +               decision of the unprovisioned device.
>  
>                 The options parameter is a dictionary that may
> contain
> -               additional scan result info (currently an empty
> placeholder for
> -               forward compatibility).
> +               additional optional configuration info:
> +
> +               uint16 Server
> +                       Specifies the remote server that received the
> +                       Unprovisioned beacon. If not present, beacon
> was
> +                       received locally.
> +
> +               uint8 array ExtendedData
> +                       If Extended data was requested during
> scanning, any
> +                       received data will be returned here.
>  
>                 If a beacon with a UUID that has already been
> reported is
>                 recieved by the daemon, it will be silently discarded
> unless it
> @@ -1082,6 +1151,26 @@ Object path      freely definable
>                 PossibleErrors:
>                         org.bluez.mesh.Error.Abort
>  
> +       uint16 unicast RequestReprovData(uint16 original, uint8
> count)
> +
> +               This method is implemented by a Provisioner capable
> application
> +               and is called when the remote node being
> reprovisioned has been
> +               fully authenticated and confirmed. This method will
> only be
> +               called if the NPPI-1 procedure (Node Address Refresh)
> is being
> +               performed.
> +
> +               The original parameter is the current unicast address
> of the
> +               node being reprovisioned.
> +
> +               The count parameter is the number of consecutive
> unicast
> +               addresses the remote node is requesting.
> +
> +               Return Parameter:
> +               unicast - Primary Unicast address of the new node
> +
> +               PossibleErrors:
> +                       org.bluez.mesh.Error.Abort
> +
>         void AddNodeComplete(array{byte}[16] uuid, uint16 unicast,
> uint8 count)
>  
>                 This method is called when the node provisioning
> initiated
> @@ -1096,6 +1185,33 @@ Object path      freely definable
>                 The new node may now be sent messages using the
> credentials
>                 supplied by the RequestProvData method.
>  
> +       void ReprovComplete(uint16 original, uint8 nppi, uint16
> unicast,
> +                                                               uint8
> count)
> +
> +               This method is called when the node Reprovisioning
> initiated
> +               by a Reprovision() method call successfully
> completed.
> +
> +               The original parameter is the former primary address
> of the
> +               node that has been reprovisioned.
> +
> +               The nppi parameter indicates which NPPI behavior was
> performed.

ingas: "behavior" -> "procedure"

> +               If behavior 1 or 2 was performed, the node is

ingas: "behavior" -> "procedure"

> materially
> +               different than it was before reprovisioning, and
> Composition,
> +               Bindings, Publication and Subscription settings
> should be
> +               refreshed.
> +
> +               The unicast parameter is the new primary address that
> has been
> +               assigned to the node, If NPPI behavior 1 was

ingas: "," -> "."
"behavior" -> "procedure"

> performed this
> +               value may be different from the original. If behavior
> 0 or 2

ingas: "behavior" -> "procedure"

> +               was performed, the original and new primary address
> should be
> +               the same.
> +
> +               The count parameter is the number of unicast
> addresses assigned
> +               to the node.
> +
> +               The node may now be sent messages using the
> credentials
> +               supplied by the RequestReprovData method.
> +
>         void AddNodeFailed(array{byte}[16] uuid, string reason)
>  
>                 This method is called when the node provisioning
> initiated by
> @@ -1109,6 +1225,18 @@ Object path      freely definable
>                 "decryption-error", "unexpected-error",
>                 "cannot-assign-addresses".
>  
> +       void ReprovFailed(uint16 unicast, string reason)
> +
> +               This method is called when node reprovisioning
> initiated by
> +               Reprovision() has failed. If reprovisioning has
> failed, the
> +               prior credentials of the remote node may still be
> valid.
> +
> +               The reason parameter identifies the reason for
> provisioning
> +               failure. The defined values are: "aborted",
> "timeout",
> +               "bad-pdu", "confirmation-failed", "out-of-resources",
> +               "decryption-error", "unexpected-error",
> +               "cannot-assign-addresses".
> +
>  Provisioning Agent Hierarchy
>  ============================
>  Service                unique name

2023-01-25 22:27:19

by Stotland, Inga

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

Tiny fix and a question below

On Tue, 2023-01-24 at 12:26 -0800, Brian Gix wrote:
> From: Brian Gix <[email protected]>
>
> Add Remote Provisioning Server
> Add Remote Provisioning Client
> Remove local scanning/provisioning
> Add delete-all dev key function
> Add NPPI procedures
> ---
>  Makefile.mesh           |   1 +
>  mesh/cfgmod-server.c    |   2 +-
>  mesh/keyring.c          |  29 +-
>  mesh/keyring.h          |   1 +
>  mesh/manager.c          | 535 +++++++++++++++++++-----
>  mesh/mesh-config-json.c | 380 +++++++++++------
>  mesh/mesh-config.h      |   6 +-
>  mesh/model.c            |  27 +-
>  mesh/node.c             | 255 +++++++++--
>  mesh/node.h             |   3 +
>  mesh/pb-adv.c           |   4 +-
>  mesh/pb-adv.h           |   2 +-
>  mesh/prov-acceptor.c    |  87 ++--
>  mesh/prov-initiator.c   | 269 +++++++++++-
>  mesh/prov.h             |   4 +-
>  mesh/provision.h        |  23 +-
>  mesh/remprv-server.c    | 907
> ++++++++++++++++++++++++++++++++++++++++
>  mesh/remprv.h           |  78 ++++
>  18 files changed, 2246 insertions(+), 367 deletions(-)
>  create mode 100644 mesh/remprv-server.c
>  create mode 100644 mesh/remprv.h
>
> diff --git a/Makefile.mesh b/Makefile.mesh
> index 3047f362b..e18a169eb 100644
> --- a/Makefile.mesh
> +++ b/Makefile.mesh
> @@ -26,6 +26,7 @@ mesh_sources = mesh/mesh.h mesh/mesh.c \
>                                 mesh/provision.h mesh/prov.h \
>                                 mesh/model.h mesh/model.c \
>                                 mesh/cfgmod.h mesh/cfgmod-server.c \
> +                               mesh/remprv.h mesh/remprv-server.c \
>                                 mesh/mesh-config.h mesh/mesh-config-
> json.c \
>                                 mesh/util.h mesh/util.c \
>                                 mesh/dbus.h mesh/dbus.c \
> diff --git a/mesh/cfgmod-server.c b/mesh/cfgmod-server.c
> index be90ef8c5..3d7efc44b 100644
> --- a/mesh/cfgmod-server.c
> +++ b/mesh/cfgmod-server.c
> @@ -30,8 +30,8 @@
>                 (SET_ID(SIG_VENDOR, l_get_le16(pkt))))
>  
>  /* Supported composition pages, sorted high to low */
> -/* Only page 0 is currently supported */
>  static const uint8_t supported_pages[] = {
> +       128,
>         0
>  };
>  
> diff --git a/mesh/keyring.c b/mesh/keyring.c
> index 995a4b88f..894fb14fa 100644
> --- a/mesh/keyring.c
> +++ b/mesh/keyring.c
> @@ -30,9 +30,9 @@
>  #include "mesh/node.h"
>  #include "mesh/keyring.h"
>  
> -const char *dev_key_dir = "/dev_keys";
> -const char *app_key_dir = "/app_keys";
> -const char *net_key_dir = "/net_keys";
> +static const char *dev_key_dir = "/dev_keys";
> +static const char *app_key_dir = "/app_keys";
> +static const char *net_key_dir = "/net_keys";
>  
>  static int open_key_file(struct mesh_node *node, const char
> *key_dir,
>                                                         uint16_t idx,
> int flags)
> @@ -295,6 +295,7 @@ bool keyring_get_remote_dev_key(struct mesh_node
> *node, uint16_t unicast,
>                 close(fd);
>         }
>  
> +

ingas: Did you accidentally add an extra empty line?

>         return result;
>  }
>  
> @@ -371,6 +372,28 @@ bool keyring_del_remote_dev_key(struct mesh_node
> *node, uint16_t unicast,
>         return true;
>  }
>  
> +bool keyring_del_remote_dev_key_all(struct mesh_node *node, uint16_t
> unicast)
> +{
> +       uint8_t dev_key[16];
> +       uint8_t test_key[16];
> +       uint8_t cnt = 1;
> +
> +       if (!keyring_get_remote_dev_key(node, unicast, dev_key))
> +               return false;
> +
> +       while (keyring_get_remote_dev_key(node, unicast + cnt,
> test_key)) {
> +               if (memcmp(dev_key, test_key, sizeof(dev_key)))
> +                       break;
> +
> +               cnt++;
> +       }
> +
> +       if (cnt > 1)
> +               return keyring_del_remote_dev_key(node, unicast + 1,
> cnt - 1);
> +

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

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