All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ v2 00/11] Bluetooth Mesh Daemon
@ 2018-04-25 17:50 Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 01/11] meshd: Shared private meshd interfaces Brian Gix
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Brian Gix @ 2018-04-25 17:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Brian Gix

v2 of patch-set to address maximum patch size limits

This is our starting point for creating a stand-alone Mesh Daemon.

The Mesh Daemon (meshd) is intended to eventually co-exist beside the
Bluetooth Daemon (bluetoothd), and either daemon may exist without the
other. The Mesh Daemon will need full control/ownership of a
BT Controller that supports at least Core Spec revision 4.0 or
greater.  It works strictly over the Advertising bearer, but we
have plans to extend it to support Mesh Proxy Server over GATT, at
which point it will require support from bluetoothd.

The current meshd, and it's companion Non-Background Command-line
version btmesh, exists fully within user-space, but requires an
HCI_USER socket to the kernel controler, and currently requires root
privledges.

This first revision is intended to operate as a standard
(non-provisioner) node.  It includes the Configuration Server model,
and if it cannot find it's Mesh Configuration, will look for it's
Composition data, and start up as unprovisioned, and accept an
incoming PB-ADV provisioning session.

Two of us (Inga and Brian) have been working on this, and will be
the initial supporters and maintainers, and we gladly invite other
interested parties to participate.

Brian Gix (4):
  meshd: Shared private meshd interfaces
  meshd: Infrastructure for Mesh daemon
  meshd: Provisioning logic for mesh
  meshd: Upper and Lower mesh transport

Inga Stotland (7):
  meshd: Header files for mesh access layer and utilities
  meshd: Source files for mesh access layer and utilities
  meshd: Source code for handling access layer mux/demux
  meshd: Mesh config server model
  meshd: Read and write mesh configuration in JSON format
  meshd: Sample device composition in JSON fromat
  Makefile for meshd and configure.ac

 Makefile.am                   |    1 +
 Makefile.meshd                |   44 +
 configure.ac                  |    2 +-
 meshd/common/agent.c          |  229 +++
 meshd/common/agent.h          |   42 +
 meshd/common/mesh-defs.h      |   84 +
 meshd/common/util.c           |   71 +
 meshd/common/util.h           |   25 +
 meshd/config/composition.json |   44 +
 meshd/mesh-json/mesh-db.c     | 1360 +++++++++++++
 meshd/mesh-json/mesh-db.h     |  144 ++
 meshd/src/appkey.c            |  538 ++++++
 meshd/src/appkey.h            |   43 +
 meshd/src/btmesh.c            |  176 ++
 meshd/src/cfgmod-server.c     | 1194 ++++++++++++
 meshd/src/cfgmod.h            |   98 +
 meshd/src/crypto.c            | 1607 ++++++++++++++++
 meshd/src/crypto.h            |  164 ++
 meshd/src/display.c           |   67 +
 meshd/src/display.h           |   29 +
 meshd/src/friend.c            | 1116 +++++++++++
 meshd/src/friend.h            |   57 +
 meshd/src/hci.c               |  699 +++++++
 meshd/src/hci.h               |   56 +
 meshd/src/main.c              |  174 ++
 meshd/src/mesh-io-api.h       |   58 +
 meshd/src/mesh-io-generic.c   |  660 +++++++
 meshd/src/mesh-io-generic.h   |   20 +
 meshd/src/mesh-io.c           |  187 ++
 meshd/src/mesh-io.h           |   99 +
 meshd/src/mesh.c              |  184 ++
 meshd/src/mesh.h              |   32 +
 meshd/src/model.c             | 1274 +++++++++++++
 meshd/src/model.h             |  146 ++
 meshd/src/net.c               | 4188 +++++++++++++++++++++++++++++++++++++++++
 meshd/src/net.h               |  392 ++++
 meshd/src/node.c              |  851 +++++++++
 meshd/src/node.h              |   80 +
 meshd/src/prov.c              |  722 +++++++
 meshd/src/prov.h              |  162 ++
 meshd/src/provision.c         | 1159 ++++++++++++
 meshd/src/provision.h         |   30 +
 meshd/src/storage.c           |  673 +++++++
 meshd/src/storage.h           |   51 +
 44 files changed, 19031 insertions(+), 1 deletion(-)
 create mode 100644 Makefile.meshd
 create mode 100644 meshd/common/agent.c
 create mode 100644 meshd/common/agent.h
 create mode 100644 meshd/common/mesh-defs.h
 create mode 100644 meshd/common/util.c
 create mode 100644 meshd/common/util.h
 create mode 100644 meshd/config/composition.json
 create mode 100644 meshd/mesh-json/mesh-db.c
 create mode 100644 meshd/mesh-json/mesh-db.h
 create mode 100644 meshd/src/appkey.c
 create mode 100644 meshd/src/appkey.h
 create mode 100644 meshd/src/btmesh.c
 create mode 100644 meshd/src/cfgmod-server.c
 create mode 100644 meshd/src/cfgmod.h
 create mode 100644 meshd/src/crypto.c
 create mode 100644 meshd/src/crypto.h
 create mode 100644 meshd/src/display.c
 create mode 100644 meshd/src/display.h
 create mode 100644 meshd/src/friend.c
 create mode 100644 meshd/src/friend.h
 create mode 100644 meshd/src/hci.c
 create mode 100644 meshd/src/hci.h
 create mode 100644 meshd/src/main.c
 create mode 100644 meshd/src/mesh-io-api.h
 create mode 100644 meshd/src/mesh-io-generic.c
 create mode 100644 meshd/src/mesh-io-generic.h
 create mode 100644 meshd/src/mesh-io.c
 create mode 100644 meshd/src/mesh-io.h
 create mode 100644 meshd/src/mesh.c
 create mode 100644 meshd/src/mesh.h
 create mode 100644 meshd/src/model.c
 create mode 100644 meshd/src/model.h
 create mode 100644 meshd/src/net.c
 create mode 100644 meshd/src/net.h
 create mode 100644 meshd/src/node.c
 create mode 100644 meshd/src/node.h
 create mode 100644 meshd/src/prov.c
 create mode 100644 meshd/src/prov.h
 create mode 100644 meshd/src/provision.c
 create mode 100644 meshd/src/provision.h
 create mode 100644 meshd/src/storage.c
 create mode 100644 meshd/src/storage.h

-- 
2.14.3


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v2 01/11] meshd: Shared private meshd interfaces
  2018-04-25 17:50 [PATCH BlueZ v2 00/11] Bluetooth Mesh Daemon Brian Gix
@ 2018-04-25 17:50 ` Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 03/11] meshd: Provisioning logic for mesh Brian Gix
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Brian Gix @ 2018-04-25 17:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Brian Gix

---
 meshd/src/crypto.h          | 164 ++++++++++++++++++
 meshd/src/display.h         |  29 ++++
 meshd/src/friend.h          |  57 +++++++
 meshd/src/hci.h             |  56 +++++++
 meshd/src/mesh-io-api.h     |  58 +++++++
 meshd/src/mesh-io-generic.h |  20 +++
 meshd/src/mesh-io.h         |  99 +++++++++++
 meshd/src/net.h             | 392 ++++++++++++++++++++++++++++++++++++++++++++
 meshd/src/prov.h            | 162 ++++++++++++++++++
 meshd/src/provision.h       |  30 ++++
 10 files changed, 1067 insertions(+)
 create mode 100644 meshd/src/crypto.h
 create mode 100644 meshd/src/display.h
 create mode 100644 meshd/src/friend.h
 create mode 100644 meshd/src/hci.h
 create mode 100644 meshd/src/mesh-io-api.h
 create mode 100644 meshd/src/mesh-io-generic.h
 create mode 100644 meshd/src/mesh-io.h
 create mode 100644 meshd/src/net.h
 create mode 100644 meshd/src/prov.h
 create mode 100644 meshd/src/provision.h

diff --git a/meshd/src/crypto.h b/meshd/src/crypto.h
new file mode 100644
index 000000000..251b2cc7f
--- /dev/null
+++ b/meshd/src/crypto.h
@@ -0,0 +1,164 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool mesh_crypto_aes_ccm_encrypt(const uint8_t nonce[13], const uint8_t key[16],
+					const uint8_t *aad, uint16_t aad_len,
+					const uint8_t *msg, uint16_t msg_len,
+					uint8_t *out_msg,
+					void *out_mic, size_t mic_size);
+bool mesh_crypto_aes_ccm_decrypt(const uint8_t nonce[13], const uint8_t key[16],
+				const uint8_t *aad, uint16_t aad_len,
+				const uint8_t *enc_msg, uint16_t enc_msg_len,
+				uint8_t *out_msg,
+				void *out_mic, size_t mic_size);
+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_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],
+				const uint8_t network_id[16],
+				uint32_t iv_index, bool kr,
+				bool iu, uint64_t *cmac);
+bool mesh_crypto_network_nonce(bool frnd, uint8_t ttl, uint32_t seq,
+				uint16_t src, uint32_t iv_index,
+				uint8_t nonce[13]);
+bool mesh_crypto_network_encrypt(bool ctl, uint8_t ttl,
+				uint32_t seq, uint16_t src,
+				uint32_t iv_index,
+				const uint8_t net_key[16],
+				const uint8_t *enc_msg, uint8_t enc_msg_len,
+				uint8_t *out, void *net_mic);
+bool mesh_crypto_network_decrypt(bool frnd, uint8_t ttl,
+				uint32_t seq, uint16_t src,
+				uint32_t iv_index,
+				const uint8_t net_key[16],
+				const uint8_t *enc_msg, uint8_t enc_msg_len,
+				uint8_t *out, void *net_mic, size_t mic_size);
+bool mesh_crypto_application_nonce(uint32_t seq, uint16_t src,
+				uint16_t dst, uint32_t iv_index,
+				bool aszmic, uint8_t nonce[13]);
+bool mesh_crypto_device_nonce(uint32_t seq, uint16_t src,
+				uint16_t dst, uint32_t iv_index,
+				bool aszmic, uint8_t nonce[13]);
+bool mesh_crypto_application_encrypt(uint8_t akf, uint32_t seq, uint16_t src,
+					uint16_t dst, uint32_t iv_index,
+					const uint8_t app_key[16],
+					const uint8_t *aad, uint8_t aad_len,
+					const uint8_t *msg, uint8_t msg_len,
+					uint8_t *out,
+					void *app_mic, size_t mic_size);
+bool mesh_crypto_application_decrypt(uint8_t akf, uint32_t seq, uint16_t src,
+				uint16_t dst, uint32_t iv_index,
+				const uint8_t app_key[16],
+				const uint8_t *aad, uint8_t aad_len,
+				const uint8_t *enc_msg, uint8_t enc_msg_len,
+				uint8_t *out, void *app_mic, size_t mic_size);
+bool mesh_crypto_device_key(const uint8_t secret[32],
+						const uint8_t salt[16],
+						uint8_t device_key[16]);
+bool mesh_crypto_virtual_addr(const uint8_t virtual_label[16],
+						uint16_t *v_addr);
+bool mesh_crypto_nonce(const uint8_t secret[32],
+					const uint8_t salt[16],
+					uint8_t nonce[13]);
+bool mesh_crypto_k1(const uint8_t ikm[16], const uint8_t salt[16],
+		const void *info, size_t info_len, uint8_t okm[16]);
+bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len,
+							uint8_t net_id[1],
+							uint8_t enc_key[16],
+							uint8_t priv_key[16]);
+bool mesh_crypto_k3(const uint8_t n[16], uint8_t out64[8]);
+bool mesh_crypto_k4(const uint8_t a[16], uint8_t out5[1]);
+bool mesh_crypto_s1(const void *info, size_t len, uint8_t salt[16]);
+bool mesh_crypto_prov_prov_salt(const uint8_t conf_salt[16],
+					const uint8_t prov_rand[16],
+					const uint8_t dev_rand[16],
+					uint8_t prov_salt[16]);
+bool mesh_crypto_prov_conf_key(const uint8_t secret[32],
+					const uint8_t salt[16],
+					uint8_t conf_key[16]);
+bool mesh_crypto_session_key(const uint8_t secret[32],
+					const uint8_t salt[16],
+					uint8_t session_key[16]);
+bool mesh_crypto_privacy_counter(uint32_t iv_index,
+						const uint8_t *payload,
+						uint8_t privacy_counter[16]);
+bool mesh_crypto_network_obfuscate(const uint8_t privacy_key[16],
+					const uint8_t privacy_counter[16],
+					bool ctl, uint8_t ttl, uint32_t seq,
+					uint16_t src, uint8_t *out);
+bool mesh_crypto_network_clarify(const uint8_t privacy_key[16],
+				const uint8_t privacy_counter[16],
+				const uint8_t net_hdr[6],
+				bool *ctl, uint8_t *ttl,
+				uint32_t *seq, uint16_t *src);
+
+bool mesh_crypto_packet_build(bool ctl, uint8_t ttl,
+				uint32_t seq,
+				uint16_t src, uint16_t dst,
+				uint8_t opcode,
+				bool segmented, uint8_t key_id,
+				bool szmic, bool relay, uint16_t seqZero,
+				uint8_t segO, uint8_t segN,
+				const uint8_t *payload, uint8_t payload_len,
+				uint8_t *packet, uint8_t *packet_len);
+bool mesh_crypto_packet_parse(const uint8_t *packet, uint8_t packet_len,
+				bool *ctl, uint8_t *ttl, uint32_t *seq,
+				uint16_t *src, uint16_t *dst,
+				uint32_t *cookie, uint8_t *opcode,
+				bool *segmented, uint8_t *key_id,
+				bool *szmic, bool *relay, uint16_t *seqZero,
+				uint8_t *segO, uint8_t *segN,
+				const uint8_t **payload, uint8_t *payload_len);
+bool mesh_crypto_payload_encrypt(uint8_t *aad, const uint8_t *payload,
+				uint8_t *out, uint16_t payload_len,
+				uint16_t src, uint16_t dst, uint8_t key_id,
+				uint32_t seq_num, uint32_t iv_index,
+				bool aszmic,
+				const uint8_t application_key[16]);
+bool mesh_crypto_payload_decrypt(uint8_t *aad, uint16_t aad_len,
+				const uint8_t *payload, uint16_t payload_len,
+				bool szmict,
+				uint16_t src, uint16_t dst, uint8_t key_id,
+				uint32_t seq_num, uint32_t iv_index,
+				uint8_t *out,
+				const uint8_t application_key[16]);
+bool mesh_crypto_packet_encode(uint8_t *packet, uint8_t packet_len,
+				const uint8_t network_key[16],
+				uint32_t iv_index,
+				const uint8_t privacy_key[16]);
+bool mesh_crypto_packet_decode(const uint8_t *packet, uint8_t packet_len,
+				bool proxy, uint8_t *out, uint32_t iv_index,
+				const uint8_t network_key[16],
+				const uint8_t privacy_key[16]);
+bool mesh_crypto_packet_label(uint8_t *packet, uint8_t packet_len,
+				uint16_t iv_index, uint8_t network_id);
+
+uint8_t mesh_crypto_compute_fcs(const uint8_t *packet, uint8_t packet_len);
+bool mesh_crypto_check_fcs(const uint8_t *packet, uint8_t packet_len,
+							uint8_t received_fcs);
+bool mesh_crypto_aes_cmac(const uint8_t key[16], const uint8_t *msg,
+					size_t msg_len, uint8_t res[16]);
+
diff --git a/meshd/src/display.h b/meshd/src/display.h
new file mode 100644
index 000000000..d43292741
--- /dev/null
+++ b/meshd/src/display.h
@@ -0,0 +1,29 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 COLOR_OFF	"\x1B[0m"
+#define COLOR_RED	"\x1B[0;91m"
+#define COLOR_GREEN	"\x1B[0;92m"
+#define COLOR_YELLOW	"\x1B[0;93m"
+#define COLOR_BLUE	"\x1B[0;94m"
+
+unsigned int num_columns(void);
+
+void print_packet(const char *label, const void *data, uint16_t size);
+
diff --git a/meshd/src/friend.h b/meshd/src/friend.h
new file mode 100644
index 000000000..1fa6ec92a
--- /dev/null
+++ b/meshd/src/friend.h
@@ -0,0 +1,57 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 OP_FRND_REQUEST			0x8040
+#define OP_FRND_INQUIRY			0x8041
+#define OP_FRND_CONFIRM			0x8042
+#define OP_FRND_SUB_LIST_ADD		0x8043
+#define OP_FRND_SUB_LIST_CONFIRM	0x8044
+#define OP_FRND_SUB_LIST_REMOVE		0x8045
+#define OP_FRND_NEGOTIATE		0x8046
+#define OP_FRND_CLEAR			0x8047
+
+void friend_poll(struct mesh_net *net, uint16_t src, bool seq,
+						struct mesh_friend *frnd);
+void friend_request(struct mesh_net *net, uint16_t src, uint8_t minReq,
+			uint8_t delay, uint32_t timeout, uint16_t prev,
+			uint8_t num_elements, uint16_t cntr, int8_t rssi);
+void friend_clear_confirm(struct mesh_net *net, uint16_t src, uint16_t lpn,
+							uint16_t lpnCounter);
+void friend_clear(struct mesh_net *net, uint16_t src, uint16_t lpn,
+			uint16_t lpnCounter, struct mesh_friend *frnd);
+void friend_sub_add(struct mesh_net *net, struct mesh_friend *frnd,
+					const uint8_t *pkt, uint8_t len);
+void friend_sub_del(struct mesh_net *net, struct mesh_friend *frnd,
+					const uint8_t *pkt, uint8_t len);
+void mesh_friend_relay_init(struct mesh_net *net, uint16_t addr);
+
+/* Low-Power-Node role */
+void frnd_sub_add(struct mesh_net *net, uint32_t parms[7]);
+void frnd_sub_del(struct mesh_net *net, uint32_t parms[7]);
+void frnd_poll(struct mesh_net *net, bool retry);
+void frnd_clear(struct mesh_net *net);
+void frnd_ack_poll(struct mesh_net *net);
+void frnd_poll_cancel(struct mesh_net *net);
+void frnd_request_friend(struct mesh_net *net, uint8_t cache,
+			uint8_t offer_delay, uint8_t delay, uint32_t timeout);
+void frnd_offer(struct mesh_net *net, uint16_t src, uint8_t window,
+			uint8_t cache, uint8_t sub_list_size,
+			int8_t r_rssi, int8_t l_rssi, uint16_t fn_cnt);
+void frnd_key_refresh(struct mesh_net *net, uint8_t phase);
+struct mesh_key_set *frnd_get_key(struct mesh_net *net);
diff --git a/meshd/src/hci.h b/meshd/src/hci.h
new file mode 100644
index 000000000..a1362b76a
--- /dev/null
+++ b/meshd/src/hci.h
@@ -0,0 +1,56 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 bt_hci;
+
+typedef void (*bt_hci_destroy_func_t)(void *user_data);
+
+struct bt_hci *bt_hci_new(int fd);
+struct bt_hci *bt_hci_new_user_channel(uint16_t index);
+struct bt_hci *bt_hci_new_raw_device(uint16_t index);
+
+struct bt_hci *bt_hci_ref(struct bt_hci *hci);
+void bt_hci_unref(struct bt_hci *hci);
+
+bool bt_hci_set_close_on_unref(struct bt_hci *hci, bool do_close);
+
+typedef void (*bt_hci_callback_func_t)(const void *data, uint8_t size,
+							void *user_data);
+
+unsigned int bt_hci_send(struct bt_hci *hci, uint16_t opcode,
+				const void *data, uint8_t size,
+				bt_hci_callback_func_t callback,
+				void *user_data, bt_hci_destroy_func_t destroy);
+bool bt_hci_cancel(struct bt_hci *hci, unsigned int id);
+bool bt_hci_flush(struct bt_hci *hci);
+
+unsigned int bt_hci_register(struct bt_hci *hci, uint8_t event,
+				bt_hci_callback_func_t callback,
+				void *user_data, bt_hci_destroy_func_t destroy);
+bool bt_hci_unregister(struct bt_hci *hci, unsigned int id);
+
+typedef void (*bt_hci_receive_func_t)(uint16_t handle, uint8_t flags,
+					const void *data, uint16_t size,
+							void *user_data);
+
+bool bt_hci_receive(struct bt_hci *hci, bt_hci_receive_func_t callback,
+				void *user_data, bt_hci_destroy_func_t destroy);
+
+bool bt_hci_write(struct bt_hci *hci, uint16_t handle, uint8_t flags,
+				const void *data, uint16_t size);
diff --git a/meshd/src/mesh-io-api.h b/meshd/src/mesh-io-api.h
new file mode 100644
index 000000000..f69fceeb2
--- /dev/null
+++ b/meshd/src/mesh-io-api.h
@@ -0,0 +1,58 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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_io_private;
+
+typedef bool (*mesh_io_init_t)(uint16_t index, struct mesh_io *io);
+typedef bool (*mesh_io_destroy_t)(struct mesh_io *io);
+typedef bool (*mesh_io_caps_t)(struct mesh_io *io, struct mesh_io_caps *caps);
+typedef bool (*mesh_io_send_t)(struct mesh_io *io,
+					struct mesh_io_send_info *info,
+					const uint8_t *data, uint16_t len);
+typedef bool (*mesh_io_register_t)(struct mesh_io *io, uint8_t filter_id,
+				mesh_io_recv_func_t cb, void *user_data);
+typedef bool (*mesh_io_deregister_t)(struct mesh_io *io, uint8_t filter_id);
+typedef bool (*mesh_io_filter_set_t)(struct mesh_io *io,
+			uint8_t filter_id, const uint8_t *data, uint8_t len,
+			mesh_io_status_func_t callback, void *user_data);
+typedef bool (*mesh_io_tx_cancel_t)(struct mesh_io *io, uint8_t *pattern,
+								uint8_t len);
+
+struct mesh_io_api {
+	mesh_io_init_t		init;
+	mesh_io_destroy_t	destroy;
+	mesh_io_caps_t		caps;
+	mesh_io_send_t		send;
+	mesh_io_register_t	reg;
+	mesh_io_deregister_t	dereg;
+	mesh_io_filter_set_t	set;
+	mesh_io_tx_cancel_t	cancel;
+};
+
+struct mesh_io {
+	enum mesh_io_type		type;
+	uint16_t			index;
+	const struct mesh_io_api	*api;
+	struct mesh_io_private		*pvt;
+};
+
+struct mesh_io_table {
+	enum mesh_io_type		type;
+	const struct mesh_io_api	*api;
+};
diff --git a/meshd/src/mesh-io-generic.h b/meshd/src/mesh-io-generic.h
new file mode 100644
index 000000000..4bf4d5cb7
--- /dev/null
+++ b/meshd/src/mesh-io-generic.h
@@ -0,0 +1,20 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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.
+ *
+ */
+
+extern const struct mesh_io_api mesh_io_generic;
diff --git a/meshd/src/mesh-io.h b/meshd/src/mesh-io.h
new file mode 100644
index 000000000..754f6129c
--- /dev/null
+++ b/meshd/src/mesh-io.h
@@ -0,0 +1,99 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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_io;
+
+#define MESH_IO_FILTER_BEACON	1
+#define MESH_IO_FILTER_PROV	2
+#define MESH_IO_FILTER_NET	3
+
+#define MESH_IO_TX_COUNT_UNLIMITED	0
+
+enum mesh_io_type {
+	MESH_IO_TYPE_NONE = 0,
+	MESH_IO_TYPE_GENERIC
+};
+
+enum mesh_io_timing_type {
+	MESH_IO_TIMING_TYPE_GENERAL = 1,
+	MESH_IO_TIMING_TYPE_POLL,
+	MESH_IO_TIMING_TYPE_POLL_RSP
+};
+
+struct mesh_io_recv_info {
+	uint32_t instant;
+	uint8_t chan;
+	int8_t rssi;
+};
+
+struct mesh_io_send_info {
+	enum mesh_io_timing_type type;
+	union {
+		struct {
+			uint16_t interval;
+			uint8_t cnt;
+			uint8_t min_delay;
+			uint8_t max_delay;
+		} gen;
+
+		struct {
+			uint16_t scan_duration;
+			uint8_t scan_delay;
+			uint8_t filter_ids[2];
+			uint8_t min_delay;
+			uint8_t max_delay;
+		} poll;
+
+		struct {
+			uint32_t instant;
+			uint8_t delay;
+		} poll_rsp;
+
+	} u;
+};
+
+struct mesh_io_caps {
+	uint8_t max_num_filters;
+	uint8_t window_accuracy;
+};
+
+typedef void (*mesh_io_recv_func_t)(void *user_data,
+					struct mesh_io_recv_info *info,
+					const uint8_t *data, uint16_t len);
+
+typedef void (*mesh_io_status_func_t)(void *user_data, int status,
+							uint8_t filter_id);
+
+struct mesh_io *mesh_io_new(uint16_t index, enum mesh_io_type type);
+void mesh_io_destroy(struct mesh_io *io);
+
+bool mesh_io_get_caps(struct mesh_io *io, struct mesh_io_caps *caps);
+
+bool mesh_io_register_recv_cb(struct mesh_io *io, uint8_t filter_id,
+				mesh_io_recv_func_t cb, void *user_data);
+
+bool mesh_io_deregister_recv_cb(struct mesh_io *io, uint8_t filter_id);
+
+bool mesh_set_filter(struct mesh_io *io, uint8_t filter_id,
+				const uint8_t *data, uint8_t len,
+				mesh_io_status_func_t cb, void *user_data);
+
+bool mesh_io_send(struct mesh_io *io, struct mesh_io_send_info *info,
+					const uint8_t *data, uint16_t len);
+bool mesh_io_send_cancel(struct mesh_io *io, uint8_t *pattern, uint8_t len);
diff --git a/meshd/src/net.h b/meshd/src/net.h
new file mode 100644
index 000000000..e48380314
--- /dev/null
+++ b/meshd/src/net.h
@@ -0,0 +1,392 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+struct mesh_io;
+struct mesh_node;
+
+#define DEV_ID	0
+
+#define UNUSED_KEY_IDX	0xffff
+
+#define APP_ID_DEV	0
+#define APP_ID_ANY	((unsigned int) -1)
+#define NET_ID_ANY	(APP_ID_ANY - 1)
+
+#define CTL		0x80
+#define TTL_MASK	0x7f
+#define SEQ_MASK	0xffffff
+
+#define CREDFLAG_MASK	0x1000
+#define APP_IDX_MASK	0x0fff
+#define APP_IDX_DEV	0x7fff
+#define APP_IDX_ANY	0x8000
+#define APP_IDX_NET	0xffff
+
+#define NET_IDX_INVALID	0xffff
+#define NET_NID_INVALID	0xff
+
+#define KEY_CACHE_SIZE	64
+#define FRND_CACHE_MAX	32
+
+#define MAX_UNSEG_LEN	15 /* msg_len == 11 + sizeof(MIC) */
+#define MAX_SEG_LEN	12 /* UnSeg length - 3 octets overhead */
+#define SEG_MAX(len)	(((len) <= MAX_UNSEG_LEN) ? 0 : \
+						(((len) - 1) / MAX_SEG_LEN))
+#define SEG_OFF(seg)	((seg) * MAX_SEG_LEN)
+#define MAX_SEG_TO_LEN(seg)	((seg) ? SEG_OFF((seg) + 1) : MAX_UNSEG_LEN)
+
+#define SEGMENTED	0x80
+#define UNSEGMENTED	0x00
+#define SEG_HDR_SHIFT	31
+#define IS_SEGMENTED(hdr)	(!!((hdr) & (true << SEG_HDR_SHIFT)))
+
+#define KEY_ID_MASK	0x7f
+#define KEY_AID_MASK	0x3f
+#define KEY_ID_AKF	0x40
+#define KEY_AID_SHIFT	0
+#define AKF_HDR_SHIFT	30
+#define KEY_HDR_SHIFT	24
+#define HAS_APP_KEY(hdr)	(!!((hdr) & (true << AKF_HDR_SHIFT)))
+
+#define OPCODE_MASK	0x7f
+#define OPCODE_HDR_SHIFT	24
+#define RELAY		0x80
+#define RELAY_HDR_SHIFT	23
+#define SZMIC		0x80
+#define SZMIC_HDR_SHIFT	23
+#define SEQ_ZERO_MASK	0x1fff
+#define SEQ_ZERO_HDR_SHIFT	10
+#define IS_RELAYED(hdr)	(!!((hdr) & (true << RELAY_HDR_SHIFT)))
+#define HAS_MIC64(hdr)	(!!((hdr) & (true << SZMIC_HDR_SHIFT)))
+
+#define SEG_MASK	0x1f
+#define SEGO_HDR_SHIFT	5
+#define SEGN_HDR_SHIFT	0
+#define SEG_TOTAL(hdr)	(((hdr) >> SEGN_HDR_SHIFT) & SEG_MASK)
+
+/* Mask of Hdr bits which must be constant over entire incoming SAR message */
+/* (SEG || AKF || AID || SZMIC || SeqZero || SegN) */
+#define HDR_KEY_MASK		((true << SEG_HDR_SHIFT) |		\
+				(KEY_ID_MASK << KEY_HDR_SHIFT) |	\
+				(true << SZMIC_HDR_SHIFT) |		\
+				(SEQ_ZERO_MASK << SEQ_ZERO_HDR_SHIFT) |	\
+				(SEG_MASK << SEGN_HDR_SHIFT))
+
+#define HDR_ACK_MASK		((OPCODE_MASK << OPCODE_HDR_SHIFT) |	\
+				(SEQ_ZERO_MASK << SEQ_ZERO_HDR_SHIFT))
+
+
+
+#define MSG_CACHE_SIZE		70
+#define REPLAY_CACHE_SIZE	10
+
+/* Proxy Configuration Opcodes */
+#define PROXY_OP_SET_FILTER_TYPE	0x00
+#define PROXY_OP_FILTER_ADD		0x01
+#define PROXY_OP_FILTER_DEL		0x02
+#define PROXY_OP_FILTER_STATUS		0x03
+
+/* Proxy Filter Defines */
+#define PROXY_FILTER_WHITELIST		0x00
+#define PROXY_FILTER_BLACKLIST		0x01
+
+/* Network Tranport Opcodes */
+#define NET_OP_SEG_ACKNOWLEDGE		0x00
+#define NET_OP_FRND_POLL		0x01
+#define NET_OP_FRND_UPDATE		0x02
+#define NET_OP_FRND_REQUEST		0x03
+#define NET_OP_FRND_OFFER		0x04
+#define NET_OP_FRND_CLEAR		0x05
+#define NET_OP_FRND_CLEAR_CONFIRM	0x06
+
+#define NET_OP_PROXY_SUB_ADD		0x07
+#define NET_OP_PROXY_SUB_REMOVE		0x08
+#define NET_OP_PROXY_SUB_CONFIRM	0x09
+#define NET_OP_HEARTBEAT		0x0a
+
+#define FRND_OPCODE(x) \
+		((x) >= NET_OP_FRND_POLL && (x) <= NET_OP_FRND_CLEAR_CONFIRM)
+
+struct mesh_net_addr_range {
+	uint16_t low;
+	uint16_t high;
+	uint16_t next;
+};
+
+struct mesh_net_prov_caps {
+	uint8_t num_ele;
+	uint16_t algorithms;
+	uint8_t pub_type;
+	uint8_t static_type;
+	uint8_t output_size;
+	uint16_t output_action;
+	uint8_t input_size;
+	uint16_t input_action;
+} __packed;
+
+struct mesh_net_heartbeat {
+	struct l_timeout *pub_timer;
+	struct l_timeout *sub_timer;
+	struct timeval sub_time;
+	bool sub_enabled;
+	uint32_t pub_period;
+	uint32_t sub_period;
+	uint32_t sub_start;
+	uint16_t pub_dst;
+	uint16_t pub_count;
+	uint16_t pub_features;
+	uint16_t features;
+	uint16_t pub_net_idx;
+	uint16_t sub_src;
+	uint16_t sub_dst;
+	uint16_t sub_count;
+	uint8_t pub_ttl;
+	uint8_t sub_min_hops;
+	uint8_t sub_max_hops;
+};
+
+struct mesh_key_set {
+	bool frnd;
+	uint8_t nid;
+	uint8_t enc_key[16];
+	uint8_t privacy_key[16];
+};
+
+struct mesh_friend {
+	struct mesh_net *net;
+	struct l_queue *pkt_cache;
+	struct l_timeout *timeout;
+	void *pkt;
+	uint16_t *grp_list;
+	uint32_t poll_timeout;
+	uint32_t last_hdr;
+	uint16_t dst; /* Primary Element unicast addr */
+	uint16_t fn_cnt;
+	uint16_t lp_cnt;
+	int16_t grp_cnt;
+	struct mesh_key_set key_set;
+	struct mesh_key_set new_key_set;
+	uint8_t ele_cnt;
+	uint8_t frd;
+	uint8_t frw;
+	bool seq;
+	bool last;
+};
+
+struct mesh_frnd_pkt {
+	uint32_t iv_index;
+	uint32_t seq;
+	uint16_t src;
+	uint16_t dst;
+	uint16_t size;
+	uint8_t segN;
+	uint8_t segO;
+	uint8_t ttl;
+	uint8_t tc;
+	bool szmict;
+	union {
+		struct {
+			uint8_t key_id;
+		} m;
+		struct {
+			uint16_t seq0;
+		} a;
+		struct {
+			uint8_t opcode;
+		} c;
+	} u;
+	uint8_t data[];
+};
+
+struct mesh_friend_seg_one {
+	uint32_t hdr;
+	uint32_t seq;
+	bool sent;
+	bool md;
+	uint8_t data[15];
+};
+
+struct mesh_friend_seg_12 {
+	uint32_t hdr;
+	uint32_t seq;
+	bool sent;
+	bool md;
+	uint8_t data[12];
+};
+
+struct mesh_friend_msg {
+	uint32_t iv_index;
+	uint32_t flags;
+	uint16_t src;
+	uint16_t dst;
+	uint8_t ttl;
+	uint8_t cnt_in;
+	uint8_t cnt_out;
+	uint8_t last_len;
+	bool done;
+	bool ctl;
+	union {
+		struct mesh_friend_seg_one one[1]; /* Single segment */
+		struct mesh_friend_seg_12 s12[0]; /* Array of segments */
+	} u;
+};
+
+typedef void (*mesh_status_func_t)(void *user_data, bool result);
+typedef void (*mesh_net_status_func_t)(uint16_t remote, uint8_t status,
+					void *data, uint16_t size,
+					void *user_data);
+
+struct mesh_net *mesh_net_new(uint16_t index);
+struct mesh_net *mesh_net_ref(struct mesh_net *net);
+void mesh_net_unref(struct mesh_net *net);
+void mesh_net_flush_msg_queues(struct mesh_net *net);
+void mesh_net_set_iv_index(struct mesh_net *net, uint32_t index, bool update);
+bool mesh_net_iv_index_update(struct mesh_net *net);
+bool mesh_net_set_seq_num(struct mesh_net *net, uint32_t number);
+uint32_t mesh_net_get_seq_num(struct mesh_net *net);
+uint32_t mesh_net_next_seq_num(struct mesh_net *net);
+bool mesh_net_set_default_ttl(struct mesh_net *net, uint8_t ttl);
+uint8_t mesh_net_get_default_ttl(struct mesh_net *net);
+bool mesh_net_get_frnd_seq(struct mesh_net *net);
+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);
+bool mesh_net_set_friend(struct mesh_net *net, uint16_t friend_addr);
+uint16_t mesh_net_get_friend(struct mesh_net *net);
+uint8_t mesh_net_get_num_ele(struct mesh_net *net);
+bool mesh_net_set_beacon_mode(struct mesh_net *net, bool enable);
+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);
+bool mesh_net_set_friend_mode(struct mesh_net *net, bool enable);
+bool mesh_net_add_keyset(struct mesh_net *net, struct mesh_key_set *key_set);
+bool mesh_net_remove_keyset(struct mesh_net *net, struct mesh_key_set *key_set);
+int mesh_net_del_key(struct mesh_net *net, uint16_t net_idx);
+int mesh_net_add_key(struct mesh_net *net, bool update,
+					uint16_t net_idx, const void *key);
+uint32_t mesh_net_get_iv_index(struct mesh_net *net);
+void mesh_net_get_snb_state(struct mesh_net *net,
+					uint8_t *flags, uint32_t *iv_index);
+bool mesh_net_get_key(struct mesh_net *net, bool new_key, uint16_t idx,
+							uint8_t key[16]);
+bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io);
+struct mesh_io *mesh_net_detach(struct mesh_net *net);
+struct l_queue *mesh_net_get_app_keys(struct mesh_net *net);
+
+bool mesh_net_flush(struct mesh_net *net);
+void mesh_net_transport_send(struct mesh_net *net, struct mesh_key_set *key_set,
+				bool fast, uint32_t iv_index, uint8_t ttl,
+				uint32_t seq, uint16_t src, uint16_t dst,
+				const uint8_t *msg, uint16_t msg_len);
+
+unsigned int mesh_net_app_send(struct mesh_net *net, bool frnd_cred,
+				uint16_t src, uint16_t dst, uint8_t key_id,
+				uint8_t ttl, uint32_t seq, uint32_t iv_index,
+				bool szmic, const void *msg, uint16_t msg_len,
+				mesh_net_status_func_t status_func,
+				void *user_data);
+void mesh_net_app_send_cancel(struct mesh_net *net, unsigned int id);
+void mesh_net_ack_send(struct mesh_net *net, struct mesh_key_set *key_set,
+				uint32_t iv_index, uint8_t ttl, uint32_t seq,
+				uint16_t src, uint16_t dst, bool rly,
+				uint16_t seqZero, uint32_t ack_flags);
+struct mesh_net_prov_caps *mesh_net_prov_caps_get(struct mesh_net *net);
+uint8_t *mesh_net_priv_key_get(struct mesh_net *net);
+bool mesh_net_priv_key_set(struct mesh_net *net, uint8_t key[32]);
+uint8_t *mesh_net_prov_rand(struct mesh_net *net);
+uint16_t mesh_net_prov_uni(struct mesh_net *net, uint8_t ele_cnt);
+bool mesh_net_id_uuid_set(struct mesh_net *net, uint8_t uuid[16]);
+uint8_t *mesh_net_test_addr(struct mesh_net *net);
+int mesh_net_get_identity_mode(struct mesh_net *net, uint16_t idx,
+								uint8_t *mode);
+char *mesh_net_id_name(struct mesh_net *net);
+bool mesh_net_test_mode(struct mesh_net *net);
+bool mesh_net_dst_reg(struct mesh_net *net, uint16_t dst);
+bool mesh_net_dst_unreg(struct mesh_net *net, uint16_t dst);
+struct mesh_friend *mesh_friend_new(struct mesh_net *net, uint16_t dst,
+					uint8_t ele_cnt, uint8_t frd,
+					uint8_t frw, uint32_t fpt,
+					uint16_t fn_cnt, uint16_t lp_cnt);
+void mesh_friend_free(void *frnd);
+bool mesh_friend_clear(struct mesh_net *net, struct mesh_friend *frnd);
+void mesh_friend_sub_add(struct mesh_net *net, uint16_t lpn, uint8_t ele_cnt,
+							uint8_t grp_cnt,
+							const uint8_t *list);
+void mesh_friend_sub_del(struct mesh_net *net, uint16_t lpn, uint8_t cnt,
+						const uint8_t *del_list);
+uint8_t mesh_net_key_refresh_phase_set(struct mesh_net *net, uint16_t net_idx,
+							uint8_t transition);
+uint8_t mesh_net_key_refresh_phase_get(struct mesh_net *net, uint16_t net_idx,
+							uint8_t *phase);
+int mesh_net_kr_phase_one(struct mesh_net *net, uint16_t net_idx,
+							const uint8_t *key);
+int mesh_net_key_refresh_phase_two(struct mesh_net *net, uint16_t net_idx);
+int mesh_net_key_refresh_finish(struct mesh_net *net, uint16_t net_idx);
+void mesh_net_send_seg(struct mesh_net *net, struct mesh_key_set *key_set,
+				uint32_t iv_index, uint8_t ttl, uint32_t seq,
+				uint16_t src, uint16_t dst, uint32_t hdr,
+				const void *seg, uint16_t seg_len);
+uint16_t mesh_net_get_features(struct mesh_net *net);
+struct mesh_net_heartbeat *mesh_net_heartbeat_get(struct mesh_net *net);
+void mesh_net_heartbeat_init(struct mesh_net *net);
+void mesh_net_heartbeat_send(struct mesh_net *net);
+void mesh_net_uni_range_set(struct mesh_net *net,
+				struct mesh_net_addr_range *range);
+struct mesh_net_addr_range mesh_net_uni_range_get(struct mesh_net *net);
+void mesh_net_provisioner_mode_set(struct mesh_net *net, bool mode);
+bool mesh_net_provisioner_mode_get(struct mesh_net *net);
+bool mesh_net_key_list_get(struct mesh_net *net, uint8_t *buf, uint16_t *count);
+uint16_t mesh_net_get_primary_idx(struct mesh_net *net);
+void mesh_net_sub_list_add(struct mesh_net *net, uint16_t addr);
+void mesh_net_sub_list_del(struct mesh_net *net, uint16_t addr);
+uint32_t mesh_net_friend_timeout(struct mesh_net *net, uint16_t addr);
+struct mesh_io *mesh_net_get_io(struct mesh_net *net);
+bool mesh_net_local_node_set(struct mesh_net *net, struct mesh_node *node,
+							bool provisioner);
+struct mesh_node *mesh_net_local_node_get(struct mesh_net *net);
+bool mesh_net_set_crpl(struct mesh_net *net, uint16_t crpl);
+uint16_t mesh_net_get_crpl(struct mesh_net *net);
+bool mesh_net_have_key(struct mesh_net *net, uint16_t net_idx);
+bool mesh_net_jconfig_set(struct mesh_net *net, void *jconfig);
+void *mesh_net_jconfig_get(struct mesh_net *net);
+bool mesh_net_cfg_file_set(struct mesh_net *net, const char *cfg);
+bool mesh_net_cfg_file_get(struct mesh_net *net, const char **cfg);
+bool mesh_net_is_local_address(struct mesh_net *net, uint16_t addr);
+void mesh_net_set_window_accuracy(struct mesh_net *net, uint8_t accuracy);
+void mesh_net_transmit_params_set(struct mesh_net *net, uint8_t count,
+							uint16_t interval);
+void mesh_net_transmit_params_get(struct mesh_net *net, uint8_t *count,
+							uint16_t *interval);
+struct mesh_prov *mesh_net_get_prov(struct mesh_net *net);
+void mesh_net_set_prov(struct mesh_net *net, struct mesh_prov *prov);
+void mesh_net_provisioned_set(struct mesh_net *net, bool provisioned);
+bool mesh_net_provisioned_get(struct mesh_net *net);
+bool mesh_net_provisioned_new(struct mesh_net *net, uint8_t device_key[16],
+				uint16_t net_idx,  uint8_t net_key[16],
+				uint16_t unicast, uint16_t snb_flags,
+				uint32_t iv_index, mesh_status_func_t cb,
+				void *user_data);
diff --git a/meshd/src/prov.h b/meshd/src/prov.h
new file mode 100644
index 000000000..09fe6c3cd
--- /dev/null
+++ b/meshd/src/prov.h
@@ -0,0 +1,162 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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.
+ *
+ */
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+struct mesh_net;
+struct mesh_dev;
+
+enum mesh_trans {
+	MESH_TRANS_IDLE,
+	MESH_TRANS_TX,
+	MESH_TRANS_RX,
+};
+
+enum mesh_bearer {
+	MESH_BEARER_IDLE,
+	MESH_BEARER_ADV,
+};
+
+enum mesh_prov_mode {
+	MESH_PROV_MODE_NONE,
+	MESH_PROV_MODE_INITIATOR,
+	MESH_PROV_MODE_GATT_ACCEPTOR,
+	MESH_PROV_MODE_ADV_ACCEPTOR,
+	MESH_PROV_MODE_GATT_CLIENT,
+	MESH_PROV_MODE_MESH_SERVER,
+	MESH_PROV_MODE_MESH_CLIENT,
+	MESH_PROV_MODE_MESH_GATT_CLIENT,
+};
+
+struct mesh_prov;
+typedef void (*mesh_prov_open_func_t)(struct mesh_prov *prov);
+typedef void (*mesh_prov_close_func_t)(struct mesh_prov *prov, uint8_t reason);
+typedef void (*mesh_prov_send_func_t)(bool success, struct mesh_prov *prov);
+typedef void (*mesh_prov_receive_func_t)(const void *data, uint16_t size,
+							struct mesh_prov *prov);
+
+struct prov_invite {
+	uint8_t attention;
+} __packed;
+
+struct prov_start {
+	uint8_t algorithm;
+	uint8_t pub_key;
+	uint8_t auth_method;
+	uint8_t auth_action;
+	uint8_t auth_size;
+} __packed;
+
+struct conf_input {
+	struct prov_invite		invite;
+	struct mesh_net_prov_caps	caps;
+	struct prov_start		start;
+	uint8_t				prv_pub_key[64];
+	uint8_t				dev_pub_key[64];
+} __packed;
+
+struct mesh_prov {
+	int ref_count;
+	struct mesh_dev *dev;
+	struct mesh_net *net;
+	enum mesh_prov_mode mode;
+	enum mesh_trans trans;
+	enum mesh_bearer bearer;
+	uint8_t uuid[16];
+	uint8_t caps[12];
+
+	uint32_t conn_id;
+	uint16_t net_idx;
+	uint16_t remote;
+	uint16_t addr;
+	uint16_t expected_len;
+	uint16_t packet_len;
+	uint8_t local_msg_num;
+	uint8_t peer_msg_num;
+	uint8_t last_peer_msg_num;
+	uint8_t got_segs;
+	uint8_t expected_segs;
+	uint8_t expected_fcs;
+	uint8_t packet_buf[80];
+	uint8_t peer_buf[80];
+	struct timeval tx_start;
+	struct l_timeout *tx_timeout;
+
+	/* Provisioning credentials and crypto material */
+	struct conf_input conf_inputs;
+	uint8_t dev_key[16];
+	uint8_t conf_salt[16];
+	uint8_t s_key[16];
+	uint8_t s_nonce[13];
+	uint8_t conf_key[16];
+	uint8_t conf[16];
+	uint8_t r_conf[16];
+	uint8_t rand_auth[32];
+	uint8_t prov_salt[16];
+	uint8_t secret[32];
+	uint8_t r_public[64];
+	uint8_t l_public[64];
+	/* End Provisioning credentials and crypto material */
+
+	mesh_prov_open_func_t open_callback;
+	mesh_prov_close_func_t close_callback;
+	mesh_prov_receive_func_t receive_callback;
+	void *receive_data;
+	mesh_prov_send_func_t send_callback;
+	void *send_data;
+};
+
+struct mesh_prov *mesh_prov_new(struct mesh_net *net, uint16_t remote);
+
+struct mesh_prov *mesh_prov_ref(struct mesh_prov *prov);
+void mesh_prov_unref(struct mesh_prov *prov);
+
+bool mesh_prov_gatt_client(struct mesh_prov *prov, struct mesh_dev *dev,
+					uint8_t uuid[16],
+					mesh_prov_open_func_t open_callback,
+					mesh_prov_close_func_t close_callback,
+					mesh_prov_receive_func_t recv_callback,
+					void *user_data);
+
+bool mesh_prov_listen(struct mesh_net *net, uint8_t uuid[16], uint8_t caps[12],
+					mesh_prov_open_func_t open_callback,
+					mesh_prov_close_func_t close_callback,
+					mesh_prov_receive_func_t recv_callback,
+					void *user_data);
+
+bool mesh_prov_connect(struct mesh_prov *prov, struct mesh_dev *dev,
+					uint16_t net_idx, uint8_t uuid[16],
+					mesh_prov_open_func_t open_callback,
+					mesh_prov_close_func_t close_callback,
+					mesh_prov_receive_func_t recv_callback,
+					void *user_data);
+
+unsigned int mesh_prov_send(struct mesh_prov *prov,
+					const void *data, uint16_t size,
+					mesh_prov_send_func_t send_callback,
+					void *user_data);
+bool mesh_prov_cancel(struct mesh_prov *prov, unsigned int id);
+
+bool mesh_prov_close(struct mesh_prov *prov, uint8_t reason);
+void mesh_prov_set_addr(struct mesh_prov *prov, uint16_t addr);
+uint16_t mesh_prov_get_addr(struct mesh_prov *prov);
+void mesh_prov_set_idx(struct mesh_prov *prov, uint16_t net_idx);
+uint16_t mesh_prov_get_idx(struct mesh_prov *prov);
diff --git a/meshd/src/provision.h b/meshd/src/provision.h
new file mode 100644
index 000000000..0c59bf037
--- /dev/null
+++ b/meshd/src/provision.h
@@ -0,0 +1,30 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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_prov;
+struct l_queue;
+
+void initiator_prov_open(struct mesh_prov *prov);
+void initiator_prov_close(struct mesh_prov *prov, uint8_t reason);
+void initiator_prov_receive(const void *pkt, uint16_t size,
+							struct mesh_prov *prov);
+void acceptor_prov_open(struct mesh_prov *prov);
+void acceptor_prov_close(struct mesh_prov *prov, uint8_t reason);
+void acceptor_prov_receive(const void *pkt, uint16_t size,
+							struct mesh_prov *prov);
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v2 03/11] meshd: Provisioning logic for mesh
  2018-04-25 17:50 [PATCH BlueZ v2 00/11] Bluetooth Mesh Daemon Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 01/11] meshd: Shared private meshd interfaces Brian Gix
@ 2018-04-25 17:50 ` Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 05/11] meshd: Header files for mesh access layer and utilities Brian Gix
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Brian Gix @ 2018-04-25 17:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Brian Gix

---
 meshd/src/prov.c      |  722 ++++++++++++++++++++++++++++++
 meshd/src/provision.c | 1159 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1881 insertions(+)
 create mode 100644 meshd/src/prov.c
 create mode 100644 meshd/src/provision.c

diff --git a/meshd/src/prov.c b/meshd/src/prov.c
new file mode 100644
index 000000000..d96f9f28b
--- /dev/null
+++ b/meshd/src/prov.c
@@ -0,0 +1,722 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 "meshd/common/mesh-defs.h"
+
+#include "meshd/src/mesh-io.h"
+#include "meshd/src/display.h"
+#include "meshd/src/crypto.h"
+#include "meshd/src/net.h"
+#include "meshd/src/prov.h"
+
+#define PB_ADV_MTU	24
+
+#define DEFAULT_CONN_ID	0x00000000
+#define DEFAULT_PROV_MSG_NUM	0x00
+#define DEFAULT_DEV_MSG_NUM	0x80
+
+#define TX_TIMEOUT	30
+
+struct mesh_prov *mesh_prov_new(struct mesh_net *net, uint16_t remote)
+{
+	struct mesh_prov *prov;
+
+	prov = l_new(struct mesh_prov, 1);
+
+	prov->remote = remote;
+	prov->net = net;
+
+	return mesh_prov_ref(prov);
+}
+
+struct mesh_prov *mesh_prov_ref(struct mesh_prov *prov)
+{
+	if (!prov)
+		return NULL;
+
+	__sync_fetch_and_add(&prov->ref_count, 1);
+
+	return prov;
+}
+
+void mesh_prov_unref(struct mesh_prov *prov)
+{
+	struct mesh_io *io;
+	uint8_t type;
+
+	if (!prov)
+		return;
+
+	if (__sync_sub_and_fetch(&prov->ref_count, 1))
+		return;
+
+	io = mesh_net_get_io(prov->net);
+	type = MESH_AD_TYPE_BEACON;
+	mesh_io_send_cancel(io, &type, 1);
+	type = MESH_AD_TYPE_PROVISION;
+	mesh_io_send_cancel(io, &type, 1);
+	mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_PROV);
+	mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_BEACON);
+	l_timeout_remove(prov->tx_timeout);
+
+
+	l_info("Freed Prov Data");
+	l_free(prov);
+}
+
+static void packet_received(struct mesh_prov *prov, const void *data,
+						uint16_t size, uint8_t fcs)
+{
+	if (prov->receive_callback)
+		prov->receive_callback(data, size, prov);
+}
+
+static void send_open_req(struct mesh_prov *prov)
+{
+	struct mesh_io *io;
+	uint8_t open_req[23] = { MESH_AD_TYPE_PROVISION };
+	struct mesh_io_send_info info = {
+		.type = MESH_IO_TIMING_TYPE_GENERAL,
+		.u.gen.interval = 50,
+		.u.gen.cnt = 1,
+		.u.gen.min_delay = 0,
+		.u.gen.max_delay = 0,
+	};
+
+	if (!prov)
+		return;
+
+	io = mesh_net_get_io(prov->net);
+	if (!io)
+		return;
+
+	l_put_be32(prov->conn_id, open_req + 1);
+	open_req[1 + 4] = prov->local_msg_num = 0;
+	open_req[1 + 4 + 1] = 0x03; /* OPEN_REQ */
+	memcpy(open_req + 1 + 4 + 1 + 1, prov->uuid, 16);
+
+	/* print_packet("PB-TX", open_req + 1, sizeof(open_req) - 1); */
+	mesh_io_send_cancel(io, open_req, 1);
+	mesh_io_send(io, &info, open_req, sizeof(open_req));
+}
+
+static void send_open_cfm(struct mesh_prov *prov)
+{
+	struct mesh_io *io;
+	uint8_t open_cfm[7] = { MESH_AD_TYPE_PROVISION };
+	struct mesh_io_send_info info = {
+		.type = MESH_IO_TIMING_TYPE_GENERAL,
+		.u.gen.interval = 50,
+		.u.gen.cnt = 1,
+		.u.gen.min_delay = 0,
+		.u.gen.max_delay = 0,
+	};
+
+	if (!prov)
+		return;
+
+	io = mesh_net_get_io(prov->net);
+	if (!io)
+		return;
+
+	l_put_be32(prov->conn_id, open_cfm + 1);
+	open_cfm[1 + 4] = 0;
+	open_cfm[1 + 4 + 1] = 0x07; /* OPEN_CFM */
+
+	/* print_packet("PB-TX", open_cfm + 1, sizeof(open_cfm) - 1); */
+
+	mesh_io_send_cancel(io, open_cfm, 1);
+	mesh_io_send(io, &info, open_cfm, sizeof(open_cfm));
+}
+
+static void send_close_ind(struct mesh_prov *prov, uint8_t reason)
+{
+	uint8_t buf[8] = { MESH_AD_TYPE_PROVISION };
+	struct mesh_io *io;
+	struct mesh_io_send_info info = {
+		.type = MESH_IO_TIMING_TYPE_GENERAL,
+		.u.gen.interval = 50,
+		.u.gen.cnt = 3,
+		.u.gen.min_delay = 0,
+		.u.gen.max_delay = 0,
+	};
+
+	if (!prov)
+		return;
+
+	if (prov->bearer == MESH_BEARER_ADV) {
+		io = mesh_net_get_io(prov->net);
+		if (!io)
+			return;
+
+		l_put_be32(prov->conn_id, buf + 1);
+		buf[5] = 0;
+		buf[6] = 0x0B; /* CLOSE_IND */
+		buf[7] = reason;
+
+		/* print_packet("PB-TX", buf + 1, sizeof(buf) - 1); */
+
+		mesh_io_send_cancel(io, buf, 1);
+		mesh_io_send(io, &info, buf, sizeof(buf));
+	}
+
+	prov->bearer = MESH_BEARER_IDLE;
+}
+
+static void tx_timeout(struct l_timeout *timeout, void *user_data)
+{
+	struct mesh_prov *prov = user_data;
+	uint8_t cancel[] = { MESH_AD_TYPE_PROVISION };
+	struct mesh_io *io;
+
+	if (!prov)
+		return;
+
+	l_timeout_remove(prov->tx_timeout);
+	prov->tx_timeout = NULL;
+
+	io = mesh_net_get_io(prov->net);
+	if (!io)
+		return;
+
+	mesh_io_send_cancel(io, cancel, sizeof(cancel));
+
+	l_info("TX timeout");
+	mesh_prov_close(prov, 1);
+}
+
+static void send_adv_segs(struct mesh_prov *prov)
+{
+	struct mesh_io *io = mesh_net_get_io(prov->net);
+	struct mesh_io_send_info info = {
+		.type = MESH_IO_TIMING_TYPE_GENERAL,
+		.u.gen.interval = 50,
+		.u.gen.cnt = MESH_IO_TX_COUNT_UNLIMITED,
+		.u.gen.min_delay = 0,
+		.u.gen.max_delay = 0,
+	};
+	const void *data = prov->packet_buf;
+	uint16_t size = prov->packet_len;
+	uint16_t init_size;
+	uint8_t buf[1 + PB_ADV_MTU + 5] = { MESH_AD_TYPE_PROVISION };
+	uint8_t max_seg;
+	uint8_t consumed;
+	int i;
+
+	if (!size)
+		return;
+
+	mesh_io_send_cancel(io, buf, 1);
+
+	l_put_be32(prov->conn_id, buf + 1);
+	buf[1 + 4] = prov->local_msg_num;
+
+	if (size > PB_ADV_MTU - 4) {
+		max_seg = 1 +
+			(((size - (PB_ADV_MTU - 4)) - 1) / (PB_ADV_MTU - 1));
+		init_size = PB_ADV_MTU - 4;
+	} else {
+		max_seg = 0;
+		init_size = size;
+	}
+
+	/* print_packet("FULL-TX", data, size); */
+
+	l_debug("Sending %u fragments for %u octets", max_seg + 1, size);
+
+	buf[1 + 4 + 1] = max_seg << 2;
+	l_put_be16(size, buf + 1 + 4 + 1 + 1);
+	buf[9] = mesh_crypto_compute_fcs(data, size);
+	memcpy(buf + 1 + 4 + 1 + 1 + 2 + 1, data, init_size);
+
+	l_debug("max_seg: %2.2x", max_seg);
+	l_debug("size: %2.2x, CRC: %2.2x", size, buf[9]);
+
+	/* print_packet("PB-TX", buf + 1, init_size + 9); */
+	mesh_io_send(io, &info, buf, init_size + 10);
+
+	consumed = init_size;
+
+	for (i = 1; i <= max_seg; i++) {
+		uint8_t seg_size; /* Amount of payload data being sent */
+
+		if (size - consumed > PB_ADV_MTU - 1)
+			seg_size = PB_ADV_MTU - 1;
+		else
+			seg_size = size - consumed;
+
+		buf[6] = (i << 2) | 0x02;
+		memcpy(buf + 7, data + consumed, seg_size);
+
+		/* print_packet("PB-TX", buf + 1, seg_size + 6); */
+
+		mesh_io_send(io, &info, buf, seg_size + 7);
+
+		consumed += seg_size;
+	}
+}
+
+static void send_adv_msg(struct mesh_prov *prov, const void *data,
+								uint16_t size)
+{
+	l_timeout_remove(prov->tx_timeout);
+	prov->tx_timeout = l_timeout_create(TX_TIMEOUT, tx_timeout, prov, NULL);
+
+	memcpy(prov->packet_buf, data, size);
+	prov->packet_len = size;
+
+	send_adv_segs(prov);
+}
+
+static void send_ack(struct mesh_prov *prov)
+{
+	struct mesh_io *io = mesh_net_get_io(prov->net);
+	struct mesh_io_send_info info = {
+		.type = MESH_IO_TIMING_TYPE_GENERAL,
+		.u.gen.interval = 50,
+		.u.gen.cnt = 1,
+		.u.gen.min_delay = 0,
+		.u.gen.max_delay = 0,
+	};
+	uint8_t ack[7] = { MESH_AD_TYPE_PROVISION };
+
+	l_put_be32(prov->conn_id, ack + 1);
+	ack[1 + 4] = prov->last_peer_msg_num;
+	ack[1 + 4 + 1] = 0x01; /* ACK */
+
+	/* print_packet("ADV-ACK", ack + 1, sizeof(ack) - 1); */
+	mesh_io_send(io, &info, ack, sizeof(ack));
+}
+
+static void adv_data_pkt(uint8_t type, const void *pkt, uint8_t size,
+								void *user_data)
+{
+	const uint8_t *data = pkt;
+	struct mesh_prov *prov = user_data;
+	uint16_t offset = 0;
+
+	if ((type & 0x03) == 0x00) {
+		uint8_t last_seg = type >> 2;
+
+		prov->expected_len = l_get_be16(data);
+		prov->expected_fcs = l_get_u8(data + 2);
+
+		/* print_packet("Pkt", pkt, size); */
+		data += 3;
+		size -= 3;
+
+		prov->trans = MESH_TRANS_RX;
+
+		if (prov->expected_len > sizeof(prov->peer_buf)) {
+			l_info("Incoming pkt exceeds storage %d > %ld",
+				prov->expected_len, sizeof(prov->peer_buf));
+			return;
+		} else if (last_seg == 0)
+			prov->trans = MESH_TRANS_IDLE;
+
+		prov->expected_segs = 0xff >> (7 - last_seg);
+		prov->got_segs |= 1;
+		memcpy(prov->peer_buf, data, size);
+
+	} else if ((type & 0x03) == 0x02) {
+		offset = (PB_ADV_MTU - 4) + ((type >> 2) - 1) *
+							(PB_ADV_MTU - 1);
+
+		if (offset + size > prov->expected_len) {
+			l_info("Incoming pkt exceeds agreed len %d + %d > %d",
+					offset, size, prov->expected_len);
+			return;
+		}
+
+		prov->trans = MESH_TRANS_RX;
+
+		l_debug("Processing fragment %u", type & 0x3f);
+
+		prov->got_segs |= 1 << (type >> 2);
+		memcpy(prov->peer_buf + offset, data, size);
+
+	} else if (type == 0x01) {
+		if (prov->send_callback) {
+			void *data = prov->send_data;
+			mesh_prov_send_func_t cb = prov->send_callback;
+
+			prov->trans = MESH_TRANS_IDLE;
+			prov->send_callback = NULL;
+			prov->send_data = NULL;
+
+			cb(true, data);
+		}
+		return;
+	} else
+		return;
+
+	if (prov->got_segs != prov->expected_segs)
+		return;
+
+	/* Validate RXed packet and pass up to Provisioning */
+	if (!mesh_crypto_check_fcs(prov->peer_buf,
+				prov->expected_len,
+				prov->expected_fcs)) {
+		l_debug("Invalid FCS");
+		return;
+	}
+
+	prov->last_peer_msg_num = prov->peer_msg_num;
+	send_ack(prov);
+
+	prov->trans = MESH_TRANS_IDLE;
+
+	packet_received(prov, prov->peer_buf,
+			prov->expected_len, prov->expected_fcs);
+
+	/* Reset Re-Assembly for next packet */
+	prov->expected_len = sizeof(prov->peer_buf);
+	prov->expected_fcs = 0;
+	prov->expected_segs = 0;
+	prov->got_segs = 0;
+
+}
+
+static void adv_bearer_packet(void *user_data, struct mesh_io_recv_info *info,
+					const uint8_t *pkt, uint16_t len)
+{
+	struct mesh_prov *prov = user_data;
+	uint32_t conn_id;
+	uint8_t msg_num;
+	uint8_t type;
+
+	if (len < 6) {
+		l_info("  Too short packet");
+		return;
+	}
+
+	conn_id = l_get_be32(pkt + 1);
+	msg_num = l_get_u8(pkt + 1 + 4);
+	type = l_get_u8(pkt + 1 + 4 + 1);
+
+	/*if (prov->conn_id == conn_id) print_packet("ADV-RX", pkt, len); */
+
+	if (prov->conn_id != DEFAULT_CONN_ID) {
+		if (prov->conn_id != conn_id) {
+			l_debug("rxed unknown conn_id: %8.8x != %8.8x",
+							conn_id, prov->conn_id);
+			return;
+		}
+	} else if (type != 0x03)
+		return;
+
+	/* print_packet("PB-ADV-RX", pkt, len); */
+
+	/* Normalize pkt to start of PROV pkt payload */
+	pkt += 7;
+	len -= 7;
+
+	if (type == 0x07) { /* OPEN_CFM */
+		if (conn_id != prov->conn_id)
+			return;
+
+		if (msg_num != prov->local_msg_num)
+			return;
+
+		l_info("Link open confirmed");
+
+		prov->bearer = MESH_BEARER_ADV;
+		if (prov->open_callback)
+			prov->open_callback(prov->receive_data);
+	} else if (type == 0x01) {
+		if (conn_id != prov->conn_id)
+			return;
+
+		if (msg_num != prov->local_msg_num)
+			return;
+
+		l_debug("Got ACK %d", msg_num);
+		adv_data_pkt(type, pkt, len, user_data);
+	} else if (type == 0x03) {
+		/*
+		 * Ignore if:
+		 * 1. We are already provisioning
+		 * 2. We are not advertising that we are unprovisioned
+		 * 3. Open request not addressed to us
+		 */
+		if (prov->conn_id != DEFAULT_CONN_ID &&
+				prov->conn_id != conn_id)
+			return;
+
+		if (prov->local_msg_num != (DEFAULT_DEV_MSG_NUM - 1))
+			return;
+
+		if (memcmp(pkt, prov->uuid, 16))
+			return;
+
+		l_info("Link open request");
+
+		prov->last_peer_msg_num = 0xFF;
+		prov->bearer = MESH_BEARER_ADV;
+		if (prov->open_callback && prov->conn_id == DEFAULT_CONN_ID)
+			prov->open_callback(prov->receive_data);
+
+		prov->conn_id = conn_id;
+		prov->peer_msg_num = msg_num;
+		send_open_cfm(prov);
+	} else if (type == 0x0B) {
+		if (prov->conn_id != conn_id)
+			return;
+
+		prov->conn_id = DEFAULT_CONN_ID;
+		prov->local_msg_num = 0xFF;
+		prov->peer_msg_num = 0xFF;
+		prov->last_peer_msg_num = 0xFF;
+
+		l_timeout_remove(prov->tx_timeout);
+		prov->tx_timeout = NULL;
+
+		l_info("Link closed notification: %2.2x", pkt[0]);
+
+		if (prov->close_callback)
+			prov->close_callback(prov->receive_data, pkt[0]);
+	} else if ((type & 0x03) == 0x00) {
+		if (prov->conn_id != conn_id)
+			return;
+
+		if (msg_num == prov->last_peer_msg_num) {
+			send_ack(prov);
+			return;
+		}
+
+		prov->peer_msg_num = msg_num;
+
+		l_debug("Processing Data with %u fragments,%d octets",
+						type >> 2, l_get_be16(pkt));
+		adv_data_pkt(type, pkt, len, user_data);
+
+	} else if ((type & 0x03) == 0x02) {
+		if (prov->conn_id != conn_id)
+			return;
+
+		if (msg_num == prov->last_peer_msg_num) {
+			send_ack(prov);
+			return;
+		}
+
+		prov->peer_msg_num = msg_num;
+
+		l_debug("Processing fragment %u", type >> 2);
+		adv_data_pkt(type, pkt, len, user_data);
+	}
+}
+
+static void beacon_packet(void *user_data, struct mesh_io_recv_info *info,
+					const uint8_t *pkt, uint16_t len)
+{
+	struct mesh_prov *prov = user_data;
+	struct mesh_io *io;
+
+	pkt++;
+	len--;
+
+	if (len < 19)
+		return;
+
+	if (!pkt[0])
+		print_packet("UnProv-BEACON-RX", pkt, len);
+
+	/* Ignore devices not matching UUID */
+	if (pkt[0] || memcmp(pkt + 1, prov->uuid, 16))
+		return;
+
+	io = mesh_net_get_io(prov->net);
+	mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_BEACON);
+
+	if ((prov->conn_id != DEFAULT_CONN_ID) ||
+			(prov->bearer != MESH_BEARER_IDLE)) {
+		l_info("PB-ADV: Already Provisioning");
+		return;
+	}
+
+	l_getrandom(&prov->conn_id, sizeof(prov->conn_id));
+	prov->bearer = MESH_BEARER_ADV;
+	send_open_req(prov);
+}
+
+static bool mesh_prov_enable(struct mesh_prov *prov, enum mesh_prov_mode mode,
+							uint8_t uuid[16])
+{
+	const uint8_t pb_adv_data[] = { MESH_AD_TYPE_BEACON, 0 };
+	uint8_t adv_data[62];
+	uint8_t adv_len, type;
+	struct mesh_io *io;
+	struct mesh_io_send_info tx_info = {
+		.type = MESH_IO_TIMING_TYPE_GENERAL,
+		.u.gen.interval = 1000,	/* ms */
+		.u.gen.cnt = 0,		/* 0 == Infinite */
+		.u.gen.min_delay = 0,	/* no delay */
+		.u.gen.max_delay = 0,	/* no delay */
+	};
+
+	if (!prov || !prov->net)
+		return false;
+
+
+	prov->mode = mode;
+	memcpy(prov->uuid, uuid, 16);
+	prov->conn_id = DEFAULT_CONN_ID;
+	io = mesh_net_get_io(prov->net);
+
+	switch (mode) {
+	case MESH_PROV_MODE_NONE:
+		break;
+	case MESH_PROV_MODE_INITIATOR:
+		print_packet("Searching for uuid", uuid, 16);
+		prov->local_msg_num = DEFAULT_PROV_MSG_NUM;
+		prov->peer_msg_num = DEFAULT_DEV_MSG_NUM;
+		mesh_io_register_recv_cb(io, MESH_IO_FILTER_PROV,
+						adv_bearer_packet, prov);
+		mesh_io_register_recv_cb(io, MESH_IO_FILTER_BEACON,
+						beacon_packet, prov);
+		break;
+
+	case MESH_PROV_MODE_ADV_ACCEPTOR:
+		prov->local_msg_num = DEFAULT_DEV_MSG_NUM - 1;
+		prov->peer_msg_num = DEFAULT_PROV_MSG_NUM;
+
+		print_packet("Beaconing as unProvisioned uuid", uuid, 16);
+		adv_len = sizeof(pb_adv_data);
+		memcpy(adv_data, pb_adv_data, adv_len);
+		memcpy(adv_data + adv_len, uuid, 16);
+		adv_len += 16;
+		adv_len += 2;
+		mesh_io_register_recv_cb(io, MESH_IO_FILTER_PROV,
+						adv_bearer_packet, prov);
+		type = MESH_AD_TYPE_BEACON;
+		mesh_io_send_cancel(io, &type, 1);
+		mesh_io_send(io, &tx_info, adv_data, adv_len);
+		break;
+
+	case MESH_PROV_MODE_GATT_CLIENT:
+	case MESH_PROV_MODE_MESH_GATT_CLIENT:
+	case MESH_PROV_MODE_GATT_ACCEPTOR:
+	case MESH_PROV_MODE_MESH_SERVER:
+	case MESH_PROV_MODE_MESH_CLIENT:
+	default:
+		l_error("Unimplemented Prov Mode: %d", mode);
+		break;
+	}
+
+	return true;
+}
+
+bool mesh_prov_listen(struct mesh_net *net, uint8_t uuid[16], uint8_t caps[12],
+					mesh_prov_open_func_t open_callback,
+					mesh_prov_close_func_t close_callback,
+					mesh_prov_receive_func_t recv_callback,
+					void *user_data)
+{
+	struct mesh_prov *prov = mesh_net_get_prov(net);
+
+	if (!prov) {
+		prov = mesh_prov_new(net, 0);
+		if (!prov)
+			return false;
+
+		mesh_net_set_prov(net, prov);
+	}
+
+	prov->open_callback = open_callback;
+	prov->close_callback = close_callback;
+	prov->receive_callback = recv_callback;
+	prov->receive_data = prov; /* TODO: retink the callback placement */
+	memcpy(prov->caps, caps, sizeof(prov->caps));
+
+	prov->trans = MESH_TRANS_IDLE;
+
+
+	return mesh_prov_enable(prov, MESH_PROV_MODE_ADV_ACCEPTOR, uuid);
+}
+
+unsigned int mesh_prov_send(struct mesh_prov *prov,
+					const void *ptr, uint16_t size,
+					mesh_prov_send_func_t send_callback,
+					void *user_data)
+{
+	const uint8_t *data = ptr;
+
+	if (!prov)
+		return 0;
+
+	if (prov->trans != MESH_TRANS_IDLE)
+		return 0;
+
+	if (prov->remote) {
+		/* TODO -- PB-Remote */
+	} else {
+		prov->send_callback = send_callback;
+		prov->send_data = user_data;
+		prov->trans = MESH_TRANS_TX;
+		prov->local_msg_num++;
+		send_adv_msg(prov, data, size);
+	}
+
+	return 1;
+}
+
+bool mesh_prov_close(struct mesh_prov *prov, uint8_t reason)
+{
+	if (!prov)
+		return false;
+
+	prov->local_msg_num = 0;
+	send_close_ind(prov, reason);
+
+	prov->conn_id = DEFAULT_CONN_ID;
+	prov->local_msg_num = 0xFF;
+	prov->peer_msg_num = 0xFF;
+	prov->last_peer_msg_num = 0xFF;
+
+	if (prov->tx_timeout) {
+		l_timeout_remove(prov->tx_timeout);
+
+		/* If timing out, give Close indication 1 second of
+		 * provisioning timing to get final Close indication out
+		 */
+		prov->tx_timeout = l_timeout_create(1, tx_timeout, prov, NULL);
+	}
+
+	if (prov->close_callback)
+		prov->close_callback(prov->receive_data, reason);
+
+	return false;
+}
+
+void mesh_prov_set_addr(struct mesh_prov *prov, uint16_t addr)
+{
+	prov->addr = addr;
+}
+
+uint16_t mesh_prov_get_idx(struct mesh_prov *prov)
+{
+	return prov->net_idx;
+}
diff --git a/meshd/src/provision.c b/meshd/src/provision.c
new file mode 100644
index 000000000..18e5b3398
--- /dev/null
+++ b/meshd/src/provision.c
@@ -0,0 +1,1159 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <time.h>
+#include <ell/ell.h>
+
+#include "meshd/common/mesh-defs.h"
+#include "src/shared/ecc.h"
+
+#include "meshd/src/display.h"
+#include "meshd/src/crypto.h"
+#include "meshd/src/net.h"
+#include "meshd/src/prov.h"
+#include "meshd/src/provision.h"
+#include "meshd/src/node.h"
+
+#define PROV_INVITE	0x00
+#define PROV_CAPS	0x01
+#define PROV_START	0x02
+#define PROV_PUB_KEY	0x03
+#define PROV_INP_CMPLT	0x04
+#define PROV_CONFIRM	0x05
+#define PROV_RANDOM	0x06
+#define PROV_DATA	0x07
+#define PROV_COMPLETE	0x08
+#define PROV_FAILED	0x09
+
+#define PROV_ERR_INVALID_PDU		0x01
+#define PROV_ERR_INVALID_FORMAT		0x02
+#define PROV_ERR_UNEXPECTED_PDU		0x03
+#define PROV_ERR_CONFIRM_FAILED		0x04
+#define PROV_ERR_INSUF_RESOURCE		0x05
+#define PROV_ERR_DECRYPT_FAILED		0x06
+#define PROV_ERR_UNEXPECTED_ERR		0x07
+#define PROV_ERR_CANT_ASSIGN_ADDR	0x08
+
+/* Expected Provisioning PDU sizes */
+static const uint16_t expected_pdu_size[] = {
+	1 + 1,					/* PROV_INVITE */
+	1 + 1 + 2 + 1 + 1 + 1 + 2 + 1 + 2,	/* PROV_CAPS */
+	1 + 1 + 1 + 1 + 1 + 1,			/* PROV_START */
+	1 + 64,					/* PROV_PUB_KEY */
+	1,					/* PROV_INP_CMPLT */
+	1 + 16,					/* PROV_CONFIRM */
+	1 + 16,					/* PROV_RANDOM */
+	1 + 16 + 2 + 1 + 4 + 2 + 8,		/* PROV_DATA */
+	1,					/* PROV_COMPLETE */
+	1 + 1,					/* PROV_FAILED */
+};
+
+static enum {
+	PUB_KEY_TYPE_ephemeral,
+	PUB_KEY_TYPE_available,
+} pub_key_type = PUB_KEY_TYPE_ephemeral;
+
+static enum {
+	AUTH_TYPE_3a,
+	AUTH_TYPE_3b,
+	AUTH_TYPE_3c,
+} prov_auth_type = AUTH_TYPE_3c;
+
+enum {
+	INT_PROV_IDLE,
+	INT_PROV_INVITE_SENT,
+	INT_PROV_INVITE_ACKED,
+	INT_PROV_START_SENT,
+	INT_PROV_START_ACKED,
+	INT_PROV_KEY_SENT,
+	INT_PROV_KEY_ACKED,
+	INT_PROV_CONF_SENT,
+	INT_PROV_CONF_ACKED,
+	INT_PROV_RAND_SENT,
+	INT_PROV_RAND_ACKED,
+	INT_PROV_DATA_SENT,
+	INT_PROV_DATA_ACKED,
+} int_prov_state = INT_PROV_IDLE;
+
+enum {
+	ACP_PROV_IDLE,
+	ACP_PROV_CAPS_SENT,
+	ACP_PROV_CAPS_ACKED,
+	ACP_PROV_KEY_SENT,
+	ACP_PROV_KEY_ACKED,
+	ACP_PROV_INP_CMPLT_SENT,
+	ACP_PROV_INP_CMPLT_ACKED,
+	ACP_PROV_CONF_SENT,
+	ACP_PROV_CONF_ACKED,
+	ACP_PROV_RAND_SENT,
+	ACP_PROV_RAND_ACKED,
+	ACP_PROV_CMPLT_SENT,
+	ACP_PROV_FAIL_SENT,
+} acp_prov_state = ACP_PROV_IDLE;
+
+static uint8_t prov_expected;
+static int8_t prov_last = -1;
+
+static void int_prov_send_cmplt(bool success, struct mesh_prov *prov);
+static void acp_prov_send_cmplt(bool success, struct mesh_prov *prov);
+
+static void swap_u256_bytes(uint8_t *u256)
+{
+	int i;
+
+	/* End-to-End byte reflection of 32 octet buffer */
+	for (i = 0; i < 16; i++) {
+		u256[i] ^= u256[31 - i];
+		u256[31 - i] ^= u256[i];
+		u256[i] ^= u256[31 - i];
+	}
+}
+
+static uint8_t u16_highest_bit(uint16_t mask)
+{
+	uint8_t cnt = 0;
+
+	if (!mask)
+		return 0xff;
+
+	while (mask & 0xfffe) {
+		cnt++;
+		mask >>= 1;
+	}
+
+	return cnt;
+}
+
+static void send_prov_start(struct mesh_prov *prov)
+{
+	struct mesh_net *net = prov->net;
+	struct mesh_net_prov_caps *caps = mesh_net_prov_caps_get(net);
+	uint8_t prov_start[6] = { PROV_START };
+
+	memset(prov_start + 1, 0, 1 + 1 + 1 + 1 + 1);
+	if (!(caps->algorithms & 0x0001)) {
+		/* We only support FIPS P-256 Elliptic Curve */
+		l_error("Unrecognized Algorithm %4.4x", caps->algorithms);
+		return;
+	}
+
+	if (caps->pub_type) {
+		/* Prov Step 2b: New device exposed PublicKey OOB */
+		prov_start[2] = 0x01;
+		pub_key_type = PUB_KEY_TYPE_available;
+	} else {
+		pub_key_type = PUB_KEY_TYPE_ephemeral;
+	}
+
+	if (caps->output_size &&
+			caps->output_action) {
+		/* Prov Step 3a: Output OOB used */
+		prov_start[3] = 0x02;
+		prov_start[4] = u16_highest_bit(caps->output_action);
+		prov_start[5] = caps->output_size > 8 ?
+			8 : caps->output_size;
+
+		prov_auth_type = AUTH_TYPE_3a;
+
+	} else if (caps->input_size &&
+			caps->input_action) {
+		/* Prov Step 3b: Input OOB used */
+		prov_start[3] = 0x03;
+		prov_start[4] = u16_highest_bit(caps->input_action);
+		prov_start[5] = caps->input_size > 8 ?
+			8 : caps->input_size;
+
+		prov_auth_type = AUTH_TYPE_3b;
+
+	} else  {
+		if (caps->static_type)
+			prov_start[3] = 0x01;
+
+		/* Prov Step 3c: Static OOB used (or no OOB available) */
+		prov_auth_type = AUTH_TYPE_3c;
+	}
+
+	memcpy(&prov->conf_inputs.start, prov_start + 1,
+					sizeof(prov->conf_inputs.start));
+
+	int_prov_state = INT_PROV_START_SENT;
+	if (pub_key_type == PUB_KEY_TYPE_ephemeral)
+		prov_expected = PROV_PUB_KEY;
+	else if (prov_auth_type == AUTH_TYPE_3b)
+		prov_expected = PROV_INP_CMPLT;
+	else
+		prov_expected = PROV_CONFIRM;
+
+	mesh_prov_send(prov, prov_start, 6,
+			int_prov_send_cmplt, prov);
+
+}
+
+static void calculate_secrets(struct mesh_prov *prov, bool initiator)
+{
+	struct mesh_net *net = prov->net;
+	uint8_t *priv_key = mesh_net_priv_key_get(net);
+	bool test_mode = mesh_net_test_mode(net);
+	uint8_t tmp[64];
+
+	if (initiator) {
+		memcpy(prov->conf_inputs.prv_pub_key,
+					prov->l_public, sizeof(prov->l_public));
+		memcpy(prov->conf_inputs.dev_pub_key,
+					prov->r_public, sizeof(prov->r_public));
+	} else {
+		memcpy(prov->conf_inputs.prv_pub_key,
+					prov->r_public, sizeof(prov->r_public));
+		memcpy(prov->conf_inputs.dev_pub_key,
+					prov->l_public, sizeof(prov->l_public));
+	}
+
+	/* Convert to Mesh byte order */
+	memcpy(tmp, prov->r_public, 64);
+	swap_u256_bytes(tmp);
+	swap_u256_bytes(tmp + 32);
+
+	ecdh_shared_secret(tmp, priv_key, prov->secret);
+
+	/* Convert to Mesh byte order */
+	swap_u256_bytes(prov->secret);
+
+	mesh_crypto_s1(&prov->conf_inputs,
+			sizeof(prov->conf_inputs), prov->conf_salt);
+
+
+	mesh_crypto_prov_conf_key(prov->secret, prov->conf_salt,
+							prov->conf_key);
+
+	if (test_mode) {
+		print_packet("PublicKeyRemote", prov->r_public, 64);
+		print_packet("PublicKeyLocal", prov->l_public, 64);
+		print_packet("PrivateKeyLocal", priv_key, 32);
+		print_packet("ConfirmationInputs", &prov->conf_inputs,
+						sizeof(prov->conf_inputs));
+		print_packet("ECDHSecret", prov->secret,
+						sizeof(prov->secret));
+		print_packet("ConfirmationSalt", prov->conf_salt, 16);
+		print_packet("ConfirmationKey", prov->conf_key,
+						sizeof(prov->conf_key));
+	}
+}
+
+static void send_prov_key(struct mesh_prov *prov,
+					mesh_prov_send_func_t send_callback)
+{
+	uint8_t send_pub_key[65] = { PROV_PUB_KEY };
+
+	memcpy(send_pub_key + 1, prov->l_public, 64);
+	mesh_prov_send(prov, send_pub_key, 65,
+			send_callback, prov);
+}
+
+static void send_prov_data(struct mesh_prov *prov)
+{
+	struct mesh_net *net = prov->net;
+	struct mesh_net_prov_caps *caps = mesh_net_prov_caps_get(net);
+	uint64_t mic;
+	uint32_t iv_index;
+	uint8_t snb_flags;
+	uint16_t net_idx = mesh_prov_get_idx(prov);
+	uint8_t prov_data[1 + 16 + 2 + 1 + 4 + 2 + sizeof(mic)] = { PROV_DATA };
+	uint16_t uni_addr = mesh_net_prov_uni(net, caps->num_ele);
+	bool test_mode = mesh_net_test_mode(net);
+
+	/* Calculate Provisioning Data */
+	prov_expected = PROV_COMPLETE;
+	mesh_net_get_snb_state(net, &snb_flags, &iv_index);
+
+	mesh_net_get_key(net, !!(snb_flags & 0x01), net_idx, prov_data + 1);
+	l_put_be16(net_idx, prov_data + 1 + 16);
+	l_put_u8(snb_flags, prov_data + 1 + 16 + 2);
+	l_put_be32(iv_index, prov_data + 1 + 16 + 2 + 1);
+	l_put_be16(uni_addr, prov_data + 1 + 16 + 2 + 1 + 4);
+
+	if (test_mode)
+		print_packet("Data", prov_data + 1, 16 + 2 + 1 + 4 + 2);
+
+	mesh_crypto_device_key(prov->secret, prov->prov_salt, prov->dev_key);
+	if (test_mode) {
+		print_packet("DevKey", prov->dev_key, 16);
+		print_packet("NetworkKey", prov_data + 1, 16);
+		print_packet("NetworkKey Index", prov_data + 1 + 16, 2);
+		print_packet("SNB Flags", prov_data + 1 + 16 + 2, 1);
+		print_packet("IVindex", prov_data + 1 + 16 + 2 + 1, 4);
+		print_packet("Unicast Addr", prov_data + 1 + 16 + 2 + 1 + 4, 2);
+	}
+
+	mesh_crypto_aes_ccm_encrypt(prov->s_nonce, prov->s_key,
+					NULL, 0,
+					&prov_data[1],
+					sizeof(prov_data) - 1 - sizeof(mic),
+					&prov_data[1],
+					&mic, sizeof(mic));
+	if (test_mode)
+		print_packet("DataEncrypted + mic", prov_data + 1,
+						sizeof(prov_data) - 1);
+
+	int_prov_state = INT_PROV_DATA_SENT;
+	mesh_prov_send(prov, prov_data, sizeof(prov_data),
+			int_prov_send_cmplt, prov);
+	mesh_prov_set_addr(prov, uni_addr);
+}
+
+static void send_prov_conf(struct mesh_prov *prov,
+			mesh_prov_send_func_t send_callback)
+{
+	struct mesh_net *net = prov->net;
+	uint8_t *test_rand = mesh_net_prov_rand(net);
+	uint8_t prov_conf[1 + sizeof(prov->conf)] = { PROV_CONFIRM };
+	bool test_mode = mesh_net_test_mode(net);
+
+	if (test_mode && test_rand[0])
+		memcpy(prov->rand_auth, test_rand, 16);
+	else
+		l_getrandom(prov->rand_auth, 16);
+
+	/* Calculate Confirmation */
+	mesh_crypto_aes_cmac(prov->conf_key, prov->rand_auth,
+				sizeof(prov->rand_auth), prov->conf);
+
+	/* Marshal Confirmation */
+	memcpy(prov_conf + 1, prov->conf, sizeof(prov->conf));
+
+	if (test_mode) {
+		print_packet("ConfirmationKey", prov->conf_key,
+						sizeof(prov->conf_key));
+		print_packet("RandomAuthValue", prov->rand_auth,
+						sizeof(prov->rand_auth));
+		print_packet("Sending Confirmation", prov->conf,
+						sizeof(prov->conf));
+	}
+
+	mesh_prov_send(prov, prov_conf, sizeof(prov_conf),
+					send_callback, prov);
+}
+
+static void send_prov_rand(struct mesh_prov *prov,
+			mesh_prov_send_func_t send_callback)
+{
+	struct mesh_net *net = prov->net;
+	uint8_t prov_rand[17] = { PROV_RANDOM };
+	bool test_mode = mesh_net_test_mode(net);
+
+	/* Marshal Random */
+	memcpy(prov_rand + 1, prov->rand_auth, 16);
+
+	if (test_mode)
+		print_packet("Sending Random", prov->rand_auth, 16);
+
+	mesh_prov_send(prov, prov_rand, sizeof(prov_rand),
+					send_callback, prov);
+}
+
+enum inputType {
+	INP_key,
+	INP_dec,
+	INP_text,
+};
+
+struct input_data {
+	struct mesh_prov *prov;
+	enum inputType type;
+	bool initiator;
+	void *dest;
+	void *user_data;
+	union {
+		struct {
+			uint8_t idx;
+			char data[129];
+		} key;
+		struct {
+			uint64_t value;
+		} dec;
+		struct {
+			uint8_t idx;
+			char str[16];
+		} text;
+	} u;
+};
+
+static void collectInput(struct mesh_prov *prov, char *prompt,
+					enum inputType type, bool initiator,
+					void *dest, void *user_data)
+{
+	struct input_data *inp = l_new(struct input_data, 1);
+
+	inp->prov = prov;
+	inp->type = type;
+	inp->dest = dest;
+	inp->initiator = initiator;
+	inp->user_data = user_data;
+
+	if (prompt)
+		l_info("%s", prompt);
+
+	/* TODO: Request agent get OOB data */
+}
+
+static uint32_t digit_mod(uint8_t power)
+{
+	uint32_t ret = 1;
+
+	while (power--)
+		ret *= 10;
+
+	return ret;
+}
+
+static char *key_type(uint8_t type)
+{
+	switch (type) {
+	case 0x01:
+		return "QR-Code";
+	case 0x02:
+		return "Barcode";
+	case 0x03:
+		return "NFC Tag";
+	case 0x04:
+		return "Printed Number";
+	default:
+		return "unknown Source";
+	}
+}
+
+static void int_prov_send_cmplt(bool success, struct mesh_prov *prov)
+{
+	struct mesh_net *net = prov->net;
+	struct mesh_net_prov_caps *caps = mesh_net_prov_caps_get(net);
+
+	l_debug("Provision sending complete");
+
+	switch (int_prov_state) {
+	case INT_PROV_INVITE_SENT:
+		int_prov_state = INT_PROV_INVITE_ACKED;
+		if (acp_prov_state == ACP_PROV_CAPS_SENT)
+			send_prov_start(prov);
+		break;
+	case INT_PROV_START_SENT:
+		int_prov_state = INT_PROV_START_ACKED;
+		if (pub_key_type == PUB_KEY_TYPE_ephemeral) {
+			int_prov_state = INT_PROV_KEY_SENT;
+			send_prov_key(prov, int_prov_send_cmplt);
+		} else {
+			collectInput(prov, NULL, INP_key, true,
+						prov->r_public, prov);
+			l_info("\n\nEnter key from %s:\n",
+				key_type(caps->pub_type));
+		}
+		break;
+	case INT_PROV_KEY_SENT:
+		int_prov_state = INT_PROV_KEY_ACKED;
+		if (pub_key_type == PUB_KEY_TYPE_ephemeral) {
+			prov_expected = PROV_PUB_KEY;
+			break;
+		}
+
+		/* Start Step 3 */
+		memset(prov->rand_auth + 16, 0, 16);
+		if (prov_auth_type == AUTH_TYPE_3a)
+			collectInput(prov,
+				"\n\nEnter prompted number from device:",
+				INP_dec, true,
+				prov->rand_auth + 32 - sizeof(uint32_t),
+				prov);
+
+		else if (prov_auth_type == AUTH_TYPE_3b) {
+			uint32_t oob_key;
+
+			l_getrandom(&oob_key, sizeof(uint32_t));
+			oob_key %= digit_mod(caps->input_size);
+			l_put_be32(oob_key,
+					prov->rand_auth + 32 -
+					sizeof(uint32_t));
+			l_info("\n\nEnter %d on Device\n", oob_key);
+			prov_expected = PROV_INP_CMPLT;
+
+		} else if (caps->static_type) {
+			collectInput(prov, NULL, INP_text, true,
+					prov->rand_auth + 16, prov);
+			l_info("\n\nstatic OOB str from %s:\n",
+				key_type(caps->static_type));
+
+		} else {
+			int_prov_state = INT_PROV_CONF_SENT;
+			send_prov_conf(prov, int_prov_send_cmplt);
+		}
+
+		break;
+	case INT_PROV_CONF_SENT:
+		int_prov_state = INT_PROV_CONF_ACKED;
+		if (acp_prov_state == ACP_PROV_CONF_SENT) {
+			int_prov_state = INT_PROV_RAND_SENT;
+			prov_expected = PROV_RANDOM;
+			send_prov_rand(prov, int_prov_send_cmplt);
+		}
+		break;
+	case INT_PROV_RAND_SENT:
+		int_prov_state = INT_PROV_RAND_ACKED;
+		if (acp_prov_state == ACP_PROV_RAND_SENT)
+			send_prov_data(prov);
+		break;
+	case INT_PROV_DATA_SENT:
+		int_prov_state = INT_PROV_DATA_ACKED;
+		break;
+	default:
+	case INT_PROV_INVITE_ACKED:
+	case INT_PROV_START_ACKED:
+	case INT_PROV_KEY_ACKED:
+	case INT_PROV_CONF_ACKED:
+	case INT_PROV_RAND_ACKED:
+	case INT_PROV_DATA_ACKED:
+	case INT_PROV_IDLE:
+		break;
+	}
+}
+
+void initiator_prov_open(struct mesh_prov *prov)
+{
+	uint8_t invite[] = { PROV_INVITE, 30 };
+	uint8_t *priv_key;
+
+	l_info("Provisioning link opened");
+
+	priv_key = mesh_net_priv_key_get(prov->net);
+	ecc_make_key(prov->l_public, priv_key);
+
+	int_prov_state = INT_PROV_INVITE_SENT;
+	prov_expected = PROV_CAPS;
+	prov_last = -1;
+	prov->conf_inputs.invite.attention = invite[1];
+	mesh_prov_send(prov, invite, sizeof(invite),
+					int_prov_send_cmplt, prov);
+}
+
+void initiator_prov_close(struct mesh_prov *prov, uint8_t reason)
+{
+	struct mesh_net *net = prov->net;
+	uint32_t iv_index;
+	uint8_t snb_flags;
+
+	l_info("Provisioning link closed");
+
+	/* Get the provisioned node's composition data*/
+	if (reason == 0) {
+		mesh_net_get_snb_state(net, &snb_flags, &iv_index);
+
+		l_info("Save provisioner's DB");
+	}
+}
+
+void initiator_prov_receive(const void *pkt, uint16_t size,
+							struct mesh_prov *prov)
+{
+	struct mesh_net *net = prov->net;
+	struct mesh_net_prov_caps *caps = mesh_net_prov_caps_get(net);
+	bool test_mode = mesh_net_test_mode(net);
+	const uint8_t *data = pkt;
+	uint8_t tmp[16];
+	uint8_t type = *data++;
+	uint8_t err = 0;
+
+
+	l_debug("Provisioning packet received type: %2.2x (%u octets)",
+								type, size);
+
+	if (type == prov_last) {
+		l_error("Ignore repeated %2.2x packet", type);
+		return;
+	} else if ((type > prov_expected || type < prov_last) &&
+						type != PROV_FAILED) {
+		l_error("Expected %2.2x, Got:%2.2x", prov_expected, type);
+		err = PROV_ERR_UNEXPECTED_PDU;
+		goto failure;
+	}
+
+	if (type >= L_ARRAY_SIZE(expected_pdu_size) ||
+					size != expected_pdu_size[type]) {
+		l_error("Expected PDU size %d, Got %d (type: %2.2x)",
+					expected_pdu_size[type], size, type);
+		err = PROV_ERR_INVALID_FORMAT;
+		goto failure;
+	}
+
+	prov_last = type;
+
+	switch (type) {
+	case PROV_CAPS: /* Capabilities */
+		int_prov_state = INT_PROV_INVITE_ACKED;
+		acp_prov_state = ACP_PROV_CAPS_SENT;
+		caps->num_ele = data[0];
+		if (test_mode)
+			l_info("Got Num Ele %d", data[0]);
+
+		caps->algorithms = l_get_be16(data + 1);
+		if (test_mode)
+			l_info("Got alg %d", caps->algorithms);
+
+		caps->pub_type = data[3];
+		if (test_mode)
+			l_info("Got pub_type %d", data[3]);
+
+		caps->static_type = data[4];
+		if (test_mode)
+			l_info("Got static_type %d", data[4]);
+
+		caps->output_size = data[5];
+		if (test_mode)
+			l_info("Got output_size %d", data[5]);
+
+		caps->output_action = l_get_be16(data + 6);
+		if (test_mode)
+			l_info("Got output_action %d", l_get_be16(data + 6));
+
+		caps->input_size = data[8];
+		if (test_mode)
+			l_info("Got input_size %d", data[8]);
+
+		caps->input_action = l_get_be16(data + 9);
+		if (test_mode)
+			l_info("Got input_action %d", l_get_be16(data + 9));
+
+		if (caps->algorithms != 0x0001) {
+			l_error("Unsupported Algorithm");
+			err = PROV_ERR_INVALID_FORMAT;
+			goto failure;
+		}
+
+		memcpy(&prov->conf_inputs.caps, data, 11);
+
+		if (int_prov_state == INT_PROV_INVITE_ACKED)
+			send_prov_start(prov);
+		break;
+
+	case PROV_PUB_KEY: /* Public Key */
+		int_prov_state = INT_PROV_KEY_ACKED;
+		acp_prov_state = ACP_PROV_KEY_SENT;
+		memcpy(prov->r_public, data, 64);
+		calculate_secrets(prov, true);
+		prov_expected = PROV_CONFIRM;
+
+		memset(prov->rand_auth + 16, 0, 16);
+		if (prov_auth_type == AUTH_TYPE_3a) {
+			collectInput(prov,
+				"\n\nEnter number from device:",
+				INP_dec, true,
+				prov->rand_auth + 32 - sizeof(uint32_t),
+				prov);
+
+		} else if (prov_auth_type == AUTH_TYPE_3b) {
+
+			uint32_t oob_key;
+
+			l_getrandom(&oob_key, sizeof(uint32_t));
+			oob_key %= digit_mod(caps->input_size);
+			l_put_be32(oob_key,
+					prov->rand_auth + 32 -
+					sizeof(uint32_t));
+			l_info("\n\nEnter %d on Device\n", oob_key);
+			prov_expected = PROV_INP_CMPLT;
+
+		} else if (caps->static_type) {
+			collectInput(prov, NULL, INP_dec, true,
+					prov->rand_auth + 16, prov);
+			l_info("\n\nstatic OOB str from %s:\n",
+				key_type(caps->static_type));
+
+		} else
+			send_prov_conf(prov, int_prov_send_cmplt);
+		break;
+
+	case PROV_INP_CMPLT: /* Provisioning Input Complete */
+		acp_prov_state = ACP_PROV_INP_CMPLT_SENT;
+		prov_expected = PROV_CONFIRM;
+		send_prov_conf(prov, int_prov_send_cmplt);
+		break;
+
+	case PROV_CONFIRM: /* Confirmation */
+		int_prov_state = INT_PROV_CONF_ACKED;
+		acp_prov_state = ACP_PROV_CONF_SENT;
+		/* RXed Device Confirmation */
+		memcpy(prov->conf, data, sizeof(prov->conf));
+		if (test_mode)
+			print_packet("ConfirmationDevice", prov->conf,
+							sizeof(prov->conf));
+
+		if (int_prov_state == INT_PROV_CONF_ACKED) {
+			prov_expected = PROV_RANDOM;
+			send_prov_rand(prov, int_prov_send_cmplt);
+		}
+		break;
+
+	case PROV_RANDOM: /* Random */
+		int_prov_state = INT_PROV_RAND_ACKED;
+		acp_prov_state = ACP_PROV_RAND_SENT;
+
+		/* Calculate SessionKey while the data is fresh */
+		mesh_crypto_prov_prov_salt(prov->conf_salt,
+						prov->rand_auth, data,
+						prov->prov_salt);
+		mesh_crypto_session_key(prov->secret, prov->prov_salt,
+							prov->s_key);
+		mesh_crypto_nonce(prov->secret, prov->prov_salt, prov->s_nonce);
+		if (test_mode) {
+			print_packet("SessionKey", prov->s_key,
+					sizeof(prov->s_key));
+			print_packet("Nonce", prov->s_nonce,
+					sizeof(prov->s_nonce));
+		}
+
+		/* RXed Device Confirmation */
+		memcpy(prov->rand_auth, data, sizeof(prov->conf));
+		if (test_mode)
+			print_packet("RandomDevice", prov->rand_auth, 16);
+
+		mesh_crypto_aes_cmac(prov->conf_key, prov->rand_auth,
+					sizeof(prov->rand_auth), tmp);
+
+		if (memcmp(tmp, prov->conf, sizeof(prov->conf))) {
+			l_error("Provisioning Failed-Confirm compare)");
+			err = PROV_ERR_CONFIRM_FAILED;
+			goto failure;
+		}
+
+		if (int_prov_state == INT_PROV_RAND_ACKED) {
+			prov_expected = PROV_COMPLETE;
+			send_prov_data(prov);
+		}
+		break;
+
+	case PROV_COMPLETE: /* Complete */
+		l_info("Provisioning Complete");
+		int_prov_state = INT_PROV_IDLE;
+		mesh_prov_close(prov, 0);
+		break;
+
+	case PROV_FAILED: /* Failed */
+		l_error("Provisioning Failed (reason: %d)", data[0]);
+		err = data[0];
+		goto failure;
+
+	default:
+		l_error("Unknown Pkt %2.2x", type);
+		err = PROV_ERR_UNEXPECTED_PDU;
+		goto failure;
+	}
+
+	return;
+
+failure:
+	int_prov_state = INT_PROV_IDLE;
+	mesh_prov_close(prov, err);
+}
+
+static void acp_prov_send_cmplt(bool success, struct mesh_prov *prov)
+{
+	l_debug("Provision sending complete");
+
+	switch (acp_prov_state) {
+	case ACP_PROV_CAPS_SENT:
+		acp_prov_state = ACP_PROV_CAPS_ACKED;
+		if (int_prov_state == INT_PROV_KEY_SENT) {
+			acp_prov_state = ACP_PROV_KEY_SENT;
+			prov_expected = PROV_CONFIRM;
+			send_prov_key(prov, acp_prov_send_cmplt);
+		}
+		break;
+	case ACP_PROV_KEY_SENT:
+		acp_prov_state = ACP_PROV_KEY_ACKED;
+		if (int_prov_state == INT_PROV_CONF_SENT) {
+			acp_prov_state = ACP_PROV_CONF_SENT;
+			prov_expected = PROV_RANDOM;
+			send_prov_conf(prov, acp_prov_send_cmplt);
+		}
+		break;
+	case ACP_PROV_INP_CMPLT_SENT:
+		acp_prov_state = ACP_PROV_INP_CMPLT_ACKED;
+		break;
+	case ACP_PROV_CONF_SENT:
+		acp_prov_state = ACP_PROV_CONF_ACKED;
+		if (int_prov_state == INT_PROV_RAND_SENT) {
+			acp_prov_state = ACP_PROV_RAND_SENT;
+			prov_expected = PROV_DATA;
+			send_prov_rand(prov, acp_prov_send_cmplt);
+		}
+		break;
+	case ACP_PROV_RAND_SENT:
+		acp_prov_state = ACP_PROV_RAND_ACKED;
+		break;
+	case ACP_PROV_CMPLT_SENT:
+		acp_prov_state = ACP_PROV_IDLE;
+		mesh_net_provisioned_set(prov->net, true);
+	default:
+	case ACP_PROV_IDLE:
+	case ACP_PROV_CAPS_ACKED:
+	case ACP_PROV_KEY_ACKED:
+	case ACP_PROV_INP_CMPLT_ACKED:
+	case ACP_PROV_CONF_ACKED:
+	case ACP_PROV_RAND_ACKED:
+	case ACP_PROV_FAIL_SENT:
+		break;
+	}
+}
+
+void acceptor_prov_open(struct mesh_prov *prov)
+{
+	uint8_t *priv_key;
+
+	l_info("Provisioning link opened");
+
+	priv_key = mesh_net_priv_key_get(prov->net);
+	ecc_make_key(prov->l_public, priv_key);
+
+	prov_expected = PROV_INVITE;
+	prov_last = -1;
+}
+
+void acceptor_prov_close(struct mesh_prov *prov, uint8_t reason)
+{
+	l_info("Provisioning link closed");
+	mesh_prov_unref(prov);
+}
+
+static void prov_store_cfm(void *user_data, bool result)
+{
+	struct mesh_prov *prov = user_data;
+	uint8_t out[2];
+
+	if (result) {
+		acp_prov_state = ACP_PROV_CMPLT_SENT;
+		out[0] = PROV_COMPLETE;
+		mesh_prov_send(prov, out, 1,
+				acp_prov_send_cmplt,
+				prov);
+	} else {
+		acp_prov_state = ACP_PROV_FAIL_SENT;
+		out[0] = PROV_FAILED;
+		out[1] = PROV_ERR_INSUF_RESOURCE;
+		mesh_prov_send(prov, out, 2, NULL, NULL);
+	}
+}
+
+void acceptor_prov_receive(const void *pkt, uint16_t size,
+							struct mesh_prov *prov)
+{
+	struct mesh_net *net = prov->net;
+	struct mesh_net_prov_caps *caps = mesh_net_prov_caps_get(net);
+	uint8_t *priv_key = mesh_net_priv_key_get(net);
+	bool test_mode = mesh_net_test_mode(net);
+	bool ret;
+	const uint8_t *data = pkt;
+	uint8_t type = *data++;
+	uint8_t out[129];
+	uint8_t tmp[16];
+	uint8_t rand_dev[16];
+	uint64_t rx_mic, decode_mic;
+
+	l_debug("Provisioning packet received type: %2.2x (%u octets)",
+								type, size);
+
+	if (type == prov_last) {
+		l_error("Ignore repeated %2.2x packet", type);
+		return;
+	} else if (type > prov_expected || type < prov_last) {
+		l_error("Expected %2.2x, Got:%2.2x", prov_expected, type);
+		out[1] = PROV_ERR_UNEXPECTED_PDU;
+		goto failure;
+	}
+
+	if (type >= L_ARRAY_SIZE(expected_pdu_size) ||
+					size != expected_pdu_size[type]) {
+		l_error("Expected PDU size %d, Got %d (type: %2.2x)",
+			size, expected_pdu_size[type], type);
+		out[1] = PROV_ERR_INVALID_FORMAT;
+		goto failure;
+	}
+
+	prov_last = type;
+
+	switch (type) {
+	case PROV_INVITE: /* Prov Invite */
+		int_prov_state = INT_PROV_INVITE_SENT;
+		/* Prov Capabilities */
+		out[0] = PROV_CAPS;
+		out[1] = caps->num_ele;
+		l_put_be16(caps->algorithms, out + 2);
+		out[4] = caps->pub_type;
+		out[5] = caps->static_type;
+		out[6] = caps->output_size;
+		l_put_be16(caps->output_action, out + 7);
+		out[9] = caps->input_size;
+		l_put_be16(caps->input_action, out + 10);
+
+		prov->conf_inputs.invite.attention = data[0];
+		memcpy(&prov->conf_inputs.caps, out + 1,
+				sizeof(prov->conf_inputs.caps));
+
+		acp_prov_state = ACP_PROV_CAPS_SENT;
+		prov_expected = PROV_START;
+		mesh_prov_send(prov, out, sizeof(*caps) + 1,
+				acp_prov_send_cmplt, prov);
+		break;
+
+	case PROV_START: /* Prov Start */
+		if (data[0]) {
+			/* Only Algorithm 0x00 supported */
+			l_error("Invalid Algorithm: %2.2x", data[0]);
+			out[1] = PROV_ERR_INVALID_FORMAT;
+			goto failure;
+		}
+
+		acp_prov_state = ACP_PROV_CAPS_ACKED;
+		int_prov_state = INT_PROV_START_SENT;
+		prov_expected = PROV_PUB_KEY;
+		memcpy(&prov->conf_inputs.start, data,
+				sizeof(prov->conf_inputs.start));
+		if (data[1] == 1 && caps->pub_type) {
+			pub_key_type = PUB_KEY_TYPE_available;
+			ecc_make_key(prov->l_public, priv_key);
+		} else if (data[1] == 0) {
+			pub_key_type = PUB_KEY_TYPE_ephemeral;
+			/* Use Ephemeral Key */
+			l_getrandom(priv_key, 32);
+			ecc_make_key(prov->l_public, priv_key);
+		} else {
+			out[1] = PROV_ERR_INVALID_FORMAT;
+			goto failure;
+		}
+
+		swap_u256_bytes(prov->l_public);
+		swap_u256_bytes(prov->l_public + 32);
+
+		switch (data[2]) {
+		default:
+			out[1] = PROV_ERR_INVALID_FORMAT;
+			goto failure;
+
+		case 0x00:
+		case 0x01:
+			prov_auth_type = AUTH_TYPE_3c;
+			break;
+
+		case 0x02:
+			prov_auth_type = AUTH_TYPE_3a;
+			caps->output_action = 1 << data[3];
+			caps->output_size = data[4];
+			break;
+
+		case 0x03:
+			prov_auth_type = AUTH_TYPE_3b;
+			caps->input_action = 1 << data[3];
+			caps->input_size = data[4];
+			break;
+		}
+		break;
+
+	case PROV_PUB_KEY: /* Public Key */
+		int_prov_state = INT_PROV_KEY_SENT;
+		prov_expected = PROV_CONFIRM;
+		/* Save Key */
+		memcpy(prov->r_public, data, 64);
+		calculate_secrets(prov, false);
+
+		if (pub_key_type == PUB_KEY_TYPE_ephemeral) {
+			acp_prov_state = ACP_PROV_KEY_SENT;
+			send_prov_key(prov, acp_prov_send_cmplt);
+		}
+
+		/* Start Step 3 */
+		memset(prov->rand_auth + 16, 0, 16);
+		if (prov_auth_type == AUTH_TYPE_3a) {
+			uint32_t oob_key;
+
+			l_getrandom(&oob_key, sizeof(uint32_t));
+			oob_key %= digit_mod(caps->output_size);
+			l_put_be32(oob_key,
+				prov->rand_auth + 32 - sizeof(uint32_t));
+			l_info("\n\nEnter %d on Provisioner\n",
+							oob_key);
+
+		} else if (prov_auth_type == AUTH_TYPE_3b) {
+			if (caps->input_action == (1 << 3)) {
+				/* TODO: Collect Text Input data */
+				;
+			} else {
+				/* TODO: Collect Decimal Input data */
+				;
+			}
+
+		} else {
+			if (caps->static_type) {
+				/* TODO: Collect Static Input data */
+				/* (If needed) */
+				;
+			}
+		}
+
+		break;
+
+	case PROV_CONFIRM: /* Confirmation */
+		int_prov_state = INT_PROV_CONF_SENT;
+		acp_prov_state = ACP_PROV_KEY_ACKED;
+		/* RXed Provision Confirmation */
+		memcpy(prov->r_conf, data, sizeof(prov->r_conf));
+		if (test_mode)
+			print_packet("ConfirmationProvisioner",
+					prov->r_conf,
+					sizeof(prov->r_conf));
+
+		if (acp_prov_state == ACP_PROV_KEY_ACKED) {
+			prov_expected = PROV_RANDOM;
+			send_prov_conf(prov, acp_prov_send_cmplt);
+		}
+		break;
+
+	case PROV_RANDOM: /* Random */
+		int_prov_state = INT_PROV_RAND_SENT;
+		acp_prov_state = ACP_PROV_CONF_ACKED;
+
+		/* Calculate Session key while the data is fresh */
+		mesh_crypto_prov_prov_salt(prov->conf_salt, data,
+						prov->rand_auth,
+						prov->prov_salt);
+		mesh_crypto_session_key(prov->secret, prov->prov_salt,
+							prov->s_key);
+		mesh_crypto_nonce(prov->secret, prov->prov_salt, prov->s_nonce);
+
+		if (test_mode) {
+			print_packet("SessionKey", prov->s_key,
+					sizeof(prov->s_key));
+			print_packet("Nonce", prov->s_nonce,
+					sizeof(prov->s_nonce));
+		}
+
+
+		/* Save Local Random data to send after verification */
+		memcpy(rand_dev, prov->rand_auth, 16);
+		/* RXed Provisioner Confirmation */
+		memcpy(prov->rand_auth, data, 16);
+		if (test_mode)
+			print_packet("RandomProvisioner", prov->rand_auth, 16);
+
+		mesh_crypto_aes_cmac(prov->conf_key, prov->rand_auth,
+					sizeof(prov->rand_auth), tmp);
+
+		if (memcmp(tmp, prov->r_conf,
+					sizeof(prov->r_conf))) {
+			l_error("Provisioning Failed-Confirm compare");
+			out[1] = PROV_ERR_CONFIRM_FAILED;
+			goto failure;
+		}
+
+
+		memcpy(prov->rand_auth, rand_dev, 16);
+		if (acp_prov_state == ACP_PROV_CONF_ACKED) {
+			prov_expected = PROV_DATA;
+			send_prov_rand(prov, acp_prov_send_cmplt);
+		}
+		break;
+
+	case PROV_DATA: /* Provisioning Data */
+		int_prov_state = INT_PROV_DATA_SENT;
+		acp_prov_state = ACP_PROV_RAND_ACKED;
+		if (test_mode) {
+			print_packet("DataEncrypted + mic", data, size - 1);
+			print_packet("Rxed-mic", data + 16 + 2 + 1 + 4 + 2, 8);
+		}
+
+		rx_mic = l_get_be64(data + 16 + 2 + 1 + 4 + 2);
+		mesh_crypto_aes_ccm_decrypt(prov->s_nonce, prov->s_key,
+				NULL, 0,
+				data, size - 1, out + 1,
+				&decode_mic, sizeof(decode_mic));
+
+		if (test_mode) {
+			print_packet("Data", out + 1, 16 + 2 + 1 + 4 + 2);
+			l_info("Calc-mic: %16.16lx", decode_mic);
+		}
+
+		if (rx_mic == decode_mic) {
+			mesh_crypto_device_key(prov->secret,
+						prov->prov_salt,
+						prov->dev_key);
+			if (test_mode) {
+				print_packet("DevKey", prov->dev_key, 16);
+				print_packet("NetworkKey", out + 1, 16);
+				print_packet("NetworkKey Index",
+					out + 1 + 16, 2);
+				print_packet("SNB Flags",
+					out + 1 + 16 + 2, 1);
+				print_packet("IVindex",
+					out + 1 + 16 + 2 + 1, 4);
+				print_packet("Unicast Addr",
+					out + 1 + 16 + 2 + 1 + 4, 2);
+			}
+
+			/* Set Provisioned Data */
+			ret = mesh_net_provisioned_new(prov->net,
+					prov->dev_key,
+					l_get_be16(out + 17),
+					out + 1,
+					l_get_be16(out + 24),
+					out[19],
+					l_get_be32(out + 20),
+					prov_store_cfm, prov);
+
+			if (!ret) {
+				out[1] = PROV_ERR_INSUF_RESOURCE;
+				goto failure;
+			}
+		} else {
+			l_error("Provisioning Failed-MIC compare");
+			out[1] = PROV_ERR_DECRYPT_FAILED;
+			goto failure;
+		}
+		break;
+
+	default:
+		l_error("Unknown Pkt %2.2x", type);
+		out[1] = PROV_ERR_UNEXPECTED_PDU;
+		goto failure;
+	}
+
+	return;
+
+failure:
+	acp_prov_state = ACP_PROV_FAIL_SENT;
+	out[0] = PROV_FAILED;
+	mesh_prov_send(prov, out, 2, acp_prov_send_cmplt, prov);
+}
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v2 05/11] meshd: Header files for mesh access layer and utilities
  2018-04-25 17:50 [PATCH BlueZ v2 00/11] Bluetooth Mesh Daemon Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 01/11] meshd: Shared private meshd interfaces Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 03/11] meshd: Provisioning logic for mesh Brian Gix
@ 2018-04-25 17:50 ` Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 06/11] meshd: Source " Brian Gix
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Brian Gix @ 2018-04-25 17:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Inga Stotland

From: Inga Stotland <inga.stotland@intel.com>

This adds initial implementation of Mesh access layer functionality
---
 meshd/common/agent.h     |  42 ++++++++++++++
 meshd/common/mesh-defs.h |  84 +++++++++++++++++++++++++++
 meshd/common/util.h      |  25 ++++++++
 meshd/src/appkey.h       |  43 ++++++++++++++
 meshd/src/cfgmod.h       |  98 +++++++++++++++++++++++++++++++
 meshd/src/mesh.h         |  32 +++++++++++
 meshd/src/model.h        | 146 +++++++++++++++++++++++++++++++++++++++++++++++
 meshd/src/node.h         |  80 ++++++++++++++++++++++++++
 meshd/src/storage.h      |  51 +++++++++++++++++
 9 files changed, 601 insertions(+)
 create mode 100644 meshd/common/agent.h
 create mode 100644 meshd/common/mesh-defs.h
 create mode 100644 meshd/common/util.h
 create mode 100644 meshd/src/appkey.h
 create mode 100644 meshd/src/cfgmod.h
 create mode 100644 meshd/src/mesh.h
 create mode 100644 meshd/src/model.h
 create mode 100644 meshd/src/node.h
 create mode 100644 meshd/src/storage.h

diff --git a/meshd/common/agent.h b/meshd/common/agent.h
new file mode 100644
index 000000000..6fb475691
--- /dev/null
+++ b/meshd/common/agent.h
@@ -0,0 +1,42 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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 MAX_HEXADECIMAL_OOB_LEN	128
+#define DECIMAL_OOB_LEN		4
+#define MAX_ASCII_OOB_LEN		16
+
+enum oob_type {
+	NONE,
+	HEXADECIMAL,
+	DECIMAL,
+	ASCII,
+	OUTPUT,
+} oob_type_t;
+
+typedef void (*agent_input_cb)(enum oob_type type, void *input, uint16_t len,
+							void *user_data);
+bool agent_input_request(enum oob_type type, uint16_t max_len,
+					agent_input_cb cb, void *user_data);
+
+bool agent_output_request(const char *str);
+void agent_output_request_cancel(void);
+bool agent_completion(void);
+bool agent_input(const char *input);
+void agent_release(void);
diff --git a/meshd/common/mesh-defs.h b/meshd/common/mesh-defs.h
new file mode 100644
index 000000000..d40fc43e8
--- /dev/null
+++ b/meshd/common/mesh-defs.h
@@ -0,0 +1,84 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 MESH_AD_TYPE_PROVISION	0x29
+#define MESH_AD_TYPE_NETWORK	0x2A
+#define MESH_AD_TYPE_BEACON	0x2B
+
+#define FEATURE_RELAY	1
+#define FEATURE_PROXY	2
+#define FEATURE_FRIEND	4
+#define FEATURE_LPN	8
+
+#define MESH_MODE_DISABLED	0
+#define MESH_MODE_ENABLED	1
+#define MESH_MODE_UNSUPPORTED	2
+
+#define KEY_REFRESH_PHASE_NONE	0x00
+#define KEY_REFRESH_PHASE_ONE	0x01
+#define KEY_REFRESH_PHASE_TWO	0x02
+#define KEY_REFRESH_PHASE_THREE	0x03
+
+#define DEFAULT_TTL		0xff
+
+/* Supported algorithms for provisioning */
+#define ALG_FIPS_256_ECC	0x0001
+
+/* Input OOB action bit flags */
+#define OOB_IN_PUSH	0x0001
+#define OOB_IN_TWIST	0x0002
+#define OOB_IN_NUMBER	0x0004
+#define OOB_IN_ALPHA	0x0008
+
+/* Output OOB action bit flags */
+#define OOB_OUT_BLINK	0x0001
+#define OOB_OUT_BEEP	0x0002
+#define OOB_OUT_VIBRATE	0x0004
+#define OOB_OUT_NUMBER	0x0008
+#define OOB_OUT_ALPHA	0x0010
+
+#define UNASSIGNED_ADDRESS	0x0000
+#define PROXIES_ADDRESS	0xfffc
+#define FRIENDS_ADDRESS	0xfffd
+#define RELAYS_ADDRESS		0xfffe
+#define ALL_NODES_ADDRESS	0xffff
+#define VIRTUAL_ADDRESS_LOW	0x8000
+#define VIRTUAL_ADDRESS_HIGH	0xbfff
+#define GROUP_ADDRESS_LOW	0xc000
+#define GROUP_ADDRESS_HIGH	0xff00
+
+#define NODE_IDENTITY_STOPPED		0x00
+#define NODE_IDENTITY_RUNNING		0x01
+#define NODE_IDENTITY_NOT_SUPPORTED	0x02
+
+#define PRIMARY_ELE_IDX		0x00
+
+#define VENDOR_ID_MASK		0xffff0000
+
+#define MAX_KEY_IDX		0x0fff
+
+#define IS_UNASSIGNED(x)	((x) == UNASSIGNED_ADDRESS)
+#define IS_UNICAST(x)		(((x) > UNASSIGNED_ADDRESS) && \
+					((x) < VIRTUAL_ADDRESS_LOW))
+#define IS_VIRTUAL(x)		(((x) >= VIRTUAL_ADDRESS_LOW) && \
+					((x) <= VIRTUAL_ADDRESS_HIGH))
+#define IS_GROUP(x)		(((x) >= GROUP_ADDRESS_LOW) && \
+					((x) <= GROUP_ADDRESS_HIGH))
+#define IS_ALL_NODES(x)	((x) == ALL_NODES_ADDRESS)
diff --git a/meshd/common/util.h b/meshd/common/util.h
new file mode 100644
index 000000000..ed880bce7
--- /dev/null
+++ b/meshd/common/util.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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.
+ *
+ *
+ */
+
+uint32_t get_timestamp_secs(void);
+bool str2hex(const char *str, uint16_t in_len, uint8_t *out,
+							uint16_t out_len);
+size_t hex2str(uint8_t *in, size_t in_len, char *out, size_t out_len);
+
diff --git a/meshd/src/appkey.h b/meshd/src/appkey.h
new file mode 100644
index 000000000..8fce3dcd0
--- /dev/null
+++ b/meshd/src/appkey.h
@@ -0,0 +1,43 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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.
+ *
+ *
+ */
+
+/* TODO: get this number from configuration */
+#define MAX_APP_KEYS	32
+
+bool appkey_key_init(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx,
+				uint8_t *key_value, uint8_t *new_key_value);
+void appkey_key_free(void *data);
+int appkey_packet_decrypt(struct mesh_net *net, bool szmict, uint32_t seq,
+				uint32_t iv_index, uint16_t src, uint16_t dst,
+				uint8_t *virt, uint16_t virt_size,
+				uint8_t key_id, const uint8_t *data,
+				uint16_t data_size, uint8_t *out);
+bool appkey_msg_in_replay_cache(struct mesh_net *net, uint16_t idx,
+				uint16_t src, uint16_t crpl, uint32_t seq,
+				uint32_t iv_index);
+const uint8_t *appkey_get_key(struct mesh_net *net, uint16_t app_idx,
+							uint8_t *key_id);
+bool appkey_have_key(struct mesh_net *net, uint16_t app_idx);
+int appkey_key_add(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx,
+					const uint8_t *new_key, bool update);
+int appkey_key_delete(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx);
+void appkey_delete_bound_keys(struct mesh_net *net, uint16_t net_idx);
+uint8_t appkey_list(struct mesh_net *net, uint16_t net_idx, uint8_t *buf,
+					uint16_t buf_size, uint16_t *size);
diff --git a/meshd/src/cfgmod.h b/meshd/src/cfgmod.h
new file mode 100644
index 000000000..bedb0c6f6
--- /dev/null
+++ b/meshd/src/cfgmod.h
@@ -0,0 +1,98 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 CONFIG_SRV_MODEL	(VENDOR_ID_MASK | 0x0000)
+#define CONFIG_CLI_MODEL	(VENDOR_ID_MASK | 0x0001)
+
+/* New List */
+#define OP_APPKEY_ADD				0x00
+#define OP_APPKEY_DELETE			0x8000
+#define OP_APPKEY_GET				0x8001
+#define OP_APPKEY_LIST				0x8002
+#define OP_APPKEY_STATUS			0x8003
+#define OP_APPKEY_UPDATE			0x01
+#define OP_DEV_COMP_GET				0x8008
+#define OP_DEV_COMP_STATUS			0x02
+#define OP_CONFIG_BEACON_GET			0x8009
+#define OP_CONFIG_BEACON_SET			0x800A
+#define OP_CONFIG_BEACON_STATUS			0x800B
+#define OP_CONFIG_DEFAULT_TTL_GET		0x800C
+#define OP_CONFIG_DEFAULT_TTL_SET		0x800D
+#define OP_CONFIG_DEFAULT_TTL_STATUS		0x800E
+#define OP_CONFIG_FRIEND_GET			0x800F
+#define OP_CONFIG_FRIEND_SET			0x8010
+#define OP_CONFIG_FRIEND_STATUS			0x8011
+#define OP_CONFIG_PROXY_GET			0x8012
+#define OP_CONFIG_PROXY_SET			0x8013
+#define OP_CONFIG_PROXY_STATUS			0x8014
+#define OP_CONFIG_KEY_REFRESH_PHASE_GET		0x8015
+#define OP_CONFIG_KEY_REFRESH_PHASE_SET		0x8016
+#define OP_CONFIG_KEY_REFRESH_PHASE_STATUS	0x8017
+#define OP_CONFIG_MODEL_PUB_GET			0x8018
+#define OP_CONFIG_MODEL_PUB_SET			0x03
+#define OP_CONFIG_MODEL_PUB_STATUS		0x8019
+#define OP_CONFIG_MODEL_PUB_VIRT_SET		0x801A
+#define OP_CONFIG_MODEL_SUB_ADD			0x801B
+#define OP_CONFIG_MODEL_SUB_DELETE		0x801C
+#define OP_CONFIG_MODEL_SUB_DELETE_ALL		0x801D
+#define OP_CONFIG_MODEL_SUB_OVERWRITE		0x801E
+#define OP_CONFIG_MODEL_SUB_STATUS		0x801F
+#define OP_CONFIG_MODEL_SUB_VIRT_ADD		0x8020
+#define OP_CONFIG_MODEL_SUB_VIRT_DELETE		0x8021
+#define OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE	0x8022
+#define OP_CONFIG_NETWORK_TRANSMIT_GET		0x8023
+#define OP_CONFIG_NETWORK_TRANSMIT_SET		0x8024
+#define OP_CONFIG_NETWORK_TRANSMIT_STATUS	0x8025
+#define OP_CONFIG_RELAY_GET			0x8026
+#define OP_CONFIG_RELAY_SET			0x8027
+#define OP_CONFIG_RELAY_STATUS			0x8028
+#define OP_CONFIG_MODEL_SUB_GET			0x8029
+#define OP_CONFIG_MODEL_SUB_LIST		0x802A
+#define OP_CONFIG_VEND_MODEL_SUB_GET		0x802B
+#define OP_CONFIG_VEND_MODEL_SUB_LIST		0x802C
+#define OP_CONFIG_POLL_TIMEOUT_LIST		0x802D
+#define OP_CONFIG_POLL_TIMEOUT_STATUS		0x802E
+/* Health opcodes in health-mod.h */
+#define OP_CONFIG_HEARTBEAT_PUB_GET		0x8038
+#define OP_CONFIG_HEARTBEAT_PUB_SET		0x8039
+#define OP_CONFIG_HEARTBEAT_PUB_STATUS		0x06
+#define OP_CONFIG_HEARTBEAT_SUB_GET		0x803A
+#define OP_CONFIG_HEARTBEAT_SUB_SET		0x803B
+#define OP_CONFIG_HEARTBEAT_SUB_STATUS		0x803C
+#define OP_MODEL_APP_BIND			0x803D
+#define OP_MODEL_APP_STATUS			0x803E
+#define OP_MODEL_APP_UNBIND			0x803F
+#define OP_NETKEY_ADD				0x8040
+#define OP_NETKEY_DELETE			0x8041
+#define OP_NETKEY_GET				0x8042
+#define OP_NETKEY_LIST				0x8043
+#define OP_NETKEY_STATUS			0x8044
+#define OP_NETKEY_UPDATE			0x8045
+#define OP_NODE_IDENTITY_GET			0x8046
+#define OP_NODE_IDENTITY_SET			0x8047
+#define OP_NODE_IDENTITY_STATUS			0x8048
+#define OP_NODE_RESET				0x8049
+#define OP_NODE_RESET_STATUS			0x804A
+#define OP_MODEL_APP_GET			0x804B
+#define OP_MODEL_APP_LIST			0x804C
+#define OP_VEND_MODEL_APP_GET			0x804C
+#define OP_VEND_MODEL_APP_LIST			0x804E
+
+void mesh_config_srv_init(struct mesh_net *net, uint8_t ele_idx);
diff --git a/meshd/src/mesh.h b/meshd/src/mesh.h
new file mode 100644
index 000000000..7cd1e6158
--- /dev/null
+++ b/meshd/src/mesh.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 bt_mesh;
+struct mesh_net;
+
+struct bt_mesh *mesh_create(uint16_t index);
+struct bt_mesh *mesh_ref(struct bt_mesh *mesh);
+void mesh_unref(struct bt_mesh *mesh);
+bool mesh_load_config(struct bt_mesh *mesh, const char *in_config_name);
+bool mesh_set_output(struct bt_mesh *mesh, const char *out_config_name);
+const char *mesh_status_str(uint8_t err);
+
+/* Command line testing */
+struct mesh_net *mesh_get_net(struct bt_mesh *mesh);
diff --git a/meshd/src/model.h b/meshd/src/model.h
new file mode 100644
index 000000000..3a41bd722
--- /dev/null
+++ b/meshd/src/model.h
@@ -0,0 +1,146 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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.
+ *
+ *
+ */
+
+#include <ell/ell.h>
+
+struct mesh_model;
+
+#define OP_UNRELIABLE			0x0100
+
+#define MAX_BINDINGS	10
+#define MAX_GRP_PER_MOD	10
+
+#define	VIRTUAL_BASE			0x10000
+
+#define MESH_MAX_ACCESS_PAYLOAD		380
+
+#define MESH_STATUS_SUCCESS		0x00
+#define MESH_STATUS_INVALID_ADDRESS	0x01
+#define MESH_STATUS_INVALID_MODEL	0x02
+#define MESH_STATUS_INVALID_APPKEY	0x03
+#define MESH_STATUS_INVALID_NETKEY	0x04
+#define MESH_STATUS_INSUFF_RESOURCES	0x05
+#define MESH_STATUS_IDX_ALREADY_STORED	0x06
+#define MESH_STATUS_INVALID_PUB_PARAM	0x07
+#define MESH_STATUS_NOT_SUB_MOD		0x08
+#define MESH_STATUS_STORAGE_FAIL	0x09
+#define MESH_STATUS_FEATURE_NO_SUPPORT	0x0a
+#define MESH_STATUS_CANNOT_UPDATE	0x0b
+#define MESH_STATUS_CANNOT_REMOVE	0x0c
+#define MESH_STATUS_CANNOT_BIND		0x0d
+#define MESH_STATUS_UNABLE_CHANGE_STATE	0x0e
+#define MESH_STATUS_CANNOT_SET		0x0f
+#define MESH_STATUS_UNSPECIFIED_ERROR	0x10
+#define MESH_STATUS_INVALID_BINDING	0x11
+
+#define OP_MODEL_TEST			0x8000fffe
+#define OP_MODEL_INVALID		0x8000ffff
+
+#define USE_PUB_VALUE			0x00
+
+#define ACTION_ADD		1
+#define ACTION_UPDATE		2
+#define ACTION_DELETE		3
+
+struct mesh_model_pub {
+	uint32_t addr;
+	uint16_t idx;
+	uint8_t ttl;
+	uint8_t credential;
+	uint8_t period;
+	uint8_t retransmit;
+};
+
+typedef void (*mesh_model_unregister)(void *user_data);
+typedef bool (*mesh_model_recv_cb)(uint16_t src, uint32_t dst, uint16_t unicast,
+					uint16_t app_idx, const uint8_t *data,
+					uint16_t len, uint8_t ttl,
+					const void *user_data);
+typedef int (*mesh_model_bind_cb)(uint16_t app_idx, int action);
+typedef int (*mesh_model_pub_cb)(struct mesh_model_pub *pub);
+typedef int (*mesh_model_sub_cb)(uint16_t sub_addr, int action);
+
+struct mesh_model_ops {
+	mesh_model_unregister unregister;
+	mesh_model_recv_cb recv;
+	mesh_model_bind_cb bind;
+	mesh_model_pub_cb pub;
+	mesh_model_sub_cb sub;
+};
+
+struct mesh_model *mesh_model_new(uint8_t ele_idx, uint32_t id, bool vendor);
+void mesh_model_free(void *data);
+uint32_t mesh_model_get_model_id(const struct mesh_model *model);
+bool mesh_model_vendor_register(struct mesh_net *net, uint8_t ele_idx,
+					uint32_t mod_id,
+					const struct mesh_model_ops *cbs,
+					void *user_data);
+bool mesh_model_register(struct mesh_net *net, uint8_t ele_idx, uint32_t mod_id,
+					const struct mesh_model_ops *cbs,
+							void *user_data);
+struct mesh_model_pub *mesh_model_pub_get(struct mesh_net *net, uint8_t ele_idx,
+						uint32_t mod_id, int *status);
+int mesh_model_pub_set(struct mesh_net *net, uint16_t addr, uint32_t id,
+			const uint8_t *mod_addr, uint16_t idx, bool cred_flag,
+			uint8_t ttl, uint8_t period, uint8_t retransmit,
+			bool b_virt, uint16_t *dst);
+struct mesh_model *mesh_model_init(struct mesh_net *net, uint8_t ele_idx,
+						struct mesh_db_model *db_mod);
+
+int mesh_model_binding_add(struct mesh_net *net, uint16_t addr, uint32_t id,
+						uint16_t app_idx);
+int mesh_model_binding_del(struct mesh_net *net, uint16_t addr, uint32_t id,
+						uint16_t idx);
+int mesh_model_get_bindings(struct mesh_net *net, uint16_t addr, uint32_t id,
+				uint8_t *buf, uint16_t buf_len, uint16_t *size);
+int mesh_model_sub_add(struct mesh_net *net, uint16_t addr, uint32_t id,
+						const uint8_t *grp, bool b_virt,
+						uint16_t *dst);
+int mesh_model_sub_del(struct mesh_net *net, uint16_t addr, uint32_t id,
+						const uint8_t *grp, bool b_virt,
+						uint16_t *dst);
+int mesh_model_sub_del_all(struct mesh_net *net, uint16_t addr, uint32_t id);
+int mesh_model_sub_ovr(struct mesh_net *net, uint16_t addr, uint32_t id,
+						const uint8_t *grp, bool b_virt,
+						uint16_t *dst);
+int mesh_model_sub_get(struct mesh_net *net, uint16_t addr, uint32_t id,
+			uint8_t *buf, uint16_t buf_size, uint16_t *size);
+uint16_t mesh_model_cfg_blk(uint8_t *pkt);
+unsigned int mesh_model_send(struct mesh_net *net, uint32_t mod_id,
+				uint16_t src, uint32_t target,
+				uint16_t app_idx, uint8_t ttl,
+				const void *msg, uint16_t msg_len);
+
+bool mesh_model_rx(struct mesh_net *net, bool szmict, uint32_t seq0,
+			uint32_t seq, uint32_t iv_index, uint8_t ttl,
+			uint16_t src, uint16_t dst, uint8_t key_id,
+			const uint8_t *data, uint16_t size);
+
+void mesh_model_app_key_generate_new(struct mesh_net *net, uint16_t net_idx);
+void mesh_model_app_key_delete(struct mesh_net *net, struct l_queue *models,
+								uint16_t idx);
+struct l_queue *mesh_model_get_appkeys(struct mesh_net *net);
+void *mesh_model_get_local_node_data(struct mesh_net *net);
+void mesh_model_add_virtual(struct mesh_net *net, const uint8_t *v);
+void mesh_model_del_virtual(struct mesh_net *net, uint32_t va24);
+void mesh_model_list_virtual(struct mesh_net *net);
+uint16_t mesh_model_opcode_set(uint32_t opcode, uint8_t *buf);
+bool mesh_model_opcode_get(const uint8_t *buf, uint16_t size,
+					uint32_t *opcode, uint16_t *n);
diff --git a/meshd/src/node.h b/meshd/src/node.h
new file mode 100644
index 000000000..8ebb41941
--- /dev/null
+++ b/meshd/src/node.h
@@ -0,0 +1,80 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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.
+ *
+ *
+ */
+
+#include "meshd/mesh-json/mesh-db.h"
+
+struct mesh_net;
+struct mesh_node;
+
+/* To prevent local node JSON cache thrashing, minimum update times */
+#define MIN_SEQ_TRIGGER	32
+#define MIN_SEQ_CACHE		(2*MIN_SEQ_TRIGGER)
+#define MIN_SEQ_CACHE_TIME	(5*60)
+
+struct mesh_node *node_new(void);
+void node_free(struct mesh_node *node);
+uint8_t *node_uuid_get(struct mesh_node *node);
+struct mesh_node *node_find_by_addr(uint16_t addr);
+struct mesh_node *node_find_by_uuid(uint8_t uuid[16]);
+bool node_is_provisioned(struct mesh_node *node);
+bool node_app_key_delete(struct mesh_net *net, uint16_t addr,
+				uint16_t net_idx, uint16_t idx);
+bool node_net_key_delete(struct mesh_node *node, uint16_t index);
+bool node_set_primary(struct mesh_node *node, uint16_t unicast);
+uint16_t node_get_primary(struct mesh_node *node);
+uint16_t node_get_primary_net_idx(struct mesh_node *node);
+bool node_set_device_key(struct mesh_node *node, uint8_t key[16]);
+const uint8_t *node_get_device_key(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);
+bool node_parse_composition(struct mesh_node *node, uint8_t *buf, uint16_t len);
+struct l_queue *node_get_net_keys(struct mesh_node *node);
+struct l_queue *node_get_app_keys(struct mesh_node *node);
+bool node_add_binding(struct mesh_node *node, uint8_t ele_idx,
+			uint32_t model_id, uint16_t app_idx);
+bool node_del_binding(struct mesh_node *node, uint8_t ele_idx,
+			uint32_t model_id, uint16_t app_idx);
+uint8_t node_default_ttl_get(struct mesh_node *node);
+bool node_default_ttl_set(struct mesh_node *node, uint8_t ttl);
+bool node_set_sequence_number(struct mesh_node *node, uint32_t seq);
+uint32_t node_get_sequence_number(struct mesh_node *node);
+int node_get_element_idx(struct mesh_node *node, uint16_t ele_addr);
+struct l_queue *node_get_element_models(struct mesh_node *node, uint8_t ele_idx,
+								int *status);
+struct mesh_model *node_get_model(struct mesh_node *node, uint8_t ele_idx,
+						uint32_t id, int *status);
+uint16_t node_get_crpl(struct mesh_node *node);
+struct mesh_node *node_create_from_storage(struct mesh_net *net,
+						struct mesh_db_node *db_node,
+								bool local);
+uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf, uint16_t sz);
+uint8_t node_lpn_mode_get(struct mesh_node *node);
+bool node_relay_mode_set(struct mesh_node *node, bool enable, uint8_t cnt,
+							uint16_t interval);
+uint8_t node_relay_mode_get(struct mesh_node *node, uint8_t *cnt,
+							uint16_t *interval);
+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);
+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);
+uint32_t node_seq_cache(struct mesh_node *node);
+void node_cleanup(struct mesh_net *net);
diff --git a/meshd/src/storage.h b/meshd/src/storage.h
new file mode 100644
index 000000000..341932dba
--- /dev/null
+++ b/meshd/src/storage.h
@@ -0,0 +1,51 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017-2918  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_net;
+
+bool storage_parse_config(struct mesh_net *net, const char *config_name);
+bool storage_save_config(struct mesh_net *net, const char *config_name,
+			bool no_wait, mesh_status_func_t cb, void *user_data);
+bool storage_save_new_config(struct mesh_net *net, const char *config_name,
+					mesh_status_func_t cb, void *user_data);
+void storage_release(struct mesh_net *net);
+
+bool storage_model_bind(struct mesh_net *net, uint16_t addr, uint32_t id,
+						uint16_t app_idx, bool unbind);
+
+bool storage_local_set_ttl(struct mesh_net *net, uint8_t ttl);
+bool storage_local_set_relay(struct mesh_net *net, bool enable, uint8_t count,
+							uint8_t interval);
+bool storage_local_set_transmit_params(struct mesh_net *net, uint8_t count,
+							uint8_t interval);
+bool storage_local_set_mode(struct mesh_net *net, uint8_t mode,
+							const char *mode_name);
+bool storage_local_net_key_add(struct mesh_net *net, uint16_t net_idx,
+					const uint8_t key[16], int phase);
+bool storage_local_net_key_del(struct mesh_net *net, uint16_t net_idx);
+bool storage_local_app_key_add(struct mesh_net *net, uint16_t net_idx,
+			uint16_t app_idx, const uint8_t key[16], bool update);
+bool storage_local_app_key_del(struct mesh_net *net, uint16_t net_idx,
+							uint16_t app_idx);
+bool storage_local_write_sequence_number(struct mesh_net *net, uint32_t seq);
+bool storage_local_set_iv_index(struct mesh_net *net, uint32_t iv_index,
+								bool update);
+bool storage_local_set_device_key(struct mesh_net *net, uint8_t dev_key[16]);
+bool storage_local_set_unicast(struct mesh_net *net, uint16_t unicast);
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v2 06/11] meshd: Source files for mesh access layer and utilities
  2018-04-25 17:50 [PATCH BlueZ v2 00/11] Bluetooth Mesh Daemon Brian Gix
                   ` (2 preceding siblings ...)
  2018-04-25 17:50 ` [PATCH BlueZ v2 05/11] meshd: Header files for mesh access layer and utilities Brian Gix
@ 2018-04-25 17:50 ` Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 07/11] meshd: Source code for handling access layer mux/demux Brian Gix
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Brian Gix @ 2018-04-25 17:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Inga Stotland

From: Inga Stotland <inga.stotland@intel.com>

This adds initial implementation of BT Mesh access layer
functionality plus utilities.
---
 meshd/common/agent.c | 229 ++++++++++++++
 meshd/common/util.c  |  71 +++++
 meshd/src/appkey.c   | 538 ++++++++++++++++++++++++++++++++
 meshd/src/btmesh.c   | 176 +++++++++++
 meshd/src/main.c     | 174 +++++++++++
 meshd/src/mesh.c     | 184 +++++++++++
 meshd/src/node.c     | 851 +++++++++++++++++++++++++++++++++++++++++++++++++++
 meshd/src/storage.c  | 673 ++++++++++++++++++++++++++++++++++++++++
 8 files changed, 2896 insertions(+)
 create mode 100644 meshd/common/agent.c
 create mode 100644 meshd/common/util.c
 create mode 100644 meshd/src/appkey.c
 create mode 100644 meshd/src/btmesh.c
 create mode 100644 meshd/src/main.c
 create mode 100644 meshd/src/mesh.c
 create mode 100644 meshd/src/node.c
 create mode 100644 meshd/src/storage.c

diff --git a/meshd/common/agent.c b/meshd/common/agent.c
new file mode 100644
index 000000000..bbd916244
--- /dev/null
+++ b/meshd/common/agent.c
@@ -0,0 +1,229 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <ell/ell.h>
+
+#include "src/shared/shell.h"
+
+#include "meshd/common/util.h"
+#include "meshd/common/agent.h"
+
+struct input_request {
+	enum oob_type type;
+	uint16_t len;
+	agent_input_cb cb;
+	void *user_data;
+};
+
+static struct input_request pending_request = {NONE, 0, NULL, NULL};
+
+bool agent_completion(void)
+{
+	if (pending_request.type == NONE)
+		return false;
+
+	return true;
+}
+
+static void reset_input_request(void)
+{
+	pending_request.type = NONE;
+	pending_request.len = 0;
+	pending_request.cb = NULL;
+	pending_request.user_data = NULL;
+}
+
+static void try_again(void)
+{
+	static int try_count;
+	enum oob_type type = pending_request.type;
+
+	if (try_count == 2) {
+		reset_input_request();
+		try_count = 0;
+		return;
+	}
+
+	pending_request.type = NONE;
+	agent_input_request(type, pending_request.len, pending_request.cb,
+						pending_request.user_data);
+
+	try_count++;
+}
+
+static void response_hexadecimal(const char *input, void *user_data)
+{
+	uint8_t buf[MAX_HEXADECIMAL_OOB_LEN];
+
+	if (!str2hex(input, strlen(input), buf, pending_request.len)) {
+		bt_shell_printf("Incorrect input: expecting %d hex octets\n",
+							pending_request.len);
+		try_again();
+		return;
+	}
+
+	if (pending_request.cb)
+		pending_request.cb(HEXADECIMAL, buf, pending_request.len,
+					pending_request.user_data);
+
+	reset_input_request();
+}
+
+static void response_decimal(const char *input, void *user_data)
+{
+	uint8_t buf[DECIMAL_OOB_LEN];
+
+	if (strlen(input) > pending_request.len) {
+		bt_shell_printf("Bad input: expected no more than %d digits\n",
+						pending_request.len);
+		try_again();
+		return;
+	}
+
+	l_put_be32(atoi(input), buf);
+
+	if (pending_request.cb)
+		pending_request.cb(DECIMAL, buf, DECIMAL_OOB_LEN,
+					pending_request.user_data);
+
+	reset_input_request();
+}
+
+static void response_ascii(const char *input, void *user_data)
+{
+	if (pending_request.cb)
+		pending_request.cb(ASCII, (uint8_t *) input, strlen(input),
+					pending_request.user_data);
+
+	reset_input_request();
+}
+
+static bool request_hexadecimal(uint16_t len)
+{
+	if (len > MAX_HEXADECIMAL_OOB_LEN)
+		return false;
+
+	bt_shell_printf("Request hexadecimal key (hex %d octets)\n", len);
+	bt_shell_prompt_input("mesh", "Enter key (hex number):",
+						response_hexadecimal, NULL);
+
+	return true;
+}
+
+static uint32_t power_ten(uint8_t power)
+{
+	uint32_t ret = 1;
+
+	while (power--)
+		ret *= 10;
+
+	return ret;
+}
+
+static bool request_decimal(uint16_t len)
+{
+	bt_shell_printf("Request decimal key (0 - %d)\n", power_ten(len) - 1);
+	bt_shell_prompt_input("mesh", "Enter Numeric key:", response_decimal,
+									NULL);
+
+	return true;
+}
+
+static bool request_ascii(uint16_t len)
+{
+	if (len > MAX_ASCII_OOB_LEN)
+		return false;
+
+	bt_shell_printf("Request ASCII key (max characters %d)\n", len);
+	bt_shell_prompt_input("mesh", "Enter key (ascii string):",
+							response_ascii, NULL);
+
+	return true;
+}
+
+bool agent_input_request(enum oob_type type, uint16_t max_len,
+					agent_input_cb cb, void *user_data)
+{
+	bool result;
+
+	if (pending_request.type != NONE)
+		return false;
+
+	switch (type) {
+	case HEXADECIMAL:
+		result = request_hexadecimal(max_len);
+		break;
+	case DECIMAL:
+		result = request_decimal(max_len);
+		break;
+	case ASCII:
+		result = request_ascii(max_len);
+		break;
+	case NONE:
+	case OUTPUT:
+	default:
+		return false;
+	};
+
+	if (result) {
+		pending_request.type = type;
+		pending_request.len = max_len;
+		pending_request.cb = cb;
+		pending_request.user_data = user_data;
+
+		return true;
+	}
+
+	return false;
+}
+
+static void response_output(const char *input, void *user_data)
+{
+	reset_input_request();
+}
+
+bool agent_output_request(const char *str)
+{
+	if (pending_request.type != NONE)
+		return false;
+
+	pending_request.type = OUTPUT;
+	bt_shell_prompt_input("mesh", str, response_output, NULL);
+	return true;
+}
+
+void agent_output_request_cancel(void)
+{
+	if (pending_request.type != OUTPUT)
+		return;
+
+	pending_request.type = NONE;
+	bt_shell_release_prompt("");
+}
diff --git a/meshd/common/util.c b/meshd/common/util.c
new file mode 100644
index 000000000..af487147f
--- /dev/null
+++ b/meshd/common/util.c
@@ -0,0 +1,71 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "meshd/common/util.h"
+
+uint32_t get_timestamp_secs(void)
+{
+	struct timespec ts;
+
+	clock_gettime(CLOCK_MONOTONIC, &ts);
+	return ts.tv_sec;
+}
+
+bool str2hex(const char *str, uint16_t in_len, uint8_t *out,
+							uint16_t out_len)
+{
+	uint16_t i;
+
+	if (in_len < out_len * 2)
+		return false;
+
+	for (i = 0; i < out_len; i++) {
+		if (sscanf(&str[i * 2], "%02hhx", &out[i]) != 1)
+			return false;
+	}
+
+	return true;
+}
+
+size_t hex2str(uint8_t *in, size_t in_len, char *out, size_t out_len)
+{
+	static const char hexdigits[] = "0123456789abcdef";
+	size_t i;
+
+	if (in_len * 2 > (out_len - 1))
+		return 0;
+
+	for (i = 0; i < in_len; i++) {
+		out[i * 2] = hexdigits[in[i] >> 4];
+		out[i * 2 + 1] = hexdigits[in[i] & 0xf];
+	}
+
+	out[in_len * 2] = '\0';
+	return i;
+}
diff --git a/meshd/src/appkey.c b/meshd/src/appkey.c
new file mode 100644
index 000000000..9bfa1e364
--- /dev/null
+++ b/meshd/src/appkey.c
@@ -0,0 +1,538 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017-2018  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 <ell/ell.h>
+
+#include "mesh/mesh-net.h"
+
+#include "meshd/common/mesh-defs.h"
+
+#include "meshd/src/mesh.h"
+#include "meshd/src/node.h"
+#include "meshd/src/net.h"
+#include "meshd/src/crypto.h"
+#include "meshd/src/display.h"
+#include "meshd/src/model.h"
+#include "meshd/src/storage.h"
+#include "meshd/src/appkey.h"
+
+struct mesh_app_key {
+	struct l_queue *replay_cache;
+	uint16_t net_idx;
+	uint16_t app_idx;
+	uint8_t key[16];
+	uint8_t key_id;
+	uint8_t new_key[16];
+	uint8_t new_key_id;
+};
+
+struct mesh_msg {
+	uint32_t iv_index;
+	uint32_t seq;
+	uint16_t src;
+};
+
+struct mod_decrypt {
+	const uint8_t *data;
+	uint8_t *out;
+	struct mesh_app_key *key;
+	uint8_t *virt;
+	uint32_t seq;
+	uint32_t iv_idx;
+	uint16_t src;
+	uint16_t dst;
+	uint16_t idx;
+	uint16_t size;
+	uint16_t virt_size;
+	uint8_t key_id;
+	bool szmict;
+	bool decrypted;
+};
+
+static bool match_key_index(const void *a, const void *b)
+{
+	const struct mesh_app_key *key = a;
+	uint16_t idx = L_PTR_TO_UINT(b);
+
+	return key->app_idx == idx;
+}
+
+static bool match_replay_cache(const void *a, const void *b)
+{
+	const struct mesh_msg *msg = a;
+	uint16_t src = L_PTR_TO_UINT(b);
+
+	return src == msg->src;
+}
+
+static bool clean_old_iv_index(void *a, void *b)
+{
+	struct mesh_msg *msg = a;
+	uint32_t iv_index = L_PTR_TO_UINT(b);
+
+	if (iv_index < 2)
+		return false;
+
+	if (msg->iv_index < iv_index - 1) {
+		l_free(msg);
+		return true;
+	}
+
+	return false;
+}
+
+static void packet_decrypt(void *a, void *b)
+{
+	struct mesh_app_key *key = a;
+	struct mod_decrypt *dec = b;
+
+	l_debug("model.c - app_packet_decrypt");
+	if (dec->decrypted)
+		return;
+
+	if (key->key_id != dec->key_id &&
+			key->new_key_id != dec->key_id)
+		return;
+
+	dec->key = key;
+
+	if (key->key_id == dec->key_id) {
+		dec->decrypted = mesh_crypto_payload_decrypt(dec->virt,
+				dec->virt_size, dec->data, dec->size,
+				dec->szmict, dec->src, dec->dst, dec->key_id,
+				dec->seq, dec->iv_idx, dec->out, key->key);
+		if (dec->decrypted)
+			print_packet("Used App Key", dec->key->key, 16);
+		else
+			print_packet("Failed with App Key", dec->key->key, 16);
+	}
+
+	if (!dec->decrypted && key->new_key_id == dec->key_id) {
+		dec->decrypted = mesh_crypto_payload_decrypt(dec->virt,
+				dec->virt_size, dec->data, dec->size,
+				dec->szmict, dec->src, dec->dst, dec->key_id,
+				dec->seq, dec->iv_idx, dec->out, key->new_key);
+		if (dec->decrypted)
+			print_packet("Used App Key", dec->key->new_key, 16);
+		else
+			print_packet("Failed with App Key",
+							dec->key->new_key, 16);
+	}
+
+	if (dec->decrypted)
+		dec->idx = key->app_idx;
+}
+
+int appkey_packet_decrypt(struct mesh_net *net, bool szmict, uint32_t seq,
+				uint32_t iv_index, uint16_t src,
+				uint16_t dst, uint8_t *virt, uint16_t virt_size,
+				uint8_t key_id, const uint8_t *data,
+				uint16_t data_size, uint8_t *out)
+{
+	struct l_queue *app_keys;
+
+	struct mod_decrypt decrypt = {
+		.src = src,
+		.dst = dst,
+		.seq = seq,
+		.data = data,
+		.out = out,
+		.size = data_size,
+		.key_id = key_id,
+		.iv_idx = iv_index,
+		.virt = virt,
+		.virt_size = virt_size,
+		.szmict = szmict,
+		.decrypted = false,
+	};
+
+	app_keys = mesh_net_get_app_keys(net);
+	if (!app_keys)
+		return -1;
+
+	l_queue_foreach(app_keys, packet_decrypt, &decrypt);
+
+	return decrypt.decrypted ? decrypt.idx : -1;
+}
+
+bool appkey_msg_in_replay_cache(struct mesh_net *net, uint16_t idx,
+				uint16_t src, uint16_t crpl, uint32_t seq,
+				uint32_t iv_index)
+{
+	struct mesh_app_key *key;
+	struct mesh_msg *msg;
+	struct l_queue *app_keys;
+
+	app_keys = mesh_net_get_app_keys(net);
+	if (!app_keys)
+		return false;
+
+	l_debug("Test Replay src: %4.4x seq: %6.6x iv: %8.8x",
+						src, seq, iv_index);
+
+	key = l_queue_find(app_keys, match_key_index, L_UINT_TO_PTR(idx));
+
+	if (!key)
+		return false;
+
+	msg = l_queue_find(key->replay_cache, match_replay_cache,
+						L_UINT_TO_PTR(src));
+
+	if (msg) {
+		if (iv_index > msg->iv_index) {
+			msg->seq = seq;
+			msg->iv_index = iv_index;
+			return false;
+		}
+
+		if (seq < msg->seq) {
+			l_info("Ignoring packet with lower sequence number");
+			return true;
+		}
+
+		if (seq == msg->seq) {
+			l_info("Message already processed (duplicate)");
+			return true;
+		}
+
+		msg->seq = seq;
+
+		return false;
+	}
+
+	l_debug("New Entry for %4.4x", src);
+	if (key->replay_cache == NULL)
+		key->replay_cache = l_queue_new();
+
+	/* Replay Cache is fixed sized */
+	if (l_queue_length(key->replay_cache) >= crpl) {
+		int ret = l_queue_foreach_remove(key->replay_cache,
+				clean_old_iv_index, L_UINT_TO_PTR(iv_index));
+
+		if (!ret)
+			return true;
+	}
+
+	msg = l_new(struct mesh_msg, 1);
+	msg->src = src;
+	msg->seq = seq;
+	msg->iv_index = iv_index;
+	l_queue_push_head(key->replay_cache, msg);
+
+	return false;
+}
+
+static struct mesh_app_key *app_key_new(void)
+{
+	struct mesh_app_key *key = l_new(struct mesh_app_key, 1);
+
+	key->new_key_id = 0xFF;
+	key->replay_cache = l_queue_new();
+	return key;
+}
+
+static bool set_key(struct mesh_app_key *key, uint16_t app_idx,
+			const uint8_t *key_value, bool is_new)
+{
+	uint8_t key_id;
+
+	if (!mesh_crypto_k4(key_value, &key_id))
+		return false;
+
+	key_id = KEY_ID_AKF | (key_id << KEY_AID_SHIFT);
+	if (!is_new)
+		key->key_id = key_id;
+	else
+		key->new_key_id = key_id;
+
+	memcpy(is_new ? key->new_key : key->key, key_value, 16);
+
+	return true;
+}
+
+void appkey_key_free(void *data)
+{
+	struct mesh_app_key *key = data;
+
+	if (!key)
+		return;
+
+	l_queue_destroy(key->replay_cache, l_free);
+	l_free(key);
+}
+
+bool appkey_key_init(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx,
+				uint8_t *key_value, uint8_t *new_key_value)
+{
+	struct mesh_app_key *key;
+	struct l_queue *app_keys;
+
+	if (net_idx > MAX_KEY_IDX || app_idx > MAX_KEY_IDX)
+		return false;
+
+	app_keys = mesh_net_get_app_keys(net);
+	if (!app_keys)
+		return NULL;
+
+	key = app_key_new();
+	if (!key)
+		return false;
+
+	if (!mesh_net_have_key(net, net_idx))
+		return false;
+
+	key->net_idx = net_idx;
+
+	if (key_value && !set_key(key, app_idx, key_value, false))
+		return false;
+
+	if (new_key_value && !set_key(key, app_idx, new_key_value, true))
+		return false;
+
+	l_queue_push_tail(app_keys, key);
+
+	return true;
+}
+
+const uint8_t *appkey_get_key(struct mesh_net *net, uint16_t app_idx,
+							uint8_t *key_id)
+{
+	struct mesh_app_key *app_key;
+	uint8_t phase;
+	struct l_queue *app_keys;
+
+	app_keys = mesh_net_get_app_keys(net);
+	if (!app_keys)
+		return NULL;
+
+	app_key = l_queue_find(app_keys, match_key_index,
+							L_UINT_TO_PTR(app_idx));
+	if (!app_key)
+		return NULL;
+
+	if (mesh_net_key_refresh_phase_get(net, app_key->net_idx, &phase) !=
+							MESH_STATUS_SUCCESS)
+		return NULL;
+
+	if (phase != NET_KEY_REFRESH_PHASE_TWO) {
+		*key_id = app_key->key_id;
+		return app_key->key;
+	}
+
+	if (app_key->new_key_id == NET_NID_INVALID)
+		return NULL;
+
+	*key_id = app_key->new_key_id;
+	return app_key->new_key;
+}
+
+bool appkey_have_key(struct mesh_net *net, uint16_t app_idx)
+{
+	struct mesh_app_key *key;
+	struct l_queue *app_keys;
+
+	app_keys = mesh_net_get_app_keys(net);
+	if (!app_keys)
+		return false;
+
+	key = l_queue_find(app_keys, match_key_index, L_UINT_TO_PTR(app_idx));
+
+	if (!key)
+		return false;
+	else
+		return true;
+}
+
+int appkey_key_add(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx,
+					const uint8_t *new_key, bool update)
+{
+	struct mesh_app_key *key;
+	struct l_queue *app_keys;
+	uint8_t phase = KEY_REFRESH_PHASE_NONE;
+
+	app_keys = mesh_net_get_app_keys(net);
+	if (!app_keys)
+		return MESH_STATUS_INSUFF_RESOURCES;
+
+	key = l_queue_find(app_keys, match_key_index, L_UINT_TO_PTR(app_idx));
+
+	if (!mesh_net_have_key(net, net_idx) ||
+					(update && key->net_idx != net_idx))
+		return MESH_STATUS_INVALID_NETKEY;
+
+	if (update && !key)
+		return MESH_STATUS_INVALID_APPKEY;
+
+	mesh_net_key_refresh_phase_get(net, net_idx, &phase);
+	if (update && phase != KEY_REFRESH_PHASE_ONE)
+		return MESH_STATUS_CANNOT_UPDATE;
+
+	if (key) {
+		if (memcmp(new_key, key->key, 16) == 0)
+			return MESH_STATUS_SUCCESS;
+
+		if (!update) {
+			l_info("Failed to add key: index already stored %x",
+				(net_idx << 16) | app_idx);
+			return MESH_STATUS_IDX_ALREADY_STORED;
+		}
+	}
+
+	if (!key) {
+		if (l_queue_length(app_keys) <= MAX_APP_KEYS)
+			return MESH_STATUS_INSUFF_RESOURCES;
+
+		key = app_key_new();
+		if (!key)
+			return MESH_STATUS_INSUFF_RESOURCES;
+
+		if (!set_key(key, app_idx, new_key, false)) {
+			appkey_key_free(key);
+			return MESH_STATUS_INSUFF_RESOURCES;
+		}
+
+		if (!storage_local_app_key_add(net, net_idx, app_idx, new_key,
+								false)) {
+			appkey_key_free(key);
+			return MESH_STATUS_STORAGE_FAIL;
+		}
+
+		key->net_idx = net_idx;
+		key->app_idx = app_idx;
+		l_queue_push_tail(app_keys, key);
+	} else {
+		if (!set_key(key, app_idx, new_key, true))
+			return MESH_STATUS_INSUFF_RESOURCES;
+
+		if (!storage_local_app_key_add(net, net_idx, app_idx, new_key,
+								true))
+			return MESH_STATUS_STORAGE_FAIL;
+	}
+
+	l_queue_clear(key->replay_cache, l_free);
+
+	return MESH_STATUS_SUCCESS;
+}
+
+int appkey_key_delete(struct mesh_net *net, uint16_t net_idx,
+							uint16_t app_idx)
+{
+	struct mesh_app_key *key;
+	struct l_queue *app_keys;
+
+	app_keys = mesh_net_get_app_keys(net);
+	if (!app_keys)
+		return MESH_STATUS_INVALID_APPKEY;
+
+	key = l_queue_find(app_keys, match_key_index, L_UINT_TO_PTR(app_idx));
+
+	if (!key)
+		return MESH_STATUS_INVALID_APPKEY;
+
+	if (key->net_idx != net_idx)
+		return MESH_STATUS_INVALID_NETKEY;
+
+	node_app_key_delete(net, mesh_net_get_address(net), net_idx, app_idx);
+
+	l_queue_remove(app_keys, key);
+	appkey_key_free(key);
+
+	if (!storage_local_app_key_del(net, net_idx, app_idx))
+		return MESH_STATUS_STORAGE_FAIL;
+
+	return MESH_STATUS_SUCCESS;
+}
+
+void appkey_delete_bound_keys(struct mesh_net *net, uint16_t net_idx)
+{
+	const struct l_queue_entry *entry;
+	struct l_queue *app_keys;
+
+	app_keys = mesh_net_get_app_keys(net);
+	if (!app_keys)
+		return;
+
+	entry = l_queue_get_entries(app_keys);
+
+	for (; entry; entry = entry->next) {
+		struct mesh_app_key *key = entry->data;
+
+		appkey_key_delete(net, net_idx, key->app_idx);
+	}
+}
+
+uint8_t appkey_list(struct mesh_net *net, uint16_t net_idx, uint8_t *buf,
+					uint16_t buf_size, uint16_t *size)
+{
+	const struct l_queue_entry *entry;
+	uint32_t idx_pair;
+	int i;
+	uint16_t datalen;
+	struct l_queue *app_keys;
+
+	*size = 0;
+
+	app_keys = mesh_net_get_app_keys(net);
+	if (!app_keys || l_queue_isempty(app_keys))
+		return MESH_STATUS_SUCCESS;
+
+	idx_pair = 0;
+	i = 0;
+	datalen = 0;
+	entry = l_queue_get_entries(app_keys);
+
+	for (; entry; entry = entry->next) {
+		struct mesh_app_key *key = entry->data;
+
+		if (net_idx != key->net_idx)
+			continue;
+
+		if (!(i & 0x1)) {
+			idx_pair = key->app_idx;
+		} else {
+			idx_pair <<= 12;
+			idx_pair += key->app_idx;
+			/* Unlikely, but check for overflow*/
+			if ((datalen + 3) > buf_size) {
+				l_warn("Appkey list too large");
+				goto done;
+			}
+			l_put_le32(idx_pair, buf);
+			buf += 3;
+			datalen += 3;
+		}
+		i++;
+	}
+
+	/* Process the last app key if present */
+	if (i & 0x1 && ((datalen + 2) <= buf_size)) {
+		l_put_le16(idx_pair, buf);
+		datalen += 2;
+	}
+
+done:
+	*size = datalen;
+
+	return MESH_STATUS_SUCCESS;
+}
diff --git a/meshd/src/btmesh.c b/meshd/src/btmesh.c
new file mode 100644
index 000000000..1be3ac196
--- /dev/null
+++ b/meshd/src/btmesh.c
@@ -0,0 +1,176 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <ell/ell.h>
+
+#include "src/shared/shell.h"
+#include "src/shared/mainloop.h"
+
+#include "meshd/src/mesh.h"
+#include "meshd/src/net.h"
+
+#define PROMPT COLOR_BLUE "[btmesh]" COLOR_OFF "# "
+
+static struct bt_mesh *mesh;
+
+static const struct option main_options[] = {
+	{ "index",	1,	0, 'i' },
+	{ "config",	1,	0, 'c' },
+	{ "save",	1,	0, 's' },
+	{ 0, 0, 0, 0 }
+};
+
+static const char *index_option;
+static const char *config_option;
+static const char *save_option;
+
+static const char **optargs[] = {
+	&index_option,
+	&config_option,
+	&save_option,
+};
+
+static const char *help[] = {
+	"Specify adapter index",
+	"Specify input configuration file",
+	"Specify output configuration file"
+};
+
+static const struct bt_shell_opt opt = {
+	.options = main_options,
+	.optno = sizeof(main_options) / sizeof(struct option),
+	.optstr = "i:c:s:",
+	.optarg = optargs,
+	.help = help,
+};
+
+static int get_arg_on_off(int argc, char *argv[])
+{
+	if (!strcmp(argv[1], "on") || !strcmp(argv[1], "yes"))
+		return 1;
+
+	if (!strcmp(argv[1], "off") || !strcmp(argv[1], "no"))
+		return 0;
+
+	bt_shell_printf("Invalid argument %s\n", argv[1]);
+	return -1;
+}
+
+static void cmd_beacon(int argc, char *argv[])
+{
+	bool res;
+	int enable;
+
+	enable = get_arg_on_off(argc, argv);
+	if (enable < 0)
+		return;
+
+	res = mesh_net_set_beacon_mode(mesh_get_net(mesh), enable);
+	if (res)
+		bt_shell_printf("Local beacon mode is %s\n",
+				enable > 0 ? "enabled" : "disabled");
+	else
+		bt_shell_printf("Failed to set local beacon mode to %s\n",
+				enable > 0 ? "enabled" : "disabled");
+}
+
+static const struct bt_shell_menu main_menu = {
+	.name = "main",
+	.entries = {
+	{ "beacon",   "<enable>",  cmd_beacon, "Enable/disable beaconing"},
+	{ } },
+};
+
+static int get_index(const char *arg)
+{
+	if (strlen(arg) > 3 && !strncasecmp(arg, "hci", 3))
+		return atoi(&arg[3]);
+	else
+		return atoi(arg);
+}
+
+static void ell_event(int fd, uint32_t events, void *user_data)
+{
+	int timeout = l_main_prepare();
+
+	l_main_iterate(timeout);
+}
+
+int main(int argc, char *argv[])
+{
+	int index;
+	int fd;
+	int status;
+
+	l_log_set_stderr();
+	l_debug_enable("*");
+
+	if (!l_main_init())
+		return -1;
+
+	bt_shell_init(argc, argv, &opt);
+	bt_shell_set_menu(&main_menu);
+
+	if (!index_option) {
+		bt_shell_usage();
+		return 0;
+	}
+
+	if (config_option)
+		l_info("Reading local configuration from %s\n", config_option);
+
+	if (save_option)
+		l_info("Saving local configuration to %s\n", save_option);
+
+	bt_shell_set_prompt(PROMPT);
+
+	index = get_index(index_option);
+
+	l_info("Starting mesh on hci%d\n", index);
+
+	mesh = mesh_create(index);
+	if (!mesh || !mesh_load_config(mesh, config_option)) {
+		l_info("Failed to create mesh\n");
+		bt_shell_cleanup();
+		return EXIT_FAILURE;
+	}
+
+	if (save_option)
+		mesh_set_output(mesh, save_option);
+
+	fd = l_main_get_epoll_fd();
+	mainloop_add_fd(fd, EPOLLIN, ell_event, NULL, NULL);
+
+	status = bt_shell_attach(fileno(stdin));
+	bt_shell_run();
+
+	mesh_unref(mesh);
+	l_main_exit();
+
+	return status;
+}
diff --git a/meshd/src/main.c b/meshd/src/main.c
new file mode 100644
index 000000000..fa753a699
--- /dev/null
+++ b/meshd/src/main.c
@@ -0,0 +1,174 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017-2018  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 <getopt.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include <sys/stat.h>
+#include <ell/ell.h>
+
+#include "meshd/src/mesh.h"
+#include "meshd/src/net.h"
+#include "meshd/src/storage.h"
+
+static const struct option main_options[] = {
+	{ "index",	required_argument,	NULL, 'i' },
+	{ "config",	optional_argument,	NULL, 'c' },
+	{ "nodetach",	no_argument,		NULL, 'n' },
+	{ "debug",	no_argument,		NULL, 'd' },
+	{ "help",	no_argument,		NULL, 'h' },
+	{ }
+};
+
+static void usage(void)
+{
+	l_info("");
+	l_info("Usage:\n"
+	       "\tmeshd [options]\n");
+	l_info("Options:\n"
+	       "\t--index <hcinum>  Use specified controller\n"
+	       "\t--config          Configuration file\n"
+	       "\t--nodetach        Run in foreground\n"
+	       "\t--debug           Enable debug output\n"
+	       "\t--help            Show %s information\n", __func__);
+}
+
+static void signal_handler(struct l_signal *signal, uint32_t signo,
+							void *user_data)
+{
+	static bool terminated;
+
+	switch (signo) {
+	case SIGINT:
+	case SIGTERM:
+		if (terminated)
+			return;
+		l_info("Terminating");
+		l_main_quit();
+		terminated = true;
+		break;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	int status;
+	bool detached = true;
+	struct l_signal *signal;
+	sigset_t mask;
+	struct bt_mesh *mesh = NULL;
+	const char *config_file = NULL;
+
+	if (!l_main_init())
+		return -1;
+
+	l_log_set_stderr();
+
+	for (;;) {
+		int opt;
+		const char *str;
+
+		opt = getopt_long(argc, argv, "i:c:ndh", main_options, NULL);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'i':
+			if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
+				str = optarg + 3;
+			else
+				str = optarg;
+			if (!isdigit(*str)) {
+				l_error("Invalid controller index value");
+				status = EXIT_FAILURE;
+				goto done;
+			}
+
+			mesh = mesh_create(atoi(str));
+			if (!mesh) {
+				l_error("Failed to initialize mesh");
+				status = EXIT_FAILURE;
+				goto done;
+			}
+
+			break;
+		case 'n':
+			detached = false;
+			break;
+		case 'd':
+			l_debug_enable("*");
+			break;
+		case 'c':
+			config_file = optarg;
+			break;
+		case 'h':
+			usage();
+			status = EXIT_SUCCESS;
+			goto done;
+		default:
+			usage();
+			status = EXIT_FAILURE;
+			goto done;
+		}
+	}
+
+	if (!mesh) {
+		usage();
+		status = EXIT_FAILURE;
+		goto done;
+	}
+
+	if (!mesh_load_config(mesh, config_file)) {
+		l_error("Failed to load mesh configuration: %s", config_file);
+		status = EXIT_FAILURE;
+		goto done;
+	}
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+	signal = l_signal_create(&mask, signal_handler, NULL, NULL);
+
+	umask(0077);
+
+	if (detached) {
+		if (daemon(0, 0)) {
+			perror("Failed to start meshd daemon");
+			status = EXIT_FAILURE;
+			goto done;
+		}
+	}
+
+	status = l_main_run();
+
+	l_signal_remove(signal);
+
+done:
+	mesh_unref(mesh);
+	l_main_exit();
+
+	return status;
+}
diff --git a/meshd/src/mesh.c b/meshd/src/mesh.c
new file mode 100644
index 000000000..b7c35f5be
--- /dev/null
+++ b/meshd/src/mesh.c
@@ -0,0 +1,184 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 <time.h>
+#include <ell/ell.h>
+
+#include "lib/bluetooth.h"
+
+#include "meshd/common/mesh-defs.h"
+
+#include "meshd/src/mesh-io.h"
+#include "meshd/src/node.h"
+#include "meshd/src/net.h"
+#include "meshd/src/storage.h"
+#include "meshd/src/cfgmod.h"
+#include "meshd/src/model.h"
+#include "meshd/src/mesh.h"
+
+struct scan_filter {
+	uint8_t id;
+	const char *pattern;
+};
+
+struct bt_mesh {
+	struct mesh_net *net;
+	int ref_count;
+	struct l_queue *filters;
+	uint8_t max_filters;
+};
+
+static void save_exit_config(struct bt_mesh *mesh)
+{
+	const char *cfg_filename;
+
+	if (!mesh_net_cfg_file_get(mesh->net, &cfg_filename) || !cfg_filename)
+		return;
+
+	/* Preserve the last sequence number before saving configuration */
+	storage_local_write_sequence_number(mesh->net,
+					mesh_net_get_seq_num(mesh->net));
+
+	if (storage_save_config(mesh->net, cfg_filename, true, NULL, NULL))
+		l_info("Saved final configuration to %s", cfg_filename);
+}
+
+struct bt_mesh *mesh_create(uint16_t index)
+{
+	struct bt_mesh *mesh;
+	struct mesh_io *io;
+	struct mesh_io_caps caps;
+
+	mesh = l_new(struct bt_mesh, 1);
+	if (!mesh)
+		return NULL;
+
+	mesh->net = mesh_net_new(index);
+	if (!mesh->net) {
+		l_free(mesh);
+		return NULL;
+	}
+
+	io = mesh_io_new(index, MESH_IO_TYPE_GENERIC);
+	if (!io) {
+		mesh_net_unref(mesh->net);
+		l_free(mesh);
+		return NULL;
+	}
+
+	mesh_io_get_caps(io, &caps);
+	mesh->max_filters = caps.max_num_filters;
+
+	mesh_net_attach(mesh->net, io);
+	mesh_net_set_window_accuracy(mesh->net, caps.window_accuracy);
+
+	return mesh_ref(mesh);
+}
+
+struct bt_mesh *mesh_ref(struct bt_mesh *mesh)
+{
+	if (!mesh)
+		return NULL;
+
+	__sync_fetch_and_add(&mesh->ref_count, 1);
+
+	return mesh;
+}
+
+void mesh_unref(struct bt_mesh *mesh)
+{
+	struct mesh_io *io;
+
+	if (!mesh)
+		return;
+
+	if (__sync_sub_and_fetch(&mesh->ref_count, 1))
+		return;
+
+	if (mesh_net_provisioned_get(mesh->net))
+		save_exit_config(mesh);
+
+	node_cleanup(mesh->net);
+
+	storage_release(mesh->net);
+	io = mesh_net_detach(mesh->net);
+	if (io)
+		mesh_io_destroy(io);
+
+	mesh_net_unref(mesh->net);
+	l_free(mesh);
+}
+
+bool mesh_load_config(struct bt_mesh *mesh, const char *in_config_name)
+{
+	if (!storage_parse_config(mesh->net, in_config_name))
+		return false;
+
+	/* Register foundational models */
+	mesh_config_srv_init(mesh->net, PRIMARY_ELE_IDX);
+
+	return true;
+}
+
+bool mesh_set_output(struct bt_mesh *mesh, const char *config_name)
+{
+	if (!config_name)
+		return false;
+
+	return mesh_net_cfg_file_set(mesh->net, config_name);
+}
+
+const char *mesh_status_str(uint8_t err)
+{
+	switch (err) {
+	case MESH_STATUS_SUCCESS: return "Success";
+	case MESH_STATUS_INVALID_ADDRESS: return "Invalid Address";
+	case MESH_STATUS_INVALID_MODEL: return "Invalid Model";
+	case MESH_STATUS_INVALID_APPKEY: return "Invalid AppKey";
+	case MESH_STATUS_INVALID_NETKEY: return "Invalid NetKey";
+	case MESH_STATUS_INSUFF_RESOURCES: return "Insufficient Resources";
+	case MESH_STATUS_IDX_ALREADY_STORED: return "Key Idx Already Stored";
+	case MESH_STATUS_INVALID_PUB_PARAM: return "Invalid Publish Parameters";
+	case MESH_STATUS_NOT_SUB_MOD: return "Not a Subscribe Model";
+	case MESH_STATUS_STORAGE_FAIL: return "Storage Failure";
+	case MESH_STATUS_FEATURE_NO_SUPPORT: return "Feature Not Supported";
+	case MESH_STATUS_CANNOT_UPDATE: return "Cannot Update";
+	case MESH_STATUS_CANNOT_REMOVE: return "Cannot Remove";
+	case MESH_STATUS_CANNOT_BIND: return "Cannot bind";
+	case MESH_STATUS_UNABLE_CHANGE_STATE: return "Unable to change state";
+	case MESH_STATUS_CANNOT_SET: return "Cannot set";
+	case MESH_STATUS_UNSPECIFIED_ERROR: return "Unspecified error";
+	case MESH_STATUS_INVALID_BINDING: return "Invalid Binding";
+
+	default: return "Unknown";
+	}
+}
+
+struct mesh_net *mesh_get_net(struct bt_mesh *mesh)
+{
+	if (!mesh)
+		return NULL;
+
+	return mesh->net;
+}
diff --git a/meshd/src/node.c b/meshd/src/node.c
new file mode 100644
index 000000000..b53da7d26
--- /dev/null
+++ b/meshd/src/node.c
@@ -0,0 +1,851 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017-2018  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 "meshd/common/mesh-defs.h"
+
+#include "meshd/src/mesh.h"
+#include "meshd/src/net.h"
+#include "meshd/src/node.h"
+#include "meshd/src/storage.h"
+#include "meshd/src/appkey.h"
+#include "meshd/src/model.h"
+
+#define MIN_COMP_SIZE 14
+
+struct node_element {
+	struct l_queue *models;
+	uint16_t location;
+	uint8_t idx;
+};
+
+struct node_composition {
+	uint16_t cid;
+	uint16_t pid;
+	uint16_t vid;
+	uint16_t crpl;
+};
+
+struct mesh_node {
+	struct mesh_net *net;
+	struct l_queue *net_keys;
+	struct l_queue *app_keys;
+	struct l_queue *elements;
+	time_t upd_sec;
+	uint32_t seq_number;
+	uint32_t seq_min_cache;
+	uint16_t primary;
+	uint16_t num_ele;
+	uint8_t dev_uuid[16];
+	uint8_t dev_key[16];
+	uint8_t ttl;
+	bool provisioner;
+	struct node_composition *comp;
+	struct {
+		uint16_t interval;
+		uint8_t cnt;
+		uint8_t mode;
+	} relay;
+	uint8_t lpn;
+	uint8_t proxy;
+	uint8_t friend;
+	uint8_t beacon;
+};
+
+static struct l_queue *nodes;
+
+static bool match_node_unicast(const void *a, const void *b)
+{
+	const struct mesh_node *node = a;
+	uint16_t dst = L_PTR_TO_UINT(b);
+
+	return (dst >= node->primary &&
+		dst <= (node->primary + node->num_ele - 1));
+}
+
+static bool match_device_uuid(const void *a, const void *b)
+{
+	const struct mesh_node *node = a;
+	const uint8_t *uuid = b;
+
+	return (memcmp(node->dev_uuid, uuid, 16) == 0);
+}
+
+static bool match_element_idx(const void *a, const void *b)
+{
+	const struct node_element *element = a;
+	uint32_t index = L_PTR_TO_UINT(b);
+
+	return (element->idx == index);
+}
+
+static bool match_key_idx(const void *a, const void *b)
+{
+	return (L_PTR_TO_UINT(a) == L_PTR_TO_UINT(b));
+}
+
+static bool match_model_id(const void *a, const void *b)
+{
+	const struct mesh_model *model = a;
+	uint32_t id = L_PTR_TO_UINT(b);
+
+	return (mesh_model_get_model_id(model) == id);
+}
+
+struct mesh_node *node_find_by_addr(uint16_t addr)
+{
+	if (!IS_UNICAST(addr))
+		return NULL;
+
+	return l_queue_find(nodes, match_node_unicast, L_UINT_TO_PTR(addr));
+}
+
+struct mesh_node *node_find_by_uuid(uint8_t uuid[16])
+{
+	return l_queue_find(nodes, match_device_uuid, uuid);
+}
+
+uint8_t *node_uuid_get(struct mesh_node *node)
+{
+	if (!node)
+		return NULL;
+	return node->dev_uuid;
+}
+
+struct mesh_node *node_new(void)
+{
+	struct mesh_node *node;
+
+	node = l_new(struct mesh_node, 1);
+
+	if (!node)
+		return NULL;
+
+	l_queue_push_tail(nodes, node);
+
+	return node;
+}
+
+static void element_free(void *data)
+{
+	struct node_element *element = data;
+
+	l_queue_destroy(element->models, mesh_model_free);
+	l_free(element);
+}
+
+static void free_node_resources(void *data)
+{
+	struct mesh_node *node = data;
+
+	l_queue_destroy(node->net_keys, NULL);
+	l_queue_destroy(node->app_keys, NULL);
+	l_queue_destroy(node->elements, element_free);
+	l_free(node->comp);
+
+	if (node->net)
+		mesh_net_unref(node->net);
+
+	l_free(node);
+}
+
+void node_free(struct mesh_node *node)
+{
+	if (!node)
+		return;
+	l_queue_remove(nodes, node);
+	free_node_resources(node);
+}
+
+static bool add_models(struct mesh_net *net, struct node_element *ele,
+						struct mesh_db_element *db_ele)
+{
+	const struct l_queue_entry *entry;
+
+	if (!ele->models)
+		ele->models = l_queue_new();
+	if (!ele->models)
+		return false;
+
+	entry = l_queue_get_entries(db_ele->models);
+	for (; entry; entry = entry->next) {
+		struct mesh_model *mod;
+		struct mesh_db_model *db_mod;
+
+		db_mod = entry->data;
+		mod = mesh_model_init(net, ele->idx, db_mod);
+		if (!mod)
+			return false;
+
+		l_queue_push_tail(ele->models, mod);
+	}
+
+	return true;
+}
+
+static bool add_element(struct mesh_node *node, struct mesh_db_element *db_ele)
+{
+	struct node_element *ele;
+
+	ele = l_new(struct node_element, 1);
+	if (!ele)
+		return false;
+
+	ele->idx = db_ele->index;
+	ele->location = db_ele->location;
+
+	if (!db_ele->models || !add_models(node->net, ele, db_ele))
+		return false;
+
+	l_queue_push_tail(node->elements, ele);
+	return true;
+}
+
+static bool add_elements(struct mesh_node *node, struct mesh_db_node *db_node)
+{
+	const struct l_queue_entry *entry;
+
+	if (!node->elements)
+		node->elements = l_queue_new();
+
+	if (!node->elements)
+		return false;
+
+	entry = l_queue_get_entries(db_node->elements);
+	for (; entry; entry = entry->next)
+		if (!add_element(node, entry->data))
+			return false;
+
+	return true;
+}
+
+struct mesh_node *node_create_from_storage(struct mesh_net *net,
+						struct mesh_db_node *db_node,
+								bool local)
+{
+	struct mesh_node *node;
+	unsigned int num_ele;
+
+	if (local && !net)
+		return NULL;
+
+	node = node_new();
+	if (!node)
+		return NULL;
+
+	node->comp = l_new(struct node_composition, 1);
+	if (!node->comp) {
+		node_free(node);
+		return NULL;
+	}
+
+	node->comp->cid = db_node->cid;
+	node->comp->pid = db_node->pid;
+	node->comp->vid = db_node->vid;
+	node->comp->crpl = db_node->crpl;
+	node->lpn = db_node->modes.lpn;
+
+	node->proxy = db_node->modes.proxy;
+	node->lpn = db_node->modes.lpn;
+	node->friend = db_node->modes.friend;
+	node->relay.mode = db_node->modes.relay.state;
+	node->relay.cnt = db_node->modes.relay.cnt;
+	node->relay.interval = db_node->modes.relay.interval;
+	node->beacon = db_node->modes.beacon;
+
+	l_info("relay %2.2x, proxy %2.2x, lpn %2.2x, friend %2.2x",
+	       node->relay.mode, node->proxy, node->friend, node->lpn);
+	node->ttl = db_node->ttl;
+	node->seq_number = db_node->seq_number;
+
+	num_ele = l_queue_length(db_node->elements);
+	if (num_ele > 0xff) {
+		node_free(node);
+		return NULL;
+	}
+
+	node->num_ele = num_ele;
+	if (num_ele != 0 && !add_elements(node, db_node)) {
+		node_free(node);
+		return NULL;
+	}
+
+	node->primary = db_node->unicast;
+
+	memcpy(node->dev_uuid, db_node->uuid, 16);
+
+	if (local)
+		node->net = mesh_net_ref(net);
+
+	return node;
+}
+
+void node_cleanup(struct mesh_net *net)
+{
+	struct mesh_node *node;
+
+	if (!net)
+		return;
+	node = mesh_net_local_node_get(net);
+	if (node)
+		node_free(node);
+
+	l_queue_destroy(nodes, free_node_resources);
+
+}
+
+bool node_is_provisioned(struct mesh_node *node)
+{
+	return (!IS_UNASSIGNED(node->primary));
+}
+
+bool node_net_key_delete(struct mesh_node *node, uint16_t idx)
+{
+	if (!node)
+		return false;
+
+	if (!l_queue_find(node->net_keys, match_key_idx, L_UINT_TO_PTR(idx)))
+		return false;
+
+	l_queue_remove(node->net_keys, L_UINT_TO_PTR(idx));
+	/* TODO: remove all associated app keys and bindings */
+	return true;
+}
+
+bool node_app_key_delete(struct mesh_net *net, uint16_t addr,
+				uint16_t net_idx, uint16_t app_idx)
+{
+	struct mesh_node *node;
+	uint32_t index;
+	const struct l_queue_entry *entry;
+
+	node = node_find_by_addr(addr);
+	if (!node)
+		return false;
+
+	index = (net_idx << 16) + app_idx;
+
+	if (!l_queue_find(node->app_keys, match_key_idx, L_UINT_TO_PTR(index)))
+		return false;
+
+	l_queue_remove(node->app_keys, L_UINT_TO_PTR(index));
+
+	storage_local_app_key_del(net, net_idx, app_idx);
+
+	entry = l_queue_get_entries(node->elements);
+	for (; entry; entry = entry->next) {
+		struct node_element *ele = entry->data;
+
+		mesh_model_app_key_delete(net, ele->models, app_idx);
+	}
+
+	return true;
+}
+
+bool node_set_primary(struct mesh_node *node, uint16_t unicast)
+{
+	if (!node)
+		return false;
+
+	node->primary = unicast;
+
+	/* If local node, save to storage */
+	if (node->net)
+		return storage_local_set_unicast(node->net, unicast);
+
+	/* TODO: for provisioner, store remote node info */
+	return true;
+}
+
+uint16_t node_get_primary(struct mesh_node *node)
+{
+	if (!node)
+		return UNASSIGNED_ADDRESS;
+	else
+		return node->primary;
+}
+
+bool node_set_device_key(struct mesh_node *node, uint8_t key[16])
+
+{
+	if (!node || !key)
+		return false;
+
+	memcpy(node->dev_key, key, 16);
+
+	/* If local node, save to storage */
+	if (node->net)
+		return storage_local_set_device_key(node->net, key);
+
+	/* TODO: for provisioner, store remote node info */
+	return true;
+}
+
+const uint8_t *node_get_device_key(struct mesh_node *node)
+{
+	if (!node)
+		return NULL;
+	else
+		return node->dev_key;
+}
+
+uint8_t node_get_num_elements(struct mesh_node *node)
+{
+	return node->num_ele;
+}
+
+struct l_queue *node_get_net_keys(struct mesh_node *node)
+{
+	if (!node)
+		return NULL;
+	else
+		return node->net_keys;
+}
+
+struct l_queue *node_get_app_keys(struct mesh_node *node)
+{
+	if (!node)
+		return NULL;
+	else
+		return node->app_keys;
+}
+
+struct l_queue *node_get_element_models(struct mesh_node *node,
+						uint8_t ele_idx, int *status)
+{
+	struct node_element *ele;
+
+	if (!node) {
+		if (status)
+			*status = MESH_STATUS_INVALID_ADDRESS;
+		return NULL;
+	}
+
+	ele = l_queue_find(node->elements, match_element_idx,
+							L_UINT_TO_PTR(ele_idx));
+	if (!ele) {
+		if (status)
+			*status = MESH_STATUS_INVALID_ADDRESS;
+		return NULL;
+	}
+
+	if (status)
+		*status = MESH_STATUS_SUCCESS;
+
+	return ele->models;
+}
+
+struct mesh_model *node_get_model(struct mesh_node *node, uint8_t ele_idx,
+						uint32_t id, int *status)
+{
+	struct l_queue *models;
+	struct mesh_model *model;
+
+	if (!node) {
+		if (status)
+			*status = MESH_STATUS_INVALID_ADDRESS;
+		return NULL;
+	}
+
+	models = node_get_element_models(node, ele_idx, status);
+	if (!models)
+		return NULL;
+
+	model = l_queue_find(models, match_model_id, L_UINT_TO_PTR(id));
+
+	if (status)
+		*status = (model) ? MESH_STATUS_SUCCESS :
+						MESH_STATUS_INVALID_MODEL;
+
+	return model;
+}
+
+uint8_t node_default_ttl_get(struct mesh_node *node)
+{
+	if (!node)
+		return DEFAULT_TTL;
+	return node->ttl;
+}
+
+bool node_default_ttl_set(struct mesh_node *node, uint8_t ttl)
+{
+	bool res, is_local;
+
+	if (!node)
+		return false;
+
+	is_local = (node->net && mesh_net_local_node_get(node->net) == node) ?
+		true : false;
+
+	res = storage_local_set_ttl(node->net, ttl);
+
+	if (res) {
+		node->ttl = ttl;
+		if (is_local)
+			mesh_net_set_default_ttl(node->net, ttl);
+	}
+
+	return res;
+}
+
+bool node_set_sequence_number(struct mesh_node *node, uint32_t seq)
+{
+	bool is_local;
+	struct timeval write_time;
+
+
+	if (!node)
+		return false;
+
+	node->seq_number = seq;
+
+	is_local = (node->net && mesh_net_local_node_get(node->net) == node) ?
+		true : false;
+
+	if (!is_local)
+		return true;
+
+	/*
+	 * Holistically determine worst case 5 minute sequence consumption
+	 * so that we typically (once we reach a steady state) rewrite the
+	 * local node file with a new seq cache value no more than once every
+	 * five minutes (or more)
+	 */
+	gettimeofday(&write_time, NULL);
+	if (node->upd_sec) {
+		uint32_t elapsed = write_time.tv_sec - node->upd_sec;
+
+		if (elapsed < MIN_SEQ_CACHE_TIME) {
+			uint32_t ideal = node->seq_min_cache;
+
+			l_info("Old Seq Cache: %d", node->seq_min_cache);
+
+			ideal *= (MIN_SEQ_CACHE_TIME / elapsed);
+
+			if (ideal > node->seq_min_cache + MIN_SEQ_CACHE)
+				node->seq_min_cache = ideal;
+			else
+				node->seq_min_cache += MIN_SEQ_CACHE;
+
+			l_info("New Seq Cache: %d", node->seq_min_cache);
+		}
+	}
+
+	node->upd_sec = write_time.tv_sec;
+
+	l_info("Storage-Write");
+	return storage_local_write_sequence_number(node->net, seq);
+}
+
+uint32_t node_get_sequence_number(struct mesh_node *node)
+{
+	if (!node)
+		return 0xffffffff;
+
+	return node->seq_number;
+}
+
+uint32_t node_seq_cache(struct mesh_node *node)
+{
+	if (node->seq_min_cache < MIN_SEQ_CACHE)
+		node->seq_min_cache = MIN_SEQ_CACHE;
+
+	return node->seq_min_cache;
+}
+
+int node_get_element_idx(struct mesh_node *node, uint16_t ele_addr)
+{
+	uint16_t addr;
+	uint8_t num_ele;
+
+	if (!node)
+		return -1;
+
+	num_ele = node_get_num_elements(node);
+	if (!num_ele)
+		return -2;
+
+	addr = node_get_primary(node);
+
+	if (ele_addr < addr || ele_addr >= addr + num_ele)
+		return -3;
+	else
+		return ele_addr - addr;
+}
+
+uint16_t node_get_crpl(struct mesh_node *node)
+{
+	if (!node)
+		return 0;
+
+	return node->comp->crpl;
+}
+
+uint8_t node_relay_mode_get(struct mesh_node *node, uint8_t *count,
+							uint16_t *interval)
+{
+	if (!node) {
+		*count = 0;
+		*interval = 0;
+		return MESH_MODE_DISABLED;
+	}
+
+	*count = node->relay.cnt;
+	*interval = node->relay.interval;
+	return node->relay.mode;
+}
+
+uint8_t node_lpn_mode_get(struct mesh_node *node)
+{
+	if (!node)
+		return MESH_MODE_DISABLED;
+
+	return node->lpn;
+}
+
+bool node_relay_mode_set(struct mesh_node *node, bool enable, uint8_t cnt,
+							uint16_t interval)
+{
+	bool res, is_local;
+
+	if (!node || node->relay.mode == MESH_MODE_UNSUPPORTED)
+		return false;
+
+	is_local = (node->net && mesh_net_local_node_get(node->net) == node) ?
+		true : false;
+
+	res = storage_local_set_relay(node->net, enable, cnt, interval);
+
+	if (res) {
+		node->relay.mode = enable ? MESH_MODE_ENABLED :
+							MESH_MODE_DISABLED;
+		node->relay.cnt = cnt;
+		node->relay.interval = interval;
+		if (is_local)
+			mesh_net_set_relay_mode(node->net, enable, cnt,
+								interval);
+	}
+
+	return res;
+}
+
+bool node_proxy_mode_set(struct mesh_node *node, bool enable)
+{
+	bool res, is_local;
+	uint8_t proxy;
+
+	if (!node || node->proxy == MESH_MODE_UNSUPPORTED)
+		return false;
+
+	is_local = (node->net && mesh_net_local_node_get(node->net) == node) ?
+		true : false;
+
+	proxy = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED;
+	res = storage_local_set_mode(node->net, proxy, "proxy");
+
+	if (res) {
+		node->proxy = proxy;
+		if (is_local)
+			mesh_net_set_proxy_mode(node->net, enable);
+	}
+
+	return res;
+}
+
+uint8_t node_proxy_mode_get(struct mesh_node *node)
+{
+	if (!node)
+		return MESH_MODE_DISABLED;
+
+	return node->proxy;
+}
+
+bool node_beacon_mode_set(struct mesh_node *node, bool enable)
+{
+	bool res, is_local;
+	uint8_t beacon;
+
+	if (!node)
+		return false;
+
+	is_local = (node->net && mesh_net_local_node_get(node->net) == node) ?
+		true : false;
+
+	beacon = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED;
+	res = storage_local_set_mode(node->net, beacon, "beacon");
+
+	if (res) {
+		node->beacon = beacon;
+		if (is_local)
+			mesh_net_set_beacon_mode(node->net, enable);
+	}
+
+	return res;
+}
+
+uint8_t node_beacon_mode_get(struct mesh_node *node)
+{
+	if (!node)
+		return MESH_MODE_DISABLED;
+
+	return node->beacon;
+}
+
+bool node_friend_mode_set(struct mesh_node *node, bool enable)
+{
+	bool res, is_local;
+	uint8_t friend;
+
+	if (!node || node->friend == MESH_MODE_UNSUPPORTED)
+		return false;
+
+	is_local = (node->net && mesh_net_local_node_get(node->net) == node) ?
+		true : false;
+
+	friend = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED;
+	res = storage_local_set_mode(node->net, friend, "friend");
+
+	if (res) {
+		node->friend = friend;
+		if (is_local)
+			mesh_net_set_friend_mode(node->net, enable);
+	}
+
+	return res;
+}
+
+uint8_t node_friend_mode_get(struct mesh_node *node)
+{
+	if (!node)
+		return MESH_MODE_DISABLED;
+
+	return node->friend;
+}
+
+uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf, uint16_t sz)
+{
+	uint16_t n, features;
+	const struct l_queue_entry *ele_entry;
+
+	if (!node || !node->comp || sz < MIN_COMP_SIZE)
+		return 0;
+
+	n = 0;
+
+	l_put_le16(node->comp->cid, buf + n);
+	n += 2;
+	l_put_le16(node->comp->pid, buf + n);
+	n += 2;
+	l_put_le16(node->comp->vid, buf + n);
+	n += 2;
+	l_put_le16(node->comp->crpl, buf + n);
+	n += 2;
+
+	features = 0;
+
+	if (node->relay.mode != MESH_MODE_UNSUPPORTED)
+		features |= FEATURE_RELAY;
+	if (node->proxy != MESH_MODE_UNSUPPORTED)
+		features |= FEATURE_PROXY;
+	if (node->friend != MESH_MODE_UNSUPPORTED)
+		features |= FEATURE_FRIEND;
+	if (node->lpn != MESH_MODE_UNSUPPORTED)
+		features |= FEATURE_LPN;
+
+	l_put_le16(features, buf + n);
+	n += 2;
+
+	ele_entry = l_queue_get_entries(node->elements);
+	for (; ele_entry; ele_entry = ele_entry->next) {
+		struct node_element *ele = ele_entry->data;
+		const struct l_queue_entry *mod_entry;
+		uint8_t num_s = 0, num_v = 0;
+		uint8_t *mod_buf;
+
+		/* At least fit location and zeros for number of models */
+		if ((n + 4) > sz)
+			return n;
+		l_info("ele->location %d", ele->location);
+		l_put_le16(ele->location, buf + n);
+		n += 2;
+
+		/* Store models IDs, store num_s and num_v later */
+		mod_buf = buf + n;
+		n += 2;
+
+		/* Get SIG models */
+		mod_entry = l_queue_get_entries(ele->models);
+		for (; mod_entry; mod_entry = mod_entry->next) {
+			struct mesh_model *mod = mod_entry->data;
+			uint32_t mod_id;
+
+			mod_id = mesh_model_get_model_id(
+					(const struct mesh_model *) mod);
+
+			if ((mod_id >> 16) == 0xffff) {
+				if (n + 2 > sz)
+					goto element_done;
+
+				l_put_le16((uint16_t) (mod_id & 0xffff),
+								buf + n);
+				n += 2;
+				num_s++;
+			}
+		}
+
+		/* Get vendor models */
+		mod_entry = l_queue_get_entries(ele->models);
+		for (; mod_entry; mod_entry = mod_entry->next) {
+			struct mesh_model *mod = mod_entry->data;
+			uint32_t mod_id;
+			uint16_t vendor;
+
+			mod_id = mesh_model_get_model_id(
+					(const struct mesh_model *) mod);
+
+			vendor = (uint16_t) (mod_id >> 16);
+			if (vendor != 0xffff) {
+				if (n + 4 > sz)
+					goto element_done;
+
+				l_put_le16(vendor, buf + n);
+				n += 2;
+				l_put_le16((uint16_t) (mod_id & 0xffff),
+								buf + n);
+				n += 2;
+				num_v++;
+			}
+
+		}
+
+element_done:
+		mod_buf[0] = num_s;
+		mod_buf[1] = num_v;
+
+	}
+
+	return n;
+}
diff --git a/meshd/src/storage.c b/meshd/src/storage.c
new file mode 100644
index 000000000..2d1b13e0b
--- /dev/null
+++ b/meshd/src/storage.c
@@ -0,0 +1,673 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017-2018  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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <json-c/json.h>
+#include <ell/ell.h>
+
+#include "mesh/mesh-net.h"
+#include "meshd/common/mesh-defs.h"
+
+#include "meshd/src/mesh.h"
+#include "meshd/src/node.h"
+
+#include "meshd/src/net.h"
+#include "meshd/src/appkey.h"
+#include "meshd/src/model.h"
+#include "meshd/src/storage.h"
+
+/*
+ * TODO: figure out naming convention to store alternative nodes
+ * Mesh storage dir wil be in configure.ac
+ */
+#define DEVICE_COMPOSITION_FILE "../config/composition.json"
+#define NODE_CONGIGURATION_FILE "../config/configuration.json"
+
+static bool read_local_node_cb(struct mesh_db_node *db_node, void *user_data)
+{
+	struct mesh_net *net = user_data;
+	struct mesh_node *node;
+	uint32_t seq_number;
+	uint16_t crpl;
+	uint8_t ttl, mode, cnt, num_ele;
+	uint16_t unicast, interval;
+	uint8_t *uuid;
+
+	if (!net)
+		return false;
+
+	node = node_create_from_storage(net, db_node, true);
+	if (!node)
+		return false;
+
+	mesh_net_local_node_set(net, node, db_node->provisioner);
+	seq_number = node_get_sequence_number(node);
+	mesh_net_set_seq_num(net, seq_number);
+	ttl = node_default_ttl_get(node);
+	mesh_net_set_default_ttl(net, ttl);
+	crpl = node_get_crpl(node);
+	mesh_net_set_crpl(net, crpl);
+
+	mode = node_proxy_mode_get(node);
+	if (mode == MESH_MODE_ENABLED || mode == MESH_MODE_DISABLED)
+		mesh_net_set_proxy_mode(net, mode == MESH_MODE_ENABLED);
+
+	mode = node_friend_mode_get(node);
+	if (mode == MESH_MODE_ENABLED || mode == MESH_MODE_DISABLED)
+		mesh_net_set_friend_mode(net, mode == MESH_MODE_ENABLED);
+
+	mode = node_relay_mode_get(node, &cnt, &interval);
+	if (mode == MESH_MODE_ENABLED || mode == MESH_MODE_DISABLED)
+		mesh_net_set_relay_mode(net, mode == MESH_MODE_ENABLED, cnt,
+								interval);
+
+	mode = node_beacon_mode_get(node);
+	if (mode == MESH_MODE_ENABLED || mode == MESH_MODE_DISABLED)
+		mesh_net_set_beacon_mode(net, mode == MESH_MODE_ENABLED);
+
+	unicast = db_node->unicast;
+	num_ele = node_get_num_elements(node);
+
+	if (!IS_UNASSIGNED(unicast) &&
+		!mesh_net_register_unicast(net, unicast, num_ele))
+		return false;
+
+	uuid = node_uuid_get(node);
+	if (uuid)
+		mesh_net_id_uuid_set(net, uuid);
+	return true;
+}
+
+static bool read_net_keys_cb(uint16_t idx, uint8_t *key, uint8_t *new_key,
+						int phase, void *user_data)
+{
+	struct mesh_net *net = user_data;
+
+	if (!net)
+		return false;
+
+	if (mesh_net_add_key(net, false, idx, key) != MESH_STATUS_SUCCESS)
+		return false;
+	/* TODO: handle restoring key refresh phase and new keys */
+
+	return true;
+}
+
+static bool read_app_keys_cb(uint16_t net_idx, uint16_t app_idx, uint8_t *key,
+					uint8_t *new_key, void *user_data)
+{
+	struct mesh_net *net = user_data;
+
+	if (!net)
+		return false;
+
+	return appkey_key_init(net, net_idx, app_idx, key, new_key);
+}
+
+static bool parse_local_node(struct mesh_net *net, json_object *jnode)
+{
+	bool bvalue;
+	uint32_t iv_index;
+	uint8_t key_buf[16];
+	uint8_t cnt;
+	uint16_t interval;
+
+	if (mesh_db_read_iv_index(jnode, &iv_index, &bvalue))
+		mesh_net_set_iv_index(net, iv_index, bvalue);
+
+	if (mesh_db_read_net_transmit(jnode, &cnt, &interval))
+		mesh_net_transmit_params_set(net, cnt, interval);
+
+	/* Node composition/configuration info */
+	if (!mesh_db_read_node(jnode, read_local_node_cb, net))
+		return false;
+
+	if (!mesh_db_read_net_keys(jnode, read_net_keys_cb, net))
+		return false;
+
+	/* TODO: use the actual "primary" network index for this node */
+	if (mesh_db_read_device_key(jnode, key_buf) &&
+		!node_set_device_key(mesh_net_local_node_get(net), key_buf))
+		return false;
+
+	mesh_db_read_app_keys(jnode, read_app_keys_cb, net);
+
+	return true;
+}
+
+static bool read_unprov_device_cb(struct mesh_db_node *db_node, void *user_data)
+{
+	struct mesh_net *net = user_data;
+	struct mesh_node *node;
+	uint16_t crpl;
+	uint8_t *uuid;
+
+	if (!net)
+		return false;
+
+	node = node_create_from_storage(net, db_node, true);
+
+	if (!node)
+		return false;
+
+	mesh_net_local_node_set(net, node, db_node->provisioner);
+	crpl = node_get_crpl(node);
+	mesh_net_set_crpl(net, crpl);
+
+	uuid = node_uuid_get(node);
+	if (uuid)
+		mesh_net_id_uuid_set(net, uuid);
+
+	return true;
+}
+
+static bool parse_unprovisioned_device(struct mesh_net *net, json_object *jnode)
+{
+	struct mesh_db_prov prov;
+	struct mesh_net_prov_caps *caps;
+	struct mesh_node *node;
+
+	/* Node composition/configuration info */
+	if (!mesh_db_read_unprovisioned_device(jnode,
+					read_unprov_device_cb, net))
+		return false;
+
+	if (!mesh_db_read_prov_info(jnode, &prov))
+		return false;
+
+	caps = mesh_net_prov_caps_get(net);
+	if (!caps)
+		return false;
+
+	node = mesh_net_local_node_get(net);
+	if (!node)
+		return false;
+
+	caps->num_ele = node_get_num_elements(node);
+	l_put_le16(prov.algorithm, &caps->algorithms);
+	caps->pub_type = prov.pub_type;
+	caps->static_type = prov.static_type;
+	caps->output_size = prov.output_oob.size;
+	l_put_le16(prov.output_oob.actions, &caps->output_action);
+	caps->input_size = prov.input_oob.size;
+	l_put_le16(prov.input_oob.actions, &caps->input_action);
+
+	return mesh_net_priv_key_set(net, prov.priv_key);
+}
+
+static bool parse_config(struct mesh_net *net, const char *config_name,
+							bool unprovisioned)
+{
+	int fd;
+	char *str;
+	const char *out;
+	struct stat st;
+	ssize_t sz;
+	json_object *jnode = NULL;
+	bool result = false;
+
+	if (!config_name)
+		return false;
+
+	fd = open(config_name, O_RDONLY);
+	if (!fd)
+		return false;
+
+	if (fstat(fd, &st) == -1) {
+		close(fd);
+		return false;
+	}
+
+	str = (char *) l_new(char, st.st_size + 1);
+	if (!str) {
+		close(fd);
+		return false;
+	}
+
+	sz = read(fd, str, st.st_size);
+	if (sz != st.st_size) {
+		l_error("Failed to read configuration file");
+		goto done;
+	}
+
+	jnode = json_tokener_parse(str);
+	if (!jnode)
+		goto done;
+
+	mesh_net_jconfig_set(net, jnode);
+
+	if (!unprovisioned)
+		result = parse_local_node(net, jnode);
+	else
+		result = parse_unprovisioned_device(net, jnode);
+
+	if (!result) {
+		storage_release(net);
+		goto done;
+	}
+
+	mesh_net_cfg_file_get(net, &out);
+	if (!out)
+		mesh_net_cfg_file_set(net, !unprovisioned ?
+					config_name : NODE_CONGIGURATION_FILE);
+done:
+	close(fd);
+	if (str)
+		l_free(str);
+
+	return result;
+}
+
+bool storage_parse_config(struct mesh_net *net, const char *config_name)
+{
+	bool result = false;
+	bool unprovisioned = !config_name;
+
+	if (unprovisioned) {
+		result = parse_config(net, DEVICE_COMPOSITION_FILE, true);
+		goto done;
+	}
+
+	result = parse_config(net, config_name, false);
+
+	if (!result) {
+		char *bak = (char *) l_malloc(strlen(config_name) + 5);
+
+		if (!bak)
+			goto done;
+
+		/* Fall-back to Backup version */
+		strncpy(bak, config_name, strlen(config_name) + 1);
+		bak = strncat(bak, ".bak", 5);
+
+		remove(config_name);
+		rename(bak, config_name);
+
+		result = parse_config(net, config_name, false);
+
+		l_free(bak);
+	}
+
+	/* If configuration read fails, try as unprovisioned device */
+	if (!result) {
+		l_info("Parse configuration failed, trying unprovisioned");
+		unprovisioned = true;
+		result = parse_config(net, DEVICE_COMPOSITION_FILE, true);
+	}
+
+done:
+	if (result)
+		mesh_net_provisioned_set(net, !unprovisioned);
+
+	return result;
+}
+
+bool storage_local_set_ttl(struct mesh_net *net, uint8_t ttl)
+{
+	json_object *jnode;
+
+	if (!net)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	return mesh_db_write_int(jnode, "defaultTTL", ttl);
+}
+
+bool storage_local_set_relay(struct mesh_net *net, bool enable,
+				uint8_t count, uint8_t interval)
+{
+	json_object *jnode;
+
+	if (!net)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	return mesh_db_write_relay_mode(jnode, enable, count, interval);
+}
+
+bool storage_local_set_transmit_params(struct mesh_net *net, uint8_t count,
+							uint8_t interval)
+{
+	json_object *jnode;
+
+	if (!net)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	return mesh_db_write_net_transmit(jnode, count, interval);
+}
+
+bool storage_local_set_mode(struct mesh_net *net, uint8_t mode,
+						const char *mode_name)
+{
+	json_object *jnode;
+
+	if (!net || !mode_name)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	return mesh_db_write_mode(jnode, mode_name, mode);
+}
+
+bool storage_model_bind(struct mesh_net *net, uint16_t addr, uint32_t mod_id,
+				uint16_t app_idx, bool unbind)
+{
+	json_object *jnode;
+	bool is_local;
+
+	if (!net)
+		return false;
+
+	is_local = mesh_net_is_local_address(net, addr);
+	if (is_local) {
+		int ele_idx;
+		bool is_vendor = (mod_id > 0xffff);
+
+		ele_idx = node_get_element_idx(mesh_net_local_node_get(net),
+									addr);
+		if (ele_idx < 0)
+			return false;
+
+		jnode = mesh_net_jconfig_get(net);
+		if (!jnode)
+			return false;
+
+		if (unbind)
+			return mesh_db_model_binding_del(jnode, ele_idx,
+						is_vendor, mod_id, app_idx);
+		else
+			return mesh_db_model_binding_add(jnode, ele_idx,
+						is_vendor, mod_id, app_idx);
+	}
+
+	/* TODO: write remote node bindings to provisioner DB */
+	return false;
+}
+
+bool storage_local_app_key_add(struct mesh_net *net, uint16_t net_idx,
+			uint16_t app_idx, const uint8_t key[16], bool update)
+{
+	json_object *jnode;
+
+	if (!net)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	return mesh_db_app_key_add(jnode, net_idx, app_idx, key, update);
+}
+
+bool storage_local_app_key_del(struct mesh_net *net, uint16_t net_idx,
+					uint16_t app_idx)
+{
+	json_object *jnode;
+
+	if (!net)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	return mesh_db_app_key_del(jnode, net_idx, app_idx);
+
+}
+
+bool storage_local_net_key_add(struct mesh_net *net, uint16_t net_idx,
+					const uint8_t key[16], int phase)
+{
+	json_object *jnode;
+
+	if (!net)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	return mesh_db_net_key_add(jnode, net_idx, key, phase);
+}
+
+bool storage_local_net_key_del(struct mesh_net *net, uint16_t net_idx)
+{
+	json_object *jnode;
+
+	if (!net)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	return mesh_db_net_key_del(jnode, net_idx);
+}
+
+bool storage_local_set_iv_index(struct mesh_net *net, uint32_t iv_index,
+								bool update)
+{
+	json_object *jnode;
+
+	if (!net)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	return mesh_db_write_iv_index(jnode, iv_index, update);
+}
+
+bool storage_local_set_device_key(struct mesh_net *net, uint8_t dev_key[16])
+{
+	json_object *jnode;
+
+	if (!net)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	return mesh_db_write_device_key(jnode, dev_key);
+}
+
+bool storage_local_set_unicast(struct mesh_net *net, uint16_t unicast)
+{
+	json_object *jnode;
+
+	if (!net)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	return mesh_db_write_uint16_hex(jnode, "unicastAddress", unicast);
+}
+
+bool storage_local_write_sequence_number(struct mesh_net *net, uint32_t seq)
+{
+	json_object *jnode;
+	const char *cfg_file;
+	bool result;
+
+	if (!net)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	result = mesh_db_write_int(jnode, "sequenceNumber", seq);
+	if (!result)
+		return false;
+
+	result = mesh_net_cfg_file_get(net, &cfg_file);
+	if (result && cfg_file)
+		result = storage_save_config(net, cfg_file, false, NULL, NULL);
+
+	return result;
+}
+
+static bool save_config(struct mesh_net *net, const char *config_name)
+{
+	FILE *outfile;
+	const char *str;
+	json_object *jnode;
+	bool result = false;
+
+	if (!net || !config_name)
+		return false;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	outfile = fopen(config_name, "w");
+	if (!outfile) {
+		l_error("Failed to save configuration to %s", config_name);
+		return false;
+	}
+
+	str = json_object_to_json_string_ext(jnode, JSON_C_TO_STRING_PRETTY);
+
+	if (fwrite(str, sizeof(char), strlen(str), outfile) < strlen(str))
+		l_warn("Incomplete write of mesh configuration");
+	else
+		result = true;
+
+	fclose(outfile);
+
+	return result;
+}
+
+struct write_info {
+	const char *config_name;
+	struct mesh_net *net;
+	void *user_data;
+	mesh_status_func_t cb;
+};
+
+static void idle_save_config(void *user_data)
+{
+	struct write_info *info = user_data;
+	char *tmp = (char *) l_malloc(strlen(info->config_name) + 5);
+	char *bak = (char *) l_malloc(strlen(info->config_name) + 5);
+	bool result = false;
+
+	if (!tmp || !bak)
+		goto done;
+
+	strncpy(tmp, info->config_name, strlen(info->config_name)  + 1);
+	strncpy(bak, info->config_name, strlen(info->config_name)  + 1);
+	tmp = strncat(tmp, ".tmp", 5);
+	bak = strncat(bak, ".bak", 5);
+	remove(tmp);
+
+	l_debug("Storage-Wrote");
+	result = save_config(info->net, tmp);
+
+	if (result) {
+		remove(bak);
+		rename(info->config_name, bak);
+		rename(tmp, info->config_name);
+	}
+
+	remove(tmp);
+done:
+	l_free(tmp);
+	l_free(bak);
+
+	if (info->cb)
+		info->cb(info->user_data, result);
+
+	l_free(info);
+}
+
+bool storage_save_config(struct mesh_net *net, const char *config_name,
+			bool no_wait, mesh_status_func_t cb, void *user_data)
+{
+	struct write_info *info;
+
+	info = l_new(struct write_info, 1);
+	if (!info)
+		return false;
+
+	info->net = net;
+	info->config_name = config_name;
+	info->cb = cb;
+	info->user_data = user_data;
+
+	if (no_wait)
+		idle_save_config(info);
+	l_idle_oneshot(idle_save_config, info, NULL);
+
+	return true;
+}
+
+bool storage_save_new_config(struct mesh_net *net, const char *config_name,
+					mesh_status_func_t cb, void *user_data)
+{
+	json_object *jnode;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (!jnode)
+		return false;
+
+	mesh_db_remove_property(jnode, "provision");
+
+	return storage_save_config(net, config_name, false, cb, user_data);
+}
+
+void storage_release(struct mesh_net *net)
+{
+	json_object *jnode;
+
+	jnode = mesh_net_jconfig_get(net);
+	if (jnode)
+		json_object_put(jnode);
+
+	mesh_net_jconfig_set(net, NULL);
+}
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v2 07/11] meshd: Source code for handling access layer mux/demux
  2018-04-25 17:50 [PATCH BlueZ v2 00/11] Bluetooth Mesh Daemon Brian Gix
                   ` (3 preceding siblings ...)
  2018-04-25 17:50 ` [PATCH BlueZ v2 06/11] meshd: Source " Brian Gix
@ 2018-04-25 17:50 ` Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 08/11] meshd: Mesh config server model Brian Gix
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Brian Gix @ 2018-04-25 17:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Inga Stotland

From: Inga Stotland <inga.stotland@intel.com>

---
 meshd/src/model.c | 1274 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1274 insertions(+)
 create mode 100644 meshd/src/model.c

diff --git a/meshd/src/model.c b/meshd/src/model.c
new file mode 100644
index 000000000..0b87312c0
--- /dev/null
+++ b/meshd/src/model.c
@@ -0,0 +1,1274 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 "meshd/common/mesh-defs.h"
+
+#include "meshd/src/mesh.h"
+#include "meshd/src/crypto.h"
+#include "meshd/src/node.h"
+#include "meshd/src/net.h"
+#include "meshd/src/appkey.h"
+#include "meshd/src/model.h"
+#include "meshd/src/display.h"
+#include "meshd/src/cfgmod.h"
+#include "meshd/src/storage.h"
+
+struct mesh_model {
+	const struct mesh_model_ops *cbs;
+	void *user_data;
+	struct l_queue *bindings;
+	struct l_queue *subs;
+	struct l_queue *virtuals;
+	struct mesh_model_pub *pub;
+	uint32_t id;
+	uint8_t ele_idx;
+};
+
+struct mesh_virtual {
+	uint32_t id; /*Identifier of internally stored addr, min val 0x10000 */
+	uint16_t ota;
+	uint16_t ref_cnt;
+	uint8_t addr[16];
+};
+
+/* These struct is used to pass lots of params to l_queue_foreach */
+struct mod_forward {
+	struct mesh_virtual *virt;
+	const uint8_t *data;
+	uint16_t src;
+	uint16_t dst;
+	uint16_t unicast;
+	uint16_t idx;
+	uint16_t size;
+	uint8_t ttl;
+	int8_t rssi;
+	bool szmict;
+	bool done;
+};
+
+static struct l_queue *mesh_virtuals;
+
+static uint32_t virt_id_next = VIRTUAL_BASE;
+static struct timeval tx_start;
+
+static void unref_virt(void *data)
+{
+	struct mesh_virtual *virt = data;
+
+	if (virt->ref_cnt > 0)
+		virt->ref_cnt--;
+
+	if (virt->ref_cnt)
+		return;
+
+	l_queue_remove(mesh_virtuals, virt);
+	l_free(virt);
+}
+
+static bool simple_match(const void *a, const void *b)
+{
+	return a == b;
+}
+
+static bool find_virt_by_id(const void *a, const void *b)
+{
+	const struct mesh_virtual *virt = a;
+	uint32_t id = L_PTR_TO_UINT(b);
+
+	return virt->id == id;
+}
+
+static bool find_virt_by_addr(const void *a, const void *b)
+{
+	const struct mesh_virtual *virt = a;
+	const uint8_t *addr = b;
+
+	return memcmp(virt->addr, addr, 16) == 0;
+}
+
+static struct mesh_model *find_model(struct mesh_net *net, uint16_t addr,
+						uint32_t mod_id, int *fail)
+{
+	int ele_idx;
+	struct mesh_node *node;
+
+	node = mesh_net_local_node_get(net);
+
+	ele_idx = node_get_element_idx(node, addr);
+
+	if (ele_idx < 0) {
+		if (fail)
+			*fail = MESH_STATUS_INVALID_ADDRESS;
+		return NULL;
+	}
+
+	return node_get_model(node, (uint8_t) ele_idx, mod_id, fail);
+}
+
+static void forward_model(void *a, void *b)
+{
+	struct mesh_model *mod = a;
+	struct mod_forward *fwd = b;
+	struct mesh_virtual *virt;
+	uint32_t dst;
+	bool has_dst = false;
+
+	if (!mod->cbs || !mod->cbs->recv)
+		return;
+
+	l_debug("model %8.8x with idx %3.3x", mod->id, fwd->idx);
+	if (fwd->idx != APP_IDX_DEV &&
+		!l_queue_find(mod->bindings, simple_match,
+						L_UINT_TO_PTR(fwd->idx)))
+		return;
+
+	dst = fwd->dst;
+	if (dst == fwd->unicast || IS_ALL_NODES(dst))
+		has_dst = true;
+	else if (fwd->virt) {
+		virt = l_queue_find(mod->virtuals, simple_match, fwd->virt);
+		if (virt) {
+			/*
+			 * Map Virtual addresses to a usable namespace that
+			 * prevents us for forwarding a false positive
+			 * (multiple Virtual Addresses that map to the same
+			 * u16 OTA addr)
+			 */
+			has_dst = true;
+			dst = virt->id;
+		}
+	} else {
+		if (l_queue_find(mod->subs, simple_match, L_UINT_TO_PTR(dst)))
+			has_dst = true;
+	}
+
+
+	if (!has_dst)
+		return;
+
+	/*
+	 * TODO: models shall be registered with a list of supported opcodes and
+	 * element address. Iterate through the list of opcodes to see if the
+	 * model is an addressee.
+	 * If this is an internal registered model, check for a "bind" callback.
+	 * For an external ("user") model, send D-Bus method (signal?) (TBD)
+	 */
+	if (mod->cbs->recv)
+		mod->cbs->recv(fwd->src, dst, fwd->unicast, fwd->idx,
+				fwd->data, fwd->size, fwd->ttl, mod->user_data);
+
+	if (dst == fwd->unicast)
+		fwd->done = true;
+}
+
+static int dev_packet_decrypt(struct mesh_net *net, const uint8_t *data,
+				uint16_t size, bool szmict, uint16_t src,
+				uint16_t dst, uint8_t key_id, uint32_t seq,
+				uint32_t iv_idx, uint8_t *out)
+{
+	struct mesh_node *node;
+	const uint8_t *dev_key;
+
+	node = mesh_net_local_node_get(net);
+	dev_key = node_get_device_key(node);
+	if (!dev_key)
+		return false;
+
+	if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict, src,
+					dst, key_id, seq, iv_idx, out, dev_key))
+		return APP_IDX_DEV;
+
+	return -1;
+}
+
+static int virt_packet_decrypt(struct mesh_net *net, const uint8_t *data,
+				uint16_t size, bool szmict, uint16_t src,
+				uint16_t dst, uint8_t key_id, uint32_t seq,
+				uint32_t iv_idx, uint8_t *out,
+				struct mesh_virtual **decrypt_virt)
+{
+	const struct l_queue_entry *v;
+
+	for (v = l_queue_get_entries(mesh_virtuals); v; v = v->next) {
+		struct mesh_virtual *virt = v->data;
+		int decrypt_idx;
+
+		if (virt->ota != dst)
+			continue;
+
+		decrypt_idx = appkey_packet_decrypt(net, szmict, seq,
+							iv_idx, src, dst,
+							virt->addr, 16, key_id,
+							data, size, out);
+
+		if (decrypt_idx >= 0) {
+			*decrypt_virt = virt;
+			return decrypt_idx;
+		}
+	}
+
+	return -1;
+}
+
+static void cmplt(uint16_t remote, uint8_t status,
+					void *data, uint16_t size,
+					void *user_data)
+{
+	struct timeval tx_end;
+
+	if (status)
+		l_info("Tx-->%4.4x (%d octets) Failed (%d)",
+				remote, size, status);
+	else
+		l_info("Tx-->%4.4x (%d octets) Succeeded", remote, size);
+
+	/* print_packet("Sent Data", data, size); */
+
+	gettimeofday(&tx_end, NULL);
+	if (tx_end.tv_sec == tx_start.tv_sec) {
+		l_info("Duration 0.%zu seconds",
+				tx_end.tv_usec - tx_start.tv_usec);
+	} else {
+		if (tx_start.tv_usec > tx_end.tv_usec)
+			l_info("Duration %zu.%zu seconds",
+				tx_end.tv_sec - tx_start.tv_sec - 1,
+				tx_end.tv_usec + 1000000 - tx_start.tv_usec);
+		else
+			l_info("Duration %zu.%zu seconds",
+					tx_end.tv_sec - tx_start.tv_sec,
+					tx_end.tv_usec - tx_start.tv_usec);
+	}
+}
+
+static bool pub_frnd_cred(struct mesh_net *net, uint16_t src, uint32_t mod_id)
+{
+	struct mesh_model *mod = find_model(net, src, mod_id, NULL);
+
+	if (!mod || !mod->pub)
+		return false;
+
+	return (mod->pub->credential != 0);
+}
+
+static unsigned int msg_send(struct mesh_net *net, uint32_t mod_id,
+				uint16_t src, uint32_t dst,
+				uint8_t key_id, const uint8_t *key,
+				uint8_t *aad, uint8_t ttl,
+				const void *msg, uint16_t msg_len)
+{
+	unsigned int ret = 0;
+	uint32_t iv_index, seq_num;
+	uint8_t *out;
+	bool szmic = false;
+	uint16_t out_len = msg_len + sizeof(uint32_t);
+
+	/* Use large MIC if it doesn't affect segmentation */
+	if (msg_len > 11 && msg_len <= 376) {
+		if ((out_len / 12) == ((out_len + 4) / 12)) {
+			szmic = true;
+			out_len = msg_len + sizeof(uint64_t);
+		}
+	}
+
+	out = l_malloc(out_len);
+
+	iv_index = mesh_net_get_iv_index(net);
+
+	seq_num = mesh_net_get_seq_num(net);
+	if (!mesh_crypto_payload_encrypt(aad, msg, out, msg_len,
+				src, dst, key_id,
+				seq_num, iv_index,
+				szmic, key)) {
+		l_error("Failed to Encrypt Payload");
+		goto done;
+	}
+
+	/* print_packet("Encrypted with", key, 16); */
+
+	ret = mesh_net_app_send(net, pub_frnd_cred(net, src, mod_id),
+				src, dst, key_id, ttl,
+				seq_num, iv_index,
+				szmic,
+				out, out_len,
+				cmplt, NULL);
+done:
+	l_free(out);
+	return ret;
+}
+
+static void model_unbind_idx(void *a, void *b)
+{
+	struct mesh_model *mod = a;
+	uint16_t idx = L_PTR_TO_UINT(b);
+
+	if (idx == mod->pub->idx) {
+		mod->pub->addr = UNASSIGNED_ADDRESS;
+		/*
+		 * TODO: callback for internal model or
+		 * D-Bus signal/method "model publication changed" (TBD)
+		 */
+	}
+
+	l_queue_remove(mod->bindings, b);
+
+	if (mod->cbs->bind)
+		mod->cbs->bind(idx, ACTION_DELETE);
+}
+
+static int model_bind_idx(struct mesh_model *mod, uint16_t idx)
+{
+	if (l_queue_length(mod->bindings) >= MAX_BINDINGS)
+		return MESH_STATUS_INSUFF_RESOURCES;
+
+	if (!l_queue_push_tail(mod->bindings, L_UINT_TO_PTR(idx)))
+		return MESH_STATUS_INSUFF_RESOURCES;
+
+	if (mod->cbs->bind)
+		mod->cbs->bind(idx, ACTION_ADD);
+
+	return MESH_STATUS_SUCCESS;
+
+}
+
+static int update_binding(struct mesh_net *net, uint16_t addr, uint32_t id,
+				uint16_t app_idx, bool unbind)
+{
+	int fail;
+	struct mesh_model *mod;
+	int status;
+
+	mod = find_model(net, addr, id, &fail);
+	if (!mod) {
+		l_info("model not found");
+		return fail;
+	}
+
+	if (id == CONFIG_SRV_MODEL || id == CONFIG_CLI_MODEL)
+		return MESH_STATUS_INVALID_MODEL;
+
+	if (!l_queue_find(mod->bindings, simple_match, L_UINT_TO_PTR(app_idx)))
+		return MESH_STATUS_CANNOT_BIND;
+
+	if (!appkey_have_key(net, app_idx))
+		return MESH_STATUS_INVALID_APPKEY;
+
+	if (unbind) {
+		model_unbind_idx(mod, &app_idx);
+
+		if (!storage_model_bind(net, addr, id, app_idx, true))
+			return MESH_STATUS_STORAGE_FAIL;
+
+		return MESH_STATUS_SUCCESS;
+	}
+
+	status = model_bind_idx(mod, app_idx);
+	if (status != MESH_STATUS_SUCCESS)
+		return status;
+
+	if (!storage_model_bind(net, addr, id, app_idx, false)) {
+		model_unbind_idx(mod, &app_idx);
+		return MESH_STATUS_STORAGE_FAIL;
+	}
+
+	return MESH_STATUS_SUCCESS;
+
+}
+
+static int set_pub(struct mesh_model *mod, const uint8_t *mod_addr,
+			uint16_t idx, bool cred_flag, uint8_t ttl,
+			uint8_t period, uint8_t retransmit, bool b_virt,
+			uint16_t *dst)
+{
+	struct mesh_virtual *virt;
+	uint16_t grp;
+
+	if (dst) {
+		if (b_virt)
+			*dst = 0;
+		else
+			*dst = l_get_le16(mod_addr);
+	}
+
+	if (b_virt) {
+		if (!mesh_crypto_virtual_addr(mod_addr, &grp))
+			return MESH_STATUS_STORAGE_FAIL;
+	}
+
+	/* If old publication was Virtual, remove it */
+	if (mod->pub && mod->pub->addr >= VIRTUAL_BASE) {
+		virt = l_queue_find(mod->virtuals, find_virt_by_id,
+						L_UINT_TO_PTR(mod->pub->addr));
+		if (virt) {
+			l_queue_remove(mod->virtuals, virt);
+			unref_virt(virt);
+		}
+	}
+
+	if (b_virt) {
+		virt = l_queue_find(mesh_virtuals, find_virt_by_addr, mod_addr);
+		if (!virt) {
+			virt = l_new(struct mesh_virtual, 1);
+			virt->id = virt_id_next++;
+			virt->ota = grp;
+			memcpy(virt->addr, mod_addr, sizeof(virt->addr));
+			l_queue_push_head(mesh_virtuals, virt);
+		} else {
+			grp = virt->ota;
+		}
+		virt->ref_cnt++;
+		l_queue_push_head(mod->virtuals, virt);
+		mod->pub->addr = virt->id;
+	} else {
+		grp = l_get_le16(mod_addr);
+		mod->pub->addr = grp;
+	}
+
+	if (dst)
+		*dst = grp;
+
+	if (IS_UNASSIGNED(grp) && mod->pub) {
+		l_free(mod->pub);
+		mod->pub = NULL;
+		/* Remove publication if Pub Addr is 0x0000 */
+	} else {
+		if (!mod->pub)
+			mod->pub = l_new(struct mesh_model_pub, 1);
+		if (!mod->pub)
+			return MESH_STATUS_STORAGE_FAIL;
+
+		mod->pub->credential = cred_flag;
+		mod->pub->idx = idx;
+		mod->pub->ttl = ttl;
+		mod->pub->period = period;
+		mod->pub->retransmit = retransmit;
+	}
+
+	return MESH_STATUS_SUCCESS;
+}
+
+static int add_sub(struct mesh_net *net, struct mesh_model *mod,
+			const uint8_t *group, bool b_virt, uint16_t *dst)
+{
+	struct mesh_virtual *virt;
+	uint16_t grp;
+
+	if (b_virt) {
+		virt = l_queue_find(mesh_virtuals, find_virt_by_addr, group);
+		if (!virt) {
+			if (!mesh_crypto_virtual_addr(group, &grp))
+				return MESH_STATUS_STORAGE_FAIL;
+
+			virt = l_new(struct mesh_virtual, 1);
+			virt->id = virt_id_next++;
+			virt->ota = grp;
+			memcpy(virt->addr, group, sizeof(virt->addr));
+			if (!l_queue_push_head(mesh_virtuals, virt))
+				return MESH_STATUS_STORAGE_FAIL;
+		} else {
+			grp = virt->ota;
+		}
+		virt->ref_cnt++;
+		l_queue_push_head(mod->virtuals, virt);
+	} else {
+		grp = l_get_le16(group);
+	}
+
+	if (dst)
+		*dst = grp;
+
+	if (!mod->subs)
+		mod->subs = l_queue_new();
+	if (!mod->subs)
+		return MESH_STATUS_STORAGE_FAIL;
+
+	if (l_queue_find(mod->subs, simple_match, L_UINT_TO_PTR(grp)))
+		/* Group already exists */
+		return MESH_STATUS_SUCCESS;
+
+	l_queue_push_tail(mod->subs, L_UINT_TO_PTR(grp));
+
+	l_info("Added %4.4x", grp);
+	if (net)
+		mesh_net_dst_reg(net, grp);
+
+	return MESH_STATUS_SUCCESS;
+}
+
+bool mesh_model_rx(struct mesh_net *net, bool szmict, uint32_t seq0,
+			uint32_t seq, uint32_t iv_index, uint8_t ttl,
+			uint16_t src, uint16_t dst, uint8_t key_id,
+			const uint8_t *data, uint16_t size)
+{
+	uint8_t *clear_text;
+	struct mod_forward forward = {
+		.src = src,
+		.dst = dst,
+		.data = NULL,
+		.size = size - (szmict ? 8 : 4),
+		.ttl = ttl,
+		.virt = NULL,
+		.done = false,
+	};
+
+	struct mesh_node *node;
+	uint8_t num_ele;
+	int decrypt_idx, i, ele_idx;
+	uint16_t addr;
+	struct mesh_virtual *decrypt_virt = NULL;
+
+	l_debug("iv_index %8.8x key_id = %2.2x", iv_index, key_id);
+	if (!dst)
+		return false;
+
+	node = mesh_net_local_node_get(net);
+	if (!node)
+		return false;
+
+	ele_idx = node_get_element_idx(node, dst);
+
+	if (dst < 0x8000 && ele_idx < 0)
+		/* Unicast and not addressed to us */
+		return false;
+
+
+	clear_text = l_malloc(size);
+	if (!clear_text)
+		return false;
+
+	forward.data = clear_text;
+
+	/*
+	 * The packet needs to be decoded by the correct key which
+	 * is hinted by key_id, but is not necessarily definitive
+	 */
+	if (key_id == APP_ID_DEV || mesh_net_provisioner_mode_get(net))
+		decrypt_idx = dev_packet_decrypt(net, data, size, szmict, src,
+						dst, key_id, seq0, iv_index,
+						clear_text);
+	else if ((dst & 0xc000) == 0x8000)
+		decrypt_idx = virt_packet_decrypt(net, data, size, szmict, src,
+							dst, key_id, seq0,
+							iv_index, clear_text,
+							&decrypt_virt);
+	else
+		decrypt_idx = appkey_packet_decrypt(net, szmict, seq0,
+							iv_index, src, dst,
+							NULL, 0, key_id, data,
+							size, clear_text);
+
+	if (decrypt_idx < 0) {
+		l_error("model.c - Failed to decrypt application payload");
+		forward.done = false;
+		goto done;
+	}
+
+	/* print_packet("Clr Rx (pre-cache-check)", clear_text, size - 4); */
+
+	if (key_id != APP_ID_DEV) {
+		uint16_t crpl = mesh_net_get_crpl(net);
+
+		if (appkey_msg_in_replay_cache(net, (uint16_t) decrypt_idx, src,
+							crpl, seq, iv_index)) {
+			forward.done = true;
+			goto done;
+		}
+	}
+
+	print_packet("Clr Rx", clear_text, size - (szmict ? 8 : 4));
+
+	forward.virt = decrypt_virt;
+	forward.idx = decrypt_idx;
+	num_ele = node_get_num_elements(node);
+	addr = node_get_primary(node);
+
+	if (!num_ele || IS_UNASSIGNED(addr))
+		goto done;
+
+	for (i = 0; i < num_ele; i++) {
+		struct l_queue *models;
+
+		if (dst < 0x8000 && ele_idx != i)
+			continue;
+
+		forward.unicast = addr + i;
+		models = node_get_element_models(node, i, NULL);
+		l_queue_foreach(models, forward_model, &forward);
+
+		if (dst < 0x8000 && ele_idx == i)
+			break;
+	}
+done:
+	l_free(clear_text);
+	return forward.done;
+}
+
+unsigned int mesh_model_send(struct mesh_net *net, uint32_t mod_id,
+				uint16_t src, uint32_t target,
+				uint16_t app_idx, uint8_t ttl,
+				const void *msg, uint16_t msg_len)
+{
+	struct mesh_model *mod;
+	uint8_t *aad = NULL;
+	uint16_t dst;
+	uint8_t key_id;
+	const uint8_t *key;
+
+	/* print_packet("Mod Tx", msg, msg_len); */
+
+	if (!net || msg_len > 380)
+		return 0;
+
+	/* If SRC is 0, use the Primary Element */
+	if (src == 0)
+		src = mesh_net_get_address(net);
+
+	mod = find_model(net, src, mod_id, NULL);
+	if (!mod) {
+		l_info("model %x not found", mod_id);
+		return 0;
+	}
+
+	gettimeofday(&tx_start, NULL);
+
+	if (target == USE_PUB_VALUE) {
+		target = mod->pub->addr;
+		app_idx = mod->pub->idx;
+	}
+
+	if (IS_UNASSIGNED(target))
+		return 0;
+
+	if (target >= VIRTUAL_BASE) {
+		struct mesh_virtual *virt = l_queue_find(mesh_virtuals,
+				find_virt_by_id,
+				L_UINT_TO_PTR(target));
+
+		if (!virt)
+			return 0;
+
+		aad = virt->addr;
+		dst = virt->ota;
+	} else
+		dst = target;
+
+	l_debug("dst=%x", dst);
+	if (app_idx == APP_IDX_DEV && mesh_net_provisioner_mode_get(net)) {
+		key = node_get_device_key(mesh_net_local_node_get(net));
+	} else if (app_idx == APP_IDX_DEV) {
+		key = node_get_device_key(mesh_net_local_node_get(net));
+		if (!key)
+			return 0;
+
+		l_debug("(%x)", app_idx);
+		key_id = APP_ID_DEV;
+	} else {
+		key = appkey_get_key(net, app_idx, &key_id);
+		if (!key) {
+			l_debug("no app key for (%x)", app_idx);
+			return 0;
+		}
+
+		l_debug("(%x) %p", app_idx, key);
+		l_debug("key_id %x", key_id);
+	}
+
+	return msg_send(net, mod_id, src, dst, key_id, key, aad, ttl,
+			msg, msg_len);
+
+}
+
+int mesh_model_pub_set(struct mesh_net *net, uint16_t addr, uint32_t id,
+			const uint8_t *mod_addr, uint16_t idx, bool cred_flag,
+			uint8_t ttl, uint8_t period, uint8_t retransmit,
+			bool b_virt, uint16_t *dst)
+{
+	int fail = MESH_STATUS_SUCCESS;
+	int ele_idx = -1;
+	struct mesh_model *mod;
+	struct mesh_node *node;
+
+	node = mesh_net_local_node_get(net);
+	if (node)
+		ele_idx = node_get_element_idx(node, addr);
+
+	if (!node || ele_idx < 0) {
+		fail = MESH_STATUS_INVALID_ADDRESS;
+		return false;
+	}
+
+	mod = node_get_model(node, (uint8_t) ele_idx, id, &fail);
+	if (!mod)
+		return fail;
+
+	if (id == CONFIG_SRV_MODEL || id == CONFIG_CLI_MODEL)
+		return MESH_STATUS_INVALID_PUB_PARAM;
+
+	if (!appkey_have_key(net, idx))
+		return MESH_STATUS_INVALID_APPKEY;
+
+	return set_pub(mod, mod_addr, idx, cred_flag, ttl, period, retransmit,
+								b_virt, dst);
+	/*
+	 * TODO: Add standardized Publication Change notification to model
+	 * definition
+	 */
+}
+
+struct mesh_model_pub *mesh_model_pub_get(struct mesh_net *net, uint8_t ele_idx,
+						uint32_t mod_id, int *status)
+{
+	struct mesh_model *mod;
+	struct mesh_node *node = mesh_net_local_node_get(net);
+
+	if (!node) {
+		*status = MESH_STATUS_INVALID_ADDRESS;
+		return NULL;
+	}
+
+	mod = node_get_model(node, ele_idx, mod_id, status);
+	if (!mod)
+		return NULL;
+
+	return mod->pub;
+}
+
+uint32_t mesh_model_get_model_id(const struct mesh_model *model)
+{
+	if (!model)
+		return 0xffffffff; /* TODO: use define */
+	return model->id;
+}
+
+void mesh_model_free(void *data)
+{
+	struct mesh_model *mod = data;
+
+	l_queue_destroy(mod->bindings, NULL);
+	l_queue_destroy(mod->subs, NULL);
+	l_queue_destroy(mod->virtuals, unref_virt);
+	l_free(mod->pub);
+	l_free(mod);
+}
+
+struct mesh_model *mesh_model_new(uint8_t ele_idx, uint32_t id, bool vendor)
+{
+	struct mesh_model *mod = l_new(struct mesh_model, 1);
+
+	if (!mod)
+		return NULL;
+
+	if (vendor)
+		id |= VENDOR_ID_MASK;
+
+	mod->id = id;
+	mod->ele_idx = ele_idx;
+	mod->virtuals = l_queue_new();
+	if (!mod->virtuals) {
+		l_free(mod);
+		return NULL;
+	}
+	return mod;
+}
+
+static void restore_model_state(void *data)
+{
+	struct mesh_model *mod = data;
+	const struct mesh_model_ops *cbs;
+	const struct l_queue_entry *b;
+
+	cbs = mod->cbs;
+
+	if (l_queue_isempty(mod->bindings) || !mod->cbs->bind) {
+		for (b = l_queue_get_entries(mod->bindings); b; b = b->next) {
+			if (cbs->bind(L_PTR_TO_UINT(b->data), ACTION_ADD) !=
+				MESH_STATUS_SUCCESS)
+				break;
+		}
+	}
+
+	if (mod->pub && cbs->pub)
+		cbs->pub(mod->pub);
+}
+
+bool mesh_model_vendor_register(struct mesh_net *net, uint8_t ele_idx,
+					uint32_t mod_id,
+					const struct mesh_model_ops *cbs,
+					void *user_data)
+{
+	struct mesh_model *mod;
+	struct mesh_node *node;
+
+	node = mesh_net_local_node_get(net);
+	if (!node)
+		return false;
+
+	mod = node_get_model(node, ele_idx, mod_id, NULL);
+	if (!mod)
+		return false;
+
+	mod->cbs = cbs;
+	mod->user_data = user_data;
+
+	l_idle_oneshot(restore_model_state, mod, NULL);
+
+	return true;
+}
+
+bool mesh_model_register(struct mesh_net *net, uint8_t ele_idx,
+					uint32_t mod_id,
+					const struct mesh_model_ops *cbs,
+					void *user_data)
+{
+	uint32_t id = VENDOR_ID_MASK | mod_id;
+
+	return mesh_model_vendor_register(net, ele_idx, id, cbs, user_data);
+}
+
+void mesh_model_app_key_delete(struct mesh_net *net, struct l_queue *models,
+							uint16_t app_idx)
+{
+	l_queue_foreach(models, model_unbind_idx, L_UINT_TO_PTR(app_idx));
+}
+
+int mesh_model_binding_del(struct mesh_net *net, uint16_t addr, uint32_t id,
+						uint16_t app_idx)
+{
+	l_debug("0x%x, 0x%x, %d", addr, id, app_idx);
+	return update_binding(net, addr, id, app_idx, true);
+}
+
+int mesh_model_binding_add(struct mesh_net *net, uint16_t addr, uint32_t id,
+						uint16_t app_idx)
+{
+	l_debug("0x%x, 0x%x, %d", addr, id, app_idx);
+	return update_binding(net, addr, id, app_idx, false);
+}
+
+int mesh_model_get_bindings(struct mesh_net *net, uint16_t addr, uint32_t id,
+				uint8_t *buf, uint16_t buf_size, uint16_t *size)
+{
+	int fail;
+	struct mesh_model *mod;
+	const struct l_queue_entry *entry;
+	uint16_t n;
+	uint32_t idx_pair;
+	int i;
+
+	mod = find_model(net, addr, id, &fail);
+
+	if (!mod) {
+		*size = 0;
+		return fail;
+	}
+
+	entry = l_queue_get_entries(mod->bindings);
+	n = 0;
+	i = 0;
+	idx_pair = 0;
+
+	for (; entry; entry = entry->next) {
+		uint16_t app_idx = (uint16_t) (L_PTR_TO_UINT(entry->data));
+
+		if (!(i & 0x1)) {
+			idx_pair = app_idx;
+		} else {
+			idx_pair <<= 12;
+			idx_pair += app_idx;
+			/* Unlikely, but check for overflow*/
+			if ((n + 3) > buf_size) {
+				l_warn("Binding list too large");
+				goto done;
+			}
+			l_put_le32(idx_pair, buf);
+			buf += 3;
+			n += 3;
+		}
+		i++;
+	}
+
+	/* Process the last app key if present */
+	if (i & 0x1 && ((n + 2) <= buf_size)) {
+		l_put_le16(idx_pair, buf);
+		n += 2;
+	}
+
+done:
+	*size = n;
+
+	return MESH_STATUS_SUCCESS;
+}
+
+int mesh_model_sub_get(struct mesh_net *net, uint16_t addr, uint32_t id,
+			uint8_t *buf, uint16_t buf_size, uint16_t *size)
+{
+	int fail = MESH_STATUS_SUCCESS;
+	int16_t n;
+	struct mesh_model *mod;
+	const struct l_queue_entry *entry;
+
+	mod = find_model(net, addr, id, &fail);
+	if (!mod)
+		return fail;
+
+	entry = l_queue_get_entries(mod->subs);
+	*size = 0;
+	n = 0;
+
+	for (; entry; entry = entry->next) {
+		if ((n + 2) > buf_size)
+			return MESH_STATUS_UNSPECIFIED_ERROR;
+
+		l_put_le16((uint16_t) L_PTR_TO_UINT(entry->data), buf);
+		buf += 2;
+		n += 2;
+	}
+
+	*size = n;
+	return MESH_STATUS_SUCCESS;
+}
+
+int mesh_model_sub_add(struct mesh_net *net, uint16_t addr, uint32_t id,
+			const uint8_t *group, bool b_virt, uint16_t *dst)
+{
+	int fail = MESH_STATUS_SUCCESS;
+	int ele_idx = -1;
+	struct mesh_model *mod;
+	struct mesh_node *node;
+
+	node = mesh_net_local_node_get(net);
+	if (node)
+		ele_idx = node_get_element_idx(node, addr);
+
+	if (!node || ele_idx < 0) {
+		fail = MESH_STATUS_INVALID_ADDRESS;
+		return false;
+	}
+
+	mod = node_get_model(node, (uint8_t) ele_idx, id, &fail);
+	if (!mod)
+		return fail;
+
+	return add_sub(net, mod, group, b_virt, dst);
+	/* TODO: communicate to registered models that sub has changed */
+}
+
+int mesh_model_sub_ovr(struct mesh_net *net, uint16_t addr, uint32_t id,
+			const uint8_t *group, bool b_virt, uint16_t *dst)
+{
+	int fail = MESH_STATUS_SUCCESS;
+	struct l_queue *virtuals, *subs;
+	struct mesh_virtual *virt;
+	struct mesh_model *mod;
+
+	mod = find_model(net, addr, id, &fail);
+	if (!mod)
+		return fail;
+
+	subs = mod->subs;
+	virtuals = mod->virtuals;
+	mod->subs = l_queue_new();
+	mod->virtuals = l_queue_new();
+
+	if (!mod->subs || !mod->virtuals)
+		return MESH_STATUS_INSUFF_RESOURCES;
+
+	/*
+	 * When overwriting the Subscription List,
+	 * make sure any virtual Publication address is preserved
+	 */
+	if (mod->pub && mod->pub->addr >= VIRTUAL_BASE) {
+		virt = l_queue_find(virtuals, find_virt_by_id,
+				L_UINT_TO_PTR(mod->pub->addr));
+		if (virt) {
+			virt->ref_cnt++;
+			l_queue_push_head(mod->virtuals, virt);
+		}
+	}
+
+	fail = mesh_model_sub_add(net, addr, id, group, b_virt, dst);
+
+	if (fail) {
+		/* Adding new group failed, so revert to old list */
+		l_queue_destroy(mod->subs, NULL);
+		mod->subs = subs;
+		l_queue_destroy(mod->virtuals, unref_virt);
+		mod->virtuals = virtuals;
+	} else {
+		const struct l_queue_entry *entry;
+
+		entry = l_queue_get_entries(subs);
+		for (; entry; entry = entry->next)
+			mesh_net_dst_unreg(net,
+					(uint16_t) L_PTR_TO_UINT(entry->data));
+
+		l_queue_destroy(subs, NULL);
+		l_queue_destroy(virtuals, unref_virt);
+	}
+
+	return fail;
+}
+
+int mesh_model_sub_del(struct mesh_net *net, uint16_t addr, uint32_t id,
+			const uint8_t *group, bool b_virt, uint16_t *dst)
+{
+	int fail = MESH_STATUS_SUCCESS;
+	uint16_t grp;
+	struct mesh_model *mod;
+
+	mod = find_model(net, addr, id, &fail);
+	if (!mod)
+		return fail;
+
+	if (b_virt) {
+		struct mesh_virtual *virt;
+
+		virt = l_queue_find(mod->virtuals, find_virt_by_addr, group);
+		if (virt) {
+			l_queue_remove(mod->virtuals, virt);
+			grp = virt->ota;
+			unref_virt(virt);
+		} else {
+			if (!mesh_crypto_virtual_addr(group, &grp))
+				return MESH_STATUS_STORAGE_FAIL;
+		}
+	} else {
+		grp = l_get_le16(group);
+	}
+
+	*dst = grp;
+
+	if (l_queue_remove(mod->subs, L_UINT_TO_PTR(grp)))
+		mesh_net_dst_unreg(net, grp);
+
+	return MESH_STATUS_SUCCESS;
+}
+
+int mesh_model_sub_del_all(struct mesh_net *net, uint16_t addr, uint32_t id)
+{
+	int fail = MESH_STATUS_SUCCESS;
+	struct mesh_model *mod;
+	const struct l_queue_entry *entry;
+
+	mod = find_model(net, addr, id, &fail);
+	if (!mod)
+		return fail;
+
+	entry = l_queue_get_entries(mod->subs);
+	for (; entry; entry = entry->next)
+		mesh_net_dst_unreg(net, (uint16_t) L_PTR_TO_UINT(entry->data));
+
+	l_queue_destroy(mod->subs, NULL);
+	l_queue_destroy(mod->virtuals, unref_virt);
+	mod->virtuals = l_queue_new();
+
+	return fail;
+}
+
+struct mesh_model *mesh_model_init(struct mesh_net *net, uint8_t ele_idx,
+						struct mesh_db_model *db_mod)
+{
+	struct mesh_model *mod;
+	uint32_t i;
+
+	mod = mesh_model_new(ele_idx, db_mod->id, db_mod->vendor);
+	if (!mod)
+		return NULL;
+
+	/* Implicitly bind config server model to device key */
+	if (db_mod->id == CONFIG_SRV_MODEL) {
+
+		if (ele_idx != PRIMARY_ELE_IDX)
+			return NULL;
+
+		l_queue_push_head(mod->bindings, L_UINT_TO_PTR(APP_IDX_DEV));
+		return mod;
+	}
+
+	if (db_mod->id == CONFIG_CLI_MODEL) {
+		l_queue_push_head(mod->bindings, L_UINT_TO_PTR(APP_IDX_DEV));
+		return mod;
+	}
+
+	/* Add application key bindings if present */
+	if (db_mod->bindings) {
+		mod->bindings = l_queue_new();
+
+		if (!mod->bindings) {
+			mesh_model_free(mod);
+			return NULL;
+		}
+
+		for (i = 0; i < db_mod->num_bindings; i++) {
+			if (!model_bind_idx(mod, db_mod->bindings[i])) {
+				mesh_model_free(mod);
+				return NULL;
+			}
+		}
+	}
+
+	/* Add publication if present */
+	if (db_mod->pub) {
+		uint16_t mod_addr;
+		uint8_t *dst;
+
+		l_put_le16(db_mod->pub->addr, &mod_addr);
+		dst = db_mod->pub->virt ? db_mod->pub->virt_addr :
+							(uint8_t *) &mod_addr;
+
+		if (set_pub(mod, dst, db_mod->pub->idx, db_mod->pub->credential,
+			db_mod->pub->ttl, db_mod->pub->period,
+			db_mod->pub->retransmit, db_mod->pub->virt, NULL) !=
+							MESH_STATUS_SUCCESS) {
+			mesh_model_free(mod);
+			return NULL;
+		}
+	}
+
+	/* Add subscriptions if present */
+	if (!db_mod->subs)
+		return mod;
+
+	for (i = 0; i < db_mod->num_subs; i++) {
+		uint16_t group;
+		uint8_t *src;
+
+		/*
+		 * To keep calculations for virtual label coherent,
+		 * convert to little endian.
+		 */
+		l_put_le16(db_mod->subs[i].src.addr, &group);
+		src = db_mod->subs[i].virt ? db_mod->subs[i].src.virt_addr :
+			(uint8_t *) &group;
+
+		if (add_sub(net, mod, src, db_mod->subs[i].virt, NULL) !=
+							MESH_STATUS_SUCCESS) {
+			mesh_model_free(mod);
+			return NULL;
+		}
+	}
+
+	return mod;
+}
+
+uint16_t mesh_model_opcode_set(uint32_t opcode, uint8_t *buf)
+{
+	if (opcode <= 0x7e) {
+		buf[0] = opcode;
+		return 1;
+	}
+
+	if (opcode >= 0x8000 && opcode <= 0xbfff) {
+		l_put_be16(opcode, buf);
+		return 2;
+	}
+
+	if (opcode >= 0xc00000 && opcode <= 0xffffff) {
+		buf[0] = (opcode >> 16) & 0xff;
+		l_put_be16(opcode, buf + 1);
+		return 3;
+	}
+
+	l_info("Illegal Opcode %x", opcode);
+	return 0;
+}
+
+bool mesh_model_opcode_get(const uint8_t *buf, uint16_t size,
+					uint32_t *opcode, uint16_t *n)
+{
+	if (!n || !opcode || size < 1)
+		return false;
+
+	switch (buf[0] & 0xc0) {
+	case 0x00:
+	case 0x40:
+		/* RFU */
+		if (buf[0] == 0x7f)
+			return false;
+
+		*n = 1;
+		*opcode = buf[0];
+		break;
+
+	case 0x80:
+		if (size < 2)
+			return false;
+
+		*n = 2;
+		*opcode = l_get_be16(buf);
+		break;
+
+	case 0xc0:
+		if (size < 3)
+			return false;
+
+		*n = 3;
+		*opcode = l_get_be16(buf + 1);
+		*opcode |= buf[0] << 16;
+		break;
+
+	default:
+		print_packet("Bad", buf, size);
+		return false;
+	}
+
+	return true;
+}
+
+void mesh_model_add_virtual(struct mesh_net *net, const uint8_t *v)
+{
+	struct mesh_virtual *virt = l_queue_find(mesh_virtuals,
+						find_virt_by_addr, v);
+
+	if (virt) {
+		virt->ref_cnt++;
+		return;
+	}
+
+	virt = l_new(struct mesh_virtual, 1);
+	if (!virt)
+		return;
+
+	if (!mesh_crypto_virtual_addr(v, &virt->ota)) {
+		l_free(virt);
+		return; /* Storage Failure */
+	}
+
+	memcpy(virt->addr, v, 16);
+	virt->ref_cnt++;
+	virt->id = virt_id_next++;
+	l_queue_push_head(mesh_virtuals, virt);
+}
+
+void mesh_model_del_virtual(struct mesh_net *net, uint32_t va24)
+{
+	struct mesh_virtual *virt = l_queue_remove_if(mesh_virtuals,
+						find_virt_by_id,
+						L_UINT_TO_PTR(va24));
+
+	if (virt)
+		unref_virt(virt);
+}
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v2 08/11] meshd: Mesh config server model
  2018-04-25 17:50 [PATCH BlueZ v2 00/11] Bluetooth Mesh Daemon Brian Gix
                   ` (4 preceding siblings ...)
  2018-04-25 17:50 ` [PATCH BlueZ v2 07/11] meshd: Source code for handling access layer mux/demux Brian Gix
@ 2018-04-25 17:50 ` Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 09/11] meshd: Read and write mesh configuration in JSON format Brian Gix
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Brian Gix @ 2018-04-25 17:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Inga Stotland

From: Inga Stotland <inga.stotland@intel.com>

---
 meshd/src/cfgmod-server.c | 1194 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1194 insertions(+)
 create mode 100644 meshd/src/cfgmod-server.c

diff --git a/meshd/src/cfgmod-server.c b/meshd/src/cfgmod-server.c
new file mode 100644
index 000000000..de7c6fcff
--- /dev/null
+++ b/meshd/src/cfgmod-server.c
@@ -0,0 +1,1194 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 <unistd.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <ell/ell.h>
+
+#include "meshd/common/mesh-defs.h"
+
+#include "meshd/src/mesh.h"
+#include "meshd/src/node.h"
+#include "meshd/src/net.h"
+#include "meshd/src/appkey.h"
+#include "meshd/src/model.h"
+#include "meshd/src/storage.h"
+
+#include "meshd/src/cfgmod.h"
+
+#define CFG_MAX_MSG_LEN 380
+
+static void send_pub_status(struct mesh_net *net, uint16_t src, uint16_t dst,
+			uint8_t status, uint16_t ele_addr, uint16_t pub_addr,
+			uint32_t mod_id, uint16_t idx, bool cred_flag,
+			uint8_t ttl, uint8_t period, uint8_t retransmit)
+{
+	uint8_t msg[16];
+	size_t n;
+
+	n = mesh_model_opcode_set(OP_CONFIG_MODEL_PUB_STATUS, msg);
+	msg[n++] = status;
+	l_put_le16(ele_addr, msg + n);
+	n += 2;
+	l_put_le16(pub_addr, msg + n);
+	n += 2;
+	idx |= cred_flag ? CREDFLAG_MASK : 0;
+	l_put_le16(idx, msg + n);
+	n += 2;
+	msg[n++] = ttl;
+	msg[n++] = period;
+	msg[n++] = retransmit;
+	if (mod_id < 0x10000) {
+		l_put_le16(mod_id, msg + n);
+		n += 2;
+	} else {
+		l_put_le16(mod_id >> 16, msg + n);
+		n += 2;
+		l_put_le16(mod_id, msg + n);
+		n += 2;
+	}
+
+	mesh_model_send(net, CONFIG_SRV_MODEL,
+			dst, src,
+			APP_IDX_DEV, DEFAULT_TTL, msg, n);
+}
+
+static bool config_pub_get(struct mesh_net *net, uint16_t src, uint16_t dst,
+					const uint8_t *pkt, uint16_t size)
+{
+	uint32_t mod_id;
+	uint16_t ele_addr;
+	int ele_idx;
+	struct mesh_model_pub *pub;
+	int status;
+
+	if (size == 4)
+		mod_id = l_get_le16(pkt + 2);
+	else if (size == 6) {
+		mod_id = l_get_le16(pkt + 2) << 16;
+		mod_id |= l_get_le16(pkt + 4);
+	} else
+		return false;
+
+	ele_addr = l_get_le16(pkt);
+	ele_idx = node_get_element_idx(mesh_net_local_node_get(net), ele_addr);
+
+	if (ele_idx >= 0)
+		pub = mesh_model_pub_get(net, ele_idx, mod_id, &status);
+	else
+		status = MESH_STATUS_INVALID_ADDRESS;
+
+	if (pub && status == MESH_STATUS_SUCCESS)
+		send_pub_status(net, src, dst, status, ele_addr, pub->addr,
+				mod_id, pub->idx, pub->credential, pub->ttl,
+						pub->period, pub->retransmit);
+	else
+		send_pub_status(net, src, dst, status, ele_addr, 0, mod_id,
+								0, 0, 0, 0, 0);
+	return true;
+}
+
+static bool config_pub_set(struct mesh_net *net, uint16_t src, uint16_t dst,
+					const uint8_t *pkt, uint16_t size,
+					bool unreliable)
+{
+	uint32_t mod_id;
+	uint16_t ele_addr, idx, ota = 0;
+	const uint8_t *pub_addr;
+	uint16_t test_addr;
+	uint8_t ttl, period;
+	uint8_t retransmit;
+	int status;
+	bool cred_flag, b_virt = false;
+
+	switch (size) {
+	default:
+		return false;
+
+	case 11:
+		idx = l_get_le16(pkt + 4);
+		ttl = pkt[6];
+		period = pkt[7];
+		retransmit = pkt[8];
+		mod_id = l_get_le16(pkt + 9);
+		break;
+
+	case 13:
+		idx = l_get_le16(pkt + 4);
+		ttl = pkt[6];
+		period = pkt[7];
+		retransmit = pkt[8];
+		mod_id = l_get_le16(pkt + 9) << 16;
+		mod_id |= l_get_le16(pkt + 11);
+		break;
+
+	case 25:
+		b_virt = true;
+		idx = l_get_le16(pkt + 18);
+		ttl = pkt[20];
+		period = pkt[21];
+		retransmit = pkt[22];
+		mod_id = l_get_le16(pkt + 23);
+		break;
+
+	case 27:
+		b_virt = true;
+		idx = l_get_le16(pkt + 18);
+		ttl = pkt[20];
+		period = pkt[21];
+		retransmit = pkt[22];
+		mod_id = l_get_le16(pkt + 23) << 16;
+		mod_id |= l_get_le16(pkt + 25);
+		break;
+	}
+	ele_addr = l_get_le16(pkt);
+	pub_addr = pkt + 2;
+
+	/* Doesn't accept out-of-range TTLs */
+	if (ttl > TTL_MASK && ttl != DEFAULT_TTL)
+		return false;
+
+	/* Get cred_flag */
+	cred_flag = !!(CREDFLAG_MASK & idx);
+
+	/* Ignore non-IDX bits */
+	idx &= APP_IDX_MASK;
+
+	/* Doesn't accept virtual seeming addresses */
+	test_addr = l_get_le16(pub_addr);
+	if (!b_virt && test_addr > 0x7fff && test_addr < 0xc000)
+		return false;
+
+	status = mesh_model_pub_set(net, ele_addr, mod_id, pub_addr, idx,
+					cred_flag, ttl, period, retransmit,
+					b_virt, &ota);
+
+	l_info("pub_set: status %d, ea %4.4x, ota: %4.4x, mod: %x, idx: %3.3x",
+					status, ele_addr, ota, mod_id, idx);
+
+	if (IS_UNASSIGNED(ota) && !b_virt)
+		ttl = period = idx = 0;
+
+	if (status >= 0 && !unreliable)
+		send_pub_status(net, src, dst, status, ele_addr, ota,
+				mod_id, idx, cred_flag, ttl, period,
+				retransmit);
+	return true;
+}
+
+static void send_sub_status(struct mesh_net *net, uint16_t src, uint16_t dst,
+					uint8_t status, uint16_t ele_addr,
+					uint16_t addr, uint32_t mod)
+{
+	uint8_t msg[12];
+	int n = mesh_model_opcode_set(OP_CONFIG_MODEL_SUB_STATUS, msg);
+
+	msg[n++] = status;
+	l_put_le16(ele_addr, msg + n);
+	n += 2;
+	l_put_le16(addr, msg + n);
+	n += 2;
+	if (mod >= 0x10000) {
+		l_put_le16(mod >> 16, msg + n);
+		l_put_le16(mod, msg + n + 2);
+		n += 4;
+	} else {
+		l_put_le16(mod, msg + n);
+		n += 2;
+	}
+
+	mesh_model_send(net, CONFIG_SRV_MODEL, dst, src, APP_IDX_DEV,
+							DEFAULT_TTL, msg, n);
+}
+
+static bool config_sub_get(struct mesh_net *net, uint16_t src, uint16_t dst,
+					const uint8_t *pkt, uint16_t size)
+{
+	uint16_t ele_addr;
+	uint32_t mod_id;
+	uint16_t n = 0;
+	int ret = 0;
+	uint8_t *status;
+	uint16_t buf_size;
+	uint8_t msg[5 + sizeof(uint16_t) * MAX_GRP_PER_MOD];
+
+	/* Incoming message has already been size-checked */
+	ele_addr = l_get_le16(pkt);
+
+	switch (size) {
+	default:
+		l_debug("Bad Len Cfg_Pub_Set: %d", size);
+		return false;
+
+	case 4:
+		mod_id = l_get_le16(pkt + 2);
+		n = mesh_model_opcode_set(OP_CONFIG_MODEL_SUB_LIST, msg);
+		status = msg + n;
+		msg[n++] = 0;
+		l_put_le16(ele_addr, msg + n);
+		n += 2;
+		l_put_le16(mod_id, msg + n);
+		n += 2;
+		break;
+
+	case 6:
+		mod_id = l_get_le16(pkt + 2) << 16;
+		mod_id |= l_get_le16(pkt + 4);
+		n = mesh_model_opcode_set(OP_CONFIG_VEND_MODEL_SUB_LIST, msg);
+		status = msg + n;
+		msg[n++] = 0;
+		l_put_le16(ele_addr, msg + n);
+		n += 2;
+		l_put_le16(mod_id >> 16, msg + n);
+		n += 2;
+		l_put_le16(mod_id, msg + n);
+		n += 2;
+		break;
+	}
+
+	buf_size = sizeof(uint16_t) * MAX_GRP_PER_MOD;
+	ret = mesh_model_sub_get(net, ele_addr, mod_id, msg + n, buf_size,
+									&size);
+
+	if (!ret)
+		n += size;
+	else if (ret > 0)
+		*status = ret;
+
+	mesh_model_send(net, CONFIG_SRV_MODEL,
+			dst, src, APP_IDX_DEV,
+			DEFAULT_TTL, msg, n);
+	return true;
+}
+
+static void config_sub_set(struct mesh_net *net, uint16_t src, uint16_t dst,
+					const uint8_t *pkt, uint16_t size,
+					bool virt, uint32_t opcode)
+{
+	uint16_t grp, ele_addr;
+	bool unreliable = !!(opcode & OP_UNRELIABLE);
+	uint32_t mod_id, func;
+	const uint8_t *addr = NULL;
+	int status = 0;
+
+	switch (size) {
+	default:
+		l_error("Bad Len Cfg_Sub_Set: %d", size);
+		return;
+	case 4:
+		if (opcode != OP_CONFIG_MODEL_SUB_DELETE_ALL)
+			return;
+		mod_id = l_get_le16(pkt + 2);
+		break;
+	case 6:
+		if (virt)
+			return;
+		if (opcode != OP_CONFIG_MODEL_SUB_DELETE_ALL)
+			mod_id = l_get_le16(pkt + 4);
+		else {
+			mod_id = l_get_le16(pkt + 2) << 16;
+			mod_id |= l_get_le16(pkt + 4);
+		}
+		break;
+	case 8:
+		if (virt)
+			return;
+		mod_id = l_get_le16(pkt + 4) << 16;
+		mod_id |= l_get_le16(pkt + 6);
+		break;
+	case 20:
+		if (!virt)
+			return;
+		mod_id = l_get_le16(pkt + 18);
+		break;
+	case 22:
+		if (!virt)
+			return;
+		mod_id = l_get_le16(pkt + 18) << 16;
+		mod_id |= l_get_le16(pkt + 20);
+		break;
+	}
+	ele_addr = l_get_le16(pkt);
+
+	if (opcode != OP_CONFIG_MODEL_SUB_DELETE_ALL) {
+		addr = pkt + 2;
+		grp = l_get_le16(addr);
+	} else
+		grp = UNASSIGNED_ADDRESS;
+
+	func = opcode & ~OP_UNRELIABLE;
+	switch (func) {
+	default:
+		l_info("Bad opcode: %x", func);
+		return;
+
+	case OP_CONFIG_MODEL_SUB_DELETE_ALL:
+		status = mesh_model_sub_del_all(net, ele_addr, mod_id);
+		break;
+
+	case OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE:
+		grp = UNASSIGNED_ADDRESS;
+		/* Fall Through */
+	case OP_CONFIG_MODEL_SUB_OVERWRITE:
+		status = mesh_model_sub_ovr(net, ele_addr, mod_id,
+							addr, virt, &grp);
+		break;
+	case OP_CONFIG_MODEL_SUB_VIRT_ADD:
+		grp = UNASSIGNED_ADDRESS;
+		/* Fall Through */
+	case OP_CONFIG_MODEL_SUB_ADD:
+		status = mesh_model_sub_add(net, ele_addr, mod_id,
+							addr, virt, &grp);
+		break;
+	case OP_CONFIG_MODEL_SUB_VIRT_DELETE:
+		grp = UNASSIGNED_ADDRESS;
+		/* Fall Through */
+	case OP_CONFIG_MODEL_SUB_DELETE:
+		status = mesh_model_sub_del(net, ele_addr, mod_id,
+							addr, virt, &grp);
+		break;
+	}
+
+	if (!unreliable && status >= 0)
+		send_sub_status(net, src, dst, status, ele_addr, grp, mod_id);
+
+}
+
+static void send_model_app_status(struct mesh_net *net, uint16_t src,
+					uint16_t dst, uint8_t status,
+					uint16_t addr, uint32_t id,
+					uint16_t idx)
+{
+	uint8_t msg[12];
+	size_t n = mesh_model_opcode_set(OP_MODEL_APP_STATUS, msg);
+
+	msg[n++] = status;
+	l_put_le16(addr, msg + n);
+	n += 2;
+	l_put_le16(idx, msg + n);
+	n += 2;
+	if (id > 0xffff) {
+		l_put_le16(id >> 16, msg + n);
+		n += 2;
+	}
+	l_put_le16(id, msg + n);
+	n += 2;
+
+	mesh_model_send(net, CONFIG_SRV_MODEL, dst, src, APP_IDX_DEV,
+				DEFAULT_TTL, msg, n);
+}
+
+static void model_app_list(struct mesh_net *net, uint16_t src, uint16_t dst,
+					const uint8_t *pkt, uint16_t size)
+{
+	uint16_t ele_addr;
+	uint32_t mod_id = 0xffff;
+	uint8_t *msg = NULL;
+	uint8_t *status;
+	uint16_t n, buf_size;
+	int result;
+
+	buf_size = MAX_BINDINGS * sizeof(uint16_t);
+	msg = l_malloc(7 + buf_size);
+	if (!msg)
+		return;
+
+	ele_addr = l_get_le16(pkt);
+
+	switch (size) {
+	default:
+		l_free(msg);
+		return;
+	case 4:
+		n = mesh_model_opcode_set(OP_MODEL_APP_LIST, msg);
+		status = msg + n;
+		mod_id = l_get_le16(pkt + 2);
+		l_put_le16(ele_addr, msg + 1 + n);
+		l_put_le16(mod_id, msg + 3 + n);
+		n += 5;
+		break;
+	case 6:
+		n = mesh_model_opcode_set(OP_VEND_MODEL_APP_LIST, msg);
+		status = msg + n;
+		mod_id = l_get_le16(pkt + 2) << 16;
+		mod_id |= l_get_le16(pkt + 4);
+
+		l_put_le16(ele_addr, msg + 1 + n);
+		l_put_le16(mod_id >> 16, msg + 3 + n);
+		l_put_le16(mod_id, msg + 5 + n);
+		n += 7;
+		break;
+	}
+
+
+	result = mesh_model_get_bindings(net, ele_addr, mod_id, msg + n,
+							buf_size, &size);
+	n += size;
+
+	if (result >= 0) {
+		*status = result;
+		mesh_model_send(net, CONFIG_SRV_MODEL, dst, src, APP_IDX_DEV,
+					DEFAULT_TTL, msg, n);
+	}
+
+	l_free(msg);
+}
+
+static bool model_app_bind(struct mesh_net *net, uint16_t src, uint16_t dst,
+					const uint8_t *pkt, uint16_t size,
+					bool unbind)
+{
+	uint16_t ele_addr;
+	uint32_t mod_id;
+	uint16_t idx;
+	int result;
+
+	switch (size) {
+	default:
+		return false;
+
+	case 6:
+		mod_id = l_get_le16(pkt + 4);
+		break;
+	case 8:
+		mod_id = l_get_le16(pkt + 4) << 16;
+		mod_id |= l_get_le16(pkt + 6);
+		break;
+	}
+
+	ele_addr = l_get_le16(pkt);
+	idx = l_get_le16(pkt + 2);
+
+	if (idx > 0xfff)
+		return false;
+
+	if (unbind)
+		result = mesh_model_binding_del(net, ele_addr, mod_id, idx);
+	else
+		result = mesh_model_binding_add(net, ele_addr, mod_id, idx);
+
+	send_model_app_status(net, src, dst, result, ele_addr, mod_id, idx);
+
+	return true;
+}
+
+static void hb_pub_timeout_func(struct l_timeout *timeout, void *user_data)
+{
+	struct mesh_net *net = user_data;
+	struct mesh_net_heartbeat *hb = mesh_net_heartbeat_get(net);
+
+	mesh_net_heartbeat_send(net);
+
+	if (hb->pub_count != 0xffff)
+		hb->pub_count--;
+	if (hb->pub_count > 0)
+		l_timeout_modify(hb->pub_timer, hb->pub_period);
+	else {
+		l_timeout_remove(hb->pub_timer);
+		hb->pub_timer = NULL;
+	}
+	l_debug("%d left", hb->pub_count);
+}
+
+static void update_hb_pub_timer(struct mesh_net *net,
+						struct mesh_net_heartbeat *hb)
+{
+	if (IS_UNASSIGNED(hb->pub_dst) || hb->pub_count == 0) {
+		l_timeout_remove(hb->pub_timer);
+		hb->pub_timer = NULL;
+		return;
+	}
+
+	if (!hb->pub_timer)
+		hb->pub_timer = l_timeout_create(hb->pub_period,
+					hb_pub_timeout_func, net, NULL);
+	else
+		l_timeout_modify(hb->pub_timer, hb->pub_period);
+}
+
+static void hb_sub_timeout_func(struct l_timeout *timeout, void *user_data)
+{
+	struct mesh_net *net = user_data;
+	struct mesh_net_heartbeat *hb = mesh_net_heartbeat_get(net);
+
+	l_info("HB Subscription Ended");
+	l_timeout_remove(hb->sub_timer);
+	hb->sub_timer = NULL;
+	hb->sub_enabled = false;
+}
+
+static uint8_t uint32_to_log(uint32_t value)
+{
+	uint32_t val = 1;
+	uint8_t ret = 1;
+
+	if (!value)
+		return 0;
+	else if (value > 0x10000)
+		return 0xff;
+
+	while (val < value) {
+		val <<= 1;
+		ret++;
+	}
+
+	return ret;
+}
+
+static uint32_t log_to_uint32(uint8_t log, uint8_t offset)
+{
+	if (!log)
+		return 0x0000;
+	else if (log > 0x11)
+		return 0xffff;
+	else
+		return (1 << (log - offset));
+}
+
+
+static int hb_subscription_set(struct mesh_net *net, uint16_t src,
+					uint16_t dst, uint8_t period_log)
+{
+	struct mesh_net_heartbeat *hb = mesh_net_heartbeat_get(net);
+	struct timeval time_now;
+
+	/* SRC must be Unicast, DST can be any legal address except Virtual */
+	if ((!IS_UNASSIGNED(src) && !IS_UNICAST(src)) || IS_VIRTUAL(dst))
+		return -1;
+
+	/* Check if the subscription should be disabled */
+	if (IS_UNASSIGNED(src) || IS_UNASSIGNED(dst)) {
+		if (IS_GROUP(hb->sub_dst))
+			mesh_net_dst_unreg(net, hb->sub_dst);
+
+		l_timeout_remove(hb->sub_timer);
+		hb->sub_timer = NULL;
+		hb->sub_enabled = false;
+		hb->sub_dst = UNASSIGNED_ADDRESS;
+		hb->sub_src = UNASSIGNED_ADDRESS;
+		hb->sub_count = 0;
+		hb->sub_period = 0;
+		hb->sub_min_hops = 0;
+		hb->sub_max_hops = 0;
+		return MESH_STATUS_SUCCESS;
+	} else if (!period_log && src == hb->sub_src && dst == hb->sub_dst) {
+		/* Preserve collected data, but disable */
+		l_timeout_remove(hb->sub_timer);
+		hb->sub_timer = NULL;
+		hb->sub_enabled = false;
+		hb->sub_period = 0;
+		return MESH_STATUS_SUCCESS;
+	}
+
+	if (hb->sub_dst != dst) {
+		if (IS_GROUP(hb->sub_dst))
+			mesh_net_dst_unreg(net, hb->sub_dst);
+		if (IS_GROUP(dst))
+			mesh_net_dst_reg(net, dst);
+	}
+
+	hb->sub_enabled = !!period_log;
+	hb->sub_src = src;
+	hb->sub_dst = dst;
+	hb->sub_count = 0;
+	hb->sub_period = log_to_uint32(period_log, 1);
+	hb->sub_min_hops = 0x00;
+	hb->sub_max_hops = 0x00;
+
+	gettimeofday(&time_now, NULL);
+	hb->sub_start = time_now.tv_sec;
+
+	if (!hb->sub_enabled) {
+		l_timeout_remove(hb->sub_timer);
+		hb->sub_timer = NULL;
+		return MESH_STATUS_SUCCESS;
+	}
+
+	hb->sub_min_hops = 0xff;
+
+	if (!hb->sub_timer)
+		hb->sub_timer = l_timeout_create(hb->sub_period,
+						hb_sub_timeout_func, net, NULL);
+	else
+		l_timeout_modify(hb->sub_timer, hb->sub_period);
+
+	return MESH_STATUS_SUCCESS;
+}
+
+static void node_reset(struct l_timeout *timeout, void *user_data)
+{
+	l_info("Node Reset");
+	l_timeout_remove(timeout);
+	l_main_quit();
+}
+
+static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
+				uint16_t unicast, uint16_t idx,
+				const uint8_t *data, uint16_t size,
+				uint8_t ttl, const void *user_data)
+{
+	struct mesh_net *net = (struct mesh_net *) user_data;
+	const uint8_t *pkt = data;
+	struct timeval time_now;
+	uint32_t opcode, tmp32;
+	int b_res = MESH_STATUS_SUCCESS;
+	uint8_t msg[11];
+	uint8_t *long_msg = NULL;
+	struct mesh_net_heartbeat *hb;
+	uint16_t net_idx, app_idx;
+	uint8_t state, status;
+	uint8_t phase;
+	bool virt = false;
+	uint8_t count;
+	uint16_t interval;
+	struct mesh_node *node;
+	uint16_t n;
+
+	if (idx != APP_IDX_DEV)
+		return false;
+
+	if (mesh_model_opcode_get(pkt, size, &opcode, &n)) {
+		size -= n;
+		pkt += n;
+	} else
+		return false;
+
+	hb = mesh_net_heartbeat_get(net);
+	l_debug("CONFIG-SRV-opcode 0x%x size %u idx %3.3x", opcode, size, idx);
+
+	node = mesh_net_local_node_get(net);
+	n = 0;
+
+	switch (opcode) {
+	default:
+		return false;
+
+	case OP_DEV_COMP_GET:
+		if (size != 1)
+			return false;
+
+		/* Only page 0 is currently supported */
+		if (pkt[0] != 0) {
+			l_info("Unsupported page number %d", pkt[0]);
+			l_info("Returning page number 0");
+		}
+		long_msg = l_malloc(CFG_MAX_MSG_LEN);
+		n = mesh_model_opcode_set(OP_DEV_COMP_STATUS, long_msg);
+		long_msg[n++] = 0;
+		n += node_generate_comp(node, long_msg + n,
+							CFG_MAX_MSG_LEN - n);
+
+		break;
+
+	case OP_CONFIG_DEFAULT_TTL_SET:
+		if (size != 1 || pkt[0] > TTL_MASK || pkt[0] == 1)
+			return true;
+
+		if (pkt[0] <= TTL_MASK)
+			node_default_ttl_set(node, pkt[0]);
+		/* Fall Through */
+
+	case OP_CONFIG_DEFAULT_TTL_GET:
+		l_info("Get/Set Default TTL");
+
+		n = mesh_model_opcode_set(OP_CONFIG_DEFAULT_TTL_STATUS, msg);
+		msg[n++] = node_default_ttl_get(node);
+		break;
+
+	case OP_CONFIG_MODEL_PUB_VIRT_SET:
+		if (size != 25 && size != 27)
+			return true;
+
+		config_pub_set(net, src, unicast, pkt, size,
+				!!(opcode & OP_UNRELIABLE));
+		break;
+
+	case OP_CONFIG_MODEL_PUB_SET:
+		if (size != 11 && size != 13)
+			return true;
+
+		config_pub_set(net, src, unicast, pkt, size,
+				!!(opcode & OP_UNRELIABLE));
+		break;
+
+	case OP_CONFIG_MODEL_PUB_GET:
+		config_pub_get(net, src, unicast, pkt, size);
+		break;
+
+	case OP_CONFIG_VEND_MODEL_SUB_GET:
+		if (size != 6)
+			return true;
+		config_sub_get(net, src, unicast, pkt, size);
+		break;
+
+	case OP_CONFIG_MODEL_SUB_GET:
+		if (size != 4)
+			return true;
+		config_sub_get(net, src, unicast, pkt, size);
+		break;
+
+	case OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE:
+	case OP_CONFIG_MODEL_SUB_VIRT_DELETE:
+	case OP_CONFIG_MODEL_SUB_VIRT_ADD:
+		virt = true;
+		/* Fall Through */
+	case OP_CONFIG_MODEL_SUB_OVERWRITE:
+	case OP_CONFIG_MODEL_SUB_DELETE:
+	case OP_CONFIG_MODEL_SUB_ADD:
+	case OP_CONFIG_MODEL_SUB_DELETE_ALL:
+		config_sub_set(net, src, unicast, pkt, size, virt, opcode);
+		break;
+
+	case OP_CONFIG_RELAY_SET:
+		if (size != 2 || pkt[0] > 0x01)
+			return true;
+
+		count = (pkt[1] >> 5) + 1;
+		interval = ((pkt[1] & 0x1f) + 1) * 10;
+		node_relay_mode_set(node, !!pkt[0], pkt[1]>>5,
+					pkt[1] & 0x1f);
+		/* Fall Through */
+
+	case OP_CONFIG_RELAY_GET:
+		n = mesh_model_opcode_set(OP_CONFIG_RELAY_STATUS, msg);
+
+		msg[n++] = node_relay_mode_get(node, &count, &interval);
+		msg[n++] = ((count - 1) << 5) + ((interval/10 - 1) & 0x1f);
+
+		l_info("Get/Set Relay Config (%d)", msg[n-1]);
+		break;
+
+	case OP_CONFIG_NETWORK_TRANSMIT_SET:
+		if (size != 1)
+			return true;
+
+		count = (pkt[0] >> 5) + 1;
+		interval = ((pkt[0] & 0x1f) + 1) * 10;
+		if (storage_local_set_transmit_params(net, count, interval))
+			mesh_net_transmit_params_set(net, count, interval);
+		/* Fall Through */
+
+	case OP_CONFIG_NETWORK_TRANSMIT_GET:
+		n = mesh_model_opcode_set(OP_CONFIG_NETWORK_TRANSMIT_STATUS,
+									msg);
+		mesh_net_transmit_params_get(net, &count, &interval);
+		msg[n++] = ((count - 1) << 5) + ((interval/10 - 1) & 0x1f);
+
+		l_info("Get/Set Network Transmit Config");
+		break;
+
+	case OP_CONFIG_PROXY_SET:
+		if (size != 1 || pkt[0] > 0x01)
+			return true;
+
+		node_proxy_mode_set(node, !!pkt[0]);
+		/* Fall Through */
+
+	case OP_CONFIG_PROXY_GET:
+		n = mesh_model_opcode_set(OP_CONFIG_PROXY_STATUS, msg);
+
+		msg[n++] = node_proxy_mode_get(node);
+		l_info("Get/Set Config Proxy (%d)", msg[n-1]);
+		break;
+
+	case OP_NODE_IDENTITY_SET:
+		if (size != 3 || pkt[2] > 0x01)
+			return true;
+
+		net_idx = l_get_le16(pkt);
+		if (net_idx > 0xfff)
+			return true;
+
+		/*
+		 * Currently no support for proxy: node identity not supported
+		 */
+
+		/* Fall Through */
+
+	case OP_NODE_IDENTITY_GET:
+		if (size < 2)
+			return true;
+
+		net_idx = l_get_le16(pkt);
+		if (net_idx > 0xfff)
+			return true;
+
+		n = mesh_model_opcode_set(OP_NODE_IDENTITY_STATUS, msg);
+
+		status = mesh_net_get_identity_mode(net, net_idx, &state);
+
+		msg[n++] = status;
+
+		l_put_le16(net_idx, msg + n);
+		n += 2;
+
+		msg[n++] = state;
+		l_info("Get/Set Config Identity (%d)", state);
+		break;
+
+	case OP_CONFIG_BEACON_SET:
+		if (size != 1 || pkt[0] > 0x01)
+			return true;
+
+		node_beacon_mode_set(node, !!pkt[0]);
+		/* Fall Through */
+
+	case OP_CONFIG_BEACON_GET:
+		n = mesh_model_opcode_set(OP_CONFIG_BEACON_STATUS, msg);
+
+		msg[n++] = node_beacon_mode_get(node);
+		l_info("Get/Set Config Beacon (%d)", msg[n-1]);
+		break;
+
+	case OP_CONFIG_FRIEND_SET:
+		if (size != 1 || pkt[0] > 0x01)
+			return true;
+
+		node_friend_mode_set(node, !!pkt[0]);
+		/* Fall Through */
+
+	case OP_CONFIG_FRIEND_GET:
+
+		n = mesh_model_opcode_set(OP_CONFIG_FRIEND_STATUS, msg);
+
+		msg[n++] = node_friend_mode_get(node);
+		l_info("Get/Set Friend (%d)", msg[n-1]);
+		break;
+
+	case OP_CONFIG_KEY_REFRESH_PHASE_SET:
+		if (size != 3 || pkt[2] > 0x03)
+			return true;
+
+		b_res = mesh_net_key_refresh_phase_set(net, l_get_le16(pkt),
+							pkt[2]);
+		size = 2;
+		/* Fall Through */
+
+	case OP_CONFIG_KEY_REFRESH_PHASE_GET:
+		if (size != 2)
+			return true;
+
+		net_idx = l_get_le16(pkt);
+
+		n = mesh_model_opcode_set(OP_CONFIG_KEY_REFRESH_PHASE_STATUS,
+						msg);
+
+		/* State: 0x00-0x03 phase of key refresh */
+		status = mesh_net_key_refresh_phase_get(net, net_idx,
+							&phase);
+		if (status != MESH_STATUS_SUCCESS) {
+			b_res = status;
+			phase = KEY_REFRESH_PHASE_NONE;
+		}
+
+		msg[n++] = b_res;
+		l_put_le16(net_idx, msg + n);
+		n += 2;
+		msg[n++] = phase;
+
+		l_info("Get/Set Key Refresh State (%d)", msg[n-1]);
+		break;
+
+	case OP_APPKEY_ADD:
+	case OP_APPKEY_UPDATE:
+		if (size != 19)
+			return true;
+
+		net_idx = l_get_le16(pkt) & 0xfff;
+		app_idx = l_get_le16(pkt + 1) >> 4;
+		b_res = appkey_key_add(net, net_idx, app_idx, pkt + 3,
+						opcode == OP_APPKEY_UPDATE);
+
+		l_info("Add/Update AppKey %s: Net_Idx %3.3x, App_Idx %3.3x",
+			(b_res == MESH_STATUS_SUCCESS) ? "success" : "fail",
+							net_idx, app_idx);
+
+
+		n = mesh_model_opcode_set(OP_APPKEY_STATUS, msg);
+
+		msg[n++] = b_res;
+		msg[n++] = pkt[0];
+		msg[n++] = pkt[1];
+		msg[n++] = pkt[2];
+		break;
+
+	case OP_APPKEY_DELETE:
+		if (size != 3)
+			return
+				true;
+
+		net_idx = l_get_le16(pkt) & 0xfff;
+		app_idx = l_get_le16(pkt + 1) >> 4;
+		b_res = appkey_key_delete(net, net_idx, app_idx);
+		if (b_res == MESH_STATUS_SUCCESS)
+			node_app_key_delete(net, dst, net_idx, app_idx);
+		l_info("Delete AppKey %s Net_Idx %3.3x to App_Idx %3.3x",
+			(b_res == MESH_STATUS_SUCCESS) ? "success" : "fail",
+							net_idx, app_idx);
+
+		n = mesh_model_opcode_set(OP_APPKEY_STATUS, msg);
+		msg[n++] = b_res;
+		msg[n++] = pkt[0];
+		msg[n++] = pkt[1];
+		msg[n++] = pkt[2];
+		break;
+
+	case OP_APPKEY_GET:
+		if (size != 2)
+			return true;
+		net_idx = l_get_le16(pkt);
+
+		long_msg = l_malloc(CFG_MAX_MSG_LEN);
+		n = mesh_model_opcode_set(OP_APPKEY_LIST, long_msg);
+
+		status = appkey_list(net, net_idx, long_msg + n + 3,
+						CFG_MAX_MSG_LEN - n - 3, &size);
+
+		long_msg[n] = status;
+		l_put_le16(net_idx, long_msg + n + 1);
+		n += (size + 3);
+		break;
+
+	case OP_NETKEY_ADD:
+	case OP_NETKEY_UPDATE:
+		if (size != 18)
+			return true;
+
+		b_res = mesh_net_add_key(net, opcode == OP_NETKEY_UPDATE,
+						l_get_le16(pkt), pkt + 2);
+
+		l_info("NetKey Add/Update %s",
+			(b_res == MESH_STATUS_SUCCESS) ? "success" : "fail");
+
+		n = mesh_model_opcode_set(OP_NETKEY_STATUS, msg);
+		msg[n++] = b_res;
+		l_put_le16(l_get_le16(pkt), msg + n);
+		n += 2;
+		break;
+
+	case OP_NETKEY_DELETE:
+		if (size != 2)
+			return true;
+
+		b_res = mesh_net_del_key(net, l_get_le16(pkt));
+
+		l_info("NetKey delete %s",
+			(b_res == MESH_STATUS_SUCCESS) ? "success" : "fail");
+
+		n = mesh_model_opcode_set(OP_NETKEY_STATUS, msg);
+		msg[n++] = b_res;
+		l_put_le16(l_get_le16(pkt), msg + n);
+		n += 2;
+		break;
+
+	case OP_NETKEY_GET:
+		long_msg = l_malloc(CFG_MAX_MSG_LEN);
+		n = mesh_model_opcode_set(OP_NETKEY_LIST, long_msg);
+		size = CFG_MAX_MSG_LEN - n;
+
+		if (mesh_net_key_list_get(net, long_msg + n, &size))
+			n += size;
+		else
+			n = 0;
+		break;
+
+	case OP_MODEL_APP_BIND:
+	case OP_MODEL_APP_UNBIND:
+		model_app_bind(net, src, unicast, pkt, size,
+				opcode != OP_MODEL_APP_BIND);
+		break;
+
+	case OP_VEND_MODEL_APP_GET:
+		if (size != 6)
+			return true;
+		model_app_list(net, src, unicast, pkt, size);
+		break;
+
+	case OP_MODEL_APP_GET:
+		if (size != 4)
+			return true;
+		model_app_list(net, src, unicast, pkt, size);
+		break;
+
+	case OP_CONFIG_HEARTBEAT_PUB_SET:
+		l_info("OP_CONFIG_HEARTBEAT_PUB_SET");
+		if (size != 9) {
+			l_info("bad size %d", size);
+			return true;
+		}
+		if (pkt[2] > 0x11 || pkt[3] > 0x10 || pkt[4] > 0x7f)
+			return true;
+		else if (IS_VIRTUAL(l_get_le16(pkt)))
+			b_res = MESH_STATUS_INVALID_ADDRESS;
+		else if (l_get_le16(pkt + 7) != mesh_net_get_primary_idx(net))
+			/* Future work: check for valid subnets */
+			b_res = MESH_STATUS_INVALID_NETKEY;
+
+		n = mesh_model_opcode_set(OP_CONFIG_HEARTBEAT_PUB_STATUS,
+						msg);
+		msg[n++] = b_res;
+
+		memcpy(&msg[n], pkt, 9);
+
+		/* Ignore RFU bits in features */
+		l_put_le16(l_get_le16(pkt + 5) & 0xf, &msg[n + 5]);
+
+		/* Add octet count to status */
+		n += 9;
+
+		if (b_res != MESH_STATUS_SUCCESS)
+			break;
+
+		hb->pub_dst = l_get_le16(pkt);
+		if (hb->pub_dst == UNASSIGNED_ADDRESS ||
+				pkt[2] == 0 || pkt[3] == 0) {
+			/*
+			 * We might still have a pub_dst here in case
+			 * we need it for State Change heartbeat
+			 */
+			hb->pub_count = 0;
+			hb->pub_period = 0;
+		} else {
+			hb->pub_count = (pkt[2] != 0xff) ?
+				log_to_uint32(pkt[2], 1) : 0xffff;
+			hb->pub_period = log_to_uint32(pkt[3], 1);
+		}
+
+		hb->pub_ttl = pkt[4];
+		hb->pub_features = l_get_le16(pkt + 5) & 0xf;
+		hb->pub_net_idx = l_get_le16(pkt + 7);
+		update_hb_pub_timer(net, hb);
+
+		break;
+
+	case OP_CONFIG_HEARTBEAT_PUB_GET:
+		n = mesh_model_opcode_set(OP_CONFIG_HEARTBEAT_PUB_STATUS, msg);
+		msg[n++] = b_res;
+		l_put_le16(hb->pub_dst, msg + n);
+		n += 2;
+		msg[n++] = uint32_to_log(hb->pub_count);
+		msg[n++] = uint32_to_log(hb->pub_period);
+		msg[n++] = hb->pub_ttl;
+		l_put_le16(hb->pub_features, msg + n);
+		n += 2;
+		l_put_le16(hb->pub_net_idx, msg + n);
+		n += 2;
+		break;
+
+	case OP_CONFIG_HEARTBEAT_SUB_SET:
+		if (size != 5)
+			return true;
+
+		l_info("Set Sub Period (Log %2.2x) %d sec",
+				pkt[4], log_to_uint32(pkt[4], 1));
+
+		b_res = hb_subscription_set(net, l_get_le16(pkt),
+						l_get_le16(pkt + 2),
+						pkt[4]);
+		if (b_res < 0)
+			return true;
+
+		/* Fall through */
+
+	case OP_CONFIG_HEARTBEAT_SUB_GET:
+		gettimeofday(&time_now, NULL);
+		time_now.tv_sec -= hb->sub_start;
+
+		if (time_now.tv_sec >= hb->sub_period)
+			time_now.tv_sec = 0;
+		else
+			time_now.tv_sec = hb->sub_period - time_now.tv_sec;
+
+		l_info("Sub Period (Log %2.2x) %d sec",
+				uint32_to_log(time_now.tv_sec),
+				(int) time_now.tv_sec);
+
+		n = mesh_model_opcode_set(OP_CONFIG_HEARTBEAT_SUB_STATUS, msg);
+		msg[n++] = b_res;
+		l_put_le16(hb->sub_src, msg + n);
+		n += 2;
+		l_put_le16(hb->sub_dst, msg + n);
+		n += 2;
+		msg[n++] = uint32_to_log(time_now.tv_sec);
+		msg[n++] = uint32_to_log(hb->sub_count);
+		msg[n++] = hb->sub_count ? hb->sub_min_hops : 0;
+		msg[n++] = hb->sub_max_hops;
+		break;
+
+	case OP_CONFIG_POLL_TIMEOUT_LIST:
+		if (size != 2 || l_get_le16(pkt) == 0 ||
+						l_get_le16(pkt) > 0x7fff)
+			return true;
+
+		n = mesh_model_opcode_set(OP_CONFIG_POLL_TIMEOUT_STATUS, msg);
+		l_put_le16(l_get_le16(pkt), msg + n);
+		n += 2;
+		tmp32 = mesh_net_friend_timeout(net, l_get_le16(pkt));
+		msg[n++] = tmp32;
+		msg[n++] = tmp32 >> 8;
+		msg[n++] = tmp32 >> 16;
+		break;
+
+	case OP_NODE_RESET:
+		n = mesh_model_opcode_set(OP_NODE_RESET_STATUS, msg);
+		l_timeout_create(1, node_reset, net, NULL);
+		break;
+	}
+
+	if (n) {
+		/* print_packet("App Tx", long_msg ? long_msg : msg, n); */
+		mesh_model_send(net, CONFIG_SRV_MODEL,
+				unicast, src,
+				APP_IDX_DEV, DEFAULT_TTL,
+				long_msg ? long_msg : msg, n);
+	}
+	l_free(long_msg);
+
+	return true;
+}
+
+static void cfgmod_srv_unregister(void *user_data)
+{
+	struct mesh_net *net = user_data;
+	struct mesh_net_heartbeat *hb = mesh_net_heartbeat_get(net);
+
+	l_timeout_remove(hb->pub_timer);
+	l_timeout_remove(hb->sub_timer);
+	hb->pub_timer = hb->sub_timer = NULL;
+}
+
+static const struct mesh_model_ops ops = {
+	.unregister = cfgmod_srv_unregister,
+	.recv = cfg_srv_pkt,
+	.bind = NULL,
+	.sub = NULL,
+	.pub = NULL
+};
+
+void mesh_config_srv_init(struct mesh_net *net, uint8_t ele_idx)
+{
+	l_debug("%2.2x", ele_idx);
+	mesh_model_register(net, ele_idx, CONFIG_SRV_MODEL, &ops, net);
+}
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v2 09/11] meshd: Read and write mesh configuration in JSON format
  2018-04-25 17:50 [PATCH BlueZ v2 00/11] Bluetooth Mesh Daemon Brian Gix
                   ` (5 preceding siblings ...)
  2018-04-25 17:50 ` [PATCH BlueZ v2 08/11] meshd: Mesh config server model Brian Gix
@ 2018-04-25 17:50 ` Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 10/11] meshd: Sample device composition in JSON fromat Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 11/11] Makefile for meshd and configure.ac Brian Gix
  8 siblings, 0 replies; 10+ messages in thread
From: Brian Gix @ 2018-04-25 17:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Inga Stotland

From: Inga Stotland <inga.stotland@intel.com>

This adds implementation for parsing and writing Mesh configuration
into JSON format. Alos, parse stored unprovisioned device composiotion.
---
 meshd/mesh-json/mesh-db.c | 1360 +++++++++++++++++++++++++++++++++++++++++++++
 meshd/mesh-json/mesh-db.h |  144 +++++
 2 files changed, 1504 insertions(+)
 create mode 100644 meshd/mesh-json/mesh-db.c
 create mode 100644 meshd/mesh-json/mesh-db.h

diff --git a/meshd/mesh-json/mesh-db.c b/meshd/mesh-json/mesh-db.c
new file mode 100644
index 000000000..6d894a920
--- /dev/null
+++ b/meshd/mesh-json/mesh-db.c
@@ -0,0 +1,1360 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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 <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <ell/ell.h>
+
+#include "meshd/common/mesh-defs.h"
+#include "meshd/common/util.h"
+
+#include "meshd/mesh-json/mesh-db.h"
+
+#define CHECK_KEY_IDX_RANGE(x) (((x) >= 0) && ((x) <= 4095))
+
+static bool get_int(json_object *jobj, const char *keyword, int *value)
+{
+	json_object *jvalue;
+
+	if (!json_object_object_get_ex(jobj, keyword, &jvalue))
+		return false;
+
+	*value = json_object_get_int(jvalue);
+	if (errno == EINVAL)
+		return false;
+
+	return true;
+}
+
+static bool add_key(json_object *jobject, const char *desc,
+					const uint8_t key[16])
+{
+	json_object *jstring;
+	char hexstr[33];
+
+	hex2str((uint8_t *) key, 16, hexstr, 33);
+	jstring = json_object_new_string(hexstr);
+	if (!jstring)
+		return false;
+
+	json_object_object_add(jobject, desc, jstring);
+	return true;
+}
+
+static json_object *get_element_model(json_object *jnode, int ele_idx,
+						uint32_t mod_id, bool vendor)
+{
+	json_object *jelements, *jelement, *jmodels;
+	int i, num_mods;
+	size_t len;
+	char buf[9];
+
+	if (!vendor)
+		snprintf(buf, 5, "%4.4x", (uint16_t)mod_id);
+	else
+		snprintf(buf, 9, "%8.8x", mod_id);
+
+	json_object_object_get_ex(jnode, "elements", &jelements);
+	if (!jelements)
+		return NULL;
+
+	jelement = json_object_array_get_idx(jelements, ele_idx);
+	if (!jelement)
+		return NULL;
+
+	json_object_object_get_ex(jelement, "models", &jmodels);
+	if (!jmodels)
+		return NULL;
+
+	num_mods = json_object_array_length(jmodels);
+	if (!num_mods)
+		return NULL;
+
+	if (!vendor) {
+		snprintf(buf, 5, "%4.4x", mod_id);
+		len = 4;
+	} else {
+		snprintf(buf, 9, "%8.8x", mod_id);
+		len = 8;
+	}
+
+	for (i = 0; i < num_mods; ++i) {
+		json_object *jmodel, *jvalue;
+		char *str;
+
+		jmodel = json_object_array_get_idx(jmodels, i);
+		json_object_object_get_ex(jmodel, "modelId", &jvalue);
+		if (!jvalue)
+			return NULL;
+
+		str = (char *)json_object_get_string(jvalue);
+		if (!str)
+			return NULL;
+
+		if (!strncmp(str, buf, len))
+			return jmodel;
+	}
+
+	return NULL;
+}
+
+static bool jarray_has_string(json_object *jarray, char *str, size_t len)
+{
+	int i, sz = json_object_array_length(jarray);
+
+	for (i = 0; i < sz; ++i) {
+		json_object *jentry;
+		char *str_entry;
+
+		jentry = json_object_array_get_idx(jarray, i);
+		str_entry = (char *)json_object_get_string(jentry);
+		if (!str_entry)
+			continue;
+
+		if (!strncmp(str, str_entry, len))
+			return true;
+	}
+
+	return false;
+}
+
+static json_object *jarray_string_del(json_object *jarray, char *str,
+								size_t len)
+{
+	int i, sz = json_object_array_length(jarray);
+	json_object *jarray_new;
+
+	jarray_new = json_object_new_array();
+	if (!jarray_new)
+		return NULL;
+
+	for (i = 0; i < sz; ++i) {
+		json_object *jentry;
+		char *str_entry;
+
+		jentry = json_object_array_get_idx(jarray, i);
+		str_entry = (char *)json_object_get_string(jentry);
+		if (str_entry && !strncmp(str, str_entry, len))
+			continue;
+
+		json_object_array_add(jarray_new, jentry);
+	}
+
+	return jarray_new;
+}
+
+static json_object *get_key_object(json_object *jarray, uint16_t idx)
+{
+	int i, sz = json_object_array_length(jarray);
+
+	for (i = 0; i < sz; ++i) {
+		json_object *jentry, *jvalue;
+		uint32_t jidx;
+
+		jentry = json_object_array_get_idx(jarray, i);
+		if (!json_object_object_get_ex(jentry, "index", &jvalue))
+			return NULL;
+
+		jidx = json_object_get_int(jvalue);
+
+		if (jidx == idx)
+			return jentry;
+	}
+
+	return NULL;
+}
+
+static json_object *jarray_key_del(json_object *jarray, int16_t idx)
+{
+	json_object *jarray_new;
+	int i, sz = json_object_array_length(jarray);
+	char idx_str[5];
+
+	snprintf(idx_str, 5, "%4.4x", idx);
+
+	jarray_new = json_object_new_array();
+	if (!jarray_new)
+		return NULL;
+
+	for (i = 0; i < sz; ++i) {
+		json_object *jentry, *jvalue;
+		char *str;
+
+		jentry = json_object_array_get_idx(jarray, i);
+
+		if (json_object_object_get_ex(jentry, "index", &jvalue)) {
+			str = (char *)json_object_get_string(jvalue);
+			if (str && !strncmp(str, idx_str, 4))
+				continue;
+		}
+
+		json_object_array_add(jarray_new, jentry);
+	}
+
+	return jarray_new;
+}
+
+bool mesh_db_read_iv_index(json_object *jobj, uint32_t *idx, bool *update)
+{
+	int tmp;
+
+	/* IV index */
+	if (!get_int(jobj, "IVindex", &tmp))
+		return false;
+
+	*idx = (uint32_t) tmp;
+
+	if (!get_int(jobj, "IVupdate", &tmp))
+		return false;
+
+	*update = tmp ? true : false;
+
+	return true;
+}
+
+bool mesh_db_read_device_key(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, "deviceKey", &jvalue) ||
+								!jvalue)
+		return false;
+
+	str = (char *)json_object_get_string(jvalue);
+	if (!str2hex(str, strlen(str), key_buf, 16))
+		return false;
+
+	return true;
+}
+
+bool mesh_db_read_app_keys(json_object *jobj, mesh_db_app_key_cb cb,
+							void *user_data)
+{
+	json_object *jarray;
+	int len;
+	int i;
+
+	if (!cb)
+		return true;
+
+	json_object_object_get_ex(jobj, "appKeys", &jarray);
+	if (!jarray || (json_object_get_type(jarray) != json_type_array))
+		return false;
+
+	len = json_object_array_length(jarray);
+
+	for (i = 0; i < len; ++i) {
+		json_object *jtemp, *jvalue;
+		int app_idx, net_idx;
+		bool key_refresh = false;
+		char *str;
+		uint8_t key[16];
+		uint8_t new_key[16];
+
+		jtemp = json_object_array_get_idx(jarray, i);
+
+		if (!get_int(jtemp, "index", &app_idx))
+			return false;
+
+		if (!CHECK_KEY_IDX_RANGE(app_idx))
+			return false;
+
+		if (!get_int(jtemp, "boundNetKey", &net_idx))
+			return false;
+
+		if (!CHECK_KEY_IDX_RANGE(net_idx))
+			return false;
+
+		json_object_object_get_ex(jtemp, "oldKey", &jvalue);
+		if (jvalue) {
+			str = (char *)json_object_get_string(jvalue);
+			if (!str2hex(str, strlen(str), key, 16))
+				return false;
+			key_refresh = true;
+		}
+
+		json_object_object_get_ex(jtemp, "key", &jvalue);
+		if (!jvalue)
+			return false;
+
+		str = (char *)json_object_get_string(jvalue);
+		if (!str2hex(str, strlen(str), key_refresh ? new_key : key, 16))
+			return false;
+
+		if (!cb((uint16_t)net_idx, (uint16_t) app_idx, key,
+				key_refresh ? new_key : NULL, user_data))
+			return false;
+	}
+
+	return true;
+}
+
+bool mesh_db_read_net_keys(json_object *jobj, mesh_db_net_key_cb cb,
+								void *user_data)
+{
+	json_object *jarray;
+	int len;
+	int i;
+
+	if (!cb)
+		return true;
+
+	json_object_object_get_ex(jobj, "netKeys", &jarray);
+	if (!jarray || (json_object_get_type(jarray) != json_type_array))
+		return false;
+
+	len = json_object_array_length(jarray);
+
+	for (i = 0; i < len; ++i) {
+		json_object *jtemp, *jvalue;
+		int idx;
+		char *str;
+		bool key_refresh = false;
+		int phase;
+		uint8_t key[16];
+		uint8_t new_key[16];
+
+		jtemp = json_object_array_get_idx(jarray, i);
+
+		if (!get_int(jtemp, "index", &idx))
+			return false;
+
+		if (!CHECK_KEY_IDX_RANGE(idx))
+			return false;
+
+		json_object_object_get_ex(jtemp, "oldKey", &jvalue);
+		if (jvalue) {
+			str = (char *)json_object_get_string(jvalue);
+			if (!str2hex(str, strlen(str), key, 16))
+				return false;
+			key_refresh = true;
+		}
+
+		json_object_object_get_ex(jtemp, "key", &jvalue);
+		if (!jvalue)
+			return false;
+
+		str = (char *)json_object_get_string(jvalue);
+		if (!str2hex(str, strlen(str), key_refresh ? new_key : key, 16))
+			return false;
+
+		json_object_object_get_ex(jtemp, "keyRefresh", &jvalue);
+		if (!jvalue)
+			phase = KEY_REFRESH_PHASE_NONE;
+		else
+			phase = json_object_get_int(jvalue);
+
+
+		if (!cb((uint16_t)idx, key, key_refresh ? new_key : NULL, phase,
+								user_data))
+			return false;
+	}
+
+	return true;
+}
+
+bool mesh_db_net_key_add(json_object *jobj, uint16_t idx,
+					const uint8_t key[16], int phase)
+{
+	json_object *jarray, *jentry = NULL, *jstring;
+	char buf[5];
+
+	json_object_object_get_ex(jobj, "netKeys", &jarray);
+	if (!jarray && (phase != KEY_REFRESH_PHASE_NONE))
+		return false;
+
+	if (jarray)
+		jentry = get_key_object(jarray, idx);
+
+	/*
+	 * The key entry should exist if the key is updated
+	 * (i.e., Key Refresh is underway)
+	 */
+	if (!jentry && (phase != KEY_REFRESH_PHASE_NONE))
+		return false;
+
+	if (jentry) {
+		uint8_t buf[16];
+		json_object *jvalue;
+		char *str;
+
+		json_object_object_get_ex(jentry, "key", &jvalue);
+		if (!jvalue)
+			return false;
+
+		str = (char *)json_object_get_string(jvalue);
+		if (!str2hex(str, strlen(str), buf, sizeof(buf)))
+			return false;
+
+		/* If the same key, return success */
+		if (memcmp(key, buf, 16) == 0)
+			return true;
+
+		return false;
+	}
+
+	if (phase == KEY_REFRESH_PHASE_NONE) {
+		jentry = json_object_new_object();
+		if (!jentry)
+			goto fail;
+
+		snprintf(buf, 5, "%4.4x", idx);
+		jstring = json_object_new_string(buf);
+		if (!jstring)
+			goto fail;
+
+		json_object_object_add(jentry, "index", jstring);
+
+		snprintf(buf, 5, "%4.4x", idx);
+		jstring = json_object_new_string(buf);
+		if (!jstring)
+			goto fail;
+
+		if (!add_key(jentry, "key", key))
+			goto fail;
+
+		if (!jarray) {
+			jarray = json_object_new_array();
+			if (!jarray)
+				goto fail;
+			json_object_object_add(jobj, "netKeys", jarray);
+		}
+
+		json_object_array_add(jarray, jentry);
+
+	} else {
+
+		if (!json_object_object_get_ex(jentry, "key", &jstring))
+			return false;
+
+		json_object_object_add(jentry, "oldKey", jstring);
+		json_object_object_del(jentry, "key");
+
+		if (!add_key(jentry, "key", key))
+			return false;
+	}
+
+
+	json_object_object_add(jentry, "keyRefresh",
+					json_object_new_int(phase));
+
+	return true;
+fail:
+
+	if (jentry)
+		json_object_put(jentry);
+
+	return false;
+}
+
+bool mesh_db_net_key_del(json_object *jobj, uint16_t idx)
+{
+	json_object *jarray, *jarray_new;
+
+	json_object_object_get_ex(jobj, "netKeys", &jarray);
+	if (!jarray)
+		return true;
+
+	/* Check if matching entry exists */
+	if (!get_key_object(jarray, idx))
+		return true;
+
+	if (json_object_array_length(jarray) == 1) {
+		json_object_object_del(jobj, "netKeys");
+		return true;
+	}
+
+	/*
+	 * There is no easy way to delete a value from json array.
+	 * Create a new copy without specified element and
+	 * then remove old array.
+	 */
+	jarray_new = jarray_key_del(jarray, idx);
+	if (!jarray_new)
+		return false;
+
+	json_object_object_del(jobj, "netKeys");
+	json_object_object_add(jobj, "netKeys", jarray_new);
+
+	return true;
+}
+
+bool mesh_db_write_device_key(json_object *jnode, uint8_t *key)
+{
+	return add_key(jnode, "deviceKey", key);
+}
+
+bool mesh_db_app_key_add(json_object *jobj, uint16_t net_idx, uint16_t app_idx,
+			 const uint8_t key[16], bool update)
+{
+	json_object *jarray, *jentry = NULL, *jstring = NULL;
+	char buf[5];
+
+	json_object_object_get_ex(jobj, "appKeys", &jarray);
+	if (!jarray && update)
+		return false;
+
+	if (jarray)
+		jentry = get_key_object(jarray, app_idx);
+
+	/* The key entry should exist if the key is updated */
+	if (!jentry  && update)
+		return false;
+
+	if (jentry) {
+		uint8_t buf[16];
+		json_object *jvalue;
+		char *str;
+
+		json_object_object_get_ex(jentry, "key", &jvalue);
+		if (!jvalue)
+			return false;
+
+		str = (char *)json_object_get_string(jvalue);
+		if (!str2hex(str, strlen(str), buf, sizeof(buf)))
+			return false;
+
+		/* If the same key, return success */
+		if (memcmp(key, buf, 16) == 0)
+			return true;
+
+		return false;
+	}
+
+	if (!update) {
+		jentry = json_object_new_object();
+		if (!jentry)
+			goto fail;
+
+		snprintf(buf, 5, "%4.4x", app_idx);
+		jstring = json_object_new_string(buf);
+		if (!jstring)
+			goto fail;
+
+		json_object_object_add(jentry, "index", jstring);
+
+		snprintf(buf, 5, "%4.4x", net_idx);
+		jstring = json_object_new_string(buf);
+		if (!jstring)
+			goto fail;
+
+		json_object_object_add(jentry, "boundNetKey", jstring);
+
+		if (!add_key(jentry, "key", key))
+			goto fail;
+
+		if (!jarray) {
+			jarray = json_object_new_array();
+			if (!jarray)
+				goto fail;
+			json_object_object_add(jobj, "appKeys", jarray);
+		}
+
+		json_object_array_add(jarray, jentry);
+
+	} else {
+
+		if (!json_object_object_get_ex(jentry, "key", &jstring))
+			return false;
+
+		json_object_object_add(jentry, "oldKey", jstring);
+		json_object_object_del(jentry, "key");
+
+		if (!add_key(jentry, "key", key))
+			return false;
+	}
+
+	return true;
+fail:
+
+	if (jentry)
+		json_object_put(jentry);
+
+	return false;
+}
+
+bool mesh_db_app_key_del(json_object *jobj, uint16_t net_idx, uint16_t idx)
+{
+	json_object *jarray, *jarray_new;
+
+	json_object_object_get_ex(jobj, "appKeys", &jarray);
+	if (!jarray)
+		return true;
+
+	/* Check if matching entry exists */
+	if (!get_key_object(jarray, idx))
+		return true;
+
+	if (json_object_array_length(jarray) == 1) {
+		json_object_object_del(jobj, "appKeys");
+		return true;
+	}
+
+	/*
+	 * There is no easy way to delete a value from json array.
+	 * Create a new copy without specified element and
+	 * then remove old array.
+	 */
+	jarray_new = jarray_key_del(jarray, idx);
+	if (!jarray_new)
+		return false;
+
+	json_object_object_del(jobj, "appKeys");
+	json_object_object_add(jobj, "appKeys", jarray_new);
+
+	return true;
+}
+
+bool mesh_db_model_binding_add(json_object *jnode, uint8_t ele_idx, bool vendor,
+				uint32_t mod_id, uint16_t app_idx)
+{
+	json_object *jmodel, *jstring, *jarray;
+	char buf[5];
+
+	jmodel = get_element_model(jnode, ele_idx, mod_id, vendor);
+	if (!jmodel)
+		return false;
+
+	json_object_object_get_ex(jmodel, "bind", &jarray);
+
+	snprintf(buf, 5, "%4.4x", app_idx);
+
+	if (jarray && jarray_has_string(jarray, buf, 4))
+		return true;
+
+	jstring = json_object_new_string(buf);
+	if (!jstring)
+		return false;
+
+	if (!jarray) {
+		jarray = json_object_new_array();
+		if (!jarray) {
+			json_object_put(jstring);
+			return false;
+		}
+		json_object_object_add(jmodel, "bind", jarray);
+	}
+
+	json_object_array_add(jarray, jstring);
+
+	return true;
+}
+
+bool mesh_db_model_binding_del(json_object *jnode, uint8_t ele_idx, bool vendor,
+				uint32_t mod_id, uint16_t app_idx)
+{
+	json_object *jmodel, *jarray, *jarray_new;
+	char buf[5];
+
+	jmodel = get_element_model(jnode, ele_idx, mod_id, vendor);
+	if (!jmodel)
+		return false;
+
+	json_object_object_get_ex(jmodel, "bind", &jarray);
+
+	snprintf(buf, 5, "%4.4x", app_idx);
+
+	if (!jarray || !jarray_has_string(jarray, buf, 4))
+		return true;
+
+	if (json_object_array_length(jarray) == 1) {
+		json_object_object_del(jmodel, "bind");
+		return true;
+	}
+
+	/*
+	 * There is no easy way to delete a value from json array.
+	 * Create a new copy without specified element and
+	 * then remove old array.
+	 */
+	jarray_new = jarray_string_del(jarray, buf, 4);
+	if (!jarray_new)
+		return false;
+
+	json_object_object_del(jmodel, "bind");
+	json_object_object_add(jmodel, "bind", jarray_new);
+
+	return true;
+}
+
+static void free_model(void *data)
+{
+	struct mesh_db_model *mod = data;
+
+	l_free(mod->bindings);
+	l_free(mod->subs);
+	l_free(mod->pub);
+	l_free(mod);
+}
+
+static void free_element(void *data)
+{
+	struct mesh_db_element *ele = data;
+
+	l_queue_destroy(ele->models, free_model);
+	l_free(ele);
+}
+
+static bool parse_bindings(json_object *jbindings, struct mesh_db_model *mod)
+{
+	int cnt;
+	int i;
+
+	cnt = json_object_array_length(jbindings);
+	if (cnt > 0xffff)
+		return false;
+
+	mod->num_subs = cnt;
+
+	/* Allow empty bindings list */
+	if (!cnt)
+		return true;
+
+	mod->bindings = l_new(uint16_t, cnt);
+	if (!mod->bindings)
+		return false;
+
+	for (i = 0; i < cnt; ++i) {
+		int idx;
+		json_object *jvalue;
+
+		jvalue = json_object_array_get_idx(jbindings, i);
+		if (!jvalue)
+			return false;
+
+		idx = json_object_get_int(jvalue);
+		if (!CHECK_KEY_IDX_RANGE(idx))
+			return false;
+
+		mod->bindings[i] = (uint16_t) idx;
+	}
+
+	return true;
+}
+
+static bool parse_models(json_object *jmodels, struct mesh_db_element *ele)
+{
+	int i, num_models;
+
+	num_models = json_object_array_length(jmodels);
+	if (!num_models)
+		return true;
+
+	for (i = 0; i < num_models; ++i) {
+		json_object *jmodel, *jarray, *jvalue;
+		struct mesh_db_model *mod;
+		uint32_t id;
+		int len;
+		char *str;
+
+		jmodel = json_object_array_get_idx(jmodels, i);
+		if (!jmodel)
+			goto fail;
+
+		mod = l_new(struct mesh_db_model, 1);
+		if (!ele)
+			goto fail;
+
+		json_object_object_get_ex(jmodel, "modelId", &jvalue);
+		str = (char *)json_object_get_string(jvalue);
+
+		len = strlen(str);
+
+		if (len != 4 && len != 8)
+			goto fail;
+
+		if (len == 4) {
+			if (sscanf(str, "%04x", &id) != 1)
+				goto fail;
+
+			id |= VENDOR_ID_MASK;
+		} else if (len == 8) {
+			if (sscanf(str, "%08x", &id) != 1)
+				goto fail;
+		} else
+			goto fail;
+
+		mod->id = id;
+
+		if (len == 8)
+			mod->vendor = true;
+
+		json_object_object_get_ex(jmodel, "bind", &jarray);
+
+		if (jarray && (json_object_get_type(jmodels) != json_type_array
+					|| !parse_bindings(jarray, mod)))
+			goto fail;
+
+		/* TODO add pub/sub */
+		l_queue_push_tail(ele->models, mod);
+	}
+
+	return true;
+
+fail:
+	l_queue_destroy(ele->models, free_model);
+	return false;
+}
+
+static bool parse_elements(json_object *jelements, struct mesh_db_node *node)
+{
+	int i, num_ele;
+
+	num_ele = json_object_array_length(jelements);
+	if (!num_ele)
+		/* Allow "empty" nodes */
+		return true;
+
+	node->elements = l_queue_new();
+	if (!node->elements)
+		return false;
+
+	for (i = 0; i < num_ele; ++i) {
+		json_object *jelement;
+		json_object *jmodels;
+		json_object *jvalue;
+		struct mesh_db_element *ele;
+		int index;
+		char *str;
+
+		jelement = json_object_array_get_idx(jelements, i);
+		if (!jelement)
+			goto fail;
+
+		if (!get_int(jelement, "elementIndex", &index) ||
+								index > num_ele)
+			goto fail;
+
+		ele = l_new(struct mesh_db_element, 1);
+		if (!ele)
+			goto fail;
+
+		ele->index = index;
+		ele->models = l_queue_new();
+		if (!ele->models)
+			goto fail;
+
+		json_object_object_get_ex(jelement, "location", &jvalue);
+		str = (char *)json_object_get_string(jvalue);
+		if (sscanf(str, "%04hx", &(ele->location)) != 1)
+			goto fail;
+
+		json_object_object_get_ex(jelement, "models", &jmodels);
+
+		if (jmodels && (json_object_get_type(jmodels) != json_type_array
+				|| !parse_models(jmodels, ele)))
+			goto fail;
+
+		l_queue_push_tail(node->elements, ele);
+	}
+
+	return true;
+
+fail:
+	l_queue_destroy(node->elements, free_element);
+	node->elements = NULL;
+
+	return false;
+}
+
+static int get_mode(json_object *jvalue)
+{
+	const char *str;
+
+	str = json_object_get_string(jvalue);
+	if (!str)
+		return 0xffffffff;
+
+	if (!strncasecmp(str, "disabled", strlen("disabled")))
+		return MESH_MODE_DISABLED;
+
+	if (!strncasecmp(str, "enabled", strlen("enabled")))
+		return MESH_MODE_ENABLED;
+
+	if (!strncasecmp(str, "unsupported", strlen("unsupported")))
+		return MESH_MODE_UNSUPPORTED;
+
+	return 0xffffffff;
+}
+
+static void parse_features(json_object *jconfig, struct mesh_db_node *node)
+{
+	json_object *jvalue, *jrelay;
+	int mode, count;
+	uint16_t interval;
+
+	json_object_object_get_ex(jconfig, "proxy", &jvalue);
+	if (jvalue) {
+		mode = get_mode(jvalue);
+		if (mode <= MESH_MODE_UNSUPPORTED)
+			node->modes.proxy = mode;
+	}
+
+	json_object_object_get_ex(jconfig, "friend", &jvalue);
+	if (jvalue) {
+		mode = get_mode(jvalue);
+		if (mode <= MESH_MODE_UNSUPPORTED)
+			node->modes.friend = mode;
+	}
+
+	json_object_object_get_ex(jconfig, "lowPower", &jvalue);
+	if (jvalue) {
+		mode = get_mode(jvalue);
+		if (mode <= MESH_MODE_UNSUPPORTED)
+			node->modes.friend = mode;
+	}
+
+	json_object_object_get_ex(jconfig, "beacon", &jvalue);
+	if (jvalue) {
+		mode = get_mode(jvalue);
+		if (mode <= MESH_MODE_ENABLED)
+			node->modes.beacon = mode;
+	}
+
+	json_object_object_get_ex(jconfig, "relay", &jrelay);
+	if (!jrelay)
+		return;
+
+	json_object_object_get_ex(jrelay, "mode", &jvalue);
+	if (jvalue) {
+		mode = get_mode(jvalue);
+		if (mode <= MESH_MODE_UNSUPPORTED)
+			node->modes.relay.state = mode;
+		else
+			return;
+	} else
+		return;
+
+	json_object_object_get_ex(jrelay, "count", &jvalue);
+	if (!jvalue)
+		return;
+
+	/* TODO: check range */
+	count = json_object_get_int(jvalue);
+	node->modes.relay.cnt = count;
+
+	json_object_object_get_ex(jrelay, "interval", &jvalue);
+	if (!jvalue)
+		return;
+
+	/* TODO: check range */
+	interval = json_object_get_int(jvalue);
+	node->modes.relay.interval = interval;
+}
+
+static bool parse_composition(json_object *jcomp, struct mesh_db_node *node)
+{
+	json_object *jvalue;
+	char *str;
+
+	/* All the fields in node composition are mandatory */
+	json_object_object_get_ex(jcomp, "cid", &jvalue);
+	if (!jvalue)
+		return false;
+
+	str = (char *)json_object_get_string(jvalue);
+	if (sscanf(str, "%04hx", &node->cid) != 1)
+		return false;
+
+	json_object_object_get_ex(jcomp, "pid", &jvalue);
+	if (!jvalue)
+		return false;
+
+	str = (char *)json_object_get_string(jvalue);
+	if (sscanf(str, "%04hx", &node->pid) != 1)
+		return false;
+
+	json_object_object_get_ex(jcomp, "vid", &jvalue);
+	if (!jvalue)
+		return false;
+
+	str = (char *)json_object_get_string(jvalue);
+	if (sscanf(str, "%04hx", &node->vid) != 1)
+		return false;
+
+	json_object_object_get_ex(jcomp, "crpl", &jvalue);
+	if (!jvalue)
+		return false;
+
+	str = (char *)json_object_get_string(jvalue);
+	if (sscanf(str, "%04hx", &node->crpl) != 1)
+		return false;
+
+	return true;
+}
+
+static uint16_t get_prov_flags(json_object *jarray, uint16_t max_value)
+{
+	int i, cnt;
+	uint16_t result = 0;
+
+	cnt = json_object_array_length(jarray);
+	if (!cnt)
+		return 0;
+
+	for (i = 0; i < cnt; ++i) {
+		json_object *jvalue;
+		int value;
+
+		jvalue = json_object_array_get_idx(jarray, i);
+		value = json_object_get_int(jvalue);
+		if (value > 16)
+			continue;
+
+		if ((1 << value) > max_value)
+			continue;
+
+		result |= (1 << value);
+	}
+
+	return result;
+}
+
+bool mesh_db_read_node(json_object *jnode, mesh_db_node_cb cb, void *user_data)
+{
+	struct mesh_db_node node;
+	json_object *jvalue;
+	char *str;
+
+	if (!cb) {
+		l_info("Node read callback is required");
+		return false;
+	}
+
+	memset(&node, 0, sizeof(node));
+
+	if (!parse_composition(jnode, &node)) {
+		l_info("Failed to parse local node composition");
+		return false;
+	}
+
+	parse_features(jnode, &node);
+
+	json_object_object_get_ex(jnode, "unicastAddress", &jvalue);
+	if (!jvalue) {
+		l_info("Bad config: Unicast address must be present");
+		return false;
+	}
+
+	str = (char *)json_object_get_string(jvalue);
+	if (sscanf(str, "%04hx", &node.unicast) != 1)
+		return false;
+
+	json_object_object_get_ex(jnode, "defaultTTL", &jvalue);
+	if (jvalue) {
+		int ttl = json_object_get_int(jvalue);
+
+		if (ttl < 0 || ttl == 1 || ttl > DEFAULT_TTL)
+			return false;
+		node.ttl = (uint8_t) ttl;
+	}
+
+	json_object_object_get_ex(jnode, "sequenceNumber", &jvalue);
+	if (jvalue)
+		node.seq_number = json_object_get_int(jvalue);
+
+	json_object_object_get_ex(jnode, "elements", &jvalue);
+	if (jvalue && json_object_get_type(jvalue) == json_type_array) {
+		if (!parse_elements(jvalue, &node))
+			return false;
+	}
+
+	return cb(&node, user_data);
+}
+
+bool mesh_db_read_unprovisioned_device(json_object *jnode, mesh_db_node_cb cb,
+								void *user_data)
+{
+	struct mesh_db_node node;
+	json_object *jvalue;
+	char *str;
+
+	if (!cb) {
+		l_info("Device read callback is required");
+		return false;
+	}
+
+	memset(&node, 0, sizeof(node));
+
+	if (!parse_composition(jnode, &node)) {
+		l_info("Failed to parse local device composition");
+		return false;
+	}
+
+	parse_features(jnode, &node);
+
+	json_object_object_get_ex(jnode, "elements", &jvalue);
+	if (jvalue && json_object_get_type(jvalue) == json_type_array) {
+		if (!parse_elements(jvalue, &node))
+			return false;
+	}
+
+	json_object_object_get_ex(jnode, "UUID", &jvalue);
+	if (!jvalue)
+		return false;
+
+	str = (char *)json_object_get_string(jvalue);
+	if (!str2hex(str, strlen(str), node.uuid, 16))
+		return false;
+
+	return cb(&node, user_data);
+}
+
+bool mesh_db_read_prov_info(json_object *jnode, struct mesh_db_prov *prov)
+{
+	json_object *jprov, *jarray, *jvalue, *jobj;
+	int value;
+	char *str;
+
+	if (!prov)
+		return false;
+
+	json_object_object_get_ex(jnode, "provision", &jprov);
+	if (!jprov)
+		return false;
+
+	json_object_object_get_ex(jprov, "algorithms", &jarray);
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	prov->algorithm = get_prov_flags(jarray, ALG_FIPS_256_ECC);
+	if (!prov->algorithm) {
+		l_info("At least one algorithm must be indicated");
+		return false;
+	}
+
+	json_object_object_get_ex(jprov, "outputOOB", &jobj);
+	json_object_object_get_ex(jobj, "size", &jvalue);
+	value = json_object_get_int(jvalue);
+	if (value > 8)
+		return false;
+
+	prov->output_oob.size = (uint8_t) value;
+	json_object_object_get_ex(jobj, "actions", &jarray);
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	prov->output_oob.actions = get_prov_flags(jarray, OOB_OUT_ALPHA);
+
+	json_object_object_get_ex(jprov, "inputOOB", &jobj);
+	json_object_object_get_ex(jobj, "size", &jvalue);
+	value = json_object_get_int(jvalue);
+	if (value > 8)
+		return false;
+
+	prov->input_oob.size = (uint8_t) value;
+	json_object_object_get_ex(jobj, "actions", &jarray);
+	if (!jarray || json_object_get_type(jarray) != json_type_array)
+		return false;
+
+	prov->input_oob.actions = get_prov_flags(jarray, OOB_IN_ALPHA);
+
+	json_object_object_get_ex(jprov, "publicType", &jvalue);
+	prov->pub_type = (json_object_get_boolean(jvalue)) ? 1 : 0;
+
+	json_object_object_get_ex(jprov, "staticType", &jvalue);
+	prov->static_type = (json_object_get_boolean(jvalue)) ? 1 : 0;
+
+	json_object_object_get_ex(jprov, "privateKey", &jvalue);
+	if (!jvalue)
+		return false;
+
+	str = (char *)json_object_get_string(jvalue);
+	if (!str2hex(str, strlen(str), prov->priv_key, 32))
+		return false;
+
+	return true;
+}
+
+bool mesh_db_write_uint16_hex(json_object *jobj, const char *desc,
+								uint16_t value)
+{
+	json_object *jstring;
+	char buf[5];
+
+	snprintf(buf, 5, "%4.4x", value);
+	jstring = json_object_new_string(buf);
+	if (!jstring)
+		return false;
+
+	json_object_object_add(jobj, desc, jstring);
+	return true;
+}
+
+bool mesh_db_write_int(json_object *jobj, const char *keyword, int value)
+{
+	json_object *jvalue;
+
+	json_object_object_del(jobj, keyword);
+
+	jvalue = json_object_new_int(value);
+	if (!jvalue)
+		return false;
+
+	json_object_object_add(jobj, keyword, jvalue);
+	return true;
+}
+
+bool mesh_db_write_bool(json_object *jobj, const char *keyword, bool value)
+{
+	json_object *jvalue;
+
+	json_object_object_del(jobj, keyword);
+
+	jvalue = json_object_new_boolean(value);
+	if (!jvalue)
+		return false;
+
+	json_object_object_add(jobj, keyword, jvalue);
+	return true;
+}
+
+bool mesh_db_write_mode(json_object *jobj, const char *keyword, int value)
+{
+	json_object *jstring;
+
+	switch (value) {
+	case MESH_MODE_DISABLED:
+		jstring = json_object_new_string("disabled");
+		break;
+	case MESH_MODE_ENABLED:
+		jstring = json_object_new_string("enabled");
+		break;
+	case MESH_MODE_UNSUPPORTED:
+		jstring = json_object_new_string("unsupported");
+		break;
+	default:
+		return false;
+	};
+
+	if (!jstring)
+		return false;
+
+	json_object_object_add(jobj, keyword, jstring);
+
+	return true;
+}
+
+bool mesh_db_write_relay_mode(json_object *jnode, uint8_t mode, uint8_t count,
+							uint16_t interval)
+{
+	json_object *jrelay;
+
+	json_object_object_del(jnode, "relay");
+
+	jrelay = json_object_new_object();
+	if (jrelay)
+		return false;
+
+	if (!mesh_db_write_mode(jrelay, "mode", mode))
+		goto fail;
+
+	if (!mesh_db_write_int(jrelay, "count", count))
+		goto fail;
+
+	if (!mesh_db_write_int(jrelay, "interval", interval))
+		goto fail;
+
+	json_object_object_add(jnode, "relay", jrelay);
+
+	return true;
+fail:
+	json_object_put(jrelay);
+	return false;
+}
+
+bool mesh_db_read_net_transmit(json_object *jobj, uint8_t *cnt,
+							uint16_t *interval)
+{
+	json_object *jretransmit, *jvalue;
+
+	json_object_object_get_ex(jobj, "retransmit", &jretransmit);
+	if (!jretransmit)
+		return false;
+
+	json_object_object_get_ex(jretransmit, "count", &jvalue);
+	if (!jvalue)
+		return false;
+
+	*cnt = (uint8_t) json_object_get_int(jvalue);
+
+	json_object_object_get_ex(jretransmit, "interval", &jvalue);
+	if (!jvalue)
+		return false;
+
+	*interval = (uint16_t) json_object_get_int(jvalue);
+
+	return true;
+}
+
+bool mesh_db_write_net_transmit(json_object *jobj, uint8_t cnt,
+							uint16_t interval)
+{
+	json_object *jretransmit;
+
+	json_object_object_del(jobj, "retransmit");
+
+	jretransmit = json_object_new_object();
+	if (jretransmit)
+		return false;
+
+	if (!mesh_db_write_int(jretransmit, "count", cnt))
+		goto fail;
+
+	if (!mesh_db_write_int(jretransmit, "interval", interval))
+		goto fail;
+
+	json_object_object_add(jobj, "retransmit", jretransmit);
+
+	return true;
+fail:
+	json_object_put(jretransmit);
+	return false;
+
+}
+
+bool mesh_db_write_iv_index(json_object *jobj, uint32_t idx, bool update)
+{
+	int tmp = update ? 1 : 0;
+
+	if (!mesh_db_write_int(jobj, "IVindex", idx))
+		return false;
+
+	if (!mesh_db_write_int(jobj, "IVupdate", tmp))
+		return false;
+
+	return true;
+}
+
+void mesh_db_remove_property(json_object *jobj, const char *desc)
+{
+	json_object_object_del(jobj, desc);
+}
diff --git a/meshd/mesh-json/mesh-db.h b/meshd/mesh-json/mesh-db.h
new file mode 100644
index 000000000..336302f28
--- /dev/null
+++ b/meshd/mesh-json/mesh-db.h
@@ -0,0 +1,144 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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.
+ *
+ *
+ */
+
+#include <json-c/json.h>
+
+struct mesh_db_sub {
+	bool virt;
+	union {
+		uint16_t addr;
+		uint8_t	virt_addr[16];
+	} src;
+};
+
+struct mesh_db_pub {
+	bool virt;
+	uint16_t addr;
+	uint16_t idx;
+	uint8_t ttl;
+	uint8_t credential;
+	uint8_t period;
+	uint8_t retransmit;
+	uint8_t virt_addr[16];
+};
+
+struct mesh_db_model {
+	struct mesh_db_sub *subs;
+	struct mesh_db_pub *pub;
+	uint16_t *bindings;
+	uint32_t id;
+	bool vendor;
+	uint32_t num_bindings;
+	uint32_t num_subs;
+};
+
+struct mesh_db_element {
+	struct l_queue *models;
+	uint16_t location;
+	uint8_t index;
+};
+
+struct mesh_db_modes {
+	struct {
+		uint16_t interval;
+		uint8_t cnt;
+		uint8_t state;
+	} relay;
+	uint8_t lpn;
+	uint8_t friend;
+	uint8_t proxy;
+	uint8_t beacon;
+};
+
+struct mesh_db_node {
+	bool provisioner;
+	uint32_t seq_number;
+	struct mesh_db_modes modes;
+	uint16_t cid;
+	uint16_t pid;
+	uint16_t vid;
+	uint16_t crpl;
+	uint16_t unicast;
+	uint8_t ttl;
+	struct l_queue *elements;
+	uint8_t uuid[16];
+};
+
+struct mesh_db_prov {
+	uint16_t algorithm;
+	struct {
+		uint16_t actions;
+		uint8_t size;
+	} input_oob;
+	uint8_t pub_type;
+	struct {
+		uint16_t actions;
+		uint8_t size;
+	} output_oob;
+	uint8_t static_type;
+	uint8_t priv_key[32];
+};
+
+typedef bool (*mesh_db_net_key_cb)(uint16_t idx, uint8_t key[16],
+			uint8_t new_key[16], int phase, void *user_data);
+typedef bool (*mesh_db_app_key_cb)(uint16_t idx, uint16_t net_idx,
+			uint8_t key[16], uint8_t new_key[16], void *user_data);
+typedef bool (*mesh_db_node_cb)(struct mesh_db_node *node, void *user_data);
+
+bool mesh_db_read_node(json_object *jobj, mesh_db_node_cb cb, void *user_data);
+bool mesh_db_read_unprovisioned_device(json_object *jnode, mesh_db_node_cb cb,
+							void *user_data);
+bool mesh_db_read_prov_info(json_object *jnode, struct mesh_db_prov *prov);
+bool mesh_db_read_iv_index(json_object *jobj, uint32_t *idx, bool *update);
+bool mesh_db_read_device_key(json_object *jobj, uint8_t key_buf[16]);
+bool mesh_db_read_net_transmit(json_object *jobj, uint8_t *cnt,
+							uint16_t *interval);
+bool mesh_db_write_net_transmit(json_object *jobj, uint8_t cnt,
+							uint16_t interval);
+bool mesh_db_read_net_keys(json_object *jobj, mesh_db_net_key_cb cb,
+							void *user_data);
+bool mesh_db_read_app_keys(json_object *jobj, mesh_db_app_key_cb cb,
+							void *user_data);
+bool mesh_db_write_device_key(json_object *jobj, uint8_t *key);
+bool mesh_db_write_network_key(json_object *jobj, uint16_t idx, uint8_t *key,
+						uint8_t *new_key, int phase);
+bool mesh_db_write_app_key(json_object *jobj, uint16_t net_idx,
+			uint16_t app_idx, uint8_t *key, uint8_t *new_key);
+bool mesh_db_write_int(json_object *jobj, const char *keyword, int value);
+bool mesh_db_write_uint16_hex(json_object *jobj, const char *desc,
+								uint16_t value);
+bool mesh_db_write_bool(json_object *jobj, const char *keyword, bool value);
+bool mesh_db_write_relay_mode(json_object *jnode, uint8_t mode, uint8_t count,
+							uint16_t interval);
+bool mesh_db_write_mode(json_object *jobj, const char *keyword, int value);
+bool mesh_db_model_binding_add(json_object *jnode, uint8_t ele_idx, bool vendor,
+					uint32_t mod_id, uint16_t app_idx);
+bool mesh_db_model_binding_del(json_object *jnode, uint8_t ele_idx, bool vendor,
+					uint32_t mod_id, uint16_t app_idx);
+bool mesh_db_app_key_add(json_object *jnode, uint16_t net_idx, uint16_t app_idx,
+					const uint8_t key[16], bool update);
+bool mesh_db_app_key_del(json_object *jobj, uint16_t net_idx, uint16_t idx);
+bool mesh_db_net_key_add(json_object *jobj, uint16_t net_idx,
+					const uint8_t key[16], int phase);
+bool mesh_db_net_key_del(json_object *jobj, uint16_t net_idx);
+bool mesh_db_write_kr_phase(json_object *jobj, uint16_t net_idx, int phase);
+bool mesh_db_write_address(json_object *jobj, uint16_t address);
+bool mesh_db_write_iv_index(json_object *jobj, uint32_t idx, bool update);
+void mesh_db_remove_property(json_object *jobj, const char *desc);
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v2 10/11] meshd: Sample device composition in JSON fromat
  2018-04-25 17:50 [PATCH BlueZ v2 00/11] Bluetooth Mesh Daemon Brian Gix
                   ` (6 preceding siblings ...)
  2018-04-25 17:50 ` [PATCH BlueZ v2 09/11] meshd: Read and write mesh configuration in JSON format Brian Gix
@ 2018-04-25 17:50 ` Brian Gix
  2018-04-25 17:50 ` [PATCH BlueZ v2 11/11] Makefile for meshd and configure.ac Brian Gix
  8 siblings, 0 replies; 10+ messages in thread
From: Brian Gix @ 2018-04-25 17:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Inga Stotland

From: Inga Stotland <inga.stotland@intel.com>

This is a sample unprovisioned device composition described
in JSON format.
---
 meshd/config/composition.json | 44 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 meshd/config/composition.json

diff --git a/meshd/config/composition.json b/meshd/config/composition.json
new file mode 100644
index 000000000..20c0d0c3a
--- /dev/null
+++ b/meshd/config/composition.json
@@ -0,0 +1,44 @@
+{
+  "$schema":"file:\/\/\/BlueZ\/MeshD\/local_schema\/mesh.jsonschema",
+  "meshName":"BT Mesh sample node",
+  "UUID":"E0ED0F0200000000203C100200000000",
+  "cid":"0002",
+  "pid":"0010",
+  "vid":"0001",
+  "crpl":"000a",
+  "proxy":"unsupported",
+  "friend":"disabled",
+  "lowPower":"disabled",
+  "relay":{
+    "mode":"enabled"
+  },
+  "elements":[
+    {
+      "elementIndex":0,
+      "location":"0001",
+      "models":[
+        {
+          "modelId":"0000"
+        },
+        {
+          "modelId":"1001"
+        }
+      ]
+    }
+  ],
+  "provision": {
+    "privateKey": "729aa0670d72cd6497502ed473502b037e8803b5c60829a5a3caa219505530ba",
+    "algorithms": [ 0 ],
+	"inputOOB": {
+       "size": 8,
+       "actions": [ 2, 3]
+    },
+    "outputOOB": {
+      "size": 8,
+      "actions": [ 3, 4]
+    },
+    "publicType": false,
+    "staticType": false
+  },
+   }
+}
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v2 11/11] Makefile for meshd and configure.ac
  2018-04-25 17:50 [PATCH BlueZ v2 00/11] Bluetooth Mesh Daemon Brian Gix
                   ` (7 preceding siblings ...)
  2018-04-25 17:50 ` [PATCH BlueZ v2 10/11] meshd: Sample device composition in JSON fromat Brian Gix
@ 2018-04-25 17:50 ` Brian Gix
  8 siblings, 0 replies; 10+ messages in thread
From: Brian Gix @ 2018-04-25 17:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Inga Stotland

From: Inga Stotland <inga.stotland@intel.com>

---
 Makefile.am    |  1 +
 Makefile.meshd | 44 ++++++++++++++++++++++++++++++++++++++++++++
 configure.ac   |  2 +-
 3 files changed, 46 insertions(+), 1 deletion(-)
 create mode 100644 Makefile.meshd

diff --git a/Makefile.am b/Makefile.am
index daf34b6ca..2a96fa1d3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -225,6 +225,7 @@ unit_tests =
 include Makefile.tools
 include Makefile.obexd
 include android/Makefile.am
+include Makefile.meshd
 
 if HID2HCI
 rulesdir = @UDEV_DIR@/rules.d
diff --git a/Makefile.meshd b/Makefile.meshd
new file mode 100644
index 000000000..4a9d45a12
--- /dev/null
+++ b/Makefile.meshd
@@ -0,0 +1,44 @@
+if MESH
+mesh_json_sources = meshd/mesh-json/mesh-db.h meshd/mesh-json/mesh-db.c
+mesh_common_sources = meshd/common/mesh-defs.h \
+					meshd/common/util.h meshd/common/util.c
+
+meshd_sources = $(mesh_common_sources) $(mesh_json_sources) \
+				meshd/src/mesh.h meshd/src/mesh.c \
+				meshd/src/mesh-io.h meshd/src/mesh-io.c \
+				meshd/src/mesh-io-api.h meshd/src/bt.h \
+				meshd/src/mesh-io-generic.h \
+				meshd/src/mesh-io-generic.c \
+				meshd/src/hci.h meshd/src/hci.c \
+				meshd/src/storage.h meshd/src/storage.c \
+				meshd/src/net.h meshd/src/net.c \
+				meshd/src/display.h meshd/src/display.c \
+				meshd/src/crypto.h meshd/src/crypto.c \
+				meshd/src/friend.h meshd/src/friend.c \
+				meshd/src/appkeys.h meshd/src/appkey.c \
+				meshd/src/node.h meshd/src/node.c \
+				meshd/src/prov.h meshd/src/prov.c \
+				meshd/src/provision.h meshd/src/provision.c \
+				meshd/src/model.h meshd/src/model.c \
+				meshd/src/cfgmod.h meshd/src/cfgmod-server.c
+
+libexec_PROGRAMS += meshd/src/meshd
+
+meshd_src_meshd_SOURCES = $(meshd_sources) \
+						meshd/src/main.c
+
+meshd_src_meshd_LDADD = /usr/lib64/libell.la src/shared/ecc.lo \
+			@DBUS_LIBS@ -lell -ljson-c -ldl
+
+noinst_PROGRAMS += meshd/src/btmesh
+
+meshd_src_btmesh_SOURCES = $(meshd_sources) \
+						meshd/common/agent.h \
+						meshd/common/agent.c \
+						meshd/src/btmesh.c
+
+meshd_src_btmesh_LDADD = /usr/lib64/libell.la src/shared/ecc.lo \
+						src/libshared-mainloop.la \
+						-lreadline -lell -ljson-c -ldl
+
+endif
diff --git a/configure.ac b/configure.ac
index 5132131f2..f6259c0fe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -248,7 +248,7 @@ AC_ARG_ENABLE(btpclient, AC_HELP_STRING([--enable-btpclient],
 		[enable BTP client]), [enable_btpclient=${enableval}])
 AM_CONDITIONAL(BTPCLIENT, test "${enable_btpclient}" = "yes")
 
-if (test "${enable_btpclient}" = "yes"); then
+if (test "${enable_btpclient}" = "yes" || test "${enable_mesh}" == "yes"); then
 	PKG_CHECK_MODULES(ELL, ell >= 0.3, dummy=yes,
 			  AC_MSG_ERROR(ell library >= 0.3 is required))
 	AC_SUBST(ELL_CFLAGS)
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2018-04-25 17:50 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-04-25 17:50 [PATCH BlueZ v2 00/11] Bluetooth Mesh Daemon Brian Gix
2018-04-25 17:50 ` [PATCH BlueZ v2 01/11] meshd: Shared private meshd interfaces Brian Gix
2018-04-25 17:50 ` [PATCH BlueZ v2 03/11] meshd: Provisioning logic for mesh Brian Gix
2018-04-25 17:50 ` [PATCH BlueZ v2 05/11] meshd: Header files for mesh access layer and utilities Brian Gix
2018-04-25 17:50 ` [PATCH BlueZ v2 06/11] meshd: Source " Brian Gix
2018-04-25 17:50 ` [PATCH BlueZ v2 07/11] meshd: Source code for handling access layer mux/demux Brian Gix
2018-04-25 17:50 ` [PATCH BlueZ v2 08/11] meshd: Mesh config server model Brian Gix
2018-04-25 17:50 ` [PATCH BlueZ v2 09/11] meshd: Read and write mesh configuration in JSON format Brian Gix
2018-04-25 17:50 ` [PATCH BlueZ v2 10/11] meshd: Sample device composition in JSON fromat Brian Gix
2018-04-25 17:50 ` [PATCH BlueZ v2 11/11] Makefile for meshd and configure.ac Brian Gix

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.